@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.
- package/README.md +2 -0
- package/build/src/runner/__tests__/jest-runner.test.js +115 -0
- package/build/src/runner/jest-runner.js +135 -15
- package/build/src/runner/runner-base.js +3 -3
- package/build/src/utils/__tests__/pipeline-summary-directory.test.js +248 -0
- package/build/src/utils/__tests__/pipeline-summary.test.js +473 -0
- package/build/src/utils/pipeline-summary.js +359 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
A modular, Jest-based unit testing framework designed to plug into existing CLI pipelines. Runs Jest **programmatically** via `@jest/core` (no shell execution), ships with sensible defaults, HTML report generation via `jest-html-reporter`, and automatic Jest globals injection.
|
|
4
4
|
|
|
5
|
+
For a command-first onboarding guide (npm commands, flags, and config options), see [UNIT_TEST_TOOL_TRYOUT.md](UNIT_TEST_TOOL_TRYOUT.md).
|
|
6
|
+
|
|
5
7
|
---
|
|
6
8
|
|
|
7
9
|
## Folder Structure
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _jestRunner = _interopRequireDefault(require("../jest-runner.js"));
|
|
4
|
+
var _pipelineSummary = require("../../utils/pipeline-summary.js");
|
|
5
|
+
var _fs = _interopRequireDefault(require("fs"));
|
|
6
|
+
var _path = _interopRequireDefault(require("path"));
|
|
7
|
+
var _url = require("url");
|
|
8
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
+
const _dirname = _path.default.dirname((0, _url.fileURLToPath)(import.meta.url));
|
|
10
|
+
const TEST_OUTPUT_DIR = _path.default.join(_dirname, '../../__temp__');
|
|
11
|
+
describe('createJestRunner with Pipeline Summary', () => {
|
|
12
|
+
let tempDir;
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
tempDir = _path.default.join(TEST_OUTPUT_DIR, 'jest-runner-test');
|
|
15
|
+
if (!_fs.default.existsSync(tempDir)) {
|
|
16
|
+
_fs.default.mkdirSync(tempDir, {
|
|
17
|
+
recursive: true
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
if (_fs.default.existsSync(tempDir)) {
|
|
23
|
+
_fs.default.rmSync(tempDir, {
|
|
24
|
+
recursive: true,
|
|
25
|
+
force: true
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
test('should return object with testResults, summary, and success flag', async () => {
|
|
30
|
+
const summaryPath = _path.default.join(tempDir, 'summary.json');
|
|
31
|
+
const result = await (0, _jestRunner.default)({
|
|
32
|
+
projectRoot: process.cwd(),
|
|
33
|
+
summaryPath,
|
|
34
|
+
testFiles: 'src/utils/__tests__/pipeline-summary.test.js'
|
|
35
|
+
});
|
|
36
|
+
expect(result).toHaveProperty('testResults');
|
|
37
|
+
expect(result).toHaveProperty('summary');
|
|
38
|
+
expect(result).toHaveProperty('success');
|
|
39
|
+
expect(typeof result.success).toBe('boolean');
|
|
40
|
+
});
|
|
41
|
+
test('should create summary file at specified path', async () => {
|
|
42
|
+
const summaryPath = _path.default.join(tempDir, 'test-summary.json');
|
|
43
|
+
await (0, _jestRunner.default)({
|
|
44
|
+
projectRoot: process.cwd(),
|
|
45
|
+
summaryPath,
|
|
46
|
+
testFiles: 'src/utils/__tests__/pipeline-summary.test.js'
|
|
47
|
+
});
|
|
48
|
+
expect(_fs.default.existsSync(summaryPath)).toBe(true);
|
|
49
|
+
const content = JSON.parse(_fs.default.readFileSync(summaryPath, 'utf-8'));
|
|
50
|
+
expect(content.status).toMatch(/^(SUCCESS|FAILURE)$/);
|
|
51
|
+
});
|
|
52
|
+
test('should generate SUCCESS summary when tests pass', async () => {
|
|
53
|
+
const summaryPath = _path.default.join(tempDir, 'success-summary.json');
|
|
54
|
+
const result = await (0, _jestRunner.default)({
|
|
55
|
+
projectRoot: process.cwd(),
|
|
56
|
+
summaryPath,
|
|
57
|
+
testFiles: 'src/utils/__tests__/pipeline-summary.test.js'
|
|
58
|
+
});
|
|
59
|
+
if (result.success) {
|
|
60
|
+
expect(result.summary.status).toBe('SUCCESS');
|
|
61
|
+
expect(result.summary.message).toContain('passed');
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
test('should include web_url in summary when provided', async () => {
|
|
65
|
+
const summaryPath = _path.default.join(tempDir, 'url-summary.json');
|
|
66
|
+
const webUrl = 'https://ci.example.com/build/123';
|
|
67
|
+
const result = await (0, _jestRunner.default)({
|
|
68
|
+
projectRoot: process.cwd(),
|
|
69
|
+
summaryPath,
|
|
70
|
+
webUrl,
|
|
71
|
+
testFiles: 'src/utils/__tests__/pipeline-summary.test.js'
|
|
72
|
+
});
|
|
73
|
+
expect(result.summary.web_url).toBe(webUrl);
|
|
74
|
+
});
|
|
75
|
+
test('should handle missing test files gracefully', async () => {
|
|
76
|
+
const summaryPath = _path.default.join(tempDir, 'error-summary.json');
|
|
77
|
+
const result = await (0, _jestRunner.default)({
|
|
78
|
+
projectRoot: process.cwd(),
|
|
79
|
+
summaryPath,
|
|
80
|
+
testFiles: 'nonexistent-file.test.js'
|
|
81
|
+
});
|
|
82
|
+
expect(_fs.default.existsSync(summaryPath)).toBe(true);
|
|
83
|
+
const content = JSON.parse(_fs.default.readFileSync(summaryPath, 'utf-8'));
|
|
84
|
+
expect(content.status).toBe('FAILURE');
|
|
85
|
+
});
|
|
86
|
+
test('should use default summary path if not provided', async () => {
|
|
87
|
+
const result = await (0, _jestRunner.default)({
|
|
88
|
+
projectRoot: process.cwd(),
|
|
89
|
+
testFiles: 'src/utils/__tests__/pipeline-summary.test.js'
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Should create reports directory and test-summary.json
|
|
93
|
+
expect(result.summary).toBeDefined();
|
|
94
|
+
expect(result.summary.status).toMatch(/^(SUCCESS|FAILURE)$/);
|
|
95
|
+
});
|
|
96
|
+
test('summary should have valid error structure on failure', async () => {
|
|
97
|
+
const summaryPath = _path.default.join(tempDir, 'error-check-summary.json');
|
|
98
|
+
const result = await (0, _jestRunner.default)({
|
|
99
|
+
projectRoot: process.cwd(),
|
|
100
|
+
summaryPath,
|
|
101
|
+
testFiles: 'nonexistent.test.js'
|
|
102
|
+
});
|
|
103
|
+
if (!result.success) {
|
|
104
|
+
const {
|
|
105
|
+
summary
|
|
106
|
+
} = result;
|
|
107
|
+
expect(summary.status).toBe('FAILURE');
|
|
108
|
+
expect(summary.error).toBeDefined();
|
|
109
|
+
expect(summary.error.code).toBeDefined();
|
|
110
|
+
expect(summary.error.category).toMatch(/^(code|environment|flaky|tool)$/);
|
|
111
|
+
expect(summary.error.message).toBeDefined();
|
|
112
|
+
expect(Array.isArray(summary.error.errors)).toBe(true);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
});
|
|
@@ -8,10 +8,11 @@ var _fs = _interopRequireDefault(require("fs"));
|
|
|
8
8
|
var _path = _interopRequireDefault(require("path"));
|
|
9
9
|
var _runnerBase = require("./runner-base.js");
|
|
10
10
|
var _configLoader = require("../config/config-loader.js");
|
|
11
|
+
var _pipelineSummary = require("../utils/pipeline-summary.js");
|
|
11
12
|
var _logger = require("../utils/logger.js");
|
|
12
13
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
|
-
function resolveTestFiles(
|
|
14
|
-
const files =
|
|
14
|
+
function resolveTestFiles(rawPattern, projectRoot) {
|
|
15
|
+
const files = rawPattern.split(',').map(f => f.trim()).filter(Boolean);
|
|
15
16
|
const resolved = [];
|
|
16
17
|
for (const file of files) {
|
|
17
18
|
const abs = _path.default.resolve(projectRoot, file);
|
|
@@ -32,21 +33,140 @@ async function createJestRunner(options = {}) {
|
|
|
32
33
|
testFiles,
|
|
33
34
|
testPathPattern,
|
|
34
35
|
configPath,
|
|
35
|
-
watch = false
|
|
36
|
+
watch = false,
|
|
37
|
+
summaryPath = _path.default.join(projectRoot, 'test-slices', 'unit-test', 'unit_reports', 'ut-test-summary.json'),
|
|
38
|
+
webUrl = process.env.CI_BUILD_URL || ''
|
|
36
39
|
} = options;
|
|
37
40
|
|
|
38
|
-
// ──
|
|
39
|
-
|
|
41
|
+
// ── 0. Setup pipeline summary (mandatory) ──────────────────────
|
|
42
|
+
_logger.Logger.info(`Summary path: ${summaryPath}`);
|
|
43
|
+
const summary = (0, _pipelineSummary.createPipelineSummary)('JestRunner', {
|
|
44
|
+
outputPath: summaryPath,
|
|
45
|
+
webUrl
|
|
46
|
+
});
|
|
47
|
+
try {
|
|
48
|
+
// ── 1. Resolve config (auto-discover / configPath / default) ──
|
|
49
|
+
const config = await (0, _configLoader.resolveConfig)(projectRoot, configPath);
|
|
50
|
+
|
|
51
|
+
// ── 2. Resolve test file pattern ──────────────────────────────
|
|
52
|
+
const rawPattern = testFiles || testPathPattern;
|
|
53
|
+
let resolvedPaths = null;
|
|
54
|
+
if (rawPattern) {
|
|
55
|
+
// Normalize: split on comma, resolve each to absolute, keep only files that exist.
|
|
56
|
+
resolvedPaths = resolveTestFiles(rawPattern, projectRoot);
|
|
57
|
+
}
|
|
40
58
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
59
|
+
// ── 3. Build argv & run Jest ──────────────────────────────────
|
|
60
|
+
const argv = (0, _runnerBase.buildArgv)(config, {
|
|
61
|
+
// Pass explicit files to Jest positional args to avoid testMatch glob filtering.
|
|
62
|
+
testPathPattern: resolvedPaths,
|
|
63
|
+
watch,
|
|
64
|
+
projectRoot
|
|
65
|
+
});
|
|
66
|
+
const testResults = await (0, _runnerBase.executeJest)(argv, projectRoot);
|
|
44
67
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
68
|
+
// ── 4. Process results and generate summary ───────────────────
|
|
69
|
+
if (!testResults) {
|
|
70
|
+
const response = summary.toolError({
|
|
71
|
+
toolName: 'jest',
|
|
72
|
+
error: 'No test results returned',
|
|
73
|
+
exitCode: 1
|
|
74
|
+
});
|
|
75
|
+
const saved = summary.saveToFile(response);
|
|
76
|
+
if (!saved) {
|
|
77
|
+
_logger.Logger.warn('⚠ Warning: Error summary file could not be saved');
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
testResults: null,
|
|
81
|
+
summary: response,
|
|
82
|
+
success: false
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (testResults.numFailedTests > 0) {
|
|
86
|
+
const response = summary.testFailure({
|
|
87
|
+
testResults
|
|
88
|
+
});
|
|
89
|
+
summary.saveToFile(response);
|
|
90
|
+
return {
|
|
91
|
+
testResults,
|
|
92
|
+
summary: response,
|
|
93
|
+
success: false
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (testResults.numTotalTests === 0) {
|
|
97
|
+
const response = summary.failure({
|
|
98
|
+
message: 'No tests found',
|
|
99
|
+
errorCode: 'NO_TESTS_FOUND',
|
|
100
|
+
errorCategory: 'code',
|
|
101
|
+
errorMessage: 'No test files matched the pattern'
|
|
102
|
+
});
|
|
103
|
+
summary.saveToFile(response);
|
|
104
|
+
return {
|
|
105
|
+
testResults,
|
|
106
|
+
summary: response,
|
|
107
|
+
success: false
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// All tests passed
|
|
112
|
+
const response = summary.success({
|
|
113
|
+
message: `All ${testResults.numTotalTests} tests passed (${testResults.numPassedTests} passed, ${testResults.numSkippedTests} skipped)`
|
|
114
|
+
});
|
|
115
|
+
const saved = summary.saveToFile(response);
|
|
116
|
+
if (!saved) {
|
|
117
|
+
_logger.Logger.warn('⚠ Warning: Summary file could not be saved');
|
|
118
|
+
} else {
|
|
119
|
+
_logger.Logger.success(`✓ Test Summary saved to: ${summaryPath}`);
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
testResults,
|
|
123
|
+
summary: response,
|
|
124
|
+
success: true
|
|
125
|
+
};
|
|
126
|
+
} catch (error) {
|
|
127
|
+
// ── 5. Error handling ────────────────────────────────────────
|
|
128
|
+
_logger.Logger.error(`✗ Jest execution error: ${error.message}`);
|
|
129
|
+
let response;
|
|
130
|
+
if (error.message.includes('heap') || error.message.includes('memory') || error.message.includes('ENOMEM')) {
|
|
131
|
+
response = summary.environmentError({
|
|
132
|
+
issue: 'Out of memory during test execution',
|
|
133
|
+
requirement: 'Sufficient heap memory',
|
|
134
|
+
suggestion: 'Increase Node.js heap size: NODE_OPTIONS=--max-old-space-size=4096'
|
|
135
|
+
});
|
|
136
|
+
} else if (error.message.includes('MODULE_NOT_FOUND') || error.message.includes('Cannot find')) {
|
|
137
|
+
response = summary.environmentError({
|
|
138
|
+
issue: 'Missing dependency',
|
|
139
|
+
suggestion: 'Run: npm install'
|
|
140
|
+
});
|
|
141
|
+
} else if (error.code === 'ENOENT') {
|
|
142
|
+
response = summary.failure({
|
|
143
|
+
message: 'Test file or configuration not found',
|
|
144
|
+
errorCode: 'FILE_NOT_FOUND',
|
|
145
|
+
errorCategory: 'environment',
|
|
146
|
+
errorMessage: `${error.path || 'Unknown file'} not found`
|
|
147
|
+
});
|
|
148
|
+
} else if (error.message.includes('ECONNREFUSED')) {
|
|
149
|
+
response = summary.environmentError({
|
|
150
|
+
issue: 'Connection refused to external service',
|
|
151
|
+
suggestion: 'Check if required services (database, cache) are running'
|
|
152
|
+
});
|
|
153
|
+
} else {
|
|
154
|
+
response = summary.toolError({
|
|
155
|
+
toolName: 'jest',
|
|
156
|
+
error,
|
|
157
|
+
exitCode: error.code || 1
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
const saved = summary.saveToFile(response);
|
|
161
|
+
if (saved) {
|
|
162
|
+
_logger.Logger.error(`✓ Error Summary saved to: ${summaryPath}`);
|
|
163
|
+
} else {
|
|
164
|
+
_logger.Logger.warn('⚠ Warning: Error summary file could not be saved');
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
testResults: null,
|
|
168
|
+
summary: response,
|
|
169
|
+
success: false
|
|
170
|
+
};
|
|
171
|
+
}
|
|
52
172
|
}
|
|
@@ -7,15 +7,15 @@ exports.buildArgv = buildArgv;
|
|
|
7
7
|
exports.executeJest = executeJest;
|
|
8
8
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
9
9
|
function buildArgv(config, {
|
|
10
|
-
|
|
10
|
+
testPathPattern,
|
|
11
11
|
watch = false
|
|
12
12
|
}) {
|
|
13
13
|
const argv = {
|
|
14
14
|
config: JSON.stringify(config),
|
|
15
15
|
watch
|
|
16
16
|
};
|
|
17
|
-
if (
|
|
18
|
-
argv.
|
|
17
|
+
if (testPathPattern && testPathPattern.length) {
|
|
18
|
+
argv._ = (argv._ || []).concat(testPathPattern);
|
|
19
19
|
}
|
|
20
20
|
return argv;
|
|
21
21
|
}
|
|
@@ -0,0 +1,248 @@
|
|
|
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_ROOT = _path.default.join(_dirname, '../../__temp__/dir-tests');
|
|
10
|
+
describe('Pipeline Summary - Directory Creation Edge Cases', () => {
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
// Cleanup
|
|
13
|
+
if (_fs.default.existsSync(TEST_ROOT)) {
|
|
14
|
+
_fs.default.rmSync(TEST_ROOT, {
|
|
15
|
+
recursive: true,
|
|
16
|
+
force: true
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
describe('Single level directory creation', () => {
|
|
21
|
+
test('should create single-level directory', () => {
|
|
22
|
+
const summaryPath = _path.default.join(TEST_ROOT, 'reports', 'summary.json');
|
|
23
|
+
const summary = (0, _pipelineSummary.createPipelineSummary)('Test', {
|
|
24
|
+
outputPath: summaryPath
|
|
25
|
+
});
|
|
26
|
+
const response = summary.success({
|
|
27
|
+
message: 'Test passed'
|
|
28
|
+
});
|
|
29
|
+
const saved = summary.saveToFile(response);
|
|
30
|
+
expect(saved).toBe(true);
|
|
31
|
+
expect(_fs.default.existsSync(summaryPath)).toBe(true);
|
|
32
|
+
const content = JSON.parse(_fs.default.readFileSync(summaryPath, 'utf-8'));
|
|
33
|
+
expect(content.status).toBe('SUCCESS');
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe('Multi-level directory creation', () => {
|
|
37
|
+
test('should create deeply nested directories', () => {
|
|
38
|
+
const summaryPath = _path.default.join(TEST_ROOT, 'a', 'b', 'c', 'd', 'e', 'summary.json');
|
|
39
|
+
const summary = (0, _pipelineSummary.createPipelineSummary)('Test', {
|
|
40
|
+
outputPath: summaryPath
|
|
41
|
+
});
|
|
42
|
+
const response = summary.failure({
|
|
43
|
+
message: 'Test failed',
|
|
44
|
+
errorCode: 'TEST_ERROR',
|
|
45
|
+
errorCategory: 'code',
|
|
46
|
+
errorMessage: 'Assertion failed'
|
|
47
|
+
});
|
|
48
|
+
const saved = summary.saveToFile(response);
|
|
49
|
+
expect(saved).toBe(true);
|
|
50
|
+
expect(_fs.default.existsSync(summaryPath)).toBe(true);
|
|
51
|
+
|
|
52
|
+
// Verify all parent directories were created
|
|
53
|
+
const allDirs = [_path.default.join(TEST_ROOT, 'a'), _path.default.join(TEST_ROOT, 'a', 'b'), _path.default.join(TEST_ROOT, 'a', 'b', 'c'), _path.default.join(TEST_ROOT, 'a', 'b', 'c', 'd'), _path.default.join(TEST_ROOT, 'a', 'b', 'c', 'd', 'e')];
|
|
54
|
+
allDirs.forEach(dir => {
|
|
55
|
+
expect(_fs.default.existsSync(dir)).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
describe('Directory already exists', () => {
|
|
60
|
+
test('should handle when directory already exists', () => {
|
|
61
|
+
const dir = _path.default.join(TEST_ROOT, 'existing');
|
|
62
|
+
_fs.default.mkdirSync(dir, {
|
|
63
|
+
recursive: true
|
|
64
|
+
});
|
|
65
|
+
const summaryPath = _path.default.join(dir, 'summary.json');
|
|
66
|
+
const summary = (0, _pipelineSummary.createPipelineSummary)('Test', {
|
|
67
|
+
outputPath: summaryPath
|
|
68
|
+
});
|
|
69
|
+
const response = summary.success();
|
|
70
|
+
const saved = summary.saveToFile(response);
|
|
71
|
+
expect(saved).toBe(true);
|
|
72
|
+
expect(_fs.default.existsSync(summaryPath)).toBe(true);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
describe('File overwrite', () => {
|
|
76
|
+
test('should overwrite existing summary file', () => {
|
|
77
|
+
const summaryPath = _path.default.join(TEST_ROOT, 'reports', 'summary.json');
|
|
78
|
+
_fs.default.mkdirSync(_path.default.dirname(summaryPath), {
|
|
79
|
+
recursive: true
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Write first response
|
|
83
|
+
_fs.default.writeFileSync(summaryPath, JSON.stringify({
|
|
84
|
+
version: 1
|
|
85
|
+
}), 'utf-8');
|
|
86
|
+
const summary = (0, _pipelineSummary.createPipelineSummary)('Test', {
|
|
87
|
+
outputPath: summaryPath
|
|
88
|
+
});
|
|
89
|
+
const response = summary.success({
|
|
90
|
+
message: 'New summary'
|
|
91
|
+
});
|
|
92
|
+
const saved = summary.saveToFile(response);
|
|
93
|
+
expect(saved).toBe(true);
|
|
94
|
+
const content = JSON.parse(_fs.default.readFileSync(summaryPath, 'utf-8'));
|
|
95
|
+
expect(content.status).toBe('SUCCESS');
|
|
96
|
+
expect(content.message).toBe('New summary');
|
|
97
|
+
expect(content.version).toBeUndefined();
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
describe('Invalid path handling', () => {
|
|
101
|
+
test('should handle null output path', () => {
|
|
102
|
+
const summary = (0, _pipelineSummary.createPipelineSummary)('Test', {
|
|
103
|
+
outputPath: null
|
|
104
|
+
});
|
|
105
|
+
const response = summary.success();
|
|
106
|
+
const saved = summary.saveToFile(response);
|
|
107
|
+
expect(saved).toBe(false);
|
|
108
|
+
});
|
|
109
|
+
test('should handle empty output path', () => {
|
|
110
|
+
const summary = (0, _pipelineSummary.createPipelineSummary)('Test', {
|
|
111
|
+
outputPath: ''
|
|
112
|
+
});
|
|
113
|
+
const response = summary.success();
|
|
114
|
+
const saved = summary.saveToFile(response);
|
|
115
|
+
expect(saved).toBe(false);
|
|
116
|
+
});
|
|
117
|
+
test('should handle override path', () => {
|
|
118
|
+
const primaryPath = _path.default.join(TEST_ROOT, 'primary.json');
|
|
119
|
+
const overridePath = _path.default.join(TEST_ROOT, 'custom', 'override.json');
|
|
120
|
+
const summary = (0, _pipelineSummary.createPipelineSummary)('Test', {
|
|
121
|
+
outputPath: primaryPath
|
|
122
|
+
});
|
|
123
|
+
const response = summary.success();
|
|
124
|
+
const saved = summary.saveToFile(response, overridePath);
|
|
125
|
+
expect(saved).toBe(true);
|
|
126
|
+
expect(_fs.default.existsSync(overridePath)).toBe(true);
|
|
127
|
+
expect(_fs.default.existsSync(primaryPath)).toBe(false);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
describe('Complex paths with special characters', () => {
|
|
131
|
+
test('should handle paths with hyphens and underscores', () => {
|
|
132
|
+
const summaryPath = _path.default.join(TEST_ROOT, 'test-reports_v2', 'unit-tests_final', 'summary-result.json');
|
|
133
|
+
const summary = (0, _pipelineSummary.createPipelineSummary)('Test', {
|
|
134
|
+
outputPath: summaryPath
|
|
135
|
+
});
|
|
136
|
+
const response = summary.success();
|
|
137
|
+
const saved = summary.saveToFile(response);
|
|
138
|
+
expect(saved).toBe(true);
|
|
139
|
+
expect(_fs.default.existsSync(summaryPath)).toBe(true);
|
|
140
|
+
});
|
|
141
|
+
test('should handle paths with dots and numbers', () => {
|
|
142
|
+
const summaryPath = _path.default.join(TEST_ROOT, 'v2.1.0', 'reports-2024.01.15', 'summary.json');
|
|
143
|
+
const summary = (0, _pipelineSummary.createPipelineSummary)('Test', {
|
|
144
|
+
outputPath: summaryPath
|
|
145
|
+
});
|
|
146
|
+
const response = summary.success();
|
|
147
|
+
const saved = summary.saveToFile(response);
|
|
148
|
+
expect(saved).toBe(true);
|
|
149
|
+
expect(_fs.default.existsSync(summaryPath)).toBe(true);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
describe('File content verification', () => {
|
|
153
|
+
test('should write valid JSON with proper formatting', () => {
|
|
154
|
+
const summaryPath = _path.default.join(TEST_ROOT, 'reports', 'summary.json');
|
|
155
|
+
const summary = (0, _pipelineSummary.createPipelineSummary)('Test', {
|
|
156
|
+
outputPath: summaryPath
|
|
157
|
+
});
|
|
158
|
+
const response = summary.success({
|
|
159
|
+
message: 'All tests passed'
|
|
160
|
+
});
|
|
161
|
+
summary.saveToFile(response);
|
|
162
|
+
const content = _fs.default.readFileSync(summaryPath, 'utf-8');
|
|
163
|
+
|
|
164
|
+
// Verify it's valid JSON
|
|
165
|
+
expect(() => JSON.parse(content)).not.toThrow();
|
|
166
|
+
|
|
167
|
+
// Verify formatting (should have indentation)
|
|
168
|
+
expect(content).toContain('\n ');
|
|
169
|
+
|
|
170
|
+
// Verify content
|
|
171
|
+
const parsed = JSON.parse(content);
|
|
172
|
+
expect(parsed.status).toBe('SUCCESS');
|
|
173
|
+
expect(parsed.message).toBe('All tests passed');
|
|
174
|
+
});
|
|
175
|
+
test('should handle complex response objects', () => {
|
|
176
|
+
const summaryPath = _path.default.join(TEST_ROOT, 'reports', 'summary.json');
|
|
177
|
+
const summary = (0, _pipelineSummary.createPipelineSummary)('Test', {
|
|
178
|
+
outputPath: summaryPath
|
|
179
|
+
});
|
|
180
|
+
const response = summary.testFailure({
|
|
181
|
+
testResults: {
|
|
182
|
+
numTotalTests: 100,
|
|
183
|
+
numFailedTests: 5,
|
|
184
|
+
numPassedTests: 95,
|
|
185
|
+
numSkippedTests: 0,
|
|
186
|
+
testResults: [{
|
|
187
|
+
name: 'test.js',
|
|
188
|
+
numFailingTests: 1,
|
|
189
|
+
assertionResults: [{
|
|
190
|
+
title: 'should work',
|
|
191
|
+
fullName: 'Suite › should work',
|
|
192
|
+
status: 'failed',
|
|
193
|
+
failureMessages: ['Expected true to be false']
|
|
194
|
+
}]
|
|
195
|
+
}]
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
const saved = summary.saveToFile(response);
|
|
199
|
+
expect(saved).toBe(true);
|
|
200
|
+
const content = JSON.parse(_fs.default.readFileSync(summaryPath, 'utf-8'));
|
|
201
|
+
expect(content.error.errors).toHaveLength(1);
|
|
202
|
+
expect(content.result[0].failedTests).toBe(5);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
describe('Concurrent operations', () => {
|
|
206
|
+
test('should handle sequential saves to different files', () => {
|
|
207
|
+
const summary = (0, _pipelineSummary.createPipelineSummary)('Test', {
|
|
208
|
+
outputPath: _path.default.join(TEST_ROOT, 'file1.json')
|
|
209
|
+
});
|
|
210
|
+
const response1 = summary.success({
|
|
211
|
+
message: 'First'
|
|
212
|
+
});
|
|
213
|
+
const response2 = summary.failure({
|
|
214
|
+
message: 'Second'
|
|
215
|
+
});
|
|
216
|
+
const saved1 = summary.saveToFile(response1);
|
|
217
|
+
const saved2 = summary.saveToFile(response2, _path.default.join(TEST_ROOT, 'file2.json'));
|
|
218
|
+
expect(saved1).toBe(true);
|
|
219
|
+
expect(saved2).toBe(true);
|
|
220
|
+
expect(_fs.default.existsSync(_path.default.join(TEST_ROOT, 'file1.json'))).toBe(true);
|
|
221
|
+
expect(_fs.default.existsSync(_path.default.join(TEST_ROOT, 'file2.json'))).toBe(true);
|
|
222
|
+
});
|
|
223
|
+
test('should handle multiple saves to same location', () => {
|
|
224
|
+
const summaryPath = _path.default.join(TEST_ROOT, 'reports', 'summary.json');
|
|
225
|
+
const summary = (0, _pipelineSummary.createPipelineSummary)('Test', {
|
|
226
|
+
outputPath: summaryPath
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// First save
|
|
230
|
+
const response1 = summary.success({
|
|
231
|
+
message: 'First attempt'
|
|
232
|
+
});
|
|
233
|
+
const saved1 = summary.saveToFile(response1);
|
|
234
|
+
expect(saved1).toBe(true);
|
|
235
|
+
|
|
236
|
+
// Second save (overwrite)
|
|
237
|
+
const response2 = summary.success({
|
|
238
|
+
message: 'Second attempt'
|
|
239
|
+
});
|
|
240
|
+
const saved2 = summary.saveToFile(response2);
|
|
241
|
+
expect(saved2).toBe(true);
|
|
242
|
+
|
|
243
|
+
// Verify only latest content exists
|
|
244
|
+
const content = JSON.parse(_fs.default.readFileSync(summaryPath, 'utf-8'));
|
|
245
|
+
expect(content.message).toBe('Second attempt');
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
});
|