@zohodesk/unit-testing-framework 0.0.30-experimental → 0.0.32-experimental

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.
@@ -0,0 +1,473 @@
1
+ "use strict";
2
+
3
+ var _fs = _interopRequireDefault(require("fs"));
4
+ var _path = _interopRequireDefault(require("path"));
5
+ var _url = require("url");
6
+ var _pipelineSummary = require("../pipeline-summary.js");
7
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
8
+ const _dirname = _path.default.dirname((0, _url.fileURLToPath)(import.meta.url));
9
+ const TEST_OUTPUT_DIR = _path.default.join(_dirname, '../../__temp__');
10
+ describe('PipelineSummaryGenerator', () => {
11
+ let summary;
12
+ beforeEach(() => {
13
+ summary = (0, _pipelineSummary.createPipelineSummary)('TestStage', {
14
+ outputPath: _path.default.join(TEST_OUTPUT_DIR, 'test-summary.json'),
15
+ webUrl: 'https://example.com/report'
16
+ });
17
+
18
+ // Create temp directory
19
+ if (!_fs.default.existsSync(TEST_OUTPUT_DIR)) {
20
+ _fs.default.mkdirSync(TEST_OUTPUT_DIR, {
21
+ recursive: true
22
+ });
23
+ }
24
+ });
25
+ afterEach(() => {
26
+ // Clean up
27
+ if (_fs.default.existsSync(TEST_OUTPUT_DIR)) {
28
+ _fs.default.rmSync(TEST_OUTPUT_DIR, {
29
+ recursive: true,
30
+ force: true
31
+ });
32
+ }
33
+ });
34
+
35
+ // ──────────────────────────────────────────────────────────────────────
36
+ // SUCCESS RESPONSE TESTS
37
+ // ──────────────────────────────────────────────────────────────────────
38
+
39
+ describe('success()', () => {
40
+ test('should create success response with defaults', () => {
41
+ const response = summary.success();
42
+ expect(response.status).toBe('SUCCESS');
43
+ expect(response.message).toBe('TestStage completed successfully');
44
+ expect(response.web_url).toBe('https://example.com/report');
45
+ });
46
+ test('should create success response with custom message', () => {
47
+ const response = summary.success({
48
+ message: 'All tests passed'
49
+ });
50
+ expect(response.status).toBe('SUCCESS');
51
+ expect(response.message).toBe('All tests passed');
52
+ });
53
+ test('should create success response with custom web_url', () => {
54
+ const response = summary.success({
55
+ webUrl: 'https://example.com/custom-report'
56
+ });
57
+ expect(response.web_url).toBe('https://example.com/custom-report');
58
+ });
59
+ test('should not contain error field in success response', () => {
60
+ const response = summary.success();
61
+ expect(response).not.toHaveProperty('error');
62
+ expect(response).not.toHaveProperty('result');
63
+ });
64
+ });
65
+
66
+ // ──────────────────────────────────────────────────────────────────────
67
+ // FAILURE RESPONSE TESTS
68
+ // ──────────────────────────────────────────────────────────────────────
69
+
70
+ describe('failure()', () => {
71
+ test('should create failure response with defaults', () => {
72
+ const response = summary.failure();
73
+ expect(response.status).toBe('FAILURE');
74
+ expect(response.message).toBe('TestStage failed');
75
+ expect(response.error.code).toBe('UNKNOWN_ERROR');
76
+ expect(response.error.category).toBe(_pipelineSummary.ERROR_CATEGORIES.TOOL);
77
+ expect(Array.isArray(response.error.errors)).toBe(true);
78
+ });
79
+ test('should create failure response with custom error details', () => {
80
+ const response = summary.failure({
81
+ message: 'Database connection failed',
82
+ errorCode: 'DB_CONN_ERROR',
83
+ errorCategory: _pipelineSummary.ERROR_CATEGORIES.ENVIRONMENT,
84
+ errorMessage: 'Cannot connect to database',
85
+ errors: ['Connection timeout', 'Host unreachable']
86
+ });
87
+ expect(response.status).toBe('FAILURE');
88
+ expect(response.message).toBe('Database connection failed');
89
+ expect(response.error.code).toBe('DB_CONN_ERROR');
90
+ expect(response.error.category).toBe(_pipelineSummary.ERROR_CATEGORIES.ENVIRONMENT);
91
+ expect(response.error.message).toBe('Cannot connect to database');
92
+ expect(response.error.errors).toHaveLength(2);
93
+ });
94
+ test('should accept single error and convert to array', () => {
95
+ const response = summary.failure({
96
+ errors: 'Single error message'
97
+ });
98
+ expect(Array.isArray(response.error.errors)).toBe(true);
99
+ expect(response.error.errors[0]).toBe('Single error message');
100
+ });
101
+ test('should include result field if provided', () => {
102
+ const response = summary.failure({
103
+ result: [{
104
+ key: 'value'
105
+ }]
106
+ });
107
+ expect(response.result).toEqual([{
108
+ key: 'value'
109
+ }]);
110
+ });
111
+ test('should not include empty result field', () => {
112
+ const response = summary.failure({
113
+ result: []
114
+ });
115
+ expect(response).not.toHaveProperty('result');
116
+ });
117
+ test('should validate and warn for invalid error category', () => {
118
+ const response = summary.failure({
119
+ errorCategory: 'invalid_category'
120
+ });
121
+ expect(response.error.category).toBe(_pipelineSummary.ERROR_CATEGORIES.TOOL);
122
+ });
123
+ });
124
+
125
+ // ──────────────────────────────────────────────────────────────────────
126
+ // TEST FAILURE TESTS
127
+ // ──────────────────────────────────────────────────────────────────────
128
+
129
+ describe('testFailure()', () => {
130
+ test('should handle test failure without results object', () => {
131
+ const response = summary.testFailure({
132
+ message: 'Tests failed'
133
+ });
134
+ expect(response.status).toBe('FAILURE');
135
+ expect(response.error.code).toBe('TEST_FAILURE');
136
+ expect(response.error.category).toBe(_pipelineSummary.ERROR_CATEGORIES.CODE);
137
+ });
138
+ test('should extract failed test information from jest results', () => {
139
+ const testResults = {
140
+ numTotalTests: 10,
141
+ numFailedTests: 2,
142
+ numPassedTests: 8,
143
+ numSkippedTests: 0,
144
+ testResults: [{
145
+ name: 'test-file-1.js',
146
+ numFailingTests: 1,
147
+ assertionResults: [{
148
+ title: 'Test 1',
149
+ fullName: 'Suite › Test 1',
150
+ status: 'failed',
151
+ failureMessages: ['Expected true to be false']
152
+ }]
153
+ }, {
154
+ name: 'test-file-2.js',
155
+ numFailingTests: 1,
156
+ assertionResults: [{
157
+ title: 'Test 2',
158
+ fullName: 'Suite › Test 2',
159
+ status: 'failed',
160
+ failureMessages: ['Timeout after 5000ms']
161
+ }]
162
+ }]
163
+ };
164
+ const response = summary.testFailure({
165
+ testResults
166
+ });
167
+ expect(response.error.errors).toHaveLength(2);
168
+ expect(response.error.errors[0].test).toBe('Suite › Test 1');
169
+ expect(response.error.errors[0].file).toBe('test-file-1.js');
170
+ expect(response.result[0].failedTests).toBe(2);
171
+ });
172
+ test('should include test statistics in result', () => {
173
+ const testResults = {
174
+ numTotalTests: 50,
175
+ numFailedTests: 5,
176
+ numPassedTests: 45,
177
+ numSkippedTests: 0,
178
+ testResults: []
179
+ };
180
+ const response = summary.testFailure({
181
+ testResults
182
+ });
183
+ expect(response.result[0]).toEqual({
184
+ totalTests: 50,
185
+ failedTests: 5,
186
+ passedTests: 45,
187
+ skippedTests: 0
188
+ });
189
+ });
190
+ });
191
+
192
+ // ──────────────────────────────────────────────────────────────────────
193
+ // TOOL ERROR TESTS
194
+ // ──────────────────────────────────────────────────────────────────────
195
+
196
+ describe('toolError()', () => {
197
+ test('should handle error object', () => {
198
+ const error = new Error('Module not found');
199
+ error.stack = 'Error: Module not found\n at line 10';
200
+ const response = summary.toolError({
201
+ toolName: 'jest',
202
+ error,
203
+ exitCode: 1
204
+ });
205
+ expect(response.status).toBe('FAILURE');
206
+ expect(response.error.code).toBe('JEST_EXECUTION_ERROR');
207
+ expect(response.error.category).toBe(_pipelineSummary.ERROR_CATEGORIES.TOOL);
208
+ expect(response.error.errors[0].tool).toBe('jest');
209
+ expect(response.error.errors[0].stack).toBeDefined();
210
+ });
211
+ test('should handle string error message', () => {
212
+ const response = summary.toolError({
213
+ toolName: 'npm',
214
+ error: 'npm ERR! code ERESOLVE',
215
+ exitCode: 1
216
+ });
217
+ expect(response.error.errors[0].message).toBe('npm ERR! code ERESOLVE');
218
+ });
219
+ test('should handle missing error', () => {
220
+ const response = summary.toolError({
221
+ toolName: 'webpack',
222
+ exitCode: 127
223
+ });
224
+ expect(response.error.errors[0].message).toBeFalsy();
225
+ expect(response.error.errors[0].tool).toBe('webpack');
226
+ });
227
+ test('should format tool name in error code', () => {
228
+ const response = summary.toolError({
229
+ toolName: 'eslint',
230
+ error: 'Linting failed'
231
+ });
232
+ expect(response.error.code).toBe('ESLINT_EXECUTION_ERROR');
233
+ });
234
+ });
235
+
236
+ // ──────────────────────────────────────────────────────────────────────
237
+ // ENVIRONMENT ERROR TESTS
238
+ // ──────────────────────────────────────────────────────────────────────
239
+
240
+ describe('environmentError()', () => {
241
+ test('should create environment error response', () => {
242
+ const response = summary.environmentError({
243
+ issue: 'Node.js version too old',
244
+ requirement: 'Node.js >= 16',
245
+ suggestion: 'Update Node.js'
246
+ });
247
+ expect(response.status).toBe('FAILURE');
248
+ expect(response.error.code).toBe('ENV_SETUP_ERROR');
249
+ expect(response.error.category).toBe(_pipelineSummary.ERROR_CATEGORIES.ENVIRONMENT);
250
+ expect(response.error.errors[0].issue).toBe('Node.js version too old');
251
+ expect(response.error.errors[0].requirement).toBe('Node.js >= 16');
252
+ expect(response.error.errors[0].suggestion).toBe('Update Node.js');
253
+ });
254
+ test('should handle partial environment error details', () => {
255
+ const response = summary.environmentError({
256
+ issue: 'Missing DATABASE_URL'
257
+ });
258
+ expect(response.error.errors[0]).toEqual({
259
+ issue: 'Missing DATABASE_URL'
260
+ });
261
+ });
262
+ test('should use default issue message', () => {
263
+ const response = summary.environmentError();
264
+ expect(response.error.errors[0].issue).toBe('Environment configuration issue');
265
+ });
266
+ });
267
+
268
+ // ──────────────────────────────────────────────────────────────────────
269
+ // FLAKY TEST TESTS
270
+ // ──────────────────────────────────────────────────────────────────────
271
+
272
+ describe('flakyTestFailure()', () => {
273
+ test('should create flaky test failure response', () => {
274
+ const response = summary.flakyTestFailure({
275
+ testNames: ['Test A', 'Test B'],
276
+ attemptNumber: 1,
277
+ maxAttempts: 3
278
+ });
279
+ expect(response.status).toBe('FAILURE');
280
+ expect(response.error.code).toBe('FLAKY_TEST_FAILURE');
281
+ expect(response.error.category).toBe(_pipelineSummary.ERROR_CATEGORIES.FLAKY);
282
+ expect(response.error.errors).toHaveLength(2);
283
+ expect(response.result[0].attemptNumber).toBe(1);
284
+ expect(response.result[0].maxAttempts).toBe(3);
285
+ });
286
+ test('should include attempt information in message', () => {
287
+ const response = summary.flakyTestFailure({
288
+ testNames: ['Flaky Test'],
289
+ attemptNumber: 2,
290
+ maxAttempts: 3
291
+ });
292
+ expect(response.message).toContain('attempt 2/3');
293
+ });
294
+ test('should handle custom message override', () => {
295
+ const response = summary.flakyTestFailure({
296
+ testNames: ['Test'],
297
+ message: 'Custom flaky message'
298
+ });
299
+ expect(response.message).toBe('Custom flaky message');
300
+ });
301
+ test('should use default attempt values', () => {
302
+ const response = summary.flakyTestFailure({
303
+ testNames: ['Test']
304
+ });
305
+ expect(response.result[0].attemptNumber).toBe(1);
306
+ expect(response.result[0].maxAttempts).toBe(3);
307
+ });
308
+ });
309
+
310
+ // ──────────────────────────────────────────────────────────────────────
311
+ // FILE SAVE TESTS
312
+ // ──────────────────────────────────────────────────────────────────────
313
+
314
+ describe('saveToFile()', () => {
315
+ test('should save response to file', () => {
316
+ const response = summary.success();
317
+ const saved = summary.saveToFile(response);
318
+ expect(saved).toBe(true);
319
+ expect(_fs.default.existsSync(summary.outputPath)).toBe(true);
320
+ const content = _fs.default.readFileSync(summary.outputPath, 'utf-8');
321
+ const parsed = JSON.parse(content);
322
+ expect(parsed.status).toBe('SUCCESS');
323
+ });
324
+ test('should create parent directories if not exist', () => {
325
+ const customPath = _path.default.join(TEST_OUTPUT_DIR, 'nested/deep/summary.json');
326
+ const customSummary = (0, _pipelineSummary.createPipelineSummary)('Test', {
327
+ outputPath: customPath
328
+ });
329
+ const response = customSummary.success();
330
+ const saved = customSummary.saveToFile(response);
331
+ expect(saved).toBe(true);
332
+ expect(_fs.default.existsSync(customPath)).toBe(true);
333
+ });
334
+ test('should handle missing output path', () => {
335
+ const noPathSummary = (0, _pipelineSummary.createPipelineSummary)('Test');
336
+ const response = noPathSummary.success();
337
+ const saved = noPathSummary.saveToFile(response);
338
+ expect(saved).toBe(false);
339
+ });
340
+ test('should format JSON with indentation', () => {
341
+ const response = summary.success();
342
+ summary.saveToFile(response);
343
+ const content = _fs.default.readFileSync(summary.outputPath, 'utf-8');
344
+ expect(content).toContain('\n');
345
+ expect(content).toContain(' ');
346
+ });
347
+ test('should override output path', () => {
348
+ const customPath = _path.default.join(TEST_OUTPUT_DIR, 'custom.json');
349
+ const response = summary.success();
350
+ summary.saveToFile(response, customPath);
351
+ expect(_fs.default.existsSync(customPath)).toBe(true);
352
+ });
353
+ });
354
+
355
+ // ──────────────────────────────────────────────────────────────────────
356
+ // SAVE AND RETURN TESTS
357
+ // ──────────────────────────────────────────────────────────────────────
358
+
359
+ describe('saveAndReturn()', () => {
360
+ test('should return response and save status', () => {
361
+ const response = summary.success();
362
+ const {
363
+ response: retResponse,
364
+ saved
365
+ } = summary.saveAndReturn(response);
366
+ expect(retResponse).toEqual(response);
367
+ expect(saved).toBe(true);
368
+ expect(_fs.default.existsSync(summary.outputPath)).toBe(true);
369
+ });
370
+ test('should indicate save failure', () => {
371
+ const noPathSummary = (0, _pipelineSummary.createPipelineSummary)('Test');
372
+ const response = noPathSummary.success();
373
+ const {
374
+ saved
375
+ } = noPathSummary.saveAndReturn(response);
376
+ expect(saved).toBe(false);
377
+ });
378
+ });
379
+
380
+ // ──────────────────────────────────────────────────────────────────────
381
+ // EDGE CASES
382
+ // ──────────────────────────────────────────────────────────────────────
383
+
384
+ describe('edge cases', () => {
385
+ test('should handle very long error messages', () => {
386
+ const longMessage = 'x'.repeat(10000);
387
+ const response = summary.failure({
388
+ errorMessage: longMessage
389
+ });
390
+ expect(response.error.message).toBe(longMessage);
391
+ });
392
+ test('should handle special characters in messages', () => {
393
+ const response = summary.failure({
394
+ message: 'Failed: "test" & <script>alert(1)</script>'
395
+ });
396
+ expect(response.message).toContain('<script>');
397
+ });
398
+ test('should handle null/undefined in optional fields', () => {
399
+ const response = summary.failure({
400
+ errorMessage: null,
401
+ errors: null
402
+ });
403
+ expect(response.error.message).toBeNull();
404
+ expect(Array.isArray(response.error.errors)).toBe(true);
405
+ });
406
+ test('should handle errors with circular references', () => {
407
+ const circularObj = {
408
+ name: 'test'
409
+ };
410
+ circularObj.self = circularObj;
411
+
412
+ // Should not throw
413
+ const response = summary.failure({
414
+ errors: [circularObj]
415
+ });
416
+ expect(response.error.errors).toBeDefined();
417
+ });
418
+ test('should handle unicode characters', () => {
419
+ const response = summary.failure({
420
+ errorMessage: '测试失败 🔥 エラーが発生しました'
421
+ });
422
+ expect(response.error.message).toContain('测试失败');
423
+ expect(response.error.message).toContain('🔥');
424
+ });
425
+ test('should handle multiple consecutive saves', () => {
426
+ const response1 = summary.success({
427
+ message: 'First'
428
+ });
429
+ const response2 = summary.failure({
430
+ message: 'Second'
431
+ });
432
+ summary.saveToFile(response1);
433
+ summary.saveToFile(response2);
434
+ const content = _fs.default.readFileSync(summary.outputPath, 'utf-8');
435
+ const parsed = JSON.parse(content);
436
+ expect(parsed.message).toBe('Second');
437
+ });
438
+ });
439
+
440
+ // ──────────────────────────────────────────────────────────────────────
441
+ // ERROR CATEGORIES EXPORT
442
+ // ──────────────────────────────────────────────────────────────────────
443
+
444
+ describe('ERROR_CATEGORIES', () => {
445
+ test('should export valid error categories', () => {
446
+ expect(_pipelineSummary.ERROR_CATEGORIES.CODE).toBe('code');
447
+ expect(_pipelineSummary.ERROR_CATEGORIES.ENVIRONMENT).toBe('environment');
448
+ expect(_pipelineSummary.ERROR_CATEGORIES.FLAKY).toBe('flaky');
449
+ expect(_pipelineSummary.ERROR_CATEGORIES.TOOL).toBe('tool');
450
+ });
451
+ });
452
+
453
+ // ──────────────────────────────────────────────────────────────────────
454
+ // FACTORY FUNCTION TESTS
455
+ // ──────────────────────────────────────────────────────────────────────
456
+
457
+ describe('createPipelineSummary()', () => {
458
+ test('should create instance with stage name', () => {
459
+ const instance = (0, _pipelineSummary.createPipelineSummary)('MyStage');
460
+ expect(instance).toBeInstanceOf(_pipelineSummary.PipelineSummaryGenerator);
461
+ const response = instance.success();
462
+ expect(response.message).toContain('MyStage');
463
+ });
464
+ test('should merge options correctly', () => {
465
+ const instance = (0, _pipelineSummary.createPipelineSummary)('Build', {
466
+ outputPath: '/path/to/file.json',
467
+ webUrl: 'https://example.com'
468
+ });
469
+ const response = instance.success();
470
+ expect(response.web_url).toBe('https://example.com');
471
+ });
472
+ });
473
+ });