import { describe, it, expect } from 'vitest' import { generate } from '../generate.js' describe('scaffold-layout / generate', () => { const spec = { projectPath: '/web', appCode: 'TestV2', force: false } it('writes PageTemplate at src/components/ui/PageTemplate.tsx (not the legacy src/components path)', () => { const files = generate(spec) const pt = files.find((f) => f.path.endsWith('PageTemplate.tsx')) expect(pt).toBeDefined() expect(pt!.path).toBe('src/components/ui/PageTemplate.tsx') // Negative: ensure the legacy non-ui path is gone — the path mismatch // (scaffold-layout writes here, scaffold-component imports from /ui/) // is the silent-spinner #1 root cause. expect(files.some((f) => f.path === 'src/components/PageTemplate.tsx')).toBe(false) }) it('declares all six props in PageTemplateProps', () => { const files = generate(spec) const pt = files.find((f) => f.path.endsWith('PageTemplate.tsx'))! // Each prop name must appear in the interface for (const prop of ['title', 'subtitle', 'icon', 'actions', 'children', 'className', 'contentClassName']) { expect(pt.content).toMatch(new RegExp(`\\b${prop}\\??:`)) } }) it('destructures and renders subtitle / icon / actions / className / contentClassName', () => { const files = generate(spec) const pt = files.find((f) => f.path.endsWith('PageTemplate.tsx'))! // Destructured in the component signature (multiline OK) expect(pt.content).toMatch(/title,[\s\S]*subtitle,[\s\S]*icon,[\s\S]*actions,[\s\S]*children,[\s\S]*className,[\s\S]*contentClassName/) // Each prop is rendered somewhere in JSX expect(pt.content).toMatch(/\{subtitle\s*&&/) expect(pt.content).toMatch(/\{icon\s*&&/) expect(pt.content).toMatch(/\{actions\s*&&/) expect(pt.content).toMatch(/className\s*\?\?\s*''/) expect(pt.content).toMatch(/contentClassName\s*\?\?\s*''/) }) it('emits PageTemplate ONLY — the chrome comes from @atlashub/smartstack', () => { // A locally emitted AppShell/Sidebar duplicated the package chrome, was // never mounted (App.tsx renders alone), and carried no // mobile branch — a pure drift source. The package owns the chrome. const files = generate(spec) expect(files.map((f) => f.path)).toEqual(['src/components/ui/PageTemplate.tsx']) expect(files.some((f) => f.path === 'src/components/AppShell.tsx')).toBe(false) expect(files.some((f) => f.path === 'src/components/Sidebar.tsx')).toBe(false) }) it('emits no chrome markup at all (no header/aside/nav shell in the output)', () => { const files = generate(spec) const all = files.map((f) => f.content).join('\n') expect(all).not.toContain('