|
| 1 | +import * as path from 'path'; |
| 2 | +import * as fs from 'fs-extra'; |
| 3 | +import * as yaml from 'js-yaml'; |
| 4 | +import prompts from 'prompts'; |
| 5 | +import { runInit } from '../../src/commands/init'; |
| 6 | +import { DevEnvConfigSchema } from '../../src/types/config'; |
| 7 | +import { logger } from '../../src/utils/logger'; |
| 8 | + |
| 9 | +describe('init wizard (runInit)', () => { |
| 10 | + let tempDir: string; |
| 11 | + const originalCwd = process.cwd(); |
| 12 | + const originalTTY = process.stdout.isTTY; |
| 13 | + |
| 14 | + beforeEach(async () => { |
| 15 | + jest.spyOn(logger, 'info').mockImplementation(); |
| 16 | + jest.spyOn(logger, 'success').mockImplementation(); |
| 17 | + jest.spyOn(logger, 'error').mockImplementation(); |
| 18 | + jest.spyOn(logger, 'debug').mockImplementation(); |
| 19 | + // Force interactive so runInit drives the wizard. |
| 20 | + process.stdout.isTTY = true; |
| 21 | + tempDir = path.join(require('os').tmpdir(), `devkit-initw-${Date.now()}-${Math.floor(performance.now())}`); |
| 22 | + await fs.ensureDir(tempDir); |
| 23 | + }); |
| 24 | + |
| 25 | + afterEach(async () => { |
| 26 | + process.stdout.isTTY = originalTTY; |
| 27 | + process.chdir(originalCwd); |
| 28 | + await fs.remove(tempDir).catch(() => {}); |
| 29 | + jest.restoreAllMocks(); |
| 30 | + }); |
| 31 | + |
| 32 | + it('builds a valid .dev-env.yml from wizard answers (minimal flow)', async () => { |
| 33 | + // Order: name, version, addDeps?, addDb?, addService?, addEnv?, dockerEnabled? |
| 34 | + prompts.inject(['my-app', '2.1.0', false, false, false, true, true]); |
| 35 | + |
| 36 | + await runInit(tempDir); |
| 37 | + |
| 38 | + const configPath = path.join(tempDir, '.dev-env.yml'); |
| 39 | + expect(await fs.pathExists(configPath)).toBe(true); |
| 40 | + const parsed = DevEnvConfigSchema.parse(yaml.load(await fs.readFile(configPath, 'utf-8'))); |
| 41 | + expect(parsed.name).toBe('my-app'); |
| 42 | + expect(parsed.version).toBe('2.1.0'); |
| 43 | + expect(parsed.env).toMatchObject({ NODE_ENV: 'development' }); |
| 44 | + expect(parsed.docker?.enabled).toBe(true); |
| 45 | + }); |
| 46 | + |
| 47 | + it('captures a database with a health check from wizard answers', async () => { |
| 48 | + // name, version, addDeps?, addDb?(yes), dbType, dbFields..., then addDb?(no) etc. |
| 49 | + // db prompt sequence after type select: name, port, user, password, database, migrations?, seed? |
| 50 | + // To stay robust we drive a redis DB which has the fewest follow-ups. |
| 51 | + prompts.inject([ |
| 52 | + 'db-app', '1.0.0', |
| 53 | + false, // addDeps? |
| 54 | + true, 'redis', // addDb? + type |
| 55 | + 'cache', '7', 6379, 'localhost', '', '', // db: name, version, port, host, user, password |
| 56 | + false, // add migrations? |
| 57 | + false, // add seed? |
| 58 | + false, // addDb? (stop) |
| 59 | + false, // addService? |
| 60 | + false, // addEnv? |
| 61 | + false, // dockerEnabled? |
| 62 | + true, // add health check? |
| 63 | + ]); |
| 64 | + |
| 65 | + await runInit(tempDir); |
| 66 | + |
| 67 | + const configPath = path.join(tempDir, '.dev-env.yml'); |
| 68 | + const parsed = DevEnvConfigSchema.parse(yaml.load(await fs.readFile(configPath, 'utf-8'))); |
| 69 | + expect(parsed.databases?.[0]?.type).toBe('redis'); |
| 70 | + expect(parsed.health_checks?.some((h) => h.type === 'redis')).toBe(true); |
| 71 | + }); |
| 72 | +}); |
0 commit comments