@testomatio/reporter 2.8.5-beta.1-remote → 2.8.5-beta.2-yarn

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.
@@ -422,11 +422,16 @@ function createHookSection(hookName, steps) {
422
422
  function formatHookName(hookName) {
423
423
  return hookName.replace(/Hook$/, '');
424
424
  }
425
+ function getCodeceptStepCategory(origin = 'test') {
426
+ if (origin === 'hook')
427
+ return 'hook';
428
+ return 'user';
429
+ }
425
430
  // Format CodeceptJS step using its built-in methods
426
431
  function formatCodeceptStep(step, screenshotOnFailPath = null) {
427
432
  if (!step)
428
433
  return null;
429
- const category = step.constructor.name === 'HelperStep' ? 'framework' : 'user';
434
+ const category = getCodeceptStepCategory('test');
430
435
  const title = (0, utils_js_1.truncate)(String(step));
431
436
  const duration = step.duration || 0;
432
437
  const formattedStep = (0, step_formatter_js_1.formatStep)({
@@ -471,7 +476,7 @@ function formatHookStep(step) {
471
476
  }
472
477
  title = (0, utils_js_1.truncate)(title);
473
478
  const formattedStep = (0, step_formatter_js_1.formatStep)({
474
- category: 'hook',
479
+ category: getCodeceptStepCategory('hook'),
475
480
  title,
476
481
  duration: step.duration || 0,
477
482
  });
package/lib/bin/cli.js CHANGED
@@ -34,9 +34,9 @@ program
34
34
  else {
35
35
  dotenv_1.default.config();
36
36
  }
37
- // --filter-list produces a machine-readable test list on stdout, so route
38
- // remaining output to stderr, skip the banner, and silence info-level
39
- // logs so the terminal isn't flooded with progress noise.
37
+ // --format / --filter-list produce machine-readable output on stdout, so route
38
+ // remaining output to stderr, skip the banner, and silence info-level logs so
39
+ // stdout stays clean for capture (e.g. RUN_ID=$(... start --format id)).
40
40
  // Set TESTOMATIO_LOG_LEVEL=INFO to re-enable progress logs for debugging.
41
41
  const subOpts = actionCommand.opts();
42
42
  if (subOpts.filterList || subOpts.format) {
@@ -51,19 +51,37 @@ program
51
51
  .command('start')
52
52
  .description('Start a new run and return its ID')
53
53
  .option('--kind <type>', 'Specify run type: automated, manual, or mixed')
54
+ .option('--filter <filter>', 'Scope the prepared run to tests matching the filter (no execution)')
55
+ .option('--format <format>', 'Machine-readable output: print only the run id to stdout (e.g. --format id)')
54
56
  .action(async (opts) => {
55
57
  (0, utils_js_1.cleanLatestRunId)();
56
- console.log('Starting a new Run on Testomat.io...');
58
+ log_js_1.log.info('Starting a new Run on Testomat.io...');
57
59
  const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || config_js_1.config.TESTOMATIO;
58
60
  const client = new client_js_1.default({ apiKey });
59
61
  const createRunParams = {};
60
- if (opts.kind) {
62
+ if (opts.kind)
61
63
  createRunParams.kind = opts.kind;
64
+ if (opts.filter) {
65
+ const [pipe, ...optsArray] = opts.filter.split(':');
66
+ const tests = await client.prepareRun({ pipe, pipeOptions: optsArray.join(':') });
67
+ if (!tests || tests.length === 0) {
68
+ log_js_1.log.warn(picocolors_1.default.yellow('No tests found for the filter. Run not created.'));
69
+ process.exit(1);
70
+ }
71
+ createRunParams.configuration = {
72
+ tests: tests.filter(id => id.startsWith('T')).map(id => id.slice(1)),
73
+ suites: tests.filter(id => id.startsWith('S')).map(id => id.slice(1)),
74
+ };
62
75
  }
63
- client.createRun(createRunParams).then(() => {
64
- console.log(process.env.runId);
65
- process.exit(0);
66
- });
76
+ await client.createRun(createRunParams);
77
+ const runId = client.pipeStore.runId || process.env.runId;
78
+ if (!runId) {
79
+ log_js_1.log.error(picocolors_1.default.red('Failed to create run on Testomat.io.'));
80
+ process.exit(1);
81
+ }
82
+ // stdout carries ONLY the run id so it can be captured: RUN_ID=$(reporter start)
83
+ console.log(runId);
84
+ process.exit(0);
67
85
  });
68
86
  program
69
87
  .command('finish')
@@ -167,12 +185,17 @@ program
167
185
  await client.createRun(createRunParams);
168
186
  }
169
187
  catch (err) {
170
- log_js_1.log.warn(picocolors_1.default.red(`CI launch failed: ${err.message || err}`));
188
+ log_js_1.log.error(picocolors_1.default.red(`CI launch failed: ${err.message || err}`));
189
+ process.exit(1);
190
+ }
191
+ // createRun swallows pipe-level errors, so a resolved promise is not proof
192
+ // the launch succeeded — the pipe only records runUrl on a real 2xx response.
193
+ if (!client.pipeStore.runUrl) {
194
+ log_js_1.log.error(picocolors_1.default.red('CI launch failed — no run was created (see the error above).'));
171
195
  process.exit(1);
172
196
  }
173
197
  log_js_1.log.info(`🚀 CI build triggered on profile ${picocolors_1.default.cyan(opts.remote)}`);
174
- if (client.pipeStore.runUrl)
175
- log_js_1.log.info(`📊 Report URL: ${picocolors_1.default.magenta(client.pipeStore.runUrl)}`);
198
+ log_js_1.log.info(`📊 Report URL: ${picocolors_1.default.magenta(client.pipeStore.runUrl)}`);
176
199
  return process.exit(0);
177
200
  }
178
201
  // just create a run (wich tests which match filters) without executing tests
package/lib/client.js CHANGED
@@ -92,8 +92,6 @@ class Client {
92
92
  // Run only the selected pipe
93
93
  const rawResult = await p.prepareRun(pipeOptions);
94
94
  const result = Array.isArray(rawResult) ? rawResult : [];
95
- // Expose resolved test ids to other pipes (e.g. TestomatioPipe builds `ci.grep` from this).
96
- this.pipeStore.preparedTestIds = result;
97
95
  debug('Execution tests list', result);
98
96
  return result;
99
97
  }
@@ -147,6 +147,7 @@ class CoveragePipe {
147
147
  }
148
148
  this.results = [...this.tests, ...this.suiteIds];
149
149
  if (this.store) {
150
+ this.store.preparedTestIds = this.results;
150
151
  this.store.coverageConfiguration = {
151
152
  tests: [...this.tests],
152
153
  suites: [...this.suiteIds],
@@ -181,6 +181,8 @@ class TestomatioPipe {
181
181
  });
182
182
  if (Array.isArray(resp.data?.tests) && resp.data?.tests?.length > 0) {
183
183
  (0, utils_js_1.foundedTestLog)(constants_js_1.APP_PREFIX, resp.data.tests);
184
+ if (this.store)
185
+ this.store.preparedTestIds = resp.data.tests;
184
186
  return resp.data.tests;
185
187
  }
186
188
  log_js_1.log.warn(`⛔ No tests found for your --filter --> ${type}=${id}`);
@@ -241,14 +243,21 @@ class TestomatioPipe {
241
243
  }
242
244
  // Assemble the `ci` block when the user asked for a remote CI launch via
243
245
  // TESTOMATIO_CI_PROFILE (e.g. `--remote github`). Grep is taken from whatever
244
- // `--filter` resolution already stashed in the shared pipeStore.
245
- /** @type {{profile: string, grep?: string, override?: Record<string, any>}|null} */
246
+ // `--filter` resolution already stashed in the shared pipeStore. When launching
247
+ // an already-prepared run (TESTOMATIO_RUN set) with no fresh filter, ask the
248
+ // server to grep that run's own stored scope via `{ type: 'run', id }`.
249
+ /** @type {{profile: string, grep?: string, type?: string, id?: string, override?: Record<string, any>}|null} */
246
250
  let ci = null;
247
251
  if (this.ciProfile) {
248
252
  ci = { profile: this.ciProfile };
249
253
  const grepIds = this.store?.preparedTestIds;
250
- if (grepIds?.length)
254
+ if (grepIds?.length) {
251
255
  ci.grep = grepIds.join('|');
256
+ }
257
+ else if (this.runId) {
258
+ ci.type = 'run';
259
+ ci.id = this.runId;
260
+ }
252
261
  if (this.ciParams)
253
262
  ci.override = this.ciParams;
254
263
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/reporter",
3
- "version": "2.8.5-beta.1-remote",
3
+ "version": "2.8.5-beta.2-yarn",
4
4
  "description": "Testomatio Reporter Client",
5
5
  "engines": {
6
6
  "node": ">=18"
@@ -86,7 +86,7 @@
86
86
  "build:watch:bun": "rm -rf ./cjs && bun build ./src/bin/reportXml.js ./src/bin/startTest.js ./src/bin/uploadArtifacts.js --outdir ./cjs --target node --watch --onSuccess \"build/scripts/post-build.js\""
87
87
  },
88
88
  "devDependencies": {
89
- "@playwright/test": "^1.49.1",
89
+ "@playwright/test": "^1.60.0",
90
90
  "@redocly/cli": "^1.0.0-beta.125",
91
91
  "@types/cross-spawn": "^6.0.6",
92
92
  "@types/cucumber": "^7.0.0",
@@ -490,11 +490,16 @@ function formatHookName(hookName) {
490
490
  return hookName.replace(/Hook$/, '');
491
491
  }
492
492
 
493
+ function getCodeceptStepCategory(origin = 'test') {
494
+ if (origin === 'hook') return 'hook';
495
+ return 'user';
496
+ }
497
+
493
498
  // Format CodeceptJS step using its built-in methods
494
499
  function formatCodeceptStep(step, screenshotOnFailPath = null) {
495
500
  if (!step) return null;
496
501
 
497
- const category = step.constructor.name === 'HelperStep' ? 'framework' : 'user';
502
+ const category = getCodeceptStepCategory('test');
498
503
  const title = truncate(String(step));
499
504
  const duration = step.duration || 0;
500
505
 
@@ -548,7 +553,7 @@ function formatHookStep(step) {
548
553
  title = truncate(title);
549
554
 
550
555
  const formattedStep = formatStep({
551
- category: 'hook',
556
+ category: getCodeceptStepCategory('hook'),
552
557
  title,
553
558
  duration: step.duration || 0,
554
559
  });
package/src/bin/cli.js CHANGED
@@ -32,9 +32,9 @@ program
32
32
  dotenv.config();
33
33
  }
34
34
 
35
- // --filter-list produces a machine-readable test list on stdout, so route
36
- // remaining output to stderr, skip the banner, and silence info-level
37
- // logs so the terminal isn't flooded with progress noise.
35
+ // --format / --filter-list produce machine-readable output on stdout, so route
36
+ // remaining output to stderr, skip the banner, and silence info-level logs so
37
+ // stdout stays clean for capture (e.g. RUN_ID=$(... start --format id)).
38
38
  // Set TESTOMATIO_LOG_LEVEL=INFO to re-enable progress logs for debugging.
39
39
  const subOpts = actionCommand.opts();
40
40
  if (subOpts.filterList || subOpts.format) {
@@ -49,22 +49,42 @@ program
49
49
  .command('start')
50
50
  .description('Start a new run and return its ID')
51
51
  .option('--kind <type>', 'Specify run type: automated, manual, or mixed')
52
+ .option('--filter <filter>', 'Scope the prepared run to tests matching the filter (no execution)')
53
+ .option('--format <format>', 'Machine-readable output: print only the run id to stdout (e.g. --format id)')
52
54
  .action(async opts => {
53
55
  cleanLatestRunId();
54
56
 
55
- console.log('Starting a new Run on Testomat.io...');
57
+ log.info('Starting a new Run on Testomat.io...');
56
58
  const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || config.TESTOMATIO;
57
59
  const client = new TestomatClient({ apiKey });
58
60
 
59
61
  const createRunParams = {};
60
- if (opts.kind) {
61
- createRunParams.kind = opts.kind;
62
+ if (opts.kind) createRunParams.kind = opts.kind;
63
+
64
+ if (opts.filter) {
65
+ const [pipe, ...optsArray] = opts.filter.split(':');
66
+ const tests = await client.prepareRun({ pipe, pipeOptions: optsArray.join(':') });
67
+ if (!tests || tests.length === 0) {
68
+ log.warn(pc.yellow('No tests found for the filter. Run not created.'));
69
+ process.exit(1);
70
+ }
71
+ createRunParams.configuration = {
72
+ tests: tests.filter(id => id.startsWith('T')).map(id => id.slice(1)),
73
+ suites: tests.filter(id => id.startsWith('S')).map(id => id.slice(1)),
74
+ };
62
75
  }
63
76
 
64
- client.createRun(createRunParams).then(() => {
65
- console.log(process.env.runId);
66
- process.exit(0);
67
- });
77
+ await client.createRun(createRunParams);
78
+
79
+ const runId = client.pipeStore.runId || process.env.runId;
80
+ if (!runId) {
81
+ log.error(pc.red('Failed to create run on Testomat.io.'));
82
+ process.exit(1);
83
+ }
84
+
85
+ // stdout carries ONLY the run id so it can be captured: RUN_ID=$(reporter start)
86
+ console.log(runId);
87
+ process.exit(0);
68
88
  });
69
89
 
70
90
  program
@@ -182,12 +202,19 @@ program
182
202
  try {
183
203
  await client.createRun(createRunParams);
184
204
  } catch (err) {
185
- log.warn(pc.red(`CI launch failed: ${err.message || err}`));
205
+ log.error(pc.red(`CI launch failed: ${err.message || err}`));
206
+ process.exit(1);
207
+ }
208
+
209
+ // createRun swallows pipe-level errors, so a resolved promise is not proof
210
+ // the launch succeeded — the pipe only records runUrl on a real 2xx response.
211
+ if (!client.pipeStore.runUrl) {
212
+ log.error(pc.red('CI launch failed — no run was created (see the error above).'));
186
213
  process.exit(1);
187
214
  }
188
215
 
189
216
  log.info(`🚀 CI build triggered on profile ${pc.cyan(opts.remote)}`);
190
- if (client.pipeStore.runUrl) log.info(`📊 Report URL: ${pc.magenta(client.pipeStore.runUrl)}`);
217
+ log.info(`📊 Report URL: ${pc.magenta(client.pipeStore.runUrl)}`);
191
218
  return process.exit(0);
192
219
  }
193
220
 
package/src/client.js CHANGED
@@ -102,9 +102,6 @@ class Client {
102
102
  const rawResult = await p.prepareRun(pipeOptions);
103
103
  const result = Array.isArray(rawResult) ? rawResult : [];
104
104
 
105
- // Expose resolved test ids to other pipes (e.g. TestomatioPipe builds `ci.grep` from this).
106
- this.pipeStore.preparedTestIds = result;
107
-
108
105
  debug('Execution tests list', result);
109
106
 
110
107
  return result;
@@ -169,6 +169,7 @@ class CoveragePipe { // or Changes for the future???
169
169
 
170
170
  this.results = [...this.tests, ...this.suiteIds];
171
171
  if (this.store) {
172
+ this.store.preparedTestIds = this.results;
172
173
  this.store.coverageConfiguration = {
173
174
  tests: [...this.tests],
174
175
  suites: [...this.suiteIds],
@@ -210,6 +210,7 @@ class TestomatioPipe {
210
210
 
211
211
  if (Array.isArray(resp.data?.tests) && resp.data?.tests?.length > 0) {
212
212
  foundedTestLog(APP_PREFIX, resp.data.tests);
213
+ if (this.store) this.store.preparedTestIds = resp.data.tests;
213
214
  return resp.data.tests;
214
215
  }
215
216
 
@@ -277,13 +278,20 @@ class TestomatioPipe {
277
278
 
278
279
  // Assemble the `ci` block when the user asked for a remote CI launch via
279
280
  // TESTOMATIO_CI_PROFILE (e.g. `--remote github`). Grep is taken from whatever
280
- // `--filter` resolution already stashed in the shared pipeStore.
281
- /** @type {{profile: string, grep?: string, override?: Record<string, any>}|null} */
281
+ // `--filter` resolution already stashed in the shared pipeStore. When launching
282
+ // an already-prepared run (TESTOMATIO_RUN set) with no fresh filter, ask the
283
+ // server to grep that run's own stored scope via `{ type: 'run', id }`.
284
+ /** @type {{profile: string, grep?: string, type?: string, id?: string, override?: Record<string, any>}|null} */
282
285
  let ci = null;
283
286
  if (this.ciProfile) {
284
287
  ci = { profile: this.ciProfile };
285
288
  const grepIds = this.store?.preparedTestIds;
286
- if (grepIds?.length) ci.grep = grepIds.join('|');
289
+ if (grepIds?.length) {
290
+ ci.grep = grepIds.join('|');
291
+ } else if (this.runId) {
292
+ ci.type = 'run';
293
+ ci.id = this.runId;
294
+ }
287
295
  if (this.ciParams) ci.override = this.ciParams;
288
296
  }
289
297
  const runParams = Object.fromEntries(