@testomatio/reporter 0.4.1 → 0.4.5
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 +19 -0
- package/README.md +6 -0
- package/lib/adapter/codecept.js +3 -2
- package/lib/adapter/playwright.js +3 -2
- package/lib/bin/startTest.js +8 -2
- package/lib/client.js +43 -16
- package/lib/fileUploader.js +43 -12
- package/package.json +1 -1
package/Changelog.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
# 0.4.5
|
|
2
|
+
|
|
3
|
+
* Fixed "Total XX artifacts publicly uploaded to S3 bucket" when no S3 bucket is configured
|
|
4
|
+
* Improved S3 connection error messages
|
|
5
|
+
|
|
6
|
+
# 0.4.4
|
|
7
|
+
|
|
8
|
+
* Fixed returning 0 exit code when a process fails when running tests in parallel via `start-test-run`. Previously was using the last exit code returned by a process. Currently prefers the highest exit code that was returned by a process.
|
|
9
|
+
|
|
10
|
+
# 0.4.3
|
|
11
|
+
|
|
12
|
+
* Added `TESTOMATIO_DISABLE_ARTIFACTS` env variable to disable publishing artifacts.
|
|
13
|
+
|
|
14
|
+
# 0.4.2
|
|
15
|
+
|
|
16
|
+
* print version of reporter
|
|
17
|
+
* print number of uploaded artifacts
|
|
18
|
+
* print access mode for uploaded artifacts
|
|
19
|
+
|
|
1
20
|
# 0.4.1
|
|
2
21
|
|
|
3
22
|
Added `global.testomatioArtifacts = []` array which can be used to add arbitrary artifacts to a report.
|
package/README.md
CHANGED
|
@@ -248,6 +248,12 @@ global.testomatioArtifacts.push({ name: 'Screenshot', path: 'img/file.png' });
|
|
|
248
248
|
|
|
249
249
|
Artifacts will be uploaded for the current test when it is finished.
|
|
250
250
|
|
|
251
|
+
To disable uploading artifacts add `TESTOMATIO_DISABLE_ARTIFACTS` environment variable:
|
|
252
|
+
|
|
253
|
+
```
|
|
254
|
+
TESTOMATIO_DISABLE_ARTIFACTS=1
|
|
255
|
+
```
|
|
256
|
+
|
|
251
257
|
|
|
252
258
|
## Starting an Empty Run
|
|
253
259
|
|
package/lib/adapter/codecept.js
CHANGED
|
@@ -7,6 +7,7 @@ const chalk = require("chalk");
|
|
|
7
7
|
const TestomatClient = require("../client");
|
|
8
8
|
const { FAILED } = require("../constants");
|
|
9
9
|
const TRConstants = require("../constants");
|
|
10
|
+
const upload = require("../fileUploader");
|
|
10
11
|
const Output = require("../output");
|
|
11
12
|
|
|
12
13
|
let currentMetaStep = [];
|
|
@@ -87,8 +88,8 @@ module.exports = (config) => {
|
|
|
87
88
|
});
|
|
88
89
|
|
|
89
90
|
event.dispatcher.on(event.all.result, async () => {
|
|
90
|
-
if (videos.length) {
|
|
91
|
-
console.log(TRConstants.APP_PREFIX, `🎞️
|
|
91
|
+
if (videos.length && upload.isEnabled()) {
|
|
92
|
+
console.log(TRConstants.APP_PREFIX, `🎞️ Uploading ${videos.length} videos...`);
|
|
92
93
|
|
|
93
94
|
let promises = [];
|
|
94
95
|
for (const video of videos) {
|
|
@@ -5,6 +5,7 @@ const path = require('path');
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const Status = require('../constants');
|
|
7
7
|
const TestomatioClient = require("../client");
|
|
8
|
+
const upload = require("../fileUploader");
|
|
8
9
|
const { parseTest } = require('../util');
|
|
9
10
|
|
|
10
11
|
class TestomatioReporter {
|
|
@@ -75,8 +76,8 @@ class TestomatioReporter {
|
|
|
75
76
|
async onEnd(result) {
|
|
76
77
|
if (!this.client) return;
|
|
77
78
|
|
|
78
|
-
if (this.videos.length) {
|
|
79
|
-
console.log(Status.APP_PREFIX, `🎞️
|
|
79
|
+
if (this.videos.length && upload.isEnabled()) {
|
|
80
|
+
console.log(Status.APP_PREFIX, `🎞️ Uploading ${this.videos.length} videos...`);
|
|
80
81
|
|
|
81
82
|
let promises = [];
|
|
82
83
|
for (const video of this.videos) {
|
package/lib/bin/startTest.js
CHANGED
|
@@ -43,6 +43,8 @@ program
|
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
let exitCode = 0;
|
|
47
|
+
|
|
46
48
|
const testCmds = command.split(" ");
|
|
47
49
|
console.log(APP_PREFIX, `🚀 Running`, chalk.green(command));
|
|
48
50
|
|
|
@@ -55,7 +57,9 @@ program
|
|
|
55
57
|
"⚠️ ",
|
|
56
58
|
`Runner exited with ${chalk.bold(code)}, report is ignored`
|
|
57
59
|
);
|
|
58
|
-
|
|
60
|
+
|
|
61
|
+
if (code > exitCode) exitCode = code;
|
|
62
|
+
process.exitCode = exitCode;
|
|
59
63
|
});
|
|
60
64
|
|
|
61
65
|
return;
|
|
@@ -71,7 +75,9 @@ program
|
|
|
71
75
|
console.log(APP_PREFIX, emoji, `Runner exited with ${chalk.bold(code)}`);
|
|
72
76
|
const status = code === 0 ? "passed" : "failed";
|
|
73
77
|
client.updateRunStatus(status, true);
|
|
74
|
-
|
|
78
|
+
|
|
79
|
+
if (code > exitCode) exitCode = code;
|
|
80
|
+
process.exitCode = exitCode;
|
|
75
81
|
});
|
|
76
82
|
});
|
|
77
83
|
});
|
package/lib/client.js
CHANGED
|
@@ -2,13 +2,15 @@ const axios = require("axios");
|
|
|
2
2
|
const JsonCycle = require("json-cycle");
|
|
3
3
|
const { URL } = require("url");
|
|
4
4
|
const createCallsiteRecord = require("callsite-record");
|
|
5
|
-
const { sep } = require("path");
|
|
5
|
+
const { sep, join } = require("path");
|
|
6
|
+
const fs = require("fs");
|
|
6
7
|
const chalk = require("chalk");
|
|
7
|
-
const
|
|
8
|
+
const upload = require("./fileUploader");
|
|
8
9
|
const { PASSED, FAILED, FINISHED, APP_PREFIX } = require("./constants");
|
|
9
10
|
|
|
10
11
|
const TESTOMAT_URL = process.env.TESTOMATIO_URL || "https://app.testomat.io";
|
|
11
|
-
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN } =
|
|
12
|
+
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN } =
|
|
13
|
+
process.env;
|
|
12
14
|
|
|
13
15
|
if (TESTOMATIO_RUN) {
|
|
14
16
|
process.env.runId = TESTOMATIO_RUN;
|
|
@@ -27,6 +29,8 @@ class TestomatClient {
|
|
|
27
29
|
this.runId = process.env.runId;
|
|
28
30
|
this.queue = Promise.resolve();
|
|
29
31
|
this.axios = axios.create();
|
|
32
|
+
this.totalUploaded = 0;
|
|
33
|
+
this.version = JSON.parse(fs.readFileSync(join(__dirname, '..', 'package.json')).toString()).version;
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
/**
|
|
@@ -36,7 +40,7 @@ class TestomatClient {
|
|
|
36
40
|
*/
|
|
37
41
|
createRun() {
|
|
38
42
|
const { runId } = process.env;
|
|
39
|
-
if (!this.apiKey) throw new Error(
|
|
43
|
+
if (!this.apiKey) throw new Error("No API key is set, can't create run");
|
|
40
44
|
const runParams = {
|
|
41
45
|
api_key: this.apiKey.trim(),
|
|
42
46
|
title: this.title,
|
|
@@ -50,7 +54,9 @@ class TestomatClient {
|
|
|
50
54
|
if (!isValidUrl(TESTOMAT_URL.trim())) {
|
|
51
55
|
console.log(
|
|
52
56
|
APP_PREFIX,
|
|
53
|
-
chalk.red(
|
|
57
|
+
chalk.red(
|
|
58
|
+
`Error creating report on Testomat.io, report url '${TESTOMAT_URL}' is invalid`
|
|
59
|
+
)
|
|
54
60
|
);
|
|
55
61
|
return;
|
|
56
62
|
}
|
|
@@ -58,7 +64,8 @@ class TestomatClient {
|
|
|
58
64
|
if (runId) {
|
|
59
65
|
this.runId = runId;
|
|
60
66
|
this.queue = this.queue.then(() =>
|
|
61
|
-
axios.put(`${TESTOMAT_URL.trim()}/api/reporter/${runId}`, runParams)
|
|
67
|
+
axios.put(`${TESTOMAT_URL.trim()}/api/reporter/${runId}`, runParams)
|
|
68
|
+
);
|
|
62
69
|
return Promise.resolve(runId);
|
|
63
70
|
}
|
|
64
71
|
|
|
@@ -75,10 +82,12 @@ class TestomatClient {
|
|
|
75
82
|
console.log(
|
|
76
83
|
APP_PREFIX,
|
|
77
84
|
"📊 Report created. Report ID:",
|
|
78
|
-
this.runId
|
|
85
|
+
this.runId,
|
|
86
|
+
chalk.gray(`v${this.version}`)
|
|
79
87
|
);
|
|
80
88
|
process.env.runId = this.runId;
|
|
81
|
-
})
|
|
89
|
+
})
|
|
90
|
+
)
|
|
82
91
|
.catch(() => {
|
|
83
92
|
console.log(
|
|
84
93
|
APP_PREFIX,
|
|
@@ -118,11 +127,7 @@ class TestomatClient {
|
|
|
118
127
|
stack = this.formatError(error);
|
|
119
128
|
}
|
|
120
129
|
if (steps) {
|
|
121
|
-
stack = stack
|
|
122
|
-
? `${steps}\n\n${chalk.bold.red(
|
|
123
|
-
"################[ Failure ]################"
|
|
124
|
-
)}\n${stack}`
|
|
125
|
-
: steps;
|
|
130
|
+
stack = this.formatSteps(stack, steps);
|
|
126
131
|
}
|
|
127
132
|
|
|
128
133
|
if (this.runId) {
|
|
@@ -132,7 +137,7 @@ class TestomatClient {
|
|
|
132
137
|
}
|
|
133
138
|
|
|
134
139
|
for (const file of files) {
|
|
135
|
-
uploadedFiles.push(uploadFile(file, this.runId));
|
|
140
|
+
uploadedFiles.push(upload.uploadFile(file, this.runId));
|
|
136
141
|
}
|
|
137
142
|
}
|
|
138
143
|
|
|
@@ -142,6 +147,8 @@ class TestomatClient {
|
|
|
142
147
|
|
|
143
148
|
global.testomatioArtifacts = [];
|
|
144
149
|
|
|
150
|
+
this.totalUploaded += uploadedFiles.filter((n) => n).length;
|
|
151
|
+
|
|
145
152
|
const json = JsonCycle.stringify({
|
|
146
153
|
api_key: this.apiKey,
|
|
147
154
|
files,
|
|
@@ -173,7 +180,7 @@ class TestomatClient {
|
|
|
173
180
|
.catch((err) => {
|
|
174
181
|
if (err.response) {
|
|
175
182
|
if (err.response.status >= 400) {
|
|
176
|
-
const data = err.response.data || { message:
|
|
183
|
+
const data = err.response.data || { message: "" };
|
|
177
184
|
console.log(
|
|
178
185
|
APP_PREFIX,
|
|
179
186
|
chalk.blue(title),
|
|
@@ -187,7 +194,12 @@ class TestomatClient {
|
|
|
187
194
|
`Report couldn't be processed: ${err.response.data.message}`
|
|
188
195
|
);
|
|
189
196
|
} else {
|
|
190
|
-
console.log(
|
|
197
|
+
console.log(
|
|
198
|
+
APP_PREFIX,
|
|
199
|
+
chalk.blue(title),
|
|
200
|
+
"Report couldn't be processed",
|
|
201
|
+
err
|
|
202
|
+
);
|
|
191
203
|
}
|
|
192
204
|
});
|
|
193
205
|
|
|
@@ -220,6 +232,15 @@ class TestomatClient {
|
|
|
220
232
|
chalk.magenta(this.runUrl)
|
|
221
233
|
);
|
|
222
234
|
}
|
|
235
|
+
|
|
236
|
+
if (upload.isEnabled() && this.totalUploaded > 0) {
|
|
237
|
+
console.log(
|
|
238
|
+
APP_PREFIX,
|
|
239
|
+
`🗄️ Total ${this.totalUploaded} artifacts ${
|
|
240
|
+
upload.isPrivate ? "privately" : chalk.bold("publicly")
|
|
241
|
+
} uploaded to S3 bucket `
|
|
242
|
+
);
|
|
243
|
+
}
|
|
223
244
|
}
|
|
224
245
|
})
|
|
225
246
|
.catch((err) => {
|
|
@@ -228,6 +249,12 @@ class TestomatClient {
|
|
|
228
249
|
return this.queue;
|
|
229
250
|
}
|
|
230
251
|
|
|
252
|
+
formatSteps(stack, steps) {
|
|
253
|
+
return stack
|
|
254
|
+
? `${steps}\n\n${chalk.bold.red("################[ Failure ]################")}\n${stack}`
|
|
255
|
+
: steps;
|
|
256
|
+
}
|
|
257
|
+
|
|
231
258
|
formatError(error, message) {
|
|
232
259
|
if (!message) message = error.message;
|
|
233
260
|
if (error.inspect) message = error.inspect();
|
package/lib/fileUploader.js
CHANGED
|
@@ -2,13 +2,22 @@ const AWS = require("aws-sdk");
|
|
|
2
2
|
const fs = require("fs");
|
|
3
3
|
const path = require("path");
|
|
4
4
|
const chalk = require("chalk");
|
|
5
|
+
const { APP_PREFIX } = require("./constants");
|
|
6
|
+
|
|
7
|
+
const isPrivate = process.env.TESTOMATIO_PRIVATE_ARTIFACTS;
|
|
8
|
+
|
|
9
|
+
function isEnabled() {
|
|
10
|
+
return process.env.S3_BUCKET && !process.env.TESTOMATIO_DISABLE_ARTIFACTS;
|
|
11
|
+
}
|
|
5
12
|
|
|
6
13
|
const uploadUsingS3 = async (filePath, runId) => {
|
|
7
14
|
const region = process.env.S3_REGION;
|
|
8
15
|
const bucket = process.env.S3_BUCKET;
|
|
9
16
|
const accessKeyId = process.env.S3_ACCESS_KEY_ID;
|
|
10
17
|
const secretAccessKey = process.env.S3_SECRET_ACCESS_KEY;
|
|
11
|
-
const
|
|
18
|
+
const isDisabled = process.env.TESTOMATIO_DISABLE_ARTIFACTS;
|
|
19
|
+
|
|
20
|
+
if (isDisabled) return;
|
|
12
21
|
|
|
13
22
|
let contentType;
|
|
14
23
|
let fileName;
|
|
@@ -28,23 +37,43 @@ const uploadUsingS3 = async (filePath, runId) => {
|
|
|
28
37
|
const file = fs.readFileSync(filePath);
|
|
29
38
|
|
|
30
39
|
fileName = `${runId}/${fileName || path.basename(filePath)}`;
|
|
40
|
+
const acl = isPrivate ? "private" : "public-read"
|
|
31
41
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
try {
|
|
43
|
+
const out = await s3
|
|
44
|
+
.upload({
|
|
45
|
+
Bucket: bucket,
|
|
46
|
+
Key: fileName,
|
|
47
|
+
Body: file,
|
|
48
|
+
ContentType: contentType,
|
|
49
|
+
ACL: acl,
|
|
50
|
+
})
|
|
51
|
+
.promise();
|
|
52
|
+
return out.Location;
|
|
53
|
+
} catch (e) {
|
|
54
|
+
console.log(APP_PREFIX, chalk.bold.red(`Failed uploading '${fileName}'. Please check S3 credentials`), {
|
|
55
|
+
accessKeyId,
|
|
56
|
+
secretAccessKey: secretAccessKey ? '**** (hidden) ***' : '(empty)',
|
|
57
|
+
region,
|
|
58
|
+
bucket,
|
|
59
|
+
acl,
|
|
60
|
+
endpoint: process.env.S3_ENDPOINT
|
|
39
61
|
})
|
|
40
|
-
.promise();
|
|
41
|
-
return out.Location;
|
|
42
|
-
};
|
|
43
62
|
|
|
63
|
+
console.log(APP_PREFIX, `To ${chalk.bold('disable')} artifact uploads set: TESTOMATIO_DISABLE_ARTIFACTS=1`)
|
|
64
|
+
if (!isPrivate) {
|
|
65
|
+
console.log(APP_PREFIX, `To enable ${chalk.bold('PRIVATE')} uploads set: TESTOMATIO_PRIVATE_ARTIFACTS=1`)
|
|
66
|
+
} else {
|
|
67
|
+
console.log(APP_PREFIX, `To enable ${chalk.bold('PUBLIC')} uploads remove TESTOMATIO_PRIVATE_ARTIFACTS env variable`)
|
|
68
|
+
}
|
|
69
|
+
console.log(APP_PREFIX, '---------------')
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
44
73
|
|
|
45
74
|
const uploadFile = async (filePath, runId) => {
|
|
46
75
|
try {
|
|
47
|
-
if (
|
|
76
|
+
if (isEnabled()) {
|
|
48
77
|
return uploadUsingS3(filePath, runId);
|
|
49
78
|
}
|
|
50
79
|
} catch (e) {
|
|
@@ -55,4 +84,6 @@ const uploadFile = async (filePath, runId) => {
|
|
|
55
84
|
|
|
56
85
|
module.exports = {
|
|
57
86
|
uploadFile,
|
|
87
|
+
isPrivate,
|
|
88
|
+
isEnabled,
|
|
58
89
|
};
|