@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.
- package/lib/adapter/codecept.js +7 -2
- package/lib/bin/cli.js +35 -12
- package/lib/client.js +0 -2
- package/lib/pipe/coverage.js +1 -0
- package/lib/pipe/testomatio.js +12 -3
- package/package.json +2 -2
- package/src/adapter/codecept.js +7 -2
- package/src/bin/cli.js +39 -12
- package/src/client.js +0 -3
- package/src/pipe/coverage.js +1 -0
- package/src/pipe/testomatio.js +11 -3
package/lib/adapter/codecept.js
CHANGED
|
@@ -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 =
|
|
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
|
|
38
|
-
// remaining output to stderr, skip the banner, and silence info-level
|
|
39
|
-
//
|
|
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
|
-
|
|
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)
|
|
64
|
-
|
|
65
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
}
|
package/lib/pipe/coverage.js
CHANGED
package/lib/pipe/testomatio.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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.
|
|
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",
|
package/src/adapter/codecept.js
CHANGED
|
@@ -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 =
|
|
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
|
|
36
|
-
// remaining output to stderr, skip the banner, and silence info-level
|
|
37
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
65
|
-
|
|
66
|
-
|
|
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.
|
|
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
|
-
|
|
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;
|
package/src/pipe/coverage.js
CHANGED
|
@@ -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],
|
package/src/pipe/testomatio.js
CHANGED
|
@@ -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
|
-
|
|
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)
|
|
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(
|