popeye-cli 1.6.0 → 1.8.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 (161) hide show
  1. package/README.md +240 -32
  2. package/cheatsheet.md +407 -0
  3. package/dist/cli/commands/db.d.ts +10 -0
  4. package/dist/cli/commands/db.d.ts.map +1 -0
  5. package/dist/cli/commands/db.js +240 -0
  6. package/dist/cli/commands/db.js.map +1 -0
  7. package/dist/cli/commands/doctor.d.ts +18 -0
  8. package/dist/cli/commands/doctor.d.ts.map +1 -0
  9. package/dist/cli/commands/doctor.js +255 -0
  10. package/dist/cli/commands/doctor.js.map +1 -0
  11. package/dist/cli/commands/index.d.ts +2 -0
  12. package/dist/cli/commands/index.d.ts.map +1 -1
  13. package/dist/cli/commands/index.js +2 -0
  14. package/dist/cli/commands/index.js.map +1 -1
  15. package/dist/cli/index.d.ts.map +1 -1
  16. package/dist/cli/index.js +3 -1
  17. package/dist/cli/index.js.map +1 -1
  18. package/dist/cli/interactive.d.ts.map +1 -1
  19. package/dist/cli/interactive.js +96 -0
  20. package/dist/cli/interactive.js.map +1 -1
  21. package/dist/generators/admin-wizard.d.ts +25 -0
  22. package/dist/generators/admin-wizard.d.ts.map +1 -0
  23. package/dist/generators/admin-wizard.js +123 -0
  24. package/dist/generators/admin-wizard.js.map +1 -0
  25. package/dist/generators/all.d.ts.map +1 -1
  26. package/dist/generators/all.js +10 -3
  27. package/dist/generators/all.js.map +1 -1
  28. package/dist/generators/database.d.ts +58 -0
  29. package/dist/generators/database.d.ts.map +1 -0
  30. package/dist/generators/database.js +229 -0
  31. package/dist/generators/database.js.map +1 -0
  32. package/dist/generators/fullstack.d.ts.map +1 -1
  33. package/dist/generators/fullstack.js +23 -7
  34. package/dist/generators/fullstack.js.map +1 -1
  35. package/dist/generators/index.d.ts +2 -0
  36. package/dist/generators/index.d.ts.map +1 -1
  37. package/dist/generators/index.js +2 -0
  38. package/dist/generators/index.js.map +1 -1
  39. package/dist/generators/templates/admin-wizard-python.d.ts +32 -0
  40. package/dist/generators/templates/admin-wizard-python.d.ts.map +1 -0
  41. package/dist/generators/templates/admin-wizard-python.js +425 -0
  42. package/dist/generators/templates/admin-wizard-python.js.map +1 -0
  43. package/dist/generators/templates/admin-wizard-react.d.ts +48 -0
  44. package/dist/generators/templates/admin-wizard-react.d.ts.map +1 -0
  45. package/dist/generators/templates/admin-wizard-react.js +554 -0
  46. package/dist/generators/templates/admin-wizard-react.js.map +1 -0
  47. package/dist/generators/templates/database-docker.d.ts +23 -0
  48. package/dist/generators/templates/database-docker.d.ts.map +1 -0
  49. package/dist/generators/templates/database-docker.js +221 -0
  50. package/dist/generators/templates/database-docker.js.map +1 -0
  51. package/dist/generators/templates/database-python.d.ts +54 -0
  52. package/dist/generators/templates/database-python.d.ts.map +1 -0
  53. package/dist/generators/templates/database-python.js +723 -0
  54. package/dist/generators/templates/database-python.js.map +1 -0
  55. package/dist/generators/templates/database-typescript.d.ts +34 -0
  56. package/dist/generators/templates/database-typescript.d.ts.map +1 -0
  57. package/dist/generators/templates/database-typescript.js +232 -0
  58. package/dist/generators/templates/database-typescript.js.map +1 -0
  59. package/dist/generators/templates/fullstack.d.ts.map +1 -1
  60. package/dist/generators/templates/fullstack.js +29 -0
  61. package/dist/generators/templates/fullstack.js.map +1 -1
  62. package/dist/generators/templates/index.d.ts +5 -0
  63. package/dist/generators/templates/index.d.ts.map +1 -1
  64. package/dist/generators/templates/index.js +5 -0
  65. package/dist/generators/templates/index.js.map +1 -1
  66. package/dist/state/index.d.ts +10 -0
  67. package/dist/state/index.d.ts.map +1 -1
  68. package/dist/state/index.js +22 -0
  69. package/dist/state/index.js.map +1 -1
  70. package/dist/types/consensus.d.ts +3 -0
  71. package/dist/types/consensus.d.ts.map +1 -1
  72. package/dist/types/consensus.js +1 -0
  73. package/dist/types/consensus.js.map +1 -1
  74. package/dist/types/database-runtime.d.ts +86 -0
  75. package/dist/types/database-runtime.d.ts.map +1 -0
  76. package/dist/types/database-runtime.js +61 -0
  77. package/dist/types/database-runtime.js.map +1 -0
  78. package/dist/types/database.d.ts +85 -0
  79. package/dist/types/database.d.ts.map +1 -0
  80. package/dist/types/database.js +71 -0
  81. package/dist/types/database.js.map +1 -0
  82. package/dist/types/index.d.ts +3 -0
  83. package/dist/types/index.d.ts.map +1 -1
  84. package/dist/types/index.js +6 -0
  85. package/dist/types/index.js.map +1 -1
  86. package/dist/types/tester.d.ts +138 -0
  87. package/dist/types/tester.d.ts.map +1 -0
  88. package/dist/types/tester.js +110 -0
  89. package/dist/types/tester.js.map +1 -0
  90. package/dist/types/workflow.d.ts +166 -0
  91. package/dist/types/workflow.d.ts.map +1 -1
  92. package/dist/types/workflow.js +14 -0
  93. package/dist/types/workflow.js.map +1 -1
  94. package/dist/workflow/db-setup-runner.d.ts +63 -0
  95. package/dist/workflow/db-setup-runner.d.ts.map +1 -0
  96. package/dist/workflow/db-setup-runner.js +336 -0
  97. package/dist/workflow/db-setup-runner.js.map +1 -0
  98. package/dist/workflow/db-state-machine.d.ts +30 -0
  99. package/dist/workflow/db-state-machine.d.ts.map +1 -0
  100. package/dist/workflow/db-state-machine.js +51 -0
  101. package/dist/workflow/db-state-machine.js.map +1 -0
  102. package/dist/workflow/execution-mode.js +2 -2
  103. package/dist/workflow/execution-mode.js.map +1 -1
  104. package/dist/workflow/index.d.ts +3 -0
  105. package/dist/workflow/index.d.ts.map +1 -1
  106. package/dist/workflow/index.js +3 -0
  107. package/dist/workflow/index.js.map +1 -1
  108. package/dist/workflow/task-workflow.d.ts +5 -0
  109. package/dist/workflow/task-workflow.d.ts.map +1 -1
  110. package/dist/workflow/task-workflow.js +172 -6
  111. package/dist/workflow/task-workflow.js.map +1 -1
  112. package/dist/workflow/tester.d.ts +120 -0
  113. package/dist/workflow/tester.d.ts.map +1 -0
  114. package/dist/workflow/tester.js +589 -0
  115. package/dist/workflow/tester.js.map +1 -0
  116. package/dist/workflow/workflow-logger.d.ts +1 -1
  117. package/dist/workflow/workflow-logger.d.ts.map +1 -1
  118. package/dist/workflow/workflow-logger.js.map +1 -1
  119. package/package.json +1 -1
  120. package/src/cli/commands/db.ts +281 -0
  121. package/src/cli/commands/doctor.ts +273 -0
  122. package/src/cli/commands/index.ts +2 -0
  123. package/src/cli/index.ts +4 -0
  124. package/src/cli/interactive.ts +102 -0
  125. package/src/generators/admin-wizard.ts +146 -0
  126. package/src/generators/all.ts +10 -3
  127. package/src/generators/database.ts +286 -0
  128. package/src/generators/fullstack.ts +26 -9
  129. package/src/generators/index.ts +12 -0
  130. package/src/generators/templates/admin-wizard-python.ts +431 -0
  131. package/src/generators/templates/admin-wizard-react.ts +560 -0
  132. package/src/generators/templates/database-docker.ts +227 -0
  133. package/src/generators/templates/database-python.ts +734 -0
  134. package/src/generators/templates/database-typescript.ts +238 -0
  135. package/src/generators/templates/fullstack.ts +29 -0
  136. package/src/generators/templates/index.ts +5 -0
  137. package/src/state/index.ts +29 -0
  138. package/src/types/consensus.ts +3 -0
  139. package/src/types/database-runtime.ts +69 -0
  140. package/src/types/database.ts +84 -0
  141. package/src/types/index.ts +50 -0
  142. package/src/types/tester.ts +136 -0
  143. package/src/types/workflow.ts +31 -0
  144. package/src/workflow/db-setup-runner.ts +391 -0
  145. package/src/workflow/db-state-machine.ts +58 -0
  146. package/src/workflow/execution-mode.ts +2 -2
  147. package/src/workflow/index.ts +3 -0
  148. package/src/workflow/task-workflow.ts +227 -5
  149. package/src/workflow/tester.ts +723 -0
  150. package/src/workflow/workflow-logger.ts +2 -0
  151. package/tests/generators/admin-wizard-orchestrator.test.ts +64 -0
  152. package/tests/generators/admin-wizard-templates.test.ts +366 -0
  153. package/tests/generators/cross-phase-integration.test.ts +383 -0
  154. package/tests/generators/database.test.ts +456 -0
  155. package/tests/generators/fe-be-db-integration.test.ts +613 -0
  156. package/tests/types/database-runtime.test.ts +158 -0
  157. package/tests/types/database.test.ts +187 -0
  158. package/tests/types/tester.test.ts +174 -0
  159. package/tests/workflow/db-setup-runner.test.ts +211 -0
  160. package/tests/workflow/db-state-machine.test.ts +117 -0
  161. package/tests/workflow/tester.test.ts +401 -0
@@ -40,6 +40,8 @@ export type WorkflowStage =
40
40
  | 'ui-design'
41
41
  | 'ui-setup'
42
42
  | 'website-strategy'
43
+ | 'test-planning'
44
+ | 'test-review'
43
45
  | 'completion';
44
46
 
45
47
  /**
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Tests for admin wizard orchestrator (file list and dependencies)
3
+ */
4
+
5
+ import { describe, it, expect } from 'vitest';
6
+
7
+ import {
8
+ getAdminWizardFiles,
9
+ ADMIN_WIZARD_PYTHON_DEPS,
10
+ } from '../../src/generators/admin-wizard.js';
11
+
12
+ const TEST_PACKAGE = 'my_project';
13
+
14
+ describe('getAdminWizardFiles', () => {
15
+ const files = getAdminWizardFiles(TEST_PACKAGE);
16
+
17
+ it('should return backend middleware files', () => {
18
+ expect(files).toContain(`apps/backend/src/${TEST_PACKAGE}/middleware/__init__.py`);
19
+ expect(files).toContain(`apps/backend/src/${TEST_PACKAGE}/middleware/admin_auth.py`);
20
+ });
21
+
22
+ it('should return backend admin route file', () => {
23
+ expect(files).toContain(`apps/backend/src/${TEST_PACKAGE}/routes/admin_db.py`);
24
+ });
25
+
26
+ it('should return frontend admin component files', () => {
27
+ expect(files).toContain('apps/frontend/src/admin/useAdminApi.ts');
28
+ expect(files).toContain('apps/frontend/src/admin/DbStatusBanner.tsx');
29
+ expect(files).toContain('apps/frontend/src/admin/ConnectionForm.tsx');
30
+ expect(files).toContain('apps/frontend/src/admin/MigrationProgress.tsx');
31
+ expect(files).toContain('apps/frontend/src/admin/DbSetupStepper.tsx');
32
+ expect(files).toContain('apps/frontend/src/admin/index.ts');
33
+ });
34
+
35
+ it('should have correct number of files', () => {
36
+ expect(files).toHaveLength(9);
37
+ });
38
+
39
+ it('should use correct package name prefix for backend files', () => {
40
+ const customFiles = getAdminWizardFiles('acme_app');
41
+ const backendFiles = customFiles.filter((f) => f.startsWith('apps/backend/'));
42
+ for (const f of backendFiles) {
43
+ expect(f).toContain('acme_app');
44
+ }
45
+ });
46
+
47
+ it('should use apps/frontend prefix for frontend files', () => {
48
+ const frontendFiles = files.filter((f) => f.startsWith('apps/frontend/'));
49
+ expect(frontendFiles).toHaveLength(6);
50
+ for (const f of frontendFiles) {
51
+ expect(f).toContain('src/admin/');
52
+ }
53
+ });
54
+ });
55
+
56
+ describe('ADMIN_WIZARD_PYTHON_DEPS', () => {
57
+ it('should include python-multipart', () => {
58
+ expect(ADMIN_WIZARD_PYTHON_DEPS).toContain('python-multipart>=0.0.7');
59
+ });
60
+
61
+ it('should be a non-empty array', () => {
62
+ expect(ADMIN_WIZARD_PYTHON_DEPS.length).toBeGreaterThan(0);
63
+ });
64
+ });
@@ -0,0 +1,366 @@
1
+ /**
2
+ * Tests for admin wizard template functions (backend + frontend)
3
+ */
4
+
5
+ import { describe, it, expect } from 'vitest';
6
+
7
+ // Python backend template imports
8
+ import {
9
+ generateAdminAuthMiddleware,
10
+ generateMiddlewareInit,
11
+ generateAdminDbRoutes,
12
+ generateFastAPIMainWithAdmin,
13
+ } from '../../src/generators/templates/admin-wizard-python.js';
14
+
15
+ // React frontend template imports
16
+ import {
17
+ generateUseAdminApiHook,
18
+ generateDbStatusBanner,
19
+ generateConnectionForm,
20
+ generateMigrationProgress,
21
+ generateDbSetupStepper,
22
+ generateAdminIndex,
23
+ generateAppTsxWithAdmin,
24
+ } from '../../src/generators/templates/admin-wizard-react.js';
25
+
26
+ const TEST_PACKAGE = 'my_project';
27
+ const TEST_PROJECT = 'my-project';
28
+
29
+ // ============================================================
30
+ // Python backend template tests
31
+ // ============================================================
32
+
33
+ describe('generateAdminAuthMiddleware', () => {
34
+ it('should include X-Admin-Token header validation', () => {
35
+ const result = generateAdminAuthMiddleware();
36
+ expect(result).toContain('X-Admin-Token');
37
+ expect(result).toContain('ADMIN_SETUP_TOKEN');
38
+ });
39
+
40
+ it('should return 403 on invalid token', () => {
41
+ const result = generateAdminAuthMiddleware();
42
+ expect(result).toContain('403');
43
+ expect(result).toContain('HTTPException');
44
+ });
45
+
46
+ it('should raise when token is not configured', () => {
47
+ const result = generateAdminAuthMiddleware();
48
+ expect(result).toContain('not configured');
49
+ });
50
+
51
+ it('should include proper docstring', () => {
52
+ const result = generateAdminAuthMiddleware();
53
+ expect(result).toContain('require_admin_token');
54
+ expect(result).toContain('FastAPI dependency');
55
+ });
56
+ });
57
+
58
+ describe('generateMiddlewareInit', () => {
59
+ it('should re-export require_admin_token', () => {
60
+ const result = generateMiddlewareInit();
61
+ expect(result).toContain('from .admin_auth import require_admin_token');
62
+ expect(result).toContain('__all__');
63
+ });
64
+ });
65
+
66
+ describe('generateAdminDbRoutes', () => {
67
+ const result = generateAdminDbRoutes(TEST_PACKAGE);
68
+
69
+ it('should use APIRouter with correct prefix', () => {
70
+ expect(result).toContain('APIRouter');
71
+ expect(result).toContain('prefix="/api/admin/db"');
72
+ expect(result).toContain('tags=["admin"]');
73
+ });
74
+
75
+ it('should use Depends(require_admin_token) for auth', () => {
76
+ expect(result).toContain('Depends(require_admin_token)');
77
+ expect(result).toContain(`from ${TEST_PACKAGE}.middleware.admin_auth import require_admin_token`);
78
+ });
79
+
80
+ it('should include GET /status endpoint', () => {
81
+ expect(result).toContain('@router.get("/status")');
82
+ expect(result).toContain('async def db_status');
83
+ expect(result).toContain('migrationsApplied');
84
+ expect(result).toContain('dbUrlConfigured');
85
+ });
86
+
87
+ it('should include POST /test endpoint with asyncpg', () => {
88
+ expect(result).toContain('@router.post("/test")');
89
+ expect(result).toContain('async def test_connection');
90
+ expect(result).toContain('asyncpg.connect');
91
+ expect(result).toContain('SELECT 1');
92
+ });
93
+
94
+ it('should include POST /apply endpoint with alembic', () => {
95
+ expect(result).toContain('@router.post("/apply")');
96
+ expect(result).toContain('async def apply_setup');
97
+ expect(result).toContain('alembic upgrade head');
98
+ expect(result).toContain('DATABASE_URL');
99
+ });
100
+
101
+ it('should include POST /retry endpoint', () => {
102
+ expect(result).toContain('@router.post("/retry")');
103
+ expect(result).toContain('async def retry_setup');
104
+ });
105
+
106
+ it('should use correct package name in imports', () => {
107
+ const custom = generateAdminDbRoutes('acme_app');
108
+ expect(custom).toContain('from acme_app.middleware.admin_auth import require_admin_token');
109
+ });
110
+ });
111
+
112
+ describe('generateFastAPIMainWithAdmin', () => {
113
+ const result = generateFastAPIMainWithAdmin(TEST_PROJECT, TEST_PACKAGE);
114
+
115
+ it('should include admin_db_router', () => {
116
+ expect(result).toContain('admin_db_router');
117
+ expect(result).toContain('app.include_router(admin_db_router)');
118
+ });
119
+
120
+ it('should include health_db_router', () => {
121
+ expect(result).toContain('health_db_router');
122
+ expect(result).toContain('app.include_router(health_db_router)');
123
+ });
124
+
125
+ it('should preserve /health endpoint', () => {
126
+ expect(result).toContain('@app.get("/health")');
127
+ expect(result).toContain('async def health_check');
128
+ expect(result).toContain('"healthy"');
129
+ });
130
+
131
+ it('should preserve / root endpoint', () => {
132
+ expect(result).toContain('@app.get("/")');
133
+ expect(result).toContain('async def root');
134
+ expect(result).toContain('/docs');
135
+ });
136
+
137
+ it('should include project name in title and message', () => {
138
+ expect(result).toContain(`title="${TEST_PROJECT} API"`);
139
+ expect(result).toContain(`Welcome to ${TEST_PROJECT} API`);
140
+ });
141
+
142
+ it('should import from correct package', () => {
143
+ expect(result).toContain(`from ${TEST_PACKAGE}.routes.admin_db import router as admin_db_router`);
144
+ expect(result).toContain(`from ${TEST_PACKAGE}.routes.health_db import router as health_db_router`);
145
+ });
146
+
147
+ it('should include CORS middleware', () => {
148
+ expect(result).toContain('CORSMiddleware');
149
+ expect(result).toContain('allow_origins');
150
+ });
151
+ });
152
+
153
+ // ============================================================
154
+ // React frontend template tests
155
+ // ============================================================
156
+
157
+ describe('generateUseAdminApiHook', () => {
158
+ const result = generateUseAdminApiHook();
159
+
160
+ it('should set X-Admin-Token header', () => {
161
+ expect(result).toContain("'X-Admin-Token'");
162
+ expect(result).toContain('adminToken');
163
+ });
164
+
165
+ it('should read VITE_API_URL from env', () => {
166
+ expect(result).toContain('VITE_API_URL');
167
+ expect(result).toContain('import.meta.env');
168
+ });
169
+
170
+ it('should read VITE_ADMIN_TOKEN from env', () => {
171
+ expect(result).toContain('VITE_ADMIN_TOKEN');
172
+ });
173
+
174
+ it('should export useAdminApi function', () => {
175
+ expect(result).toContain('export function useAdminApi');
176
+ expect(result).toContain('callApi');
177
+ });
178
+ });
179
+
180
+ describe('generateDbStatusBanner', () => {
181
+ const result = generateDbStatusBanner();
182
+
183
+ it('should poll /api/admin/db/status on mount', () => {
184
+ expect(result).toContain('/api/admin/db/status');
185
+ expect(result).toContain('useEffect');
186
+ });
187
+
188
+ it('should accept onSetupClick prop', () => {
189
+ expect(result).toContain('onSetupClick');
190
+ expect(result).toContain('DbStatusBannerProps');
191
+ });
192
+
193
+ it('should hide when status is ready', () => {
194
+ expect(result).toContain("status === 'ready'");
195
+ expect(result).toContain('return null');
196
+ });
197
+
198
+ it('should use amber Tailwind classes', () => {
199
+ expect(result).toContain('bg-amber-50');
200
+ expect(result).toContain('border-amber-200');
201
+ expect(result).toContain('bg-amber-500');
202
+ });
203
+
204
+ it('should show "Set up database" button', () => {
205
+ expect(result).toContain('Set up database');
206
+ });
207
+ });
208
+
209
+ describe('generateConnectionForm', () => {
210
+ const result = generateConnectionForm();
211
+
212
+ it('should include DATABASE_URL input', () => {
213
+ expect(result).toContain('DATABASE_URL');
214
+ expect(result).toContain('db-url');
215
+ expect(result).toContain('<input');
216
+ });
217
+
218
+ it('should call POST /api/admin/db/test', () => {
219
+ expect(result).toContain('/api/admin/db/test');
220
+ expect(result).toContain("method: 'POST'");
221
+ });
222
+
223
+ it('should accept onTestSuccess callback', () => {
224
+ expect(result).toContain('onTestSuccess');
225
+ expect(result).toContain('ConnectionFormProps');
226
+ });
227
+
228
+ it('should accept onBack callback', () => {
229
+ expect(result).toContain('onBack');
230
+ });
231
+
232
+ it('should show Test Connection button', () => {
233
+ expect(result).toContain('Test Connection');
234
+ });
235
+
236
+ it('should show success/error feedback', () => {
237
+ expect(result).toContain('bg-green-50');
238
+ expect(result).toContain('bg-red-50');
239
+ });
240
+ });
241
+
242
+ describe('generateMigrationProgress', () => {
243
+ const result = generateMigrationProgress();
244
+
245
+ it('should call POST /api/admin/db/apply', () => {
246
+ expect(result).toContain('/api/admin/db/apply');
247
+ });
248
+
249
+ it('should poll status during applying phase', () => {
250
+ expect(result).toContain('/api/admin/db/status');
251
+ expect(result).toContain('setInterval');
252
+ expect(result).toContain('2000');
253
+ });
254
+
255
+ it('should accept databaseUrl prop', () => {
256
+ expect(result).toContain('databaseUrl');
257
+ expect(result).toContain('MigrationProgressProps');
258
+ });
259
+
260
+ it('should accept onComplete and onError callbacks', () => {
261
+ expect(result).toContain('onComplete');
262
+ expect(result).toContain('onError');
263
+ });
264
+
265
+ it('should show step progress items', () => {
266
+ expect(result).toContain('steps.map');
267
+ expect(result).toContain('StepResult');
268
+ });
269
+ });
270
+
271
+ describe('generateDbSetupStepper', () => {
272
+ const result = generateDbSetupStepper();
273
+
274
+ it('should include all wizard steps', () => {
275
+ expect(result).toContain("'choose'");
276
+ expect(result).toContain("'credentials'");
277
+ expect(result).toContain("'apply'");
278
+ expect(result).toContain("'ready'");
279
+ });
280
+
281
+ it('should render as overlay with backdrop', () => {
282
+ expect(result).toContain('fixed inset-0');
283
+ expect(result).toContain('z-50');
284
+ expect(result).toContain('bg-black/50');
285
+ });
286
+
287
+ it('should accept onClose prop', () => {
288
+ expect(result).toContain('onClose');
289
+ expect(result).toContain('DbSetupStepperProps');
290
+ });
291
+
292
+ it('should have a close button', () => {
293
+ expect(result).toContain('aria-label="Close"');
294
+ });
295
+
296
+ it('should show step indicator bar', () => {
297
+ expect(result).toContain('STEP_ORDER');
298
+ expect(result).toContain('STEP_LABELS');
299
+ expect(result).toContain('bg-blue-500');
300
+ });
301
+
302
+ it('should render ConnectionForm and MigrationProgress', () => {
303
+ expect(result).toContain('<ConnectionForm');
304
+ expect(result).toContain('<MigrationProgress');
305
+ });
306
+
307
+ it('should show completion state with checkmark', () => {
308
+ expect(result).toContain('Database is ready');
309
+ expect(result).toContain('bg-green-100');
310
+ });
311
+ });
312
+
313
+ describe('generateAdminIndex', () => {
314
+ const result = generateAdminIndex();
315
+
316
+ it('should export DbStatusBanner', () => {
317
+ expect(result).toContain("export { DbStatusBanner }");
318
+ expect(result).toContain("'./DbStatusBanner'");
319
+ });
320
+
321
+ it('should export DbSetupStepper', () => {
322
+ expect(result).toContain("export { DbSetupStepper }");
323
+ expect(result).toContain("'./DbSetupStepper'");
324
+ });
325
+ });
326
+
327
+ describe('generateAppTsxWithAdmin', () => {
328
+ const result = generateAppTsxWithAdmin(TEST_PROJECT);
329
+
330
+ it('should import DbStatusBanner and DbSetupStepper', () => {
331
+ expect(result).toContain("import { DbStatusBanner } from './admin'");
332
+ expect(result).toContain("import { DbSetupStepper } from './admin'");
333
+ });
334
+
335
+ it('should include showWizard state', () => {
336
+ expect(result).toContain('showWizard');
337
+ expect(result).toContain('setShowWizard');
338
+ expect(result).toContain('useState(false)');
339
+ });
340
+
341
+ it('should render DbStatusBanner with onSetupClick', () => {
342
+ expect(result).toContain('<DbStatusBanner');
343
+ expect(result).toContain('onSetupClick');
344
+ expect(result).toContain('setShowWizard(true)');
345
+ });
346
+
347
+ it('should render DbSetupStepper conditionally', () => {
348
+ expect(result).toContain('{showWizard && (');
349
+ expect(result).toContain('<DbSetupStepper');
350
+ expect(result).toContain('setShowWizard(false)');
351
+ });
352
+
353
+ it('should include project name', () => {
354
+ expect(result).toContain(TEST_PROJECT);
355
+ });
356
+
357
+ it('should preserve health check logic', () => {
358
+ expect(result).toContain('HealthStatus');
359
+ expect(result).toContain('/health');
360
+ expect(result).toContain('Backend Status');
361
+ });
362
+
363
+ it('should export default App', () => {
364
+ expect(result).toContain('export default App');
365
+ });
366
+ });