@friggframework/devtools 2.0.0--canary.545.c459392.0 → 2.0.0--canary.547.67ebb53.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 (128) hide show
  1. package/frigg-cli/README.md +1 -1
  2. package/frigg-cli/__tests__/unit/commands/build.test.js +1 -1
  3. package/frigg-cli/__tests__/unit/commands/doctor.test.js +2 -0
  4. package/frigg-cli/__tests__/unit/commands/install.test.js +19 -23
  5. package/frigg-cli/__tests__/unit/dependencies.test.js +2 -2
  6. package/frigg-cli/build-command/index.js +11 -123
  7. package/frigg-cli/deploy-command/index.js +1 -83
  8. package/frigg-cli/doctor-command/index.js +16 -37
  9. package/frigg-cli/generate-iam-command.js +1 -21
  10. package/frigg-cli/index.js +6 -21
  11. package/frigg-cli/index.test.js +2 -7
  12. package/frigg-cli/init-command/backend-first-handler.js +42 -124
  13. package/frigg-cli/init-command/index.js +1 -2
  14. package/frigg-cli/init-command/template-handler.js +3 -13
  15. package/frigg-cli/install-command/backend-js.js +3 -3
  16. package/frigg-cli/install-command/environment-variables.js +19 -16
  17. package/frigg-cli/install-command/environment-variables.test.js +13 -12
  18. package/frigg-cli/install-command/index.js +9 -14
  19. package/frigg-cli/install-command/integration-file.js +3 -3
  20. package/frigg-cli/install-command/logger.js +12 -0
  21. package/frigg-cli/install-command/validate-package.js +9 -5
  22. package/frigg-cli/jest.config.js +1 -4
  23. package/frigg-cli/repair-command/index.js +128 -121
  24. package/frigg-cli/start-command/index.js +2 -324
  25. package/frigg-cli/ui-command/index.js +36 -58
  26. package/frigg-cli/utils/repo-detection.js +37 -85
  27. package/infrastructure/create-frigg-infrastructure.js +0 -93
  28. package/infrastructure/docs/iam-policy-templates.md +1 -1
  29. package/infrastructure/domains/networking/vpc-builder.test.js +4 -2
  30. package/infrastructure/domains/networking/vpc-resolver.test.js +1 -1
  31. package/infrastructure/domains/shared/cloudformation-discovery.test.js +7 -4
  32. package/infrastructure/domains/shared/resource-discovery.js +5 -5
  33. package/infrastructure/domains/shared/types/app-definition.js +0 -21
  34. package/infrastructure/domains/shared/types/discovery-result.test.js +1 -1
  35. package/infrastructure/domains/shared/utilities/base-definition-factory.js +1 -10
  36. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +2 -2
  37. package/infrastructure/infrastructure-composer.js +0 -2
  38. package/infrastructure/infrastructure-composer.test.js +2 -2
  39. package/management-ui/README.md +109 -245
  40. package/package.json +7 -8
  41. package/frigg-cli/__tests__/application/use-cases/AddApiModuleToIntegrationUseCase.test.js +0 -326
  42. package/frigg-cli/__tests__/application/use-cases/CreateApiModuleUseCase.test.js +0 -337
  43. package/frigg-cli/__tests__/domain/entities/ApiModule.test.js +0 -373
  44. package/frigg-cli/__tests__/domain/entities/AppDefinition.test.js +0 -313
  45. package/frigg-cli/__tests__/domain/services/IntegrationValidator.test.js +0 -269
  46. package/frigg-cli/__tests__/domain/value-objects/IntegrationName.test.js +0 -82
  47. package/frigg-cli/__tests__/infrastructure/adapters/IntegrationJsUpdater.test.js +0 -408
  48. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemApiModuleRepository.test.js +0 -583
  49. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemAppDefinitionRepository.test.js +0 -314
  50. package/frigg-cli/__tests__/infrastructure/repositories/FileSystemIntegrationRepository.test.js +0 -383
  51. package/frigg-cli/__tests__/unit/commands/init.test.js +0 -406
  52. package/frigg-cli/__tests__/unit/commands/provider-dispatch.test.js +0 -383
  53. package/frigg-cli/__tests__/unit/commands/repair.test.js +0 -275
  54. package/frigg-cli/__tests__/unit/start-command/application/RunPreflightChecksUseCase.test.js +0 -411
  55. package/frigg-cli/__tests__/unit/start-command/infrastructure/DatabaseAdapter.test.js +0 -405
  56. package/frigg-cli/__tests__/unit/start-command/infrastructure/DockerAdapter.test.js +0 -496
  57. package/frigg-cli/__tests__/unit/start-command/presentation/InteractivePromptAdapter.test.js +0 -474
  58. package/frigg-cli/__tests__/unit/utils/output.test.js +0 -196
  59. package/frigg-cli/application/use-cases/AddApiModuleToIntegrationUseCase.js +0 -93
  60. package/frigg-cli/application/use-cases/CreateApiModuleUseCase.js +0 -93
  61. package/frigg-cli/application/use-cases/CreateIntegrationUseCase.js +0 -103
  62. package/frigg-cli/container.js +0 -172
  63. package/frigg-cli/docs/OUTPUT_MIGRATION_GUIDE.md +0 -286
  64. package/frigg-cli/domain/entities/ApiModule.js +0 -272
  65. package/frigg-cli/domain/entities/AppDefinition.js +0 -227
  66. package/frigg-cli/domain/entities/Integration.js +0 -198
  67. package/frigg-cli/domain/exceptions/DomainException.js +0 -24
  68. package/frigg-cli/domain/ports/IApiModuleRepository.js +0 -53
  69. package/frigg-cli/domain/ports/IAppDefinitionRepository.js +0 -43
  70. package/frigg-cli/domain/ports/IIntegrationRepository.js +0 -61
  71. package/frigg-cli/domain/services/IntegrationValidator.js +0 -185
  72. package/frigg-cli/domain/value-objects/IntegrationId.js +0 -42
  73. package/frigg-cli/domain/value-objects/IntegrationName.js +0 -60
  74. package/frigg-cli/domain/value-objects/SemanticVersion.js +0 -70
  75. package/frigg-cli/infrastructure/UnitOfWork.js +0 -46
  76. package/frigg-cli/infrastructure/adapters/BackendJsUpdater.js +0 -197
  77. package/frigg-cli/infrastructure/adapters/FileSystemAdapter.js +0 -224
  78. package/frigg-cli/infrastructure/adapters/IntegrationJsUpdater.js +0 -249
  79. package/frigg-cli/infrastructure/adapters/SchemaValidator.js +0 -92
  80. package/frigg-cli/infrastructure/repositories/FileSystemApiModuleRepository.js +0 -373
  81. package/frigg-cli/infrastructure/repositories/FileSystemAppDefinitionRepository.js +0 -116
  82. package/frigg-cli/infrastructure/repositories/FileSystemIntegrationRepository.js +0 -277
  83. package/frigg-cli/package-lock.json +0 -16226
  84. package/frigg-cli/start-command/application/RunPreflightChecksUseCase.js +0 -376
  85. package/frigg-cli/start-command/infrastructure/DatabaseAdapter.js +0 -591
  86. package/frigg-cli/start-command/infrastructure/DockerAdapter.js +0 -306
  87. package/frigg-cli/start-command/presentation/InteractivePromptAdapter.js +0 -329
  88. package/frigg-cli/templates/backend/.env.example +0 -62
  89. package/frigg-cli/templates/backend/.eslintrc.json +0 -12
  90. package/frigg-cli/templates/backend/.prettierrc +0 -6
  91. package/frigg-cli/templates/backend/docker-compose.yml +0 -22
  92. package/frigg-cli/templates/backend/index.js +0 -96
  93. package/frigg-cli/templates/backend/infrastructure.js +0 -12
  94. package/frigg-cli/templates/backend/jest.config.js +0 -17
  95. package/frigg-cli/templates/backend/package.json +0 -50
  96. package/frigg-cli/templates/backend/src/api-modules/.gitkeep +0 -10
  97. package/frigg-cli/templates/backend/src/base/.gitkeep +0 -7
  98. package/frigg-cli/templates/backend/src/integrations/.gitkeep +0 -10
  99. package/frigg-cli/templates/backend/src/integrations/ExampleIntegration.js +0 -65
  100. package/frigg-cli/templates/backend/src/utils/.gitkeep +0 -7
  101. package/frigg-cli/templates/backend/test/setup.js +0 -30
  102. package/frigg-cli/templates/backend/ui-extensions/.gitkeep +0 -0
  103. package/frigg-cli/templates/backend/ui-extensions/README.md +0 -77
  104. package/frigg-cli/utils/__tests__/provider-helper.test.js +0 -55
  105. package/frigg-cli/utils/__tests__/repo-detection.test.js +0 -436
  106. package/frigg-cli/utils/output.js +0 -382
  107. package/frigg-cli/utils/provider-helper.js +0 -75
  108. package/frigg-cli/validate-command/__tests__/adapters/validate-command.test.js +0 -205
  109. package/frigg-cli/validate-command/__tests__/application/validate-app-use-case.test.js +0 -104
  110. package/frigg-cli/validate-command/__tests__/domain/fix-suggestion.test.js +0 -153
  111. package/frigg-cli/validate-command/__tests__/domain/validation-error.test.js +0 -162
  112. package/frigg-cli/validate-command/__tests__/domain/validation-result.test.js +0 -152
  113. package/frigg-cli/validate-command/__tests__/infrastructure/api-module-validator.test.js +0 -332
  114. package/frigg-cli/validate-command/__tests__/infrastructure/app-definition-validator.test.js +0 -191
  115. package/frigg-cli/validate-command/__tests__/infrastructure/integration-class-validator.test.js +0 -146
  116. package/frigg-cli/validate-command/__tests__/infrastructure/template-validation.test.js +0 -155
  117. package/frigg-cli/validate-command/adapters/cli/validate-command.js +0 -199
  118. package/frigg-cli/validate-command/application/use-cases/validate-app-use-case.js +0 -35
  119. package/frigg-cli/validate-command/domain/entities/validation-result.js +0 -74
  120. package/frigg-cli/validate-command/domain/value-objects/fix-suggestion.js +0 -74
  121. package/frigg-cli/validate-command/domain/value-objects/validation-error.js +0 -68
  122. package/frigg-cli/validate-command/infrastructure/validators/api-module-validator.js +0 -181
  123. package/frigg-cli/validate-command/infrastructure/validators/app-definition-validator.js +0 -128
  124. package/frigg-cli/validate-command/infrastructure/validators/integration-class-validator.js +0 -113
  125. package/infrastructure/domains/admin-scripts/admin-script-builder.js +0 -200
  126. package/infrastructure/domains/admin-scripts/admin-script-builder.test.js +0 -499
  127. package/infrastructure/domains/admin-scripts/index.js +0 -5
  128. package/infrastructure/jest.config.js +0 -16
@@ -1,474 +0,0 @@
1
- /**
2
- * InteractivePromptAdapter Tests
3
- * Handles prompts in terminal mode (inquirer) or IPC mode (JSON over stdio)
4
- *
5
- * Tests follow TDD pattern - written BEFORE implementation
6
- */
7
-
8
- // Mock @inquirer/prompts
9
- jest.mock('@inquirer/prompts', () => ({
10
- confirm: jest.fn(),
11
- select: jest.fn(),
12
- input: jest.fn()
13
- }));
14
-
15
- const { confirm, select, input } = require('@inquirer/prompts');
16
-
17
- // Import after mocks
18
- const {
19
- InteractivePromptAdapter,
20
- TerminalPromptAdapter,
21
- IpcPromptAdapter
22
- } = require('../../../../start-command/presentation/InteractivePromptAdapter');
23
-
24
- describe('InteractivePromptAdapter', () => {
25
- describe('factory method - create()', () => {
26
- it('should create TerminalPromptAdapter when mode is terminal', () => {
27
- const adapter = InteractivePromptAdapter.create({ mode: 'terminal' });
28
- expect(adapter).toBeInstanceOf(TerminalPromptAdapter);
29
- });
30
-
31
- it('should create IpcPromptAdapter when mode is ipc', () => {
32
- const adapter = InteractivePromptAdapter.create({ mode: 'ipc' });
33
- expect(adapter).toBeInstanceOf(IpcPromptAdapter);
34
- });
35
-
36
- it('should default to terminal mode when no mode specified', () => {
37
- const adapter = InteractivePromptAdapter.create({});
38
- expect(adapter).toBeInstanceOf(TerminalPromptAdapter);
39
- });
40
-
41
- it('should use ipc mode when FRIGG_IPC env var is true', () => {
42
- const originalEnv = process.env.FRIGG_IPC;
43
- process.env.FRIGG_IPC = 'true';
44
-
45
- const adapter = InteractivePromptAdapter.create({});
46
- expect(adapter).toBeInstanceOf(IpcPromptAdapter);
47
-
48
- process.env.FRIGG_IPC = originalEnv;
49
- });
50
- });
51
- });
52
-
53
- describe('TerminalPromptAdapter', () => {
54
- let adapter;
55
-
56
- beforeEach(() => {
57
- jest.clearAllMocks();
58
- adapter = new TerminalPromptAdapter();
59
- });
60
-
61
- describe('confirm()', () => {
62
- it('should call inquirer confirm with message', async () => {
63
- confirm.mockResolvedValue(true);
64
-
65
- const result = await adapter.confirm({
66
- message: 'Start Docker Desktop?',
67
- default: true
68
- });
69
-
70
- expect(result).toBe(true);
71
- expect(confirm).toHaveBeenCalledWith({
72
- message: 'Start Docker Desktop?',
73
- default: true
74
- });
75
- });
76
-
77
- it('should return false when user declines', async () => {
78
- confirm.mockResolvedValue(false);
79
-
80
- const result = await adapter.confirm({
81
- message: 'Continue?'
82
- });
83
-
84
- expect(result).toBe(false);
85
- });
86
-
87
- it('should use default value when provided', async () => {
88
- confirm.mockResolvedValue(false);
89
-
90
- await adapter.confirm({
91
- message: 'Continue?',
92
- default: false
93
- });
94
-
95
- expect(confirm).toHaveBeenCalledWith(expect.objectContaining({
96
- default: false
97
- }));
98
- });
99
- });
100
-
101
- describe('select()', () => {
102
- it('should call inquirer select with options', async () => {
103
- select.mockResolvedValue('option1');
104
-
105
- const result = await adapter.select({
106
- message: 'Choose an option:',
107
- choices: [
108
- { value: 'option1', name: 'Option 1' },
109
- { value: 'option2', name: 'Option 2' }
110
- ]
111
- });
112
-
113
- expect(result).toBe('option1');
114
- expect(select).toHaveBeenCalledWith(expect.objectContaining({
115
- message: 'Choose an option:'
116
- }));
117
- });
118
-
119
- it('should return selected value', async () => {
120
- select.mockResolvedValue('option2');
121
-
122
- const result = await adapter.select({
123
- message: 'Choose:',
124
- choices: [
125
- { value: 'option1', name: 'Option 1' },
126
- { value: 'option2', name: 'Option 2' }
127
- ]
128
- });
129
-
130
- expect(result).toBe('option2');
131
- });
132
- });
133
-
134
- describe('input()', () => {
135
- it('should call inquirer input with message', async () => {
136
- input.mockResolvedValue('user input');
137
-
138
- const result = await adapter.input({
139
- message: 'Enter value:'
140
- });
141
-
142
- expect(result).toBe('user input');
143
- expect(input).toHaveBeenCalledWith(expect.objectContaining({
144
- message: 'Enter value:'
145
- }));
146
- });
147
-
148
- it('should use default value when provided', async () => {
149
- input.mockResolvedValue('default');
150
-
151
- await adapter.input({
152
- message: 'Enter value:',
153
- default: 'default'
154
- });
155
-
156
- expect(input).toHaveBeenCalledWith(expect.objectContaining({
157
- default: 'default'
158
- }));
159
- });
160
- });
161
-
162
- describe('promptForResolution()', () => {
163
- it('should prompt confirm for start_docker resolution', async () => {
164
- confirm.mockResolvedValue(true);
165
-
166
- const result = await adapter.promptForResolution({
167
- name: 'docker_running',
168
- status: 'failed',
169
- message: 'Docker is not running',
170
- canResolve: true,
171
- resolution: {
172
- type: 'start_docker',
173
- prompt: 'Would you like to start Docker Desktop?'
174
- }
175
- });
176
-
177
- expect(result.shouldResolve).toBe(true);
178
- expect(confirm).toHaveBeenCalledWith(expect.objectContaining({
179
- message: 'Would you like to start Docker Desktop?'
180
- }));
181
- });
182
-
183
- it('should prompt confirm for start_docker_compose resolution', async () => {
184
- confirm.mockResolvedValue(true);
185
-
186
- const result = await adapter.promptForResolution({
187
- name: 'database_reachable',
188
- status: 'failed',
189
- message: 'Database not reachable',
190
- canResolve: true,
191
- resolution: {
192
- type: 'start_docker_compose',
193
- prompt: 'Would you like to run docker-compose up?',
194
- composePath: '/test/docker-compose.yml'
195
- }
196
- });
197
-
198
- expect(result.shouldResolve).toBe(true);
199
- expect(result.composePath).toBe('/test/docker-compose.yml');
200
- });
201
-
202
- it('should return shouldResolve: false when user declines', async () => {
203
- confirm.mockResolvedValue(false);
204
-
205
- const result = await adapter.promptForResolution({
206
- name: 'docker_running',
207
- status: 'failed',
208
- message: 'Docker not running',
209
- canResolve: true,
210
- resolution: {
211
- type: 'start_docker',
212
- prompt: 'Start Docker?'
213
- }
214
- });
215
-
216
- expect(result.shouldResolve).toBe(false);
217
- });
218
-
219
- it('should return shouldResolve: false for non-resolvable checks', async () => {
220
- const result = await adapter.promptForResolution({
221
- name: 'docker_installed',
222
- status: 'failed',
223
- message: 'Docker not installed',
224
- canResolve: false,
225
- resolution: {
226
- type: 'manual',
227
- instructions: 'Install Docker manually'
228
- }
229
- });
230
-
231
- expect(result.shouldResolve).toBe(false);
232
- expect(confirm).not.toHaveBeenCalled();
233
- });
234
- });
235
- });
236
-
237
- describe('IpcPromptAdapter', () => {
238
- let adapter;
239
- let originalStdout;
240
- let mockStdout;
241
-
242
- beforeEach(() => {
243
- jest.clearAllMocks();
244
- adapter = new IpcPromptAdapter();
245
-
246
- // Mock stdout.write
247
- mockStdout = jest.fn();
248
- originalStdout = process.stdout.write;
249
- process.stdout.write = mockStdout;
250
- });
251
-
252
- afterEach(() => {
253
- process.stdout.write = originalStdout;
254
- });
255
-
256
- describe('confirm()', () => {
257
- it('should output JSON prompt request to stdout', async () => {
258
- // Mock requestId generation for predictable test
259
- adapter._generateRequestId = () => 'test-id';
260
-
261
- const resultPromise = adapter.confirm({
262
- message: 'Start Docker?',
263
- default: true
264
- });
265
-
266
- // Resolve immediately for test
267
- adapter._resolvePrompt('test-id', true);
268
-
269
- const result = await resultPromise;
270
-
271
- expect(result).toBe(true);
272
- expect(mockStdout).toHaveBeenCalledWith(expect.stringContaining('frigg_ipc'));
273
- expect(mockStdout).toHaveBeenCalledWith(expect.stringContaining('prompt_request'));
274
- expect(mockStdout).toHaveBeenCalledWith(expect.stringContaining('confirm'));
275
- });
276
-
277
- it('should include requestId in output', async () => {
278
- adapter._generateRequestId = () => 'unique-id-123';
279
- adapter._resolvePrompt = jest.fn();
280
-
281
- // Start the promise but don't await yet
282
- adapter.confirm({ message: 'Test?' });
283
-
284
- // Give time for stdout.write to be called
285
- await new Promise(resolve => setTimeout(resolve, 10));
286
-
287
- const output = mockStdout.mock.calls[0][0];
288
- expect(output).toContain('unique-id-123');
289
- });
290
-
291
- it('should output newline-terminated JSON', async () => {
292
- adapter._generateRequestId = () => 'test-id';
293
-
294
- adapter.confirm({ message: 'Test?' });
295
-
296
- await new Promise(resolve => setTimeout(resolve, 10));
297
-
298
- const output = mockStdout.mock.calls[0][0];
299
- expect(output.endsWith('\n')).toBe(true);
300
- });
301
- });
302
-
303
- describe('_parseIpcMessage()', () => {
304
- it('should parse valid prompt response', () => {
305
- const message = JSON.stringify({
306
- frigg_ipc: 'prompt_response',
307
- requestId: 'test-id',
308
- response: true
309
- });
310
-
311
- const result = adapter._parseIpcMessage(message);
312
-
313
- expect(result.type).toBe('prompt_response');
314
- expect(result.requestId).toBe('test-id');
315
- expect(result.response).toBe(true);
316
- });
317
-
318
- it('should return null for non-IPC messages', () => {
319
- const message = 'regular log message';
320
- const result = adapter._parseIpcMessage(message);
321
-
322
- expect(result).toBeNull();
323
- });
324
-
325
- it('should return null for invalid JSON', () => {
326
- const message = '{ invalid json }';
327
- const result = adapter._parseIpcMessage(message);
328
-
329
- expect(result).toBeNull();
330
- });
331
- });
332
-
333
- describe('_formatIpcOutput()', () => {
334
- it('should format prompt request as JSON', () => {
335
- const output = adapter._formatIpcOutput('prompt_request', {
336
- requestId: 'test-123',
337
- prompt: {
338
- type: 'confirm',
339
- message: 'Continue?',
340
- default: true
341
- }
342
- });
343
-
344
- const parsed = JSON.parse(output.trim());
345
- expect(parsed.frigg_ipc).toBe('prompt_request');
346
- expect(parsed.requestId).toBe('test-123');
347
- expect(parsed.prompt.type).toBe('confirm');
348
- });
349
-
350
- it('should add newline to output', () => {
351
- const output = adapter._formatIpcOutput('prompt_request', {});
352
- expect(output.endsWith('\n')).toBe(true);
353
- });
354
- });
355
-
356
- describe('handleResponse()', () => {
357
- it('should resolve pending prompt with response', async () => {
358
- adapter._generateRequestId = () => 'test-id';
359
-
360
- const resultPromise = adapter.confirm({ message: 'Test?' });
361
-
362
- // Wait for prompt to be registered
363
- await new Promise(resolve => setTimeout(resolve, 10));
364
-
365
- // Handle the response
366
- adapter.handleResponse('test-id', true);
367
-
368
- const result = await resultPromise;
369
- expect(result).toBe(true);
370
- });
371
-
372
- it('should ignore responses for unknown requestIds', () => {
373
- // Should not throw
374
- expect(() => {
375
- adapter.handleResponse('unknown-id', true);
376
- }).not.toThrow();
377
- });
378
- });
379
-
380
- describe('promptForResolution()', () => {
381
- it('should output prompt in IPC format', async () => {
382
- adapter._generateRequestId = () => 'test-id';
383
-
384
- const check = {
385
- name: 'docker_running',
386
- status: 'failed',
387
- message: 'Docker not running',
388
- canResolve: true,
389
- resolution: {
390
- type: 'start_docker',
391
- prompt: 'Start Docker Desktop?'
392
- }
393
- };
394
-
395
- const resultPromise = adapter.promptForResolution(check);
396
-
397
- await new Promise(resolve => setTimeout(resolve, 10));
398
-
399
- const output = mockStdout.mock.calls[0][0];
400
- expect(output).toContain('prompt_request');
401
- expect(output).toContain('Start Docker Desktop?');
402
-
403
- // Resolve to complete the test
404
- adapter.handleResponse('test-id', true);
405
- await resultPromise;
406
- });
407
- });
408
- });
409
-
410
- describe('IPC Protocol Format', () => {
411
- describe('prompt_request format', () => {
412
- it('should match expected IPC protocol for confirm prompts', () => {
413
- const adapter = new IpcPromptAdapter();
414
- const output = adapter._formatIpcOutput('prompt_request', {
415
- requestId: 'prompt-1234',
416
- prompt: {
417
- type: 'confirm',
418
- message: 'Docker is not running. Start Docker Desktop?',
419
- default: true
420
- }
421
- });
422
-
423
- const parsed = JSON.parse(output.trim());
424
-
425
- expect(parsed).toEqual({
426
- frigg_ipc: 'prompt_request',
427
- requestId: 'prompt-1234',
428
- prompt: {
429
- type: 'confirm',
430
- message: 'Docker is not running. Start Docker Desktop?',
431
- default: true
432
- }
433
- });
434
- });
435
-
436
- it('should match expected IPC protocol for select prompts', () => {
437
- const adapter = new IpcPromptAdapter();
438
- const output = adapter._formatIpcOutput('prompt_request', {
439
- requestId: 'prompt-5678',
440
- prompt: {
441
- type: 'select',
442
- message: 'Choose an action:',
443
- choices: [
444
- { value: 'start', name: 'Start services' },
445
- { value: 'skip', name: 'Skip' }
446
- ]
447
- }
448
- });
449
-
450
- const parsed = JSON.parse(output.trim());
451
-
452
- expect(parsed.frigg_ipc).toBe('prompt_request');
453
- expect(parsed.prompt.type).toBe('select');
454
- expect(parsed.prompt.choices).toHaveLength(2);
455
- });
456
- });
457
-
458
- describe('prompt_response format', () => {
459
- it('should parse prompt_response messages correctly', () => {
460
- const adapter = new IpcPromptAdapter();
461
- const response = JSON.stringify({
462
- frigg_ipc: 'prompt_response',
463
- requestId: 'prompt-1234',
464
- response: true
465
- });
466
-
467
- const parsed = adapter._parseIpcMessage(response);
468
-
469
- expect(parsed.type).toBe('prompt_response');
470
- expect(parsed.requestId).toBe('prompt-1234');
471
- expect(parsed.response).toBe(true);
472
- });
473
- });
474
- });
@@ -1,196 +0,0 @@
1
- /**
2
- * Tests for unified Output utility
3
- */
4
-
5
- const output = require('../../../utils/output');
6
-
7
- describe('Output Utility', () => {
8
- // Mock console methods
9
- let originalConsole;
10
-
11
- beforeEach(() => {
12
- originalConsole = { ...console };
13
- console.log = jest.fn();
14
- console.error = jest.fn();
15
- console.warn = jest.fn();
16
- });
17
-
18
- afterEach(() => {
19
- console.log = originalConsole.log;
20
- console.error = originalConsole.error;
21
- console.warn = originalConsole.warn;
22
- });
23
-
24
- describe('success()', () => {
25
- it('should display success message with checkmark', () => {
26
- output.success('Operation completed');
27
- expect(console.log).toHaveBeenCalled();
28
- const args = console.log.mock.calls[0];
29
- expect(args.join(' ')).toContain('Operation completed');
30
- });
31
- });
32
-
33
- describe('error()', () => {
34
- it('should display error message with X mark', () => {
35
- output.error('Operation failed');
36
- expect(console.error).toHaveBeenCalled();
37
- const args = console.error.mock.calls[0];
38
- expect(args.join(' ')).toContain('Operation failed');
39
- });
40
-
41
- it('should display error stack in debug mode', () => {
42
- const originalDebug = process.env.DEBUG;
43
- process.env.DEBUG = 'true';
44
-
45
- const error = new Error('Test error');
46
- output.error('Operation failed', error);
47
-
48
- expect(console.error).toHaveBeenCalledTimes(2);
49
-
50
- process.env.DEBUG = originalDebug;
51
- });
52
- });
53
-
54
- describe('info()', () => {
55
- it('should display info message', () => {
56
- output.info('Information message');
57
- expect(console.log).toHaveBeenCalled();
58
- const args = console.log.mock.calls[0];
59
- expect(args.join(' ')).toContain('Information message');
60
- });
61
- });
62
-
63
- describe('warn()', () => {
64
- it('should display warning message', () => {
65
- output.warn('Warning message');
66
- expect(console.warn).toHaveBeenCalled();
67
- const args = console.warn.mock.calls[0];
68
- expect(args.join(' ')).toContain('Warning message');
69
- });
70
- });
71
-
72
- describe('header()', () => {
73
- it('should display formatted header', () => {
74
- output.header('Test Header');
75
- expect(console.log).toHaveBeenCalledTimes(3); // empty line, title, separator
76
- });
77
- });
78
-
79
- describe('table()', () => {
80
- it('should display table with data', () => {
81
- const data = [
82
- { name: 'Module A', version: '1.0.0', status: 'active' },
83
- { name: 'Module B', version: '2.1.0', status: 'inactive' }
84
- ];
85
-
86
- output.table(data);
87
- expect(console.log).toHaveBeenCalled();
88
- expect(console.log.mock.calls.length).toBeGreaterThan(3); // header + separator + rows
89
- });
90
-
91
- it('should handle empty data', () => {
92
- output.table([]);
93
- expect(console.log).toHaveBeenCalledWith(expect.anything(), expect.stringContaining('No data to display'));
94
- });
95
-
96
- it('should handle specific columns', () => {
97
- const data = [
98
- { name: 'Module A', version: '1.0.0', status: 'active', extra: 'ignored' }
99
- ];
100
-
101
- output.table(data, ['name', 'version']);
102
- expect(console.log).toHaveBeenCalled();
103
- });
104
- });
105
-
106
- describe('keyValue()', () => {
107
- it('should display key-value pairs', () => {
108
- const data = {
109
- 'Module Name': 'test-module',
110
- 'Version': '1.0.0',
111
- 'Status': 'active'
112
- };
113
-
114
- output.keyValue(data);
115
- expect(console.log).toHaveBeenCalledTimes(3);
116
- });
117
- });
118
-
119
- describe('json()', () => {
120
- it('should display formatted JSON', () => {
121
- const data = { name: 'test', version: '1.0.0', active: true };
122
-
123
- output.json(data);
124
- expect(console.log).toHaveBeenCalled();
125
- const output_text = console.log.mock.calls[0][0];
126
- expect(output_text).toContain('name');
127
- expect(output_text).toContain('1.0.0');
128
- });
129
- });
130
-
131
- describe('spinner()', () => {
132
- jest.useFakeTimers();
133
-
134
- it('should create and control spinner', () => {
135
- const spinner = output.spinner('Loading...');
136
-
137
- // Spinner should have control methods
138
- expect(spinner).toHaveProperty('update');
139
- expect(spinner).toHaveProperty('succeed');
140
- expect(spinner).toHaveProperty('fail');
141
- expect(spinner).toHaveProperty('stop');
142
-
143
- spinner.stop();
144
- });
145
-
146
- it('should succeed with message', () => {
147
- const spinner = output.spinner('Loading...');
148
- spinner.succeed('Loaded successfully');
149
-
150
- expect(console.log).toHaveBeenCalled();
151
- });
152
-
153
- it('should fail with message', () => {
154
- const spinner = output.spinner('Loading...');
155
- spinner.fail('Loading failed');
156
-
157
- expect(console.error).toHaveBeenCalled();
158
- });
159
-
160
- jest.useRealTimers();
161
- });
162
-
163
- describe('progress()', () => {
164
- let originalStdout;
165
-
166
- beforeEach(() => {
167
- originalStdout = process.stdout.write;
168
- process.stdout.write = jest.fn();
169
- });
170
-
171
- afterEach(() => {
172
- process.stdout.write = originalStdout;
173
- });
174
-
175
- it('should display progress bar', () => {
176
- output.progress(50, 100, 'Processing...');
177
- expect(process.stdout.write).toHaveBeenCalled();
178
-
179
- const output_text = process.stdout.write.mock.calls[0][0];
180
- expect(output_text).toContain('%');
181
- expect(output_text).toContain('Processing...');
182
- });
183
-
184
- it('should complete progress bar', () => {
185
- output.progress(100, 100);
186
- expect(console.log).toHaveBeenCalled(); // Newline on completion
187
- });
188
- });
189
-
190
- describe('log()', () => {
191
- it('should log raw messages', () => {
192
- output.log('Raw message');
193
- expect(console.log).toHaveBeenCalledWith('Raw message');
194
- });
195
- });
196
- });