@testomatio/reporter 1.2.0-beta-4 → 1.2.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/README.md +27 -18
- package/lib/adapter/codecept.js +83 -11
- package/lib/adapter/cucumber/current.js +17 -10
- package/lib/adapter/cucumber/legacy.js +4 -3
- package/lib/adapter/cypress-plugin/index.js +51 -24
- package/lib/adapter/jest.js +47 -23
- package/lib/adapter/mocha.js +99 -45
- package/lib/adapter/playwright.js +94 -32
- package/lib/bin/startTest.js +8 -7
- package/lib/client.js +119 -62
- package/lib/config.js +34 -0
- package/lib/constants.js +9 -7
- package/lib/data-storage.js +203 -0
- package/lib/fileUploader.js +125 -46
- package/lib/junit-adapter/index.js +3 -2
- package/lib/pipe/gitlab.js +1 -1
- package/lib/pipe/html.js +248 -246
- package/lib/pipe/testomatio.js +73 -50
- package/lib/reporter-functions.js +46 -0
- package/lib/reporter.js +11 -10
- package/lib/services/artifacts.js +57 -0
- package/lib/services/index.js +13 -0
- package/lib/services/key-values.js +58 -0
- package/lib/{logger.js → services/logger.js} +58 -66
- package/lib/utils/utils.js +28 -2
- package/lib/xmlReader.js +106 -105
- package/package.json +5 -3
- package/lib/_ArtifactStorageOld.js +0 -142
- package/lib/artifactStorage.js +0 -25
- package/lib/dataStorage.js +0 -232
package/README.md
CHANGED
|
@@ -65,23 +65,16 @@ yarn add @testomatio/reporter --dev
|
|
|
65
65
|
|
|
66
66
|
### 1️⃣ Attach Reporter to the Test Runner
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
* #### [JUnit](./docs/junit.md#junit)
|
|
79
|
-
* #### [NUnit](./docs/junit.md#nunit)
|
|
80
|
-
* #### [PyTest](./docs/junit.md#pytest)
|
|
81
|
-
* #### [PHPUnit](./docs/junit.md#phpunit)
|
|
82
|
-
* #### [Protractor](./docs/frameworks.md#protractor)
|
|
83
|
-
|
|
84
|
-
or any [other via JUnit](./docs/junit.md) report....
|
|
68
|
+
| | | |
|
|
69
|
+
|--|--|--|
|
|
70
|
+
| [Playwright](./docs/frameworks.md#playwright) | [CodeceptJS](./docs/frameworks.md#CodeceptJS) | [Cypress](./docs/frameworks.md#Cypress) |
|
|
71
|
+
| [Jest](./docs/frameworks.md#Jest) | [Mocha](./docs/frameworks.md#Mocha) | [WebDriverIO](./docs/frameworks.md#WebDriverIO) |
|
|
72
|
+
| [TestCafe](./docs/frameworks.md#TestCafe) | [Detox](./docs/frameworks.md#Detox) | [Codeception](https://github.com/testomatio/php-reporter) |
|
|
73
|
+
| [Newman (Postman)](./docs/frameworks.md#Newman) | [JUnit](./docs/junit.md#junit) | [NUnit](./docs/junit.md#nunit) |
|
|
74
|
+
| [PyTest](./docs/junit.md#pytest) | [PHPUnit](./docs/junit.md#phpunit) | [Protractor](./docs/frameworks.md#protractor) |
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
or **any [other via JUnit](./docs/junit.md)** report....
|
|
85
78
|
|
|
86
79
|
### 2️⃣ Configure Reports
|
|
87
80
|
|
|
@@ -104,7 +97,21 @@ GitHub report published as a comment to Pull Request:
|
|
|
104
97
|
|
|
105
98
|
Intercept your logger messages or log anything with our [Logger](./docs/logger.md) (_work in progress_).
|
|
106
99
|
|
|
107
|
-
### 5️⃣
|
|
100
|
+
### 5️⃣ TESTOMATIO HTML Report
|
|
101
|
+
|
|
102
|
+
Testomatio now features a custom reporting tool that allows you to generate a standalone HTML/CSS report for easy visualization of your test runs. This mode provides a quick way to gain a clear and visually appealing overview of your test execution.
|
|
103
|
+
|
|
104
|
+
With our reporter, you can:
|
|
105
|
+
|
|
106
|
+
- Easily analyze the results of your test runs in a convenient HTML format.
|
|
107
|
+
- Visualize data on successful and failed tests, including statistics and error details.
|
|
108
|
+
- Quickly share reports with your team members or stakeholders.
|
|
109
|
+
|
|
110
|
+

|
|
111
|
+
|
|
112
|
+
Learn more about generating HTML reports [here](./docs/pipes/html.md)
|
|
113
|
+
|
|
114
|
+
### 6️⃣ Add to CI Pipeline
|
|
108
115
|
|
|
109
116
|
After you tested reporter locally add it to your CI pipeline.
|
|
110
117
|
|
|
@@ -125,6 +132,7 @@ Bring this reporter on CI and never lose test results again!
|
|
|
125
132
|
* [GitHub](./docs/pipes/github.md)
|
|
126
133
|
* [Gitlab](./docs/pipes/gitlab.md)
|
|
127
134
|
* [CSV](./docs/pipes/csv.md)
|
|
135
|
+
* [HTML report](./docs/pipes/html.md)
|
|
128
136
|
* 📓 [JUnit](./docs/junit.md)
|
|
129
137
|
* 🗄️ [Artifacts](./docs/artifacts.md)
|
|
130
138
|
* 🔂 [Workflows](./docs/workflows.md)
|
|
@@ -153,3 +161,4 @@ To print all reporter logs of a specific pipe:
|
|
|
153
161
|
```
|
|
154
162
|
DEBUG=@testomatio/reporter:pipe:github
|
|
155
163
|
```
|
|
164
|
+
|
package/lib/adapter/codecept.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
const debug = require('debug')('@testomatio/reporter:adapter:codeceptjs');
|
|
2
2
|
const chalk = require('chalk');
|
|
3
3
|
const TestomatClient = require('../client');
|
|
4
|
-
const { STATUS, APP_PREFIX,
|
|
4
|
+
const { STATUS, APP_PREFIX, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
|
|
5
5
|
const upload = require('../fileUploader');
|
|
6
6
|
const { parseTest: getIdFromTestTitle, fileSystem } = require('../utils/utils');
|
|
7
|
+
const { services } = require('../services');
|
|
7
8
|
|
|
8
9
|
if (!global.codeceptjs) {
|
|
9
10
|
// eslint-disable-next-line global-require, import/no-extraneous-dependencies
|
|
@@ -51,21 +52,56 @@ function CodeceptReporter(config) {
|
|
|
51
52
|
|
|
52
53
|
recorder.startUnlessRunning();
|
|
53
54
|
|
|
54
|
-
global.testomatioRunningEnvironment = 'codeceptjs';
|
|
55
|
-
|
|
56
55
|
// Listening to events
|
|
57
56
|
event.dispatcher.on(event.all.before, () => {
|
|
58
57
|
// clear tmp dir
|
|
59
|
-
fileSystem.clearDir(
|
|
58
|
+
fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
|
|
60
59
|
|
|
61
|
-
recorder.add('Creating new run', () =>
|
|
60
|
+
// recorder.add('Creating new run', () => );
|
|
61
|
+
client.createRun();
|
|
62
62
|
videos = [];
|
|
63
63
|
traces = [];
|
|
64
64
|
|
|
65
65
|
if (!global.testomatioDataStore) global.testomatioDataStore = {};
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
let hookSteps = [];
|
|
69
|
+
let suiteHookRunning = false;
|
|
70
|
+
|
|
71
|
+
event.dispatcher.on(event.suite.before, suite => {
|
|
72
|
+
suiteHookRunning = true;
|
|
73
|
+
hookSteps = [];
|
|
74
|
+
global.testomatioDataStore.steps = [];
|
|
75
|
+
|
|
76
|
+
services.setContext(suite.fullTitle());
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
event.dispatcher.on(event.suite.after, () => {
|
|
80
|
+
services.setContext(null);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
event.dispatcher.on(event.hook.started, () => {
|
|
84
|
+
// global.testomatioDataStore.steps = [];
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
event.dispatcher.on(event.hook.passed, () => {
|
|
88
|
+
if (suiteHookRunning) {
|
|
89
|
+
hookSteps.push(...global.testomatioDataStore.steps);
|
|
90
|
+
services.setContext(null);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
event.dispatcher.on(event.hook.failed, () => {
|
|
95
|
+
if (suiteHookRunning) {
|
|
96
|
+
hookSteps.push(...global.testomatioDataStore.steps);
|
|
97
|
+
services.setContext(null);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
event.dispatcher.on(event.test.before, test => {
|
|
102
|
+
suiteHookRunning = false;
|
|
103
|
+
global.testomatioDataStore.steps = [];
|
|
104
|
+
|
|
69
105
|
recorder.add(() => {
|
|
70
106
|
currentMetaStep = [];
|
|
71
107
|
// output.reset();
|
|
@@ -76,11 +112,16 @@ function CodeceptReporter(config) {
|
|
|
76
112
|
if (!global.testomatioDataStore) global.testomatioDataStore = {};
|
|
77
113
|
// reset steps
|
|
78
114
|
global.testomatioDataStore.steps = [];
|
|
115
|
+
|
|
116
|
+
services.setContext(test.fullTitle());
|
|
79
117
|
});
|
|
80
118
|
|
|
81
119
|
event.dispatcher.on(event.test.started, test => {
|
|
120
|
+
services.setContext(test.fullTitle());
|
|
121
|
+
|
|
82
122
|
testTimeMap[test.id] = Date.now();
|
|
83
123
|
if (global.testomatioDataStore) global.testomatioDataStore.currentlyRunningTestId = getIdFromTestTitle(test.title);
|
|
124
|
+
// start logging
|
|
84
125
|
});
|
|
85
126
|
|
|
86
127
|
event.dispatcher.on(event.all.result, async () => {
|
|
@@ -104,6 +145,12 @@ function CodeceptReporter(config) {
|
|
|
104
145
|
}
|
|
105
146
|
const testId = parseTest(tags);
|
|
106
147
|
const testObj = getTestAndMessage(title);
|
|
148
|
+
|
|
149
|
+
const logs = getTestLogs(test);
|
|
150
|
+
const manuallyAttachedArtifacts = services.artifacts.get(test.fullTitle());
|
|
151
|
+
const keyValues = services.keyValues.get(test.fullTitle());
|
|
152
|
+
services.setContext(null);
|
|
153
|
+
|
|
107
154
|
client.addTestRun(STATUS.PASSED, {
|
|
108
155
|
...stripExampleFromTitle(title),
|
|
109
156
|
suite_title: test.parent && test.parent.title,
|
|
@@ -111,6 +158,9 @@ function CodeceptReporter(config) {
|
|
|
111
158
|
time: getDuration(test),
|
|
112
159
|
steps: global.testomatioDataStore.steps.join('\n') || null,
|
|
113
160
|
test_id: testId,
|
|
161
|
+
logs,
|
|
162
|
+
manuallyAttachedArtifacts,
|
|
163
|
+
meta: keyValues,
|
|
114
164
|
});
|
|
115
165
|
// output.stop();
|
|
116
166
|
});
|
|
@@ -147,14 +197,16 @@ function CodeceptReporter(config) {
|
|
|
147
197
|
failedTests.push(id || title);
|
|
148
198
|
let testId = parseTest(tags);
|
|
149
199
|
const testObj = getTestAndMessage(title);
|
|
150
|
-
if (error && error.stack && test.steps && test.steps.length) {
|
|
151
|
-
error.stack = test.steps[test.steps.length - 1].line();
|
|
152
|
-
}
|
|
153
200
|
|
|
154
201
|
const files = [];
|
|
155
202
|
if (artifacts.screenshot) files.push({ path: artifacts.screenshot, type: 'image/png' });
|
|
156
203
|
// todo: video must be uploaded later....
|
|
157
204
|
|
|
205
|
+
const logs = getTestLogs(test);
|
|
206
|
+
const manuallyAttachedArtifacts = services.artifacts.get(test.fullTitle());
|
|
207
|
+
const keyValues = services.keyValues.get(test.fullTitle());
|
|
208
|
+
services.setContext(null);
|
|
209
|
+
|
|
158
210
|
const reportTestPromise = client
|
|
159
211
|
.addTestRun(STATUS.FAILED, {
|
|
160
212
|
...stripExampleFromTitle(title),
|
|
@@ -165,6 +217,9 @@ function CodeceptReporter(config) {
|
|
|
165
217
|
time: getDuration(test),
|
|
166
218
|
files,
|
|
167
219
|
steps: global.testomatioDataStore?.steps?.join('\n') || null,
|
|
220
|
+
logs,
|
|
221
|
+
manuallyAttachedArtifacts,
|
|
222
|
+
meta: keyValues,
|
|
168
223
|
})
|
|
169
224
|
.then(pipes => {
|
|
170
225
|
testId = pipes.filter(p => p.pipe.includes('Testomatio'))[0]?.result?.data?.test_id;
|
|
@@ -233,7 +288,7 @@ function CodeceptReporter(config) {
|
|
|
233
288
|
currentMetaStep = metaSteps;
|
|
234
289
|
stepShift = 2 * shift;
|
|
235
290
|
|
|
236
|
-
const durationMs = +new Date() -
|
|
291
|
+
const durationMs = +new Date() - +stepStart;
|
|
237
292
|
let duration = '';
|
|
238
293
|
if (durationMs) {
|
|
239
294
|
duration = repeat(1) + chalk.grey(`(${durationMs}ms)`);
|
|
@@ -258,7 +313,7 @@ async function uploadAttachments(client, attachments, messagePrefix, attachmentT
|
|
|
258
313
|
if (attachments.length > 0) {
|
|
259
314
|
console.log(APP_PREFIX, `Attachments: ${messagePrefix} ${attachments.length} ${attachmentType}/-s ...`);
|
|
260
315
|
|
|
261
|
-
const promises = attachments.map(async
|
|
316
|
+
const promises = attachments.map(async attachment => {
|
|
262
317
|
const { testId, title, path, type } = attachment;
|
|
263
318
|
const file = { path, type, title };
|
|
264
319
|
return client.addTestRun(undefined, {
|
|
@@ -306,4 +361,21 @@ function repeat(num) {
|
|
|
306
361
|
return ''.padStart(num, ' ');
|
|
307
362
|
}
|
|
308
363
|
|
|
364
|
+
// TODO: think about moving to some common utils
|
|
365
|
+
function getTestLogs(test) {
|
|
366
|
+
const suiteLogsArr = services.logger.getLogs(test.parent.fullTitle());
|
|
367
|
+
const suiteLogs = suiteLogsArr ? suiteLogsArr.join('\n').trim() : '';
|
|
368
|
+
const testLogsArr = services.logger.getLogs(test.fullTitle());
|
|
369
|
+
const testLogs = testLogsArr ? testLogsArr.join('\n').trim() : '';
|
|
370
|
+
|
|
371
|
+
let logs = '';
|
|
372
|
+
if (suiteLogs) {
|
|
373
|
+
logs += `${chalk.bold('\t--- BeforeSuite ---')}\n${suiteLogs}`;
|
|
374
|
+
}
|
|
375
|
+
if (testLogs) {
|
|
376
|
+
logs += `\n${chalk.bold('\t--- Test ---')}\n${testLogs}`;
|
|
377
|
+
}
|
|
378
|
+
return logs;
|
|
379
|
+
}
|
|
380
|
+
|
|
309
381
|
module.exports = CodeceptReporter;
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
const { Formatter, formatterHelpers } = require('@cucumber/cucumber');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
4
|
const fs = require('fs');
|
|
5
|
-
const { STATUS,
|
|
5
|
+
const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../../constants');
|
|
6
6
|
const TestomatClient = require('../../client');
|
|
7
|
-
const logger = require('../../logger');
|
|
8
7
|
const { parseTest, fileSystem } = require('../../utils/utils');
|
|
8
|
+
const config = require('../../config');
|
|
9
|
+
const { services } = require('../../services');
|
|
9
10
|
|
|
10
11
|
const { GherkinDocumentParser, PickleParser } = formatterHelpers;
|
|
11
12
|
const { getGherkinScenarioLocationMap, getGherkinStepMap } = GherkinDocumentParser;
|
|
@@ -29,15 +30,13 @@ class CucumberReporter extends Formatter {
|
|
|
29
30
|
this.failures = [];
|
|
30
31
|
this.cases = [];
|
|
31
32
|
|
|
32
|
-
this.client = new TestomatClient({ apiKey: options.apiKey ||
|
|
33
|
+
this.client = new TestomatClient({ apiKey: options.apiKey || config.TESTOMATIO });
|
|
33
34
|
this.status = STATUS.PASSED;
|
|
34
|
-
|
|
35
|
-
global.testomatioRunningEnvironment = 'cucumber:current';
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
parseEnvelope(envelope) {
|
|
39
38
|
if (envelope.testRunStarted) {
|
|
40
|
-
fileSystem.clearDir(
|
|
39
|
+
fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
|
|
41
40
|
}
|
|
42
41
|
if (envelope.testCaseStarted && this.client) {
|
|
43
42
|
this.client.createRun();
|
|
@@ -49,9 +48,10 @@ class CucumberReporter extends Formatter {
|
|
|
49
48
|
|
|
50
49
|
onTestCaseStarted(testCaseStarted) {
|
|
51
50
|
const testCaseAttempt = this.eventDataCollector.getTestCaseAttempt(testCaseStarted.id);
|
|
52
|
-
const testId = getTestId(testCaseAttempt.pickle);
|
|
53
51
|
if (!global.testomatioDataStore) global.testomatioDataStore = {};
|
|
54
|
-
|
|
52
|
+
|
|
53
|
+
const testTitle = testCaseAttempt.pickle.name + testCaseAttempt.pickle.uri;
|
|
54
|
+
services.setContext(testTitle);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
onTestCaseFinished(testCaseFinished) {
|
|
@@ -109,7 +109,10 @@ class CucumberReporter extends Formatter {
|
|
|
109
109
|
|
|
110
110
|
if (!this.client) return;
|
|
111
111
|
|
|
112
|
-
const
|
|
112
|
+
const testTitle = testCaseAttempt.pickle.name + testCaseAttempt.pickle.uri;
|
|
113
|
+
const logs = services.logger.getLogs(testTitle).join('\n');
|
|
114
|
+
const artifacts = services.artifacts.get(testTitle);
|
|
115
|
+
const keyValues = services.keyValues.get(testTitle);
|
|
113
116
|
|
|
114
117
|
this.client.addTestRun(status, {
|
|
115
118
|
// error: testCaseAttempt.worstTestStepResult.message,
|
|
@@ -119,11 +122,15 @@ class CucumberReporter extends Formatter {
|
|
|
119
122
|
.join('\n')
|
|
120
123
|
.trim(),
|
|
121
124
|
example: { ...example },
|
|
122
|
-
|
|
125
|
+
logs,
|
|
126
|
+
manuallyAttachedArtifacts: artifacts,
|
|
127
|
+
meta: keyValues,
|
|
123
128
|
title: scenario,
|
|
124
129
|
test_id: testId,
|
|
125
130
|
time,
|
|
126
131
|
});
|
|
132
|
+
|
|
133
|
+
services.setContext(null);
|
|
127
134
|
}
|
|
128
135
|
|
|
129
136
|
onTestRunFinished(envelope) {
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
const { Formatter } = require('cucumber');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
4
|
const { parseTest, fileSystem } = require('../../utils/utils');
|
|
5
|
-
const { STATUS,
|
|
5
|
+
const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../../constants');
|
|
6
6
|
const TestomatClient = require('../../client');
|
|
7
|
+
const config = require('../../config');
|
|
7
8
|
|
|
8
9
|
const createTestomatFormatter = apiKey => {
|
|
9
10
|
if (!apiKey || apiKey === '') {
|
|
@@ -97,7 +98,7 @@ const createTestomatFormatter = apiKey => {
|
|
|
97
98
|
|
|
98
99
|
options.eventBroadcaster.on('gherkin-document', addDocument);
|
|
99
100
|
options.eventBroadcaster.on('test-run-started', () => {
|
|
100
|
-
fileSystem.clearDir(
|
|
101
|
+
fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
|
|
101
102
|
this?.client?.createRun();
|
|
102
103
|
});
|
|
103
104
|
options.eventBroadcaster.on('test-case-finished', this.onTestCaseFinished.bind(this));
|
|
@@ -151,4 +152,4 @@ const createTestomatFormatter = apiKey => {
|
|
|
151
152
|
};
|
|
152
153
|
};
|
|
153
154
|
|
|
154
|
-
module.exports = createTestomatFormatter(
|
|
155
|
+
module.exports = createTestomatFormatter(config.TESTOMATIO);
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
const { STATUS } = require('../../constants');
|
|
2
2
|
const { parseTest, parseSuite } = require('../../utils/utils');
|
|
3
3
|
const TestomatClient = require('../../client');
|
|
4
|
+
const config = require('../../config');
|
|
4
5
|
|
|
5
6
|
const testomatioReporter = on => {
|
|
6
|
-
if (!
|
|
7
|
+
if (!config.TESTOMATIO) {
|
|
7
8
|
console.log('TESTOMATIO key is empty, ignoring reports');
|
|
8
|
-
return
|
|
9
|
+
return;
|
|
9
10
|
}
|
|
10
|
-
const client = new TestomatClient({ apiKey:
|
|
11
|
+
const client = new TestomatClient({ apiKey: config.TESTOMATIO });
|
|
11
12
|
|
|
12
|
-
on('before:run', async
|
|
13
|
+
on('before:run', async run => {
|
|
14
|
+
// TODO: looks like client.env does not exist
|
|
13
15
|
if (!client.env) {
|
|
14
|
-
client.env = `${run.browser.displayName},${run.system.osName}
|
|
16
|
+
client.env = `${run.browser.displayName},${run.system.osName}`;
|
|
15
17
|
}
|
|
16
18
|
await client.createRun();
|
|
17
19
|
});
|
|
@@ -25,8 +27,9 @@ const testomatioReporter = on => {
|
|
|
25
27
|
const lastAttemptIndex = test.attempts.length - 1;
|
|
26
28
|
const latestAttempt = test.attempts[lastAttemptIndex];
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
+
// latestAttempt.duration && latestAttempt.error were available in adapters version up to 13 JFYI
|
|
31
|
+
const time = latestAttempt.duration || latestAttempt.wallClockDuration || test.duration;
|
|
32
|
+
let error = latestAttempt.error;
|
|
30
33
|
|
|
31
34
|
let title = test.title.pop();
|
|
32
35
|
const examples = title.match(/\(example (#\d+)\)/);
|
|
@@ -39,35 +42,59 @@ const testomatioReporter = on => {
|
|
|
39
42
|
const testId = parseTest(title);
|
|
40
43
|
const suiteId = parseSuite(suiteTitle);
|
|
41
44
|
|
|
42
|
-
if (error) {
|
|
43
|
-
error
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
45
|
+
if (!error && test.displayError) {
|
|
46
|
+
error = { message: test.displayError };
|
|
47
|
+
error.inspect = function () {
|
|
48
|
+
// eslint-disable-line func-names
|
|
49
|
+
return this.message;
|
|
50
|
+
};
|
|
49
51
|
}
|
|
50
52
|
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
const formattedError = error
|
|
54
|
+
? {
|
|
55
|
+
message: error.message,
|
|
56
|
+
inspect:
|
|
57
|
+
error.inspect ||
|
|
58
|
+
function () {
|
|
59
|
+
return this.message;
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
: '';
|
|
63
|
+
|
|
64
|
+
const screenshots = Array.isArray(results.screenshots)
|
|
65
|
+
? results.screenshots
|
|
66
|
+
.filter(screenshot => screenshot?.path && screenshot?.path.includes(title) && screenshot?.takenAt)
|
|
67
|
+
.map(screenshot => screenshot.path)
|
|
68
|
+
: [];
|
|
55
69
|
|
|
56
70
|
const files = [...videos, ...screenshots];
|
|
57
71
|
|
|
58
72
|
let state;
|
|
59
73
|
switch (test.state) {
|
|
60
|
-
case 'passed':
|
|
61
|
-
|
|
74
|
+
case 'passed':
|
|
75
|
+
state = STATUS.PASSED;
|
|
76
|
+
break;
|
|
77
|
+
case 'failed':
|
|
78
|
+
state = STATUS.FAILED;
|
|
79
|
+
break;
|
|
62
80
|
case 'skipped':
|
|
63
|
-
case 'pending':
|
|
81
|
+
case 'pending':
|
|
64
82
|
default:
|
|
65
83
|
state = STATUS.SKIPPED;
|
|
66
84
|
}
|
|
67
85
|
|
|
68
|
-
addSpecTestsPromises.push(
|
|
69
|
-
|
|
70
|
-
|
|
86
|
+
addSpecTestsPromises.push(
|
|
87
|
+
client.addTestRun(state, {
|
|
88
|
+
title,
|
|
89
|
+
time,
|
|
90
|
+
example,
|
|
91
|
+
error: formattedError,
|
|
92
|
+
files,
|
|
93
|
+
suite_title: suiteTitle,
|
|
94
|
+
test_id: testId,
|
|
95
|
+
suite_id: suiteId,
|
|
96
|
+
}),
|
|
97
|
+
);
|
|
71
98
|
}
|
|
72
99
|
|
|
73
100
|
await Promise.all(addSpecTestsPromises);
|
package/lib/adapter/jest.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
1
2
|
const TestomatClient = require('../client');
|
|
2
|
-
const { STATUS,
|
|
3
|
+
const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
|
|
3
4
|
const { parseTest, ansiRegExp, fileSystem } = require('../utils/utils');
|
|
5
|
+
const { services } = require('../services');
|
|
6
|
+
const debug = require('debug')('@testomatio/reporter:adapter-jest');
|
|
7
|
+
const path = require('path');
|
|
4
8
|
|
|
5
9
|
class JestReporter {
|
|
6
10
|
constructor(globalConfig, options) {
|
|
@@ -11,22 +15,24 @@ class JestReporter {
|
|
|
11
15
|
this.client.createRun();
|
|
12
16
|
}
|
|
13
17
|
|
|
14
|
-
static getIdOfCurrentlyRunningTest() {
|
|
15
|
-
if (!process.env.JEST_WORKER_ID) return null;
|
|
16
|
-
try {
|
|
17
|
-
// @ts-expect-error "expect" could only be defined inside Jest environement (forbidden to import it outside)
|
|
18
|
-
// eslint-disable-next-line no-undef
|
|
19
|
-
if (expect && expect?.getState()?.currentTestName) return parseTest(expect?.getState()?.currentTestName);
|
|
20
|
-
} catch (e) {
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
18
|
onRunStart() {
|
|
26
19
|
// clear tmp dir
|
|
27
|
-
fileSystem.clearDir(
|
|
20
|
+
fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// start of test file (including beforeAll)
|
|
24
|
+
onTestStart(testFile) {
|
|
25
|
+
debug('Start running test file:', testFile.path);
|
|
26
|
+
services.setContext(testFile.path);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// start of the test (including beforeEach)
|
|
30
|
+
onTestCaseStart(test, testCase) {
|
|
31
|
+
debug('Start running test:', testCase.fullName);
|
|
32
|
+
services.setContext(testCase.fullName);
|
|
28
33
|
}
|
|
29
34
|
|
|
35
|
+
// end of test file! (there is also onTestCaseResult listener)
|
|
30
36
|
onTestResult(test, testResult) {
|
|
31
37
|
if (!this.client) return;
|
|
32
38
|
|
|
@@ -42,13 +48,15 @@ class JestReporter {
|
|
|
42
48
|
steps = failureMessages[0];
|
|
43
49
|
}
|
|
44
50
|
const testId = parseTest(title);
|
|
45
|
-
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
|
|
52
|
+
// suite titles from most outer to most inner, separated by space
|
|
53
|
+
let fullSuiteTitle = testResult.ancestorTitles?.join(' ');
|
|
54
|
+
// if no suite titles, use file name
|
|
55
|
+
if (!fullSuiteTitle && testResult.testFilePath) fullSuiteTitle = path.basename(testResult.testFilePath);
|
|
56
|
+
|
|
57
|
+
const logs = getTestLogs(result);
|
|
58
|
+
const artifacts = services.artifacts.get(result.fullName);
|
|
59
|
+
const keyValues = services.keyValues.get(result.fullName);
|
|
52
60
|
|
|
53
61
|
const deducedStatus = status === 'pending' ? 'skipped' : status;
|
|
54
62
|
// In jest if test is not matched with test name pattern it is considered as skipped.
|
|
@@ -56,11 +64,14 @@ class JestReporter {
|
|
|
56
64
|
if (!this._globalConfig.testNamePattern || deducedStatus !== 'skipped') {
|
|
57
65
|
this.client.addTestRun(deducedStatus, {
|
|
58
66
|
test_id: testId,
|
|
59
|
-
suite_title,
|
|
67
|
+
suite_title: fullSuiteTitle,
|
|
60
68
|
error,
|
|
61
69
|
steps,
|
|
62
70
|
title,
|
|
63
71
|
time: duration,
|
|
72
|
+
logs,
|
|
73
|
+
manuallyAttachedArtifacts: artifacts,
|
|
74
|
+
meta: keyValues,
|
|
64
75
|
});
|
|
65
76
|
}
|
|
66
77
|
}
|
|
@@ -72,10 +83,23 @@ class JestReporter {
|
|
|
72
83
|
const { numFailedTests } = results;
|
|
73
84
|
const status = numFailedTests === 0 ? STATUS.PASSED : STATUS.FAILED;
|
|
74
85
|
this.client.updateRunStatus(status);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
75
88
|
|
|
76
|
-
|
|
77
|
-
|
|
89
|
+
function getTestLogs(testResult) {
|
|
90
|
+
const suiteLogsArr = services.logger.getLogs(testResult.testFilePath);
|
|
91
|
+
const suiteLogs = suiteLogsArr ? suiteLogsArr.join('\n').trim() : '';
|
|
92
|
+
const testLogsArr = services.logger.getLogs(testResult.fullName);
|
|
93
|
+
const testLogs = testLogsArr ? testLogsArr.join('\n').trim() : '';
|
|
94
|
+
|
|
95
|
+
let logs = '';
|
|
96
|
+
if (suiteLogs) {
|
|
97
|
+
logs += `${chalk.bold('\t--- Suite ---')}\n${suiteLogs}`;
|
|
98
|
+
}
|
|
99
|
+
if (testLogs) {
|
|
100
|
+
logs += `\n${chalk.bold('\t--- Test ---')}\n${testLogs}`;
|
|
78
101
|
}
|
|
102
|
+
return logs;
|
|
79
103
|
}
|
|
80
104
|
|
|
81
105
|
module.exports = JestReporter;
|