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