@testomatio/reporter 2.5.1 → 2.6.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.
@@ -3,11 +3,12 @@ import pc from 'picocolors';
3
3
  import { Gaxios } from 'gaxios';
4
4
  import JsonCycle from 'json-cycle';
5
5
  import { APP_PREFIX, STATUS, AXIOS_TIMEOUT, REPORTER_REQUEST_RETRIES } from '../constants.js';
6
- import { isValidUrl,
7
- foundedTestLog,
8
- readLatestRunId,
9
- transformEnvVarToBoolean,
10
- getGitCommitSha
6
+ import {
7
+ isValidUrl,
8
+ foundedTestLog,
9
+ readLatestRunId,
10
+ transformEnvVarToBoolean,
11
+ getGitCommitSha,
11
12
  } from '../utils/utils.js';
12
13
  import { parseFilterParams, generateFilterRequestParams, setS3Credentials } from '../utils/pipe_utils.js';
13
14
  import { config } from '../config.js';
@@ -198,6 +199,9 @@ class TestomatioPipe {
198
199
  if (!this.isEnabled) return;
199
200
  if (this.batch.isEnabled && this.isEnabled)
200
201
  this.batch.intervalFunction = setInterval(this.#batchUpload, this.batch.intervalTime);
202
+ if (this.store) {
203
+ this.store.runKind = params.kind;
204
+ }
201
205
 
202
206
  let buildUrl = process.env.BUILD_URL || process.env.CI_JOB_URL || process.env.CIRCLE_BUILD_URL;
203
207
 
@@ -219,6 +223,16 @@ class TestomatioPipe {
219
223
 
220
224
  const accessEvent = process.env.TESTOMATIO_PUBLISH ? 'publish' : null;
221
225
 
226
+ const coverageConfiguration = this.store?.coverageConfiguration;
227
+ let description = null;
228
+ let configuration = null;
229
+ if (coverageConfiguration && (coverageConfiguration.tests?.length || coverageConfiguration.suites?.length)) {
230
+ description = this.store?.coverageDescription || null;
231
+ configuration = {
232
+ tests: coverageConfiguration.tests?.map(id => id.replace(/^T/, '')) || [],
233
+ suites: coverageConfiguration.suites?.map(id => id.replace(/^S/, '')) || [],
234
+ };
235
+ }
222
236
  const runParams = Object.fromEntries(
223
237
  Object.entries({
224
238
  ci_build_url: buildUrl,
@@ -232,6 +246,8 @@ class TestomatioPipe {
232
246
  shared_run: this.sharedRun,
233
247
  shared_run_timeout: this.sharedRunTimeout,
234
248
  kind: params.kind,
249
+ configuration,
250
+ description,
235
251
  }).filter(([, value]) => !!value),
236
252
  );
237
253
  debug(' >>>>>> Run params', JSON.stringify(runParams, null, 2));
@@ -278,11 +294,13 @@ class TestomatioPipe {
278
294
  if (!this.apiKey) console.error('Testomat.io API key is not set');
279
295
  if (!this.apiKey?.startsWith('tstmt')) console.error('Testomat.io API key is invalid');
280
296
 
297
+ if (process.env.DEBUG || process.env.TESTOMATIO_DEBUG) this.#logFailedResponse(err);
298
+
281
299
  console.error(
282
300
  APP_PREFIX,
283
301
  'Error creating Testomat.io report (see details above), please check if your API key is valid. Skipping report',
284
302
  );
285
- printCreateIssue(err);
303
+ printCreateIssue();
286
304
  }
287
305
  debug('"createRun" function finished');
288
306
  }
@@ -329,24 +347,8 @@ class TestomatioPipe {
329
347
  this.requestFailures++;
330
348
  this.notReportedTestsCount++;
331
349
  if (err.response) {
332
- if (err.response.status >= 400) {
333
- const responseData = err.response.data || { message: '' };
334
- console.log(
335
- APP_PREFIX,
336
- pc.yellow(`Warning: ${responseData.message} (${err.response.status})`),
337
- pc.gray(data?.title || ''),
338
- );
339
- if (err.response?.data?.message?.includes('could not be matched')) {
340
- this.hasUnmatchedTests = true;
341
- }
342
- return;
343
- }
344
- console.log(
345
- APP_PREFIX,
346
- pc.yellow(`Warning: ${data?.title || ''} (${err.response?.status})`),
347
- `Report couldn't be processed: ${err?.response?.data?.message}`,
348
- );
349
- printCreateIssue(err);
350
+ this.#logFailedResponse(err);
351
+ printCreateIssue();
350
352
  } else {
351
353
  console.log(APP_PREFIX, pc.blue(data?.title || ''), "Report couldn't be processed", err);
352
354
  }
@@ -395,20 +397,8 @@ class TestomatioPipe {
395
397
  this.requestFailures++;
396
398
  this.notReportedTestsCount += testsToSend.length;
397
399
  if (err.response) {
398
- if (err.response.status >= 400) {
399
- const responseData = err.response.data || { message: '' };
400
- console.log(APP_PREFIX, pc.yellow(`Warning: ${responseData.message} (${err.response.status})`));
401
- if (err.response?.data?.message?.includes('could not be matched')) {
402
- this.hasUnmatchedTests = true;
403
- }
404
- return;
405
- }
406
- console.log(
407
- APP_PREFIX,
408
- pc.yellow(`Warning: (${err.response?.status})`),
409
- `Report couldn't be processed: ${err?.response?.data?.message}`,
410
- );
411
- printCreateIssue(err);
400
+ this.#logFailedResponse(err);
401
+ printCreateIssue();
412
402
  } else {
413
403
  console.log(APP_PREFIX, "Report couldn't be processed", err);
414
404
  }
@@ -521,37 +511,81 @@ class TestomatioPipe {
521
511
  }
522
512
  } catch (err) {
523
513
  console.log(APP_PREFIX, 'Error updating status, skipping...', err);
524
- printCreateIssue(err);
514
+ if (process.env.DEBUG || process.env.TESTOMATIO_DEBUG) this.#logFailedResponse(err);
515
+ printCreateIssue();
525
516
  }
526
517
  debug('Run finished');
527
518
  }
528
519
 
520
+ #logFailedResponse(error) {
521
+ let responseBody = stringify(error.response?.data ?? error.response ?? error, { pretty: true });
522
+ if (!responseBody) responseBody = '<empty>';
523
+ responseBody = hideTestomatioToken(responseBody);
524
+
525
+ const statusCode = error.status || error.code || error.response?.status || '<unknown status code>';
526
+ const method = error.response?.config.method || '<unknown method>';
527
+ const url = error.response?.config.url || '<unknown url>';
528
+
529
+ let message = pc.yellow('\n⚠️ Request to Testomat.io failed:\n');
530
+ message += pc.bold(`${pc.red(statusCode)} ${method} ${url}\n`);
531
+ message += `\t${pc.bold('response: ')}${pc.gray(responseBody)}\n`;
532
+
533
+ const requestBody = hideTestomatioToken(stringify(error.response?.config?.data));
534
+ if (process.env.DEBUG || process.env.TESTOMATIO_DEBUG) {
535
+ message += `\t${pc.bold('request: ')}${pc.gray(requestBody)}\n`;
536
+ } else {
537
+ const requestBodyCut = requestBody.slice(0, 1000);
538
+ message += `\t${pc.bold('request: ')}${pc.gray(`${requestBodyCut}.....`)}\n`;
539
+ message += '\trequest body is cut, run with TESTOMATIO_DEBUG=1 to see full body\n';
540
+ }
541
+
542
+ console.log(message);
543
+
544
+ if (error.response?.data?.message?.includes('could not be matched')) {
545
+ this.hasUnmatchedTests = true;
546
+ }
547
+ }
548
+
529
549
  toString() {
530
550
  return 'Testomatio Reporter';
531
551
  }
532
552
  }
533
553
 
534
554
  let registeredErrorHints = false;
535
- function printCreateIssue(err) {
555
+ function printCreateIssue() {
536
556
  if (registeredErrorHints) return;
537
557
  registeredErrorHints = true;
538
558
  process.on('exit', () => {
539
- console.log();
540
- console.log(APP_PREFIX, 'There was an error reporting to Testomat.io:');
541
559
  console.log(
542
560
  APP_PREFIX,
543
- 'If you think this is a bug please create an issue: https://github.com/testomatio/reporter/issues/new',
561
+ 'There was an error reporting to Testomat.io.\n',
562
+ pc.yellow(
563
+ 'If you think this is a bug please create an issue: https://github.com/testomatio/reporter/issues/new.',
564
+ ),
565
+ pc.yellow('Provide the logs from above'),
544
566
  );
545
- console.log(APP_PREFIX, 'Provide this information:');
546
- console.log('Error:', err.message || err.code);
547
- if (!err.config) return;
548
-
549
- const time = new Date().toUTCString();
550
- const { body, url, baseURL, method } = err?.config || {};
551
- console.log('```js');
552
- console.log({ body: body?.replace(/"(tstmt_[^"]+)"/g, 'tstmt_*'), url, baseURL, method, time });
553
- console.log('```');
554
567
  });
555
568
  }
556
569
 
570
+ /**
571
+ * Removes Testomatio token from string data
572
+ *
573
+ * @param {string} data
574
+ * @returns {string}
575
+ */
576
+ function hideTestomatioToken(data) {
577
+ return data.replace(/"api_key": "[^"]+"/g, '"api_key": "<hidden>"').replace(/"(tstmt_[^"]+)"/g, 'tstmt_***');
578
+ }
579
+
580
+ /**
581
+ * Stringifies provided data
582
+ *
583
+ * @param {any} anything
584
+ * @param {{ pretty: boolean }} opts
585
+ * @returns {string}
586
+ */
587
+ function stringify(anything, opts = { pretty: false }) {
588
+ return typeof anything === 'string' ? anything : JSON.stringify(anything, null, opts.pretty ? 2 : undefined);
589
+ }
590
+
557
591
  export default TestomatioPipe;
@@ -76,6 +76,10 @@ function setLabel(key, value = null) {
76
76
  * @returns {void}
77
77
  */
78
78
  function linkTest(...testIds) {
79
+ if (isPlaywright) {
80
+ console.log(`[TESTOMATIO-LINK-TESTS] ${JSON.stringify(testIds)}`);
81
+ return;
82
+ }
79
83
  const links = testIds.map(testId => ({ test: testId }));
80
84
  services.links.put(links);
81
85
  }
@@ -86,6 +90,10 @@ function linkTest(...testIds) {
86
90
  * @returns {void}
87
91
  */
88
92
  function linkJira(...jiraIds) {
93
+ if (isPlaywright) {
94
+ console.log(`[TESTOMATIO-LINK-JIRA] ${JSON.stringify(jiraIds)}`);
95
+ return;
96
+ }
89
97
  const links = jiraIds.map(jiraId => ({ jira: jiraId }));
90
98
  services.links.put(links);
91
99
  }
@@ -30,7 +30,7 @@ class LinkStorage {
30
30
  /**
31
31
  * Returns links array for the test
32
32
  * @param {*} context testId or test context from test runner
33
- * @returns {object[]} links array, e.g. [{test: 'TEST-123'}, {jira: 'JIRA-456'}]
33
+ * @returns {{[key: 'test' | 'jira']: string}[]} links array, e.g. [{test: 'TEST-123'}, {jira: 'JIRA-456'}]
34
34
  */
35
35
  get(context = null) {
36
36
  const linksList = dataStorage.getData('links', context);
package/types/types.d.ts CHANGED
@@ -17,7 +17,7 @@ declare module '@testomatio/reporter' {
17
17
  * @param message - step message
18
18
  * @param logs - optional key-value object with additional info (e.g. logs)
19
19
  */
20
- export function step(message: string, logs?: {[key: string]: any}): void;
20
+ export function step(message: string, logs?: { [key: string]: any }): void;
21
21
 
22
22
  /**
23
23
  * Add key-value pair(s) to the test report