@testomatio/reporter 2.19.0 → 2.20.0
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 +1 -0
- package/lib/adapter/cucumber/current.js +1 -0
- package/lib/adapter/cypress-plugin/index.js +1 -0
- package/lib/adapter/e2e.d.ts +49 -0
- package/lib/adapter/e2e.js +250 -0
- package/lib/adapter/jasmine.js +1 -0
- package/lib/adapter/jest.js +1 -0
- package/lib/adapter/mocha.d.ts +4 -1
- package/lib/adapter/mocha.js +123 -122
- package/lib/adapter/nightwatch.js +1 -0
- package/lib/adapter/playwright.js +1 -0
- package/lib/adapter/vitest.js +1 -0
- package/lib/adapter/webdriver.js +1 -0
- package/lib/allureReader.d.ts +4 -1
- package/lib/allureReader.js +2 -1
- package/lib/bin/cli.js +32 -13
- package/lib/client.d.ts +1 -0
- package/lib/client.js +12 -3
- package/lib/data-storage.js +4 -3
- package/lib/pipe/testomatio.js +17 -1
- package/lib/replay.js +1 -0
- package/lib/uploader.d.ts +1 -0
- package/lib/uploader.js +21 -1
- package/lib/utils/pipe_utils.d.ts +16 -3
- package/lib/utils/pipe_utils.js +45 -4
- package/lib/xmlReader.js +2 -0
- package/package.json +7 -2
- package/src/adapter/codecept.js +2 -0
- package/src/adapter/cucumber/current.js +2 -0
- package/src/adapter/cypress-plugin/index.js +2 -0
- package/src/adapter/e2e.js +245 -0
- package/src/adapter/jasmine.js +2 -0
- package/src/adapter/jest.js +2 -0
- package/src/adapter/mocha.js +146 -145
- package/src/adapter/nightwatch.js +2 -0
- package/src/adapter/playwright.js +2 -0
- package/src/adapter/vitest.js +2 -0
- package/src/adapter/webdriver.js +2 -0
- package/src/allureReader.js +2 -1
- package/src/bin/cli.js +30 -11
- package/src/client.js +11 -3
- package/src/data-storage.js +4 -3
- package/src/pipe/testomatio.js +27 -1
- package/src/replay.js +1 -0
- package/src/uploader.js +30 -1
- package/src/utils/pipe_utils.js +45 -4
- package/src/xmlReader.js +3 -0
- package/types/types.d.ts +1 -0
package/lib/adapter/codecept.js
CHANGED
|
@@ -16,6 +16,7 @@ const step_formatter_js_1 = require("./utils/step-formatter.js");
|
|
|
16
16
|
const codeceptjs_1 = __importDefault(require("codeceptjs"));
|
|
17
17
|
const log_js_1 = require("../utils/log.js");
|
|
18
18
|
const debug = (0, debug_1.default)('@testomatio/reporter:adapter:codeceptjs');
|
|
19
|
+
global.TESTOMATIO_ADAPTER = 'codeceptjs';
|
|
19
20
|
// @ts-ignore
|
|
20
21
|
if (!global.codeceptjs) {
|
|
21
22
|
// @ts-ignore
|
|
@@ -12,6 +12,7 @@ const client_js_1 = __importDefault(require("../../client.js"));
|
|
|
12
12
|
const utils_js_1 = require("../../utils/utils.js");
|
|
13
13
|
const config_js_1 = require("../../config.js");
|
|
14
14
|
const index_js_1 = require("../../services/index.js");
|
|
15
|
+
global.TESTOMATIO_ADAPTER = 'cucumber';
|
|
15
16
|
const { GherkinDocumentParser, PickleParser } = cucumber_1.formatterHelpers;
|
|
16
17
|
const { getGherkinScenarioLocationMap, getGherkinStepMap } = GherkinDocumentParser;
|
|
17
18
|
const { getPickleStepMap } = PickleParser;
|
|
@@ -7,6 +7,7 @@ const constants_js_1 = require("../../constants.js");
|
|
|
7
7
|
const utils_js_1 = require("../../utils/utils.js");
|
|
8
8
|
const client_js_1 = __importDefault(require("../../client.js"));
|
|
9
9
|
const config_js_1 = require("../../config.js");
|
|
10
|
+
global.TESTOMATIO_ADAPTER = 'cypress';
|
|
10
11
|
const testomatioReporter = on => {
|
|
11
12
|
if (!config_js_1.config.TESTOMATIO) {
|
|
12
13
|
console.log('TESTOMATIO key is empty, ignoring reports');
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reporter for the e2e framework (https://github.com/tester-army/e2e).
|
|
3
|
+
* Add it to `reporters` in `e2e.config.ts`: `reporters: ['list', testomatio()]`.
|
|
4
|
+
*
|
|
5
|
+
* The config is loaded by every worker, so the client is created only inside
|
|
6
|
+
* handlers, which run in the runner process.
|
|
7
|
+
*
|
|
8
|
+
* @param {{ apiKey?: string }} [options]
|
|
9
|
+
*/
|
|
10
|
+
export function testomatio(options?: {
|
|
11
|
+
apiKey?: string;
|
|
12
|
+
}): {
|
|
13
|
+
name: string;
|
|
14
|
+
onEvent(event: any): void;
|
|
15
|
+
onRunFinished(run: any, signal: any): Promise<{
|
|
16
|
+
label: string;
|
|
17
|
+
text: any;
|
|
18
|
+
}[]>;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Converts one report-1 result into Testomat.io test data.
|
|
22
|
+
* Only the final attempt is reported; earlier attempts are counted as retries.
|
|
23
|
+
*/
|
|
24
|
+
export function toTestData(result: any, groups: any, artifactsRoot: any): {
|
|
25
|
+
status: string;
|
|
26
|
+
data: {
|
|
27
|
+
rid: any;
|
|
28
|
+
title: any;
|
|
29
|
+
suite_title: string;
|
|
30
|
+
file: any;
|
|
31
|
+
test_id: string;
|
|
32
|
+
tags: any;
|
|
33
|
+
time: any;
|
|
34
|
+
timestamp: number;
|
|
35
|
+
error: Error & {
|
|
36
|
+
expected?: string;
|
|
37
|
+
actual?: string;
|
|
38
|
+
};
|
|
39
|
+
steps: any;
|
|
40
|
+
logs: string;
|
|
41
|
+
files: any[];
|
|
42
|
+
meta: {
|
|
43
|
+
target: any;
|
|
44
|
+
platform: any;
|
|
45
|
+
browser: any;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
export default testomatio;
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.testomatio = testomatio;
|
|
7
|
+
exports.toTestData = toTestData;
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const debug_1 = __importDefault(require("debug"));
|
|
10
|
+
const client_js_1 = __importDefault(require("../client.js"));
|
|
11
|
+
const constants_js_1 = require("../constants.js");
|
|
12
|
+
const utils_js_1 = require("../utils/utils.js");
|
|
13
|
+
const step_formatter_js_1 = require("./utils/step-formatter.js");
|
|
14
|
+
const log_js_1 = require("../utils/log.js");
|
|
15
|
+
const debug = (0, debug_1.default)('@testomatio/reporter:adapter-e2e');
|
|
16
|
+
/**
|
|
17
|
+
* Reporter for the e2e framework (https://github.com/tester-army/e2e).
|
|
18
|
+
* Add it to `reporters` in `e2e.config.ts`: `reporters: ['list', testomatio()]`.
|
|
19
|
+
*
|
|
20
|
+
* The config is loaded by every worker, so the client is created only inside
|
|
21
|
+
* handlers, which run in the runner process.
|
|
22
|
+
*
|
|
23
|
+
* @param {{ apiKey?: string }} [options]
|
|
24
|
+
*/
|
|
25
|
+
function testomatio(options = {}) {
|
|
26
|
+
let client = null;
|
|
27
|
+
let runPromise = null;
|
|
28
|
+
function getClient() {
|
|
29
|
+
if (!client)
|
|
30
|
+
client = new client_js_1.default({ apiKey: options.apiKey });
|
|
31
|
+
return client;
|
|
32
|
+
}
|
|
33
|
+
function startRun() {
|
|
34
|
+
if (!runPromise) {
|
|
35
|
+
runPromise = getClient()
|
|
36
|
+
.createRun()
|
|
37
|
+
.catch(err => log_js_1.log.error('Failed to create Testomat.io run:', err?.message || err));
|
|
38
|
+
}
|
|
39
|
+
return runPromise;
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
name: 'testomatio',
|
|
43
|
+
onEvent(event) {
|
|
44
|
+
try {
|
|
45
|
+
if (event?.type === 'run-started')
|
|
46
|
+
startRun();
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
debug('onEvent failed', err);
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
async onRunFinished(run, signal) {
|
|
53
|
+
const reporter = getClient();
|
|
54
|
+
await startRun();
|
|
55
|
+
const { report, artifactsRoot } = run;
|
|
56
|
+
const groups = new Map((report.run.serialGroups || []).map(group => [group.id, group]));
|
|
57
|
+
const promises = [];
|
|
58
|
+
for (const result of report.run.results) {
|
|
59
|
+
if (signal?.aborted)
|
|
60
|
+
break;
|
|
61
|
+
// results left out by `--last-failed` belong to the previous run
|
|
62
|
+
if (result.selected === false)
|
|
63
|
+
continue;
|
|
64
|
+
const { status, data } = toTestData(result, groups, artifactsRoot);
|
|
65
|
+
promises.push(reporter.addTestRun(status, data));
|
|
66
|
+
}
|
|
67
|
+
await Promise.all(promises);
|
|
68
|
+
const duration = (Date.parse(report.run.finishedAt) - Date.parse(report.run.startedAt)) / 1000;
|
|
69
|
+
await reporter.updateRunStatus(runStatus(report.run.status), { duration });
|
|
70
|
+
const runUrl = reporter.pipeStore?.runUrl;
|
|
71
|
+
if (runUrl)
|
|
72
|
+
return [{ label: 'Testomat.io', text: runUrl }];
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Converts one report-1 result into Testomat.io test data.
|
|
78
|
+
* Only the final attempt is reported; earlier attempts are counted as retries.
|
|
79
|
+
*/
|
|
80
|
+
function toTestData(result, groups, artifactsRoot) {
|
|
81
|
+
const attempts = attemptsOf(result, groups);
|
|
82
|
+
const final = attempts.at(-1);
|
|
83
|
+
const titlePath = [...result.titlePath];
|
|
84
|
+
const title = titlePath.pop();
|
|
85
|
+
let suiteTitle = titlePath.join(' › ');
|
|
86
|
+
if (!suiteTitle)
|
|
87
|
+
suiteTitle = path_1.default.basename(result.file);
|
|
88
|
+
const meta = {
|
|
89
|
+
target: result.targetId,
|
|
90
|
+
platform: result.platform,
|
|
91
|
+
browser: final?.environment?.browser,
|
|
92
|
+
};
|
|
93
|
+
if (result.agent && result.agent !== 'default')
|
|
94
|
+
meta.agent = result.agent;
|
|
95
|
+
if (result.repeat)
|
|
96
|
+
meta.repeat = result.repeat;
|
|
97
|
+
if (attempts.length > 1)
|
|
98
|
+
meta.retryCount = attempts.length - 1;
|
|
99
|
+
if (result.status === 'flaky')
|
|
100
|
+
meta.isFlaky = true;
|
|
101
|
+
const filesById = new Map();
|
|
102
|
+
for (const artifact of final?.artifacts || []) {
|
|
103
|
+
if (artifact.path) {
|
|
104
|
+
filesById.set(artifact.id, {
|
|
105
|
+
path: path_1.default.join(artifactsRoot, artifact.path),
|
|
106
|
+
type: artifact.mediaType,
|
|
107
|
+
title: artifact.kind,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
else if (artifact.url) {
|
|
111
|
+
// hosted engines keep videos remotely
|
|
112
|
+
meta[artifact.kind] = artifact.url;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const files = [...filesById.values()];
|
|
116
|
+
const steps = (final?.steps || []).map(step => toStep(step, filesById));
|
|
117
|
+
const data = {
|
|
118
|
+
rid: result.id,
|
|
119
|
+
title,
|
|
120
|
+
suite_title: suiteTitle,
|
|
121
|
+
file: result.file,
|
|
122
|
+
test_id: (0, utils_js_1.getTestomatIdFromTestTitle)(title),
|
|
123
|
+
tags: result.tags || [],
|
|
124
|
+
time: final?.durationMs || 0,
|
|
125
|
+
timestamp: timestampOf(final),
|
|
126
|
+
error: toError(final?.error, final?.secondaryErrors),
|
|
127
|
+
steps: steps.length ? steps : undefined,
|
|
128
|
+
logs: logsOf(result, final),
|
|
129
|
+
files: files.length ? files : undefined,
|
|
130
|
+
meta,
|
|
131
|
+
};
|
|
132
|
+
return { status: testStatus(result.status), data };
|
|
133
|
+
}
|
|
134
|
+
/** Attempts of a result; a serial member keeps its part of the group's attempts. */
|
|
135
|
+
function attemptsOf(result, groups) {
|
|
136
|
+
if (!result.serialGroupId)
|
|
137
|
+
return result.attempts || [];
|
|
138
|
+
const groupAttempts = groups.get(result.serialGroupId)?.attempts || [];
|
|
139
|
+
return groupAttempts.map(attempt => {
|
|
140
|
+
const member = attempt.members?.find(candidate => candidate.testId === result.testId);
|
|
141
|
+
return {
|
|
142
|
+
status: member?.status || attempt.status,
|
|
143
|
+
error: member?.error || attempt.error,
|
|
144
|
+
steps: member?.steps || [],
|
|
145
|
+
appLog: member?.appLog || [],
|
|
146
|
+
skip: member?.skip,
|
|
147
|
+
durationMs: member?.durationMs,
|
|
148
|
+
startedAt: member?.startedAt || attempt.startedAt,
|
|
149
|
+
artifacts: attempt.artifacts || [],
|
|
150
|
+
secondaryErrors: [...(member?.secondaryErrors || []), ...(attempt.secondaryErrors || [])],
|
|
151
|
+
environment: attempt.environment,
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
function timestampOf(attempt) {
|
|
156
|
+
if (!attempt?.startedAt)
|
|
157
|
+
return undefined;
|
|
158
|
+
return Date.parse(attempt.startedAt);
|
|
159
|
+
}
|
|
160
|
+
function testStatus(status) {
|
|
161
|
+
if (status === 'passed' || status === 'flaky')
|
|
162
|
+
return constants_js_1.STATUS.PASSED;
|
|
163
|
+
if (status === 'skipped')
|
|
164
|
+
return constants_js_1.STATUS.SKIPPED;
|
|
165
|
+
// failed, timed-out, interrupted: the test reached no passing verdict
|
|
166
|
+
return constants_js_1.STATUS.FAILED;
|
|
167
|
+
}
|
|
168
|
+
function runStatus(status) {
|
|
169
|
+
if (status === 'passed')
|
|
170
|
+
return constants_js_1.STATUS.PASSED;
|
|
171
|
+
return constants_js_1.STATUS.FAILED;
|
|
172
|
+
}
|
|
173
|
+
function toError(error, secondaryErrors = []) {
|
|
174
|
+
if (!error)
|
|
175
|
+
return undefined;
|
|
176
|
+
/** @type {Error & { expected?: string, actual?: string }} */
|
|
177
|
+
const err = new Error(error.message);
|
|
178
|
+
err.name = error.code;
|
|
179
|
+
err.stack = `${error.code}: ${error.message}\n${error.stack || sourceFrame(error.source)}`;
|
|
180
|
+
if (error.details?.expected && error.details?.observed) {
|
|
181
|
+
err.expected = error.details.expected;
|
|
182
|
+
err.actual = error.details.observed;
|
|
183
|
+
}
|
|
184
|
+
for (const secondary of secondaryErrors) {
|
|
185
|
+
err.stack += `\n\nAlso failed (${secondary.phase || secondary.category}) ${secondary.code}: ${secondary.message}`;
|
|
186
|
+
}
|
|
187
|
+
return err;
|
|
188
|
+
}
|
|
189
|
+
function sourceFrame(source) {
|
|
190
|
+
if (!source)
|
|
191
|
+
return '';
|
|
192
|
+
return ` at ${source.file}:${source.line}:${source.column}`;
|
|
193
|
+
}
|
|
194
|
+
function toStep(step, filesById) {
|
|
195
|
+
let category = 'user';
|
|
196
|
+
if (step.phase === 'beforeEach' || step.phase === 'afterEach')
|
|
197
|
+
category = 'hook';
|
|
198
|
+
let title = step.api;
|
|
199
|
+
if (step.label)
|
|
200
|
+
title = `${step.api} ${step.label}`;
|
|
201
|
+
const formatted = (0, step_formatter_js_1.formatStep)({
|
|
202
|
+
category,
|
|
203
|
+
title,
|
|
204
|
+
duration: step.durationMs,
|
|
205
|
+
log: stepLog(step),
|
|
206
|
+
});
|
|
207
|
+
let status = 'failed';
|
|
208
|
+
if (step.status === 'passed')
|
|
209
|
+
status = 'passed';
|
|
210
|
+
(0, step_formatter_js_1.addStatusToStep)(formatted, status, step.error);
|
|
211
|
+
if (step.error) {
|
|
212
|
+
formatted.error = { message: (0, utils_js_1.truncate)(`${step.error.code}: ${step.error.message}`, 250) };
|
|
213
|
+
}
|
|
214
|
+
if (constants_js_1.SCREENSHOTS_ON_STEPS) {
|
|
215
|
+
const screenshot = (step.artifacts || []).map(id => filesById.get(id)).find(file => file?.type === 'image/png');
|
|
216
|
+
if (screenshot)
|
|
217
|
+
(0, step_formatter_js_1.addArtifactsToStep)(formatted, { screenshot: screenshot.path });
|
|
218
|
+
}
|
|
219
|
+
return formatted;
|
|
220
|
+
}
|
|
221
|
+
/** Agent reasoning and non-binary step statuses, which a passed/failed step would lose. */
|
|
222
|
+
function stepLog(step) {
|
|
223
|
+
const lines = [];
|
|
224
|
+
if (step.status !== 'passed' && step.status !== 'failed')
|
|
225
|
+
lines.push(`status: ${step.status}`);
|
|
226
|
+
if (step.explanation)
|
|
227
|
+
lines.push(step.explanation);
|
|
228
|
+
for (const event of step.events || []) {
|
|
229
|
+
if (event.reasoning)
|
|
230
|
+
lines.push(event.reasoning);
|
|
231
|
+
}
|
|
232
|
+
return lines.join('\n');
|
|
233
|
+
}
|
|
234
|
+
function logsOf(result, final) {
|
|
235
|
+
const lines = [];
|
|
236
|
+
const skip = final?.skip || result.skip;
|
|
237
|
+
if (skip)
|
|
238
|
+
lines.push(`Skipped (${skip.cause}): ${skip.reason}`);
|
|
239
|
+
if (final?.appLog?.length) {
|
|
240
|
+
lines.push('App log:');
|
|
241
|
+
for (const line of final.appLog)
|
|
242
|
+
lines.push(`[${line.source}] ${line.level}: ${line.text}`);
|
|
243
|
+
}
|
|
244
|
+
return lines.join('\n');
|
|
245
|
+
}
|
|
246
|
+
module.exports = testomatio;
|
|
247
|
+
|
|
248
|
+
module.exports.testomatio = testomatio;
|
|
249
|
+
|
|
250
|
+
module.exports.toTestData = toTestData;
|
package/lib/adapter/jasmine.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.JasmineReporter = void 0;
|
|
|
7
7
|
const client_js_1 = __importDefault(require("../client.js"));
|
|
8
8
|
const utils_js_1 = require("../utils/utils.js");
|
|
9
9
|
const constants_js_1 = require("../constants.js");
|
|
10
|
+
global.TESTOMATIO_ADAPTER = 'jasmine';
|
|
10
11
|
class JasmineReporter {
|
|
11
12
|
constructor(options) {
|
|
12
13
|
this.testTimeMap = {};
|
package/lib/adapter/jest.js
CHANGED
|
@@ -12,6 +12,7 @@ const index_js_1 = require("../services/index.js");
|
|
|
12
12
|
const path_1 = __importDefault(require("path"));
|
|
13
13
|
const debug_1 = __importDefault(require("debug"));
|
|
14
14
|
const debug = (0, debug_1.default)('@testomatio/reporter:adapter-jest');
|
|
15
|
+
global.TESTOMATIO_ADAPTER = 'jest';
|
|
15
16
|
class JestReporter {
|
|
16
17
|
constructor(globalConfig, options) {
|
|
17
18
|
this._globalConfig = globalConfig;
|
package/lib/adapter/mocha.d.ts
CHANGED
package/lib/adapter/mocha.js
CHANGED
|
@@ -10,133 +10,137 @@ const utils_js_1 = require("../utils/utils.js");
|
|
|
10
10
|
const config_js_1 = require("../config.js");
|
|
11
11
|
const index_js_1 = require("../services/index.js");
|
|
12
12
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
13
|
+
global.TESTOMATIO_ADAPTER = 'mocha';
|
|
13
14
|
const { EVENT_RUN_BEGIN, EVENT_RUN_END, EVENT_TEST_FAIL, EVENT_TEST_PASS, EVENT_TEST_PENDING, EVENT_SUITE_BEGIN, EVENT_SUITE_END, EVENT_TEST_BEGIN, EVENT_TEST_END, } = mocha_1.default.Runner.constants;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
15
|
+
// ES class so it works with mocha 12, where Base is a class and utils.inherits is gone
|
|
16
|
+
class MochaReporter extends mocha_1.default.reporters.Base {
|
|
17
|
+
constructor(runner, opts) {
|
|
18
|
+
super(runner, opts);
|
|
19
|
+
let passes = 0;
|
|
20
|
+
let failures = 0;
|
|
21
|
+
let skipped = 0;
|
|
22
|
+
// let artifactStore;
|
|
23
|
+
const apiKey = opts?.reporterOptions?.apiKey || config_js_1.config.TESTOMATIO;
|
|
24
|
+
const client = new client_js_1.default({ apiKey });
|
|
25
|
+
// Track hook failures
|
|
26
|
+
const hookFailures = new Map();
|
|
27
|
+
runner.on(EVENT_RUN_BEGIN, () => {
|
|
28
|
+
client.createRun();
|
|
29
|
+
// clear dir with artifacts/logs
|
|
30
|
+
utils_js_1.fileSystem.clearDir(constants_js_1.TESTOMAT_TMP_STORAGE_DIR);
|
|
31
|
+
});
|
|
32
|
+
runner.on(EVENT_SUITE_BEGIN, async (suite) => {
|
|
33
|
+
index_js_1.services.setContext(suite.fullTitle());
|
|
34
|
+
});
|
|
35
|
+
runner.on(EVENT_SUITE_END, async (suite) => {
|
|
36
|
+
if (hookFailures.has(suite.fullTitle())) {
|
|
37
|
+
const { error, suiteTitle } = hookFailures.get(suite.fullTitle());
|
|
38
|
+
for (const test of suite.tests) {
|
|
39
|
+
if (test.state === 'pending' || !test.state) {
|
|
40
|
+
const testId = (0, utils_js_1.getTestomatIdFromTestTitle)(test.title);
|
|
41
|
+
const artifacts = index_js_1.services.artifacts.get(test.fullTitle());
|
|
42
|
+
const keyValues = index_js_1.services.keyValues.get(test.fullTitle());
|
|
43
|
+
const links = index_js_1.services.links.get(test.fullTitle());
|
|
44
|
+
client.addTestRun(constants_js_1.STATUS.FAILED, {
|
|
45
|
+
error,
|
|
46
|
+
suite_title: suiteTitle || suite.title,
|
|
47
|
+
file: suite.file,
|
|
48
|
+
test_id: testId,
|
|
49
|
+
title: test.title,
|
|
50
|
+
code: process.env.TESTOMATIO_UPDATE_CODE ? test.body.toString() : '',
|
|
51
|
+
time: 0,
|
|
52
|
+
manuallyAttachedArtifacts: artifacts,
|
|
53
|
+
meta: keyValues,
|
|
54
|
+
links,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
53
57
|
}
|
|
54
58
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
59
|
+
index_js_1.services.setContext(null);
|
|
60
|
+
});
|
|
61
|
+
runner.on(EVENT_TEST_BEGIN, async (test) => {
|
|
62
|
+
index_js_1.services.setContext(test.fullTitle());
|
|
63
|
+
});
|
|
64
|
+
runner.on(EVENT_TEST_END, async () => {
|
|
65
|
+
index_js_1.services.setContext(null);
|
|
66
|
+
});
|
|
67
|
+
runner.on(EVENT_TEST_PASS, async (test) => {
|
|
68
|
+
passes += 1;
|
|
69
|
+
console.log(picocolors_1.default.bold(picocolors_1.default.green('✔')), test.fullTitle());
|
|
70
|
+
const testId = (0, utils_js_1.getTestomatIdFromTestTitle)(test.title);
|
|
71
|
+
const logs = getTestLogs(test);
|
|
72
|
+
const artifacts = index_js_1.services.artifacts.get(test.fullTitle());
|
|
73
|
+
const keyValues = index_js_1.services.keyValues.get(test.fullTitle());
|
|
74
|
+
const links = index_js_1.services.links.get(test.fullTitle());
|
|
75
|
+
client.addTestRun(constants_js_1.STATUS.PASSED, {
|
|
76
|
+
test_id: testId,
|
|
77
|
+
suite_title: getSuiteTitle(test),
|
|
78
|
+
title: getTestName(test),
|
|
79
|
+
code: process.env.TESTOMATIO_UPDATE_CODE ? test.body.toString() : '',
|
|
80
|
+
file: getFile(test),
|
|
81
|
+
time: test.duration,
|
|
82
|
+
logs,
|
|
83
|
+
manuallyAttachedArtifacts: artifacts,
|
|
84
|
+
meta: keyValues,
|
|
85
|
+
links,
|
|
86
|
+
});
|
|
83
87
|
});
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
88
|
+
runner.on(EVENT_TEST_PENDING, test => {
|
|
89
|
+
skipped += 1;
|
|
90
|
+
console.log('skip: %s', test.fullTitle());
|
|
91
|
+
const testId = (0, utils_js_1.getTestomatIdFromTestTitle)(test.title);
|
|
92
|
+
const artifacts = index_js_1.services.artifacts.get(test.fullTitle());
|
|
93
|
+
const keyValues = index_js_1.services.keyValues.get(test.fullTitle());
|
|
94
|
+
const links = index_js_1.services.links.get(test.fullTitle());
|
|
95
|
+
client.addTestRun(constants_js_1.STATUS.SKIPPED, {
|
|
96
|
+
title: getTestName(test),
|
|
97
|
+
suite_title: getSuiteTitle(test),
|
|
98
|
+
code: process.env.TESTOMATIO_UPDATE_CODE ? test.body.toString() : '',
|
|
99
|
+
file: getFile(test),
|
|
100
|
+
test_id: testId,
|
|
101
|
+
time: test.duration,
|
|
102
|
+
manuallyAttachedArtifacts: artifacts,
|
|
103
|
+
meta: keyValues,
|
|
104
|
+
links,
|
|
105
|
+
});
|
|
102
106
|
});
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
runner.on(EVENT_TEST_FAIL, async (test, err) => {
|
|
108
|
+
failures += 1;
|
|
109
|
+
console.log(picocolors_1.default.bold(picocolors_1.default.red('✖')), test.fullTitle(), picocolors_1.default.gray(err.message));
|
|
110
|
+
const isHookFailure = test.title.includes('before each') || test.title.includes('after each');
|
|
111
|
+
if (isHookFailure && test.parent) {
|
|
112
|
+
hookFailures.set(test.parent.fullTitle(), {
|
|
113
|
+
error: err,
|
|
114
|
+
suiteTitle: getSuiteTitle(test),
|
|
115
|
+
});
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const testId = (0, utils_js_1.getTestomatIdFromTestTitle)(test.title);
|
|
119
|
+
const logs = getTestLogs(test);
|
|
120
|
+
const artifacts = index_js_1.services.artifacts.get(test.fullTitle());
|
|
121
|
+
const keyValues = index_js_1.services.keyValues.get(test.fullTitle());
|
|
122
|
+
const links = index_js_1.services.links.get(test.fullTitle());
|
|
123
|
+
client.addTestRun(constants_js_1.STATUS.FAILED, {
|
|
110
124
|
error: err,
|
|
111
|
-
|
|
125
|
+
suite_title: getSuiteTitle(test),
|
|
126
|
+
file: getFile(test),
|
|
127
|
+
test_id: testId,
|
|
128
|
+
title: getTestName(test),
|
|
129
|
+
code: process.env.TESTOMATIO_UPDATE_CODE ? test.body.toString() : '',
|
|
130
|
+
time: test.duration,
|
|
131
|
+
logs,
|
|
132
|
+
manuallyAttachedArtifacts: artifacts,
|
|
133
|
+
meta: keyValues,
|
|
134
|
+
links,
|
|
112
135
|
});
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
const testId = (0, utils_js_1.getTestomatIdFromTestTitle)(test.title);
|
|
116
|
-
const logs = getTestLogs(test);
|
|
117
|
-
const artifacts = index_js_1.services.artifacts.get(test.fullTitle());
|
|
118
|
-
const keyValues = index_js_1.services.keyValues.get(test.fullTitle());
|
|
119
|
-
const links = index_js_1.services.links.get(test.fullTitle());
|
|
120
|
-
client.addTestRun(constants_js_1.STATUS.FAILED, {
|
|
121
|
-
error: err,
|
|
122
|
-
suite_title: getSuiteTitle(test),
|
|
123
|
-
file: getFile(test),
|
|
124
|
-
test_id: testId,
|
|
125
|
-
title: getTestName(test),
|
|
126
|
-
code: process.env.TESTOMATIO_UPDATE_CODE ? test.body.toString() : '',
|
|
127
|
-
time: test.duration,
|
|
128
|
-
logs,
|
|
129
|
-
manuallyAttachedArtifacts: artifacts,
|
|
130
|
-
meta: keyValues,
|
|
131
|
-
links,
|
|
132
136
|
});
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
137
|
+
runner.on(EVENT_RUN_END, () => {
|
|
138
|
+
const status = failures === 0 ? constants_js_1.STATUS.PASSED : constants_js_1.STATUS.FAILED;
|
|
139
|
+
console.log(picocolors_1.default.bold(status), `${passes} passed, ${failures} failed, ${skipped} skipped`);
|
|
140
|
+
// @ts-ignore
|
|
141
|
+
client.updateRunStatus(status);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
140
144
|
}
|
|
141
145
|
function getTestLogs(test) {
|
|
142
146
|
const suiteLogsArr = index_js_1.services.logger.getLogs(test.parent.fullTitle());
|
|
@@ -166,7 +170,4 @@ function getTestName(test) {
|
|
|
166
170
|
return test.fullTitle();
|
|
167
171
|
return test.title;
|
|
168
172
|
}
|
|
169
|
-
// To have this reporter "extend" a built-in reporter uncomment the following line:
|
|
170
|
-
// @ts-ignore
|
|
171
|
-
mocha_1.default.utils.inherits(MochaReporter, mocha_1.default.reporters.Spec);
|
|
172
173
|
module.exports = MochaReporter;
|
|
@@ -7,6 +7,7 @@ const client_js_1 = __importDefault(require("../client.js"));
|
|
|
7
7
|
const config_js_1 = require("../config.js");
|
|
8
8
|
const constants_js_1 = require("../constants.js");
|
|
9
9
|
const utils_js_1 = require("../utils/utils.js");
|
|
10
|
+
global.TESTOMATIO_ADAPTER = 'nightwatch';
|
|
10
11
|
const apiKey = config_js_1.config.TESTOMATIO;
|
|
11
12
|
const client = new client_js_1.default({ apiKey });
|
|
12
13
|
module.exports = {
|
|
@@ -21,6 +21,7 @@ const playwright_js_1 = require("./utils/playwright.js");
|
|
|
21
21
|
Object.defineProperty(exports, "fetchLinksFromLogs", { enumerable: true, get: function () { return playwright_js_1.fetchLinksFromLogs; } });
|
|
22
22
|
const step_formatter_js_1 = require("./utils/step-formatter.js");
|
|
23
23
|
const log_js_1 = require("../utils/log.js");
|
|
24
|
+
global.TESTOMATIO_ADAPTER = 'playwright';
|
|
24
25
|
class PlaywrightReporter {
|
|
25
26
|
constructor(config = {}) {
|
|
26
27
|
this.client = new client_js_1.default({ apiKey: config?.apiKey });
|
package/lib/adapter/vitest.js
CHANGED
|
@@ -10,6 +10,7 @@ const constants_js_1 = require("../constants.js");
|
|
|
10
10
|
const utils_js_1 = require("../utils/utils.js");
|
|
11
11
|
const debug_1 = __importDefault(require("debug"));
|
|
12
12
|
const debug = (0, debug_1.default)('@testomatio/reporter:adapter-jest');
|
|
13
|
+
global.TESTOMATIO_ADAPTER = 'vitest';
|
|
13
14
|
/**
|
|
14
15
|
* @typedef {import('../../types/types.js').VitestTest} VitestTest
|
|
15
16
|
* @typedef {import('../../types/types.js').VitestTestFile} VitestTestFile
|
package/lib/adapter/webdriver.js
CHANGED
|
@@ -42,6 +42,7 @@ const utils_js_1 = require("../utils/utils.js");
|
|
|
42
42
|
const index_js_1 = require("../services/index.js");
|
|
43
43
|
const constants_js_1 = require("../constants.js");
|
|
44
44
|
const data_storage_js_1 = require("../data-storage.js");
|
|
45
|
+
global.TESTOMATIO_ADAPTER = 'webdriverio';
|
|
45
46
|
class WebdriverReporter extends reporter_1.default {
|
|
46
47
|
constructor(options) {
|
|
47
48
|
super(options);
|