@testomatio/reporter 2.7.1 β†’ 2.7.2-beta.1

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 (49) 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/bin/cli.js +28 -31
  8. package/lib/bin/reportXml.js +5 -6
  9. package/lib/bin/uploadArtifacts.js +6 -6
  10. package/lib/client.d.ts +8 -0
  11. package/lib/client.js +71 -10
  12. package/lib/constants.d.ts +1 -0
  13. package/lib/constants.js +7 -1
  14. package/lib/pipe/bitbucket.js +2 -1
  15. package/lib/pipe/coverage.js +16 -15
  16. package/lib/pipe/debug.js +3 -3
  17. package/lib/pipe/github.js +3 -2
  18. package/lib/pipe/gitlab.js +2 -1
  19. package/lib/pipe/index.js +5 -5
  20. package/lib/pipe/testomatio.js +21 -24
  21. package/lib/uploader.js +3 -2
  22. package/lib/utils/log.d.ts +45 -0
  23. package/lib/utils/log.js +98 -0
  24. package/lib/utils/pipe_utils.js +5 -5
  25. package/lib/utils/utils.d.ts +10 -0
  26. package/lib/utils/utils.js +16 -1
  27. package/lib/xmlReader.js +5 -4
  28. package/package.json +1 -1
  29. package/src/adapter/codecept.js +99 -29
  30. package/src/adapter/playwright.js +64 -39
  31. package/src/adapter/utils/step-formatter.js +232 -0
  32. package/src/bin/cli.js +34 -31
  33. package/src/bin/reportXml.js +5 -6
  34. package/src/bin/uploadArtifacts.js +6 -6
  35. package/src/client.js +76 -26
  36. package/src/constants.js +4 -0
  37. package/src/pipe/bitbucket.js +2 -1
  38. package/src/pipe/coverage.js +16 -15
  39. package/src/pipe/debug.js +3 -3
  40. package/src/pipe/github.js +4 -3
  41. package/src/pipe/gitlab.js +2 -1
  42. package/src/pipe/index.js +5 -7
  43. package/src/pipe/testomatio.js +32 -25
  44. package/src/uploader.js +3 -2
  45. package/src/utils/log.js +87 -0
  46. package/src/utils/pipe_utils.js +5 -5
  47. package/src/utils/utils.js +14 -0
  48. package/src/xmlReader.js +5 -4
  49. package/types/types.d.ts +3 -0
@@ -3,14 +3,16 @@ import os from 'os';
3
3
  import path from 'path';
4
4
  import { v4 as uuidv4 } from 'uuid';
5
5
  import fs from 'fs';
6
- import { APP_PREFIX, STATUS as Status, TESTOMAT_TMP_STORAGE_DIR } from '../constants.js';
6
+ import { APP_PREFIX, STATUS as Status, TESTOMAT_TMP_STORAGE_DIR, SCREENSHOTS_ON_STEPS } from '../constants.js';
7
7
  import TestomatioClient from '../client.js';
8
- import { getTestomatIdFromTestTitle, fileSystem } from '../utils/utils.js';
8
+ import { getTestomatIdFromTestTitle, fileSystem, truncate } from '../utils/utils.js';
9
9
  import { services } from '../services/index.js';
10
10
  import { dataStorage } from '../data-storage.js';
11
11
  import { extensionMap } from '../utils/constants.js';
12
12
  import pc from 'picocolors';
13
13
  import { fetchLinksFromLogs } from './utils/playwright.js';
14
+ import { formatStep, addStatusToStep, addArtifactsToStep } from './utils/step-formatter.js';
15
+ import { log } from '../utils/log.js';
14
16
 
15
17
  const reportTestPromises = [];
16
18
 
@@ -35,7 +37,7 @@ class PlaywrightReporter {
35
37
  dataStorage.setContext(fullTestTitle);
36
38
  }
37
39
 
38
- onTestEnd(test, result) {
40
+ async onTestEnd(test, result) {
39
41
  // test.parent.project().__projectId
40
42
 
41
43
  if (!this.client) return;
@@ -54,13 +56,26 @@ class PlaywrightReporter {
54
56
 
55
57
  const suite_title = test.parent ? test.parent?.title : path.basename(test?.location?.file);
56
58
 
57
- const steps = [];
58
- for (const step of result.steps) {
59
- const appendedStep = appendStep(step);
60
- if (appendedStep) {
61
- steps.push(appendedStep);
62
- }
63
- }
59
+ const rid = test.id || test.testId || uuidv4();
60
+
61
+ /**
62
+ * @type {{
63
+ * browser?: string,
64
+ * dependencies: string[],
65
+ * isMobile?: boolean
66
+ * metadata: Record<string, any>,
67
+ * name: string,
68
+ * }}
69
+ */
70
+ const project = {
71
+ browser: test.parent.project().use.defaultBrowserType,
72
+ dependencies: test.parent.project().dependencies,
73
+ isMobile: test.parent.project().use.isMobile,
74
+ metadata: test.parent.project().metadata,
75
+ name: test.parent.project().name,
76
+ };
77
+
78
+ const steps = result.steps.map(step => appendStep(step, 0)).filter(step => step !== null);
64
79
 
65
80
  // Extract and normalize tags
66
81
  const tags = extractTags(test);
@@ -86,24 +101,6 @@ class PlaywrightReporter {
86
101
  */
87
102
  const manuallyAttachedArtifacts = services.artifacts.get(fullTestTitle);
88
103
  const testMeta = services.keyValues.get(fullTestTitle);
89
- const rid = test.id || test.testId || uuidv4();
90
-
91
- /**
92
- * @type {{
93
- * browser?: string,
94
- * dependencies: string[],
95
- * isMobile?: boolean
96
- * metadata: Record<string, any>,
97
- * name: string,
98
- * }}
99
- */
100
- const project = {
101
- browser: test.parent.project().use.defaultBrowserType,
102
- dependencies: test.parent.project().dependencies,
103
- isMobile: test.parent.project().use.isMobile,
104
- metadata: test.parent.project().metadata,
105
- name: test.parent.project().name,
106
- };
107
104
 
108
105
  let status = result.status;
109
106
  // process test.fail() annotation
@@ -179,7 +176,7 @@ class PlaywrightReporter {
179
176
  await Promise.all(reportTestPromises);
180
177
 
181
178
  if (this.uploads.length) {
182
- if (this.client.uploader.isEnabled) console.log(APP_PREFIX, `🎞️ Uploading ${this.uploads.length} files...`);
179
+ if (this.client.uploader.isEnabled) log.info(`🎞️ Uploading ${this.uploads.length} files...`);
183
180
 
184
181
  const promises = [];
185
182
 
@@ -242,6 +239,44 @@ function appendStep(step, shift = 0) {
242
239
  newCategory = 'framework';
243
240
  }
244
241
 
242
+ const resultStep = formatStep({
243
+ category: newCategory,
244
+ title: step.title,
245
+ duration: step.duration,
246
+ });
247
+
248
+ // Add status based on error
249
+ addStatusToStep(resultStep, step.error ? 'failed' : 'passed', step.error);
250
+
251
+ // Add error if present
252
+ if (step.error !== undefined) {
253
+ if (typeof step.error === 'object') {
254
+ resultStep.error = {
255
+ message: truncate(String(step.error.message), 250),
256
+ stack: truncate(String(step.error.stack || ''), 250),
257
+ };
258
+ } else {
259
+ resultStep.error = truncate(String(step.error), 250);
260
+ }
261
+ }
262
+
263
+ // Add log if present
264
+ if (step.log) {
265
+ resultStep.log = truncate(String(step.log), 250);
266
+ }
267
+
268
+ // Add artifacts from attachments
269
+ if (step.attachments && step.attachments.length > 0 && SCREENSHOTS_ON_STEPS) {
270
+ const screenshotAttachment = step.attachments.find(att =>
271
+ att.contentType === 'image/png' && att.name === 'screenshot'
272
+ );
273
+ if (screenshotAttachment && screenshotAttachment.path) {
274
+ const artifacts = { screenshot: screenshotAttachment.path };
275
+ addArtifactsToStep(resultStep, artifacts);
276
+ }
277
+ }
278
+
279
+ // Process nested steps
245
280
  const formattedSteps = [];
246
281
  for (const child of step.steps || []) {
247
282
  const appendedChild = appendStep(child, shift + 2);
@@ -250,20 +285,10 @@ function appendStep(step, shift = 0) {
250
285
  }
251
286
  }
252
287
 
253
- const resultStep = {
254
- category: newCategory,
255
- title: step.title,
256
- duration: step.duration,
257
- };
258
-
259
288
  if (formattedSteps.length) {
260
289
  resultStep.steps = formattedSteps.filter(s => !!s);
261
290
  }
262
291
 
263
- if (step.error !== undefined) {
264
- resultStep.error = step.error;
265
- }
266
-
267
292
  return resultStep;
268
293
  }
269
294
 
@@ -0,0 +1,232 @@
1
+ import { truncate } from '../../utils/utils.js';
2
+ import path from 'path';
3
+ import crypto from 'crypto';
4
+ import fs from 'fs';
5
+
6
+ /**
7
+ * Generates a short unique filename from screenshot path
8
+ * If original filename is too long, uses hash-based name
9
+ *
10
+ * @param {string} screenshotPath - Path to screenshot file
11
+ * @returns {string} Short filename (max 80 chars)
12
+ */
13
+ export function generateShortFilename(screenshotPath) {
14
+ const originalFilename = path.basename(screenshotPath);
15
+ const stepPrefix = originalFilename.match(/^(\d{3,4}_)/)?.[1] || '';
16
+
17
+ if (originalFilename.length < 40) {
18
+ return originalFilename;
19
+ }
20
+
21
+ const ext = path.extname(screenshotPath);
22
+
23
+ const hash = crypto
24
+ .createHash('sha256')
25
+ .update(screenshotPath)
26
+ .digest('hex')
27
+ .slice(0, 16);
28
+
29
+ return `${stepPrefix}screenshot_${hash}${ext}`;
30
+ }
31
+
32
+ /**
33
+ * Formats a step object according to Testomat.io Step Schema
34
+ *
35
+ * This function transforms a raw step object from test frameworks (CodeceptJS, Playwright, etc.)
36
+ * into a standardized format compatible with Testomat.io API. It ensures all text fields are
37
+ * truncated to 250 characters as defined in testomat-api-definition.yml.
38
+ *
39
+ * Processed fields:
40
+ * - category: step type (framework, user, hook) - defaults to 'user'
41
+ * - title: step name/description, truncated to 250 chars
42
+ * - duration: step execution time in seconds
43
+ * - log: optional log output, truncated to 250 chars
44
+ * - artifacts: optional array of artifact URLs (screenshots), each truncated to 250 chars
45
+ * - error: error details (message + stack) if step failed, each truncated to 250 chars
46
+ * - steps: recursively formats nested steps
47
+ *
48
+ * Schema reference: testomat-api-definition.yml (Step object)
49
+ *
50
+ * @param {Object} step - Raw step object from test framework
51
+ * @param {string} [step.category] - Step category: 'user', 'framework', or 'hook'
52
+ * @param {string} [step.title] - Step title/name
53
+ * @param {number} [step.duration] - Step duration in seconds
54
+ * @param {string} [step.log] - Log output for this step
55
+ * @param {string[]} [step.artifacts] - Array of artifact URLs (screenshots)
56
+ * @param {string|Object} [step.error] - Error details - can be string or object with message/stack
57
+ * @param {Object[]} [step.steps] - Array of nested child steps
58
+ * @returns {Object} Formatted step object matching Testomat.io Step Schema with:
59
+ * category, title, duration, and optional log, artifacts, error, and steps fields
60
+ *
61
+ * @example
62
+ * const rawStep = {
63
+ * category: 'user',
64
+ * title: 'I click on button',
65
+ * duration: 1.5,
66
+ * error: { message: 'Element not found', stack: 'at test.js:10:5' }
67
+ * };
68
+ * const formatted = formatStep(rawStep);
69
+ * // Returns: { category: 'user', title: 'I click on button', duration: 1.5, error: {...} }
70
+ */
71
+ export function formatStep(step) {
72
+ const formattedStep = {
73
+ category: step.category || 'user',
74
+ title: truncate(String(step.title || ''), 250),
75
+ duration: step.duration || 0,
76
+ };
77
+
78
+ if (step.log) {
79
+ formattedStep.log = truncate(String(step.log), 250);
80
+ }
81
+
82
+ if (step.artifacts && Array.isArray(step.artifacts)) {
83
+ formattedStep.artifacts = step.artifacts.map(artifact => truncate(String(artifact), 250));
84
+ }
85
+
86
+ if (step.error) {
87
+ if (typeof step.error === 'object') {
88
+ formattedStep.error = {
89
+ message: truncate(String(step.error.message || 'Step failed'), 250),
90
+ stack: truncate(String(step.error.stack || ''), 250),
91
+ };
92
+ } else {
93
+ formattedStep.error = truncate(String(step.error), 250);
94
+ }
95
+ }
96
+
97
+ if (step.steps && Array.isArray(step.steps)) {
98
+ formattedStep.steps = step.steps.map(s => formatStep(s));
99
+ }
100
+
101
+ return formattedStep;
102
+ }
103
+
104
+ /**
105
+ * Adds status field to step
106
+ *
107
+ * Normalizes step status from test frameworks to Testomat.io standard format.
108
+ * Maps framework-specific statuses ('success', 'failed', 'passed') to Testomat.io
109
+ * standard values ('passed', 'failed').
110
+ *
111
+ * Status mapping:
112
+ * - 'success' β†’ 'passed'
113
+ * - 'passed' β†’ 'passed'
114
+ * - 'failed' β†’ 'failed'
115
+ * - Any other value β†’ 'passed' (default)
116
+ *
117
+ * If step already has a status, it won't be overwritten. If error is provided
118
+ * and step doesn't have status, it will be set to 'failed'.
119
+ *
120
+ * Schema reference: testomat-api-definition.yml (Step.status enum)
121
+ *
122
+ * @param {Object} step - Step object to add status to (modified in place)
123
+ * @param {string} [step.status] - Existing status (won't be overwritten if present)
124
+ * @param {string} status - Status from test framework: 'success', 'failed', or 'passed'
125
+ * @param {Error|Object|null} err - Error object if step failed
126
+ * @returns {Object} The same step object with added status field
127
+ *
128
+ * @example
129
+ * const step = { title: 'Click button' };
130
+ * addStatusToStep(step, 'success', null);
131
+ * // step.status === 'passed'
132
+ *
133
+ * @example
134
+ * const step2 = { title: 'Find element' };
135
+ * addStatusToStep(step2, 'failed', new Error('Not found'));
136
+ * // step2.status === 'failed'
137
+ */
138
+ export function addStatusToStep(step, status, err) {
139
+ if (step.status) return step;
140
+
141
+ const statusMap = {
142
+ 'success': 'passed',
143
+ 'failed': 'failed',
144
+ 'passed': 'passed',
145
+ };
146
+
147
+ step.status = statusMap[status] || 'passed';
148
+
149
+ if (err && !step.status) {
150
+ step.status = 'failed';
151
+ }
152
+
153
+ return step;
154
+ }
155
+
156
+ /**
157
+ * Adds screenshot to step as artifacts array
158
+ *
159
+ * Extracts screenshot path from artifacts and adds it to the step's artifacts array.
160
+ * The actual upload will happen in the client's addTestRun method.
161
+ *
162
+ * Artifact format supports:
163
+ * - Array format: [{ screenshot: '/path/to/screenshot.png' }]
164
+ * - Object format: { screenshot: '/path/to/screenshot.png' }
165
+ *
166
+ * Screenshot path can be specified as:
167
+ * - Object with path property: { screenshot: { path: '/path/to/file.png' } }
168
+ * - Object with screenshot property: { screenshot: { screenshot: '/path/to/file.png' } }
169
+ * - Direct string path: { screenshot: '/path/to/file.png' }
170
+ *
171
+ * @param {Object} step - Step object to add artifacts to (modified in place)
172
+ * @param {string[]} [step.artifacts] - Existing artifacts array (won't be overwritten if present)
173
+ * @param {Object|Object[]|null} artifacts - Artifacts from test framework
174
+ * @returns {Object} The same step object with artifacts array added
175
+ *
176
+ * @example
177
+ * const step = { title: 'Click button' };
178
+ * const artifacts = { screenshot: '/tmp/screenshot.png' };
179
+ * addArtifactsToStep(step, artifacts);
180
+ * // step.artifacts === ['/tmp/screenshot.png']
181
+ */
182
+ export function addArtifactsToStep(step, artifacts) {
183
+ if (!artifacts) return step;
184
+
185
+ let screenshotPath = null;
186
+
187
+ if (Array.isArray(artifacts)) {
188
+ const screenshotArtifact = artifacts.find(a => a.screenshot);
189
+ if (screenshotArtifact && screenshotArtifact.path) {
190
+ screenshotPath = screenshotArtifact.path;
191
+ } else if (screenshotArtifact && screenshotArtifact.screenshot) {
192
+ screenshotPath = screenshotArtifact.screenshot;
193
+ }
194
+ } else if (artifacts.screenshot) {
195
+ screenshotPath = artifacts.screenshot;
196
+ }
197
+
198
+ if (screenshotPath && fs.existsSync(screenshotPath)) {
199
+ const truncatedPath = truncate(String(screenshotPath), 250);
200
+ if (step.artifacts && Array.isArray(step.artifacts)) {
201
+ step.artifacts.push(truncatedPath);
202
+ } else {
203
+ step.artifacts = [truncatedPath];
204
+ }
205
+ }
206
+
207
+ return step;
208
+ }
209
+
210
+ /**
211
+ * Appends one artifact path to a step.
212
+ *
213
+ * Unlike addArtifactsToStep, this helper accepts a direct path (or URL-like string)
214
+ * and does not check file existence, so callers can attach fallback artifacts
215
+ * collected from logs or async trace outputs.
216
+ *
217
+ * @param {Object} step - Step object to update (modified in place)
218
+ * @param {string} artifactPath - Artifact path to append
219
+ * @returns {Object} The same step object with updated artifacts
220
+ */
221
+ export function addArtifactPathToStep(step, artifactPath) {
222
+ if (!step || !artifactPath) return step;
223
+
224
+ const truncatedPath = truncate(String(artifactPath), 250);
225
+ if (step.artifacts && Array.isArray(step.artifacts)) {
226
+ if (!step.artifacts.includes(truncatedPath)) step.artifacts.push(truncatedPath);
227
+ } else {
228
+ step.artifacts = [truncatedPath];
229
+ }
230
+
231
+ return step;
232
+ }
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