@testomatio/reporter 1.4.5 → 1.5.0-beta-vitest
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 +33 -29
- package/lib/adapter/playwright.js +22 -18
- package/lib/adapter/vitest.js +182 -0
- package/lib/bin/reportXml.js +9 -4
- package/lib/client.js +22 -10
- package/lib/pipe/gitlab.js +4 -4
- package/lib/pipe/testomatio.js +0 -3
- package/lib/utils/utils.js +1 -1
- package/lib/xmlReader.js +2 -3
- package/package.json +2 -1
package/lib/adapter/codecept.js
CHANGED
|
@@ -129,8 +129,8 @@ function CodeceptReporter(config) {
|
|
|
129
129
|
await Promise.all(reportTestPromises);
|
|
130
130
|
|
|
131
131
|
if (upload.isArtifactsEnabled()) {
|
|
132
|
-
|
|
133
|
-
|
|
132
|
+
uploadAttachments(client, videos, '🎞️ Uploading', 'video');
|
|
133
|
+
uploadAttachments(client, traces, '📁 Uploading', 'trace');
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
const status = failedTests.length === 0 ? STATUS.PASSED : STATUS.FAILED;
|
|
@@ -142,6 +142,7 @@ function CodeceptReporter(config) {
|
|
|
142
142
|
if (id && failedTests.includes(id)) {
|
|
143
143
|
failedTests = failedTests.filter(failed => id !== failed);
|
|
144
144
|
}
|
|
145
|
+
const testId = getTestomatIdFromTestTitle(`${title} ${tags?.join(' ')}`);
|
|
145
146
|
const testObj = getTestAndMessage(title);
|
|
146
147
|
|
|
147
148
|
const logs = getTestLogs(test);
|
|
@@ -151,12 +152,11 @@ function CodeceptReporter(config) {
|
|
|
151
152
|
|
|
152
153
|
client.addTestRun(STATUS.PASSED, {
|
|
153
154
|
...stripExampleFromTitle(title),
|
|
154
|
-
rid: id,
|
|
155
155
|
suite_title: test.parent && test.parent.title,
|
|
156
156
|
message: testObj.message,
|
|
157
157
|
time: getDuration(test),
|
|
158
158
|
steps: global.testomatioDataStore.steps.join('\n') || null,
|
|
159
|
-
test_id:
|
|
159
|
+
test_id: testId,
|
|
160
160
|
logs,
|
|
161
161
|
manuallyAttachedArtifacts,
|
|
162
162
|
meta: keyValues,
|
|
@@ -179,7 +179,6 @@ function CodeceptReporter(config) {
|
|
|
179
179
|
const testId = getTestomatIdFromTestTitle(`${title} ${tags?.join(' ')}`);
|
|
180
180
|
|
|
181
181
|
client.addTestRun(STATUS.FAILED, {
|
|
182
|
-
rid: id,
|
|
183
182
|
...stripExampleFromTitle(title),
|
|
184
183
|
suite_title: suite.title,
|
|
185
184
|
test_id: testId,
|
|
@@ -195,6 +194,7 @@ function CodeceptReporter(config) {
|
|
|
195
194
|
if (test.err) error = test.err;
|
|
196
195
|
const { id, tags, title, artifacts } = test;
|
|
197
196
|
failedTests.push(id || title);
|
|
197
|
+
let testId = getTestomatIdFromTestTitle(`${title} ${tags?.join(' ')}`);
|
|
198
198
|
const testObj = getTestAndMessage(title);
|
|
199
199
|
|
|
200
200
|
const files = [];
|
|
@@ -206,10 +206,10 @@ function CodeceptReporter(config) {
|
|
|
206
206
|
const keyValues = services.keyValues.get(test.fullTitle());
|
|
207
207
|
services.setContext(null);
|
|
208
208
|
|
|
209
|
-
client
|
|
209
|
+
const reportTestPromise = client
|
|
210
|
+
.addTestRun(STATUS.FAILED, {
|
|
210
211
|
...stripExampleFromTitle(title),
|
|
211
|
-
|
|
212
|
-
test_id: getTestomatIdFromTestTitle(`${title} ${tags?.join(' ')}`),
|
|
212
|
+
test_id: testId,
|
|
213
213
|
suite_title: test.parent && test.parent.title,
|
|
214
214
|
error,
|
|
215
215
|
message: testObj.message,
|
|
@@ -219,14 +219,19 @@ function CodeceptReporter(config) {
|
|
|
219
219
|
logs,
|
|
220
220
|
manuallyAttachedArtifacts,
|
|
221
221
|
meta: keyValues,
|
|
222
|
-
|
|
222
|
+
})
|
|
223
|
+
.then(pipes => {
|
|
224
|
+
testId = pipes.filter(p => p.pipe.includes('Testomatio'))[0]?.result?.data?.test_id;
|
|
223
225
|
|
|
224
|
-
|
|
226
|
+
debug('artifacts', artifacts);
|
|
225
227
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
228
|
+
for (const aid in artifacts) {
|
|
229
|
+
if (aid.startsWith('video')) videos.push({ testId, title, path: artifacts[aid], type: 'video/webm' });
|
|
230
|
+
if (aid.startsWith('trace')) traces.push({ testId, title, path: artifacts[aid], type: 'application/zip' });
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
reportTestPromises.push(reportTestPromise);
|
|
230
235
|
|
|
231
236
|
// output.stop();
|
|
232
237
|
});
|
|
@@ -235,11 +240,11 @@ function CodeceptReporter(config) {
|
|
|
235
240
|
const { id, tags, title } = test;
|
|
236
241
|
if (failedTests.includes(id || title)) return;
|
|
237
242
|
|
|
243
|
+
const testId = getTestomatIdFromTestTitle(`${title} ${tags?.join(' ')}`);
|
|
238
244
|
const testObj = getTestAndMessage(title);
|
|
239
245
|
client.addTestRun(STATUS.SKIPPED, {
|
|
240
|
-
rid: id,
|
|
241
246
|
...stripExampleFromTitle(title),
|
|
242
|
-
test_id:
|
|
247
|
+
test_id: testId,
|
|
243
248
|
suite_title: test.parent && test.parent.title,
|
|
244
249
|
message: testObj.message,
|
|
245
250
|
time: getDuration(test),
|
|
@@ -304,22 +309,21 @@ function CodeceptReporter(config) {
|
|
|
304
309
|
}
|
|
305
310
|
|
|
306
311
|
async function uploadAttachments(client, attachments, messagePrefix, attachmentType) {
|
|
307
|
-
if (
|
|
308
|
-
|
|
309
|
-
console.log(APP_PREFIX, `Attachments: ${messagePrefix} ${attachments.length} ${attachmentType} ...`);
|
|
312
|
+
if (attachments.length > 0) {
|
|
313
|
+
console.log(APP_PREFIX, `Attachments: ${messagePrefix} ${attachments.length} ${attachmentType}/-s ...`);
|
|
310
314
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
315
|
+
const promises = attachments.map(async attachment => {
|
|
316
|
+
const { testId, title, path, type } = attachment;
|
|
317
|
+
const file = { path, type, title };
|
|
318
|
+
return client.addTestRun(undefined, {
|
|
319
|
+
...stripExampleFromTitle(title),
|
|
320
|
+
test_id: testId,
|
|
321
|
+
files: [file],
|
|
322
|
+
});
|
|
318
323
|
});
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
await Promise.all(promises);
|
|
322
324
|
|
|
325
|
+
await Promise.all(promises);
|
|
326
|
+
}
|
|
323
327
|
}
|
|
324
328
|
|
|
325
329
|
function getTestAndMessage(title) {
|
|
@@ -2,7 +2,6 @@ const chalk = require('chalk');
|
|
|
2
2
|
const crypto = require('crypto');
|
|
3
3
|
const os = require('os');
|
|
4
4
|
const path = require('path');
|
|
5
|
-
const { v4: uuidv4 } = require('uuid');
|
|
6
5
|
const fs = require('fs');
|
|
7
6
|
const { APP_PREFIX, STATUS: Status, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
|
|
8
7
|
const TestomatioClient = require('../client');
|
|
@@ -40,6 +39,8 @@ class PlaywrightReporter {
|
|
|
40
39
|
|
|
41
40
|
const { title } = test;
|
|
42
41
|
|
|
42
|
+
let testId = getTestomatIdFromTestTitle(`${title} ${test.tags?.join(' ')}`);
|
|
43
|
+
|
|
43
44
|
const { error, duration } = result;
|
|
44
45
|
|
|
45
46
|
const suite_title = test.parent ? test.parent?.title : path.basename(test?.location?.file);
|
|
@@ -57,12 +58,10 @@ class PlaywrightReporter {
|
|
|
57
58
|
const manuallyAttachedArtifacts = services.artifacts.get(fullTestTitle);
|
|
58
59
|
const keyValues = services.keyValues.get(fullTestTitle);
|
|
59
60
|
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
const reportTestPromise = this.client.addTestRun(checkStatus(result.status), {
|
|
63
|
-
rid,
|
|
61
|
+
const reportTestPromise = this.client
|
|
62
|
+
.addTestRun(checkStatus(result.status), {
|
|
64
63
|
error,
|
|
65
|
-
test_id:
|
|
64
|
+
test_id: testId,
|
|
66
65
|
suite_title,
|
|
67
66
|
title,
|
|
68
67
|
steps: steps.join('\n'),
|
|
@@ -71,16 +70,20 @@ class PlaywrightReporter {
|
|
|
71
70
|
manuallyAttachedArtifacts,
|
|
72
71
|
meta: keyValues,
|
|
73
72
|
file: test.location?.file,
|
|
74
|
-
})
|
|
73
|
+
})
|
|
74
|
+
.then(pipes => {
|
|
75
|
+
testId = pipes?.filter(p => p.pipe.includes('Testomatio'))[0]?.result?.data?.test_id;
|
|
75
76
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
77
|
+
this.uploads.push({
|
|
78
|
+
testId,
|
|
79
|
+
title,
|
|
80
|
+
suite_title,
|
|
81
|
+
files: result.attachments.filter(a => a.body || a.path),
|
|
82
|
+
file: test.location?.file,
|
|
83
|
+
});
|
|
84
|
+
// remove empty uploads
|
|
85
|
+
this.uploads = this.uploads.filter(upload => upload.files.length);
|
|
86
|
+
});
|
|
84
87
|
|
|
85
88
|
reportTestPromises.push(reportTestPromise);
|
|
86
89
|
}
|
|
@@ -112,7 +115,7 @@ class PlaywrightReporter {
|
|
|
112
115
|
const promises = [];
|
|
113
116
|
|
|
114
117
|
for (const upload of this.uploads) {
|
|
115
|
-
const {
|
|
118
|
+
const { title, testId, suite_title } = upload;
|
|
116
119
|
|
|
117
120
|
const files = upload.files.map(attachment => ({
|
|
118
121
|
path: this.#getArtifactPath(attachment),
|
|
@@ -122,10 +125,11 @@ class PlaywrightReporter {
|
|
|
122
125
|
|
|
123
126
|
promises.push(
|
|
124
127
|
this.client.addTestRun(undefined, {
|
|
125
|
-
|
|
128
|
+
test_id: testId,
|
|
126
129
|
title,
|
|
130
|
+
suite_title,
|
|
127
131
|
files,
|
|
128
|
-
file,
|
|
132
|
+
file: upload.file,
|
|
129
133
|
}),
|
|
130
134
|
);
|
|
131
135
|
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const { TestomatioClient } = require('../client');
|
|
3
|
+
const { STATUS } = require('../constants');
|
|
4
|
+
const { getTestomatIdFromTestTitle } = require('../utils/utils');
|
|
5
|
+
const debug = require('debug')('@testomatio/reporter:adapter:vitest');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {import('../../types').VitestTest} VitestTest
|
|
9
|
+
* @typedef {import('../../types').VitestTestFile} VitestTestFile
|
|
10
|
+
* @typedef {import('../../types').VitestSuite} VitestSuite
|
|
11
|
+
* @typedef {import('../../types').VitestTestLogs} VitestTestLogs
|
|
12
|
+
* @typedef {import('../../vitest.types').ErrorWithDiff} ErrorWithDiff
|
|
13
|
+
* @typedef {typeof import('../constants').STATUS} STATUS
|
|
14
|
+
* @typedef {import('../../types').TestData} TestData
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
class VitestReporter {
|
|
18
|
+
constructor(config = {}) {
|
|
19
|
+
this.client = new TestomatioClient({ apiKey: config?.apiKey });
|
|
20
|
+
/**
|
|
21
|
+
* @type {(TestData & {status: string})[]} tests
|
|
22
|
+
*/
|
|
23
|
+
this.tests = [];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// on run start
|
|
27
|
+
onInit() {
|
|
28
|
+
this.client.createRun();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {VitestTestFile[] | undefined} files // array with results;
|
|
33
|
+
* @param {unknown[] | undefined} errors // errors does not contain errors from tests; probably its testrunner errors
|
|
34
|
+
*/
|
|
35
|
+
async onFinished(files, errors) {
|
|
36
|
+
if (!files || !files.length) console.info('No tests executed');
|
|
37
|
+
|
|
38
|
+
files.forEach(file => {
|
|
39
|
+
// task could be test or suite
|
|
40
|
+
file.tasks.forEach(taskOrSuite => {
|
|
41
|
+
if (taskOrSuite.type === 'test') {
|
|
42
|
+
const test = taskOrSuite;
|
|
43
|
+
this.tests.push(this.#getDataFromTest(test));
|
|
44
|
+
} else if (taskOrSuite.type === 'suite') {
|
|
45
|
+
const suite = taskOrSuite;
|
|
46
|
+
this.#processTasksOfSuite(suite);
|
|
47
|
+
} else {
|
|
48
|
+
throw new Error('Unprocessed case. Unknown task type');
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
debug(this.tests.length, 'tests collected');
|
|
54
|
+
|
|
55
|
+
// send tests to Testomat.io
|
|
56
|
+
for (const test of this.tests) {
|
|
57
|
+
await this.client.addTestRun(test.status, test);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.log('finished');
|
|
61
|
+
if (errors.length) console.error('Vitest adapter errors:', errors);
|
|
62
|
+
|
|
63
|
+
await this.client.updateRunStatus(getRunStatusFromResults(files));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* non-used listeners
|
|
67
|
+
onUserConsoleLog(log) {}
|
|
68
|
+
onPathsCollected(paths) {} // paths array to files with tests
|
|
69
|
+
onCollected(files) {} // files array with tests (but without results)
|
|
70
|
+
onTaskUpdate(packs) {} // some updates come here on afterAll block execution
|
|
71
|
+
onTestRemoved(trigger) {}
|
|
72
|
+
onWatcherStart(files, errors) {}
|
|
73
|
+
onWatcherRerun(files, trigger) {}
|
|
74
|
+
onServerRestart(reason) {}
|
|
75
|
+
onProcessTimeout() {}
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Recursively gets all tasks from suite and pushes them to "tests" array
|
|
80
|
+
*
|
|
81
|
+
* @param {VitestSuite} suite
|
|
82
|
+
*/
|
|
83
|
+
#processTasksOfSuite(suite) {
|
|
84
|
+
suite.tasks.forEach(taskOrSuite => {
|
|
85
|
+
if (taskOrSuite.type === 'test') {
|
|
86
|
+
const test = taskOrSuite;
|
|
87
|
+
this.tests.push(this.#getDataFromTest(test));
|
|
88
|
+
} else if (taskOrSuite.type === 'suite') {
|
|
89
|
+
const theSuite = taskOrSuite;
|
|
90
|
+
this.#processTasksOfSuite(theSuite);
|
|
91
|
+
} else {
|
|
92
|
+
throw new Error('Unprocessed case. Unknown task type');
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Processes task and returns test data ready to be sent to Testomat.io
|
|
99
|
+
*
|
|
100
|
+
* @param {VitestTest} test
|
|
101
|
+
*
|
|
102
|
+
* @returns {TestData & {status: string}}
|
|
103
|
+
*/
|
|
104
|
+
#getDataFromTest(test) {
|
|
105
|
+
return {
|
|
106
|
+
error: test.result?.errors ? test.result.errors[0] : undefined,
|
|
107
|
+
file: test.file.name,
|
|
108
|
+
logs: test.logs ? transformLogsToString(test.logs) : '',
|
|
109
|
+
meta: test.meta,
|
|
110
|
+
status: getTestStatus(test),
|
|
111
|
+
suite_title: test.suite.name || test.file?.name,
|
|
112
|
+
test_id: getTestomatIdFromTestTitle(test.name),
|
|
113
|
+
time: test.result?.duration || 0,
|
|
114
|
+
title: test.name,
|
|
115
|
+
// testomatio functions (artifacts, logs, steps, meta) are not supported
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Returns run status based on test results
|
|
122
|
+
*
|
|
123
|
+
* @param {VitestTestFile[]} files
|
|
124
|
+
* @returns {'passed' | 'failed' | 'finished'}
|
|
125
|
+
*/
|
|
126
|
+
function getRunStatusFromResults(files) {
|
|
127
|
+
/**
|
|
128
|
+
* @type {'passed' | 'failed' | 'finished'}
|
|
129
|
+
*/
|
|
130
|
+
let status = 'finished'; // default status (if no failed or passed tests)
|
|
131
|
+
|
|
132
|
+
files.forEach(file => {
|
|
133
|
+
// search for failed tests
|
|
134
|
+
file.tasks.forEach(taskOrSuite => {
|
|
135
|
+
if (taskOrSuite.result?.state === 'fail') {
|
|
136
|
+
status = 'failed'; // set status to failed if any test failed
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// if there are no failed tests > search for passed tests
|
|
141
|
+
if (status !== 'failed') {
|
|
142
|
+
file.tasks.forEach(taskOrSuite => {
|
|
143
|
+
if (taskOrSuite.result?.state === 'pass') {
|
|
144
|
+
status = 'passed'; // set status to passed if any test passed (and there are no failed tests)
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
return status;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Returns test status in Testomat.io format
|
|
155
|
+
*
|
|
156
|
+
* @param {VitestTest} test
|
|
157
|
+
* @returns 'passed' | 'failed' | 'skipped'
|
|
158
|
+
*/
|
|
159
|
+
function getTestStatus(test) {
|
|
160
|
+
if (test.result?.state === 'fail') return STATUS.FAILED;
|
|
161
|
+
if (test.result?.state === 'pass') return STATUS.PASSED;
|
|
162
|
+
if (!test.result && test.mode === 'skip') return STATUS.SKIPPED;
|
|
163
|
+
console.error(chalk.red('Unprocessed case for defining test status. Contact dev team. Test:'), test);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* @param {VitestTestLogs[]} logs
|
|
168
|
+
* @returns string
|
|
169
|
+
*/
|
|
170
|
+
function transformLogsToString(logs) {
|
|
171
|
+
if (!logs) return '';
|
|
172
|
+
let logsStr = '';
|
|
173
|
+
logs.forEach(log => {
|
|
174
|
+
if (log.type === 'stdout') logsStr += `${log.content}\n`;
|
|
175
|
+
if (log.type === 'stderr') logsStr += `${chalk.red(log.content)}\n`;
|
|
176
|
+
});
|
|
177
|
+
return logsStr;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
module.exports.VitestReporter = VitestReporter;
|
|
181
|
+
module.exports.default = VitestReporter;
|
|
182
|
+
module.exports = VitestReporter;
|
package/lib/bin/reportXml.js
CHANGED
|
@@ -44,10 +44,15 @@ program
|
|
|
44
44
|
|
|
45
45
|
let timeoutTimer;
|
|
46
46
|
if (opts.timelimit) {
|
|
47
|
-
timeoutTimer = setTimeout(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
timeoutTimer = setTimeout(
|
|
48
|
+
() => {
|
|
49
|
+
console.log(
|
|
50
|
+
`⚠️ Reached timeout of ${opts.timelimit}s. Exiting... (Exit code is 0 to not fail the pipeline)`,
|
|
51
|
+
);
|
|
52
|
+
process.exit(0);
|
|
53
|
+
},
|
|
54
|
+
parseInt(opts.timelimit, 10) * 1000,
|
|
55
|
+
);
|
|
51
56
|
}
|
|
52
57
|
|
|
53
58
|
try {
|
package/lib/client.js
CHANGED
|
@@ -123,7 +123,7 @@ class Client {
|
|
|
123
123
|
if (isTestShouldBeExculedFromReport(testData)) return [];
|
|
124
124
|
|
|
125
125
|
if (status === STATUS.SKIPPED && process.env.TESTOMATIO_EXCLUDE_SKIPPED) {
|
|
126
|
-
debug('Skipping test from report', testData?.title)
|
|
126
|
+
debug('Skipping test from report', testData?.title);
|
|
127
127
|
return []; // do not log skipped tests
|
|
128
128
|
}
|
|
129
129
|
|
|
@@ -133,10 +133,12 @@ class Client {
|
|
|
133
133
|
suite_title: 'Unknown suite',
|
|
134
134
|
};
|
|
135
135
|
|
|
136
|
+
/**
|
|
137
|
+
* @type {TestData}
|
|
138
|
+
*/
|
|
136
139
|
const {
|
|
137
|
-
rid,
|
|
138
140
|
error = null,
|
|
139
|
-
time =
|
|
141
|
+
time = 0,
|
|
140
142
|
example = null,
|
|
141
143
|
files = [],
|
|
142
144
|
filesBuffers = [],
|
|
@@ -185,7 +187,6 @@ class Client {
|
|
|
185
187
|
this.totalUploaded += artifacts.length;
|
|
186
188
|
|
|
187
189
|
const data = {
|
|
188
|
-
rid,
|
|
189
190
|
files,
|
|
190
191
|
steps,
|
|
191
192
|
status,
|
|
@@ -198,7 +199,7 @@ class Client {
|
|
|
198
199
|
suite_id,
|
|
199
200
|
test_id,
|
|
200
201
|
message,
|
|
201
|
-
run_time: parseFloat(time),
|
|
202
|
+
run_time: typeof time === 'number' ? time : parseFloat(time),
|
|
202
203
|
artifacts,
|
|
203
204
|
meta,
|
|
204
205
|
};
|
|
@@ -224,7 +225,8 @@ class Client {
|
|
|
224
225
|
/**
|
|
225
226
|
*
|
|
226
227
|
* Updates the status of the current test run and finishes the run.
|
|
227
|
-
* @param {
|
|
228
|
+
* @param {'passed' | 'failed' | 'finished'} status - The status of the current test run.
|
|
229
|
+
* Must be one of "passed", "failed", or "finished"
|
|
228
230
|
* @param {boolean} [isParallel] - Whether the current test run was executed in parallel with other tests.
|
|
229
231
|
* @returns {Promise<any>} - A Promise that resolves when finishes the run.
|
|
230
232
|
*/
|
|
@@ -286,13 +288,22 @@ class Client {
|
|
|
286
288
|
if (!message) message = error.message;
|
|
287
289
|
if (error.inspect) message = error.inspect() || '';
|
|
288
290
|
|
|
289
|
-
let stack =
|
|
291
|
+
let stack = '';
|
|
292
|
+
if (error.name) stack += `${chalk.red(error.name)}`;
|
|
293
|
+
if (error.operator) stack += ` (${chalk.red(error.operator)})`;
|
|
294
|
+
// add new line if something was added to stack
|
|
295
|
+
if (stack) stack += ': ';
|
|
290
296
|
|
|
291
|
-
|
|
292
|
-
|
|
297
|
+
stack += `${message}\n`;
|
|
298
|
+
|
|
299
|
+
if (error.diff) {
|
|
300
|
+
stack += error.diff;
|
|
301
|
+
stack += '\n\n';
|
|
302
|
+
} else if (error.actual && error.expected && error.actual !== error.expected) {
|
|
303
|
+
// diffs for mocha, cypress, codeceptjs style
|
|
293
304
|
stack += `\n\n${chalk.bold.green('+ expected')} ${chalk.bold.red('- actual')}`;
|
|
294
|
-
stack += `\n${chalk.red(`- ${error.actual.toString().split('\n').join('\n- ')}`)}`;
|
|
295
305
|
stack += `\n${chalk.green(`+ ${error.expected.toString().split('\n').join('\n+ ')}`)}`;
|
|
306
|
+
stack += `\n${chalk.red(`- ${error.actual.toString().split('\n').join('\n- ')}`)}`;
|
|
296
307
|
stack += '\n\n';
|
|
297
308
|
}
|
|
298
309
|
|
|
@@ -365,3 +376,4 @@ function isTestShouldBeExculedFromReport(testData) {
|
|
|
365
376
|
}
|
|
366
377
|
|
|
367
378
|
module.exports = Client;
|
|
379
|
+
module.exports.TestomatioClient = Client;
|
package/lib/pipe/gitlab.js
CHANGED
|
@@ -79,13 +79,13 @@ class GitLabPipe {
|
|
|
79
79
|
let summary = `${this.hiddenCommentData}
|
|
80
80
|
|
|
81
81
|
| [](https://testomat.io) | ${statusEmoji(
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
runParams.status,
|
|
83
|
+
)} ${runParams.status.toUpperCase()} ${statusEmoji(runParams.status)} |
|
|
84
84
|
| --- | --- |
|
|
85
85
|
| Tests | ✔️ **${this.tests.length}** tests run |
|
|
86
86
|
| Summary | ${statusEmoji('failed')} **${failedCount}** failed; ${statusEmoji(
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
'passed',
|
|
88
|
+
)} **${passedCount}** passed; **${statusEmoji('skipped')}** ${skippedCount} skipped |
|
|
89
89
|
| Duration | 🕐 **${humanizeDuration(
|
|
90
90
|
parseInt(
|
|
91
91
|
this.tests.reduce((a, t) => a + (t.run_time || 0), 0),
|
package/lib/pipe/testomatio.js
CHANGED
|
@@ -339,9 +339,6 @@ class TestomatioPipe {
|
|
|
339
339
|
addTest(data) {
|
|
340
340
|
if (!this.isEnabled) return;
|
|
341
341
|
if (!this.runId) return;
|
|
342
|
-
|
|
343
|
-
// add test ID + run ID
|
|
344
|
-
data.rid = `${this.runId}-${data.rid}`;
|
|
345
342
|
data.api_key = this.apiKey;
|
|
346
343
|
data.create = this.createNewTests;
|
|
347
344
|
|
package/lib/utils/utils.js
CHANGED
|
@@ -248,7 +248,7 @@ const foundedTestLog = (app, tests) => {
|
|
|
248
248
|
const humanize = text => {
|
|
249
249
|
// if there are no spaces, decamelize
|
|
250
250
|
if (!text.trim().includes(' ')) text = decamelize(text);
|
|
251
|
-
|
|
251
|
+
|
|
252
252
|
return text
|
|
253
253
|
.replace(/_./g, match => ` ${match.charAt(1).toUpperCase()}`)
|
|
254
254
|
.trim()
|
package/lib/xmlReader.js
CHANGED
|
@@ -460,7 +460,6 @@ function reduceTestCases(prev, item) {
|
|
|
460
460
|
// SpecFlow config
|
|
461
461
|
let { title, tags } = fetchProperties(item.type === 'ParameterizedMethod' ? item : testCaseItem);
|
|
462
462
|
let example = null;
|
|
463
|
-
const suiteTitle = reduceOptions.preferClassname ? testCaseItem.classname : item.name || testCaseItem.classname;
|
|
464
463
|
|
|
465
464
|
title ||= testCaseItem.name || testCaseItem.methodname || testCaseItem.classname;
|
|
466
465
|
tags ||= [];
|
|
@@ -494,7 +493,7 @@ function reduceTestCases(prev, item) {
|
|
|
494
493
|
run_time: parseFloat(testCaseItem.time || testCaseItem.duration) * 1000,
|
|
495
494
|
status,
|
|
496
495
|
title,
|
|
497
|
-
suite_title:
|
|
496
|
+
suite_title: reduceOptions.preferClassname ? testCaseItem.classname : item.name || testCaseItem.classname,
|
|
498
497
|
});
|
|
499
498
|
});
|
|
500
499
|
return prev;
|
|
@@ -528,4 +527,4 @@ function fetchProperties(item) {
|
|
|
528
527
|
.filter(p => p.name === 'Category')
|
|
529
528
|
.forEach(p => tags.push(p.value));
|
|
530
529
|
return { title, tags };
|
|
531
|
-
}
|
|
530
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testomatio/reporter",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0-beta-vitest",
|
|
4
4
|
"description": "Testomatio Reporter Client",
|
|
5
5
|
"main": "./lib/reporter.js",
|
|
6
6
|
"typings": "typings/index.d.ts",
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
"test:adapter:jasmine:example": "./tests/adapter/examples/jasmine/passReporterOpts.sh && jasmine './tests/adapter/examples/jasmine/index.test.js' --reporter=./../../../lib/adapter/jasmine.js",
|
|
56
56
|
"test:adapter:codecept:example": "codeceptjs run --config='./tests/adapter/examples/codecept/codecept.conf.js'",
|
|
57
57
|
"test:adapter:cucumber:example": "cd ./tests/adapter/examples/cucumber && npx cucumber-js",
|
|
58
|
+
"test:adapter:vitest:example": "vitest --config='./tests/adapter/examples/vitest/vitest.config.js'",
|
|
58
59
|
"test:storage": "npx mocha tests-storage/artifact-storage.test.js && npx mocha tests-storage/data-storage.test.js && TESTOMATIO_INTERCEPT_CONSOLE_LOGS=true npx mocha tests-storage/logger.test.js && npx mocha tests-storage/logger-2.test.js && npx mocha tests-storage/reporter-functions.test.js"
|
|
59
60
|
},
|
|
60
61
|
"devDependencies": {
|