popeye-cli 1.4.7 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +264 -63
  3. package/dist/adapters/gemini.d.ts +1 -0
  4. package/dist/adapters/gemini.d.ts.map +1 -1
  5. package/dist/adapters/gemini.js +9 -4
  6. package/dist/adapters/gemini.js.map +1 -1
  7. package/dist/adapters/grok.d.ts +1 -0
  8. package/dist/adapters/grok.d.ts.map +1 -1
  9. package/dist/adapters/grok.js +9 -4
  10. package/dist/adapters/grok.js.map +1 -1
  11. package/dist/adapters/openai.d.ts +1 -1
  12. package/dist/adapters/openai.d.ts.map +1 -1
  13. package/dist/adapters/openai.js +35 -9
  14. package/dist/adapters/openai.js.map +1 -1
  15. package/dist/cli/commands/create.d.ts.map +1 -1
  16. package/dist/cli/commands/create.js +54 -4
  17. package/dist/cli/commands/create.js.map +1 -1
  18. package/dist/cli/interactive.d.ts +29 -0
  19. package/dist/cli/interactive.d.ts.map +1 -1
  20. package/dist/cli/interactive.js +132 -7
  21. package/dist/cli/interactive.js.map +1 -1
  22. package/dist/generators/all.d.ts +8 -2
  23. package/dist/generators/all.d.ts.map +1 -1
  24. package/dist/generators/all.js +37 -316
  25. package/dist/generators/all.js.map +1 -1
  26. package/dist/generators/doc-parser.d.ts +64 -0
  27. package/dist/generators/doc-parser.d.ts.map +1 -0
  28. package/dist/generators/doc-parser.js +407 -0
  29. package/dist/generators/doc-parser.js.map +1 -0
  30. package/dist/generators/frontend-design-analyzer.d.ts +30 -0
  31. package/dist/generators/frontend-design-analyzer.d.ts.map +1 -0
  32. package/dist/generators/frontend-design-analyzer.js +208 -0
  33. package/dist/generators/frontend-design-analyzer.js.map +1 -0
  34. package/dist/generators/shared-packages.d.ts +45 -0
  35. package/dist/generators/shared-packages.d.ts.map +1 -0
  36. package/dist/generators/shared-packages.js +456 -0
  37. package/dist/generators/shared-packages.js.map +1 -0
  38. package/dist/generators/templates/index.d.ts +8 -0
  39. package/dist/generators/templates/index.d.ts.map +1 -1
  40. package/dist/generators/templates/index.js +8 -0
  41. package/dist/generators/templates/index.js.map +1 -1
  42. package/dist/generators/templates/website-components.d.ts +33 -0
  43. package/dist/generators/templates/website-components.d.ts.map +1 -0
  44. package/dist/generators/templates/website-components.js +303 -0
  45. package/dist/generators/templates/website-components.js.map +1 -0
  46. package/dist/generators/templates/website-config.d.ts +55 -0
  47. package/dist/generators/templates/website-config.d.ts.map +1 -0
  48. package/dist/generators/templates/website-config.js +425 -0
  49. package/dist/generators/templates/website-config.js.map +1 -0
  50. package/dist/generators/templates/website-conversion.d.ts +27 -0
  51. package/dist/generators/templates/website-conversion.d.ts.map +1 -0
  52. package/dist/generators/templates/website-conversion.js +326 -0
  53. package/dist/generators/templates/website-conversion.js.map +1 -0
  54. package/dist/generators/templates/website-landing.d.ts +24 -0
  55. package/dist/generators/templates/website-landing.d.ts.map +1 -0
  56. package/dist/generators/templates/website-landing.js +276 -0
  57. package/dist/generators/templates/website-landing.js.map +1 -0
  58. package/dist/generators/templates/website-layout.d.ts +42 -0
  59. package/dist/generators/templates/website-layout.d.ts.map +1 -0
  60. package/dist/generators/templates/website-layout.js +408 -0
  61. package/dist/generators/templates/website-layout.js.map +1 -0
  62. package/dist/generators/templates/website-pricing.d.ts +11 -0
  63. package/dist/generators/templates/website-pricing.d.ts.map +1 -0
  64. package/dist/generators/templates/website-pricing.js +313 -0
  65. package/dist/generators/templates/website-pricing.js.map +1 -0
  66. package/dist/generators/templates/website-sections.d.ts +102 -0
  67. package/dist/generators/templates/website-sections.d.ts.map +1 -0
  68. package/dist/generators/templates/website-sections.js +444 -0
  69. package/dist/generators/templates/website-sections.js.map +1 -0
  70. package/dist/generators/templates/website-seo.d.ts +76 -0
  71. package/dist/generators/templates/website-seo.d.ts.map +1 -0
  72. package/dist/generators/templates/website-seo.js +326 -0
  73. package/dist/generators/templates/website-seo.js.map +1 -0
  74. package/dist/generators/templates/website.d.ts +10 -83
  75. package/dist/generators/templates/website.d.ts.map +1 -1
  76. package/dist/generators/templates/website.js +12 -875
  77. package/dist/generators/templates/website.js.map +1 -1
  78. package/dist/generators/website-content-scanner.d.ts +37 -0
  79. package/dist/generators/website-content-scanner.d.ts.map +1 -0
  80. package/dist/generators/website-content-scanner.js +165 -0
  81. package/dist/generators/website-content-scanner.js.map +1 -0
  82. package/dist/generators/website-context.d.ts +119 -0
  83. package/dist/generators/website-context.d.ts.map +1 -0
  84. package/dist/generators/website-context.js +350 -0
  85. package/dist/generators/website-context.js.map +1 -0
  86. package/dist/generators/website-debug.d.ts +68 -0
  87. package/dist/generators/website-debug.d.ts.map +1 -0
  88. package/dist/generators/website-debug.js +93 -0
  89. package/dist/generators/website-debug.js.map +1 -0
  90. package/dist/generators/website.d.ts +5 -0
  91. package/dist/generators/website.d.ts.map +1 -1
  92. package/dist/generators/website.js +136 -11
  93. package/dist/generators/website.js.map +1 -1
  94. package/dist/generators/workspace-root.d.ts +27 -0
  95. package/dist/generators/workspace-root.d.ts.map +1 -0
  96. package/dist/generators/workspace-root.js +100 -0
  97. package/dist/generators/workspace-root.js.map +1 -0
  98. package/dist/state/index.d.ts +35 -0
  99. package/dist/state/index.d.ts.map +1 -1
  100. package/dist/state/index.js +40 -0
  101. package/dist/state/index.js.map +1 -1
  102. package/dist/types/consensus.d.ts +3 -0
  103. package/dist/types/consensus.d.ts.map +1 -1
  104. package/dist/types/consensus.js +1 -0
  105. package/dist/types/consensus.js.map +1 -1
  106. package/dist/types/website-strategy.d.ts +263 -0
  107. package/dist/types/website-strategy.d.ts.map +1 -0
  108. package/dist/types/website-strategy.js +105 -0
  109. package/dist/types/website-strategy.js.map +1 -0
  110. package/dist/types/workflow.d.ts +21 -0
  111. package/dist/types/workflow.d.ts.map +1 -1
  112. package/dist/types/workflow.js +8 -0
  113. package/dist/types/workflow.js.map +1 -1
  114. package/dist/upgrade/handlers.d.ts +15 -0
  115. package/dist/upgrade/handlers.d.ts.map +1 -1
  116. package/dist/upgrade/handlers.js +52 -0
  117. package/dist/upgrade/handlers.js.map +1 -1
  118. package/dist/workflow/auto-fix-bundler.d.ts +37 -0
  119. package/dist/workflow/auto-fix-bundler.d.ts.map +1 -0
  120. package/dist/workflow/auto-fix-bundler.js +320 -0
  121. package/dist/workflow/auto-fix-bundler.js.map +1 -0
  122. package/dist/workflow/auto-fix.d.ts.map +1 -1
  123. package/dist/workflow/auto-fix.js +10 -3
  124. package/dist/workflow/auto-fix.js.map +1 -1
  125. package/dist/workflow/consensus.d.ts.map +1 -1
  126. package/dist/workflow/consensus.js +2 -0
  127. package/dist/workflow/consensus.js.map +1 -1
  128. package/dist/workflow/execution-mode.d.ts.map +1 -1
  129. package/dist/workflow/execution-mode.js +18 -0
  130. package/dist/workflow/execution-mode.js.map +1 -1
  131. package/dist/workflow/index.d.ts +4 -0
  132. package/dist/workflow/index.d.ts.map +1 -1
  133. package/dist/workflow/index.js +37 -0
  134. package/dist/workflow/index.js.map +1 -1
  135. package/dist/workflow/overview.d.ts +89 -0
  136. package/dist/workflow/overview.d.ts.map +1 -0
  137. package/dist/workflow/overview.js +358 -0
  138. package/dist/workflow/overview.js.map +1 -0
  139. package/dist/workflow/plan-mode.d.ts +6 -4
  140. package/dist/workflow/plan-mode.d.ts.map +1 -1
  141. package/dist/workflow/plan-mode.js +148 -6
  142. package/dist/workflow/plan-mode.js.map +1 -1
  143. package/dist/workflow/website-strategy.d.ts +79 -0
  144. package/dist/workflow/website-strategy.d.ts.map +1 -0
  145. package/dist/workflow/website-strategy.js +310 -0
  146. package/dist/workflow/website-strategy.js.map +1 -0
  147. package/dist/workflow/website-updater.d.ts +17 -0
  148. package/dist/workflow/website-updater.d.ts.map +1 -0
  149. package/dist/workflow/website-updater.js +116 -0
  150. package/dist/workflow/website-updater.js.map +1 -0
  151. package/dist/workflow/workflow-logger.d.ts +1 -1
  152. package/dist/workflow/workflow-logger.d.ts.map +1 -1
  153. package/dist/workflow/workflow-logger.js.map +1 -1
  154. package/package.json +1 -1
  155. package/src/adapters/gemini.ts +10 -4
  156. package/src/adapters/grok.ts +10 -4
  157. package/src/adapters/openai.ts +38 -6
  158. package/src/cli/commands/create.ts +58 -4
  159. package/src/cli/interactive.ts +143 -7
  160. package/src/generators/all.ts +49 -332
  161. package/src/generators/doc-parser.ts +449 -0
  162. package/src/generators/frontend-design-analyzer.ts +261 -0
  163. package/src/generators/shared-packages.ts +500 -0
  164. package/src/generators/templates/index.ts +8 -0
  165. package/src/generators/templates/website-components.ts +330 -0
  166. package/src/generators/templates/website-config.ts +444 -0
  167. package/src/generators/templates/website-conversion.ts +341 -0
  168. package/src/generators/templates/website-landing.ts +331 -0
  169. package/src/generators/templates/website-layout.ts +443 -0
  170. package/src/generators/templates/website-pricing.ts +330 -0
  171. package/src/generators/templates/website-sections.ts +541 -0
  172. package/src/generators/templates/website-seo.ts +370 -0
  173. package/src/generators/templates/website.ts +38 -905
  174. package/src/generators/website-content-scanner.ts +208 -0
  175. package/src/generators/website-context.ts +493 -0
  176. package/src/generators/website-debug.ts +130 -0
  177. package/src/generators/website.ts +178 -20
  178. package/src/generators/workspace-root.ts +113 -0
  179. package/src/state/index.ts +56 -0
  180. package/src/types/consensus.ts +3 -0
  181. package/src/types/website-strategy.ts +243 -0
  182. package/src/types/workflow.ts +21 -0
  183. package/src/upgrade/handlers.ts +65 -0
  184. package/src/workflow/auto-fix-bundler.ts +392 -0
  185. package/src/workflow/auto-fix.ts +11 -3
  186. package/src/workflow/consensus.ts +2 -0
  187. package/src/workflow/execution-mode.ts +21 -0
  188. package/src/workflow/index.ts +37 -0
  189. package/src/workflow/overview.ts +475 -0
  190. package/src/workflow/plan-mode.ts +193 -8
  191. package/src/workflow/website-strategy.ts +379 -0
  192. package/src/workflow/website-updater.ts +142 -0
  193. package/src/workflow/workflow-logger.ts +1 -0
  194. package/tests/adapters/persona-switching.test.ts +63 -0
  195. package/tests/cli/project-naming.test.ts +136 -0
  196. package/tests/generators/doc-parser.test.ts +121 -0
  197. package/tests/generators/frontend-design-analyzer.test.ts +90 -0
  198. package/tests/generators/quality-gate.test.ts +183 -0
  199. package/tests/generators/shared-packages.test.ts +83 -0
  200. package/tests/generators/website-components.test.ts +159 -0
  201. package/tests/generators/website-config.test.ts +84 -0
  202. package/tests/generators/website-content-scanner.test.ts +181 -0
  203. package/tests/generators/website-context.test.ts +331 -0
  204. package/tests/generators/website-debug.test.ts +77 -0
  205. package/tests/generators/website-landing.test.ts +188 -0
  206. package/tests/generators/website-pricing.test.ts +98 -0
  207. package/tests/generators/website-sections.test.ts +245 -0
  208. package/tests/generators/website-seo-quality.test.ts +246 -0
  209. package/tests/generators/workspace-root.test.ts +105 -0
  210. package/tests/upgrade/handlers.test.ts +162 -0
  211. package/tests/workflow/auto-fix-bundler.test.ts +242 -0
  212. package/tests/workflow/overview.test.ts +392 -0
  213. package/tests/workflow/plan-mode.test.ts +111 -1
  214. package/tests/workflow/website-strategy.test.ts +246 -0
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Website shared component tests
3
+ * Validates Header, Footer, and Navigation generation with strategy data
4
+ */
5
+
6
+ import { describe, it, expect } from 'vitest';
7
+ import {
8
+ generateWebsiteHeader,
9
+ generateWebsiteFooter,
10
+ generateWebsiteNavigation,
11
+ } from '../../src/generators/templates/website-components.js';
12
+ import type { WebsiteContentContext } from '../../src/generators/website-context.js';
13
+ import type { WebsiteStrategyDocument } from '../../src/types/website-strategy.js';
14
+
15
+ const mockStrategy: WebsiteStrategyDocument = {
16
+ icp: { primaryPersona: 'Developers', painPoints: [], goals: [], objections: [] },
17
+ positioning: { category: 'DevTools', differentiators: [], valueProposition: 'Fast deploys', proofPoints: [] },
18
+ messaging: { headline: 'Ship Fast', subheadline: 'AI CI/CD', elevatorPitch: 'Deploy faster.', longDescription: 'AI CI/CD platform.' },
19
+ seoStrategy: { primaryKeywords: [], secondaryKeywords: [], longTailKeywords: [], titleTemplates: {}, metaDescriptions: {} },
20
+ siteArchitecture: {
21
+ pages: [
22
+ { path: '/', title: 'Home', purpose: 'conversion', pageType: 'landing', sections: [], seoKeywords: [], conversionGoal: 'trial' },
23
+ ],
24
+ navigation: [
25
+ { label: 'Features', href: '/#features' },
26
+ { label: 'Pricing', href: '/pricing' },
27
+ { label: 'API Docs', href: '/docs' },
28
+ ],
29
+ footerSections: [
30
+ { title: 'Product', links: [{ label: 'Features', href: '/#features' }, { label: 'Pricing', href: '/pricing' }] },
31
+ { title: 'Company', links: [{ label: 'About', href: '/about' }] },
32
+ { title: 'Legal', links: [{ label: 'Privacy', href: '/privacy' }] },
33
+ ],
34
+ },
35
+ conversionStrategy: {
36
+ primaryCta: { text: 'Get Started Free', href: '/signup' },
37
+ secondaryCta: { text: 'View Docs', href: '/docs' },
38
+ trustSignals: [], socialProof: [], leadCapture: 'webhook',
39
+ },
40
+ competitiveContext: { category: 'DevTools', competitors: [], differentiators: [] },
41
+ };
42
+
43
+ const contextWithLogo: WebsiteContentContext = {
44
+ productName: 'DeployAI',
45
+ features: [],
46
+ rawDocs: '',
47
+ brandAssets: {
48
+ logoPath: '/path/to/logo.png',
49
+ logoOutputPath: 'public/brand/logo.png',
50
+ },
51
+ };
52
+
53
+ const contextNoLogo: WebsiteContentContext = {
54
+ productName: 'DeployAI',
55
+ features: [],
56
+ rawDocs: '',
57
+ };
58
+
59
+ describe('generateWebsiteHeader', () => {
60
+ it('renders logo image when brandAssets.logoOutputPath provided', () => {
61
+ const header = generateWebsiteHeader('deploy-ai', contextWithLogo, mockStrategy);
62
+ expect(header).toContain('Image');
63
+ expect(header).toContain('logo.png');
64
+ expect(header).toContain('alt="DeployAI"');
65
+ });
66
+
67
+ it('renders text fallback when no logo', () => {
68
+ const header = generateWebsiteHeader('deploy-ai', contextNoLogo, mockStrategy);
69
+ expect(header).toContain('DeployAI');
70
+ expect(header).toContain('font-bold text-foreground');
71
+ });
72
+
73
+ it('includes navigation links from strategy', () => {
74
+ const header = generateWebsiteHeader('deploy-ai', contextNoLogo, mockStrategy);
75
+ expect(header).toContain('Features');
76
+ expect(header).toContain('/#features');
77
+ expect(header).toContain('Pricing');
78
+ expect(header).toContain('/pricing');
79
+ expect(header).toContain('API Docs');
80
+ });
81
+
82
+ it('includes primary CTA from strategy', () => {
83
+ const header = generateWebsiteHeader('deploy-ai', contextNoLogo, mockStrategy);
84
+ expect(header).toContain('Get Started Free');
85
+ expect(header).toContain('/signup');
86
+ });
87
+
88
+ it('includes mobile hamburger menu', () => {
89
+ const header = generateWebsiteHeader('deploy-ai', contextNoLogo, mockStrategy);
90
+ expect(header).toContain('mobileMenuOpen');
91
+ expect(header).toContain('md:hidden');
92
+ });
93
+
94
+ it('uses default nav items when no strategy', () => {
95
+ const header = generateWebsiteHeader('deploy-ai', contextNoLogo);
96
+ expect(header).toContain('Features');
97
+ expect(header).toContain('Pricing');
98
+ expect(header).toContain('Docs');
99
+ expect(header).toContain('Blog');
100
+ });
101
+ });
102
+
103
+ describe('generateWebsiteFooter', () => {
104
+ it('includes multi-column sections from strategy', () => {
105
+ const footer = generateWebsiteFooter('deploy-ai', contextNoLogo, mockStrategy);
106
+ expect(footer).toContain('Product');
107
+ expect(footer).toContain('Company');
108
+ expect(footer).toContain('Legal');
109
+ expect(footer).toContain('Privacy');
110
+ expect(footer).toContain('/about');
111
+ });
112
+
113
+ it('includes brand name and tagline', () => {
114
+ const ctxWithTagline: WebsiteContentContext = {
115
+ ...contextNoLogo,
116
+ tagline: 'Ship Code 10x Faster',
117
+ };
118
+ const footer = generateWebsiteFooter('deploy-ai', ctxWithTagline, mockStrategy);
119
+ expect(footer).toContain('DeployAI');
120
+ expect(footer).toContain('Ship Code 10x Faster');
121
+ });
122
+
123
+ it('includes copyright notice', () => {
124
+ const footer = generateWebsiteFooter('deploy-ai', contextNoLogo);
125
+ expect(footer).toContain('All rights reserved');
126
+ expect(footer).toContain('DeployAI');
127
+ });
128
+
129
+ it('uses default sections when no strategy', () => {
130
+ const footer = generateWebsiteFooter('deploy-ai', contextNoLogo);
131
+ expect(footer).toContain('Product');
132
+ expect(footer).toContain('Resources');
133
+ expect(footer).toContain('Legal');
134
+ });
135
+ });
136
+
137
+ describe('generateWebsiteNavigation', () => {
138
+ it('generates nav config from strategy', () => {
139
+ const nav = generateWebsiteNavigation(mockStrategy);
140
+ expect(nav).toContain('NAV_ITEMS');
141
+ expect(nav).toContain('Features');
142
+ expect(nav).toContain('/#features');
143
+ expect(nav).toContain('Pricing');
144
+ });
145
+
146
+ it('uses default nav items when no strategy', () => {
147
+ const nav = generateWebsiteNavigation();
148
+ expect(nav).toContain('Features');
149
+ expect(nav).toContain('Pricing');
150
+ expect(nav).toContain('Docs');
151
+ expect(nav).toContain('Blog');
152
+ });
153
+
154
+ it('exports NavItem interface', () => {
155
+ const nav = generateWebsiteNavigation();
156
+ expect(nav).toContain('export interface NavItem');
157
+ expect(nav).toContain('children?: NavItem[]');
158
+ });
159
+ });
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Tests for website-config tailwind generation with brand colors
3
+ * Extended to verify full color token set, animations, and CSS vars
4
+ */
5
+
6
+ import { describe, it, expect } from 'vitest';
7
+ import { generateWebsiteTailwindConfig } from '../../src/generators/templates/website-config.js';
8
+
9
+ describe('generateWebsiteTailwindConfig', () => {
10
+ it('generates config with brand primary color', () => {
11
+ const config = generateWebsiteTailwindConfig({
12
+ primaryColor: '#2563EB',
13
+ });
14
+
15
+ // Should contain generated color scale, NOT default sky-blue
16
+ expect(config).not.toContain('#0ea5e9');
17
+ expect(config).not.toContain('#0284c7');
18
+ // Should be valid TypeScript with Config type
19
+ expect(config).toContain("import type { Config } from 'tailwindcss'");
20
+ expect(config).toContain('primary:');
21
+ });
22
+
23
+ it('includes workspace design-tokens preset import', () => {
24
+ const config = generateWebsiteTailwindConfig({
25
+ workspaceMode: true,
26
+ projectName: 'gateco',
27
+ });
28
+
29
+ expect(config).toContain("import designPreset from '@gateco/design-tokens/tailwind'");
30
+ expect(config).toContain('presets: [designPreset]');
31
+ });
32
+
33
+ it('uses default sky-blue when no options provided', () => {
34
+ const config = generateWebsiteTailwindConfig();
35
+
36
+ // Should contain default sky-blue palette
37
+ expect(config).toContain('#0ea5e9');
38
+ expect(config).toContain('#0284c7');
39
+ // Should NOT have preset import
40
+ expect(config).not.toContain('designPreset');
41
+ });
42
+
43
+ it('includes full shadcn-compatible color token set', () => {
44
+ const config = generateWebsiteTailwindConfig();
45
+
46
+ // Background/foreground
47
+ expect(config).toContain("background: 'hsl(var(--background))'");
48
+ expect(config).toContain("foreground: 'hsl(var(--foreground))'");
49
+
50
+ // Muted with sub-tokens
51
+ expect(config).toContain("muted:");
52
+ expect(config).toContain("hsl(var(--muted))");
53
+ expect(config).toContain("hsl(var(--muted-foreground))");
54
+
55
+ // Accent
56
+ expect(config).toContain("accent:");
57
+ expect(config).toContain("hsl(var(--accent))");
58
+
59
+ // Card
60
+ expect(config).toContain("card:");
61
+ expect(config).toContain("hsl(var(--card))");
62
+
63
+ // Border and ring
64
+ expect(config).toContain("border: 'hsl(var(--border))'");
65
+ expect(config).toContain("ring: 'hsl(var(--ring))'");
66
+ });
67
+
68
+ it('includes animation utilities', () => {
69
+ const config = generateWebsiteTailwindConfig();
70
+
71
+ expect(config).toContain('keyframes:');
72
+ expect(config).toContain('fadeIn:');
73
+ expect(config).toContain('slideUp:');
74
+ expect(config).toContain('animation:');
75
+ });
76
+
77
+ it('includes borderColor and borderRadius extensions', () => {
78
+ const config = generateWebsiteTailwindConfig();
79
+
80
+ expect(config).toContain('borderColor:');
81
+ expect(config).toContain('borderRadius:');
82
+ expect(config).toContain('var(--radius)');
83
+ });
84
+ });
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Tests for post-generation website content scanner
3
+ * Verifies detection of placeholder fingerprints in generated files
4
+ */
5
+
6
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
7
+ import { promises as fs } from 'node:fs';
8
+ import path from 'node:path';
9
+ import os from 'node:os';
10
+ import { scanGeneratedContent } from '../../src/generators/website-content-scanner.js';
11
+
12
+ let tmpDir: string;
13
+
14
+ beforeEach(async () => {
15
+ tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'scanner-test-'));
16
+ await fs.mkdir(path.join(tmpDir, 'src', 'app'), { recursive: true });
17
+ await fs.mkdir(path.join(tmpDir, 'src', 'components'), { recursive: true });
18
+ });
19
+
20
+ afterEach(async () => {
21
+ await fs.rm(tmpDir, { recursive: true, force: true });
22
+ });
23
+
24
+ describe('scanGeneratedContent', () => {
25
+ it('produces no issues for clean files', async () => {
26
+ await fs.writeFile(
27
+ path.join(tmpDir, 'src', 'app', 'page.tsx'),
28
+ `export default function Home() {
29
+ return <main><h1>Welcome to Gateco</h1></main>;
30
+ }
31
+ `,
32
+ );
33
+
34
+ const result = await scanGeneratedContent(tmpDir);
35
+
36
+ expect(result.issues).toHaveLength(0);
37
+ expect(result.filesScanned).toBe(1);
38
+ expect(result.score).toBe(100);
39
+ });
40
+
41
+ it('flags TODO block comments as errors', async () => {
42
+ await fs.writeFile(
43
+ path.join(tmpDir, 'src', 'app', 'page.tsx'),
44
+ `export default function Home() {
45
+ return <main>/* TODO: Replace with real content */</main>;
46
+ }
47
+ `,
48
+ );
49
+
50
+ const result = await scanGeneratedContent(tmpDir);
51
+
52
+ expect(result.issues.length).toBeGreaterThan(0);
53
+ const todoIssue = result.issues.find((i) => /TODO/i.test(i.message));
54
+ expect(todoIssue).toBeDefined();
55
+ expect(todoIssue!.severity).toBe('error');
56
+ });
57
+
58
+ it('flags TODO line comments as errors', async () => {
59
+ await fs.writeFile(
60
+ path.join(tmpDir, 'src', 'components', 'Header.tsx'),
61
+ `// TODO: add real navigation
62
+ export function Header() { return <header />; }
63
+ `,
64
+ );
65
+
66
+ const result = await scanGeneratedContent(tmpDir);
67
+
68
+ expect(result.issues.some((i) => /TODO/i.test(i.message))).toBe(true);
69
+ });
70
+
71
+ it('detects default pricing pattern in file content', async () => {
72
+ await fs.writeFile(
73
+ path.join(tmpDir, 'src', 'app', 'page.tsx'),
74
+ `const tiers = [
75
+ { name: 'Starter', price: '$0/mo', features: ['1 user'] },
76
+ { name: 'Pro', price: '$29/mo', features: ['10 users'] },
77
+ { name: 'Enterprise', price: 'Custom', features: ['Unlimited'] },
78
+ ];
79
+ export default function Page() { return <div />; }
80
+ `,
81
+ );
82
+
83
+ const result = await scanGeneratedContent(tmpDir);
84
+
85
+ expect(result.issues.some((i) => /pricing/i.test(i.message))).toBe(true);
86
+ expect(result.issues.some((i) => /\$29/i.test(i.message))).toBe(true);
87
+ });
88
+
89
+ it('detects default tagline text', async () => {
90
+ await fs.writeFile(
91
+ path.join(tmpDir, 'src', 'components', 'Footer.tsx'),
92
+ `export function Footer() { return <p>Build something amazing</p>; }
93
+ `,
94
+ );
95
+
96
+ const result = await scanGeneratedContent(tmpDir);
97
+
98
+ expect(result.issues.some((i) => /tagline/i.test(i.message))).toBe(true);
99
+ });
100
+
101
+ it('detects generic description text', async () => {
102
+ await fs.writeFile(
103
+ path.join(tmpDir, 'src', 'app', 'page.tsx'),
104
+ `export default function Page() { return <p>Your modern web application</p>; }
105
+ `,
106
+ );
107
+
108
+ const result = await scanGeneratedContent(tmpDir);
109
+
110
+ expect(result.issues.some((i) => /generic description/i.test(i.message))).toBe(true);
111
+ });
112
+
113
+ it('detects default How It Works steps', async () => {
114
+ await fs.writeFile(
115
+ path.join(tmpDir, 'src', 'app', 'page.tsx'),
116
+ `const steps = [
117
+ { title: 'Sign Up', desc: 'Create account' },
118
+ { title: 'Configure', desc: 'Set preferences' },
119
+ { title: 'Deploy', desc: 'Go live' },
120
+ ];
121
+ export default function Page() { return <div />; }
122
+ `,
123
+ );
124
+
125
+ const result = await scanGeneratedContent(tmpDir);
126
+
127
+ expect(result.issues.some((i) => /How It Works/i.test(i.message))).toBe(true);
128
+ });
129
+
130
+ it('returns score of 100 when no issues found', async () => {
131
+ await fs.writeFile(
132
+ path.join(tmpDir, 'src', 'app', 'page.tsx'),
133
+ `export default function Page() { return <h1>Gateco</h1>; }
134
+ `,
135
+ );
136
+
137
+ const result = await scanGeneratedContent(tmpDir);
138
+
139
+ expect(result.score).toBe(100);
140
+ });
141
+
142
+ it('decreases score with multiple issues', async () => {
143
+ await fs.writeFile(
144
+ path.join(tmpDir, 'src', 'app', 'page.tsx'),
145
+ `// TODO: fix this
146
+ export default function Page() {
147
+ return <div>
148
+ <p>Build something amazing</p>
149
+ <p>Your modern web application costs $29/mo</p>
150
+ </div>;
151
+ }
152
+ `,
153
+ );
154
+
155
+ const result = await scanGeneratedContent(tmpDir);
156
+
157
+ expect(result.score).toBeLessThan(80);
158
+ expect(result.issues.length).toBeGreaterThanOrEqual(3);
159
+ });
160
+
161
+ it('handles empty src directory gracefully', async () => {
162
+ const result = await scanGeneratedContent(tmpDir);
163
+
164
+ expect(result.issues).toHaveLength(0);
165
+ expect(result.filesScanned).toBe(0);
166
+ expect(result.score).toBe(100);
167
+ });
168
+
169
+ it('skips node_modules directory', async () => {
170
+ await fs.mkdir(path.join(tmpDir, 'src', 'node_modules'), { recursive: true });
171
+ await fs.writeFile(
172
+ path.join(tmpDir, 'src', 'node_modules', 'bad.tsx'),
173
+ '// TODO: should be ignored',
174
+ );
175
+
176
+ const result = await scanGeneratedContent(tmpDir);
177
+
178
+ expect(result.filesScanned).toBe(0);
179
+ expect(result.issues).toHaveLength(0);
180
+ });
181
+ });