@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/lib/adapter/mocha.js
CHANGED
|
@@ -1,57 +1,78 @@
|
|
|
1
1
|
// eslint-disable-next-line global-require, import/no-extraneous-dependencies
|
|
2
2
|
const Mocha = require('mocha');
|
|
3
|
-
const debug = require('debug')('@testomatio/reporter:adapter:mocha');
|
|
4
3
|
const chalk = require('chalk');
|
|
5
4
|
const TestomatClient = require('../client');
|
|
6
|
-
const { STATUS,
|
|
7
|
-
const { parseTest,
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
const { STATUS, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
|
|
6
|
+
const { parseTest, fileSystem } = require('../utils/utils');
|
|
7
|
+
const config = require('../config');
|
|
8
|
+
const { services } = require('../services');
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
EVENT_RUN_BEGIN,
|
|
12
|
+
EVENT_RUN_END,
|
|
13
|
+
EVENT_TEST_FAIL,
|
|
14
|
+
EVENT_TEST_PASS,
|
|
15
|
+
EVENT_TEST_PENDING,
|
|
16
|
+
EVENT_SUITE_BEGIN,
|
|
17
|
+
EVENT_SUITE_END,
|
|
18
|
+
EVENT_TEST_BEGIN,
|
|
19
|
+
EVENT_TEST_END,
|
|
20
|
+
} = Mocha.Runner.constants;
|
|
12
21
|
|
|
13
22
|
function MochaReporter(runner, opts) {
|
|
14
23
|
Mocha.reporters.Base.call(this, runner);
|
|
15
24
|
let passes = 0;
|
|
16
25
|
let failures = 0;
|
|
17
26
|
let skipped = 0;
|
|
18
|
-
let artifactStore;
|
|
27
|
+
// let artifactStore;
|
|
19
28
|
|
|
20
|
-
const apiKey = opts?.reporterOptions?.apiKey ||
|
|
29
|
+
const apiKey = opts?.reporterOptions?.apiKey || config.TESTOMATIO;
|
|
21
30
|
|
|
22
31
|
const client = new TestomatClient({ apiKey });
|
|
23
32
|
|
|
24
33
|
runner.on(EVENT_RUN_BEGIN, () => {
|
|
25
34
|
client.createRun();
|
|
26
35
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
36
|
+
fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
runner.on(EVENT_SUITE_BEGIN, async suite => {
|
|
40
|
+
services.setContext(suite.fullTitle());
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
runner.on(EVENT_SUITE_END, async () => {
|
|
44
|
+
services.setContext(null);
|
|
45
|
+
});
|
|
30
46
|
|
|
31
|
-
|
|
47
|
+
runner.on(EVENT_TEST_BEGIN, async test => {
|
|
48
|
+
services.setContext(test.fullTitle());
|
|
49
|
+
});
|
|
32
50
|
|
|
33
|
-
|
|
51
|
+
runner.on(EVENT_TEST_END, async () => {
|
|
52
|
+
services.setContext(null);
|
|
34
53
|
});
|
|
35
54
|
|
|
36
55
|
runner.on(EVENT_TEST_PASS, async test => {
|
|
37
56
|
passes += 1;
|
|
57
|
+
|
|
38
58
|
console.log(chalk.bold.green('✔'), test.fullTitle());
|
|
39
59
|
const testId = parseTest(test.title);
|
|
40
60
|
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
debug(`test=${specificTest} content = `, content);
|
|
61
|
+
const logs = getTestLogs(test);
|
|
62
|
+
const artifacts = services.artifacts.get(test.fullTitle());
|
|
63
|
+
const keyValues = services.keyValues.get(test.fullTitle());
|
|
45
64
|
|
|
46
|
-
client.addTestRun(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
65
|
+
client.addTestRun(STATUS.PASSED, {
|
|
66
|
+
test_id: testId,
|
|
67
|
+
suite_title: getSuiteTitle(test),
|
|
68
|
+
title: getTestName(test),
|
|
69
|
+
code: test.body.toString(),
|
|
70
|
+
file: getFile(test),
|
|
71
|
+
time: test.duration,
|
|
72
|
+
logs,
|
|
73
|
+
manuallyAttachedArtifacts: artifacts,
|
|
74
|
+
meta: keyValues,
|
|
75
|
+
});
|
|
55
76
|
});
|
|
56
77
|
|
|
57
78
|
runner.on(EVENT_TEST_PENDING, test => {
|
|
@@ -59,7 +80,10 @@ function MochaReporter(runner, opts) {
|
|
|
59
80
|
console.log('skip: %s', test.fullTitle());
|
|
60
81
|
const testId = parseTest(test.title);
|
|
61
82
|
client.addTestRun(STATUS.SKIPPED, {
|
|
62
|
-
title: test
|
|
83
|
+
title: getTestName(test),
|
|
84
|
+
suite_title: getSuiteTitle(test),
|
|
85
|
+
code: test.body.toString(),
|
|
86
|
+
file: getFile(test),
|
|
63
87
|
test_id: testId,
|
|
64
88
|
time: test.duration,
|
|
65
89
|
});
|
|
@@ -70,33 +94,63 @@ function MochaReporter(runner, opts) {
|
|
|
70
94
|
console.log(chalk.bold.red('✖'), test.fullTitle(), chalk.gray(err.message));
|
|
71
95
|
const testId = parseTest(test.title);
|
|
72
96
|
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
},
|
|
86
|
-
content,
|
|
87
|
-
);
|
|
97
|
+
const logs = getTestLogs(test);
|
|
98
|
+
|
|
99
|
+
client.addTestRun(STATUS.FAILED, {
|
|
100
|
+
error: err,
|
|
101
|
+
suite_title: getSuiteTitle(test),
|
|
102
|
+
file: getFile(test),
|
|
103
|
+
test_id: testId,
|
|
104
|
+
title: getTestName(test),
|
|
105
|
+
code: test.body.toString(),
|
|
106
|
+
time: test.duration,
|
|
107
|
+
logs,
|
|
108
|
+
});
|
|
88
109
|
});
|
|
89
110
|
|
|
90
111
|
runner.on(EVENT_RUN_END, () => {
|
|
91
112
|
const status = failures === 0 ? STATUS.PASSED : STATUS.FAILED;
|
|
92
113
|
console.log(chalk.bold(status), `${passes} passed, ${failures} failed, ${skipped} skipped`);
|
|
93
114
|
client.updateRunStatus(status);
|
|
94
|
-
|
|
95
|
-
artifactStore.cleanup();
|
|
96
115
|
});
|
|
97
116
|
}
|
|
98
117
|
|
|
118
|
+
function getTestLogs(test) {
|
|
119
|
+
|
|
120
|
+
const suiteLogsArr = services.logger.getLogs(test.parent.fullTitle());
|
|
121
|
+
const suiteLogs = suiteLogsArr ? suiteLogsArr.join('\n').trim() : '';
|
|
122
|
+
const testLogsArr = services.logger.getLogs(test.fullTitle());
|
|
123
|
+
const testLogs = testLogsArr ? testLogsArr.join('\n').trim() : '';
|
|
124
|
+
|
|
125
|
+
let logs = '';
|
|
126
|
+
if (suiteLogs) {
|
|
127
|
+
logs += `${chalk.bold('\t--- BeforeSuite ---')}\n${suiteLogs}`;
|
|
128
|
+
}
|
|
129
|
+
if (testLogs) {
|
|
130
|
+
logs += `\n${chalk.bold('\t--- Test ---')}\n${testLogs}`;
|
|
131
|
+
}
|
|
132
|
+
return logs;
|
|
133
|
+
}
|
|
134
|
+
|
|
99
135
|
// To have this reporter "extend" a built-in reporter uncomment the following line:
|
|
100
136
|
Mocha.utils.inherits(MochaReporter, Mocha.reporters.Spec);
|
|
101
137
|
|
|
102
138
|
module.exports = MochaReporter;
|
|
139
|
+
|
|
140
|
+
function getSuiteTitle(test, pathArr = []) {
|
|
141
|
+
|
|
142
|
+
if (test.parent.parent) getSuiteTitle(test.parent, pathArr);
|
|
143
|
+
|
|
144
|
+
pathArr.push(test.parent.title);
|
|
145
|
+
|
|
146
|
+
return pathArr.filter(t => !!t)[0];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function getFile(test) {
|
|
150
|
+
return test.parent.file?.replace(process.cwd(), '');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function getTestName(test) {
|
|
154
|
+
if (process.env.TESTOMATIO_CREATE === 'fulltitle') return test.fullTitle();
|
|
155
|
+
return test.title;
|
|
156
|
+
}
|
|
@@ -3,10 +3,13 @@ const crypto = require('crypto');
|
|
|
3
3
|
const os = require('os');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const fs = require('fs');
|
|
6
|
-
const { APP_PREFIX, STATUS: Status,
|
|
6
|
+
const { APP_PREFIX, STATUS: Status, TESTOMAT_TMP_STORAGE_DIR } = require('../constants');
|
|
7
7
|
const TestomatioClient = require('../client');
|
|
8
8
|
const { isArtifactsEnabled } = require('../fileUploader');
|
|
9
9
|
const { parseTest, fileSystem } = require('../utils/utils');
|
|
10
|
+
// const debug = require('debug')('@testomatio/reporter:adapter:playwright');
|
|
11
|
+
const { services } = require('../services');
|
|
12
|
+
const { dataStorage } = require('../data-storage');
|
|
10
13
|
|
|
11
14
|
const reportTestPromises = [];
|
|
12
15
|
|
|
@@ -17,54 +20,90 @@ class PlaywrightReporter {
|
|
|
17
20
|
this.uploads = [];
|
|
18
21
|
}
|
|
19
22
|
|
|
20
|
-
onBegin(
|
|
23
|
+
onBegin(config, suite) {
|
|
21
24
|
// clean data storage
|
|
22
|
-
fileSystem.clearDir(
|
|
25
|
+
fileSystem.clearDir(TESTOMAT_TMP_STORAGE_DIR);
|
|
23
26
|
if (!this.client) return;
|
|
24
27
|
this.suite = suite;
|
|
28
|
+
this.config = config;
|
|
25
29
|
this.client.createRun();
|
|
26
30
|
}
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
onTestBegin(testInfo) {
|
|
33
|
+
const fullTestTitle = getTestContextName(testInfo);
|
|
34
|
+
dataStorage.setContext(fullTestTitle);
|
|
35
|
+
}
|
|
31
36
|
|
|
32
37
|
onTestEnd(test, result) {
|
|
33
38
|
if (!this.client) return;
|
|
34
39
|
|
|
35
|
-
let testId = parseTest(test.title);
|
|
36
|
-
|
|
37
40
|
const { title } = test;
|
|
41
|
+
|
|
42
|
+
let testId = parseTest(title);
|
|
43
|
+
|
|
38
44
|
const { error, duration } = result;
|
|
39
45
|
|
|
40
|
-
const suite_title = test.parent ? test.parent
|
|
46
|
+
const suite_title = test.parent ? test.parent?.title : path.basename(test?.location?.file);
|
|
41
47
|
|
|
42
48
|
const steps = [];
|
|
43
49
|
for (const step of result.steps) {
|
|
44
50
|
appendStep(step, steps);
|
|
45
51
|
}
|
|
46
52
|
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
53
|
+
const fullTestTitle = getTestContextName(test);
|
|
54
|
+
let logs = '';
|
|
55
|
+
if (result.stderr.length || result.stdout.length) {
|
|
56
|
+
logs = `\n\n${chalk.bold('Logs:')}\n${chalk.red(result.stderr.join(''))}\n${result.stdout.join('')}`;
|
|
57
|
+
}
|
|
58
|
+
const manuallyAttachedArtifacts = services.artifacts.get(fullTestTitle);
|
|
59
|
+
const keyValues = services.keyValues.get(fullTestTitle);
|
|
60
|
+
|
|
61
|
+
const reportTestPromise = this.client
|
|
62
|
+
.addTestRun(checkStatus(result.status), {
|
|
63
|
+
error,
|
|
64
|
+
test_id: testId,
|
|
65
|
+
suite_title,
|
|
66
|
+
title,
|
|
67
|
+
steps: steps.join('\n'),
|
|
68
|
+
time: duration,
|
|
69
|
+
logs,
|
|
70
|
+
manuallyAttachedArtifacts,
|
|
71
|
+
meta: keyValues,
|
|
72
|
+
file: test.location?.file,
|
|
73
|
+
})
|
|
74
|
+
.then(pipes => {
|
|
75
|
+
testId = pipes?.filter(p => p.pipe.includes('Testomatio'))[0]?.result?.data?.test_id;
|
|
76
|
+
|
|
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);
|
|
62
86
|
});
|
|
63
|
-
});
|
|
64
87
|
|
|
65
88
|
reportTestPromises.push(reportTestPromise);
|
|
66
89
|
}
|
|
67
90
|
|
|
91
|
+
#getArtifactPath(artifact) {
|
|
92
|
+
if (artifact.path) {
|
|
93
|
+
if (path.isAbsolute(artifact.path)) return artifact.path;
|
|
94
|
+
|
|
95
|
+
return path.join(this.config.outputDir || this.config.projects[0].outputDir, artifact.path);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (artifact.body) {
|
|
99
|
+
const fileName = tmpFile();
|
|
100
|
+
fs.writeFileSync(fileName, artifact.body);
|
|
101
|
+
return fileName;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
68
107
|
async onEnd(result) {
|
|
69
108
|
if (!this.client) return;
|
|
70
109
|
|
|
@@ -78,13 +117,11 @@ class PlaywrightReporter {
|
|
|
78
117
|
for (const upload of this.uploads) {
|
|
79
118
|
const { title, testId, suite_title } = upload;
|
|
80
119
|
|
|
81
|
-
const files = upload.files.map(attachment => {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return { path: attachment.path, title, type: attachment.contentType };
|
|
87
|
-
});
|
|
120
|
+
const files = upload.files.map(attachment => ({
|
|
121
|
+
path: this.#getArtifactPath(attachment),
|
|
122
|
+
title,
|
|
123
|
+
type: attachment.contentType
|
|
124
|
+
}));
|
|
88
125
|
|
|
89
126
|
promises.push(
|
|
90
127
|
this.client.addTestRun(undefined, {
|
|
@@ -92,6 +129,7 @@ class PlaywrightReporter {
|
|
|
92
129
|
title,
|
|
93
130
|
suite_title,
|
|
94
131
|
files,
|
|
132
|
+
file: upload.file,
|
|
95
133
|
}),
|
|
96
134
|
);
|
|
97
135
|
}
|
|
@@ -131,4 +169,28 @@ function tmpFile(prefix = 'tmp.') {
|
|
|
131
169
|
return path.join(tmpdir, prefix + crypto.randomBytes(16).toString('hex'));
|
|
132
170
|
}
|
|
133
171
|
|
|
172
|
+
/**
|
|
173
|
+
* Returns filename + test title
|
|
174
|
+
* @param {*} test - testInfo object from Playwright
|
|
175
|
+
* @returns
|
|
176
|
+
*/
|
|
177
|
+
function getTestContextName(test) {
|
|
178
|
+
return `${test._requireFile || ''}_${test.title}`;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function initPlaywrightForStorage() {
|
|
182
|
+
try {
|
|
183
|
+
// @ts-ignore-next-line
|
|
184
|
+
// eslint-disable-next-line import/no-unresolved
|
|
185
|
+
const { test } = require('@playwright/test');
|
|
186
|
+
// eslint-disable-next-line no-empty-pattern
|
|
187
|
+
test.beforeEach(async ({}, testInfo) => {
|
|
188
|
+
global.testomatioTestTitle = `${testInfo.file || ''}_${testInfo.title}`;
|
|
189
|
+
});
|
|
190
|
+
} catch (e) {
|
|
191
|
+
// ignore
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
134
195
|
module.exports = PlaywrightReporter;
|
|
196
|
+
module.exports.initPlaywrightForStorage = initPlaywrightForStorage;
|
package/lib/bin/startTest.js
CHANGED
|
@@ -5,6 +5,7 @@ const chalk = require('chalk');
|
|
|
5
5
|
const TestomatClient = require('../client');
|
|
6
6
|
const { APP_PREFIX, STATUS } = require('../constants');
|
|
7
7
|
const { version } = require('../../package.json');
|
|
8
|
+
const config = require('../config');
|
|
8
9
|
|
|
9
10
|
console.log(chalk.cyan.bold(` 🤩 Testomat.io Reporter v${version}`));
|
|
10
11
|
|
|
@@ -20,7 +21,7 @@ program
|
|
|
20
21
|
|
|
21
22
|
if (opts.envFile) require('dotenv').config(opts.envFile); // eslint-disable-line
|
|
22
23
|
|
|
23
|
-
const apiKey = process.env['INPUT_TESTOMATIO-KEY'] ||
|
|
24
|
+
const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || config.TESTOMATIO;
|
|
24
25
|
const title = process.env.TESTOMATIO_TITLE;
|
|
25
26
|
|
|
26
27
|
if (launch) {
|
|
@@ -53,6 +54,12 @@ program
|
|
|
53
54
|
|
|
54
55
|
let exitCode = 0;
|
|
55
56
|
|
|
57
|
+
if (!command.split) {
|
|
58
|
+
process.exitCode = 255;
|
|
59
|
+
console.log(APP_PREFIX, `No command provided. Use -c option to launch a test runner.`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
56
63
|
const client = new TestomatClient({ apiKey, title, parallel: true });
|
|
57
64
|
|
|
58
65
|
if(filter) {
|
|
@@ -74,12 +81,6 @@ program
|
|
|
74
81
|
}
|
|
75
82
|
}
|
|
76
83
|
|
|
77
|
-
if (!command.split) {
|
|
78
|
-
process.exitCode = 255;
|
|
79
|
-
console.log(APP_PREFIX, `No command provided. Use -c option to launch a test runner.`);
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
84
|
const testCmds = command.split(' ');
|
|
84
85
|
console.log(APP_PREFIX, `🚀 Running`, chalk.green(command));
|
|
85
86
|
|