@testomatio/reporter 0.5.0-beta.3 → 0.5.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/Changelog.md +5 -0
- package/lib/adapter/codecept.js +1 -1
- package/lib/adapter/playwright.js +1 -1
- package/lib/adapter/webdriver.js +21 -5
- package/lib/client.js +18 -10
- package/lib/fileUploader.js +119 -38
- package/package.json +1 -1
package/Changelog.md
CHANGED
package/lib/adapter/codecept.js
CHANGED
|
@@ -72,7 +72,7 @@ function CodeceptReporter(config) {
|
|
|
72
72
|
});
|
|
73
73
|
|
|
74
74
|
event.dispatcher.on(event.all.result, async () => {
|
|
75
|
-
if (videos.length && upload.
|
|
75
|
+
if (videos.length && upload.isArtifactsEnabled) {
|
|
76
76
|
console.log(TRConstants.APP_PREFIX, `🎞️ Uploading ${videos.length} videos...`);
|
|
77
77
|
|
|
78
78
|
const promises = [];
|
|
@@ -75,7 +75,7 @@ class TestomatioReporter {
|
|
|
75
75
|
async onEnd(result) {
|
|
76
76
|
if (!this.client) return;
|
|
77
77
|
|
|
78
|
-
if (this.videos.length && upload.
|
|
78
|
+
if (this.videos.length && upload.isArtifactsEnabled) {
|
|
79
79
|
console.log(Status.APP_PREFIX, `🎞️ Uploading ${this.videos.length} videos...`);
|
|
80
80
|
|
|
81
81
|
const promises = [];
|
package/lib/adapter/webdriver.js
CHANGED
|
@@ -11,23 +11,39 @@ class WebdriverReporter extends WDIOReporter {
|
|
|
11
11
|
|
|
12
12
|
this.client = new TestomatClient({ apiKey });
|
|
13
13
|
options = Object.assign(options, { stdout: true });
|
|
14
|
+
|
|
15
|
+
this._isSynchronising = false;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get isSynchronised() {
|
|
19
|
+
return this._isSynchronising === false;
|
|
14
20
|
}
|
|
15
21
|
|
|
16
|
-
onTestEnd(test) {
|
|
17
|
-
this.
|
|
22
|
+
async onTestEnd(test) {
|
|
23
|
+
this._isSynchronising = true;
|
|
24
|
+
|
|
25
|
+
await this.addTest(test);
|
|
26
|
+
|
|
27
|
+
this._isSynchronising = false;
|
|
18
28
|
}
|
|
19
29
|
|
|
20
|
-
addTest(test) {
|
|
30
|
+
async addTest(test) {
|
|
21
31
|
if (!this.client) return;
|
|
22
32
|
|
|
23
|
-
const { title, _duration: duration, state, error } = test;
|
|
33
|
+
const { title, _duration: duration, state, error, output } = test;
|
|
24
34
|
|
|
25
35
|
const testId = parseTest(title);
|
|
26
36
|
|
|
27
|
-
|
|
37
|
+
const screenshotEndpoint = '/session/:sessionId/screenshot';
|
|
38
|
+
const screenshotsBuffers = output
|
|
39
|
+
.filter(el => el.endpoint === screenshotEndpoint && el.result && el.result.value)
|
|
40
|
+
.map(el => Buffer.from(el.result.value, 'base64'));
|
|
41
|
+
|
|
42
|
+
await this.client.addTestRun(testId, state, {
|
|
28
43
|
error,
|
|
29
44
|
title,
|
|
30
45
|
time: duration,
|
|
46
|
+
filesBuffers: screenshotsBuffers,
|
|
31
47
|
});
|
|
32
48
|
}
|
|
33
49
|
}
|
package/lib/client.js
CHANGED
|
@@ -65,12 +65,14 @@ class TestomatClient {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
this.queue = this.queue
|
|
68
|
-
.then(() =>
|
|
69
|
-
this.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
.then(() =>
|
|
69
|
+
this.axios.post(`${TESTOMAT_URL.trim()}/api/reporter`, runParams).then(resp => {
|
|
70
|
+
this.runId = resp.data.uid;
|
|
71
|
+
this.runUrl = `${TESTOMAT_URL}/${resp.data.url.split('/').splice(3).join('/')}`;
|
|
72
|
+
console.log(APP_PREFIX, '📊 Report created. Report ID:', this.runId, chalk.gray(`v${this.version}`));
|
|
73
|
+
process.env.runId = this.runId;
|
|
74
|
+
}),
|
|
75
|
+
)
|
|
74
76
|
.catch(() => {
|
|
75
77
|
console.log(
|
|
76
78
|
APP_PREFIX,
|
|
@@ -92,6 +94,7 @@ class TestomatClient {
|
|
|
92
94
|
time = '',
|
|
93
95
|
example = null,
|
|
94
96
|
files = [],
|
|
97
|
+
filesBuffers = [],
|
|
95
98
|
steps,
|
|
96
99
|
title,
|
|
97
100
|
suite_title,
|
|
@@ -121,7 +124,11 @@ class TestomatClient {
|
|
|
121
124
|
}
|
|
122
125
|
|
|
123
126
|
for (const file of files) {
|
|
124
|
-
uploadedFiles.push(upload.
|
|
127
|
+
uploadedFiles.push(upload.uploadFileByPath(file, this.runId));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
for (const [idx, buffer] of filesBuffers.entries()) {
|
|
131
|
+
uploadedFiles.push(upload.uploadFileAsBuffer(buffer, idx + 1, this.runId));
|
|
125
132
|
}
|
|
126
133
|
}
|
|
127
134
|
|
|
@@ -200,11 +207,11 @@ class TestomatClient {
|
|
|
200
207
|
console.log(APP_PREFIX, '📊 Report Saved. Report URL:', chalk.magenta(this.runUrl));
|
|
201
208
|
}
|
|
202
209
|
|
|
203
|
-
if (upload.
|
|
210
|
+
if (upload.isArtifactsEnabled && this.totalUploaded > 0) {
|
|
204
211
|
console.log(
|
|
205
212
|
APP_PREFIX,
|
|
206
213
|
`🗄️ Total ${this.totalUploaded} artifacts ${
|
|
207
|
-
|
|
214
|
+
process.env.TESTOMATIO_PRIVATE_ARTIFACTS ? 'privately' : chalk.bold('publicly')
|
|
208
215
|
} uploaded to S3 bucket `,
|
|
209
216
|
);
|
|
210
217
|
}
|
|
@@ -240,7 +247,8 @@ class TestomatClient {
|
|
|
240
247
|
});
|
|
241
248
|
if (record && !record.filename.startsWith('http')) {
|
|
242
249
|
stack += record.renderSync({
|
|
243
|
-
stackFilter: frame =>
|
|
250
|
+
stackFilter: frame =>
|
|
251
|
+
frame.getFileName().indexOf(sep) > -1 &&
|
|
244
252
|
frame.getFileName().indexOf('node_modules') < 0 &&
|
|
245
253
|
frame.getFileName().indexOf('internal') < 0,
|
|
246
254
|
});
|
package/lib/fileUploader.js
CHANGED
|
@@ -5,63 +5,83 @@ const chalk = require('chalk');
|
|
|
5
5
|
const memoize = require('lodash.memoize');
|
|
6
6
|
const { APP_PREFIX } = require('./constants');
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
|
|
8
|
+
const {
|
|
9
|
+
S3_ENDPOINT,
|
|
10
|
+
S3_REGION,
|
|
11
|
+
S3_BUCKET,
|
|
12
|
+
S3_ACCESS_KEY_ID,
|
|
13
|
+
S3_SECRET_ACCESS_KEY,
|
|
14
|
+
TESTOMATIO_DISABLE_ARTIFACTS,
|
|
15
|
+
TESTOMATIO_PRIVATE_ARTIFACTS,
|
|
16
|
+
S3_FORCE_PATH_STYLE,
|
|
17
|
+
} = process.env;
|
|
10
18
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
const secretAccessKey = process.env.S3_SECRET_ACCESS_KEY;
|
|
16
|
-
const isDisabled = process.env.TESTOMATIO_DISABLE_ARTIFACTS;
|
|
17
|
-
const s3ForcePathStyle = process.env.S3_FORCE_PATH_STYLE;
|
|
19
|
+
const isArtifactsEnabled = S3_BUCKET && !TESTOMATIO_DISABLE_ARTIFACTS;
|
|
20
|
+
|
|
21
|
+
const _getFileExtBase64 = str => {
|
|
22
|
+
const type = str.charAt(0);
|
|
18
23
|
|
|
19
|
-
|
|
24
|
+
return (
|
|
25
|
+
{
|
|
26
|
+
'/': '.jpg',
|
|
27
|
+
i: '.png',
|
|
28
|
+
R: '.gif',
|
|
29
|
+
U: '.webp',
|
|
30
|
+
}[type] || ''
|
|
31
|
+
);
|
|
32
|
+
};
|
|
20
33
|
|
|
21
|
-
|
|
22
|
-
let
|
|
34
|
+
const uploadUsingS3 = async (filePath, runId) => {
|
|
35
|
+
let ContentType;
|
|
36
|
+
let Key;
|
|
23
37
|
|
|
24
38
|
if (typeof filePath === 'object') {
|
|
25
|
-
|
|
39
|
+
ContentType = filePath.type;
|
|
26
40
|
filePath = filePath.path;
|
|
27
|
-
|
|
41
|
+
Key = filePath.name;
|
|
28
42
|
}
|
|
29
43
|
|
|
30
|
-
const config = {
|
|
31
|
-
|
|
32
|
-
|
|
44
|
+
const config = {
|
|
45
|
+
region: S3_REGION,
|
|
46
|
+
accessKeyId: S3_ACCESS_KEY_ID,
|
|
47
|
+
secretAccessKey: S3_SECRET_ACCESS_KEY,
|
|
48
|
+
s3ForcePathStyle: S3_FORCE_PATH_STYLE,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
if (S3_ENDPOINT) {
|
|
52
|
+
config.endpoint = new AWS.Endpoint(S3_ENDPOINT);
|
|
33
53
|
}
|
|
34
54
|
|
|
35
55
|
const s3 = new AWS.S3(config);
|
|
36
56
|
const file = fs.readFileSync(filePath);
|
|
37
57
|
|
|
38
|
-
|
|
39
|
-
const
|
|
58
|
+
Key = `${runId}/${Key || path.basename(filePath)}`;
|
|
59
|
+
const ACL = TESTOMATIO_PRIVATE_ARTIFACTS ? 'private' : 'public-read';
|
|
40
60
|
|
|
41
61
|
try {
|
|
42
62
|
const out = await s3
|
|
43
63
|
.upload({
|
|
44
|
-
Bucket:
|
|
45
|
-
Key
|
|
64
|
+
Bucket: S3_BUCKET,
|
|
65
|
+
Key,
|
|
46
66
|
Body: file,
|
|
47
|
-
ContentType
|
|
48
|
-
ACL
|
|
67
|
+
ContentType,
|
|
68
|
+
ACL,
|
|
49
69
|
})
|
|
50
70
|
.promise();
|
|
51
71
|
|
|
52
72
|
return out.Location;
|
|
53
73
|
} catch (e) {
|
|
54
|
-
console.log(APP_PREFIX, chalk.bold.red(`Failed uploading '${
|
|
55
|
-
accessKeyId,
|
|
56
|
-
secretAccessKey:
|
|
57
|
-
region,
|
|
58
|
-
bucket,
|
|
59
|
-
acl,
|
|
60
|
-
endpoint:
|
|
74
|
+
console.log(APP_PREFIX, chalk.bold.red(`Failed uploading '${Key}'. Please check S3 credentials`), {
|
|
75
|
+
accessKeyId: S3_ACCESS_KEY_ID,
|
|
76
|
+
secretAccessKey: S3_SECRET_ACCESS_KEY ? '**** (hidden) ***' : '(empty)',
|
|
77
|
+
region: S3_REGION,
|
|
78
|
+
bucket: S3_BUCKET,
|
|
79
|
+
acl: ACL,
|
|
80
|
+
endpoint: S3_ENDPOINT,
|
|
61
81
|
});
|
|
62
82
|
|
|
63
83
|
console.log(APP_PREFIX, `To ${chalk.bold('disable')} artifact uploads set: TESTOMATIO_DISABLE_ARTIFACTS=1`);
|
|
64
|
-
if (!
|
|
84
|
+
if (!TESTOMATIO_PRIVATE_ARTIFACTS) {
|
|
65
85
|
console.log(APP_PREFIX, `To enable ${chalk.bold('PRIVATE')} uploads set: TESTOMATIO_PRIVATE_ARTIFACTS=1`);
|
|
66
86
|
} else {
|
|
67
87
|
console.log(
|
|
@@ -70,23 +90,84 @@ const uploadUsingS3 = async (filePath, runId) => {
|
|
|
70
90
|
);
|
|
71
91
|
}
|
|
72
92
|
console.log(APP_PREFIX, '---------------');
|
|
73
|
-
return null;
|
|
74
93
|
}
|
|
75
94
|
};
|
|
76
95
|
|
|
77
|
-
const
|
|
96
|
+
const uploadUsingS3AsBuffer = async (buffer, fileName, runId) => {
|
|
97
|
+
const config = {
|
|
98
|
+
region: S3_REGION,
|
|
99
|
+
accessKeyId: S3_ACCESS_KEY_ID,
|
|
100
|
+
secretAccessKey: S3_SECRET_ACCESS_KEY,
|
|
101
|
+
s3ForcePathStyle: S3_FORCE_PATH_STYLE,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
if (S3_ENDPOINT) {
|
|
105
|
+
config.endpoint = new AWS.Endpoint(S3_ENDPOINT);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const s3 = new AWS.S3(config);
|
|
109
|
+
|
|
110
|
+
const ACL = TESTOMATIO_PRIVATE_ARTIFACTS ? 'private' : 'public-read';
|
|
111
|
+
|
|
112
|
+
const fileExtension = _getFileExtBase64(buffer.toString('base64'));
|
|
113
|
+
const Key = `${runId}/${fileName}${fileExtension}`;
|
|
114
|
+
|
|
78
115
|
try {
|
|
79
|
-
|
|
116
|
+
const out = await s3
|
|
117
|
+
.upload({
|
|
118
|
+
Bucket: S3_BUCKET,
|
|
119
|
+
Key,
|
|
120
|
+
Body: buffer,
|
|
121
|
+
ACL,
|
|
122
|
+
})
|
|
123
|
+
.promise();
|
|
124
|
+
|
|
125
|
+
return out.Location;
|
|
126
|
+
} catch (e) {
|
|
127
|
+
console.log(APP_PREFIX, chalk.bold.red(`Failed uploading '${Key}'. Please check S3 credentials`), {
|
|
128
|
+
accessKeyId: S3_ACCESS_KEY_ID,
|
|
129
|
+
secretAccessKey: S3_SECRET_ACCESS_KEY ? '**** (hidden) ***' : '(empty)',
|
|
130
|
+
region: S3_REGION,
|
|
131
|
+
bucket: S3_BUCKET,
|
|
132
|
+
acl: ACL,
|
|
133
|
+
endpoint: S3_ENDPOINT,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
console.log(APP_PREFIX, `To ${chalk.bold('disable')} artifact uploads set: TESTOMATIO_DISABLE_ARTIFACTS=1`);
|
|
137
|
+
if (!TESTOMATIO_PRIVATE_ARTIFACTS) {
|
|
138
|
+
console.log(APP_PREFIX, `To enable ${chalk.bold('PRIVATE')} uploads set: TESTOMATIO_PRIVATE_ARTIFACTS=1`);
|
|
139
|
+
} else {
|
|
140
|
+
console.log(
|
|
141
|
+
APP_PREFIX,
|
|
142
|
+
`To enable ${chalk.bold('PUBLIC')} uploads remove TESTOMATIO_PRIVATE_ARTIFACTS env variable`,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
console.log(APP_PREFIX, '---------------');
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const uploadFileByPath = async (filePath, runId) => {
|
|
150
|
+
try {
|
|
151
|
+
if (isArtifactsEnabled) {
|
|
80
152
|
return uploadUsingS3(filePath, runId);
|
|
81
153
|
}
|
|
82
154
|
} catch (e) {
|
|
83
155
|
console.error(chalk.red('Error occurred while uploading artifacts'), e);
|
|
84
156
|
}
|
|
85
|
-
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const uploadFileAsBuffer = async (buffer, fileName, runId) => {
|
|
160
|
+
try {
|
|
161
|
+
if (isArtifactsEnabled) {
|
|
162
|
+
return uploadUsingS3AsBuffer(buffer, fileName, runId);
|
|
163
|
+
}
|
|
164
|
+
} catch (e) {
|
|
165
|
+
console.error(chalk.red('Error occurred while uploading artifacts'), e);
|
|
166
|
+
}
|
|
86
167
|
};
|
|
87
168
|
|
|
88
169
|
module.exports = {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
170
|
+
uploadFileByPath: memoize(uploadFileByPath),
|
|
171
|
+
uploadFileAsBuffer: memoize(uploadFileAsBuffer),
|
|
172
|
+
isArtifactsEnabled,
|
|
92
173
|
};
|