@testomatio/reporter 2.0.0-beta-esm → 2.0.0-beta.1-xml

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.
Files changed (123) hide show
  1. package/lib/adapter/codecept.d.ts +2 -0
  2. package/lib/adapter/codecept.js +31 -26
  3. package/lib/adapter/cucumber/current.d.ts +14 -0
  4. package/lib/adapter/cucumber/legacy.d.ts +0 -0
  5. package/lib/adapter/cucumber.d.ts +2 -0
  6. package/lib/adapter/cypress-plugin/index.d.ts +2 -0
  7. package/lib/adapter/cypress-plugin/index.js +10 -10
  8. package/lib/adapter/jasmine.d.ts +11 -0
  9. package/lib/adapter/jest.d.ts +13 -0
  10. package/lib/adapter/mocha.d.ts +2 -0
  11. package/lib/adapter/mocha.js +4 -4
  12. package/lib/adapter/nightwatch.d.ts +4 -0
  13. package/lib/adapter/nightwatch.js +80 -0
  14. package/lib/adapter/playwright.d.ts +14 -0
  15. package/lib/adapter/playwright.js +58 -33
  16. package/lib/adapter/vitest.d.ts +35 -0
  17. package/lib/adapter/vitest.js +6 -6
  18. package/lib/adapter/webdriver.d.ts +24 -0
  19. package/lib/adapter/webdriver.js +51 -14
  20. package/lib/bin/cli.d.ts +2 -0
  21. package/lib/bin/cli.js +250 -0
  22. package/lib/bin/reportXml.d.ts +2 -0
  23. package/lib/bin/reportXml.js +15 -11
  24. package/lib/bin/startTest.d.ts +2 -0
  25. package/lib/bin/startTest.js +12 -7
  26. package/lib/bin/uploadArtifacts.d.ts +2 -0
  27. package/lib/bin/uploadArtifacts.js +82 -0
  28. package/lib/client.d.ts +76 -0
  29. package/lib/client.js +128 -53
  30. package/lib/config.d.ts +1 -0
  31. package/lib/config.js +2 -2
  32. package/lib/constants.d.ts +25 -0
  33. package/lib/constants.js +5 -1
  34. package/lib/data-storage.d.ts +34 -0
  35. package/lib/data-storage.js +19 -9
  36. package/lib/junit-adapter/adapter.d.ts +9 -0
  37. package/lib/junit-adapter/csharp.d.ts +5 -0
  38. package/lib/junit-adapter/csharp.js +11 -1
  39. package/lib/junit-adapter/index.d.ts +3 -0
  40. package/lib/junit-adapter/java.d.ts +5 -0
  41. package/lib/junit-adapter/javascript.d.ts +4 -0
  42. package/lib/junit-adapter/python.d.ts +5 -0
  43. package/lib/junit-adapter/ruby.d.ts +4 -0
  44. package/lib/output.d.ts +11 -0
  45. package/lib/package.json +3 -1
  46. package/lib/pipe/bitbucket.d.ts +23 -0
  47. package/lib/pipe/bitbucket.js +19 -9
  48. package/lib/pipe/csv.d.ts +47 -0
  49. package/lib/pipe/csv.js +2 -2
  50. package/lib/pipe/debug.d.ts +29 -0
  51. package/lib/pipe/debug.js +108 -0
  52. package/lib/pipe/github.d.ts +30 -0
  53. package/lib/pipe/github.js +37 -5
  54. package/lib/pipe/gitlab.d.ts +23 -0
  55. package/lib/pipe/gitlab.js +2 -3
  56. package/lib/pipe/html.d.ts +35 -0
  57. package/lib/pipe/html.js +9 -4
  58. package/lib/pipe/index.d.ts +1 -0
  59. package/lib/pipe/index.js +20 -10
  60. package/lib/pipe/testomatio.d.ts +70 -0
  61. package/lib/pipe/testomatio.js +54 -39
  62. package/lib/reporter-functions.d.ts +34 -0
  63. package/lib/reporter-functions.js +17 -7
  64. package/lib/reporter.d.ts +232 -0
  65. package/lib/reporter.js +19 -33
  66. package/lib/services/artifacts.d.ts +33 -0
  67. package/lib/services/index.d.ts +9 -0
  68. package/lib/services/key-values.d.ts +27 -0
  69. package/lib/services/key-values.js +1 -1
  70. package/lib/services/logger.d.ts +64 -0
  71. package/lib/services/logger.js +1 -2
  72. package/lib/template/testomatio.hbs +651 -1366
  73. package/lib/uploader.d.ts +60 -0
  74. package/lib/uploader.js +312 -0
  75. package/lib/utils/pipe_utils.d.ts +41 -0
  76. package/lib/utils/pipe_utils.js +3 -5
  77. package/lib/utils/utils.d.ts +47 -0
  78. package/lib/utils/utils.js +99 -12
  79. package/lib/xmlReader.d.ts +92 -0
  80. package/lib/xmlReader.js +64 -25
  81. package/package.json +19 -13
  82. package/src/adapter/codecept.js +30 -26
  83. package/src/adapter/cypress-plugin/index.js +5 -5
  84. package/src/adapter/mocha.cjs +1 -1
  85. package/src/adapter/mocha.js +4 -4
  86. package/src/adapter/nightwatch.js +88 -0
  87. package/src/adapter/playwright.js +59 -31
  88. package/src/adapter/vitest.js +6 -6
  89. package/src/adapter/webdriver.js +42 -12
  90. package/src/bin/cli.js +303 -0
  91. package/src/bin/reportXml.js +19 -9
  92. package/src/bin/startTest.js +9 -4
  93. package/src/bin/uploadArtifacts.js +91 -0
  94. package/src/client.js +137 -57
  95. package/src/config.js +2 -2
  96. package/src/constants.js +5 -1
  97. package/src/data-storage.js +2 -2
  98. package/src/junit-adapter/csharp.js +13 -1
  99. package/src/pipe/bitbucket.js +2 -2
  100. package/src/pipe/csv.js +3 -3
  101. package/src/pipe/debug.js +104 -0
  102. package/src/pipe/github.js +3 -5
  103. package/src/pipe/gitlab.js +6 -7
  104. package/src/pipe/html.js +14 -7
  105. package/src/pipe/index.js +5 -7
  106. package/src/pipe/testomatio.js +75 -76
  107. package/src/reporter-functions.js +18 -7
  108. package/src/reporter.cjs_decprecated +21 -0
  109. package/src/reporter.js +20 -11
  110. package/src/services/key-values.js +1 -1
  111. package/src/services/logger.js +5 -4
  112. package/src/template/testomatio.hbs +651 -1366
  113. package/src/uploader.js +371 -0
  114. package/src/utils/pipe_utils.js +4 -12
  115. package/src/utils/utils.js +64 -15
  116. package/src/xmlReader.js +76 -26
  117. package/lib/adapter/jasmine/jasmine.js +0 -63
  118. package/lib/adapter/mocha/mocha.js +0 -125
  119. package/lib/fileUploader.js +0 -245
  120. package/lib/utils/chalk.js +0 -10
  121. package/src/fileUploader.js +0 -307
  122. package/src/reporter.cjs +0 -22
  123. package/src/utils/chalk.js +0 -13
@@ -1,14 +1,19 @@
1
1
  #!/usr/bin/env node
2
- import program from 'commander';
2
+ import { Command } from 'commander';
3
3
  import pc from 'picocolors';
4
- import glob from 'glob';
4
+ import { glob } from 'glob';
5
5
  import createDebugMessages from 'debug';
6
6
  import { APP_PREFIX } from '../constants.js';
7
7
  import XmlReader from '../xmlReader.js';
8
- import { version } from '../../package.json';
8
+ import { getPackageVersion } from '../utils/utils.js';
9
+ import dotenv from 'dotenv';
10
+ import path from 'path';
11
+
12
+ const version = getPackageVersion();
9
13
 
10
14
  const debug = createDebugMessages('@testomatio/reporter:xml-cli');
11
15
  console.log(pc.cyan(pc.bold(` 🤩 Testomat.io XML Reporter v${version}`)));
16
+ const program = new Command();
12
17
 
13
18
  program
14
19
  .arguments('<pattern>')
@@ -25,10 +30,10 @@ program
25
30
  if (opts.envFile) {
26
31
  console.log(APP_PREFIX, 'Loading env file:', opts.envFile);
27
32
  debug('Loading env file: %s', opts.envFile);
28
- require('dotenv').config({ path: opts.envFile }); // eslint-disable-line
33
+ dotenv.config({ path: opts.envFile });
29
34
  }
30
- if (javaTests === true) javaTests = 'src/test/java';
31
35
  lang = lang?.toLowerCase();
36
+ if (javaTests === true || (lang === 'java' && !javaTests)) javaTests = 'src/test/java';
32
37
  const runReader = new XmlReader({ javaTests, lang });
33
38
  const files = glob.sync(pattern, { cwd: opts.dir || process.cwd() });
34
39
  if (!files.length) {
@@ -44,10 +49,15 @@ program
44
49
 
45
50
  let timeoutTimer;
46
51
  if (opts.timelimit) {
47
- timeoutTimer = setTimeout(() => {
48
- console.log(`⚠️ Reached timeout of ${opts.timelimit}s. Exiting... (Exit code is 0 to not fail the pipeline)`);
49
- process.exit(0);
50
- }, parseInt(opts.timelimit, 10) * 1000);
52
+ timeoutTimer = setTimeout(
53
+ () => {
54
+ console.log(
55
+ `⚠️ Reached timeout of ${opts.timelimit}s. Exiting... (Exit code is 0 to not fail the pipeline)`,
56
+ );
57
+ process.exit(0);
58
+ },
59
+ parseInt(opts.timelimit, 10) * 1000,
60
+ );
51
61
  }
52
62
 
53
63
  try {
@@ -1,13 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawn } from 'cross-spawn';
3
- import program from 'commander';
3
+ import { Command } from 'commander';
4
4
  import pc from 'picocolors';
5
5
  import TestomatClient from '../client.js';
6
6
  import { APP_PREFIX, STATUS } from '../constants.js';
7
- import { version } from '../../package.json';
8
- import {config} from '../config.js';
7
+ import { getPackageVersion } from '../utils/utils.js';
8
+ import { config } from '../config.js';
9
+ import dotenv from 'dotenv';
9
10
 
11
+ const version = getPackageVersion();
10
12
  console.log(pc.cyan(pc.bold(` 🤩 Testomat.io Reporter v${version}`)));
13
+ const program = new Command();
11
14
 
12
15
  program
13
16
  .option('-c, --command <cmd>', 'Test runner command')
@@ -19,7 +22,7 @@ program
19
22
  const { launch, finish, filter } = opts;
20
23
  let { command } = opts;
21
24
 
22
- if (opts.envFile) require('dotenv').config(opts.envFile); // eslint-disable-line
25
+ if (opts.envFile) dotenv.config({ path: opts.envFile });
23
26
 
24
27
  const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || config.TESTOMATIO;
25
28
  const title = process.env.TESTOMATIO_TITLE;
@@ -36,6 +39,8 @@ program
36
39
  }
37
40
 
38
41
  if (finish) {
42
+ // TODO: add error in case of TESTOMATIO environment variable is not set
43
+ // because command is fine in console, but actually (on testomat.io) run is not finished
39
44
  if (!process.env.TESTOMATIO_RUN) {
40
45
  console.log('TESTOMATIO_RUN environment variable must be set.');
41
46
  return process.exit(1);
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import pc from 'picocolors';
5
+ import createDebugMessages from 'debug';
6
+ import TestomatClient from '../client.js';
7
+ import { APP_PREFIX } from '../constants.js';
8
+ import { getPackageVersion } from '../utils/utils.js';
9
+ import { config } from '../config.js';
10
+ import { readLatestRunId } from '../utils/utils.js';
11
+ import dotenv from 'dotenv';
12
+
13
+ const debug = createDebugMessages('@testomatio/reporter:upload-cli');
14
+ const version = getPackageVersion();
15
+ console.log(pc.cyan(pc.bold(` 🤩 Testomat.io Reporter v${version}`)));
16
+ const program = new Command();
17
+
18
+ program
19
+ .option('--env-file <envfile>', 'Load environment variables from env file')
20
+ .option('--force', 'Re-upload artifacts even if they were uploaded before')
21
+ .action(async opts => {
22
+ if (opts.envFile) {
23
+ dotenv.config({ path: opts.envFile });
24
+ } else {
25
+ dotenv.config();
26
+ }
27
+
28
+ const apiKey = config.TESTOMATIO;
29
+ process.env.TESTOMATIO_DISABLE_ARTIFACTS = '';
30
+ const runId = process.env.TESTOMATIO_RUN || process.env.runId || readLatestRunId();
31
+
32
+ if (!runId) {
33
+ console.log('TESTOMATIO_RUN environment variable must be set or restored from a previous run.');
34
+ return process.exit(1);
35
+ }
36
+
37
+ const client = new TestomatClient({
38
+ apiKey,
39
+ runId,
40
+ isBatchEnabled: false,
41
+ });
42
+ let testruns = client.uploader.readUploadedFiles(process.env.TESTOMATIO_RUN);
43
+
44
+ const numTotalArtifacts = testruns.length;
45
+
46
+ debug('Found testruns:', testruns);
47
+
48
+ if (!opts.force) testruns = testruns.filter(tr => !tr.uploaded);
49
+
50
+ if (!testruns.length) {
51
+ console.log(APP_PREFIX, 'Total artifacts:', numTotalArtifacts);
52
+ if (numTotalArtifacts) {
53
+ console.log(APP_PREFIX, 'No new artifacts to upload');
54
+ console.log(APP_PREFIX, 'To re-upload artifacts run this command with --force flag');
55
+ }
56
+ process.exit(0);
57
+ }
58
+
59
+ const testrunsByRid = testruns.reduce((acc, { rid, file }) => {
60
+ if (!acc[rid]) {
61
+ acc[rid] = [];
62
+ }
63
+ if (!acc[rid].includes(file)) acc[rid].push(file);
64
+ return acc;
65
+ }, {});
66
+
67
+ // we need to obtain S3 credentials
68
+ await client.createRun();
69
+
70
+ client.uploader.checkEnabled();
71
+ client.uploader.disableLogStorage();
72
+
73
+ for (const rid in testrunsByRid) {
74
+ const files = testrunsByRid[rid];
75
+ await client.addTestRun(undefined, {
76
+ rid,
77
+ files,
78
+ });
79
+ }
80
+
81
+ console.log(APP_PREFIX, client.uploader.successfulUploads.length, 'artifacts uploaded');
82
+ if (client.uploader.failedUploads.length) {
83
+ console.log(APP_PREFIX, client.uploader.failedUploads.length, 'artifacts failed to upload');
84
+ }
85
+ });
86
+
87
+ if (process.argv.length <= 1) {
88
+ program.outputHelp();
89
+ }
90
+
91
+ program.parse(process.argv);
package/src/client.js CHANGED
@@ -4,24 +4,26 @@ import { minimatch } from 'minimatch';
4
4
  import fs from 'fs';
5
5
  import pc from 'picocolors';
6
6
  import { randomUUID } from 'crypto';
7
- import {upload} from './fileUploader.js';
8
7
  import { APP_PREFIX, STATUS } from './constants.js';
9
8
  import { pipesFactory } from './pipe/index.js';
10
9
  import { glob } from 'glob';
11
- import path, { sep} from 'path';
10
+ import path, { sep } from 'path';
12
11
  import { fileURLToPath } from 'node:url';
12
+ import { S3Uploader } from './uploader.js';
13
+ import { formatStep, storeRunId } from './utils/utils.js';
14
+ import { filesize as prettyBytes } from 'filesize';
13
15
 
14
16
  const debug = createDebugMessages('@testomatio/reporter:client');
15
17
 
16
18
  // removed __dirname usage, because:
17
19
  // 1. replaced with ESM syntax (import.meta.url), but it throws an error on tsc compilation;
18
- // 2. got error "__dirname already defined" in compiles js code (cjs dir)
20
+ // 2. got error "__dirname already defined" in compiles js code (cjs dir)
19
21
 
20
22
  let listOfTestFilesToExcludeFromReport = null;
21
23
 
22
24
  /**
23
- * @typedef {import('../types').TestData} TestData
24
- * @typedef {import('../types').PipeResult} PipeResult
25
+ * @typedef {import('../types/types.js').TestData} TestData
26
+ * @typedef {import('../types/types.js').PipeResult} PipeResult
25
27
  */
26
28
 
27
29
  class Client {
@@ -29,12 +31,11 @@ class Client {
29
31
  * Create a Testomat client instance
30
32
  * @returns
31
33
  */
32
- // eslint-disable-next-line
33
34
  constructor(params = {}) {
34
- this.uuid = randomUUID();
35
+ this.paramsForPipesFactory = params;
36
+ this.pipeStore = {};
37
+ this.runId = randomUUID(); // will be replaced by real run id
35
38
  this.queue = Promise.resolve();
36
- this.totalUploaded = 0;
37
- this.failedToUpload = 0;
38
39
 
39
40
  // @ts-ignore this line will be removed in compiled code, because __dirname is defined in commonjs
40
41
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -47,6 +48,7 @@ class Client {
47
48
  }
48
49
  this.executionList = Promise.resolve();
49
50
 
51
+ this.uploader = new S3Uploader();
50
52
  }
51
53
 
52
54
  /**
@@ -66,8 +68,7 @@ class Client {
66
68
  * or resolves to undefined if no valid results are found or if all pipes are disabled.
67
69
  */
68
70
  async prepareRun(params) {
69
- const store = {};
70
- this.pipes = await pipesFactory(params, store);
71
+ this.pipes = await pipesFactory(params || this.paramsForPipesFactory || {}, this.pipeStore);
71
72
  const { pipe, pipeOptions } = params;
72
73
  // all pipes disabled, skipping
73
74
  if (!this.pipes.some(p => p.isEnabled)) {
@@ -77,7 +78,7 @@ class Client {
77
78
  try {
78
79
  const filterPipe = this.pipes.find(p => p.constructor.name.toLowerCase() === `${pipe.toLowerCase()}pipe`);
79
80
 
80
- if (!filterPipe.isEnabled) {
81
+ if (!filterPipe?.isEnabled) {
81
82
  // TODO:for the future for the another pipes
82
83
  console.warn(
83
84
  APP_PREFIX,
@@ -110,7 +111,8 @@ class Client {
110
111
  * @returns {Promise<any>} - resolves to Run id which should be used to update / add test
111
112
  */
112
113
  async createRun(params) {
113
- if (!this.pipes || !this.pipes.length) this.pipes = await pipesFactory(params || {}, {});
114
+ if (!this.pipes || !this.pipes.length)
115
+ this.pipes = await pipesFactory(params || this.paramsForPipesFactory || {}, this.pipeStore);
114
116
  debug('Creating run...');
115
117
  // all pipes disabled, skipping
116
118
  if (!this.pipes?.filter(p => p.isEnabled).length) return Promise.resolve();
@@ -118,6 +120,12 @@ class Client {
118
120
  this.queue = this.queue
119
121
  .then(() => Promise.all(this.pipes.map(p => p.createRun())))
120
122
  .catch(err => console.log(APP_PREFIX, err))
123
+ .then(() => {
124
+ const runId = this.pipeStore?.runId;
125
+ if (runId) this.runId = runId;
126
+ storeRunId(this.runId);
127
+ })
128
+ .then(() => this.uploader.checkEnabled())
121
129
  .then(() => undefined); // fixes return type
122
130
  // debug('Run', this.queue);
123
131
  return this.queue;
@@ -165,9 +173,45 @@ class Client {
165
173
  suite_id,
166
174
  test_id,
167
175
  manuallyAttachedArtifacts,
168
- meta,
169
176
  } = testData;
170
- let { message = '' } = testData;
177
+ let { message = '', meta = {} } = testData;
178
+
179
+ // stringify meta values and limit keys and values length to 255
180
+ meta = Object.entries(meta)
181
+ .filter(([, value]) => value !== null && value !== undefined)
182
+ .map(([key, value]) => {
183
+ try {
184
+ if (typeof value === 'object') {
185
+ value = JSON.stringify(value);
186
+ } else if (typeof value !== 'string') {
187
+ try {
188
+ value = value.toString();
189
+ } catch (err) {
190
+ console.warn(APP_PREFIX, `Can't convert meta value to string`, err);
191
+ }
192
+ }
193
+
194
+ if (value?.length > 255) {
195
+ value = value.substring(0, 255);
196
+ debug(APP_PREFIX, `Meta info value "${value}" is too long, trimmed to 255 characters`);
197
+ }
198
+
199
+ if (key?.length > 255) {
200
+ const newKey = key.substring(0, 255);
201
+ debug(APP_PREFIX, `Meta info key "${key}" is too long, trimmed to 255 characters`);
202
+ return [newKey, value];
203
+ }
204
+
205
+ return [key, value];
206
+ } catch (err) {
207
+ debug(APP_PREFIX, `Error while processing meta info key ${key}`, err);
208
+ return [null, null];
209
+ }
210
+ })
211
+ .reduce((acc, [key, value]) => {
212
+ if (key) acc[key] = value;
213
+ return acc;
214
+ }, {});
171
215
 
172
216
  let errorFormatted = '';
173
217
  if (error) {
@@ -183,24 +227,24 @@ class Client {
183
227
 
184
228
  const uploadedFiles = [];
185
229
 
186
- for (const f of files) {
187
- uploadedFiles.push(upload.uploadFileByPath(f, this.uuid));
230
+ for (let f of files) {
231
+ if (!f) continue; // f === null
232
+ if (typeof f === 'object') {
233
+ if (!f.path) continue;
234
+
235
+ f = f.path;
236
+ }
237
+
238
+ uploadedFiles.push(this.uploader.uploadFileByPath(f, [this.runId, rid, path.basename(f)]));
188
239
  }
189
240
 
190
241
  for (const [idx, buffer] of filesBuffers.entries()) {
191
242
  const fileName = `${idx + 1}-${title.replace(/\s+/g, '-')}`;
192
- uploadedFiles.push(upload.uploadFileAsBuffer(buffer, fileName, this.uuid));
243
+ uploadedFiles.push(this.uploader.uploadFileAsBuffer(buffer, [this.runId, rid, fileName]));
193
244
  }
194
245
 
195
246
  const artifacts = (await Promise.all(uploadedFiles)).filter(n => !!n);
196
247
 
197
- if (artifacts.length < uploadedFiles.length) {
198
- const failedUploading = uploadedFiles.length - artifacts.length;
199
- this.failedToUpload += failedUploading;
200
- }
201
-
202
- this.totalUploaded += artifacts.length;
203
-
204
248
  const data = {
205
249
  rid,
206
250
  files,
@@ -258,27 +302,81 @@ class Client {
258
302
  this.queue = this.queue
259
303
  .then(() => Promise.all(this.pipes.map(p => p.finishRun(runParams))))
260
304
  .then(() => {
261
- debug('TOTAL artifacts', this.totalUploaded);
262
- if (this.totalUploaded && !upload.isArtifactsEnabled())
263
- debug(`${this.totalUploaded} artifacts are not uploaded, because artifacts uploading is not enabled`);
305
+ if (!this.uploader.isEnabled) return;
306
+
307
+ const filesizeStrMaxLength = 7;
308
+
309
+ if (this.uploader.successfulUploads.length) {
310
+ debug('\n', APP_PREFIX, `🗄️ ${this.uploader.successfulUploads.length} artifacts uploaded to S3 bucket`);
311
+ const uploadedArtifacts = this.uploader.successfulUploads.map(file => ({
312
+ relativePath: file.path.replace(process.cwd(), ''),
313
+ link: file.link,
314
+ sizePretty: prettyBytes(file.size, { round: 0 }).toString(),
315
+ }));
316
+
317
+ uploadedArtifacts.forEach(upload => {
318
+ debug(
319
+ `🟢Uploaded artifact`,
320
+ `${upload.relativePath},`,
321
+ 'size:',
322
+ `${upload.sizePretty},`,
323
+ 'link:',
324
+ `${upload.link}`,
325
+ );
326
+ });
327
+ }
264
328
 
265
- if (this.totalUploaded && upload.isArtifactsEnabled()) {
329
+ if (this.uploader.failedUploads.length) {
266
330
  console.log(
267
331
  APP_PREFIX,
268
- `🗄️ ${this.totalUploaded} artifacts ${
269
- process.env.TESTOMATIO_PRIVATE_ARTIFACTS ? 'privately' : pc.bold('publicly')
270
- } uploaded to S3 bucket`,
332
+ `🗄️ ${this.uploader.failedUploads.length} artifacts 🔴${pc.bold('failed')} to upload`,
271
333
  );
334
+ const failedUploads = this.uploader.failedUploads.map(file => ({
335
+ relativePath: file.path.replace(process.cwd(), ''),
336
+ sizePretty: prettyBytes(file.size, { round: 0 }).toString(),
337
+ }));
272
338
 
273
- if (this.failedToUpload > 0) {
339
+ const pathPadding = Math.max(...failedUploads.map(upload => upload.relativePath.length)) + 1;
340
+
341
+ failedUploads.forEach(upload => {
274
342
  console.log(
275
- APP_PREFIX,
276
- pc.yellow(
277
- `Some artifacts were not uploaded. ${this.failedToUpload} artifacts could not be uploaded.
278
- Run tests with DEBUG="@testomatio/reporter:file-uploader" to see details"`,
279
- ),
343
+ ` ${pc.gray('|')} 🔴 ${upload.relativePath.padEnd(pathPadding)} ${pc.gray(
344
+ `| ${upload.sizePretty.padStart(filesizeStrMaxLength)} |`,
345
+ )}`,
280
346
  );
281
- }
347
+ });
348
+ }
349
+
350
+ if (this.uploader.skippedUploads.length) {
351
+ console.log(
352
+ '\n',
353
+ APP_PREFIX,
354
+ `🗄️ ${pc.bold(this.uploader.skippedUploads.length)} artifacts uploading 🟡${pc.bold('skipped')}`,
355
+ );
356
+ const skippedUploads = this.uploader.skippedUploads.map(file => ({
357
+ relativePath: file.path.replace(process.cwd(), ''),
358
+ sizePretty: file.size === null ? 'unknown' : prettyBytes(file.size, { round: 0 }).toString(),
359
+ }));
360
+ const pathPadding = Math.max(...skippedUploads.map(upload => upload.relativePath.length)) + 1;
361
+ skippedUploads.forEach(upload => {
362
+ console.log(
363
+ ` ${pc.gray('|')} 🟡 ${upload.relativePath.padEnd(pathPadding)} ${pc.gray(
364
+ `| ${upload.sizePretty.padStart(filesizeStrMaxLength)} |`,
365
+ )}`,
366
+ );
367
+ });
368
+ }
369
+
370
+ if (this.uploader.skippedUploads.length || this.uploader.failedUploads.length) {
371
+ const command = `TESTOMATIO=<your_api_key> TESTOMATIO_RUN=${
372
+ this.runId
373
+ } npx @testomatio/reporter upload-artifacts`;
374
+ const numberOfNotUploadedArtifacts = this.uploader.skippedUploads.length + this.uploader.failedUploads.length;
375
+ console.log(
376
+ APP_PREFIX,
377
+ `${numberOfNotUploadedArtifacts} artifacts were not uploaded.
378
+ Run "${pc.magenta(command)}" with valid S3 credentials to upload skipped & failed artifacts`,
379
+ );
282
380
  }
283
381
  })
284
382
  .catch(err => console.log(APP_PREFIX, err));
@@ -365,24 +463,6 @@ function isNotInternalFrame(frame) {
365
463
  );
366
464
  }
367
465
 
368
- function formatStep(step, shift = 0) {
369
- const prefix = ' '.repeat(shift);
370
-
371
- const lines = [];
372
-
373
- if (step.error) {
374
- lines.push(`${prefix}${pc.red(step.title)} ${pc.gray(`${step.duration}ms`)}`);
375
- } else {
376
- lines.push(`${prefix}${step.title} ${pc.gray(`${step.duration}ms`)}`);
377
- }
378
-
379
- for (const child of step.steps || []) {
380
- lines.push(...formatStep(child, shift + 2));
381
- }
382
-
383
- return lines;
384
- }
385
-
386
466
  /**
387
467
  *
388
468
  * @param {TestData} testData
package/src/config.js CHANGED
@@ -6,10 +6,10 @@ const debug = createDebugMessages('@testomatio/reporter:config');
6
6
  /* for possibility to use multiple env files (reading different paths)
7
7
  const envFileVars = dotenv.config({ path: '.env' }).parsed; */
8
8
 
9
- if (process.env.TESTOMATIO_API_KEY) {
9
+ if (process.env.TESTOMATIO_API_KEY && !process.env.TESTOMATIO) {
10
10
  process.env.TESTOMATIO = process.env.TESTOMATIO_API_KEY;
11
11
  }
12
- if (process.env.TESTOMATIO_TOKEN) {
12
+ if (process.env.TESTOMATIO_TOKEN && !process.env.TESTOMATIO) {
13
13
  process.env.TESTOMATIO = process.env.TESTOMATIO_TOKEN;
14
14
  }
15
15
 
package/src/constants.js CHANGED
@@ -3,7 +3,11 @@ import os from 'os';
3
3
  import path from 'path';
4
4
 
5
5
  const APP_PREFIX = pc.gray('[TESTOMATIO]');
6
- const AXIOS_TIMEOUT = 20 * 1000; // sum = 20sec
6
+ const TESTOMATIO_REQUEST_TIMEOUT = parseInt(process.env.TESTOMATIO_REQUEST_TIMEOUT, 10);
7
+ if (TESTOMATIO_REQUEST_TIMEOUT) {
8
+ console.log(`${APP_PREFIX} Request timeout is set to ${TESTOMATIO_REQUEST_TIMEOUT / 1000}s`);
9
+ }
10
+ const AXIOS_TIMEOUT = TESTOMATIO_REQUEST_TIMEOUT || 20 * 1000;
7
11
 
8
12
  const TESTOMAT_TMP_STORAGE_DIR = path.join(os.tmpdir(), 'testomatio_tmp');
9
13
 
@@ -52,7 +52,7 @@ class DataStorage {
52
52
 
53
53
  context = context || this.context || testRunnerHelper.getNameOfCurrentlyRunningTest();
54
54
  if (!context) {
55
- debug(`No context provided for "${dataType}" data:`, data);
55
+ // debug(`No context provided for "${dataType}" data:`, data);
56
56
  return;
57
57
  }
58
58
  const contextHash = stringToMD5Hash(context);
@@ -101,7 +101,7 @@ class DataStorage {
101
101
  if (testDataFromFile.length) {
102
102
  return testDataFromFile;
103
103
  }
104
- debug(`No "${dataType}" data for context "${contextHash}" in both file and global variable`);
104
+ // debug(`No "${dataType}" data for context "${contextHash}" in both file and global variable`);
105
105
 
106
106
  // in case no data found for context
107
107
  return null;
@@ -1,3 +1,4 @@
1
+ import path from 'path';
1
2
  import Adapter from './adapter.js';
2
3
 
3
4
  class CSharpAdapter extends Adapter {
@@ -7,10 +8,21 @@ class CSharpAdapter extends Adapter {
7
8
  if (example) t.example = { ...example[1].split(',') };
8
9
  const suite = t.suite_title.split('.');
9
10
  t.suite_title = suite.pop();
10
- t.file = suite.join('/');
11
+ t.file = namespaceToFileName(t.file);
11
12
  t.title = title.trim();
12
13
  return t;
13
14
  }
15
+
16
+ getFilePath(t) {
17
+ const fileName = namespaceToFileName(t.file);
18
+ return fileName;
19
+ }
14
20
  }
15
21
 
16
22
  export default CSharpAdapter;
23
+
24
+ function namespaceToFileName(fileName) {
25
+ const fileParts = fileName.split('.');
26
+ fileParts[fileParts.length - 1] = fileParts[fileParts.length - 1]?.replace(/\$.*/, '');
27
+ return `${fileParts.join(path.sep)}.cs`;
28
+ }
@@ -15,8 +15,8 @@ const debug = createDebugMessages('@testomatio/reporter:pipe:bitbucket');
15
15
 
16
16
  /**
17
17
  * @class BitbucketPipe
18
- * @typedef {import('../../types').Pipe} Pipe
19
- * @typedef {import('../../types').TestData} TestData
18
+ * @typedef {import('../../types/types.js').Pipe} Pipe
19
+ * @typedef {import('../../types/types.js').TestData} TestData
20
20
  */
21
21
  export class BitbucketPipe {
22
22
  constructor(params, store = {}) {
package/src/pipe/csv.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import createDebugMessages from 'debug';
2
2
  import path from 'path';
3
3
  import fs from 'fs';
4
- import {createObjectCsvWriter} from 'csv-writer';
4
+ import { createObjectCsvWriter } from 'csv-writer';
5
5
  import pc from 'picocolors';
6
6
  import merge from 'lodash.merge';
7
7
  import { isSameTest, getCurrentDateTime, ansiRegExp } from '../utils/utils.js';
@@ -9,8 +9,8 @@ import { CSV_HEADERS } from '../constants.js';
9
9
 
10
10
  const debug = createDebugMessages('@testomatio/reporter:pipe:csv');
11
11
  /**
12
- * @typedef {import('../../types').Pipe} Pipe
13
- * @typedef {import('../../types').TestData} TestData
12
+ * @typedef {import('../../types/types.js').Pipe} Pipe
13
+ * @typedef {import('../../types/types.js').TestData} TestData
14
14
  * @class CsvPipe
15
15
  * @implements {Pipe}
16
16
  */