@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.
@@ -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.join(' ')}`);
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.join(' ')}`);
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.join(' ')}`);
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.join(' ')}`);
243
+ const testId = getTestomatIdFromTestTitle(`${title} ${tags?.join(' ')}`);
244
244
  const testObj = getTestAndMessage(title);
245
245
  client.addTestRun(STATUS.SKIPPED, {
246
246
  ...stripExampleFromTitle(title),
@@ -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;
@@ -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
- addTest(data) {
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/reporter",
3
- "version": "1.4.0-beta-csv-enable",
3
+ "version": "1.4.0",
4
4
  "description": "Testomatio Reporter Client",
5
5
  "main": "./lib/reporter.js",
6
6
  "typings": "typings/index.d.ts",