pnpm-catalog-updates 1.0.2 → 1.1.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.
- package/README.md +15 -0
- package/dist/index.js +22031 -10684
- package/dist/index.js.map +1 -1
- package/package.json +7 -2
- package/src/cli/__tests__/commandRegistrar.test.ts +248 -0
- package/src/cli/commandRegistrar.ts +785 -0
- package/src/cli/commands/__tests__/aiCommand.test.ts +161 -0
- package/src/cli/commands/__tests__/analyzeCommand.test.ts +283 -0
- package/src/cli/commands/__tests__/checkCommand.test.ts +435 -0
- package/src/cli/commands/__tests__/graphCommand.test.ts +312 -0
- package/src/cli/commands/__tests__/initCommand.test.ts +317 -0
- package/src/cli/commands/__tests__/rollbackCommand.test.ts +400 -0
- package/src/cli/commands/__tests__/securityCommand.test.ts +467 -0
- package/src/cli/commands/__tests__/themeCommand.test.ts +166 -0
- package/src/cli/commands/__tests__/updateCommand.test.ts +720 -0
- package/src/cli/commands/__tests__/workspaceCommand.test.ts +286 -0
- package/src/cli/commands/aiCommand.ts +163 -0
- package/src/cli/commands/analyzeCommand.ts +219 -0
- package/src/cli/commands/checkCommand.ts +91 -98
- package/src/cli/commands/graphCommand.ts +475 -0
- package/src/cli/commands/initCommand.ts +64 -54
- package/src/cli/commands/rollbackCommand.ts +334 -0
- package/src/cli/commands/securityCommand.ts +165 -100
- package/src/cli/commands/themeCommand.ts +148 -0
- package/src/cli/commands/updateCommand.ts +215 -263
- package/src/cli/commands/workspaceCommand.ts +73 -0
- package/src/cli/constants/cliChoices.ts +93 -0
- package/src/cli/formatters/__tests__/__snapshots__/outputFormatter.test.ts.snap +557 -0
- package/src/cli/formatters/__tests__/ciFormatter.test.ts +526 -0
- package/src/cli/formatters/__tests__/outputFormatter.test.ts +448 -0
- package/src/cli/formatters/__tests__/progressBar.test.ts +709 -0
- package/src/cli/formatters/ciFormatter.ts +964 -0
- package/src/cli/formatters/colorUtils.ts +145 -0
- package/src/cli/formatters/outputFormatter.ts +615 -332
- package/src/cli/formatters/progressBar.ts +43 -52
- package/src/cli/formatters/versionFormatter.ts +132 -0
- package/src/cli/handlers/aiAnalysisHandler.ts +205 -0
- package/src/cli/handlers/changelogHandler.ts +113 -0
- package/src/cli/handlers/index.ts +9 -0
- package/src/cli/handlers/installHandler.ts +130 -0
- package/src/cli/index.ts +175 -726
- package/src/cli/interactive/InteractiveOptionsCollector.ts +387 -0
- package/src/cli/interactive/interactivePrompts.ts +189 -83
- package/src/cli/interactive/optionUtils.ts +89 -0
- package/src/cli/themes/colorTheme.ts +43 -16
- package/src/cli/utils/cliOutput.ts +118 -0
- package/src/cli/utils/commandHelpers.ts +249 -0
- package/src/cli/validators/commandValidator.ts +321 -336
- package/src/cli/validators/index.ts +37 -2
- package/src/cli/options/globalOptions.ts +0 -437
- package/src/cli/options/index.ts +0 -5
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OutputFormatter Snapshot Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for the OutputFormatter class using snapshot testing
|
|
5
|
+
* to ensure consistent output formatting across different formats.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
OutdatedReport,
|
|
10
|
+
UpdatePlan,
|
|
11
|
+
UpdateResult,
|
|
12
|
+
WorkspaceInfo,
|
|
13
|
+
WorkspaceStats,
|
|
14
|
+
WorkspaceValidationReport,
|
|
15
|
+
} from '@pcu/core'
|
|
16
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
17
|
+
import { OutputFormatter } from '../outputFormatter.js'
|
|
18
|
+
|
|
19
|
+
// Mock @pcu/utils functions
|
|
20
|
+
vi.mock('@pcu/utils', () => ({
|
|
21
|
+
t: (key: string, params?: Record<string, unknown>) => {
|
|
22
|
+
if (params) {
|
|
23
|
+
let result = key
|
|
24
|
+
for (const [k, v] of Object.entries(params)) {
|
|
25
|
+
result = result.replace(`{{${k}}}`, String(v))
|
|
26
|
+
}
|
|
27
|
+
return result
|
|
28
|
+
}
|
|
29
|
+
return key
|
|
30
|
+
},
|
|
31
|
+
// Include async utilities that may be used by the code
|
|
32
|
+
timeout: vi.fn().mockImplementation((promise: Promise<unknown>) => promise),
|
|
33
|
+
delay: vi.fn().mockResolvedValue(undefined),
|
|
34
|
+
retry: vi.fn().mockImplementation((fn: () => Promise<unknown>) => fn()),
|
|
35
|
+
}))
|
|
36
|
+
|
|
37
|
+
// Test fixtures
|
|
38
|
+
const mockWorkspace = {
|
|
39
|
+
name: 'test-workspace',
|
|
40
|
+
path: '/path/to/workspace',
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const mockOutdatedReport: OutdatedReport = {
|
|
44
|
+
workspace: mockWorkspace,
|
|
45
|
+
hasUpdates: true,
|
|
46
|
+
totalOutdated: 3,
|
|
47
|
+
catalogs: [
|
|
48
|
+
{
|
|
49
|
+
catalogName: 'default',
|
|
50
|
+
totalDependencies: 10,
|
|
51
|
+
outdatedCount: 3,
|
|
52
|
+
outdatedDependencies: [
|
|
53
|
+
{
|
|
54
|
+
packageName: 'lodash',
|
|
55
|
+
currentVersion: '^4.17.0',
|
|
56
|
+
latestVersion: '^4.17.21',
|
|
57
|
+
updateType: 'patch',
|
|
58
|
+
isSecurityUpdate: false,
|
|
59
|
+
affectedPackages: ['app1', 'app2'],
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
packageName: 'react',
|
|
63
|
+
currentVersion: '^17.0.0',
|
|
64
|
+
latestVersion: '^18.2.0',
|
|
65
|
+
updateType: 'major',
|
|
66
|
+
isSecurityUpdate: false,
|
|
67
|
+
affectedPackages: ['app1'],
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
packageName: 'axios',
|
|
71
|
+
currentVersion: '^0.21.0',
|
|
72
|
+
latestVersion: '^1.6.0',
|
|
73
|
+
updateType: 'major',
|
|
74
|
+
isSecurityUpdate: true,
|
|
75
|
+
affectedPackages: ['app2', 'lib1'],
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const mockEmptyOutdatedReport: OutdatedReport = {
|
|
83
|
+
workspace: mockWorkspace,
|
|
84
|
+
hasUpdates: false,
|
|
85
|
+
totalOutdated: 0,
|
|
86
|
+
catalogs: [
|
|
87
|
+
{
|
|
88
|
+
catalogName: 'default',
|
|
89
|
+
totalDependencies: 5,
|
|
90
|
+
outdatedCount: 0,
|
|
91
|
+
outdatedDependencies: [],
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const mockUpdatePlan: UpdatePlan = {
|
|
97
|
+
workspace: mockWorkspace,
|
|
98
|
+
totalUpdates: 2,
|
|
99
|
+
hasConflicts: false,
|
|
100
|
+
conflicts: [],
|
|
101
|
+
updates: [
|
|
102
|
+
{
|
|
103
|
+
catalogName: 'default',
|
|
104
|
+
packageName: 'lodash',
|
|
105
|
+
currentVersion: '^4.17.0',
|
|
106
|
+
newVersion: '^4.17.21',
|
|
107
|
+
updateType: 'patch',
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
catalogName: 'default',
|
|
111
|
+
packageName: 'express',
|
|
112
|
+
currentVersion: '^4.17.0',
|
|
113
|
+
newVersion: '^4.18.2',
|
|
114
|
+
updateType: 'minor',
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const mockUpdatePlanWithConflicts: UpdatePlan = {
|
|
120
|
+
workspace: mockWorkspace,
|
|
121
|
+
totalUpdates: 1,
|
|
122
|
+
hasConflicts: true,
|
|
123
|
+
conflicts: [
|
|
124
|
+
{
|
|
125
|
+
packageName: 'typescript',
|
|
126
|
+
catalogs: [
|
|
127
|
+
{ catalogName: 'default', currentVersion: '4.9.0', proposedVersion: '5.0.0' },
|
|
128
|
+
{ catalogName: 'legacy', currentVersion: '4.5.0', proposedVersion: '4.9.0' },
|
|
129
|
+
],
|
|
130
|
+
recommendation: 'Consider aligning TypeScript versions across catalogs',
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
updates: [
|
|
134
|
+
{
|
|
135
|
+
catalogName: 'default',
|
|
136
|
+
packageName: 'lodash',
|
|
137
|
+
currentVersion: '^4.17.0',
|
|
138
|
+
newVersion: '^4.17.21',
|
|
139
|
+
updateType: 'patch',
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const mockUpdateResult: UpdateResult = {
|
|
145
|
+
workspace: mockWorkspace,
|
|
146
|
+
success: true,
|
|
147
|
+
totalUpdated: 2,
|
|
148
|
+
totalSkipped: 1,
|
|
149
|
+
totalErrors: 0,
|
|
150
|
+
updatedDependencies: [
|
|
151
|
+
{
|
|
152
|
+
catalogName: 'default',
|
|
153
|
+
packageName: 'lodash',
|
|
154
|
+
fromVersion: '^4.17.0',
|
|
155
|
+
toVersion: '^4.17.21',
|
|
156
|
+
updateType: 'patch',
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
catalogName: 'default',
|
|
160
|
+
packageName: 'express',
|
|
161
|
+
fromVersion: '^4.17.0',
|
|
162
|
+
toVersion: '^4.18.2',
|
|
163
|
+
updateType: 'minor',
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
skippedDependencies: [
|
|
167
|
+
{
|
|
168
|
+
catalogName: 'default',
|
|
169
|
+
packageName: 'react',
|
|
170
|
+
reason: 'Major version update requires --force flag',
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
errors: [],
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const mockFailedUpdateResult: UpdateResult = {
|
|
177
|
+
workspace: mockWorkspace,
|
|
178
|
+
success: false,
|
|
179
|
+
totalUpdated: 0,
|
|
180
|
+
totalSkipped: 0,
|
|
181
|
+
totalErrors: 1,
|
|
182
|
+
updatedDependencies: [],
|
|
183
|
+
skippedDependencies: [],
|
|
184
|
+
errors: [
|
|
185
|
+
{
|
|
186
|
+
catalogName: 'default',
|
|
187
|
+
packageName: 'broken-pkg',
|
|
188
|
+
error: 'Network error while fetching version',
|
|
189
|
+
fatal: true,
|
|
190
|
+
},
|
|
191
|
+
],
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const mockWorkspaceInfo: WorkspaceInfo = {
|
|
195
|
+
name: 'test-workspace',
|
|
196
|
+
path: '/path/to/workspace',
|
|
197
|
+
packageCount: 5,
|
|
198
|
+
catalogCount: 2,
|
|
199
|
+
catalogNames: ['default', 'legacy'],
|
|
200
|
+
isValid: true,
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const mockWorkspaceStats: WorkspaceStats = {
|
|
204
|
+
workspace: mockWorkspace,
|
|
205
|
+
packages: {
|
|
206
|
+
total: 10,
|
|
207
|
+
withCatalogReferences: 8,
|
|
208
|
+
},
|
|
209
|
+
catalogs: {
|
|
210
|
+
total: 2,
|
|
211
|
+
totalEntries: 25,
|
|
212
|
+
},
|
|
213
|
+
dependencies: {
|
|
214
|
+
total: 50,
|
|
215
|
+
catalogReferences: 40,
|
|
216
|
+
byType: {
|
|
217
|
+
dependencies: 30,
|
|
218
|
+
devDependencies: 15,
|
|
219
|
+
peerDependencies: 3,
|
|
220
|
+
optionalDependencies: 2,
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const mockValidationReport: WorkspaceValidationReport = {
|
|
226
|
+
workspace: {
|
|
227
|
+
path: '/path/to/workspace',
|
|
228
|
+
name: 'test-workspace',
|
|
229
|
+
packageCount: 5,
|
|
230
|
+
catalogCount: 2,
|
|
231
|
+
},
|
|
232
|
+
isValid: true,
|
|
233
|
+
errors: [],
|
|
234
|
+
warnings: ['Consider updating deprecated package "old-package"'],
|
|
235
|
+
recommendations: [
|
|
236
|
+
'Add catalog entries for commonly used packages',
|
|
237
|
+
'Consider using stricter version ranges',
|
|
238
|
+
],
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const mockInvalidValidationReport: WorkspaceValidationReport = {
|
|
242
|
+
workspace: {
|
|
243
|
+
path: '/path/to/workspace',
|
|
244
|
+
name: 'test-workspace',
|
|
245
|
+
packageCount: 5,
|
|
246
|
+
catalogCount: 2,
|
|
247
|
+
},
|
|
248
|
+
isValid: false,
|
|
249
|
+
errors: ['Missing required catalog "default"', 'Invalid version range for package "broken-pkg"'],
|
|
250
|
+
warnings: ['Consider updating deprecated package "old-package"'],
|
|
251
|
+
recommendations: ['Fix the errors above before proceeding'],
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
describe('OutputFormatter', () => {
|
|
255
|
+
// Use consistent settings for snapshot tests: no color for readable snapshots
|
|
256
|
+
const formats = ['table', 'json', 'yaml', 'minimal'] as const
|
|
257
|
+
|
|
258
|
+
describe('formatOutdatedReport', () => {
|
|
259
|
+
formats.forEach((format) => {
|
|
260
|
+
it(`should format outdated report as ${format}`, () => {
|
|
261
|
+
const formatter = new OutputFormatter(format, false)
|
|
262
|
+
const result = formatter.formatOutdatedReport(mockOutdatedReport)
|
|
263
|
+
expect(result).toMatchSnapshot()
|
|
264
|
+
})
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
it('should format empty outdated report', () => {
|
|
268
|
+
const formatter = new OutputFormatter('table', false)
|
|
269
|
+
const result = formatter.formatOutdatedReport(mockEmptyOutdatedReport)
|
|
270
|
+
expect(result).toMatchSnapshot()
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
it('should format outdated report minimal with aligned columns', () => {
|
|
274
|
+
const formatter = new OutputFormatter('minimal', false)
|
|
275
|
+
const result = formatter.formatOutdatedReport(mockOutdatedReport)
|
|
276
|
+
// Verify alignment by checking that all lines have similar structure
|
|
277
|
+
const lines = result.split('\n')
|
|
278
|
+
expect(lines.length).toBe(3)
|
|
279
|
+
lines.forEach((line) => {
|
|
280
|
+
expect(line).toContain('→')
|
|
281
|
+
})
|
|
282
|
+
})
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
describe('formatUpdatePlan', () => {
|
|
286
|
+
formats.forEach((format) => {
|
|
287
|
+
it(`should format update plan as ${format}`, () => {
|
|
288
|
+
const formatter = new OutputFormatter(format, false)
|
|
289
|
+
const result = formatter.formatUpdatePlan(mockUpdatePlan)
|
|
290
|
+
expect(result).toMatchSnapshot()
|
|
291
|
+
})
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
it('should format update plan with conflicts', () => {
|
|
295
|
+
const formatter = new OutputFormatter('table', false)
|
|
296
|
+
const result = formatter.formatUpdatePlan(mockUpdatePlanWithConflicts)
|
|
297
|
+
expect(result).toMatchSnapshot()
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
it('should handle empty update plan', () => {
|
|
301
|
+
const emptyPlan: UpdatePlan = {
|
|
302
|
+
workspace: mockWorkspace,
|
|
303
|
+
totalUpdates: 0,
|
|
304
|
+
hasConflicts: false,
|
|
305
|
+
conflicts: [],
|
|
306
|
+
updates: [],
|
|
307
|
+
}
|
|
308
|
+
const formatter = new OutputFormatter('table', false)
|
|
309
|
+
const result = formatter.formatUpdatePlan(emptyPlan)
|
|
310
|
+
expect(result).toMatchSnapshot()
|
|
311
|
+
})
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
describe('formatUpdateResult', () => {
|
|
315
|
+
formats.forEach((format) => {
|
|
316
|
+
it(`should format update result as ${format}`, () => {
|
|
317
|
+
const formatter = new OutputFormatter(format, false)
|
|
318
|
+
const result = formatter.formatUpdateResult(mockUpdateResult)
|
|
319
|
+
expect(result).toMatchSnapshot()
|
|
320
|
+
})
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
it('should format failed update result', () => {
|
|
324
|
+
const formatter = new OutputFormatter('table', false)
|
|
325
|
+
const result = formatter.formatUpdateResult(mockFailedUpdateResult)
|
|
326
|
+
expect(result).toMatchSnapshot()
|
|
327
|
+
})
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
describe('formatWorkspaceInfo', () => {
|
|
331
|
+
formats.forEach((format) => {
|
|
332
|
+
it(`should format workspace info as ${format}`, () => {
|
|
333
|
+
const formatter = new OutputFormatter(format, false)
|
|
334
|
+
const result = formatter.formatWorkspaceInfo(mockWorkspaceInfo)
|
|
335
|
+
expect(result).toMatchSnapshot()
|
|
336
|
+
})
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
it('should format workspace info without catalog names', () => {
|
|
340
|
+
const infoWithoutCatalogs: WorkspaceInfo = {
|
|
341
|
+
...mockWorkspaceInfo,
|
|
342
|
+
catalogNames: [],
|
|
343
|
+
}
|
|
344
|
+
const formatter = new OutputFormatter('table', false)
|
|
345
|
+
const result = formatter.formatWorkspaceInfo(infoWithoutCatalogs)
|
|
346
|
+
expect(result).toMatchSnapshot()
|
|
347
|
+
})
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
describe('formatWorkspaceStats', () => {
|
|
351
|
+
formats.forEach((format) => {
|
|
352
|
+
it(`should format workspace stats as ${format}`, () => {
|
|
353
|
+
const formatter = new OutputFormatter(format, false)
|
|
354
|
+
const result = formatter.formatWorkspaceStats(mockWorkspaceStats)
|
|
355
|
+
expect(result).toMatchSnapshot()
|
|
356
|
+
})
|
|
357
|
+
})
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
describe('formatValidationReport', () => {
|
|
361
|
+
formats.forEach((format) => {
|
|
362
|
+
it(`should format valid report as ${format}`, () => {
|
|
363
|
+
const formatter = new OutputFormatter(format, false)
|
|
364
|
+
const result = formatter.formatValidationReport(mockValidationReport)
|
|
365
|
+
expect(result).toMatchSnapshot()
|
|
366
|
+
})
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
it('should format invalid validation report', () => {
|
|
370
|
+
const formatter = new OutputFormatter('table', false)
|
|
371
|
+
const result = formatter.formatValidationReport(mockInvalidValidationReport)
|
|
372
|
+
expect(result).toMatchSnapshot()
|
|
373
|
+
})
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
describe('formatMessage', () => {
|
|
377
|
+
it('should format success message', () => {
|
|
378
|
+
const formatter = new OutputFormatter('table', false)
|
|
379
|
+
const result = formatter.formatMessage('Operation completed', 'success')
|
|
380
|
+
expect(result).toBe('Operation completed')
|
|
381
|
+
})
|
|
382
|
+
|
|
383
|
+
it('should format error message', () => {
|
|
384
|
+
const formatter = new OutputFormatter('table', false)
|
|
385
|
+
const result = formatter.formatMessage('Something went wrong', 'error')
|
|
386
|
+
expect(result).toBe('Something went wrong')
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
it('should format warning message', () => {
|
|
390
|
+
const formatter = new OutputFormatter('table', false)
|
|
391
|
+
const result = formatter.formatMessage('Please review', 'warning')
|
|
392
|
+
expect(result).toBe('Please review')
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
it('should format info message', () => {
|
|
396
|
+
const formatter = new OutputFormatter('table', false)
|
|
397
|
+
const result = formatter.formatMessage('FYI', 'info')
|
|
398
|
+
expect(result).toBe('FYI')
|
|
399
|
+
})
|
|
400
|
+
})
|
|
401
|
+
|
|
402
|
+
describe('constructor defaults', () => {
|
|
403
|
+
it('should use table format by default', () => {
|
|
404
|
+
const formatter = new OutputFormatter()
|
|
405
|
+
const result = formatter.formatWorkspaceInfo(mockWorkspaceInfo)
|
|
406
|
+
// Table format includes box-drawing characters
|
|
407
|
+
expect(result).toContain('│')
|
|
408
|
+
})
|
|
409
|
+
|
|
410
|
+
it('should accept color option', () => {
|
|
411
|
+
// Note: Color output depends on terminal capabilities and environment
|
|
412
|
+
// We test that the color option is accepted without error
|
|
413
|
+
const formatterWithColor = new OutputFormatter('table', true)
|
|
414
|
+
const formatterWithoutColor = new OutputFormatter('table', false)
|
|
415
|
+
const resultWithColor = formatterWithColor.formatMessage('test', 'success')
|
|
416
|
+
const resultWithoutColor = formatterWithoutColor.formatMessage('test', 'success')
|
|
417
|
+
// Both should produce valid output
|
|
418
|
+
expect(resultWithColor).toBe('test')
|
|
419
|
+
expect(resultWithoutColor).toBe('test')
|
|
420
|
+
})
|
|
421
|
+
})
|
|
422
|
+
|
|
423
|
+
describe('JSON output', () => {
|
|
424
|
+
it('should produce valid JSON', () => {
|
|
425
|
+
const formatter = new OutputFormatter('json', false)
|
|
426
|
+
const result = formatter.formatOutdatedReport(mockOutdatedReport)
|
|
427
|
+
expect(() => JSON.parse(result)).not.toThrow()
|
|
428
|
+
})
|
|
429
|
+
|
|
430
|
+
it('should include all data in JSON', () => {
|
|
431
|
+
const formatter = new OutputFormatter('json', false)
|
|
432
|
+
const result = formatter.formatOutdatedReport(mockOutdatedReport)
|
|
433
|
+
const parsed = JSON.parse(result)
|
|
434
|
+
expect(parsed.workspace.name).toBe('test-workspace')
|
|
435
|
+
expect(parsed.totalOutdated).toBe(3)
|
|
436
|
+
expect(parsed.catalogs).toHaveLength(1)
|
|
437
|
+
})
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
describe('YAML output', () => {
|
|
441
|
+
it('should produce valid YAML structure', () => {
|
|
442
|
+
const formatter = new OutputFormatter('yaml', false)
|
|
443
|
+
const result = formatter.formatOutdatedReport(mockOutdatedReport)
|
|
444
|
+
expect(result).toContain('workspace:')
|
|
445
|
+
expect(result).toContain('name: test-workspace')
|
|
446
|
+
})
|
|
447
|
+
})
|
|
448
|
+
})
|