@testomatio/reporter 2.7.1 → 2.7.2

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 (52) hide show
  1. package/README.md +2 -1
  2. package/lib/adapter/codecept.js +81 -26
  3. package/lib/adapter/playwright.d.ts +1 -1
  4. package/lib/adapter/playwright.js +54 -34
  5. package/lib/adapter/utils/step-formatter.d.ts +134 -0
  6. package/lib/adapter/utils/step-formatter.js +237 -0
  7. package/lib/adapter/vitest.d.ts +9 -0
  8. package/lib/adapter/vitest.js +75 -29
  9. package/lib/bin/cli.js +28 -31
  10. package/lib/bin/reportXml.js +5 -6
  11. package/lib/bin/uploadArtifacts.js +6 -6
  12. package/lib/client.d.ts +8 -0
  13. package/lib/client.js +71 -10
  14. package/lib/constants.d.ts +1 -0
  15. package/lib/constants.js +7 -1
  16. package/lib/pipe/bitbucket.js +2 -1
  17. package/lib/pipe/coverage.js +16 -15
  18. package/lib/pipe/debug.js +3 -3
  19. package/lib/pipe/github.js +3 -2
  20. package/lib/pipe/gitlab.js +2 -1
  21. package/lib/pipe/index.js +5 -5
  22. package/lib/pipe/testomatio.js +21 -24
  23. package/lib/uploader.js +3 -2
  24. package/lib/utils/log.d.ts +45 -0
  25. package/lib/utils/log.js +98 -0
  26. package/lib/utils/pipe_utils.js +5 -5
  27. package/lib/utils/utils.d.ts +10 -0
  28. package/lib/utils/utils.js +16 -1
  29. package/lib/xmlReader.js +5 -4
  30. package/package.json +1 -1
  31. package/src/adapter/codecept.js +99 -29
  32. package/src/adapter/playwright.js +64 -39
  33. package/src/adapter/utils/step-formatter.js +232 -0
  34. package/src/adapter/vitest.js +70 -26
  35. package/src/bin/cli.js +34 -31
  36. package/src/bin/reportXml.js +5 -6
  37. package/src/bin/uploadArtifacts.js +6 -6
  38. package/src/client.js +76 -26
  39. package/src/constants.js +4 -0
  40. package/src/pipe/bitbucket.js +2 -1
  41. package/src/pipe/coverage.js +16 -15
  42. package/src/pipe/debug.js +3 -3
  43. package/src/pipe/github.js +4 -3
  44. package/src/pipe/gitlab.js +2 -1
  45. package/src/pipe/index.js +5 -7
  46. package/src/pipe/testomatio.js +32 -25
  47. package/src/uploader.js +3 -2
  48. package/src/utils/log.js +87 -0
  49. package/src/utils/pipe_utils.js +5 -5
  50. package/src/utils/utils.js +14 -0
  51. package/src/xmlReader.js +5 -4
  52. package/types/types.d.ts +3 -0
package/src/bin/cli.js CHANGED
@@ -14,6 +14,7 @@ import pc from 'picocolors';
14
14
  import { filesize as prettyBytes } from 'filesize';
15
15
  import dotenv from 'dotenv';
16
16
  import Replay from '../replay.js';
17
+ import { log } from '../utils/log.js';
17
18
 
18
19
  const debug = createDebugMessages('@testomatio/reporter:cli');
19
20
  const version = getPackageVersion();
@@ -89,7 +90,7 @@ program
89
90
  const client = new TestomatClient({ apiKey, title });
90
91
 
91
92
  if (opts.filter || opts.filterList) {
92
- console.log(APP_PREFIX,'Filtering tests...');
93
+ log.info('Filtering tests...');
93
94
  // Example of use: npx @testomatio/reporter run "npx jest" --filter "testomatio:tag-name=frontend"
94
95
  // Example of use: npx @testomatio/reporter run "npx jest" --filter "coverage:file=coverage.yml"
95
96
  // Example of use: npx @testomatio/reporter run "npx jest" --filter-list "coverage:file=coverage.yml"
@@ -105,7 +106,7 @@ program
105
106
  const tests = await client.prepareRun(prepareRunParams);
106
107
 
107
108
  if (!tests || tests.length === 0) {
108
- console.log(APP_PREFIX, pc.yellow('No tests found.'));
109
+ log.info( pc.yellow('No tests found.'));
109
110
  return;
110
111
  }
111
112
 
@@ -115,8 +116,8 @@ program
115
116
  debug(`Execution pattern: "${pattern}"`);
116
117
 
117
118
  if(opts.filterList) {
118
- console.log(APP_PREFIX, pc.blue(`Matched test/suite IDs: ${tests.join(', ')}`));
119
- if (command) console.log(APP_PREFIX, pc.green(`Full Running Command: ${filteredCommand}`));
119
+ log.info( pc.blue(`Matched test/suite IDs: ${tests.join(', ')}`));
120
+ if (command) log.info( pc.green(`Full Running Command: ${filteredCommand}`));
120
121
  return;
121
122
  }
122
123
 
@@ -125,7 +126,7 @@ program
125
126
  }
126
127
  }
127
128
  catch (err) {
128
- console.log(APP_PREFIX, err.message || err);
129
+ log.info( err.message || err);
129
130
  return;
130
131
  }
131
132
  }
@@ -143,20 +144,20 @@ program
143
144
  if (apiKey) {
144
145
  await client.createRun(createRunParams);
145
146
  const runId = process.env.TESTOMATIO_RUN || process.env.runId;
146
- if (client.pipeStore.runUrl) console.log(APP_PREFIX, `📊 Report URL: ${pc.magenta(client.pipeStore.runUrl)}`);
147
+ if (client.pipeStore.runUrl) log.info( `📊 Report URL: ${pc.magenta(client.pipeStore.runUrl)}`);
147
148
 
148
149
  if (opts.kind !== 'manual') {
149
- console.log(APP_PREFIX, `No command passed, so you need to run tests yourself:`);
150
- console.log(APP_PREFIX, `TESTOMATIO_RUN=${runId} <command>`);
150
+ log.info( `No command passed, so you need to run tests yourself:`);
151
+ log.info( `TESTOMATIO_RUN=${runId} <command>`);
151
152
  }
152
153
  } else {
153
- console.log(APP_PREFIX, 'âš ī¸ No API key provided. Cannot create run without TESTOMATIO key.');
154
+ log.info( 'âš ī¸ No API key provided. Cannot create run without TESTOMATIO key.');
154
155
  process.exit(1);
155
156
  }
156
157
  return process.exit(0);
157
158
  }
158
159
 
159
- console.log(APP_PREFIX, `🚀 Running`, pc.green(command));
160
+ log.info( `🚀 Running`, pc.green(command));
160
161
 
161
162
  const runTests = async () => {
162
163
  const testCmds = command.split(' ');
@@ -167,7 +168,7 @@ program
167
168
 
168
169
  cmd.on('close', async code => {
169
170
  const emoji = code === 0 ? 'đŸŸĸ' : '🔴';
170
- console.log(APP_PREFIX, emoji, `Runner exited with ${pc.bold(code)}`);
171
+ log.info( emoji, `Runner exited with ${pc.bold(code)}`);
171
172
  if (apiKey) {
172
173
  const status = code === 0 ? 'passed' : 'failed';
173
174
  await client.updateRunStatus(status);
@@ -209,7 +210,7 @@ program
209
210
  // const runReader = new XmlReader({ javaTests, lang });
210
211
  // const files = glob.sync(pattern, { cwd: opts.dir || process.cwd() });
211
212
  // if (!files.length) {
212
- // console.log(APP_PREFIX, `Report can't be created. No XML files found đŸ˜Ĩ`);
213
+ // log.info( `Report can't be created. No XML files found đŸ˜Ĩ`);
213
214
  // process.exit(1);
214
215
  // }
215
216
 
@@ -231,12 +232,12 @@ program
231
232
  const runReader = new XmlReader({ javaTests, lang });
232
233
  const files = glob.sync(pattern, { cwd: opts.dir || process.cwd() });
233
234
  if (!files.length) {
234
- console.log(APP_PREFIX, `Report can't be created. No XML files found đŸ˜Ĩ`);
235
+ log.info( `Report can't be created. No XML files found đŸ˜Ĩ`);
235
236
  process.exit(1);
236
237
  }
237
238
 
238
239
  for (const file of files) {
239
- console.log(APP_PREFIX, `Parsed ${file}`);
240
+ log.info( `Parsed ${file}`);
240
241
  runReader.parse(file);
241
242
  }
242
243
 
@@ -257,7 +258,7 @@ program
257
258
  await runReader.createRun();
258
259
  await runReader.uploadData();
259
260
  } catch (err) {
260
- console.log(APP_PREFIX, 'Error updating status, skipping...', err);
261
+ log.info( 'Error updating status, skipping...', err);
261
262
  }
262
263
 
263
264
  if (timeoutTimer) clearTimeout(timeoutTimer);
@@ -292,10 +293,10 @@ program
292
293
  if (!opts.force) testruns = testruns.filter(tr => !tr.uploaded);
293
294
 
294
295
  if (!testruns.length) {
295
- console.log(APP_PREFIX, 'đŸ—„ī¸ Total artifacts:', numTotalArtifacts);
296
+ log.info( 'đŸ—„ī¸ Total artifacts:', numTotalArtifacts);
296
297
  if (numTotalArtifacts) {
297
- console.log(APP_PREFIX, 'No new artifacts to upload');
298
- console.log(APP_PREFIX, 'To re-upload artifacts run this command with --force flag');
298
+ log.info( 'No new artifacts to upload');
299
+ log.info( 'To re-upload artifacts run this command with --force flag');
299
300
  }
300
301
  process.exit(0);
301
302
  }
@@ -317,7 +318,7 @@ program
317
318
  await client.addTestRun(undefined, { rid, files });
318
319
  }
319
320
 
320
- console.log(APP_PREFIX, 'đŸ—„ī¸', client.uploader.successfulUploads.length, 'artifacts đŸŸĸuploaded');
321
+ log.info( 'đŸ—„ī¸', client.uploader.successfulUploads.length, 'artifacts đŸŸĸuploaded');
321
322
 
322
323
  if (client.uploader.successfulUploads.length) {
323
324
  debug('\n', APP_PREFIX, `đŸ—„ī¸ ${client.uploader.successfulUploads.length} artifacts uploaded to S3 bucket`);
@@ -376,11 +377,11 @@ program
376
377
  const replayService = new Replay({
377
378
  apiKey: config.TESTOMATIO,
378
379
  dryRun: opts.dryRun,
379
- onLog: message => console.log(APP_PREFIX, message),
380
- onError: message => console.error(APP_PREFIX, 'âš ī¸ ', message),
380
+ onLog: message => log.info( message),
381
+ onError: message => log.error( 'âš ī¸ ', message),
381
382
  onProgress: ({ current, total }) => {
382
383
  if (current % 10 === 0 || current === total) {
383
- console.log(APP_PREFIX, `📊 Progress: ${current}/${total} tests processed`);
384
+ log.info( `📊 Progress: ${current}/${total} tests processed`);
384
385
  }
385
386
  },
386
387
  });
@@ -388,23 +389,25 @@ program
388
389
  const result = await replayService.replay(debugFile);
389
390
 
390
391
  if (result.dryRun) {
391
- console.log(APP_PREFIX, '🔍 Dry run completed:');
392
- console.log(APP_PREFIX, ` - Tests found: ${result.testsCount}`);
393
- console.log(APP_PREFIX, ` - Environment variables: ${Object.keys(result.envVars).length}`);
394
- console.log(APP_PREFIX, ` - Run parameters:`, result.runParams);
395
- console.log(APP_PREFIX, ' Use without --dry-run to actually send the data');
392
+ log.info(
393
+ '🔍 Dry run completed:\n',
394
+ ` - Tests found: ${result.testsCount}\n`,
395
+ ` - Environment variables: ${Object.keys(result.envVars).length}\n`,
396
+ ' - Run parameters:', result.runParams, '\n',
397
+ ' Use without --dry-run to actually send the data',
398
+ );
396
399
  } else {
397
- console.log(APP_PREFIX, `✅ Successfully replayed ${result.successCount}/${result.testsCount} tests`);
400
+ log.info( `✅ Successfully replayed ${result.successCount}/${result.testsCount} tests`);
398
401
  if (result.failureCount > 0) {
399
- console.log(APP_PREFIX, `âš ī¸ ${result.failureCount} tests failed to upload`);
402
+ log.info( `âš ī¸ ${result.failureCount} tests failed to upload`);
400
403
  }
401
404
  }
402
405
 
403
406
  process.exit(0);
404
407
  } catch (err) {
405
- console.error(APP_PREFIX, '❌ Error replaying debug data:', err.message);
408
+ log.error( '❌ Error replaying debug data:', err.message);
406
409
  if (err.message.includes('Debug file not found')) {
407
- console.error(APP_PREFIX, '💡 Hint: Run tests with TESTOMATIO_DEBUG=1 to generate debug files');
410
+ log.error( '💡 Hint: Run tests with TESTOMATIO_DEBUG=1 to generate debug files');
408
411
  }
409
412
  process.exit(1);
410
413
  }
@@ -3,11 +3,10 @@ import { Command } from 'commander';
3
3
  import pc from 'picocolors';
4
4
  import { glob } from 'glob';
5
5
  import createDebugMessages from 'debug';
6
- import { APP_PREFIX } from '../constants.js';
7
6
  import XmlReader from '../xmlReader.js';
8
7
  import { getPackageVersion } from '../utils/utils.js';
9
8
  import dotenv from 'dotenv';
10
- import path from 'path';
9
+ import { log } from '../utils/log.js';
11
10
 
12
11
  const version = getPackageVersion();
13
12
 
@@ -28,7 +27,7 @@ program
28
27
  }
29
28
  let { javaTests, lang } = opts;
30
29
  if (opts.envFile) {
31
- console.log(APP_PREFIX, 'Loading env file:', opts.envFile);
30
+ log.info( 'Loading env file:', opts.envFile);
32
31
  debug('Loading env file: %s', opts.envFile);
33
32
  dotenv.config({ path: opts.envFile });
34
33
  }
@@ -40,13 +39,13 @@ program
40
39
  });
41
40
  const files = glob.sync(pattern, { cwd: opts.dir || process.cwd() });
42
41
  if (!files.length) {
43
- console.log(APP_PREFIX, `Report can't be created. No XML files found đŸ˜Ĩ`);
42
+ log.info( `Report can't be created. No XML files found đŸ˜Ĩ`);
44
43
  process.exitCode = 1;
45
44
  return;
46
45
  }
47
46
 
48
47
  for (const file of files) {
49
- console.log(APP_PREFIX, `Parsed ${file}`);
48
+ log.info( `Parsed ${file}`);
50
49
  runReader.parse(file);
51
50
  }
52
51
 
@@ -67,7 +66,7 @@ program
67
66
  await runReader.createRun();
68
67
  await runReader.uploadData();
69
68
  } catch (err) {
70
- console.log(APP_PREFIX, 'Error updating status, skipping...', err);
69
+ log.info( 'Error updating status, skipping...', err);
71
70
  }
72
71
 
73
72
  if (timeoutTimer) clearTimeout(timeoutTimer);
@@ -4,11 +4,11 @@ import { Command } from 'commander';
4
4
  import pc from 'picocolors';
5
5
  import createDebugMessages from 'debug';
6
6
  import TestomatClient from '../client.js';
7
- import { APP_PREFIX } from '../constants.js';
8
7
  import { getPackageVersion } from '../utils/utils.js';
9
8
  import { config } from '../config.js';
10
9
  import { readLatestRunId } from '../utils/utils.js';
11
10
  import dotenv from 'dotenv';
11
+ import { log } from '../utils/log.js';
12
12
 
13
13
  const debug = createDebugMessages('@testomatio/reporter:upload-cli');
14
14
  const version = getPackageVersion();
@@ -48,10 +48,10 @@ program
48
48
  if (!opts.force) testruns = testruns.filter(tr => !tr.uploaded);
49
49
 
50
50
  if (!testruns.length) {
51
- console.log(APP_PREFIX, 'Total artifacts:', numTotalArtifacts);
51
+ log.info('Total artifacts:', numTotalArtifacts);
52
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');
53
+ log.info('No new artifacts to upload');
54
+ log.info('To re-upload artifacts run this command with --force flag');
55
55
  }
56
56
  process.exit(0);
57
57
  }
@@ -78,9 +78,9 @@ program
78
78
  });
79
79
  }
80
80
 
81
- console.log(APP_PREFIX, client.uploader.successfulUploads.length, 'artifacts uploaded');
81
+ log.info(client.uploader.successfulUploads.length, 'artifacts uploaded');
82
82
  if (client.uploader.failedUploads.length) {
83
- console.log(APP_PREFIX, client.uploader.failedUploads.length, 'artifacts failed to upload');
83
+ log.info(client.uploader.failedUploads.length, 'artifacts failed to upload');
84
84
  }
85
85
  });
86
86
 
package/src/client.js CHANGED
@@ -1,15 +1,17 @@
1
1
  import createDebugMessages from 'debug';
2
2
  import fs from 'fs';
3
3
  import pc from 'picocolors';
4
- import { APP_PREFIX, STATUS } from './constants.js';
4
+ import { APP_PREFIX, STATUS, SCREENSHOTS_ON_STEPS } from './constants.js';
5
5
  import { pipesFactory } from './pipe/index.js';
6
6
  import { glob } from 'glob';
7
7
  import path from 'path';
8
8
  import { fileURLToPath } from 'node:url';
9
9
  import { S3Uploader } from './uploader.js';
10
- import { readLatestRunId, storeRunId, validateSuiteId, transformEnvVarToBoolean } from './utils/utils.js';
10
+ import { readLatestRunId, storeRunId, validateSuiteId, transformEnvVarToBoolean, isHttpUrl } from './utils/utils.js';
11
+ import { generateShortFilename } from './adapter/utils/step-formatter.js';
11
12
  import { filesize as prettyBytes } from 'filesize';
12
13
  import { formatLogs, formatError, stripColors } from './utils/log-formatter.js';
14
+ import { log } from './utils/log.js';
13
15
 
14
16
  const debug = createDebugMessages('@testomatio/reporter:client');
15
17
 
@@ -40,7 +42,7 @@ class Client {
40
42
  const pathToPackageJSON = path.join(__dirname, '../package.json');
41
43
  try {
42
44
  this.version = JSON.parse(fs.readFileSync(pathToPackageJSON).toString()).version;
43
- console.log(APP_PREFIX, `Testomatio Reporter v${this.version}`);
45
+ log.info(`Testomatio Reporter v${this.version}`);
44
46
  } catch (e) {
45
47
  // do nothing
46
48
  }
@@ -71,7 +73,7 @@ class Client {
71
73
 
72
74
  // ❗ Validation: pipe is required
73
75
  if (!pipe || !pipeOptions) {
74
- console.warn(`❗ No valid pipe found in filter cmd. Expected format: <pipe>:<options>
76
+ log.warn(`❗ No valid pipe found in filter cmd. Expected format: <pipe>:<options>
75
77
  Examples:
76
78
  --filter "testomatio:tag-name=frontend"
77
79
  --filter "coverage:file=coverage.yml"
@@ -92,10 +94,7 @@ class Client {
92
94
  // const p = this.pipes.find(p => p.id === `${pipe.toLowerCase()}`); TODO: as future updates
93
95
 
94
96
  if (!p?.isEnabled) {
95
- console.warn(
96
- APP_PREFIX,
97
- "đŸšĢ No active pipes were found in the system. Execution aborted!"
98
- );
97
+ log.warn('đŸšĢ No active pipes were found in the system. Execution aborted!');
99
98
  return;
100
99
  }
101
100
 
@@ -107,7 +106,7 @@ class Client {
107
106
 
108
107
  return result;
109
108
  } catch (err) {
110
- console.error(APP_PREFIX, err);
109
+ log.error(err);
111
110
  }
112
111
  }
113
112
 
@@ -125,7 +124,7 @@ class Client {
125
124
 
126
125
  this.queue = this.queue
127
126
  .then(() => Promise.all(this.pipes.map(p => p.createRun(params))))
128
- .catch(err => console.log(APP_PREFIX, err))
127
+ .catch(err => log.info(err))
129
128
  .then(() => {
130
129
  const runId = this.pipeStore?.runId;
131
130
  if (runId) this.runId = runId;
@@ -137,6 +136,60 @@ class Client {
137
136
  return this.queue;
138
137
  }
139
138
 
139
+ /**
140
+ * Recursively uploads artifacts from steps
141
+ *
142
+ * @param {*} steps - Steps payload (validated inside function)
143
+ * @param {string} testRid - Test/result ID
144
+ * @returns {Promise<void>}
145
+ */
146
+ async uploadStepArtifacts(steps, testRid) {
147
+ if (!steps || !Array.isArray(steps)) return;
148
+ if (!this.uploader.isEnabled || !SCREENSHOTS_ON_STEPS) return;
149
+
150
+ try {
151
+ for (const step of steps) {
152
+ if (!(step.artifacts && Array.isArray(step.artifacts))) {
153
+ if (step.steps) {
154
+ await this.uploadStepArtifacts(step.steps, testRid);
155
+ }
156
+ continue;
157
+ }
158
+
159
+ const uploadedArtifacts = [];
160
+ for (const artifact of step.artifacts) {
161
+ if (typeof artifact === 'string' && !isHttpUrl(artifact)) {
162
+ const filename = generateShortFilename(artifact);
163
+ try {
164
+ const uploadResult = await this.uploader.uploadFileByPath(
165
+ artifact,
166
+ [this.runId, testRid, 'steps', filename]
167
+ );
168
+ if (uploadResult) {
169
+ uploadedArtifacts.push(uploadResult);
170
+ } else {
171
+ uploadedArtifacts.push(artifact);
172
+ }
173
+ } catch (uploadErr) {
174
+ uploadedArtifacts.push(artifact);
175
+ }
176
+ } else {
177
+ uploadedArtifacts.push(artifact);
178
+ }
179
+ }
180
+ step.artifacts = uploadedArtifacts;
181
+
182
+ if (step.steps) {
183
+ await this.uploadStepArtifacts(step.steps, testRid);
184
+ }
185
+ }
186
+
187
+ } catch (err) {
188
+ console.error(APP_PREFIX, 'Error in uploadStepArtifacts for testRid', testRid, ':', err);
189
+ throw err;
190
+ }
191
+ }
192
+
140
193
  /**
141
194
  * Updates test status and its data
142
195
  *
@@ -162,6 +215,13 @@ class Client {
162
215
  const { rid, error = null, steps: originalSteps, title, suite_title } = testData;
163
216
  let steps = originalSteps;
164
217
 
218
+ // Upload artifacts from steps
219
+ try {
220
+ await this.uploadStepArtifacts(steps, rid);
221
+ } catch (err) {
222
+ console.log(APP_PREFIX, 'Failed to upload step artifacts:', err);
223
+ }
224
+
165
225
  const uploadedFiles = [];
166
226
  const stackArtifactsEnabled = transformEnvVarToBoolean(process.env.TESTOMATIO_STACK_ARTIFACTS);
167
227
 
@@ -286,7 +346,7 @@ class Client {
286
346
  const result = await pipe.addTest(data);
287
347
  return { pipe: pipe.toString(), result };
288
348
  } catch (err) {
289
- console.log(APP_PREFIX, pipe.toString(), err);
349
+ log.info(pipe.toString(), err);
290
350
  }
291
351
  }),
292
352
  ),
@@ -341,10 +401,7 @@ class Client {
341
401
  }
342
402
 
343
403
  if (this.uploader.failedUploads.length) {
344
- console.log(
345
- APP_PREFIX,
346
- `đŸ—„ī¸ ${this.uploader.failedUploads.length} artifacts 🔴${pc.bold('failed')} to upload`,
347
- );
404
+ log.info(`đŸ—„ī¸ ${this.uploader.failedUploads.length} artifacts 🔴${pc.bold('failed')} to upload`);
348
405
  const failedUploads = this.uploader.failedUploads.map(file => ({
349
406
  relativePath: file.path.replace(process.cwd(), ''),
350
407
  sizePretty: file.size == null ? 'unknown' : prettyBytes(file.size, { round: 0 }).toString(),
@@ -362,11 +419,7 @@ class Client {
362
419
  }
363
420
 
364
421
  if (this.uploader.skippedUploads.length) {
365
- console.log(
366
- '\n',
367
- APP_PREFIX,
368
- `đŸ—„ī¸ ${pc.bold(this.uploader.skippedUploads.length)} artifacts uploading 🟡${pc.bold('skipped')}`,
369
- );
422
+ log.info(`đŸ—„ī¸ ${pc.bold(this.uploader.skippedUploads.length)} artifacts uploading 🟡${pc.bold('skipped')}`);
370
423
  const skippedUploads = this.uploader.skippedUploads.map(file => ({
371
424
  relativePath: file.path.replace(process.cwd(), ''),
372
425
  sizePretty: file.size === null ? 'unknown' : prettyBytes(file.size, { round: 0 }).toString(),
@@ -386,14 +439,11 @@ class Client {
386
439
  this.runId
387
440
  } npx @testomatio/reporter upload-artifacts`;
388
441
  const numberOfNotUploadedArtifacts = this.uploader.skippedUploads.length + this.uploader.failedUploads.length;
389
- console.log(
390
- APP_PREFIX,
391
- `${numberOfNotUploadedArtifacts} artifacts were not uploaded.
392
- Run "${pc.magenta(command)}" with valid S3 credentials to upload skipped & failed artifacts`,
393
- );
442
+ log.info(`${numberOfNotUploadedArtifacts} artifacts were not uploaded.
443
+ Run "${pc.magenta(command)}" with valid S3 credentials to upload skipped & failed artifacts`);
394
444
  }
395
445
  })
396
- .catch(err => console.log(APP_PREFIX, err));
446
+ .catch(err => log.info(err));
397
447
 
398
448
  return this.queue;
399
449
  }
package/src/constants.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import pc from 'picocolors';
2
2
  import os from 'os';
3
3
  import path from 'path';
4
+ import { transformEnvVarToBoolean } from './utils/utils.js';
4
5
 
5
6
  const APP_PREFIX = pc.gray('[TESTOMATIO]');
6
7
  const TESTOMATIO_REQUEST_TIMEOUT = parseInt(process.env.TESTOMATIO_REQUEST_TIMEOUT, 10);
@@ -8,6 +9,8 @@ if (TESTOMATIO_REQUEST_TIMEOUT) {
8
9
  console.log(`${APP_PREFIX} Request timeout is set to ${TESTOMATIO_REQUEST_TIMEOUT / 1000}s`);
9
10
  }
10
11
  const AXIOS_TIMEOUT = TESTOMATIO_REQUEST_TIMEOUT || 20 * 1000;
12
+ const SCREENSHOTS_ON_STEPS = process.env.TESTOMATIO_SCREENSHOTS_ON_STEPS == null
13
+ || transformEnvVarToBoolean(process.env.TESTOMATIO_SCREENSHOTS_ON_STEPS);
11
14
 
12
15
  const TESTOMAT_TMP_STORAGE_DIR = path.join(os.tmpdir(), 'testomatio_tmp');
13
16
 
@@ -50,4 +53,5 @@ export {
50
53
  AXIOS_TIMEOUT,
51
54
  testomatLogoURL,
52
55
  REPORTER_REQUEST_RETRIES,
56
+ SCREENSHOTS_ON_STEPS,
53
57
  };
@@ -7,6 +7,7 @@ import humanizeDuration from 'humanize-duration';
7
7
  import merge from 'lodash.merge';
8
8
  import path from 'path';
9
9
  import createDebugMessages from 'debug';
10
+ import { log } from '../utils/log.js';
10
11
 
11
12
  const debug = createDebugMessages('@testomatio/reporter:pipe:bitbucket');
12
13
 
@@ -193,7 +194,7 @@ export class BitbucketPipe {
193
194
  // eslint-disable-next-line max-len
194
195
  const commentURL = `https://bitbucket.org/${this.ENV.BITBUCKET_WORKSPACE}/${this.ENV.BITBUCKET_REPO_SLUG}/pull-requests/${this.ENV.BITBUCKET_PR_ID}#comment-${commentID}`;
195
196
 
196
- console.log(APP_PREFIX, pc.yellow('Bitbucket'), `Report created: ${pc.magenta(commentURL)}`);
197
+ log.info(pc.yellow('Bitbucket'), `Report created: ${pc.magenta(commentURL)}`);
197
198
  } catch (err) {
198
199
  console.error(
199
200
  APP_PREFIX,
@@ -9,6 +9,7 @@ import { generateFilterRequestParams } from '../utils/pipe_utils.js';
9
9
  import { parsePipeOptions } from '../utils/pipe_utils.js';
10
10
  import { config } from '../config.js';
11
11
  import createDebugMessages from 'debug';
12
+ import { log } from '../utils/log.js';
12
13
 
13
14
  const debug = createDebugMessages('@testomatio/reporter:pipe:csv');
14
15
 
@@ -136,11 +137,11 @@ class CoveragePipe { // or Changes for the future???
136
137
  // Step 2: Extract all available tests and compare with coverage file
137
138
  const lines = await this.extractRelevantTestsFromChanges();
138
139
  if (this.store?.filterList && lines.size > 0) {
139
- console.log(APP_PREFIX, `Matched files: ${[...lines].join(', ')}`);
140
+ log.info( `Matched files: ${[...lines].join(', ')}`);
140
141
  }
141
142
 
142
143
  if (lines.size === 0) {
143
- console.log(APP_PREFIX, 'â„šī¸ No matching entries in coverage file for provided Git changes.');
144
+ log.info( 'â„šī¸ No matching entries in coverage file for provided Git changes.');
144
145
  return [];
145
146
  }
146
147
 
@@ -164,7 +165,7 @@ class CoveragePipe { // or Changes for the future???
164
165
  }
165
166
 
166
167
  if (this.tests.size === 0 && this.suiteIds.size === 0) {
167
- console.log(APP_PREFIX, 'â„šī¸ No tests found for execution based on Git changes.');
168
+ log.info( 'â„šī¸ No tests found for execution based on Git changes.');
168
169
  return [];
169
170
  }
170
171
 
@@ -235,7 +236,7 @@ class CoveragePipe { // or Changes for the future???
235
236
  });
236
237
 
237
238
  if (!Array.isArray(resp.data?.tests) && resp.data?.tests?.length === 0) {
238
- console.log(APP_PREFIX, `🔍 No test by ${type}=${id} were found on the Testomat.io server side!`);
239
+ log.info( `🔍 No test by ${type}=${id} were found on the Testomat.io server side!`);
239
240
 
240
241
  return undefined;
241
242
  }
@@ -275,7 +276,7 @@ class CoveragePipe { // or Changes for the future???
275
276
  const errorMessage = err.message || '';
276
277
  // Git edge: Not a git repository or other error
277
278
  if (errorMessage.includes('Not a git repository')) {
278
- console.error(APP_PREFIX, '❌ Error: This folder is not a Git repository.');
279
+ log.error( '❌ Error: This folder is not a Git repository.');
279
280
  }
280
281
  else {
281
282
  throw new Error(`❌ Git command failed ("${cmd}"):\n`, errorMessage);
@@ -319,11 +320,11 @@ class CoveragePipe { // or Changes for the future???
319
320
  cmd = this.#buildGitCommand();
320
321
  }
321
322
  catch (err) {
322
- console.error(APP_PREFIX, err.message);
323
+ log.error( err.message);
323
324
  return undefined;
324
325
  }
325
326
 
326
- console.error(APP_PREFIX, `â„šī¸ We will use '${cmd}' Git command.`);
327
+ log.error( `â„šī¸ We will use '${cmd}' Git command.`);
327
328
 
328
329
  try {
329
330
  // For clear unit testing process -> Like test_defaultGitChangedFile = todomvc-tests/edit-todos_test.js
@@ -344,12 +345,12 @@ class CoveragePipe { // or Changes for the future???
344
345
  }
345
346
  }
346
347
  catch (err) {
347
- console.error(APP_PREFIX, err.message);
348
- console.error(APP_PREFIX, "🔍 Pls, check this Git command manually to understand the original problem.");
348
+ log.error( err.message);
349
+ log.error( "🔍 Pls, check this Git command manually to understand the original problem.");
349
350
  return undefined;
350
351
  }
351
352
 
352
- console.log(APP_PREFIX, `📑 GIT changed files:\n - ${this.changedFiles.join('\n - ')}`);
353
+ log.info( `📑 GIT changed files:\n - ${this.changedFiles.join('\n - ')}`);
353
354
  return this;
354
355
  }
355
356
 
@@ -369,20 +370,20 @@ class CoveragePipe { // or Changes for the future???
369
370
  validateCoverageFile() {
370
371
  // Validate the presence of the coverage filepath
371
372
  if (!fs.existsSync(this.coverageFilePath)) {
372
- console.log(APP_PREFIX, '❌ Coverage file not found:', this.coverageFilePath);
373
+ log.info( '❌ Coverage file not found:', this.coverageFilePath);
373
374
  return undefined;
374
375
  }
375
376
 
376
377
  // Ensure the given path is a file (not a directory or other type)
377
378
  const stat = fs.statSync(this.coverageFilePath);
378
379
  if (!stat.isFile()) {
379
- console.log(APP_PREFIX, '❌ Provided coverage path is not a file:', this.coverageFilePath);
380
+ log.info( '❌ Provided coverage path is not a file:', this.coverageFilePath);
380
381
  return undefined;
381
382
  }
382
383
 
383
384
  // Validate the file extension to be ".yml" to ensure it's a YAML file
384
385
  if (path.extname(this.coverageFilePath) !== ".yml") {
385
- console.log(APP_PREFIX, '❌ Coverage file must have a .yml extension:', this.coverageFilePath);
386
+ log.info( '❌ Coverage file must have a .yml extension:', this.coverageFilePath);
386
387
  return undefined;
387
388
  }
388
389
 
@@ -408,12 +409,12 @@ class CoveragePipe { // or Changes for the future???
408
409
  this.parsedCoverage = yaml.load(rawYml) || {};
409
410
 
410
411
  debug(`Coverage filepath = ${this.coverageFilePath})`);
411
- console.log(APP_PREFIX, `✅ Coverage file parsed successfully: ${this.coverageFilePath}`);
412
+ log.info( `✅ Coverage file parsed successfully: ${this.coverageFilePath}`);
412
413
 
413
414
  return this;
414
415
  }
415
416
  catch (err) {
416
- console.error(APP_PREFIX, '❌ Failed to parse YAML:', err.message);
417
+ log.error( '❌ Failed to parse YAML:', err.message);
417
418
  return undefined;
418
419
  }
419
420
  }
package/src/pipe/debug.js CHANGED
@@ -2,8 +2,8 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
  import os from 'os';
4
4
  import createDebugMessages from 'debug';
5
- import { APP_PREFIX } from '../constants.js';
6
5
  import prettyMs from 'pretty-ms';
6
+ import { log } from '../utils/log.js';
7
7
 
8
8
  const debug = createDebugMessages('@testomatio/reporter:pipe:debug');
9
9
 
@@ -40,7 +40,7 @@ export class DebugPipe {
40
40
  debug('Failed to create symlink:', err.message);
41
41
  }
42
42
 
43
- console.log(APP_PREFIX, 'đŸĒ˛ Debug file created');
43
+ log.info('đŸĒ˛ Debug file created');
44
44
  this.testomatioEnvVars = Object.keys(process.env)
45
45
  .filter(key => key.startsWith('TESTOMATIO_'))
46
46
  .reduce((acc, key) => {
@@ -115,7 +115,7 @@ export class DebugPipe {
115
115
  await this.sync();
116
116
  if (this.batch.intervalFunction) clearInterval(this.batch.intervalFunction);
117
117
  this.logToFile({ action: 'finishRun', params });
118
- console.log(APP_PREFIX, 'đŸĒ˛ Debug Saved to', this.logFilePath);
118
+ log.info('đŸĒ˛ Debug Saved to', this.logFilePath);
119
119
  }
120
120
 
121
121
  async sync() {