@testomatio/reporter 2.3.0-beta.6-links → 2.3.1-beta.1-dependency
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/codecept.js +2 -2
- package/lib/adapter/playwright.js +27 -1
- package/lib/adapter/webdriver.js +4 -2
- package/lib/bin/cli.js +9 -6
- package/lib/client.d.ts +1 -2
- package/lib/client.js +4 -4
- package/lib/data-storage.d.ts +1 -1
- package/lib/pipe/debug.js +1 -1
- package/lib/pipe/testomatio.d.ts +0 -1
- package/lib/pipe/testomatio.js +19 -19
- package/lib/replay.js +1 -1
- package/lib/reporter.d.ts +12 -12
- package/lib/services/artifacts.d.ts +1 -1
- package/lib/services/key-values.d.ts +1 -1
- package/lib/services/links.d.ts +1 -1
- package/lib/services/logger.d.ts +1 -1
- package/package.json +37 -28
- package/src/adapter/codecept.js +2 -2
- package/src/adapter/playwright.js +31 -1
- package/src/adapter/webdriver.js +6 -2
- package/src/bin/cli.js +9 -6
- package/src/client.js +4 -3
- package/src/pipe/debug.js +2 -3
- package/src/pipe/testomatio.js +75 -77
- package/src/replay.js +1 -1
package/lib/adapter/codecept.js
CHANGED
|
@@ -401,10 +401,10 @@ function formatHookStep(step) {
|
|
|
401
401
|
// For hook steps, construct title from available properties
|
|
402
402
|
let title = step.name;
|
|
403
403
|
if (step.actor && step.name) {
|
|
404
|
-
title = `${step.actor}
|
|
404
|
+
title = `${step.actor} ${step.name}`;
|
|
405
405
|
if (step.args && step.args.length > 0) {
|
|
406
406
|
const argsStr = step.args.map(arg => JSON.stringify(arg)).join(', ');
|
|
407
|
-
title += `
|
|
407
|
+
title += ` ${argsStr}`;
|
|
408
408
|
}
|
|
409
409
|
}
|
|
410
410
|
return {
|
|
@@ -48,6 +48,8 @@ class PlaywrightReporter {
|
|
|
48
48
|
steps.push(appendedStep);
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
+
// Extract and normalize tags
|
|
52
|
+
const tags = extractTags(test);
|
|
51
53
|
const fullTestTitle = getTestContextName(test);
|
|
52
54
|
let logs = '';
|
|
53
55
|
if (result.stderr.length || result.stdout.length) {
|
|
@@ -86,9 +88,10 @@ class PlaywrightReporter {
|
|
|
86
88
|
const reportTestPromise = this.client.addTestRun(checkStatus(status), {
|
|
87
89
|
rid: `${rid}-${project.name}`,
|
|
88
90
|
error,
|
|
89
|
-
test_id: (0, utils_js_1.getTestomatIdFromTestTitle)(`${title} ${
|
|
91
|
+
test_id: (0, utils_js_1.getTestomatIdFromTestTitle)(`${title} ${tags.join(' ')}`),
|
|
90
92
|
suite_title,
|
|
91
93
|
title,
|
|
94
|
+
tags,
|
|
92
95
|
steps: steps.length ? steps : undefined,
|
|
93
96
|
time: duration,
|
|
94
97
|
logs,
|
|
@@ -218,6 +221,29 @@ function generateTmpFilepath(filename = '') {
|
|
|
218
221
|
const tmpdir = os_1.default.tmpdir();
|
|
219
222
|
return path_1.default.join(tmpdir, filename);
|
|
220
223
|
}
|
|
224
|
+
/**
|
|
225
|
+
* Extracts and normalizes tags from test title, test options, and suite level
|
|
226
|
+
* @param {*} test - testInfo object from Playwright
|
|
227
|
+
* @returns {string[]} - array of normalized tags
|
|
228
|
+
*/
|
|
229
|
+
function extractTags(test) {
|
|
230
|
+
const tagsSet = new Set();
|
|
231
|
+
// Extract tags from test title (@tag format)
|
|
232
|
+
const titleTagsMatch = test.title.match(/@\w+/g);
|
|
233
|
+
if (titleTagsMatch) {
|
|
234
|
+
titleTagsMatch.forEach(tag => {
|
|
235
|
+
tagsSet.add(tag.replace('@', '').toLowerCase());
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
// Extract tags from test.tags (Playwright built-in tags)
|
|
239
|
+
if (test.tags && Array.isArray(test.tags)) {
|
|
240
|
+
test.tags.forEach(tag => {
|
|
241
|
+
const normalizedTag = typeof tag === 'string' ? tag.replace('@', '').toLowerCase() : String(tag).toLowerCase();
|
|
242
|
+
tagsSet.add(normalizedTag);
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
return Array.from(tagsSet);
|
|
246
|
+
}
|
|
221
247
|
/**
|
|
222
248
|
* Returns filename + test title
|
|
223
249
|
* @param {*} test - testInfo object from Playwright
|
package/lib/adapter/webdriver.js
CHANGED
|
@@ -41,6 +41,7 @@ const client_js_1 = __importDefault(require("../client.js"));
|
|
|
41
41
|
const utils_js_1 = require("../utils/utils.js");
|
|
42
42
|
const index_js_1 = require("../services/index.js");
|
|
43
43
|
const constants_js_1 = require("../constants.js");
|
|
44
|
+
const data_storage_js_1 = require("../data-storage.js");
|
|
44
45
|
class WebdriverReporter extends reporter_1.default {
|
|
45
46
|
constructor(options) {
|
|
46
47
|
super(options);
|
|
@@ -98,9 +99,10 @@ class WebdriverReporter extends reporter_1.default {
|
|
|
98
99
|
const screenshotsBuffers = output
|
|
99
100
|
.filter(el => el.endpoint === screenshotEndpoint && el.result && el.result.value)
|
|
100
101
|
.map(el => Buffer.from(el.result.value, 'base64'));
|
|
102
|
+
const rid = (0, data_storage_js_1.stringToMD5Hash)(test.fullTitle);
|
|
101
103
|
await this.client.addTestRun(state, {
|
|
102
|
-
rid
|
|
103
|
-
manuallyAttachedArtifacts: artifacts,
|
|
104
|
+
rid,
|
|
105
|
+
manuallyAttachedArtifacts: test.artifacts,
|
|
104
106
|
error,
|
|
105
107
|
logs,
|
|
106
108
|
meta,
|
package/lib/bin/cli.js
CHANGED
|
@@ -78,7 +78,7 @@ program
|
|
|
78
78
|
console.log(constants_js_1.APP_PREFIX, `No command provided. Use -c option to launch a test runner.`);
|
|
79
79
|
return process.exit(255);
|
|
80
80
|
}
|
|
81
|
-
const client = new client_js_1.default({ apiKey, title
|
|
81
|
+
const client = new client_js_1.default({ apiKey, title });
|
|
82
82
|
if (opts.filter) {
|
|
83
83
|
const [pipe, ...optsArray] = opts.filter.split(':');
|
|
84
84
|
const pipeOptions = optsArray.join(':');
|
|
@@ -95,13 +95,16 @@ program
|
|
|
95
95
|
console.log(constants_js_1.APP_PREFIX, `🚀 Running`, picocolors_1.default.green(command));
|
|
96
96
|
const runTests = async () => {
|
|
97
97
|
const testCmds = command.split(' ');
|
|
98
|
-
const cmd = (0, cross_spawn_1.spawn)(testCmds[0], testCmds.slice(1), {
|
|
98
|
+
const cmd = (0, cross_spawn_1.spawn)(testCmds[0], testCmds.slice(1), {
|
|
99
|
+
stdio: 'inherit',
|
|
100
|
+
env: { ...process.env, TESTOMATIO_PROCEED: 'true', runId: client.runId },
|
|
101
|
+
});
|
|
99
102
|
cmd.on('close', async (code) => {
|
|
100
103
|
const emoji = code === 0 ? '🟢' : '🔴';
|
|
101
104
|
console.log(constants_js_1.APP_PREFIX, emoji, `Runner exited with ${picocolors_1.default.bold(code)}`);
|
|
102
105
|
if (apiKey) {
|
|
103
106
|
const status = code === 0 ? 'passed' : 'failed';
|
|
104
|
-
await client.updateRunStatus(status
|
|
107
|
+
await client.updateRunStatus(status);
|
|
105
108
|
}
|
|
106
109
|
process.exit(code);
|
|
107
110
|
});
|
|
@@ -257,13 +260,13 @@ program
|
|
|
257
260
|
const replayService = new replay_js_1.default({
|
|
258
261
|
apiKey: config_js_1.config.TESTOMATIO,
|
|
259
262
|
dryRun: opts.dryRun,
|
|
260
|
-
onLog:
|
|
261
|
-
onError:
|
|
263
|
+
onLog: message => console.log(constants_js_1.APP_PREFIX, message),
|
|
264
|
+
onError: message => console.error(constants_js_1.APP_PREFIX, '⚠️ ', message),
|
|
262
265
|
onProgress: ({ current, total }) => {
|
|
263
266
|
if (current % 10 === 0 || current === total) {
|
|
264
267
|
console.log(constants_js_1.APP_PREFIX, `📊 Progress: ${current}/${total} tests processed`);
|
|
265
268
|
}
|
|
266
|
-
}
|
|
269
|
+
},
|
|
267
270
|
});
|
|
268
271
|
const result = await replayService.replay(debugFile);
|
|
269
272
|
if (result.dryRun) {
|
package/lib/client.d.ts
CHANGED
|
@@ -58,10 +58,9 @@ export class Client {
|
|
|
58
58
|
* Updates the status of the current test run and finishes the run.
|
|
59
59
|
* @param {'passed' | 'failed' | 'skipped' | 'finished'} status - The status of the current test run.
|
|
60
60
|
* Must be one of "passed", "failed", or "finished"
|
|
61
|
-
* @param {boolean} [isParallel] - Whether the current test run was executed in parallel with other tests.
|
|
62
61
|
* @returns {Promise<any>} - A Promise that resolves when finishes the run.
|
|
63
62
|
*/
|
|
64
|
-
updateRunStatus(status: "passed" | "failed" | "skipped" | "finished"
|
|
63
|
+
updateRunStatus(status: "passed" | "failed" | "skipped" | "finished"): Promise<any>;
|
|
65
64
|
/**
|
|
66
65
|
* Returns the formatted stack including the stack trace, steps, and logs.
|
|
67
66
|
* @returns {string}
|
package/lib/client.js
CHANGED
|
@@ -181,7 +181,7 @@ class Client {
|
|
|
181
181
|
/**
|
|
182
182
|
* @type {TestData}
|
|
183
183
|
*/
|
|
184
|
-
const { rid, error = null, time = 0, example = null, files = [], filesBuffers = [], steps, code = null, title, file, suite_title, suite_id, test_id, timestamp, links, manuallyAttachedArtifacts, overwrite, } = testData;
|
|
184
|
+
const { rid, error = null, time = 0, example = null, files = [], filesBuffers = [], steps, code = null, title, file, suite_title, suite_id, test_id, timestamp, links, manuallyAttachedArtifacts, overwrite, tags, } = testData;
|
|
185
185
|
let { message = '', meta = {} } = testData;
|
|
186
186
|
// stringify meta values and limit keys and values length to 255
|
|
187
187
|
meta = Object.entries(meta)
|
|
@@ -242,6 +242,7 @@ class Client {
|
|
|
242
242
|
meta,
|
|
243
243
|
links,
|
|
244
244
|
overwrite,
|
|
245
|
+
tags,
|
|
245
246
|
...(rootSuiteId && { root_suite_id: rootSuiteId }),
|
|
246
247
|
};
|
|
247
248
|
// debug('Adding test run...', data);
|
|
@@ -263,17 +264,16 @@ class Client {
|
|
|
263
264
|
* Updates the status of the current test run and finishes the run.
|
|
264
265
|
* @param {'passed' | 'failed' | 'skipped' | 'finished'} status - The status of the current test run.
|
|
265
266
|
* Must be one of "passed", "failed", or "finished"
|
|
266
|
-
* @param {boolean} [isParallel] - Whether the current test run was executed in parallel with other tests.
|
|
267
267
|
* @returns {Promise<any>} - A Promise that resolves when finishes the run.
|
|
268
268
|
*/
|
|
269
|
-
async updateRunStatus(status
|
|
269
|
+
async updateRunStatus(status) {
|
|
270
270
|
this.pipes ||= await (0, index_js_1.pipesFactory)(this.paramsForPipesFactory || {}, this.pipeStore);
|
|
271
271
|
this.runId ||= (0, utils_js_1.readLatestRunId)();
|
|
272
272
|
debug('Updating run status...');
|
|
273
273
|
// all pipes disabled, skipping
|
|
274
274
|
if (!this.pipes?.filter(p => p.isEnabled).length)
|
|
275
275
|
return Promise.resolve();
|
|
276
|
-
const runParams = { status
|
|
276
|
+
const runParams = { status };
|
|
277
277
|
this.queue = this.queue
|
|
278
278
|
.then(() => Promise.all(this.pipes.map(p => p.finishRun(runParams))))
|
|
279
279
|
.then(() => {
|
package/lib/data-storage.d.ts
CHANGED
package/lib/pipe/debug.js
CHANGED
|
@@ -18,7 +18,7 @@ class DebugPipe {
|
|
|
18
18
|
this.isEnabled = !!process.env.TESTOMATIO_DEBUG || !!process.env.DEBUG;
|
|
19
19
|
if (this.isEnabled) {
|
|
20
20
|
this.batch = {
|
|
21
|
-
isEnabled: this.params.isBatchEnabled ?? !process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD
|
|
21
|
+
isEnabled: this.params.isBatchEnabled ?? !process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD,
|
|
22
22
|
intervalFunction: null,
|
|
23
23
|
intervalTime: 5000,
|
|
24
24
|
tests: [],
|
package/lib/pipe/testomatio.d.ts
CHANGED
package/lib/pipe/testomatio.js
CHANGED
|
@@ -23,7 +23,7 @@ if (process.env.TESTOMATIO_RUN)
|
|
|
23
23
|
class TestomatioPipe {
|
|
24
24
|
constructor(params, store) {
|
|
25
25
|
this.batch = {
|
|
26
|
-
isEnabled: params?.isBatchEnabled ?? !process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD
|
|
26
|
+
isEnabled: params?.isBatchEnabled ?? !process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD,
|
|
27
27
|
intervalFunction: null, // will be created in createRun by setInterval function
|
|
28
28
|
intervalTime: 5000, // how often tests are sent
|
|
29
29
|
tests: [], // array of tests in batch
|
|
@@ -43,7 +43,6 @@ class TestomatioPipe {
|
|
|
43
43
|
debug('Testomatio Pipe: Enabled');
|
|
44
44
|
const proxyUrl = process.env.HTTP_PROXY || process.env.HTTPS_PROXY;
|
|
45
45
|
const proxy = proxyUrl ? new URL(proxyUrl) : null;
|
|
46
|
-
this.parallel = params.parallel;
|
|
47
46
|
this.store = store || {};
|
|
48
47
|
this.title = params.title || process.env.TESTOMATIO_TITLE;
|
|
49
48
|
this.sharedRun = !!process.env.TESTOMATIO_SHARED_RUN;
|
|
@@ -61,7 +60,7 @@ class TestomatioPipe {
|
|
|
61
60
|
retry: constants_js_1.REPORTER_REQUEST_RETRIES.retriesPerRequest,
|
|
62
61
|
retryDelay: constants_js_1.REPORTER_REQUEST_RETRIES.retryTimeout,
|
|
63
62
|
httpMethodsToRetry: ['GET', 'PUT', 'HEAD', 'OPTIONS', 'DELETE', 'POST'],
|
|
64
|
-
shouldRetry:
|
|
63
|
+
shouldRetry: error => {
|
|
65
64
|
if (!error.response)
|
|
66
65
|
return false;
|
|
67
66
|
switch (error.response?.status) {
|
|
@@ -74,8 +73,8 @@ class TestomatioPipe {
|
|
|
74
73
|
break;
|
|
75
74
|
}
|
|
76
75
|
return error.response?.status >= 401; // Retry on 401+ and 5xx
|
|
77
|
-
}
|
|
78
|
-
}
|
|
76
|
+
},
|
|
77
|
+
},
|
|
79
78
|
});
|
|
80
79
|
this.isEnabled = true;
|
|
81
80
|
// do not finish this run (for parallel testing)
|
|
@@ -154,7 +153,6 @@ class TestomatioPipe {
|
|
|
154
153
|
const accessEvent = process.env.TESTOMATIO_PUBLISH ? 'publish' : null;
|
|
155
154
|
const runParams = Object.fromEntries(Object.entries({
|
|
156
155
|
ci_build_url: buildUrl,
|
|
157
|
-
parallel: this.parallel,
|
|
158
156
|
api_key: this.apiKey.trim(),
|
|
159
157
|
group_title: this.groupTitle,
|
|
160
158
|
access_event: accessEvent,
|
|
@@ -173,7 +171,7 @@ class TestomatioPipe {
|
|
|
173
171
|
method: 'PUT',
|
|
174
172
|
url: `/api/reporter/${this.runId}`,
|
|
175
173
|
data: runParams,
|
|
176
|
-
responseType: 'json'
|
|
174
|
+
responseType: 'json',
|
|
177
175
|
});
|
|
178
176
|
if (resp.data.artifacts)
|
|
179
177
|
(0, pipe_utils_js_1.setS3Credentials)(resp.data.artifacts);
|
|
@@ -186,7 +184,7 @@ class TestomatioPipe {
|
|
|
186
184
|
url: '/api/reporter',
|
|
187
185
|
data: runParams,
|
|
188
186
|
maxContentLength: Infinity,
|
|
189
|
-
responseType: 'json'
|
|
187
|
+
responseType: 'json',
|
|
190
188
|
});
|
|
191
189
|
this.runId = resp.data.uid;
|
|
192
190
|
this.runUrl = `${this.url}/${resp.data.url.split('/').splice(3).join('/')}`;
|
|
@@ -243,15 +241,17 @@ class TestomatioPipe {
|
|
|
243
241
|
}
|
|
244
242
|
const json = json_cycle_1.default.stringify(data);
|
|
245
243
|
debug('Adding test', json);
|
|
246
|
-
return this.client
|
|
244
|
+
return this.client
|
|
245
|
+
.request({
|
|
247
246
|
method: 'POST',
|
|
248
247
|
url: `/api/reporter/${this.runId}/testrun`,
|
|
249
248
|
data: json,
|
|
250
249
|
headers: {
|
|
251
250
|
'Content-Type': 'application/json',
|
|
252
251
|
},
|
|
253
|
-
maxContentLength: Infinity
|
|
254
|
-
})
|
|
252
|
+
maxContentLength: Infinity,
|
|
253
|
+
})
|
|
254
|
+
.catch(err => {
|
|
255
255
|
this.requestFailures++;
|
|
256
256
|
this.notReportedTestsCount++;
|
|
257
257
|
if (err.response) {
|
|
@@ -296,19 +296,21 @@ class TestomatioPipe {
|
|
|
296
296
|
// get tests from batch and clear batch
|
|
297
297
|
const testsToSend = this.batch.tests.splice(0);
|
|
298
298
|
debug('📨 Batch upload', testsToSend.length, 'tests');
|
|
299
|
-
return this.client
|
|
299
|
+
return this.client
|
|
300
|
+
.request({
|
|
300
301
|
method: 'POST',
|
|
301
302
|
url: `/api/reporter/${this.runId}/testrun`,
|
|
302
303
|
data: {
|
|
303
304
|
api_key: this.apiKey,
|
|
304
305
|
tests: testsToSend,
|
|
305
|
-
batch_index: this.batch.batchIndex
|
|
306
|
+
batch_index: this.batch.batchIndex,
|
|
306
307
|
},
|
|
307
308
|
headers: {
|
|
308
309
|
'Content-Type': 'application/json',
|
|
309
310
|
},
|
|
310
|
-
maxContentLength: Infinity
|
|
311
|
-
})
|
|
311
|
+
maxContentLength: Infinity,
|
|
312
|
+
})
|
|
313
|
+
.catch(err => {
|
|
312
314
|
this.requestFailures++;
|
|
313
315
|
this.notReportedTestsCount += testsToSend.length;
|
|
314
316
|
if (err.response) {
|
|
@@ -377,7 +379,7 @@ class TestomatioPipe {
|
|
|
377
379
|
const errorMessage = picocolors_1.default.red(`⚠️ Due to request failures, ${this.notReportedTestsCount} test(s) were not reported to Testomat.io`);
|
|
378
380
|
console.warn(`${constants_js_1.APP_PREFIX} ${errorMessage}`);
|
|
379
381
|
}
|
|
380
|
-
const { status
|
|
382
|
+
const { status } = params;
|
|
381
383
|
let status_event;
|
|
382
384
|
if (status === constants_js_1.STATUS.FINISHED)
|
|
383
385
|
status_event = 'finish';
|
|
@@ -385,8 +387,6 @@ class TestomatioPipe {
|
|
|
385
387
|
status_event = 'pass';
|
|
386
388
|
if (status === constants_js_1.STATUS.FAILED)
|
|
387
389
|
status_event = 'fail';
|
|
388
|
-
if (parallel)
|
|
389
|
-
status_event += '_parallel';
|
|
390
390
|
try {
|
|
391
391
|
if (this.runId && !this.proceed) {
|
|
392
392
|
await this.client.request({
|
|
@@ -398,7 +398,7 @@ class TestomatioPipe {
|
|
|
398
398
|
status_event,
|
|
399
399
|
detach: params.detach,
|
|
400
400
|
tests: params.tests,
|
|
401
|
-
}
|
|
401
|
+
},
|
|
402
402
|
});
|
|
403
403
|
if (this.runUrl) {
|
|
404
404
|
console.log(constants_js_1.APP_PREFIX, '📊 Report Saved. Report URL:', picocolors_1.default.magenta(this.runUrl));
|
package/lib/replay.js
CHANGED
|
@@ -238,7 +238,7 @@ class Replay {
|
|
|
238
238
|
});
|
|
239
239
|
}
|
|
240
240
|
}
|
|
241
|
-
await client.updateRunStatus(finishParams.status || constants_js_1.STATUS.FINISHED
|
|
241
|
+
await client.updateRunStatus(finishParams.status || constants_js_1.STATUS.FINISHED);
|
|
242
242
|
const result = {
|
|
243
243
|
success: true,
|
|
244
244
|
testsCount: tests.length,
|
package/lib/reporter.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export const artifact: (data: string | {
|
|
|
5
5
|
}, context?: any) => void;
|
|
6
6
|
export const log: (...args: any[]) => void;
|
|
7
7
|
export const logger: {
|
|
8
|
-
|
|
8
|
+
#originalUserLogger: {
|
|
9
9
|
assert(condition?: boolean, ...data: any[]): void;
|
|
10
10
|
assert(value: any, message?: string, ...optionalParams: any[]): void;
|
|
11
11
|
clear(): void;
|
|
@@ -50,13 +50,13 @@ export const logger: {
|
|
|
50
50
|
profile(label?: string): void;
|
|
51
51
|
profileEnd(label?: string): void;
|
|
52
52
|
};
|
|
53
|
-
|
|
53
|
+
#userLoggerWithOverridenMethods: any;
|
|
54
54
|
logLevel: string;
|
|
55
55
|
step(strings: any, ...values: any[]): void;
|
|
56
56
|
getLogs(context: string): string[];
|
|
57
|
-
|
|
57
|
+
#stringifyLogs(...args: any[]): string;
|
|
58
58
|
_templateLiteralLog(strings: any, ...args: any[]): void;
|
|
59
|
-
|
|
59
|
+
#logWrapper(argsArray: any, level: any): void;
|
|
60
60
|
assert(...args: any[]): void;
|
|
61
61
|
debug(...args: any[]): void;
|
|
62
62
|
error(...args: any[]): void;
|
|
@@ -81,7 +81,7 @@ export const linkTest: (...testIds: string[]) => void;
|
|
|
81
81
|
export const linkJira: (...jiraIds: string[]) => void;
|
|
82
82
|
declare namespace _default {
|
|
83
83
|
let testomatioLogger: {
|
|
84
|
-
|
|
84
|
+
#originalUserLogger: {
|
|
85
85
|
assert(condition?: boolean, ...data: any[]): void;
|
|
86
86
|
assert(value: any, message?: string, ...optionalParams: any[]): void;
|
|
87
87
|
clear(): void;
|
|
@@ -126,13 +126,13 @@ declare namespace _default {
|
|
|
126
126
|
profile(label?: string): void;
|
|
127
127
|
profileEnd(label?: string): void;
|
|
128
128
|
};
|
|
129
|
-
|
|
129
|
+
#userLoggerWithOverridenMethods: any;
|
|
130
130
|
logLevel: string;
|
|
131
131
|
step(strings: any, ...values: any[]): void;
|
|
132
132
|
getLogs(context: string): string[];
|
|
133
|
-
|
|
133
|
+
#stringifyLogs(...args: any[]): string;
|
|
134
134
|
_templateLiteralLog(strings: any, ...args: any[]): void;
|
|
135
|
-
|
|
135
|
+
#logWrapper(argsArray: any, level: any): void;
|
|
136
136
|
assert(...args: any[]): void;
|
|
137
137
|
debug(...args: any[]): void;
|
|
138
138
|
error(...args: any[]): void;
|
|
@@ -155,7 +155,7 @@ declare namespace _default {
|
|
|
155
155
|
}, context?: any) => void;
|
|
156
156
|
let log: (...args: any[]) => void;
|
|
157
157
|
let logger: {
|
|
158
|
-
|
|
158
|
+
#originalUserLogger: {
|
|
159
159
|
assert(condition?: boolean, ...data: any[]): void;
|
|
160
160
|
assert(value: any, message?: string, ...optionalParams: any[]): void;
|
|
161
161
|
clear(): void;
|
|
@@ -200,13 +200,13 @@ declare namespace _default {
|
|
|
200
200
|
profile(label?: string): void;
|
|
201
201
|
profileEnd(label?: string): void;
|
|
202
202
|
};
|
|
203
|
-
|
|
203
|
+
#userLoggerWithOverridenMethods: any;
|
|
204
204
|
logLevel: string;
|
|
205
205
|
step(strings: any, ...values: any[]): void;
|
|
206
206
|
getLogs(context: string): string[];
|
|
207
|
-
|
|
207
|
+
#stringifyLogs(...args: any[]): string;
|
|
208
208
|
_templateLiteralLog(strings: any, ...args: any[]): void;
|
|
209
|
-
|
|
209
|
+
#logWrapper(argsArray: any, level: any): void;
|
|
210
210
|
assert(...args: any[]): void;
|
|
211
211
|
debug(...args: any[]): void;
|
|
212
212
|
error(...args: any[]): void;
|
package/lib/services/links.d.ts
CHANGED
package/lib/services/logger.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testomatio/reporter",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.1-beta.1-dependency",
|
|
4
4
|
"description": "Testomatio Reporter Client",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=18"
|
|
@@ -10,13 +10,20 @@
|
|
|
10
10
|
"author": "Michael Bodnarchuk <davert@testomat.io>,Koushik Mohan <koushikmohan1996@gmail.com>",
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"type": "module",
|
|
13
|
+
"overrides": {
|
|
14
|
+
"tmp": "0.2.4",
|
|
15
|
+
"external-editor": {
|
|
16
|
+
"tmp": "0.2.4"
|
|
17
|
+
},
|
|
18
|
+
"follow-redirects": "1.15.6",
|
|
19
|
+
"axios": "^1.7.0"
|
|
20
|
+
},
|
|
13
21
|
"dependencies": {
|
|
14
22
|
"@aws-sdk/client-s3": "^3.279.0",
|
|
15
23
|
"@aws-sdk/lib-storage": "^3.279.0",
|
|
16
|
-
"@cucumber/cucumber": "^
|
|
24
|
+
"@cucumber/cucumber": "^12.2.0",
|
|
17
25
|
"@octokit/rest": "^21.1.1",
|
|
18
26
|
"aws-sdk": "^2.1072.0",
|
|
19
|
-
"gaxios": ">=6.0 || >=7.0.0-rc.4 || <8",
|
|
20
27
|
"callsite-record": "^4.1.4",
|
|
21
28
|
"commander": "^12",
|
|
22
29
|
"cross-spawn": "^7.0.3",
|
|
@@ -26,6 +33,7 @@
|
|
|
26
33
|
"fast-xml-parser": "^4.4.1",
|
|
27
34
|
"file-url": "3.0.0",
|
|
28
35
|
"filesize": "^10.1.6",
|
|
36
|
+
"gaxios": ">=6.0 || >=7.0.0-rc.4 || <8",
|
|
29
37
|
"glob": "^10.3",
|
|
30
38
|
"handlebars": "^4.7.8",
|
|
31
39
|
"has-flag": "^5.0.1",
|
|
@@ -85,7 +93,7 @@
|
|
|
85
93
|
"@types/mocha": "^10.0.7",
|
|
86
94
|
"@wdio/reporter": "^7.16.13",
|
|
87
95
|
"chai": "^4.3.6",
|
|
88
|
-
"codeceptjs": "^3.
|
|
96
|
+
"codeceptjs": "^3.7.4",
|
|
89
97
|
"cucumber": "^6.0.7",
|
|
90
98
|
"eslint": "^9.24.0",
|
|
91
99
|
"eslint-config-prettier": "^8.3.0",
|
|
@@ -93,13 +101,13 @@
|
|
|
93
101
|
"jasmine": "^5.2.0",
|
|
94
102
|
"jest": "^27.4.7",
|
|
95
103
|
"jsdom": "^22.1.0",
|
|
96
|
-
"mocha": "^
|
|
104
|
+
"mocha": "^11.7.2",
|
|
97
105
|
"mock-http-server": "^1.4.5",
|
|
98
106
|
"pino": "^8.15.0",
|
|
99
107
|
"prettier": "^3.2.5",
|
|
100
108
|
"puppeteer": "^22.15.0",
|
|
101
109
|
"typescript": "^5.5.4",
|
|
102
|
-
"vitest": "^
|
|
110
|
+
"vitest": "^3.2.4"
|
|
103
111
|
},
|
|
104
112
|
"bin": {
|
|
105
113
|
"@testomatio/reporter": "./lib/bin/cli.js",
|
|
@@ -113,27 +121,28 @@
|
|
|
113
121
|
"require": "./lib/reporter.js",
|
|
114
122
|
"types": "./types/types.d.ts"
|
|
115
123
|
},
|
|
116
|
-
"
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
124
|
+
"exports": {
|
|
125
|
+
".": {
|
|
126
|
+
"import": "./src/reporter.js",
|
|
127
|
+
"require": "./lib/reporter.js",
|
|
128
|
+
"types": "./types/types.d.ts"
|
|
129
|
+
},
|
|
130
|
+
"./cypress-plugin": "./lib/adapter/cypress-plugin/index.js",
|
|
131
|
+
"./cypress": "./lib/adapter/cypress-plugin/index.js",
|
|
132
|
+
"./lib/adapter/jasmine.js": "./lib/adapter/jasmine.js",
|
|
133
|
+
"./jasmine": "./lib/adapter/jasmine.js",
|
|
134
|
+
"./lib/adapter/jest.js": "./lib/adapter/jest.js",
|
|
135
|
+
"./jest": "./lib/adapter/jest.js",
|
|
136
|
+
"./lib/adapter/mocha/mocha.js": "./lib/adapter/mocha.js",
|
|
137
|
+
"./mocha": "./lib/adapter/mocha.js",
|
|
138
|
+
"./lib/adapter/playwright.js": "./lib/adapter/playwright.js",
|
|
139
|
+
"./nightwatch": "./lib/adapter/nightwatch.js",
|
|
140
|
+
"./playwright": "./lib/adapter/playwright.js",
|
|
141
|
+
"./vitest": "./lib/adapter/vitest.js",
|
|
142
|
+
"./lib/adapter/webdriver.js": "./lib/adapter/webdriver.js",
|
|
143
|
+
"./lib/adapter/webdriver": "./lib/adapter/webdriver.js",
|
|
144
|
+
"./webdriver": "./lib/adapter/webdriver.js",
|
|
145
|
+
"./wdio": "./lib/adapter/webdriver.js"
|
|
146
|
+
}
|
|
138
147
|
}
|
|
139
148
|
}
|
package/src/adapter/codecept.js
CHANGED
|
@@ -467,10 +467,10 @@ function formatHookStep(step) {
|
|
|
467
467
|
// For hook steps, construct title from available properties
|
|
468
468
|
let title = step.name;
|
|
469
469
|
if (step.actor && step.name) {
|
|
470
|
-
title = `${step.actor}
|
|
470
|
+
title = `${step.actor} ${step.name}`;
|
|
471
471
|
if (step.args && step.args.length > 0) {
|
|
472
472
|
const argsStr = step.args.map(arg => JSON.stringify(arg)).join(', ');
|
|
473
|
-
title += `
|
|
473
|
+
title += ` ${argsStr}`;
|
|
474
474
|
}
|
|
475
475
|
}
|
|
476
476
|
|
|
@@ -51,6 +51,9 @@ class PlaywrightReporter {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
// Extract and normalize tags
|
|
55
|
+
const tags = extractTags(test);
|
|
56
|
+
|
|
54
57
|
const fullTestTitle = getTestContextName(test);
|
|
55
58
|
let logs = '';
|
|
56
59
|
if (result.stderr.length || result.stdout.length) {
|
|
@@ -90,9 +93,10 @@ class PlaywrightReporter {
|
|
|
90
93
|
const reportTestPromise = this.client.addTestRun(checkStatus(status), {
|
|
91
94
|
rid: `${rid}-${project.name}`,
|
|
92
95
|
error,
|
|
93
|
-
test_id: getTestomatIdFromTestTitle(`${title} ${
|
|
96
|
+
test_id: getTestomatIdFromTestTitle(`${title} ${tags.join(' ')}`),
|
|
94
97
|
suite_title,
|
|
95
98
|
title,
|
|
99
|
+
tags,
|
|
96
100
|
steps: steps.length ? steps : undefined,
|
|
97
101
|
time: duration,
|
|
98
102
|
logs,
|
|
@@ -243,6 +247,32 @@ function generateTmpFilepath(filename = '') {
|
|
|
243
247
|
return path.join(tmpdir, filename);
|
|
244
248
|
}
|
|
245
249
|
|
|
250
|
+
/**
|
|
251
|
+
* Extracts and normalizes tags from test title, test options, and suite level
|
|
252
|
+
* @param {*} test - testInfo object from Playwright
|
|
253
|
+
* @returns {string[]} - array of normalized tags
|
|
254
|
+
*/
|
|
255
|
+
function extractTags(test) {
|
|
256
|
+
const tagsSet = new Set();
|
|
257
|
+
|
|
258
|
+
// Extract tags from test title (@tag format)
|
|
259
|
+
const titleTagsMatch = test.title.match(/@\w+/g);
|
|
260
|
+
if (titleTagsMatch) {
|
|
261
|
+
titleTagsMatch.forEach(tag => {
|
|
262
|
+
tagsSet.add(tag.replace('@', '').toLowerCase());
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Extract tags from test.tags (Playwright built-in tags)
|
|
267
|
+
if (test.tags && Array.isArray(test.tags)) {
|
|
268
|
+
test.tags.forEach(tag => {
|
|
269
|
+
const normalizedTag = typeof tag === 'string' ? tag.replace('@', '').toLowerCase() : String(tag).toLowerCase();
|
|
270
|
+
tagsSet.add(normalizedTag);
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
return Array.from(tagsSet);
|
|
274
|
+
}
|
|
275
|
+
|
|
246
276
|
/**
|
|
247
277
|
* Returns filename + test title
|
|
248
278
|
* @param {*} test - testInfo object from Playwright
|
package/src/adapter/webdriver.js
CHANGED
|
@@ -3,6 +3,7 @@ import TestomatClient from '../client.js';
|
|
|
3
3
|
import { getTestomatIdFromTestTitle, fileSystem } from '../utils/utils.js';
|
|
4
4
|
import { services } from '../services/index.js';
|
|
5
5
|
import { TESTOMAT_TMP_STORAGE_DIR } from '../constants.js';
|
|
6
|
+
import { stringToMD5Hash } from '../data-storage.js';
|
|
6
7
|
|
|
7
8
|
class WebdriverReporter extends WDIOReporter {
|
|
8
9
|
constructor(options) {
|
|
@@ -52,6 +53,7 @@ class WebdriverReporter extends WDIOReporter {
|
|
|
52
53
|
onTestEnd(test) {
|
|
53
54
|
test.suite = test.parent;
|
|
54
55
|
const logs = getTestLogs(test.fullTitle);
|
|
56
|
+
|
|
55
57
|
test.artifacts = services.artifacts.get(test.fullTitle);
|
|
56
58
|
test.meta = services.keyValues.get(test.fullTitle);
|
|
57
59
|
test.links = services.links.get(test.fullTitle);
|
|
@@ -79,9 +81,11 @@ class WebdriverReporter extends WDIOReporter {
|
|
|
79
81
|
.filter(el => el.endpoint === screenshotEndpoint && el.result && el.result.value)
|
|
80
82
|
.map(el => Buffer.from(el.result.value, 'base64'));
|
|
81
83
|
|
|
84
|
+
const rid = stringToMD5Hash(test.fullTitle);
|
|
85
|
+
|
|
82
86
|
await this.client.addTestRun(state, {
|
|
83
|
-
rid
|
|
84
|
-
manuallyAttachedArtifacts: artifacts,
|
|
87
|
+
rid,
|
|
88
|
+
manuallyAttachedArtifacts: test.artifacts,
|
|
85
89
|
error,
|
|
86
90
|
logs,
|
|
87
91
|
meta,
|
package/src/bin/cli.js
CHANGED
|
@@ -85,7 +85,7 @@ program
|
|
|
85
85
|
return process.exit(255);
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
const client = new TestomatClient({ apiKey, title
|
|
88
|
+
const client = new TestomatClient({ apiKey, title });
|
|
89
89
|
|
|
90
90
|
if (opts.filter) {
|
|
91
91
|
const [pipe, ...optsArray] = opts.filter.split(':');
|
|
@@ -105,14 +105,17 @@ program
|
|
|
105
105
|
|
|
106
106
|
const runTests = async () => {
|
|
107
107
|
const testCmds = command.split(' ');
|
|
108
|
-
const cmd = spawn(testCmds[0], testCmds.slice(1), {
|
|
108
|
+
const cmd = spawn(testCmds[0], testCmds.slice(1), {
|
|
109
|
+
stdio: 'inherit',
|
|
110
|
+
env: { ...process.env, TESTOMATIO_PROCEED: 'true', runId: client.runId },
|
|
111
|
+
});
|
|
109
112
|
|
|
110
113
|
cmd.on('close', async code => {
|
|
111
114
|
const emoji = code === 0 ? '🟢' : '🔴';
|
|
112
115
|
console.log(APP_PREFIX, emoji, `Runner exited with ${pc.bold(code)}`);
|
|
113
116
|
if (apiKey) {
|
|
114
117
|
const status = code === 0 ? 'passed' : 'failed';
|
|
115
|
-
await client.updateRunStatus(status
|
|
118
|
+
await client.updateRunStatus(status);
|
|
116
119
|
}
|
|
117
120
|
process.exit(code);
|
|
118
121
|
});
|
|
@@ -310,13 +313,13 @@ program
|
|
|
310
313
|
const replayService = new Replay({
|
|
311
314
|
apiKey: config.TESTOMATIO,
|
|
312
315
|
dryRun: opts.dryRun,
|
|
313
|
-
onLog:
|
|
314
|
-
onError:
|
|
316
|
+
onLog: message => console.log(APP_PREFIX, message),
|
|
317
|
+
onError: message => console.error(APP_PREFIX, '⚠️ ', message),
|
|
315
318
|
onProgress: ({ current, total }) => {
|
|
316
319
|
if (current % 10 === 0 || current === total) {
|
|
317
320
|
console.log(APP_PREFIX, `📊 Progress: ${current}/${total} tests processed`);
|
|
318
321
|
}
|
|
319
|
-
}
|
|
322
|
+
},
|
|
320
323
|
});
|
|
321
324
|
|
|
322
325
|
const result = await replayService.replay(debugFile);
|
package/src/client.js
CHANGED
|
@@ -184,6 +184,7 @@ class Client {
|
|
|
184
184
|
links,
|
|
185
185
|
manuallyAttachedArtifacts,
|
|
186
186
|
overwrite,
|
|
187
|
+
tags,
|
|
187
188
|
} = testData;
|
|
188
189
|
let { message = '', meta = {} } = testData;
|
|
189
190
|
|
|
@@ -254,6 +255,7 @@ class Client {
|
|
|
254
255
|
meta,
|
|
255
256
|
links,
|
|
256
257
|
overwrite,
|
|
258
|
+
tags,
|
|
257
259
|
...(rootSuiteId && { root_suite_id: rootSuiteId }),
|
|
258
260
|
};
|
|
259
261
|
|
|
@@ -282,10 +284,9 @@ class Client {
|
|
|
282
284
|
* Updates the status of the current test run and finishes the run.
|
|
283
285
|
* @param {'passed' | 'failed' | 'skipped' | 'finished'} status - The status of the current test run.
|
|
284
286
|
* Must be one of "passed", "failed", or "finished"
|
|
285
|
-
* @param {boolean} [isParallel] - Whether the current test run was executed in parallel with other tests.
|
|
286
287
|
* @returns {Promise<any>} - A Promise that resolves when finishes the run.
|
|
287
288
|
*/
|
|
288
|
-
async updateRunStatus(status
|
|
289
|
+
async updateRunStatus(status) {
|
|
289
290
|
this.pipes ||= await pipesFactory(this.paramsForPipesFactory || {}, this.pipeStore);
|
|
290
291
|
this.runId ||= readLatestRunId();
|
|
291
292
|
|
|
@@ -293,7 +294,7 @@ class Client {
|
|
|
293
294
|
// all pipes disabled, skipping
|
|
294
295
|
if (!this.pipes?.filter(p => p.isEnabled).length) return Promise.resolve();
|
|
295
296
|
|
|
296
|
-
const runParams = { status
|
|
297
|
+
const runParams = { status };
|
|
297
298
|
|
|
298
299
|
this.queue = this.queue
|
|
299
300
|
.then(() => Promise.all(this.pipes.map(p => p.finishRun(runParams))))
|
package/src/pipe/debug.js
CHANGED
|
@@ -15,7 +15,7 @@ export class DebugPipe {
|
|
|
15
15
|
this.isEnabled = !!process.env.TESTOMATIO_DEBUG || !!process.env.DEBUG;
|
|
16
16
|
if (this.isEnabled) {
|
|
17
17
|
this.batch = {
|
|
18
|
-
isEnabled: this.params.isBatchEnabled ?? !process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD
|
|
18
|
+
isEnabled: this.params.isBatchEnabled ?? !process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD,
|
|
19
19
|
intervalFunction: null,
|
|
20
20
|
intervalTime: 5000,
|
|
21
21
|
tests: [],
|
|
@@ -93,8 +93,7 @@ export class DebugPipe {
|
|
|
93
93
|
const logData = { action: 'addTest', testId: data };
|
|
94
94
|
if (this.store.runId) logData.runId = this.store.runId;
|
|
95
95
|
this.logToFile(logData);
|
|
96
|
-
}
|
|
97
|
-
else this.batch.tests.push(data);
|
|
96
|
+
} else this.batch.tests.push(data);
|
|
98
97
|
|
|
99
98
|
if (!this.batch.intervalFunction) await this.batchUpload();
|
|
100
99
|
}
|
package/src/pipe/testomatio.js
CHANGED
|
@@ -20,7 +20,7 @@ if (process.env.TESTOMATIO_RUN) process.env.runId = process.env.TESTOMATIO_RUN;
|
|
|
20
20
|
class TestomatioPipe {
|
|
21
21
|
constructor(params, store) {
|
|
22
22
|
this.batch = {
|
|
23
|
-
isEnabled: params?.isBatchEnabled ?? !process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD
|
|
23
|
+
isEnabled: params?.isBatchEnabled ?? !process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD,
|
|
24
24
|
intervalFunction: null, // will be created in createRun by setInterval function
|
|
25
25
|
intervalTime: 5000, // how often tests are sent
|
|
26
26
|
tests: [], // array of tests in batch
|
|
@@ -43,7 +43,6 @@ class TestomatioPipe {
|
|
|
43
43
|
const proxyUrl = process.env.HTTP_PROXY || process.env.HTTPS_PROXY;
|
|
44
44
|
const proxy = proxyUrl ? new URL(proxyUrl) : null;
|
|
45
45
|
|
|
46
|
-
this.parallel = params.parallel;
|
|
47
46
|
this.store = store || {};
|
|
48
47
|
this.title = params.title || process.env.TESTOMATIO_TITLE;
|
|
49
48
|
this.sharedRun = !!process.env.TESTOMATIO_SHARED_RUN;
|
|
@@ -61,8 +60,8 @@ class TestomatioPipe {
|
|
|
61
60
|
retryConfig: {
|
|
62
61
|
retry: REPORTER_REQUEST_RETRIES.retriesPerRequest,
|
|
63
62
|
retryDelay: REPORTER_REQUEST_RETRIES.retryTimeout,
|
|
64
|
-
httpMethodsToRetry: ['GET','PUT','HEAD','OPTIONS','DELETE','POST'],
|
|
65
|
-
shouldRetry:
|
|
63
|
+
httpMethodsToRetry: ['GET', 'PUT', 'HEAD', 'OPTIONS', 'DELETE', 'POST'],
|
|
64
|
+
shouldRetry: error => {
|
|
66
65
|
if (!error.response) return false;
|
|
67
66
|
switch (error.response?.status) {
|
|
68
67
|
case 400: // Bad request (probably wrong API key)
|
|
@@ -74,8 +73,8 @@ class TestomatioPipe {
|
|
|
74
73
|
break;
|
|
75
74
|
}
|
|
76
75
|
return error.response?.status >= 401; // Retry on 401+ and 5xx
|
|
77
|
-
}
|
|
78
|
-
}
|
|
76
|
+
},
|
|
77
|
+
},
|
|
79
78
|
});
|
|
80
79
|
|
|
81
80
|
this.isEnabled = true;
|
|
@@ -167,7 +166,6 @@ class TestomatioPipe {
|
|
|
167
166
|
const runParams = Object.fromEntries(
|
|
168
167
|
Object.entries({
|
|
169
168
|
ci_build_url: buildUrl,
|
|
170
|
-
parallel: this.parallel,
|
|
171
169
|
api_key: this.apiKey.trim(),
|
|
172
170
|
group_title: this.groupTitle,
|
|
173
171
|
access_event: accessEvent,
|
|
@@ -188,7 +186,7 @@ class TestomatioPipe {
|
|
|
188
186
|
method: 'PUT',
|
|
189
187
|
url: `/api/reporter/${this.runId}`,
|
|
190
188
|
data: runParams,
|
|
191
|
-
responseType: 'json'
|
|
189
|
+
responseType: 'json',
|
|
192
190
|
});
|
|
193
191
|
if (resp.data.artifacts) setS3Credentials(resp.data.artifacts);
|
|
194
192
|
return;
|
|
@@ -201,7 +199,7 @@ class TestomatioPipe {
|
|
|
201
199
|
url: '/api/reporter',
|
|
202
200
|
data: runParams,
|
|
203
201
|
maxContentLength: Infinity,
|
|
204
|
-
responseType: 'json'
|
|
202
|
+
responseType: 'json',
|
|
205
203
|
});
|
|
206
204
|
|
|
207
205
|
this.runId = resp.data.uid;
|
|
@@ -265,40 +263,42 @@ class TestomatioPipe {
|
|
|
265
263
|
|
|
266
264
|
debug('Adding test', json);
|
|
267
265
|
|
|
268
|
-
return this.client
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
266
|
+
return this.client
|
|
267
|
+
.request({
|
|
268
|
+
method: 'POST',
|
|
269
|
+
url: `/api/reporter/${this.runId}/testrun`,
|
|
270
|
+
data: json,
|
|
271
|
+
headers: {
|
|
272
|
+
'Content-Type': 'application/json',
|
|
273
|
+
},
|
|
274
|
+
maxContentLength: Infinity,
|
|
275
|
+
})
|
|
276
|
+
.catch(err => {
|
|
277
|
+
this.requestFailures++;
|
|
278
|
+
this.notReportedTestsCount++;
|
|
279
|
+
if (err.response) {
|
|
280
|
+
if (err.response.status >= 400) {
|
|
281
|
+
const responseData = err.response.data || { message: '' };
|
|
282
|
+
console.log(
|
|
283
|
+
APP_PREFIX,
|
|
284
|
+
pc.yellow(`Warning: ${responseData.message} (${err.response.status})`),
|
|
285
|
+
pc.gray(data?.title || ''),
|
|
286
|
+
);
|
|
287
|
+
if (err.response?.data?.message?.includes('could not be matched')) {
|
|
288
|
+
this.hasUnmatchedTests = true;
|
|
289
|
+
}
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
282
292
|
console.log(
|
|
283
293
|
APP_PREFIX,
|
|
284
|
-
pc.yellow(`Warning: ${
|
|
285
|
-
|
|
294
|
+
pc.yellow(`Warning: ${data?.title || ''} (${err.response?.status})`),
|
|
295
|
+
`Report couldn't be processed: ${err?.response?.data?.message}`,
|
|
286
296
|
);
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
return;
|
|
297
|
+
printCreateIssue(err);
|
|
298
|
+
} else {
|
|
299
|
+
console.log(APP_PREFIX, pc.blue(data?.title || ''), "Report couldn't be processed", err);
|
|
291
300
|
}
|
|
292
|
-
|
|
293
|
-
APP_PREFIX,
|
|
294
|
-
pc.yellow(`Warning: ${data?.title || ''} (${err.response?.status})`),
|
|
295
|
-
`Report couldn't be processed: ${err?.response?.data?.message}`,
|
|
296
|
-
);
|
|
297
|
-
printCreateIssue(err);
|
|
298
|
-
} else {
|
|
299
|
-
console.log(APP_PREFIX, pc.blue(data?.title || ''), "Report couldn't be processed", err);
|
|
300
|
-
}
|
|
301
|
-
});
|
|
301
|
+
});
|
|
302
302
|
};
|
|
303
303
|
|
|
304
304
|
/**
|
|
@@ -325,43 +325,42 @@ class TestomatioPipe {
|
|
|
325
325
|
const testsToSend = this.batch.tests.splice(0);
|
|
326
326
|
debug('📨 Batch upload', testsToSend.length, 'tests');
|
|
327
327
|
|
|
328
|
-
return this.client
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
328
|
+
return this.client
|
|
329
|
+
.request({
|
|
330
|
+
method: 'POST',
|
|
331
|
+
url: `/api/reporter/${this.runId}/testrun`,
|
|
332
|
+
data: {
|
|
333
|
+
api_key: this.apiKey,
|
|
334
|
+
tests: testsToSend,
|
|
335
|
+
batch_index: this.batch.batchIndex,
|
|
336
|
+
},
|
|
337
|
+
headers: {
|
|
338
|
+
'Content-Type': 'application/json',
|
|
339
|
+
},
|
|
340
|
+
maxContentLength: Infinity,
|
|
341
|
+
})
|
|
342
|
+
.catch(err => {
|
|
343
|
+
this.requestFailures++;
|
|
344
|
+
this.notReportedTestsCount += testsToSend.length;
|
|
345
|
+
if (err.response) {
|
|
346
|
+
if (err.response.status >= 400) {
|
|
347
|
+
const responseData = err.response.data || { message: '' };
|
|
348
|
+
console.log(APP_PREFIX, pc.yellow(`Warning: ${responseData.message} (${err.response.status})`));
|
|
349
|
+
if (err.response?.data?.message?.includes('could not be matched')) {
|
|
350
|
+
this.hasUnmatchedTests = true;
|
|
351
|
+
}
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
346
354
|
console.log(
|
|
347
355
|
APP_PREFIX,
|
|
348
|
-
pc.yellow(`Warning:
|
|
356
|
+
pc.yellow(`Warning: (${err.response?.status})`),
|
|
357
|
+
`Report couldn't be processed: ${err?.response?.data?.message}`,
|
|
349
358
|
);
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
return;
|
|
359
|
+
printCreateIssue(err);
|
|
360
|
+
} else {
|
|
361
|
+
console.log(APP_PREFIX, "Report couldn't be processed", err);
|
|
354
362
|
}
|
|
355
|
-
|
|
356
|
-
APP_PREFIX,
|
|
357
|
-
pc.yellow(`Warning: (${err.response?.status})`),
|
|
358
|
-
`Report couldn't be processed: ${err?.response?.data?.message}`,
|
|
359
|
-
);
|
|
360
|
-
printCreateIssue(err);
|
|
361
|
-
} else {
|
|
362
|
-
console.log(APP_PREFIX, "Report couldn't be processed", err);
|
|
363
|
-
}
|
|
364
|
-
});
|
|
363
|
+
});
|
|
365
364
|
};
|
|
366
365
|
|
|
367
366
|
/**
|
|
@@ -387,9 +386,9 @@ class TestomatioPipe {
|
|
|
387
386
|
else this.batch.tests.push(data);
|
|
388
387
|
|
|
389
388
|
// if test is added after run which is already finished
|
|
390
|
-
|
|
389
|
+
if (!this.batch.intervalFunction) uploading = this.#batchUpload();
|
|
391
390
|
|
|
392
|
-
|
|
391
|
+
// return promise to be able to wait for it
|
|
393
392
|
return uploading;
|
|
394
393
|
}
|
|
395
394
|
|
|
@@ -419,14 +418,13 @@ class TestomatioPipe {
|
|
|
419
418
|
console.warn(`${APP_PREFIX} ${errorMessage}`);
|
|
420
419
|
}
|
|
421
420
|
|
|
422
|
-
const { status
|
|
421
|
+
const { status } = params;
|
|
423
422
|
|
|
424
423
|
let status_event;
|
|
425
424
|
|
|
426
425
|
if (status === STATUS.FINISHED) status_event = 'finish';
|
|
427
426
|
if (status === STATUS.PASSED) status_event = 'pass';
|
|
428
427
|
if (status === STATUS.FAILED) status_event = 'fail';
|
|
429
|
-
if (parallel) status_event += '_parallel';
|
|
430
428
|
|
|
431
429
|
try {
|
|
432
430
|
if (this.runId && !this.proceed) {
|
|
@@ -439,7 +437,7 @@ class TestomatioPipe {
|
|
|
439
437
|
status_event,
|
|
440
438
|
detach: params.detach,
|
|
441
439
|
tests: params.tests,
|
|
442
|
-
}
|
|
440
|
+
},
|
|
443
441
|
});
|
|
444
442
|
if (this.runUrl) {
|
|
445
443
|
console.log(APP_PREFIX, '📊 Report Saved. Report URL:', pc.magenta(this.runUrl));
|
package/src/replay.js
CHANGED
|
@@ -246,7 +246,7 @@ export class Replay {
|
|
|
246
246
|
}
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
-
await client.updateRunStatus(finishParams.status || STATUS.FINISHED
|
|
249
|
+
await client.updateRunStatus(finishParams.status || STATUS.FINISHED);
|
|
250
250
|
|
|
251
251
|
const result = {
|
|
252
252
|
success: true,
|