@testomatio/reporter 2.8.4 → 2.8.5-beta.1-remote

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.
package/lib/bin/cli.js CHANGED
@@ -91,7 +91,19 @@ program
91
91
  .option('--filter-list <filter>', 'Get a list of all tests by filter before running')
92
92
  .option('--format <format>', 'Machine-readable output format for --filter-list (grep, json, newline, ids)')
93
93
  .option('--kind <type>', 'Specify run type: automated, manual, or mixed')
94
+ .option('--remote <profile>', 'Trigger run on the named Testomat.io CI profile instead of executing locally')
95
+ .option('--remote-param <kv>', 'key=value pair forwarded to the CI profile config (repeat for multiple)', (value, prev) => prev.concat([value]), [])
94
96
  .action(async (command, opts) => {
97
+ if (opts.remote) {
98
+ if (opts.filterList) {
99
+ log_js_1.log.warn(picocolors_1.default.red('⚠️ --filter-list cannot be combined with --remote'));
100
+ process.exit(1);
101
+ }
102
+ process.env.TESTOMATIO_CI_PROFILE = opts.remote;
103
+ if (opts.remoteParam?.length) {
104
+ process.env.TESTOMATIO_CI_PARAMS = opts.remoteParam.join(',');
105
+ }
106
+ }
95
107
  const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || config_js_1.config.TESTOMATIO;
96
108
  const title = process.env.TESTOMATIO_TITLE;
97
109
  const client = new client_js_1.default({ apiKey, title });
@@ -127,7 +139,7 @@ program
127
139
  }
128
140
  return;
129
141
  }
130
- if (command && command.split) {
142
+ if (command && command.split && !opts.remote) {
131
143
  command = (0, utils_js_1.applyFilter)(command, tests);
132
144
  }
133
145
  }
@@ -138,6 +150,31 @@ program
138
150
  return;
139
151
  }
140
152
  }
153
+ if (opts.remote) {
154
+ if (!apiKey) {
155
+ log_js_1.log.warn(picocolors_1.default.red('⚠️ TESTOMATIO API key required for --remote'));
156
+ process.exit(1);
157
+ }
158
+ if (command) {
159
+ log_js_1.log.warn(picocolors_1.default.yellow('Note: positional command is ignored when --remote is set; CI runs the workflow.'));
160
+ }
161
+ const createRunParams = {};
162
+ if (title)
163
+ createRunParams.title = title;
164
+ if (opts.kind)
165
+ createRunParams.kind = opts.kind;
166
+ try {
167
+ await client.createRun(createRunParams);
168
+ }
169
+ catch (err) {
170
+ log_js_1.log.warn(picocolors_1.default.red(`CI launch failed: ${err.message || err}`));
171
+ process.exit(1);
172
+ }
173
+ 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)}`);
176
+ return process.exit(0);
177
+ }
141
178
  // just create a run (wich tests which match filters) without executing tests
142
179
  if (!command || !command.split) {
143
180
  const createRunParams = {};
package/lib/client.js CHANGED
@@ -92,6 +92,8 @@ 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;
95
97
  debug('Execution tests list', result);
96
98
  return result;
97
99
  }
@@ -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()}`,
@@ -213,6 +239,19 @@ class TestomatioPipe {
213
239
  this.store.configuration = { ...(this.store.configuration || {}), ...params.configuration };
214
240
  }
215
241
  }
242
+ // Assemble the `ci` block when the user asked for a remote CI launch via
243
+ // 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
+ let ci = null;
247
+ if (this.ciProfile) {
248
+ ci = { profile: this.ciProfile };
249
+ const grepIds = this.store?.preparedTestIds;
250
+ if (grepIds?.length)
251
+ ci.grep = grepIds.join('|');
252
+ if (this.ciParams)
253
+ ci.override = this.ciParams;
254
+ }
216
255
  const runParams = Object.fromEntries(Object.entries({
217
256
  ci_build_url: buildUrl,
218
257
  api_key: this.apiKey.trim(),
@@ -227,6 +266,7 @@ class TestomatioPipe {
227
266
  kind: params.kind,
228
267
  configuration,
229
268
  description,
269
+ ci,
230
270
  }).filter(([, value]) => !!value));
231
271
  debug(' >>>>>> Run params', JSON.stringify(runParams, null, 2));
232
272
  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.1-remote",
4
4
  "description": "Testomatio Reporter Client",
5
5
  "engines": {
6
6
  "node": ">=18"
package/src/bin/cli.js CHANGED
@@ -97,7 +97,25 @@ program
97
97
  .option('--filter-list <filter>', 'Get a list of all tests by filter before running')
98
98
  .option('--format <format>', 'Machine-readable output format for --filter-list (grep, json, newline, ids)')
99
99
  .option('--kind <type>', 'Specify run type: automated, manual, or mixed')
100
+ .option('--remote <profile>', 'Trigger run on the named Testomat.io CI profile instead of executing locally')
101
+ .option(
102
+ '--remote-param <kv>',
103
+ 'key=value pair forwarded to the CI profile config (repeat for multiple)',
104
+ (value, prev) => prev.concat([value]),
105
+ [],
106
+ )
100
107
  .action(async (command, opts) => {
108
+ if (opts.remote) {
109
+ if (opts.filterList) {
110
+ log.warn(pc.red('⚠️ --filter-list cannot be combined with --remote'));
111
+ process.exit(1);
112
+ }
113
+ process.env.TESTOMATIO_CI_PROFILE = opts.remote;
114
+ if (opts.remoteParam?.length) {
115
+ process.env.TESTOMATIO_CI_PARAMS = opts.remoteParam.join(',');
116
+ }
117
+ }
118
+
101
119
  const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || config.TESTOMATIO;
102
120
  const title = process.env.TESTOMATIO_TITLE;
103
121
  const client = new TestomatClient({ apiKey, title });
@@ -137,7 +155,7 @@ program
137
155
  return;
138
156
  }
139
157
 
140
- if (command && command.split) {
158
+ if (command && command.split && !opts.remote) {
141
159
  command = applyFilter(command, tests);
142
160
  }
143
161
  }
@@ -148,6 +166,31 @@ program
148
166
  }
149
167
  }
150
168
 
169
+ if (opts.remote) {
170
+ if (!apiKey) {
171
+ log.warn(pc.red('⚠️ TESTOMATIO API key required for --remote'));
172
+ process.exit(1);
173
+ }
174
+ if (command) {
175
+ log.warn(pc.yellow('Note: positional command is ignored when --remote is set; CI runs the workflow.'));
176
+ }
177
+
178
+ const createRunParams = {};
179
+ if (title) createRunParams.title = title;
180
+ if (opts.kind) createRunParams.kind = opts.kind;
181
+
182
+ try {
183
+ await client.createRun(createRunParams);
184
+ } catch (err) {
185
+ log.warn(pc.red(`CI launch failed: ${err.message || err}`));
186
+ process.exit(1);
187
+ }
188
+
189
+ 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)}`);
191
+ return process.exit(0);
192
+ }
193
+
151
194
  // just create a run (wich tests which match filters) without executing tests
152
195
  if (!command || !command.split) {
153
196
  const createRunParams = {};
package/src/client.js CHANGED
@@ -102,6 +102,9 @@ 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
+
105
108
  debug('Execution tests list', result);
106
109
 
107
110
  return result;
@@ -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()}`,
@@ -248,6 +274,18 @@ class TestomatioPipe {
248
274
  this.store.configuration = { ...(this.store.configuration || {}), ...params.configuration };
249
275
  }
250
276
  }
277
+
278
+ // Assemble the `ci` block when the user asked for a remote CI launch via
279
+ // 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} */
282
+ let ci = null;
283
+ if (this.ciProfile) {
284
+ ci = { profile: this.ciProfile };
285
+ const grepIds = this.store?.preparedTestIds;
286
+ if (grepIds?.length) ci.grep = grepIds.join('|');
287
+ if (this.ciParams) ci.override = this.ciParams;
288
+ }
251
289
  const runParams = Object.fromEntries(
252
290
  Object.entries({
253
291
  ci_build_url: buildUrl,
@@ -263,6 +301,7 @@ class TestomatioPipe {
263
301
  kind: params.kind,
264
302
  configuration,
265
303
  description,
304
+ ci,
266
305
  }).filter(([, value]) => !!value),
267
306
  );
268
307
  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
  */