@testomatio/reporter 1.4.0-beta-csv-enable → 1.4.0
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 +4 -4
- package/lib/adapter/webdriver.js +45 -0
- package/lib/pipe/testomatio.js +88 -10
- package/lib/xmlReader.js +7 -0
- package/package.json +1 -1
package/lib/adapter/codecept.js
CHANGED
|
@@ -142,7 +142,7 @@ function CodeceptReporter(config) {
|
|
|
142
142
|
if (id && failedTests.includes(id)) {
|
|
143
143
|
failedTests = failedTests.filter(failed => id !== failed);
|
|
144
144
|
}
|
|
145
|
-
const testId = getTestomatIdFromTestTitle(`${title} ${tags
|
|
145
|
+
const testId = getTestomatIdFromTestTitle(`${title} ${tags?.join(' ')}`);
|
|
146
146
|
const testObj = getTestAndMessage(title);
|
|
147
147
|
|
|
148
148
|
const logs = getTestLogs(test);
|
|
@@ -176,7 +176,7 @@ function CodeceptReporter(config) {
|
|
|
176
176
|
for (const test of suite.tests) {
|
|
177
177
|
const { id, tags, title } = test;
|
|
178
178
|
failedTests.push(id || title);
|
|
179
|
-
const testId = getTestomatIdFromTestTitle(`${title} ${tags
|
|
179
|
+
const testId = getTestomatIdFromTestTitle(`${title} ${tags?.join(' ')}`);
|
|
180
180
|
|
|
181
181
|
client.addTestRun(STATUS.FAILED, {
|
|
182
182
|
...stripExampleFromTitle(title),
|
|
@@ -194,7 +194,7 @@ function CodeceptReporter(config) {
|
|
|
194
194
|
if (test.err) error = test.err;
|
|
195
195
|
const { id, tags, title, artifacts } = test;
|
|
196
196
|
failedTests.push(id || title);
|
|
197
|
-
let testId = getTestomatIdFromTestTitle(`${title} ${tags
|
|
197
|
+
let testId = getTestomatIdFromTestTitle(`${title} ${tags?.join(' ')}`);
|
|
198
198
|
const testObj = getTestAndMessage(title);
|
|
199
199
|
|
|
200
200
|
const files = [];
|
|
@@ -240,7 +240,7 @@ function CodeceptReporter(config) {
|
|
|
240
240
|
const { id, tags, title } = test;
|
|
241
241
|
if (failedTests.includes(id || title)) return;
|
|
242
242
|
|
|
243
|
-
const testId = getTestomatIdFromTestTitle(`${title} ${tags
|
|
243
|
+
const testId = getTestomatIdFromTestTitle(`${title} ${tags?.join(' ')}`);
|
|
244
244
|
const testObj = getTestAndMessage(title);
|
|
245
245
|
client.addTestRun(STATUS.SKIPPED, {
|
|
246
246
|
...stripExampleFromTitle(title),
|
package/lib/adapter/webdriver.js
CHANGED
|
@@ -31,6 +31,18 @@ class WebdriverReporter extends WDIOReporter {
|
|
|
31
31
|
this._addTestPromises.push(this.addTest(test));
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
// wdio-cucumber does not trigger onTestEnd hook, thus, using this one
|
|
35
|
+
/**
|
|
36
|
+
*
|
|
37
|
+
* @param {} scerario
|
|
38
|
+
* @returns
|
|
39
|
+
*/
|
|
40
|
+
onSuiteEnd(scerario) {
|
|
41
|
+
if (scerario.type === 'scenario') {
|
|
42
|
+
this._addTestPromises.push(this.addBddScenario(scerario));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
34
46
|
async addTest(test) {
|
|
35
47
|
if (!this.client) return;
|
|
36
48
|
|
|
@@ -51,6 +63,39 @@ class WebdriverReporter extends WDIOReporter {
|
|
|
51
63
|
filesBuffers: screenshotsBuffers,
|
|
52
64
|
});
|
|
53
65
|
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @param {import('../../types').WebdriverIOScenario} scenario
|
|
69
|
+
*/
|
|
70
|
+
addBddScenario(scenario) {
|
|
71
|
+
if (!this.client) return;
|
|
72
|
+
|
|
73
|
+
const { title, _duration: duration } = scenario;
|
|
74
|
+
|
|
75
|
+
const testId = getTestomatIdFromTestTitle(title || scenario.tags.map(tag => tag.name).join(' '));
|
|
76
|
+
|
|
77
|
+
let scenarioState = scenario.tests.every(test => test.state === 'passed') ? 'passed' : 'failed';
|
|
78
|
+
if (scenario.tests.every(test => test.state === 'skipped')) {
|
|
79
|
+
scenarioState = 'skipped';
|
|
80
|
+
}
|
|
81
|
+
const errors = scenario.tests
|
|
82
|
+
.filter(test => test.state === 'failed')
|
|
83
|
+
.map(test => test.error?.stack)
|
|
84
|
+
.filter(Boolean);
|
|
85
|
+
const error = errors.join('\n');
|
|
86
|
+
|
|
87
|
+
const tags = scenario.tags.map(tag => tag.name);
|
|
88
|
+
|
|
89
|
+
return this.client.addTestRun(scenarioState, {
|
|
90
|
+
error: error ? Error(error) : null,
|
|
91
|
+
title,
|
|
92
|
+
test_id: testId,
|
|
93
|
+
time: duration,
|
|
94
|
+
tags,
|
|
95
|
+
file: scenario.file,
|
|
96
|
+
// filesBuffers: screenshotsBuffers,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
54
99
|
}
|
|
55
100
|
|
|
56
101
|
module.exports = WebdriverReporter;
|
package/lib/pipe/testomatio.js
CHANGED
|
@@ -23,6 +23,13 @@ if (process.env.TESTOMATIO_RUN) {
|
|
|
23
23
|
*/
|
|
24
24
|
class TestomatioPipe {
|
|
25
25
|
constructor(params, store) {
|
|
26
|
+
this.batch = {
|
|
27
|
+
isEnabled: params.isBatchEnabled ?? !process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD ?? true,
|
|
28
|
+
intervalFunction: null, // will be created in createRun by setInterval function
|
|
29
|
+
intervalTime: 5000, // how often tests are sent
|
|
30
|
+
tests: [], // array of tests in batch
|
|
31
|
+
batchIndex: 0, // represents the current batch index (starts from 1 and increments by 1 for each batch)
|
|
32
|
+
};
|
|
26
33
|
this.retriesTimestamps = [];
|
|
27
34
|
this.reportingCanceledDueToReqFailures = false;
|
|
28
35
|
this.notReportedTestsCount = 0;
|
|
@@ -126,11 +133,15 @@ class TestomatioPipe {
|
|
|
126
133
|
}
|
|
127
134
|
|
|
128
135
|
/**
|
|
136
|
+
* Creates a new run on Testomat.io
|
|
137
|
+
* @param {{isBatchEnabled?: boolean}} params
|
|
129
138
|
* @returns Promise<void>
|
|
130
139
|
*/
|
|
131
|
-
async createRun() {
|
|
140
|
+
async createRun(params = {}) {
|
|
141
|
+
this.batch.isEnabled = params.isBatchEnabled ?? this.batch.isEnabled;
|
|
132
142
|
debug('Creating run...');
|
|
133
143
|
if (!this.isEnabled) return;
|
|
144
|
+
if (this.batch.isEnabled) this.batch.intervalFunction = setInterval(this.#batchUpload, this.batch.intervalTime);
|
|
134
145
|
|
|
135
146
|
let buildUrl = process.env.BUILD_URL || process.env.CI_JOB_URL || process.env.CIRCLE_BUILD_URL;
|
|
136
147
|
|
|
@@ -233,7 +244,7 @@ class TestomatioPipe {
|
|
|
233
244
|
return false;
|
|
234
245
|
}
|
|
235
246
|
|
|
236
|
-
|
|
247
|
+
#uploadSingleTest = async data => {
|
|
237
248
|
if (!this.isEnabled) return;
|
|
238
249
|
if (!this.runId) return;
|
|
239
250
|
if (this.#cancelTestReportingInCaseOfTooManyReqFailures(data)) return;
|
|
@@ -250,14 +261,7 @@ class TestomatioPipe {
|
|
|
250
261
|
debug('Adding test', json);
|
|
251
262
|
|
|
252
263
|
return this.axios
|
|
253
|
-
.post(`/api/reporter/${this.runId}/testrun`, json,
|
|
254
|
-
maxContentLength: Infinity,
|
|
255
|
-
maxBodyLength: Infinity,
|
|
256
|
-
headers: {
|
|
257
|
-
// Overwrite Axios's automatically set Content-Type
|
|
258
|
-
'Content-Type': 'application/json',
|
|
259
|
-
},
|
|
260
|
-
})
|
|
264
|
+
.post(`/api/reporter/${this.runId}/testrun`, json, axiosAddTestrunRequestConfig)
|
|
261
265
|
.catch(err => {
|
|
262
266
|
if (err.response) {
|
|
263
267
|
if (err.response.status >= 400) {
|
|
@@ -282,6 +286,67 @@ class TestomatioPipe {
|
|
|
282
286
|
console.log(APP_PREFIX, chalk.blue(data?.title || ''), "Report couldn't be processed", err);
|
|
283
287
|
}
|
|
284
288
|
});
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Uploads tests as a batch (multiple tests at once). Intended to be used with a setInterval
|
|
294
|
+
*/
|
|
295
|
+
#batchUpload = async () => {
|
|
296
|
+
this.batch.batchIndex++;
|
|
297
|
+
if (!this.batch.isEnabled) return;
|
|
298
|
+
if (!this.batch.tests.length) return;
|
|
299
|
+
|
|
300
|
+
// get tests from batch and clear batch
|
|
301
|
+
const testsToSend = this.batch.tests.splice(0);
|
|
302
|
+
debug('📨 Batch upload', testsToSend.length, 'tests');
|
|
303
|
+
|
|
304
|
+
return this.axios
|
|
305
|
+
.post(
|
|
306
|
+
`/api/reporter/${this.runId}/testrun`,
|
|
307
|
+
{ api_key: this.apiKey, tests: testsToSend, batch_index: this.batch.batchIndex },
|
|
308
|
+
axiosAddTestrunRequestConfig,
|
|
309
|
+
)
|
|
310
|
+
.catch(err => {
|
|
311
|
+
if (err.response) {
|
|
312
|
+
if (err.response.status >= 400) {
|
|
313
|
+
const responseData = err.response.data || { message: '' };
|
|
314
|
+
console.log(
|
|
315
|
+
APP_PREFIX,
|
|
316
|
+
chalk.yellow(`Warning: ${responseData.message} (${err.response.status})`),
|
|
317
|
+
// chalk.grey(data?.title || ''),
|
|
318
|
+
);
|
|
319
|
+
if (err.response?.data?.message?.includes('could not be matched')) {
|
|
320
|
+
this.hasUnmatchedTests = true;
|
|
321
|
+
}
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
console.log(
|
|
325
|
+
APP_PREFIX,
|
|
326
|
+
chalk.yellow(`Warning: (${err.response?.status})`),
|
|
327
|
+
`Report couldn't be processed: ${err?.response?.data?.message}`,
|
|
328
|
+
);
|
|
329
|
+
printCreateIssue(err);
|
|
330
|
+
} else {
|
|
331
|
+
console.log(APP_PREFIX, "Report couldn't be processed", err);
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Adds a test to the batch uploader (or reports a single test if batch uploading is disabled)
|
|
338
|
+
*/
|
|
339
|
+
addTest(data) {
|
|
340
|
+
if (!this.isEnabled) return;
|
|
341
|
+
if (!this.runId) return;
|
|
342
|
+
data.api_key = this.apiKey;
|
|
343
|
+
data.create = this.createNewTests;
|
|
344
|
+
|
|
345
|
+
if (!this.batch.isEnabled) this.#uploadSingleTest(data);
|
|
346
|
+
else this.batch.tests.push(data);
|
|
347
|
+
|
|
348
|
+
// if test is added after run already finished
|
|
349
|
+
if (!this.batch.intervalFunction) this.#batchUpload();
|
|
285
350
|
}
|
|
286
351
|
|
|
287
352
|
/**
|
|
@@ -289,6 +354,10 @@ class TestomatioPipe {
|
|
|
289
354
|
* @returns
|
|
290
355
|
*/
|
|
291
356
|
async finishRun(params) {
|
|
357
|
+
this.#batchUpload();
|
|
358
|
+
if (this.batch.intervalFunction) clearInterval(this.batch.intervalFunction);
|
|
359
|
+
|
|
360
|
+
debug('Finishing run...');
|
|
292
361
|
if (!this.isEnabled) return;
|
|
293
362
|
debug('Finishing run...');
|
|
294
363
|
|
|
@@ -387,4 +456,13 @@ function printCreateIssue(err) {
|
|
|
387
456
|
});
|
|
388
457
|
}
|
|
389
458
|
|
|
459
|
+
const axiosAddTestrunRequestConfig = {
|
|
460
|
+
maxContentLength: Infinity,
|
|
461
|
+
maxBodyLength: Infinity,
|
|
462
|
+
headers: {
|
|
463
|
+
// Overwrite Axios's automatically set Content-Type
|
|
464
|
+
'Content-Type': 'application/json',
|
|
465
|
+
},
|
|
466
|
+
};
|
|
467
|
+
|
|
390
468
|
module.exports = TestomatioPipe;
|
package/lib/xmlReader.js
CHANGED
|
@@ -38,6 +38,8 @@ class XmlReader {
|
|
|
38
38
|
title: TESTOMATIO_TITLE,
|
|
39
39
|
env: TESTOMATIO_ENV,
|
|
40
40
|
group_title: TESTOMATIO_RUNGROUP_TITLE,
|
|
41
|
+
// batch uploading is implemented for xml already
|
|
42
|
+
isBatchEnabled: false,
|
|
41
43
|
};
|
|
42
44
|
this.runId = opts.runId || TESTOMATIO_RUN;
|
|
43
45
|
this.adapter = adapterFactory(opts.lang?.toLowerCase(), opts);
|
|
@@ -376,6 +378,7 @@ class XmlReader {
|
|
|
376
378
|
title: this.requestParams.title,
|
|
377
379
|
env: this.requestParams.env,
|
|
378
380
|
group_title: this.requestParams.group_title,
|
|
381
|
+
isBatchEnabled: this.requestParams.isBatchEnabled,
|
|
379
382
|
};
|
|
380
383
|
|
|
381
384
|
debug('Run', runParams);
|
|
@@ -405,6 +408,10 @@ class XmlReader {
|
|
|
405
408
|
|
|
406
409
|
return Promise.all(this.pipes.map(p => p.finishRun(dataString)));
|
|
407
410
|
}
|
|
411
|
+
|
|
412
|
+
async _finishRun() {
|
|
413
|
+
return Promise.all(this.pipes.map(p => p.finishRun({ status: 'finished' })));
|
|
414
|
+
}
|
|
408
415
|
}
|
|
409
416
|
|
|
410
417
|
module.exports = XmlReader;
|