@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 +38 -1
- package/lib/client.js +2 -0
- package/lib/pipe/testomatio.d.ts +2 -0
- package/lib/pipe/testomatio.js +40 -0
- package/package.json +1 -1
- package/src/bin/cli.js +44 -1
- package/src/client.js +3 -0
- package/src/pipe/testomatio.js +39 -0
- package/types/types.d.ts +23 -1
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
|
}
|
package/lib/pipe/testomatio.d.ts
CHANGED
package/lib/pipe/testomatio.js
CHANGED
|
@@ -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
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;
|
package/src/pipe/testomatio.js
CHANGED
|
@@ -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
|
*/
|