@sprouted/create-vibes
Version:
Create a new Vibes monorepo
238 lines (208 loc) • 6.68 kB
text/typescript
import { CreateAppCommand, CreateAppOptions } from './create-app'
import { TemplateEngine } from '../template-engine/engine'
import inquirer from 'inquirer'
import * as fs from 'fs/promises'
jest.mock('../template-engine/engine')
jest.mock('inquirer')
jest.mock('fs/promises')
describe('CreateAppCommand', () => {
let command: CreateAppCommand
let mockEngine: jest.Mocked<TemplateEngine>
beforeEach(() => {
mockEngine = {
generateFromTemplate: jest.fn().mockResolvedValue(undefined)
} as any
command = new CreateAppCommand(mockEngine)
jest.spyOn(console, 'log').mockImplementation()
;(fs.mkdir as jest.Mock).mockResolvedValue(undefined)
})
afterEach(() => {
jest.clearAllMocks()
})
it('should create a web app with provided options', async () => {
const options: CreateAppOptions = {
type: 'web',
name: 'my-app',
framework: 'vite-react',
includeRouter: true,
includeTailwind: false
}
await command.execute(options)
expect(fs.mkdir).toHaveBeenCalledWith(
expect.stringContaining('apps'),
{ recursive: true }
)
expect(mockEngine.generateFromTemplate).toHaveBeenCalledWith(
expect.stringContaining('templates/typescript/apps/web'),
{
appName: 'my-app',
framework: 'vite-react',
includeRouter: true,
includeTailwind: false
},
{
outputPath: expect.stringContaining('apps/my-app'),
dryRun: false
}
)
expect(console.log).toHaveBeenCalledWith('✨ Created web app: my-app')
})
it('should create a mobile app with provided options', async () => {
const options: CreateAppOptions = {
type: 'mobile',
name: 'my-mobile-app',
displayName: 'My Mobile App',
includeNavigator: true,
includeAuth: false,
includeApiClient: true
}
await command.execute(options)
expect(mockEngine.generateFromTemplate).toHaveBeenCalledWith(
expect.stringContaining('templates/typescript/apps/mobile'),
{
appName: 'my-mobile-app',
displayName: 'My Mobile App',
includeNavigator: true,
includeAuth: false,
includeApiClient: true,
framework: undefined,
includeRouter: undefined,
includeTailwind: undefined
},
{
outputPath: expect.stringContaining('apps/my-mobile-app'),
dryRun: false
}
)
expect(console.log).toHaveBeenCalledWith('✨ Created mobile app: my-mobile-app')
})
it('should create an API app with provided options', async () => {
const options: CreateAppOptions = {
type: 'api',
name: 'my-api',
port: 3001,
includeDatabase: true,
includeAuth: true,
includeValidation: true,
includeSwagger: false,
includeGraphQL: false
}
await command.execute(options)
expect(mockEngine.generateFromTemplate).toHaveBeenCalledWith(
expect.stringContaining('templates/typescript/apps/api'),
{
appName: 'my-api',
port: 3001,
includeDatabase: true,
includeAuth: true,
includeValidation: true,
includeSwagger: false,
includeGraphQL: false,
displayName: undefined,
framework: undefined,
includeRouter: undefined,
includeTailwind: undefined,
includeNavigator: undefined,
includeApiClient: undefined
},
{
outputPath: expect.stringContaining('apps/my-api'),
dryRun: false
}
)
expect(console.log).toHaveBeenCalledWith('✨ Created api app: my-api')
})
it('should create a desktop app with provided options', async () => {
const options: CreateAppOptions = {
type: 'desktop',
name: 'my-desktop',
displayName: 'My Desktop App',
appId: 'com.vibes.my-desktop',
includeAutoUpdater: true,
includeSystemTray: false,
includeMenuBar: true,
includeIpc: true,
includeDarkMode: true
}
await command.execute(options)
expect(mockEngine.generateFromTemplate).toHaveBeenCalledWith(
expect.stringContaining('templates/typescript/apps/desktop'),
{
appName: 'my-desktop',
displayName: 'My Desktop App',
appId: 'com.vibes.my-desktop',
includeAutoUpdater: true,
includeSystemTray: false,
includeMenuBar: true,
includeIpc: true,
includeDarkMode: true,
framework: undefined,
includeRouter: undefined,
includeTailwind: undefined,
includeNavigator: undefined,
includeAuth: undefined,
includeApiClient: undefined,
port: undefined,
includeDatabase: undefined,
includeValidation: undefined,
includeSwagger: undefined,
includeGraphQL: undefined
},
{
outputPath: expect.stringContaining('apps/my-desktop'),
dryRun: false
}
)
expect(console.log).toHaveBeenCalledWith('✨ Created desktop app: my-desktop')
})
it('should prompt for missing options', async () => {
const options: CreateAppOptions = {
type: 'web'
}
;(inquirer.prompt as jest.Mock).mockResolvedValue({
name: 'prompted-app',
framework: 'nextjs',
includeRouter: false,
includeTailwind: true
})
await command.execute(options)
expect(inquirer.prompt).toHaveBeenCalledWith(
expect.arrayContaining([
expect.objectContaining({ name: 'name' }),
expect.objectContaining({ name: 'framework' }),
expect.objectContaining({ name: 'includeRouter' }),
expect.objectContaining({ name: 'includeTailwind' })
])
)
expect(mockEngine.generateFromTemplate).toHaveBeenCalledWith(
expect.any(String),
{
appName: 'prompted-app',
framework: 'nextjs',
includeRouter: false,
includeTailwind: true
},
expect.any(Object)
)
})
it('should validate app name', async () => {
const options: CreateAppOptions = {
type: 'web'
}
;(inquirer.prompt as jest.Mock).mockImplementation((questions) => {
const nameQuestion = questions.find((q: any) => q.name === 'name')
expect(nameQuestion.validate('Invalid Name!')).toBe(
'App name must be lowercase alphanumeric with dashes'
)
expect(nameQuestion.validate('')).toBe('App name is required')
expect(nameQuestion.validate('valid-name')).toBe(true)
return Promise.resolve({
name: 'valid-name',
framework: 'vite-react',
includeRouter: true,
includeTailwind: false
})
})
await command.execute(options)
})
})