@testomatio/reporter 2.0.1-beta.7 ā 2.0.1-beta.9
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/lib/adapter/playwright.js +2 -7
- package/lib/pipe/debug.d.ts +1 -0
- package/lib/pipe/debug.js +25 -10
- package/lib/replay.d.ts +13 -0
- package/lib/replay.js +404 -16
- package/lib/utils/constants.d.ts +12 -0
- package/lib/utils/constants.js +15 -0
- package/package.json +1 -1
- package/src/adapter/playwright.js +2 -7
- package/src/pipe/debug.js +27 -12
- package/src/replay.js +415 -18
- package/src/utils/constants.js +12 -0
|
@@ -14,6 +14,7 @@ const client_js_1 = __importDefault(require("../client.js"));
|
|
|
14
14
|
const utils_js_1 = require("../utils/utils.js");
|
|
15
15
|
const index_js_1 = require("../services/index.js");
|
|
16
16
|
const data_storage_js_1 = require("../data-storage.js");
|
|
17
|
+
const constants_js_2 = require("../utils/constants.js");
|
|
17
18
|
const reportTestPromises = [];
|
|
18
19
|
class PlaywrightReporter {
|
|
19
20
|
constructor(config = {}) {
|
|
@@ -123,15 +124,9 @@ class PlaywrightReporter {
|
|
|
123
124
|
}
|
|
124
125
|
if (artifact.body) {
|
|
125
126
|
let filePath = generateTmpFilepath(artifact.name);
|
|
126
|
-
// Check if file already has an extension
|
|
127
127
|
const hasExtension = artifact.name && path_1.default.extname(artifact.name);
|
|
128
128
|
if (!hasExtension && artifact.contentType) {
|
|
129
|
-
const
|
|
130
|
-
const extensionMap = {
|
|
131
|
-
jpeg: 'jpg',
|
|
132
|
-
plain: 'txt',
|
|
133
|
-
};
|
|
134
|
-
const extension = extensionMap[mimeType] || mimeType;
|
|
129
|
+
const extension = constants_js_2.extensionMap[artifact.contentType] || artifact.contentType.split('/')[1];
|
|
135
130
|
if (extension)
|
|
136
131
|
filePath += `.${extension}`;
|
|
137
132
|
}
|
package/lib/pipe/debug.d.ts
CHANGED
package/lib/pipe/debug.js
CHANGED
|
@@ -27,19 +27,20 @@ class DebugPipe {
|
|
|
27
27
|
this.logFilePath = path_1.default.join(os_1.default.tmpdir(), `testomatio.debug.${Date.now()}.json`);
|
|
28
28
|
debug('Creating debug file:', this.logFilePath);
|
|
29
29
|
fs_1.default.writeFileSync(this.logFilePath, '');
|
|
30
|
-
// Create
|
|
31
|
-
|
|
30
|
+
// Create latest debug file reference (Windows compatible)
|
|
31
|
+
this.latestFilePath = path_1.default.join(os_1.default.tmpdir(), 'testomatio.debug.latest.json');
|
|
32
32
|
try {
|
|
33
|
-
// Remove existing
|
|
34
|
-
if (fs_1.default.existsSync(
|
|
35
|
-
fs_1.default.
|
|
33
|
+
// Remove existing latest file if it exists
|
|
34
|
+
if (fs_1.default.existsSync(this.latestFilePath)) {
|
|
35
|
+
fs_1.default.rmSync(this.latestFilePath);
|
|
36
36
|
}
|
|
37
|
-
//
|
|
38
|
-
fs_1.default.
|
|
39
|
-
debug('Created
|
|
37
|
+
// Initialize latest file
|
|
38
|
+
fs_1.default.writeFileSync(this.latestFilePath, '');
|
|
39
|
+
debug('Created latest debug file:', this.latestFilePath);
|
|
40
40
|
}
|
|
41
41
|
catch (err) {
|
|
42
|
-
debug('Failed to create
|
|
42
|
+
debug('Failed to create latest debug file:', err.message);
|
|
43
|
+
this.latestFilePath = null; // Disable latest file if creation fails
|
|
43
44
|
}
|
|
44
45
|
console.log(constants_js_1.APP_PREFIX, 'šŖ² Debug file created');
|
|
45
46
|
this.testomatioEnvVars = Object.keys(process.env)
|
|
@@ -67,7 +68,18 @@ class DebugPipe {
|
|
|
67
68
|
const timePassedFromLastAction = Date.now() - (this.lastActionTimestamp || Date.now());
|
|
68
69
|
this.lastActionTimestamp = Date.now();
|
|
69
70
|
const logLine = JSON.stringify({ t: `+${(0, pretty_ms_1.default)(timePassedFromLastAction)}`, ...logData });
|
|
70
|
-
|
|
71
|
+
const logLineWithNewline = `${logLine}\n`;
|
|
72
|
+
// Write to timestamped debug file
|
|
73
|
+
fs_1.default.appendFileSync(this.logFilePath, logLineWithNewline);
|
|
74
|
+
// Also write to latest debug file for Windows compatibility
|
|
75
|
+
if (this.latestFilePath) {
|
|
76
|
+
try {
|
|
77
|
+
fs_1.default.appendFileSync(this.latestFilePath, logLineWithNewline);
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
debug('Failed to write to latest debug file:', err.message);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
71
83
|
}
|
|
72
84
|
async prepareRun(opts) {
|
|
73
85
|
if (!this.isEnabled)
|
|
@@ -119,6 +131,9 @@ class DebugPipe {
|
|
|
119
131
|
clearInterval(this.batch.intervalFunction);
|
|
120
132
|
this.logToFile({ action: 'finishRun', params });
|
|
121
133
|
console.log(constants_js_1.APP_PREFIX, 'šŖ² Debug Saved to', this.logFilePath);
|
|
134
|
+
if (this.latestFilePath) {
|
|
135
|
+
console.log(constants_js_1.APP_PREFIX, 'šŖ² Latest Debug file:', this.latestFilePath);
|
|
136
|
+
}
|
|
122
137
|
}
|
|
123
138
|
toString() {
|
|
124
139
|
return 'Debug Reporter';
|
package/lib/replay.d.ts
CHANGED
|
@@ -10,6 +10,19 @@ export class Replay {
|
|
|
10
10
|
* @returns {string} Path to the latest debug file
|
|
11
11
|
*/
|
|
12
12
|
getDefaultDebugFile(): string;
|
|
13
|
+
/**
|
|
14
|
+
* Read artifacts file for a specific run
|
|
15
|
+
* @param {string} runId - Run ID to find artifacts for
|
|
16
|
+
* @returns {Array} Array of artifact records with rid and file paths
|
|
17
|
+
*/
|
|
18
|
+
readArtifactsFile(runId: string): any[];
|
|
19
|
+
/**
|
|
20
|
+
* Merge artifacts data with test data
|
|
21
|
+
* @param {Array} tests - Array of test objects
|
|
22
|
+
* @param {Array} artifacts - Array of artifact records
|
|
23
|
+
* @returns {Array} Tests with merged artifact data
|
|
24
|
+
*/
|
|
25
|
+
mergeArtifactsWithTests(tests: any[], artifacts: any[]): any[];
|
|
13
26
|
/**
|
|
14
27
|
* Parse a debug file and extract test data
|
|
15
28
|
* @param {string} debugFile - Path to the debug file
|
package/lib/replay.js
CHANGED
|
@@ -10,6 +10,7 @@ const os_1 = __importDefault(require("os"));
|
|
|
10
10
|
const client_js_1 = __importDefault(require("./client.js"));
|
|
11
11
|
const constants_js_1 = require("./constants.js");
|
|
12
12
|
const config_js_1 = require("./config.js");
|
|
13
|
+
const isEnabled = !!process.env.TESTOMATIO_DEBUG || !!process.env.DEBUG;
|
|
13
14
|
class Replay {
|
|
14
15
|
constructor(options = {}) {
|
|
15
16
|
this.apiKey = options.apiKey || config_js_1.config.TESTOMATIO || undefined;
|
|
@@ -25,6 +26,93 @@ class Replay {
|
|
|
25
26
|
getDefaultDebugFile() {
|
|
26
27
|
return path_1.default.join(os_1.default.tmpdir(), 'testomatio.debug.latest.json');
|
|
27
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Read artifacts file for a specific run
|
|
31
|
+
* @param {string} runId - Run ID to find artifacts for
|
|
32
|
+
* @returns {Array} Array of artifact records with rid and file paths
|
|
33
|
+
*/
|
|
34
|
+
readArtifactsFile(runId) {
|
|
35
|
+
if (!runId)
|
|
36
|
+
return [];
|
|
37
|
+
const artifactsFile = path_1.default.join(os_1.default.tmpdir(), `testomatio.run.${runId}.json`);
|
|
38
|
+
if (!fs_1.default.existsSync(artifactsFile)) {
|
|
39
|
+
if (isEnabled)
|
|
40
|
+
console.log(`No artifacts file found: ${artifactsFile}`);
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const data = fs_1.default.readFileSync(artifactsFile, 'utf-8');
|
|
45
|
+
const lines = data.split('\n').filter(Boolean);
|
|
46
|
+
const artifacts = lines.map(line => JSON.parse(line));
|
|
47
|
+
if (isEnabled)
|
|
48
|
+
console.log(`Found ${artifacts.length} artifact records in ${artifactsFile}`);
|
|
49
|
+
return artifacts;
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
if (isEnabled)
|
|
53
|
+
console.log(`Error reading artifacts file: ${err.message}`);
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Merge artifacts data with test data
|
|
59
|
+
* @param {Array} tests - Array of test objects
|
|
60
|
+
* @param {Array} artifacts - Array of artifact records
|
|
61
|
+
* @returns {Array} Tests with merged artifact data
|
|
62
|
+
*/
|
|
63
|
+
mergeArtifactsWithTests(tests, artifacts) {
|
|
64
|
+
if (!artifacts.length)
|
|
65
|
+
return tests;
|
|
66
|
+
// Extract runId prefix from first test
|
|
67
|
+
const runIdPrefix = tests[0]?.rid ? tests[0].rid.split('-')[0] + '-' : '';
|
|
68
|
+
// Group artifacts by RID
|
|
69
|
+
const artifactsByRid = artifacts.reduce((acc, artifact) => {
|
|
70
|
+
const fullRid = runIdPrefix && !artifact.rid.startsWith(runIdPrefix) ? runIdPrefix + artifact.rid : artifact.rid;
|
|
71
|
+
if (!acc[fullRid])
|
|
72
|
+
acc[fullRid] = [];
|
|
73
|
+
acc[fullRid].push(artifact);
|
|
74
|
+
return acc;
|
|
75
|
+
}, {});
|
|
76
|
+
// Merge artifacts into tests
|
|
77
|
+
let mergedCount = 0;
|
|
78
|
+
const result = tests.map(test => {
|
|
79
|
+
if (!test.rid || !artifactsByRid[test.rid])
|
|
80
|
+
return test;
|
|
81
|
+
const testArtifacts = artifactsByRid[test.rid];
|
|
82
|
+
mergedCount++;
|
|
83
|
+
if (isEnabled && test.title) {
|
|
84
|
+
console.log(`š Adding artifact info for test: ${test.title}`);
|
|
85
|
+
console.log(` š Existing files: ${(test.files || []).length} | Artifact records: ${testArtifacts.length}`);
|
|
86
|
+
}
|
|
87
|
+
// Deduplicate artifacts by filename
|
|
88
|
+
const seenFiles = new Set();
|
|
89
|
+
const deduplicatedArtifacts = testArtifacts.filter(artifact => {
|
|
90
|
+
if (!artifact.file)
|
|
91
|
+
return true;
|
|
92
|
+
const fileName = path_1.default.basename(artifact.file);
|
|
93
|
+
if (seenFiles.has(fileName)) {
|
|
94
|
+
if (isEnabled && test.title) {
|
|
95
|
+
console.log(` ā ļø Removed duplicate artifact: ${fileName}`);
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
seenFiles.add(fileName);
|
|
100
|
+
return true;
|
|
101
|
+
});
|
|
102
|
+
if (isEnabled && test.title && deduplicatedArtifacts.length !== testArtifacts.length) {
|
|
103
|
+
console.log(` š§¹ Deduplicated: ${testArtifacts.length} ā ${deduplicatedArtifacts.length} artifacts`);
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
...test,
|
|
107
|
+
files: test.files || [],
|
|
108
|
+
artifactRecords: deduplicatedArtifacts,
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
if (isEnabled) {
|
|
112
|
+
console.log(`š Added artifact info to ${mergedCount} tests (no file duplication)`);
|
|
113
|
+
}
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
28
116
|
/**
|
|
29
117
|
* Parse a debug file and extract test data
|
|
30
118
|
* @param {string} debugFile - Path to the debug file
|
|
@@ -35,7 +123,10 @@ class Replay {
|
|
|
35
123
|
throw new Error(`Debug file not found: ${debugFile}`);
|
|
36
124
|
}
|
|
37
125
|
const fileContent = fs_1.default.readFileSync(debugFile, 'utf-8');
|
|
38
|
-
const lines = fileContent
|
|
126
|
+
const lines = fileContent
|
|
127
|
+
.trim()
|
|
128
|
+
.split('\n')
|
|
129
|
+
.filter(line => line.trim() !== '');
|
|
39
130
|
if (lines.length === 0) {
|
|
40
131
|
throw new Error('Debug file is empty');
|
|
41
132
|
}
|
|
@@ -72,14 +163,24 @@ class Replay {
|
|
|
72
163
|
Object.keys(test).forEach(key => {
|
|
73
164
|
if (test[key] !== null && test[key] !== undefined) {
|
|
74
165
|
if (key === 'files' && Array.isArray(test[key]) && test[key].length > 0) {
|
|
75
|
-
//
|
|
76
|
-
|
|
166
|
+
// Deduplicate files within the array itself
|
|
167
|
+
const seen = new Set();
|
|
168
|
+
mergedTest.files = test[key].filter(file => {
|
|
169
|
+
const fileName = typeof file === 'string'
|
|
170
|
+
? path_1.default.basename(file)
|
|
171
|
+
: path_1.default.basename(file?.path || file?.file || '');
|
|
172
|
+
if (seen.has(fileName))
|
|
173
|
+
return false;
|
|
174
|
+
seen.add(fileName);
|
|
175
|
+
return true;
|
|
176
|
+
});
|
|
77
177
|
}
|
|
78
178
|
else if (key === 'artifacts' && Array.isArray(test[key]) && test[key].length > 0) {
|
|
79
|
-
//
|
|
80
|
-
mergedTest.artifacts =
|
|
179
|
+
// Keep the most recent artifacts array (don't merge to avoid duplicates)
|
|
180
|
+
mergedTest.artifacts = test[key];
|
|
81
181
|
}
|
|
82
|
-
else if (existingTest[key] === null ||
|
|
182
|
+
else if (existingTest[key] === null ||
|
|
183
|
+
existingTest[key] === undefined ||
|
|
83
184
|
(Array.isArray(existingTest[key]) && existingTest[key].length === 0)) {
|
|
84
185
|
// Use new value if existing is null/undefined/empty array
|
|
85
186
|
mergedTest[key] = test[key];
|
|
@@ -108,8 +209,33 @@ class Replay {
|
|
|
108
209
|
// Handle tests with rid (deduplicate)
|
|
109
210
|
const existingTest = testsMap.get(test.rid);
|
|
110
211
|
if (existingTest) {
|
|
111
|
-
// Merge
|
|
112
|
-
const mergedTest = { ...existingTest
|
|
212
|
+
// Merge test data - prioritize non-null/non-empty values (same logic as addTestsBatch)
|
|
213
|
+
const mergedTest = { ...existingTest };
|
|
214
|
+
Object.keys(test).forEach(key => {
|
|
215
|
+
if (test[key] !== null && test[key] !== undefined) {
|
|
216
|
+
if (key === 'files' && Array.isArray(test[key]) && test[key].length > 0) {
|
|
217
|
+
// Deduplicate files within the array itself
|
|
218
|
+
const seen = new Set();
|
|
219
|
+
mergedTest.files = test[key].filter(file => {
|
|
220
|
+
const fileName = typeof file === 'string' ? path_1.default.basename(file) : path_1.default.basename(file?.path || file?.file || '');
|
|
221
|
+
if (seen.has(fileName))
|
|
222
|
+
return false;
|
|
223
|
+
seen.add(fileName);
|
|
224
|
+
return true;
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
else if (key === 'artifacts' && Array.isArray(test[key]) && test[key].length > 0) {
|
|
228
|
+
// Keep the most recent artifacts array (don't merge to avoid duplicates)
|
|
229
|
+
mergedTest.artifacts = test[key];
|
|
230
|
+
}
|
|
231
|
+
else if (existingTest[key] === null ||
|
|
232
|
+
existingTest[key] === undefined ||
|
|
233
|
+
(Array.isArray(existingTest[key]) && existingTest[key].length === 0)) {
|
|
234
|
+
// Use new value if existing is null/undefined/empty array
|
|
235
|
+
mergedTest[key] = test[key];
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
});
|
|
113
239
|
testsMap.set(test.rid, mergedTest);
|
|
114
240
|
}
|
|
115
241
|
else {
|
|
@@ -145,7 +271,7 @@ class Replay {
|
|
|
145
271
|
envVars,
|
|
146
272
|
parseErrors,
|
|
147
273
|
totalLines: lines.length,
|
|
148
|
-
runId
|
|
274
|
+
runId,
|
|
149
275
|
};
|
|
150
276
|
}
|
|
151
277
|
/**
|
|
@@ -175,14 +301,132 @@ class Replay {
|
|
|
175
301
|
this.onLog(`Replaying data from debug file: ${debugFile}`);
|
|
176
302
|
// Parse the debug file
|
|
177
303
|
const debugData = this.parseDebugFile(debugFile);
|
|
178
|
-
const { runParams, finishParams,
|
|
304
|
+
const { runParams, finishParams, envVars, runId } = debugData;
|
|
305
|
+
let tests = debugData.tests;
|
|
179
306
|
this.onLog(`Found ${tests.length} tests to replay`);
|
|
180
307
|
if (tests.length === 0) {
|
|
181
308
|
throw new Error('No test data found in debug file');
|
|
182
309
|
}
|
|
310
|
+
// Read and merge artifacts if runId is available
|
|
311
|
+
if (runId) {
|
|
312
|
+
const artifacts = this.readArtifactsFile(runId);
|
|
313
|
+
if (artifacts.length > 0) {
|
|
314
|
+
if (isEnabled)
|
|
315
|
+
console.log(`Found ${artifacts.length} artifact records in testomatio.run.${runId}.json`);
|
|
316
|
+
tests = this.mergeArtifactsWithTests(tests, artifacts);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
183
319
|
// Restore environment variables
|
|
184
320
|
this.restoreEnvironmentVariables(envVars);
|
|
185
321
|
if (this.dryRun) {
|
|
322
|
+
if (isEnabled)
|
|
323
|
+
console.log('š DRY RUN - Tests to be sent:');
|
|
324
|
+
if (isEnabled)
|
|
325
|
+
console.log('='.repeat(60));
|
|
326
|
+
let totalArtifacts = 0;
|
|
327
|
+
let totalFiles = 0;
|
|
328
|
+
let uploadedArtifacts = 0;
|
|
329
|
+
let notUploadedArtifacts = 0;
|
|
330
|
+
tests.forEach((test, index) => {
|
|
331
|
+
const status = test.status === 'passed' ? 'ā
' : test.status === 'failed' ? 'ā' : 'āļø';
|
|
332
|
+
if (isEnabled)
|
|
333
|
+
console.log(`${index + 1}. ${status} ${test.title || test.id}`);
|
|
334
|
+
if (isEnabled)
|
|
335
|
+
console.log(` š File: ${test.file || 'Unknown'}`);
|
|
336
|
+
if (isEnabled)
|
|
337
|
+
console.log(` š Status: ${test.status}`);
|
|
338
|
+
if (isEnabled)
|
|
339
|
+
console.log(` š RID: ${test.rid || 'No RID'}`);
|
|
340
|
+
if (test.steps && test.steps.length > 0) {
|
|
341
|
+
if (isEnabled)
|
|
342
|
+
console.log(` š Steps: ${test.steps.length}`);
|
|
343
|
+
test.steps.slice(0, 3).forEach((step, stepIndex) => {
|
|
344
|
+
const stepStatus = step.status === 'passed' ? 'ā
' : step.status === 'failed' ? 'ā' : 'āŖ';
|
|
345
|
+
if (isEnabled)
|
|
346
|
+
console.log(` ${stepIndex + 1}. ${stepStatus} ${step.title || step.name || 'Step'}`);
|
|
347
|
+
});
|
|
348
|
+
if (test.steps.length > 3) {
|
|
349
|
+
if (isEnabled)
|
|
350
|
+
console.log(` ... also ${test.steps.length - 3} steps`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
// Show either artifact records (if available) or files
|
|
354
|
+
if (test.artifactRecords && test.artifactRecords.length > 0) {
|
|
355
|
+
totalArtifacts += test.artifactRecords.length;
|
|
356
|
+
if (isEnabled)
|
|
357
|
+
console.log(` š Artifacts: ${test.artifactRecords.length}`);
|
|
358
|
+
test.artifactRecords.forEach((artifact, artIndex) => {
|
|
359
|
+
const uploaded = artifact.uploaded === true
|
|
360
|
+
? 'ā
Uploaded'
|
|
361
|
+
: artifact.uploaded === false
|
|
362
|
+
? 'š¤ Not Uploaded'
|
|
363
|
+
: 'ā³ Unknown';
|
|
364
|
+
const fileName = artifact.file ? path_1.default.basename(artifact.file) : 'Unknown';
|
|
365
|
+
if (isEnabled)
|
|
366
|
+
console.log(` ${artIndex + 1}. ${uploaded}: ${fileName}`);
|
|
367
|
+
// Count artifact upload status
|
|
368
|
+
if (artifact.uploaded === true) {
|
|
369
|
+
uploadedArtifacts++;
|
|
370
|
+
}
|
|
371
|
+
else if (artifact.uploaded === false) {
|
|
372
|
+
notUploadedArtifacts++;
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
else if (test.files && test.files.length > 0) {
|
|
377
|
+
totalFiles += test.files.length;
|
|
378
|
+
if (isEnabled)
|
|
379
|
+
console.log(` š Files: ${test.files.length}`);
|
|
380
|
+
test.files.slice(0, 3).forEach((file, fileIndex) => {
|
|
381
|
+
// Handle both string paths and object formats
|
|
382
|
+
let fileName;
|
|
383
|
+
if (typeof file === 'string') {
|
|
384
|
+
fileName = path_1.default.basename(file);
|
|
385
|
+
}
|
|
386
|
+
else if (file && typeof file === 'object') {
|
|
387
|
+
const filePath = file.path || file.file || JSON.stringify(file);
|
|
388
|
+
fileName = file.name || (typeof filePath === 'string' ? path_1.default.basename(filePath) : 'Unknown');
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
fileName = 'Unknown';
|
|
392
|
+
}
|
|
393
|
+
if (isEnabled)
|
|
394
|
+
console.log(` ${fileIndex + 1}. ${fileName}`);
|
|
395
|
+
});
|
|
396
|
+
if (test.files.length > 3) {
|
|
397
|
+
if (isEnabled)
|
|
398
|
+
console.log(` ... also ${test.files.length - 3} files`);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
if (isEnabled)
|
|
402
|
+
console.log('');
|
|
403
|
+
});
|
|
404
|
+
if (isEnabled)
|
|
405
|
+
console.log('='.repeat(60));
|
|
406
|
+
if (isEnabled)
|
|
407
|
+
console.log('š SUMMARY:');
|
|
408
|
+
if (isEnabled)
|
|
409
|
+
console.log(` š Total tests: ${tests.length}`);
|
|
410
|
+
if (isEnabled)
|
|
411
|
+
console.log(` š Total files: ${totalFiles}`);
|
|
412
|
+
if (isEnabled)
|
|
413
|
+
console.log(` š Total artifact records: ${totalArtifacts}`);
|
|
414
|
+
if (totalArtifacts > 0) {
|
|
415
|
+
if (isEnabled)
|
|
416
|
+
console.log(` ā
Uploaded: ${uploadedArtifacts}`);
|
|
417
|
+
if (isEnabled)
|
|
418
|
+
console.log(` š¤ Not Uploaded: ${notUploadedArtifacts}`);
|
|
419
|
+
}
|
|
420
|
+
if (isEnabled)
|
|
421
|
+
console.log(` š Run ID: ${runId || 'Will create new'}`);
|
|
422
|
+
if (isEnabled)
|
|
423
|
+
console.log(` š Environment: ${envVars.TESTOMATIO_ENV || 'Unknown'}`);
|
|
424
|
+
if (isEnabled)
|
|
425
|
+
console.log(` š API URL: ${envVars.TESTOMATIO_URL || 'Default'}`);
|
|
426
|
+
if (isEnabled)
|
|
427
|
+
console.log('');
|
|
428
|
+
if (isEnabled)
|
|
429
|
+
console.log('ā
Use without --dry-run to send this data to Testomat.io');
|
|
186
430
|
return {
|
|
187
431
|
success: true,
|
|
188
432
|
testsCount: tests.length,
|
|
@@ -190,51 +434,175 @@ class Replay {
|
|
|
190
434
|
finishParams,
|
|
191
435
|
envVars,
|
|
192
436
|
runId,
|
|
193
|
-
dryRun: true
|
|
437
|
+
dryRun: true,
|
|
194
438
|
};
|
|
195
439
|
}
|
|
196
440
|
// Create client and restore the run
|
|
197
441
|
const client = new client_js_1.default({
|
|
198
442
|
apiKey: this.apiKey,
|
|
199
|
-
isBatchEnabled: true,
|
|
443
|
+
// isBatchEnabled: true,
|
|
200
444
|
...runParams,
|
|
201
445
|
});
|
|
446
|
+
if (isEnabled) {
|
|
447
|
+
console.log('š§ CLIENT CONFIGURATION:');
|
|
448
|
+
console.log(` š API Key: ${this.apiKey ? `${this.apiKey.slice(0, 10)}...` : 'NOT SET'}`);
|
|
449
|
+
console.log(` šŖ Batch enabled: ${runParams.isBatchEnabled || 'unknown'}`);
|
|
450
|
+
console.log(` š” Pipes count: ${client.pipes ? client.pipes.length : 0}`);
|
|
451
|
+
if (client.pipes && client.pipes.length > 0) {
|
|
452
|
+
client.pipes.forEach((pipe, index) => {
|
|
453
|
+
console.log(` ${index + 1}. ${pipe.toString()} - enabled: ${pipe.isEnabled}`);
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
console.log(` ā ļø WARNING: No pipes configured!`);
|
|
458
|
+
}
|
|
459
|
+
console.log(` š¤ Uploader enabled: ${client.uploader ? client.uploader.isEnabled : 'unknown'}`);
|
|
460
|
+
console.log('');
|
|
461
|
+
}
|
|
202
462
|
// Use the stored runId if available, otherwise create a new run
|
|
203
463
|
if (runId) {
|
|
204
464
|
this.onLog(`Using existing run ID: ${runId}`);
|
|
465
|
+
if (isEnabled)
|
|
466
|
+
console.log(`š Restoring run with ID: ${runId}`);
|
|
467
|
+
if (isEnabled)
|
|
468
|
+
console.log(`š Run params:`, JSON.stringify(runParams, null, 2));
|
|
205
469
|
client.runId = runId;
|
|
470
|
+
// Always call createRun to initialize batch system and update run params
|
|
471
|
+
if (isEnabled)
|
|
472
|
+
console.log(`š Initializing batch system for existing run...`);
|
|
473
|
+
// await client.createRun({ ...runParams, isBatchEnabled: true });
|
|
474
|
+
await client.createRun(runParams);
|
|
475
|
+
this.onLog(`Create new run`);
|
|
206
476
|
}
|
|
207
477
|
else {
|
|
208
478
|
this.onLog('Publishing to run...');
|
|
479
|
+
if (isEnabled)
|
|
480
|
+
console.log(`š Creating new run with params:`, JSON.stringify(runParams, null, 2));
|
|
209
481
|
await client.createRun(runParams);
|
|
482
|
+
if (isEnabled)
|
|
483
|
+
console.log(`ā
New run created with ID: ${client.runId}`);
|
|
484
|
+
}
|
|
485
|
+
if (isEnabled) {
|
|
486
|
+
console.log('š§ POST-INITIALIZATION STATUS:');
|
|
487
|
+
console.log(` š Final Run ID: ${client.runId}`);
|
|
488
|
+
// Check each pipe status
|
|
489
|
+
if (!client.pipes?.length) {
|
|
490
|
+
console.log(' ā ļø No pipes found!');
|
|
491
|
+
}
|
|
492
|
+
else {
|
|
493
|
+
client.pipes.forEach((pipe, index) => {
|
|
494
|
+
console.log(` š” Pipe ${index + 1}: ${pipe.toString()}`);
|
|
495
|
+
console.log(` ā
Enabled: ${pipe.isEnabled}`);
|
|
496
|
+
console.log(` š Run ID: ${pipe.runId || 'NOT SET'}`);
|
|
497
|
+
if (!pipe.toString().includes('Testomatio') || !pipe.batch)
|
|
498
|
+
return;
|
|
499
|
+
console.log(` š Batch enabled: ${pipe.batch.isEnabled}`);
|
|
500
|
+
console.log(` ā±ļø Batch interval: ${pipe.batch.intervalFunction ? 'RUNNING' : 'NOT RUNNING'}`);
|
|
501
|
+
console.log(` š¦ Tests in queue: ${pipe.batch.tests?.length || 0}`);
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
console.log('');
|
|
505
|
+
}
|
|
506
|
+
if (isEnabled) {
|
|
507
|
+
console.log('š SENDING TESTS TO TESTOMAT.IO');
|
|
508
|
+
console.log('='.repeat(60));
|
|
210
509
|
}
|
|
211
510
|
// Send each test result
|
|
212
511
|
let successCount = 0;
|
|
213
512
|
let failureCount = 0;
|
|
214
513
|
for (const [index, test] of tests.entries()) {
|
|
215
514
|
try {
|
|
216
|
-
|
|
515
|
+
if (isEnabled) {
|
|
516
|
+
const status = test.status === 'passed' ? 'ā
' : test.status === 'failed' ? 'ā' : 'āļø';
|
|
517
|
+
console.log(`\nš¤ Sending test ${index + 1}/${tests.length}: ${status} ${test.title || test.id}`);
|
|
518
|
+
console.log(` š File: ${test.file || 'Unknown'}`);
|
|
519
|
+
console.log(` š RID: ${test.rid || 'No RID'}`);
|
|
520
|
+
console.log(` š Status: ${test.status}${test.steps?.length > 0 ? ` | steps: ${test.steps.length}` : ''}`);
|
|
521
|
+
// Show artifacts info
|
|
522
|
+
if (test.artifactRecords?.length) {
|
|
523
|
+
console.log(` š Artifacts: ${test.artifactRecords.length}`);
|
|
524
|
+
test.artifactRecords.forEach((artifact, i) => {
|
|
525
|
+
const status = artifact.uploaded ? 'ā
Uploaded' : 'š¤ Not Uploaded';
|
|
526
|
+
const name = artifact.file ? path_1.default.basename(artifact.file) : 'Unknown';
|
|
527
|
+
console.log(` ${i + 1}. ${status}: ${name}`);
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
else if (test.files?.length) {
|
|
531
|
+
console.log(` š Files: ${test.files.length}`);
|
|
532
|
+
test.files.slice(0, 3).forEach((file, i) => {
|
|
533
|
+
const name = typeof file === 'string' ? path_1.default.basename(file) : path_1.default.basename(file?.path || file?.file || 'Unknown');
|
|
534
|
+
console.log(` ${i + 1}. ${name}`);
|
|
535
|
+
});
|
|
536
|
+
if (test.files.length > 3) {
|
|
537
|
+
console.log(` ... also ${test.files.length - 3} more files`);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
// Show the actual data payload being sent
|
|
541
|
+
const uniqueFiles = test.files
|
|
542
|
+
? [
|
|
543
|
+
...new Set(test.files.map(f => typeof f === 'string' ? path_1.default.basename(f) : path_1.default.basename(f?.path || f?.file || ''))),
|
|
544
|
+
].length
|
|
545
|
+
: 0;
|
|
546
|
+
console.log(` š¦ Payload preview:`, JSON.stringify({
|
|
547
|
+
status: test.status,
|
|
548
|
+
title: test.title,
|
|
549
|
+
id: test.id,
|
|
550
|
+
rid: test.rid,
|
|
551
|
+
uniqueFiles,
|
|
552
|
+
steps: test.steps ? test.steps.length : 0,
|
|
553
|
+
artifactRecords: test.artifactRecords ? test.artifactRecords.length : 0,
|
|
554
|
+
}, null, 2));
|
|
555
|
+
}
|
|
556
|
+
const addTestRunResult = await client.addTestRun(test.status, test);
|
|
217
557
|
successCount++;
|
|
218
558
|
this.onProgress({
|
|
219
559
|
current: index + 1,
|
|
220
560
|
total: tests.length,
|
|
221
561
|
test,
|
|
222
|
-
success: true
|
|
562
|
+
success: true,
|
|
223
563
|
});
|
|
224
564
|
}
|
|
225
565
|
catch (err) {
|
|
226
566
|
failureCount++;
|
|
567
|
+
if (isEnabled) {
|
|
568
|
+
console.log(` ā Failed to send: ${err.message}`);
|
|
569
|
+
console.log(` š Error details:`, err);
|
|
570
|
+
}
|
|
227
571
|
this.onError(`Failed to send test ${index + 1}: ${err.message}`);
|
|
228
572
|
this.onProgress({
|
|
229
573
|
current: index + 1,
|
|
230
574
|
total: tests.length,
|
|
231
575
|
test,
|
|
232
576
|
success: false,
|
|
233
|
-
error: err.message
|
|
577
|
+
error: err.message,
|
|
234
578
|
});
|
|
235
579
|
}
|
|
236
580
|
}
|
|
581
|
+
if (isEnabled) {
|
|
582
|
+
console.log('\n' + '='.repeat(60));
|
|
583
|
+
console.log('š FINISHING RUN');
|
|
584
|
+
console.log(`š Finish params:`, JSON.stringify(finishParams, null, 2));
|
|
585
|
+
}
|
|
237
586
|
await client.updateRunStatus(finishParams.status || constants_js_1.STATUS.FINISHED, finishParams.parallel || false);
|
|
587
|
+
if (isEnabled) {
|
|
588
|
+
console.log(`ā
Run finished with status: ${finishParams.status || constants_js_1.STATUS.FINISHED}`);
|
|
589
|
+
// Wait a bit for batch system to finish sending remaining tests
|
|
590
|
+
const testomatioPipe = client.pipes && client.pipes.find(p => p.toString().includes('Testomatio'));
|
|
591
|
+
if (testomatioPipe &&
|
|
592
|
+
testomatioPipe.batch &&
|
|
593
|
+
testomatioPipe.batch.tests &&
|
|
594
|
+
testomatioPipe.batch.tests.length > 0) {
|
|
595
|
+
console.log(`ā³ Waiting for batch system to send ${testomatioPipe.batch.tests.length} remaining tests...`);
|
|
596
|
+
await new Promise(resolve => setTimeout(resolve, 6000)); // Wait 6 seconds for batch upload
|
|
597
|
+
const remainingAfterWait = testomatioPipe.batch.tests ? testomatioPipe.batch.tests.length : 0;
|
|
598
|
+
if (remainingAfterWait > 0) {
|
|
599
|
+
console.log(`ā ļø ${remainingAfterWait} tests still in batch queue after waiting`);
|
|
600
|
+
}
|
|
601
|
+
else {
|
|
602
|
+
console.log(`ā
All tests successfully sent via batch system`);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
238
606
|
const result = {
|
|
239
607
|
success: true,
|
|
240
608
|
testsCount: tests.length,
|
|
@@ -243,8 +611,28 @@ class Replay {
|
|
|
243
611
|
runParams,
|
|
244
612
|
finishParams,
|
|
245
613
|
envVars,
|
|
246
|
-
runId: runId || client.runId
|
|
614
|
+
runId: runId || client.runId,
|
|
247
615
|
};
|
|
616
|
+
if (isEnabled) {
|
|
617
|
+
console.log('\n' + '='.repeat(60));
|
|
618
|
+
console.log('š FINAL SUMMARY:');
|
|
619
|
+
console.log(` š Total tests: ${tests.length}`);
|
|
620
|
+
console.log(` ā
Successfully sent: ${successCount}`);
|
|
621
|
+
console.log(` ā Failed to send: ${failureCount}`);
|
|
622
|
+
console.log(` š Run ID: ${runId || client.runId}`);
|
|
623
|
+
console.log(` š Environment: ${envVars.TESTOMATIO_ENV || 'Unknown'}`);
|
|
624
|
+
console.log(` š API URL: ${envVars.TESTOMATIO_URL || 'Default'}`);
|
|
625
|
+
// Check final batch status
|
|
626
|
+
if (client.pipes && client.pipes.length > 0) {
|
|
627
|
+
const testomatioPipe = client.pipes.find(p => p.toString().includes('Testomatio'));
|
|
628
|
+
if (testomatioPipe && testomatioPipe.batch) {
|
|
629
|
+
const remainingTests = testomatioPipe.batch.tests ? testomatioPipe.batch.tests.length : 0;
|
|
630
|
+
console.log(` š Final batch queue: ${remainingTests} tests remaining`);
|
|
631
|
+
console.log(` ā±ļø Batch interval: ${testomatioPipe.batch.intervalFunction ? 'STILL RUNNING' : 'STOPPED'}`);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
console.log('='.repeat(60));
|
|
635
|
+
}
|
|
248
636
|
this.onLog(`Successfully replayed ${successCount}/${tests.length} tests from debug file`);
|
|
249
637
|
return result;
|
|
250
638
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const extensionMap: {
|
|
2
|
+
'application/json': string;
|
|
3
|
+
'text/plain': string;
|
|
4
|
+
'image/jpeg': string;
|
|
5
|
+
'image/png': string;
|
|
6
|
+
'text/html': string;
|
|
7
|
+
'text/css': string;
|
|
8
|
+
'text/javascript': string;
|
|
9
|
+
'application/pdf': string;
|
|
10
|
+
'application/xml': string;
|
|
11
|
+
'text/xml': string;
|
|
12
|
+
};
|