@testomatio/reporter 0.8.2-beta.1 → 1.0.0-beta.1
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/Changelog.md +6 -0
- package/README.md +11 -0
- package/lib/fileUploader.js +10 -3
- package/lib/pipe/csv.js +1 -1
- package/lib/pipe/github.js +3 -2
- package/lib/pipe/gitlab.js +3 -1
- package/lib/pipe/testomatio.js +20 -1
- package/lib/xmlReader.js +6 -4
- package/package.json +1 -1
package/Changelog.md
CHANGED
package/README.md
CHANGED
|
@@ -440,6 +440,17 @@ Give a title to your reports by passing it as environment variable to `TESTOMATI
|
|
|
440
440
|
TESTOMATIO={API_KEY} TESTOMATIO_TITLE="title for the report" <actual run command>
|
|
441
441
|
```
|
|
442
442
|
|
|
443
|
+
### Reporting Parallel Executionto To Same Run
|
|
444
|
+
|
|
445
|
+
Provide a shared unique title to all runs that will be running in parallel, and add `TESTOMATIO_SHARED_RUN` environment var. So all reports will be sent to this run.
|
|
446
|
+
|
|
447
|
+
```bash
|
|
448
|
+
TESTOMATIO={API_KEY} TESTOMATIO_TITLE="report for commit ${GIT_COMMIT}" TESTOMATIO_SHARED_RUN=1 <actual run command>
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
We recommend using a commit hash as a title to create a new Run. In this case we ensure that run title is unique and will be the same for all parallel jobs running exactly for this commit.
|
|
452
|
+
|
|
453
|
+
|
|
443
454
|
### Adding Report to RunGroup
|
|
444
455
|
|
|
445
456
|
Create/Add run to group by providing `TESTOMATIO_RUNGROUP_TITLE`:
|
package/lib/fileUploader.js
CHANGED
|
@@ -22,6 +22,11 @@ const keys = [
|
|
|
22
22
|
|
|
23
23
|
let config;
|
|
24
24
|
|
|
25
|
+
function resetConfig() {
|
|
26
|
+
config = undefined;
|
|
27
|
+
isEnabled = undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
function getConfig() {
|
|
26
31
|
if (config) return config;
|
|
27
32
|
config = keys.reduce((acc, key) => {
|
|
@@ -32,8 +37,8 @@ function getConfig() {
|
|
|
32
37
|
}
|
|
33
38
|
|
|
34
39
|
function getMaskedConfig() {
|
|
35
|
-
|
|
36
|
-
|
|
40
|
+
return Object.fromEntries(Object.entries(getConfig())
|
|
41
|
+
.map(([key, value]) => [key, key === 'S3_SECRET_ACCESS_KEY' || key === 'S3_ACCESS_KEY_ID' ? '***' : value]));
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
let isEnabled;
|
|
@@ -111,6 +116,7 @@ const fileData = fs.readFileSync(filePath);
|
|
|
111
116
|
Bucket: S3_BUCKET,
|
|
112
117
|
Key,
|
|
113
118
|
Body: fileData,
|
|
119
|
+
ContentType,
|
|
114
120
|
ACL,
|
|
115
121
|
};
|
|
116
122
|
|
|
@@ -126,7 +132,7 @@ const fileData = fs.readFileSync(filePath);
|
|
|
126
132
|
return out.singleUploadResult.Location;
|
|
127
133
|
} catch (e) {
|
|
128
134
|
console.log(e);
|
|
129
|
-
console.log(APP_PREFIX, chalk.
|
|
135
|
+
console.log(APP_PREFIX, chalk.red(`Failed uploading '${Key}'. Please check S3 credentials`), getMaskedConfig());
|
|
130
136
|
|
|
131
137
|
console.log(APP_PREFIX, `To ${chalk.bold('disable')} artifact uploads set: TESTOMATIO_DISABLE_ARTIFACTS=1`);
|
|
132
138
|
if (!TESTOMATIO_PRIVATE_ARTIFACTS) {
|
|
@@ -215,4 +221,5 @@ module.exports = {
|
|
|
215
221
|
uploadFileByPath: memoize(uploadFileByPath),
|
|
216
222
|
uploadFileAsBuffer: memoize(uploadFileAsBuffer),
|
|
217
223
|
isArtifactsEnabled: memoize(isArtifactsEnabled),
|
|
224
|
+
resetConfig,
|
|
218
225
|
};
|
package/lib/pipe/csv.js
CHANGED
|
@@ -18,11 +18,11 @@ class CsvPipe {
|
|
|
18
18
|
constructor(params, store) {
|
|
19
19
|
this.store = store || {};
|
|
20
20
|
this.title = params.title || process.env.TESTOMATIO_TITLE;
|
|
21
|
-
this.isEnabled = true;
|
|
22
21
|
this.results = [];
|
|
23
22
|
|
|
24
23
|
this.outputDir = 'export';
|
|
25
24
|
this.csvFilename = process.env.TESTOMATIO_CSV_FILENAME;
|
|
25
|
+
this.isEnabled = !!this.csvFilename
|
|
26
26
|
this.isCsvSave = false;
|
|
27
27
|
|
|
28
28
|
if (this.csvFilename !== undefined && this.csvFilename.split('.').length > 0) {
|
package/lib/pipe/github.js
CHANGED
|
@@ -79,7 +79,9 @@ class GitHubPipe {
|
|
|
79
79
|
| Summary | ${statusEmoji('failed')} **${failedCount}** failed; ${statusEmoji(
|
|
80
80
|
'passed',
|
|
81
81
|
)} **${passedCount}** passed; **${statusEmoji('skipped')}** ${skippedCount} skipped |
|
|
82
|
-
| Duration | 🕐 **${humanizeDuration(
|
|
82
|
+
| Duration | 🕐 **${humanizeDuration(parseInt(this.tests.reduce((a, t) => a + (t.run_time || 0), 0), 10), {
|
|
83
|
+
maxDecimalPoints: 0,
|
|
84
|
+
})}** |
|
|
83
85
|
`;
|
|
84
86
|
if (this.store.runUrl) {
|
|
85
87
|
summary += `| Testomat.io Report | 📊 [Run #${this.store.runId}](${this.store.runUrl}) | `;
|
|
@@ -154,7 +156,6 @@ class GitHubPipe {
|
|
|
154
156
|
.join('\n');
|
|
155
157
|
body += '\n</details>';
|
|
156
158
|
}
|
|
157
|
-
|
|
158
159
|
|
|
159
160
|
await deletePreviousReport(this.octokit, owner, repo, this.issue, this.hiddenCommentData);
|
|
160
161
|
|
package/lib/pipe/gitlab.js
CHANGED
|
@@ -81,7 +81,9 @@ class GitLabPipe {
|
|
|
81
81
|
| Summary | ${statusEmoji('failed')} **${failedCount}** failed; ${statusEmoji(
|
|
82
82
|
'passed',
|
|
83
83
|
)} **${passedCount}** passed; **${statusEmoji('skipped')}** ${skippedCount} skipped |
|
|
84
|
-
| Duration | 🕐 **${humanizeDuration(
|
|
84
|
+
| Duration | 🕐 **${humanizeDuration(parseInt(this.tests.reduce((a, t) => a + (t.run_time || 0), 0), 10), {
|
|
85
|
+
maxDecimalPoints: 0,
|
|
86
|
+
})}** |
|
|
85
87
|
`;
|
|
86
88
|
|
|
87
89
|
if (this.ENV.CI_JOB_NAME && this.ENV.CI_JOB_ID) {
|
package/lib/pipe/testomatio.js
CHANGED
|
@@ -4,6 +4,7 @@ const axios = require('axios');
|
|
|
4
4
|
const JsonCycle = require('json-cycle');
|
|
5
5
|
const { APP_PREFIX, STATUS } = require('../constants');
|
|
6
6
|
const { isValidUrl } = require('../util');
|
|
7
|
+
const { resetConfig } = require('../fileUploader');
|
|
7
8
|
|
|
8
9
|
const { TESTOMATIO_RUN } = process.env;
|
|
9
10
|
if (TESTOMATIO_RUN) {
|
|
@@ -28,6 +29,7 @@ class TestomatioPipe {
|
|
|
28
29
|
debug('Testomatio Pipe: Enabled');
|
|
29
30
|
this.store = store || {};
|
|
30
31
|
this.title = params.title || process.env.TESTOMATIO_TITLE;
|
|
32
|
+
this.sharedRun = !!process.env.TESTOMATIO_SHARED_RUN;
|
|
31
33
|
this.groupTitle = params.groupTitle || process.env.TESTOMATIO_RUNGROUP_TITLE;
|
|
32
34
|
this.env = process.env.TESTOMATIO_ENV;
|
|
33
35
|
this.axios = axios.create();
|
|
@@ -55,11 +57,14 @@ class TestomatioPipe {
|
|
|
55
57
|
group_title: this.groupTitle,
|
|
56
58
|
env: this.env,
|
|
57
59
|
title: this.title,
|
|
60
|
+
shared_run: this.sharedRun,
|
|
58
61
|
}).filter(([, value]) => !!value)
|
|
59
62
|
);
|
|
60
63
|
|
|
61
64
|
if (this.runId) {
|
|
62
|
-
|
|
65
|
+
const resp = await this.axios.put(`${this.url.trim()}/api/reporter/${this.runId}`, runParams);
|
|
66
|
+
if (resp.data.artifacts) setS3Credentials(resp.data.artifacts);
|
|
67
|
+
return;
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
try {
|
|
@@ -69,6 +74,7 @@ class TestomatioPipe {
|
|
|
69
74
|
});
|
|
70
75
|
this.runId = resp.data.uid;
|
|
71
76
|
this.runUrl = `${this.url}/${resp.data.url.split('/').splice(3).join('/')}`;
|
|
77
|
+
if (resp.data.artifacts) setS3Credentials(resp.data.artifacts);
|
|
72
78
|
this.store.runUrl = this.runUrl;
|
|
73
79
|
this.store.runId = this.runId;
|
|
74
80
|
console.log(APP_PREFIX, '📊 Report created. Report ID:', this.runId);
|
|
@@ -162,3 +168,16 @@ class TestomatioPipe {
|
|
|
162
168
|
}
|
|
163
169
|
|
|
164
170
|
module.exports = TestomatioPipe;
|
|
171
|
+
|
|
172
|
+
function setS3Credentials(artifacts) {
|
|
173
|
+
if (!Object.keys(artifacts).length) return;
|
|
174
|
+
|
|
175
|
+
console.log(APP_PREFIX, 'Setting S3 credentials obtained from Testomat.io...');
|
|
176
|
+
|
|
177
|
+
if (artifacts.ACCESS_KEY_ID) process.env.S3_ACCESS_KEY_ID = artifacts.ACCESS_KEY_ID;
|
|
178
|
+
if (artifacts.SECRET_ACCESS_KEY) process.env.S3_SECRET_ACCESS_KEY = artifacts.SECRET_ACCESS_KEY;
|
|
179
|
+
if (artifacts.REGION) process.env.S3_REGION = artifacts.REGION;
|
|
180
|
+
if (artifacts.BUCKET) process.env.S3_BUCKET = artifacts.BUCKET;
|
|
181
|
+
if (artifacts.ENDPOINT) process.env.S3_ENDPOINT = artifacts.ENDPOINT;
|
|
182
|
+
resetConfig();
|
|
183
|
+
}
|
package/lib/xmlReader.js
CHANGED
|
@@ -144,8 +144,9 @@ class XmlReader {
|
|
|
144
144
|
if (!Array.isArray(result)) result = [result].filter(d => !!d);
|
|
145
145
|
|
|
146
146
|
const results = result.map(td => ({
|
|
147
|
-
id: td.executionId,
|
|
148
|
-
|
|
147
|
+
id: td.executionId,
|
|
148
|
+
// seconds are used in junit reports, but ms are used by testomatio
|
|
149
|
+
run_time: parseFloat(td.duration) * 1000,
|
|
149
150
|
status: td.outcome,
|
|
150
151
|
stack: td.Output.StdOut,
|
|
151
152
|
files: td?.ResultFiles?.ResultFile?.map(rf => rf.path)
|
|
@@ -278,7 +279,7 @@ class XmlReader {
|
|
|
278
279
|
async uploadArtifacts() {
|
|
279
280
|
for (const test of this.tests.filter(t => !!t.stack)) {
|
|
280
281
|
let files = [];
|
|
281
|
-
if (test.files
|
|
282
|
+
if (test.files?.length) files = test.files.map(f => path.join(process.cwd(), f))
|
|
282
283
|
files = [...files, ...fetchFilesFromStackTrace(test.stack)];
|
|
283
284
|
debug('Uploading files', files)
|
|
284
285
|
test.artifacts = await Promise.all(files.map(f => upload.uploadFileByPath(f, this.runId)));
|
|
@@ -361,7 +362,8 @@ function reduceTestCases(prev, item) {
|
|
|
361
362
|
stack,
|
|
362
363
|
message,
|
|
363
364
|
line: testCaseItem.lineno,
|
|
364
|
-
|
|
365
|
+
// seconds are used in junit reports, but ms are used by testomatio
|
|
366
|
+
run_time: parseFloat(testCaseItem.time || testCaseItem.duration) * 1000,
|
|
365
367
|
status,
|
|
366
368
|
title: testCaseItem.name,
|
|
367
369
|
suite_title: reduceOptions.preferClassname ? testCaseItem.classname : (item.name || testCaseItem.classname),
|