@testomatio/reporter 2.8.4 → 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')
@@ -91,7 +109,19 @@ program
91
109
  .option('--filter-list <filter>', 'Get a list of all tests by filter before running')
92
110
  .option('--format <format>', 'Machine-readable output format for --filter-list (grep, json, newline, ids)')
93
111
  .option('--kind <type>', 'Specify run type: automated, manual, or mixed')
112
+ .option('--remote <profile>', 'Trigger run on the named Testomat.io CI profile instead of executing locally')
113
+ .option('--remote-param <kv>', 'key=value pair forwarded to the CI profile config (repeat for multiple)', (value, prev) => prev.concat([value]), [])
94
114
  .action(async (command, opts) => {
115
+ if (opts.remote) {
116
+ if (opts.filterList) {
117
+ log_js_1.log.warn(picocolors_1.default.red('⚠️ --filter-list cannot be combined with --remote'));
118
+ process.exit(1);
119
+ }
120
+ process.env.TESTOMATIO_CI_PROFILE = opts.remote;
121
+ if (opts.remoteParam?.length) {
122
+ process.env.TESTOMATIO_CI_PARAMS = opts.remoteParam.join(',');
123
+ }
124
+ }
95
125
  const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || config_js_1.config.TESTOMATIO;
96
126
  const title = process.env.TESTOMATIO_TITLE;
97
127
  const client = new client_js_1.default({ apiKey, title });
@@ -127,7 +157,7 @@ program
127
157
  }
128
158
  return;
129
159
  }
130
- if (command && command.split) {
160
+ if (command && command.split && !opts.remote) {
131
161
  command = (0, utils_js_1.applyFilter)(command, tests);
132
162
  }
133
163
  }
@@ -138,6 +168,36 @@ program
138
168
  return;
139
169
  }
140
170
  }
171
+ if (opts.remote) {
172
+ if (!apiKey) {
173
+ log_js_1.log.warn(picocolors_1.default.red('⚠️ TESTOMATIO API key required for --remote'));
174
+ process.exit(1);
175
+ }
176
+ if (command) {
177
+ log_js_1.log.warn(picocolors_1.default.yellow('Note: positional command is ignored when --remote is set; CI runs the workflow.'));
178
+ }
179
+ const createRunParams = {};
180
+ if (title)
181
+ createRunParams.title = title;
182
+ if (opts.kind)
183
+ createRunParams.kind = opts.kind;
184
+ try {
185
+ await client.createRun(createRunParams);
186
+ }
187
+ catch (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).'));
195
+ process.exit(1);
196
+ }
197
+ log_js_1.log.info(`🚀 CI build triggered on profile ${picocolors_1.default.cyan(opts.remote)}`);
198
+ log_js_1.log.info(`📊 Report URL: ${picocolors_1.default.magenta(client.pipeStore.runUrl)}`);
199
+ return process.exit(0);
200
+ }
141
201
  // just create a run (wich tests which match filters) without executing tests
142
202
  if (!command || !command.split) {
143
203
  const createRunParams = {};
@@ -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],
@@ -31,6 +31,8 @@ declare class TestomatioPipe implements Pipe {
31
31
  env: string;
32
32
  label: string;
33
33
  description: any;
34
+ ciProfile: string;
35
+ ciParams: Record<string, string>;
34
36
  client: Gaxios;
35
37
  proceed: boolean;
36
38
  jiraId: string;
@@ -15,6 +15,26 @@ const log_js_1 = require("../utils/log.js");
15
15
  const debug = (0, debug_1.default)('@testomatio/reporter:pipe:testomatio');
16
16
  if (process.env.TESTOMATIO_RUN)
17
17
  process.env.runId = process.env.TESTOMATIO_RUN;
18
+ /**
19
+ * Parse `TESTOMATIO_CI_PARAMS` (comma-separated `key=value` pairs) into an object.
20
+ * Entries without `=` or with empty keys are skipped. Returns undefined when input is empty.
21
+ *
22
+ * @param {string|undefined} raw
23
+ * @returns {Record<string, string>|undefined}
24
+ */
25
+ function parseCiParams(raw) {
26
+ if (!raw)
27
+ return undefined;
28
+ /** @type {Record<string, string>} */
29
+ const result = {};
30
+ for (const entry of raw.split(',')) {
31
+ const idx = entry.indexOf('=');
32
+ if (idx <= 0)
33
+ continue;
34
+ result[entry.slice(0, idx).trim()] = entry.slice(idx + 1);
35
+ }
36
+ return Object.keys(result).length ? result : undefined;
37
+ }
18
38
  /**
19
39
  * @typedef {import('../../types/types.js').Pipe} Pipe
20
40
  * @typedef {import('../../types/types.js').TestData} TestData
@@ -68,6 +88,12 @@ class TestomatioPipe {
68
88
  this.env = process.env.TESTOMATIO_ENV;
69
89
  this.label = process.env.TESTOMATIO_LABEL;
70
90
  this.description = params.description || process.env.TESTOMATIO_DESCRIPTION;
91
+ // Remote CI launch — when `TESTOMATIO_CI_PROFILE` is set the run will be created
92
+ // on the server *and* the named CI profile will be dispatched. Optional
93
+ // `TESTOMATIO_CI_PARAMS` is a comma-separated list of `key=value` pairs
94
+ // forwarded to the CI profile config (e.g. `branch=develop,REGION=eu`).
95
+ this.ciProfile = process.env.TESTOMATIO_CI_PROFILE;
96
+ this.ciParams = parseCiParams(process.env.TESTOMATIO_CI_PARAMS);
71
97
  // Create a new instance of gaxios with a custom config
72
98
  this.client = new gaxios_1.Gaxios({
73
99
  baseURL: `${this.url.trim()}`,
@@ -155,6 +181,8 @@ class TestomatioPipe {
155
181
  });
156
182
  if (Array.isArray(resp.data?.tests) && resp.data?.tests?.length > 0) {
157
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;
158
186
  return resp.data.tests;
159
187
  }
160
188
  log_js_1.log.warn(`⛔ No tests found for your --filter --> ${type}=${id}`);
@@ -213,6 +241,26 @@ class TestomatioPipe {
213
241
  this.store.configuration = { ...(this.store.configuration || {}), ...params.configuration };
214
242
  }
215
243
  }
244
+ // Assemble the `ci` block when the user asked for a remote CI launch via
245
+ // TESTOMATIO_CI_PROFILE (e.g. `--remote github`). Grep is taken from whatever
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} */
250
+ let ci = null;
251
+ if (this.ciProfile) {
252
+ ci = { profile: this.ciProfile };
253
+ const grepIds = this.store?.preparedTestIds;
254
+ if (grepIds?.length) {
255
+ ci.grep = grepIds.join('|');
256
+ }
257
+ else if (this.runId) {
258
+ ci.type = 'run';
259
+ ci.id = this.runId;
260
+ }
261
+ if (this.ciParams)
262
+ ci.override = this.ciParams;
263
+ }
216
264
  const runParams = Object.fromEntries(Object.entries({
217
265
  ci_build_url: buildUrl,
218
266
  api_key: this.apiKey.trim(),
@@ -227,6 +275,7 @@ class TestomatioPipe {
227
275
  kind: params.kind,
228
276
  configuration,
229
277
  description,
278
+ ci,
230
279
  }).filter(([, value]) => !!value));
231
280
  debug(' >>>>>> Run params', JSON.stringify(runParams, null, 2));
232
281
  if (this.runId) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/reporter",
3
- "version": "2.8.4",
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
@@ -97,7 +117,25 @@ program
97
117
  .option('--filter-list <filter>', 'Get a list of all tests by filter before running')
98
118
  .option('--format <format>', 'Machine-readable output format for --filter-list (grep, json, newline, ids)')
99
119
  .option('--kind <type>', 'Specify run type: automated, manual, or mixed')
120
+ .option('--remote <profile>', 'Trigger run on the named Testomat.io CI profile instead of executing locally')
121
+ .option(
122
+ '--remote-param <kv>',
123
+ 'key=value pair forwarded to the CI profile config (repeat for multiple)',
124
+ (value, prev) => prev.concat([value]),
125
+ [],
126
+ )
100
127
  .action(async (command, opts) => {
128
+ if (opts.remote) {
129
+ if (opts.filterList) {
130
+ log.warn(pc.red('⚠️ --filter-list cannot be combined with --remote'));
131
+ process.exit(1);
132
+ }
133
+ process.env.TESTOMATIO_CI_PROFILE = opts.remote;
134
+ if (opts.remoteParam?.length) {
135
+ process.env.TESTOMATIO_CI_PARAMS = opts.remoteParam.join(',');
136
+ }
137
+ }
138
+
101
139
  const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || config.TESTOMATIO;
102
140
  const title = process.env.TESTOMATIO_TITLE;
103
141
  const client = new TestomatClient({ apiKey, title });
@@ -137,7 +175,7 @@ program
137
175
  return;
138
176
  }
139
177
 
140
- if (command && command.split) {
178
+ if (command && command.split && !opts.remote) {
141
179
  command = applyFilter(command, tests);
142
180
  }
143
181
  }
@@ -148,6 +186,38 @@ program
148
186
  }
149
187
  }
150
188
 
189
+ if (opts.remote) {
190
+ if (!apiKey) {
191
+ log.warn(pc.red('⚠️ TESTOMATIO API key required for --remote'));
192
+ process.exit(1);
193
+ }
194
+ if (command) {
195
+ log.warn(pc.yellow('Note: positional command is ignored when --remote is set; CI runs the workflow.'));
196
+ }
197
+
198
+ const createRunParams = {};
199
+ if (title) createRunParams.title = title;
200
+ if (opts.kind) createRunParams.kind = opts.kind;
201
+
202
+ try {
203
+ await client.createRun(createRunParams);
204
+ } catch (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).'));
213
+ process.exit(1);
214
+ }
215
+
216
+ log.info(`🚀 CI build triggered on profile ${pc.cyan(opts.remote)}`);
217
+ log.info(`📊 Report URL: ${pc.magenta(client.pipeStore.runUrl)}`);
218
+ return process.exit(0);
219
+ }
220
+
151
221
  // just create a run (wich tests which match filters) without executing tests
152
222
  if (!command || !command.split) {
153
223
  const createRunParams = {};
@@ -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],
@@ -24,6 +24,25 @@ const debug = createDebugMessages('@testomatio/reporter:pipe:testomatio');
24
24
 
25
25
  if (process.env.TESTOMATIO_RUN) process.env.runId = process.env.TESTOMATIO_RUN;
26
26
 
27
+ /**
28
+ * Parse `TESTOMATIO_CI_PARAMS` (comma-separated `key=value` pairs) into an object.
29
+ * Entries without `=` or with empty keys are skipped. Returns undefined when input is empty.
30
+ *
31
+ * @param {string|undefined} raw
32
+ * @returns {Record<string, string>|undefined}
33
+ */
34
+ function parseCiParams(raw) {
35
+ if (!raw) return undefined;
36
+ /** @type {Record<string, string>} */
37
+ const result = {};
38
+ for (const entry of raw.split(',')) {
39
+ const idx = entry.indexOf('=');
40
+ if (idx <= 0) continue;
41
+ result[entry.slice(0, idx).trim()] = entry.slice(idx + 1);
42
+ }
43
+ return Object.keys(result).length ? result : undefined;
44
+ }
45
+
27
46
  /**
28
47
  * @typedef {import('../../types/types.js').Pipe} Pipe
29
48
  * @typedef {import('../../types/types.js').TestData} TestData
@@ -85,6 +104,13 @@ class TestomatioPipe {
85
104
  this.label = process.env.TESTOMATIO_LABEL;
86
105
  this.description = params.description || process.env.TESTOMATIO_DESCRIPTION;
87
106
 
107
+ // Remote CI launch — when `TESTOMATIO_CI_PROFILE` is set the run will be created
108
+ // on the server *and* the named CI profile will be dispatched. Optional
109
+ // `TESTOMATIO_CI_PARAMS` is a comma-separated list of `key=value` pairs
110
+ // forwarded to the CI profile config (e.g. `branch=develop,REGION=eu`).
111
+ this.ciProfile = process.env.TESTOMATIO_CI_PROFILE;
112
+ this.ciParams = parseCiParams(process.env.TESTOMATIO_CI_PARAMS);
113
+
88
114
  // Create a new instance of gaxios with a custom config
89
115
  this.client = new Gaxios({
90
116
  baseURL: `${this.url.trim()}`,
@@ -184,6 +210,7 @@ class TestomatioPipe {
184
210
 
185
211
  if (Array.isArray(resp.data?.tests) && resp.data?.tests?.length > 0) {
186
212
  foundedTestLog(APP_PREFIX, resp.data.tests);
213
+ if (this.store) this.store.preparedTestIds = resp.data.tests;
187
214
  return resp.data.tests;
188
215
  }
189
216
 
@@ -248,6 +275,25 @@ class TestomatioPipe {
248
275
  this.store.configuration = { ...(this.store.configuration || {}), ...params.configuration };
249
276
  }
250
277
  }
278
+
279
+ // Assemble the `ci` block when the user asked for a remote CI launch via
280
+ // TESTOMATIO_CI_PROFILE (e.g. `--remote github`). Grep is taken from whatever
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} */
285
+ let ci = null;
286
+ if (this.ciProfile) {
287
+ ci = { profile: this.ciProfile };
288
+ const grepIds = this.store?.preparedTestIds;
289
+ if (grepIds?.length) {
290
+ ci.grep = grepIds.join('|');
291
+ } else if (this.runId) {
292
+ ci.type = 'run';
293
+ ci.id = this.runId;
294
+ }
295
+ if (this.ciParams) ci.override = this.ciParams;
296
+ }
251
297
  const runParams = Object.fromEntries(
252
298
  Object.entries({
253
299
  ci_build_url: buildUrl,
@@ -263,6 +309,7 @@ class TestomatioPipe {
263
309
  kind: params.kind,
264
310
  configuration,
265
311
  description,
312
+ ci,
266
313
  }).filter(([, value]) => !!value),
267
314
  );
268
315
  debug(' >>>>>> Run params', JSON.stringify(runParams, null, 2));
package/types/types.d.ts CHANGED
@@ -293,7 +293,7 @@ export interface Pipe {
293
293
  store: {};
294
294
 
295
295
  /** starts run */
296
- createRun(): Promise<void>;
296
+ createRun(params?: CreateRunParams): Promise<void>;
297
297
 
298
298
  /** adds a test to the current run */
299
299
  addTest(test: TestData): any;
@@ -316,6 +316,28 @@ export interface PipeResult {
316
316
  result?: any;
317
317
  }
318
318
 
319
+ /**
320
+ * Parameters accepted by `Client.createRun`.
321
+ *
322
+ * Remote CI launch (via `--remote <profile>` / matching `TESTOMATIO_CI_PROFILE`
323
+ * env var) is **not** passed through this object — `TestomatioPipe` reads the
324
+ * env vars directly and assembles the request body. See
325
+ * `TESTOMATIO_CI_PROFILE` and `TESTOMATIO_CI_OVERRIDE`.
326
+ */
327
+ export interface CreateRunParams {
328
+ /** Run kind. Defaults to `automated` server-side. */
329
+ kind?: 'automated' | 'manual' | 'mixed';
330
+
331
+ /** Run title. */
332
+ title?: string;
333
+
334
+ /** Run configuration merged into the server-side run configuration. */
335
+ configuration?: Record<string, any>;
336
+
337
+ /** Override batch upload on/off. */
338
+ isBatchEnabled?: boolean;
339
+ }
340
+
319
341
  /**
320
342
  * Represents a step in a test.
321
343
  */