@testomatio/reporter 2.0.1-beta.3 → 2.0.1-beta.5-timestamp
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 +335 -293
- package/lib/adapter/cucumber/current.js +203 -195
- package/lib/adapter/cucumber/legacy.js +155 -130
- package/lib/adapter/cucumber.js +16 -5
- package/lib/adapter/cypress-plugin/index.js +105 -91
- package/lib/adapter/jasmine.js +53 -54
- package/lib/adapter/jest.js +99 -97
- package/lib/adapter/mocha.js +141 -112
- package/lib/adapter/playwright.js +231 -199
- package/lib/adapter/vitest.js +149 -150
- package/lib/adapter/webdriver.js +121 -144
- package/lib/bin/cli.js +211 -229
- package/lib/bin/reportXml.js +52 -51
- package/lib/bin/startTest.js +95 -83
- package/lib/bin/uploadArtifacts.js +61 -56
- package/lib/client.js +465 -424
- package/lib/config.js +23 -18
- package/lib/constants.js +44 -50
- package/lib/data-storage.js +188 -216
- package/lib/junit-adapter/adapter.js +20 -17
- package/lib/junit-adapter/csharp.js +14 -28
- package/lib/junit-adapter/index.js +25 -27
- package/lib/junit-adapter/java.js +53 -41
- package/lib/junit-adapter/javascript.js +27 -30
- package/lib/junit-adapter/python.js +37 -38
- package/lib/junit-adapter/ruby.js +8 -11
- package/lib/output.js +52 -44
- package/lib/pipe/bitbucket.js +230 -223
- package/lib/pipe/csv.js +126 -113
- package/lib/pipe/debug.js +99 -118
- package/lib/pipe/github.js +213 -218
- package/lib/pipe/gitlab.js +206 -183
- package/lib/pipe/html.js +321 -258
- package/lib/pipe/index.js +66 -94
- package/lib/pipe/testomatio.js +474 -429
- package/lib/reporter-functions.js +26 -28
- package/lib/reporter.js +29 -34
- package/lib/services/artifacts.js +51 -55
- package/lib/services/index.js +12 -14
- package/lib/services/key-values.js +53 -56
- package/lib/services/logger.js +245 -226
- package/lib/template/testomatio.hbs +1366 -1026
- package/lib/uploader.js +364 -295
- package/lib/utils/pipe_utils.js +85 -89
- package/lib/utils/utils.js +307 -398
- package/lib/xmlReader.js +532 -525
- package/package.json +21 -64
- package/lib/adapter/codecept.d.ts +0 -2
- package/lib/adapter/cucumber/current.d.ts +0 -14
- package/lib/adapter/cucumber/legacy.d.ts +0 -0
- package/lib/adapter/cucumber.d.ts +0 -2
- package/lib/adapter/cypress-plugin/index.d.ts +0 -2
- package/lib/adapter/jasmine.d.ts +0 -11
- package/lib/adapter/jest.d.ts +0 -13
- package/lib/adapter/mocha.d.ts +0 -2
- package/lib/adapter/nightwatch.d.ts +0 -4
- package/lib/adapter/nightwatch.js +0 -80
- package/lib/adapter/playwright.d.ts +0 -14
- package/lib/adapter/vitest.d.ts +0 -35
- package/lib/adapter/webdriver.d.ts +0 -24
- package/lib/bin/cli.d.ts +0 -2
- package/lib/bin/reportXml.d.ts +0 -2
- package/lib/bin/startTest.d.ts +0 -2
- package/lib/bin/uploadArtifacts.d.ts +0 -2
- package/lib/client.d.ts +0 -76
- package/lib/config.d.ts +0 -1
- package/lib/constants.d.ts +0 -25
- package/lib/data-storage.d.ts +0 -34
- package/lib/junit-adapter/adapter.d.ts +0 -9
- package/lib/junit-adapter/csharp.d.ts +0 -5
- package/lib/junit-adapter/index.d.ts +0 -3
- package/lib/junit-adapter/java.d.ts +0 -5
- package/lib/junit-adapter/javascript.d.ts +0 -4
- package/lib/junit-adapter/python.d.ts +0 -5
- package/lib/junit-adapter/ruby.d.ts +0 -4
- package/lib/output.d.ts +0 -11
- package/lib/package.json +0 -3
- package/lib/pipe/bitbucket.d.ts +0 -25
- package/lib/pipe/csv.d.ts +0 -47
- package/lib/pipe/debug.d.ts +0 -29
- package/lib/pipe/github.d.ts +0 -30
- package/lib/pipe/gitlab.d.ts +0 -25
- package/lib/pipe/html.d.ts +0 -35
- package/lib/pipe/index.d.ts +0 -1
- package/lib/pipe/testomatio.d.ts +0 -71
- package/lib/replay.d.ts +0 -31
- package/lib/replay.js +0 -237
- package/lib/reporter-functions.d.ts +0 -34
- package/lib/reporter.d.ts +0 -232
- package/lib/services/artifacts.d.ts +0 -33
- package/lib/services/index.d.ts +0 -9
- package/lib/services/key-values.d.ts +0 -27
- package/lib/services/logger.d.ts +0 -64
- package/lib/uploader.d.ts +0 -60
- package/lib/utils/pipe_utils.d.ts +0 -41
- package/lib/utils/utils.d.ts +0 -54
- package/lib/xmlReader.d.ts +0 -92
- package/src/adapter/codecept.js +0 -373
- package/src/adapter/cucumber/current.js +0 -228
- package/src/adapter/cucumber/legacy.js +0 -158
- package/src/adapter/cucumber.js +0 -4
- package/src/adapter/cypress-plugin/index.js +0 -110
- package/src/adapter/jasmine.js +0 -60
- package/src/adapter/jest.js +0 -107
- package/src/adapter/mocha.cjs +0 -2
- package/src/adapter/mocha.js +0 -156
- package/src/adapter/nightwatch.js +0 -88
- package/src/adapter/playwright.js +0 -254
- package/src/adapter/vitest.js +0 -183
- package/src/adapter/webdriver.js +0 -142
- package/src/bin/cli.js +0 -348
- package/src/bin/reportXml.js +0 -77
- package/src/bin/startTest.js +0 -124
- package/src/bin/uploadArtifacts.js +0 -91
- package/src/client.js +0 -508
- package/src/config.js +0 -30
- package/src/constants.js +0 -53
- package/src/data-storage.js +0 -204
- package/src/junit-adapter/adapter.js +0 -23
- package/src/junit-adapter/csharp.js +0 -28
- package/src/junit-adapter/index.js +0 -28
- package/src/junit-adapter/java.js +0 -58
- package/src/junit-adapter/javascript.js +0 -31
- package/src/junit-adapter/python.js +0 -42
- package/src/junit-adapter/ruby.js +0 -10
- package/src/output.js +0 -57
- package/src/pipe/bitbucket.js +0 -252
- package/src/pipe/csv.js +0 -140
- package/src/pipe/debug.js +0 -119
- package/src/pipe/github.js +0 -232
- package/src/pipe/gitlab.js +0 -247
- package/src/pipe/html.js +0 -373
- package/src/pipe/index.js +0 -71
- package/src/pipe/testomatio.js +0 -504
- package/src/replay.js +0 -245
- package/src/reporter-functions.js +0 -55
- package/src/reporter.cjs_decprecated +0 -21
- package/src/reporter.js +0 -33
- package/src/services/artifacts.js +0 -59
- package/src/services/index.js +0 -13
- package/src/services/key-values.js +0 -59
- package/src/services/logger.js +0 -315
- package/src/template/emptyData.svg +0 -23
- package/src/template/testomatio.hbs +0 -1081
- package/src/uploader.js +0 -376
- package/src/utils/pipe_utils.js +0 -119
- package/src/utils/utils.js +0 -416
- package/src/xmlReader.js +0 -614
package/lib/adapter/vitest.js
CHANGED
|
@@ -1,123 +1,122 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const picocolors_1 = __importDefault(require("picocolors"));
|
|
8
|
-
const client_js_1 = require("../client.js");
|
|
9
|
-
const constants_js_1 = require("../constants.js");
|
|
10
|
-
const utils_js_1 = require("../utils/utils.js");
|
|
11
|
-
const debug_1 = __importDefault(require("debug"));
|
|
12
|
-
const debug = (0, debug_1.default)('@testomatio/reporter:adapter-jest');
|
|
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
|
+
|
|
13
7
|
/**
|
|
14
|
-
* @typedef {import('../../types
|
|
15
|
-
* @typedef {import('../../types
|
|
16
|
-
* @typedef {import('../../types
|
|
17
|
-
* @typedef {import('../../types
|
|
18
|
-
* @typedef {import('../../
|
|
19
|
-
* @typedef {typeof import('../constants
|
|
20
|
-
* @typedef {import('../../types
|
|
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
|
|
21
15
|
*/
|
|
16
|
+
|
|
22
17
|
class VitestReporter {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* @type {(TestData & {status: string})[]} tests
|
|
27
|
-
*/
|
|
28
|
-
this.tests = [];
|
|
29
|
-
}
|
|
30
|
-
// on run start
|
|
31
|
-
onInit() {
|
|
32
|
-
this.client.createRun();
|
|
33
|
-
}
|
|
18
|
+
constructor(config = {}) {
|
|
19
|
+
this.client = new TestomatioClient({ apiKey: config?.apiKey });
|
|
34
20
|
/**
|
|
35
|
-
* @
|
|
36
|
-
* @param {unknown[] | undefined} errors // errors does not contain errors from tests; probably its testrunner errors
|
|
21
|
+
* @type {(TestData & {status: string})[]} tests
|
|
37
22
|
*/
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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');
|
|
61
49
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
onCollected(files) {} // files array with tests (but without results)
|
|
71
|
-
onTaskUpdate(packs) {} // some updates come here on afterAll block execution
|
|
72
|
-
onTestRemoved(trigger) {}
|
|
73
|
-
onWatcherStart(files, errors) {}
|
|
74
|
-
onWatcherRerun(files, trigger) {}
|
|
75
|
-
onServerRestart(reason) {}
|
|
76
|
-
onProcessTimeout() {}
|
|
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
|
-
}
|
|
89
|
-
else if (taskOrSuite.type === 'suite') {
|
|
90
|
-
const theSuite = taskOrSuite;
|
|
91
|
-
this.#processTasksOfSuite(theSuite);
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
throw new Error('Unprocessed case. Unknown task type');
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Processes task and returns test data ready to be sent to Testomat.io
|
|
100
|
-
*
|
|
101
|
-
* @param {VitestTest} test
|
|
102
|
-
*
|
|
103
|
-
* @returns {TestData & {status: string}}
|
|
104
|
-
*/
|
|
105
|
-
#getDataFromTest(test) {
|
|
106
|
-
return {
|
|
107
|
-
error: test.result?.errors ? test.result.errors[0] : undefined,
|
|
108
|
-
file: test.file.name,
|
|
109
|
-
logs: test.logs ? transformLogsToString(test.logs) : '',
|
|
110
|
-
meta: test.meta,
|
|
111
|
-
status: getTestStatus(test),
|
|
112
|
-
suite_title: test.suite.name || test.file?.name,
|
|
113
|
-
test_id: (0, utils_js_1.getTestomatIdFromTestTitle)(test.name),
|
|
114
|
-
time: test.result?.duration || 0,
|
|
115
|
-
title: test.name,
|
|
116
|
-
// testomatio functions (artifacts, logs, steps, meta) are not supported
|
|
117
|
-
};
|
|
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);
|
|
118
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
|
+
}
|
|
119
118
|
}
|
|
120
|
-
|
|
119
|
+
|
|
121
120
|
/**
|
|
122
121
|
* Returns run status based on test results
|
|
123
122
|
*
|
|
@@ -125,28 +124,32 @@ exports.VitestReporter = VitestReporter;
|
|
|
125
124
|
* @returns {'passed' | 'failed' | 'finished'}
|
|
126
125
|
*/
|
|
127
126
|
function getRunStatusFromResults(files) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
// if there are no failed tests > search for passed tests
|
|
140
|
-
if (status !== 'failed') {
|
|
141
|
-
file.tasks.forEach(taskOrSuite => {
|
|
142
|
-
if (taskOrSuite.result?.state === 'pass') {
|
|
143
|
-
status = 'passed'; // set status to passed if any test passed (and there are no failed tests)
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
}
|
|
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
|
+
}
|
|
147
138
|
});
|
|
148
|
-
|
|
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;
|
|
149
151
|
}
|
|
152
|
+
|
|
150
153
|
/**
|
|
151
154
|
* Returns test status in Testomat.io format
|
|
152
155
|
*
|
|
@@ -154,30 +157,26 @@ function getRunStatusFromResults(files) {
|
|
|
154
157
|
* @returns 'passed' | 'failed' | 'skipped'
|
|
155
158
|
*/
|
|
156
159
|
function getTestStatus(test) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (!test.result && test.mode === 'skip')
|
|
162
|
-
return constants_js_1.STATUS.SKIPPED;
|
|
163
|
-
console.error(picocolors_1.default.red('Unprocessed case for defining test status. Contact dev team. Test:'), 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
164
|
}
|
|
165
|
+
|
|
165
166
|
/**
|
|
166
167
|
* @param {VitestTestLogs[]} logs
|
|
167
168
|
* @returns string
|
|
168
169
|
*/
|
|
169
170
|
function transformLogsToString(logs) {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
logsStr += `${picocolors_1.default.red(log.content)}\n`;
|
|
178
|
-
});
|
|
179
|
-
return logsStr;
|
|
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;
|
|
180
178
|
}
|
|
181
|
-
module.exports = VitestReporter;
|
|
182
179
|
|
|
183
180
|
module.exports.VitestReporter = VitestReporter;
|
|
181
|
+
module.exports.default = VitestReporter;
|
|
182
|
+
module.exports = VitestReporter;
|
package/lib/adapter/webdriver.js
CHANGED
|
@@ -1,155 +1,132 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
// eslint-disable-next-line global-require, import/no-extraneous-dependencies
|
|
2
|
+
const WDIOReporter = require('@wdio/reporter').default;
|
|
3
|
+
const TestomatClient = require('../client');
|
|
4
|
+
const { getTestomatIdFromTestTitle, fileSystem } = require('../utils/utils');
|
|
5
|
+
const { services } = require('../services');
|
|
6
|
+
const { TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
|
|
7
|
+
|
|
8
|
+
class WebdriverReporter extends WDIOReporter {
|
|
9
|
+
constructor(options) {
|
|
10
|
+
super(options);
|
|
11
|
+
|
|
12
|
+
this.client = new TestomatClient({ apiKey: options?.apiKey });
|
|
13
|
+
options = Object.assign(options, { stdout: true });
|
|
14
|
+
|
|
15
|
+
this._addTestPromises = [];
|
|
16
|
+
|
|
17
|
+
this._isSynchronising = false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
onRunnerStart() {
|
|
21
|
+
// clear dir with artifacts/logs
|
|
22
|
+
fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
onTestStart(test) {
|
|
26
|
+
services.setContext(test.fullTitle);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get isSynchronised() {
|
|
30
|
+
return this._isSynchronising === false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async onRunnerEnd() {
|
|
34
|
+
this._isSynchronising = true;
|
|
35
|
+
|
|
36
|
+
await Promise.all(this._addTestPromises);
|
|
37
|
+
|
|
38
|
+
this._isSynchronising = false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
onTestEnd(test) {
|
|
42
|
+
test.suite = test.parent;
|
|
43
|
+
const logs = getTestLogs(test.fullTitle);
|
|
44
|
+
// TODO: FIX: artifacts for some reason leads to empty report on Testomat.io
|
|
45
|
+
// const artifacts = services.artifacts.get(test.fullTitle);
|
|
46
|
+
// const keyValues = services.keyValues.get(test.fullTitle);
|
|
47
|
+
test.logs = logs;
|
|
48
|
+
// test.artifacts = artifacts;
|
|
49
|
+
// test.meta = keyValues;
|
|
50
|
+
this._addTestPromises.push(this.addTest(test));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// wdio-cucumber does not trigger onTestEnd hook, thus, using this one
|
|
54
|
+
onSuiteEnd(scerario) {
|
|
55
|
+
if (scerario.type === 'scenario') {
|
|
56
|
+
this._addTestPromises.push(this.addBddScenario(scerario));
|
|
7
57
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
const
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
options = Object.assign(options, { stdout: true });
|
|
49
|
-
this._addTestPromises = [];
|
|
50
|
-
this._isSynchronising = false;
|
|
51
|
-
// NOTE: new functionality; may break everything
|
|
52
|
-
this.client.createRun();
|
|
53
|
-
}
|
|
54
|
-
get isSynchronised() {
|
|
55
|
-
return this._isSynchronising === false;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
*
|
|
59
|
-
* @param {RunnerStats} runData
|
|
60
|
-
*/
|
|
61
|
-
async onRunnerEnd(runData) {
|
|
62
|
-
this._isSynchronising = true;
|
|
63
|
-
await Promise.all(this._addTestPromises);
|
|
64
|
-
this._isSynchronising = false;
|
|
65
|
-
// NOTE: new functionality; may break everything
|
|
66
|
-
// also this may require additional status mapping
|
|
67
|
-
await this.client.updateRunStatus(runData.failures ? 'failed' : 'passed');
|
|
68
|
-
}
|
|
69
|
-
onRunnerStart() {
|
|
70
|
-
// clear dir with artifacts/logs
|
|
71
|
-
//
|
|
72
|
-
utils_js_1.fileSystem.clearDir(constants_js_1.TESTOMAT_TMP_STORAGE_DIR);
|
|
73
|
-
}
|
|
74
|
-
onTestStart(test) {
|
|
75
|
-
index_js_1.services.setContext(test.fullTitle);
|
|
76
|
-
}
|
|
77
|
-
onTestEnd(test) {
|
|
78
|
-
test.suite = test.parent;
|
|
79
|
-
const logs = getTestLogs(test.fullTitle);
|
|
80
|
-
// TODO: FIX: artifacts for some reason leads to empty report on Testomat.io
|
|
81
|
-
// const artifacts = services.artifacts.get(test.fullTitle);
|
|
82
|
-
// const keyValues = services.keyValues.get(test.fullTitle);
|
|
83
|
-
test.logs = logs;
|
|
84
|
-
// test.artifacts = artifacts;
|
|
85
|
-
// test.meta = keyValues;
|
|
86
|
-
this._addTestPromises.push(this.addTest(test));
|
|
87
|
-
}
|
|
88
|
-
// wdio-cucumber does not trigger onTestEnd hook, thus, using this one
|
|
89
|
-
onSuiteEnd(scerario) {
|
|
90
|
-
if (scerario.type === 'scenario') {
|
|
91
|
-
this._addTestPromises.push(this.addBddScenario(scerario));
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
async addTest(test) {
|
|
95
|
-
if (!this.client)
|
|
96
|
-
return;
|
|
97
|
-
const { title, _duration: duration, state, error, output } = test;
|
|
98
|
-
const testId = (0, utils_js_1.getTestomatIdFromTestTitle)(title);
|
|
99
|
-
const screenshotEndpoint = '/session/:sessionId/screenshot';
|
|
100
|
-
const screenshotsBuffers = output
|
|
101
|
-
.filter(el => el.endpoint === screenshotEndpoint && el.result && el.result.value)
|
|
102
|
-
.map(el => Buffer.from(el.result.value, 'base64'));
|
|
103
|
-
await this.client.addTestRun(state, {
|
|
104
|
-
rid: test.uid || '',
|
|
105
|
-
manuallyAttachedArtifacts: test.artifacts,
|
|
106
|
-
error,
|
|
107
|
-
logs: test.logs,
|
|
108
|
-
meta: test.meta,
|
|
109
|
-
title,
|
|
110
|
-
test_id: testId,
|
|
111
|
-
time: duration,
|
|
112
|
-
filesBuffers: screenshotsBuffers,
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* @param {import('../../types/types.js').WebdriverIOScenario} scenario
|
|
117
|
-
*/
|
|
118
|
-
addBddScenario(scenario) {
|
|
119
|
-
if (!this.client)
|
|
120
|
-
return;
|
|
121
|
-
const { title, _duration: duration } = scenario;
|
|
122
|
-
const testId = (0, utils_js_1.getTestomatIdFromTestTitle)(title || scenario.tags.map(tag => tag.name).join(' '));
|
|
123
|
-
let scenarioState = scenario.tests.every(test => test.state === 'passed') ? 'passed' : 'failed';
|
|
124
|
-
if (scenario.tests.every(test => test.state === 'skipped')) {
|
|
125
|
-
scenarioState = 'skipped';
|
|
126
|
-
}
|
|
127
|
-
const errors = scenario.tests
|
|
128
|
-
.filter(test => test.state === 'failed')
|
|
129
|
-
.map(test => test.error?.stack)
|
|
130
|
-
.filter(Boolean);
|
|
131
|
-
const error = errors.join('\n');
|
|
132
|
-
const tags = scenario.tags.map(tag => tag.name);
|
|
133
|
-
return this.client.addTestRun(scenarioState, {
|
|
134
|
-
error: error ? Error(error) : null,
|
|
135
|
-
title,
|
|
136
|
-
test_id: testId,
|
|
137
|
-
time: duration,
|
|
138
|
-
tags,
|
|
139
|
-
file: scenario.file,
|
|
140
|
-
// filesBuffers: screenshotsBuffers,
|
|
141
|
-
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async addTest(test) {
|
|
61
|
+
if (!this.client) return;
|
|
62
|
+
|
|
63
|
+
const { title, _duration: duration, state, error, output } = test;
|
|
64
|
+
|
|
65
|
+
const testId = getTestomatIdFromTestTitle(title);
|
|
66
|
+
|
|
67
|
+
const screenshotEndpoint = '/session/:sessionId/screenshot';
|
|
68
|
+
const screenshotsBuffers = output
|
|
69
|
+
.filter(el => el.endpoint === screenshotEndpoint && el.result && el.result.value)
|
|
70
|
+
.map(el => Buffer.from(el.result.value, 'base64'));
|
|
71
|
+
|
|
72
|
+
await this.client.addTestRun(state, {
|
|
73
|
+
rid: test.uid,
|
|
74
|
+
manuallyAttachedArtifacts: test.artifacts,
|
|
75
|
+
error,
|
|
76
|
+
logs: test.logs,
|
|
77
|
+
meta: test.meta,
|
|
78
|
+
title,
|
|
79
|
+
test_id: testId,
|
|
80
|
+
time: duration,
|
|
81
|
+
filesBuffers: screenshotsBuffers,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @param {import('../../types').WebdriverIOScenario} scenario
|
|
87
|
+
*/
|
|
88
|
+
addBddScenario(scenario) {
|
|
89
|
+
if (!this.client) return;
|
|
90
|
+
|
|
91
|
+
const { title, _duration: duration } = scenario;
|
|
92
|
+
|
|
93
|
+
const testId = getTestomatIdFromTestTitle(title || scenario.tags.map(tag => tag.name).join(' '));
|
|
94
|
+
|
|
95
|
+
let scenarioState = scenario.tests.every(test => test.state === 'passed') ? 'passed' : 'failed';
|
|
96
|
+
if (scenario.tests.every(test => test.state === 'skipped')) {
|
|
97
|
+
scenarioState = 'skipped';
|
|
142
98
|
}
|
|
99
|
+
const errors = scenario.tests
|
|
100
|
+
.filter(test => test.state === 'failed')
|
|
101
|
+
.map(test => test.error?.stack)
|
|
102
|
+
.filter(Boolean);
|
|
103
|
+
const error = errors.join('\n');
|
|
104
|
+
|
|
105
|
+
const tags = scenario.tags.map(tag => tag.name);
|
|
106
|
+
|
|
107
|
+
return this.client.addTestRun(scenarioState, {
|
|
108
|
+
rid: scenario.uid,
|
|
109
|
+
error: error ? Error(error) : null,
|
|
110
|
+
title,
|
|
111
|
+
test_id: testId,
|
|
112
|
+
time: duration,
|
|
113
|
+
tags,
|
|
114
|
+
file: scenario.file,
|
|
115
|
+
// filesBuffers: screenshotsBuffers,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
143
118
|
}
|
|
119
|
+
|
|
144
120
|
/**
|
|
145
121
|
*
|
|
146
122
|
* @param {*} fullTestTitle
|
|
147
123
|
* @returns string
|
|
148
124
|
*/
|
|
149
125
|
function getTestLogs(fullTestTitle) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
126
|
+
const logsArr = services.logger.getLogs(fullTestTitle);
|
|
127
|
+
// remove duplicates (for some reason, logs are duplicated several times)
|
|
128
|
+
const logs = logsArr ? Array.from(new Set(logsArr)).join('\n').trim() : '';
|
|
129
|
+
return logs;
|
|
154
130
|
}
|
|
131
|
+
|
|
155
132
|
module.exports = WebdriverReporter;
|