// RED phase: Test that scaffolding produces working projects import assert from 'node:assert'; import { test, describe } from 'node:test'; import { mkdtempSync, existsSync, readFileSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { initProject } from './init.js'; import { getPresetNames, getPreset } from './presets/index.js'; describe('imhotep init', () => { test('react-playwright preset creates expected files', () => { const targetDir = mkdtempSync(join(tmpdir(), 'imhotep-init-test-')); try { initProject({ preset: 'react', targetDir }); // Verify all expected files exist assert.ok(existsSync(join(targetDir, 'imhotep.config.js')), 'config file should exist'); assert.ok(existsSync(join(targetDir, 'tests', 'example.test.ts')), 'test file should exist'); assert.ok(existsSync(join(targetDir, 'fixtures', 'example.html')), 'fixture file should exist'); assert.ok(existsSync(join(targetDir, 'package.json')), 'package.json should exist'); assert.ok(existsSync(join(targetDir, 'README.md')), 'README should exist'); // Verify config has render target const config = readFileSync(join(targetDir, 'imhotep.config.js'), 'utf-8'); assert.ok(config.includes('render'), 'config should mention render target'); // Verify test file has assertions const testFile = readFileSync(join(targetDir, 'tests', 'example.test.ts'), 'utf-8'); assert.ok(testFile.includes('expect'), 'test should have assertions'); // Verify package.json has test scripts const pkg = JSON.parse(readFileSync(join(targetDir, 'package.json'), 'utf-8')); assert.ok(pkg.scripts.test, 'package.json should have test script'); assert.ok(pkg.scripts['test:ci'], 'package.json should have ci script'); } finally { rmSync(targetDir, { recursive: true, force: true }); } }); test('vue preset creates expected files', () => { const targetDir = mkdtempSync(join(tmpdir(), 'imhotep-init-test-')); try { initProject({ preset: 'vue', targetDir }); assert.ok(existsSync(join(targetDir, 'imhotep.config.js')), 'config file should exist'); assert.ok(existsSync(join(targetDir, 'tests', 'example.test.ts')), 'test file should exist'); assert.ok(existsSync(join(targetDir, 'fixtures', 'example.html')), 'fixture file should exist'); assert.ok(existsSync(join(targetDir, 'package.json')), 'package.json should exist'); assert.ok(existsSync(join(targetDir, 'README.md')), 'README should exist'); const config = readFileSync(join(targetDir, 'imhotep.config.js'), 'utf-8'); assert.ok(config.includes('render'), 'config should mention render target'); } finally { rmSync(targetDir, { recursive: true, force: true }); } }); test('storybook preset creates expected files', () => { const targetDir = mkdtempSync(join(tmpdir(), 'imhotep-init-test-')); try { initProject({ preset: 'storybook', targetDir }); assert.ok(existsSync(join(targetDir, 'imhotep.config.js')), 'config file should exist'); assert.ok(existsSync(join(targetDir, 'tests', 'example.test.ts')), 'test file should exist'); assert.ok(existsSync(join(targetDir, 'fixtures', 'example.html')), 'fixture file should exist'); assert.ok(existsSync(join(targetDir, 'package.json')), 'package.json should exist'); assert.ok(existsSync(join(targetDir, 'README.md')), 'README should exist'); const config = readFileSync(join(targetDir, 'imhotep.config.js'), 'utf-8'); assert.ok(config.includes('render'), 'config should mention render target'); } finally { rmSync(targetDir, { recursive: true, force: true }); } }); test('throws on unknown preset', () => { const targetDir = mkdtempSync(join(tmpdir(), 'imhotep-init-test-')); try { assert.throws(() => { initProject({ preset: 'unknown-preset', targetDir }); }, /Unknown preset/); } finally { rmSync(targetDir, { recursive: true, force: true }); } }); test('scaffolded react tests have passing assertions syntax', () => { const targetDir = mkdtempSync(join(tmpdir(), 'imhotep-init-test-')); try { initProject({ preset: 'react', targetDir }); const testFile = readFileSync(join(targetDir, 'tests', 'example.test.ts'), 'utf-8'); assert.ok(testFile.includes('const ui = await imhotep(page);'), 'template should await imhotep(page)'); assert.ok(testFile.includes('await ui.checkAll();'), 'template should evaluate batched assertions with checkAll()'); } finally { rmSync(targetDir, { recursive: true, force: true }); } }); test('all presets use publish-safe dependency ranges (no workspace protocol)', () => { for (const name of getPresetNames()) { const preset = getPreset(name); const pkg = preset.packageJson; for (const [dep, version] of Object.entries(pkg.devDependencies ?? {})) { assert.ok(typeof version === 'string', `dependency ${dep} in ${name} should be a string`); assert.ok(!version.startsWith('workspace:'), `${name} must not use workspace protocol for ${dep}`); } } }); test('new framework presets scaffold expected files', () => { const extraPresets = ['next', 'nuxt', 'remix', 'astro']; for (const preset of extraPresets) { const targetDir = mkdtempSync(join(tmpdir(), 'imhotep-init-test-')); try { initProject({ preset, targetDir }); assert.ok(existsSync(join(targetDir, 'imhotep.config.js')), `${preset} config should exist`); assert.ok(existsSync(join(targetDir, 'tests', 'example.test.ts')), `${preset} test should exist`); assert.ok(existsSync(join(targetDir, 'fixtures', 'example.html')), `${preset} fixture should exist`); } finally { rmSync(targetDir, { recursive: true, force: true }); } } }); });