@testomatio/reporter 1.6.6 → 1.6.8
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/lib/adapter/playwright.js +28 -4
- package/lib/client.js +25 -5
- package/lib/pipe/testomatio.js +11 -3
- package/lib/reporter-functions.js +6 -2
- package/lib/uploader.js +10 -3
- package/lib/utils/pipe_utils.js +2 -2
- package/lib/xmlReader.js +4 -1
- package/package.json +1 -1
|
@@ -55,11 +55,28 @@ class PlaywrightReporter {
|
|
|
55
55
|
logs = `\n\n${chalk.bold('Logs:')}\n${chalk.red(result.stderr.join(''))}\n${result.stdout.join('')}`;
|
|
56
56
|
}
|
|
57
57
|
const manuallyAttachedArtifacts = services.artifacts.get(fullTestTitle);
|
|
58
|
-
const
|
|
58
|
+
const testMeta = services.keyValues.get(fullTestTitle);
|
|
59
59
|
const rid = test.id || test.testId || uuidv4();
|
|
60
60
|
|
|
61
|
+
/**
|
|
62
|
+
* @type {{
|
|
63
|
+
* browser?: string,
|
|
64
|
+
* dependencies: string[],
|
|
65
|
+
* isMobile?: boolean
|
|
66
|
+
* metadata: Record<string, any>,
|
|
67
|
+
* name: string,
|
|
68
|
+
* }}
|
|
69
|
+
*/
|
|
70
|
+
const project = {
|
|
71
|
+
browser: test.parent.project().use.defaultBrowserType,
|
|
72
|
+
dependencies: test.parent.project().dependencies,
|
|
73
|
+
isMobile: test.parent.project().use.isMobile,
|
|
74
|
+
metadata: test.parent.project().metadata,
|
|
75
|
+
name: test.parent.project().name,
|
|
76
|
+
};
|
|
77
|
+
|
|
61
78
|
const reportTestPromise = this.client.addTestRun(checkStatus(result.status), {
|
|
62
|
-
rid
|
|
79
|
+
rid: `${rid}-${project.name}`,
|
|
63
80
|
error,
|
|
64
81
|
test_id: getTestomatIdFromTestTitle(`${title} ${test.tags?.join(' ')}`),
|
|
65
82
|
suite_title,
|
|
@@ -68,12 +85,19 @@ class PlaywrightReporter {
|
|
|
68
85
|
time: duration,
|
|
69
86
|
logs,
|
|
70
87
|
manuallyAttachedArtifacts,
|
|
71
|
-
meta:
|
|
88
|
+
meta: {
|
|
89
|
+
browser: project.browser,
|
|
90
|
+
isMobile: project.isMobile,
|
|
91
|
+
project: project.name,
|
|
92
|
+
projectDependencies: project.dependencies?.length ? project.dependencies : null,
|
|
93
|
+
...testMeta,
|
|
94
|
+
...project.metadata, // metadata has any type (in playwright), but we will stringify it in client.js
|
|
95
|
+
},
|
|
72
96
|
file: test.location?.file,
|
|
73
97
|
});
|
|
74
98
|
|
|
75
99
|
this.uploads.push({
|
|
76
|
-
rid
|
|
100
|
+
rid: `${rid}-${project.name}`,
|
|
77
101
|
title: test.title,
|
|
78
102
|
files: result.attachments.filter(a => a.body || a.path),
|
|
79
103
|
file: test.location?.file,
|
package/lib/client.js
CHANGED
|
@@ -165,6 +165,28 @@ class Client {
|
|
|
165
165
|
} = testData;
|
|
166
166
|
let { message = '' } = testData;
|
|
167
167
|
|
|
168
|
+
// stringify meta values and limit keys and values length to 255
|
|
169
|
+
if (meta) {
|
|
170
|
+
for (const key in meta) {
|
|
171
|
+
if (key === undefined || key === null) continue;
|
|
172
|
+
if (typeof meta[key] === 'object') {
|
|
173
|
+
meta[key] = JSON.stringify(meta[key]);
|
|
174
|
+
}
|
|
175
|
+
// cut values
|
|
176
|
+
if (meta[key].length > 255) {
|
|
177
|
+
meta[key] = meta[key].substring(0, 255);
|
|
178
|
+
debug(APP_PREFIX, `Meta info value "${meta[key]}" is too long, trimmed to 255 characters`);
|
|
179
|
+
}
|
|
180
|
+
// cut keys
|
|
181
|
+
if (key.length > 255) {
|
|
182
|
+
const newKey = key.substring(0, 255);
|
|
183
|
+
meta[newKey] = meta[key];
|
|
184
|
+
delete meta[key];
|
|
185
|
+
debug(APP_PREFIX, `Meta info key "${key}" is too long, trimmed to 255 characters`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
168
190
|
let errorFormatted = '';
|
|
169
191
|
if (error) {
|
|
170
192
|
errorFormatted += this.formatError(error) || '';
|
|
@@ -259,7 +281,7 @@ class Client {
|
|
|
259
281
|
APP_PREFIX,
|
|
260
282
|
`🗄️ ${this.uploader.successfulUploads.length} artifacts ${
|
|
261
283
|
process.env.TESTOMATIO_PRIVATE_ARTIFACTS ? 'privately' : chalk.bold('publicly')
|
|
262
|
-
} 🟢
|
|
284
|
+
} 🟢uploaded to S3 bucket`,
|
|
263
285
|
);
|
|
264
286
|
}
|
|
265
287
|
|
|
@@ -275,7 +297,7 @@ class Client {
|
|
|
275
297
|
|
|
276
298
|
uploadedArtifacts.forEach(upload => {
|
|
277
299
|
debug(
|
|
278
|
-
`🟢
|
|
300
|
+
`🟢Uploaded artifact`,
|
|
279
301
|
`${upload.relativePath},`,
|
|
280
302
|
'size:',
|
|
281
303
|
`${upload.sizePretty},`,
|
|
@@ -311,9 +333,7 @@ class Client {
|
|
|
311
333
|
console.log(
|
|
312
334
|
'\n',
|
|
313
335
|
APP_PREFIX,
|
|
314
|
-
`🗄️ ${chalk.bold(this.uploader.skippedUploads.length)} artifacts uploading 🟡${chalk.bold(
|
|
315
|
-
'skipped',
|
|
316
|
-
)} (due to large size)`,
|
|
336
|
+
`🗄️ ${chalk.bold(this.uploader.skippedUploads.length)} artifacts uploading 🟡${chalk.bold('skipped')}`,
|
|
317
337
|
);
|
|
318
338
|
const skippedUploads = this.uploader.skippedUploads.map(file => ({
|
|
319
339
|
relativePath: file.path.replace(process.cwd(), ''),
|
package/lib/pipe/testomatio.js
CHANGED
|
@@ -215,7 +215,7 @@ class TestomatioPipe {
|
|
|
215
215
|
this.store.runPublicUrl = this.runPublicUrl;
|
|
216
216
|
this.store.runId = this.runId;
|
|
217
217
|
console.log(APP_PREFIX, '📊 Report created. Report ID:', this.runId);
|
|
218
|
-
process.env.runId = this.runId;
|
|
218
|
+
process.env.runId = this.runId;
|
|
219
219
|
debug('Run created', this.runId);
|
|
220
220
|
} catch (err) {
|
|
221
221
|
const errorText = err.response?.data?.message || err.message;
|
|
@@ -241,7 +241,7 @@ class TestomatioPipe {
|
|
|
241
241
|
const cancelReporting = this.requestFailures >= parseInt(process.env.TESTOMATIO_MAX_REQUEST_FAILURES, 10);
|
|
242
242
|
if (cancelReporting) {
|
|
243
243
|
this.reportingCanceledDueToReqFailures = true;
|
|
244
|
-
const errorMessage =
|
|
244
|
+
const errorMessage =
|
|
245
245
|
`⚠️ ${process.env.TESTOMATIO_MAX_REQUEST_FAILURES} requests were failed, reporting to Testomat aborted.`;
|
|
246
246
|
console.warn(`${APP_PREFIX} ${chalk.yellow(errorMessage)}`);
|
|
247
247
|
}
|
|
@@ -371,7 +371,14 @@ class TestomatioPipe {
|
|
|
371
371
|
if (!this.isEnabled) return;
|
|
372
372
|
|
|
373
373
|
await this.#batchUpload();
|
|
374
|
-
if (this.batch.intervalFunction)
|
|
374
|
+
if (this.batch.intervalFunction) {
|
|
375
|
+
clearInterval(this.batch.intervalFunction);
|
|
376
|
+
// this code is required in case test is added after run is finished
|
|
377
|
+
// (e.g. if test has artifacts, add test function will be invoked only after artifacts are uploaded)
|
|
378
|
+
// batch stops working after run is finished; thus, disable it to use single test uploading
|
|
379
|
+
this.batch.intervalFunction = null;
|
|
380
|
+
this.batch.isEnabled = false;
|
|
381
|
+
}
|
|
375
382
|
|
|
376
383
|
debug('Finishing run...');
|
|
377
384
|
|
|
@@ -396,6 +403,7 @@ class TestomatioPipe {
|
|
|
396
403
|
await this.axios.put(`/api/reporter/${this.runId}`, {
|
|
397
404
|
api_key: this.apiKey,
|
|
398
405
|
status_event,
|
|
406
|
+
detach: params.detach,
|
|
399
407
|
duration: params.duration,
|
|
400
408
|
tests: params.tests,
|
|
401
409
|
});
|
|
@@ -32,9 +32,13 @@ function addStep(message) {
|
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* Add key-value pair(s) to the test report
|
|
35
|
-
* @param {*} keyValue
|
|
35
|
+
* @param {*} keyValue or key
|
|
36
|
+
* @param {string?} value
|
|
36
37
|
*/
|
|
37
|
-
function setKeyValue(keyValue) {
|
|
38
|
+
function setKeyValue(keyValue, value = null) {
|
|
39
|
+
if (value && typeof keyValue === 'string') {
|
|
40
|
+
keyValue = { [keyValue]: value };
|
|
41
|
+
}
|
|
38
42
|
services.keyValues.put(keyValue);
|
|
39
43
|
}
|
|
40
44
|
|
package/lib/uploader.js
CHANGED
|
@@ -106,14 +106,14 @@ class S3Uploader {
|
|
|
106
106
|
|
|
107
107
|
debug('Uploading to S3:', Key);
|
|
108
108
|
|
|
109
|
-
const s3Config = this.#getS3Config()
|
|
109
|
+
const s3Config = this.#getS3Config();
|
|
110
110
|
const s3 = new S3(s3Config);
|
|
111
111
|
|
|
112
112
|
const params = {
|
|
113
113
|
Bucket: S3_BUCKET,
|
|
114
114
|
Key,
|
|
115
115
|
Body,
|
|
116
|
-
}
|
|
116
|
+
};
|
|
117
117
|
|
|
118
118
|
// disable ACL for I AM roles
|
|
119
119
|
if (!s3Config.credentials.sessionToken) {
|
|
@@ -206,6 +206,9 @@ class S3Uploader {
|
|
|
206
206
|
* @returns
|
|
207
207
|
*/
|
|
208
208
|
async uploadFileByPath(filePath, pathInS3) {
|
|
209
|
+
// sometimes artifacts uploading started before createRun function completion
|
|
210
|
+
this.isEnabled = this.isEnabled ?? this.checkEnabled();
|
|
211
|
+
|
|
209
212
|
const [runId, rid] = pathInS3;
|
|
210
213
|
|
|
211
214
|
if (!filePath) return;
|
|
@@ -248,7 +251,11 @@ class S3Uploader {
|
|
|
248
251
|
this.storeUploadedFile(filePath, runId, rid, false);
|
|
249
252
|
this.skippedUploads.push(skippedArtifact);
|
|
250
253
|
debug(
|
|
251
|
-
chalk.yellow(
|
|
254
|
+
chalk.yellow(
|
|
255
|
+
`Artifacts file ${JSON.stringify(skippedArtifact)} exceeds the maximum allowed size ${
|
|
256
|
+
TESTOMATIO_ARTIFACT_MAX_SIZE_MB
|
|
257
|
+
}MB. Skipping.`,
|
|
258
|
+
),
|
|
252
259
|
);
|
|
253
260
|
return;
|
|
254
261
|
}
|
package/lib/utils/pipe_utils.js
CHANGED
|
@@ -15,8 +15,8 @@ function setS3Credentials(artifacts) {
|
|
|
15
15
|
if (artifacts.BUCKET) process.env.S3_BUCKET = artifacts.BUCKET;
|
|
16
16
|
if (artifacts.SESSION_TOKEN) process.env.S3_SESSION_TOKEN = artifacts.SESSION_TOKEN;
|
|
17
17
|
if (artifacts.presign) process.env.TESTOMATIO_PRIVATE_ARTIFACTS = '1';
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
// endpoint is not received from the server; and shuld be empty if IAM used (credentails obtained from the testomat)
|
|
19
|
+
process.env.S3_ENDPOINT = artifacts.ENDPOINT || '';
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
22
|
* Generates mode request parameters based on the input params.
|
package/lib/xmlReader.js
CHANGED
|
@@ -21,7 +21,8 @@ const config = require('./config');
|
|
|
21
21
|
const ridRunId = randomUUID();
|
|
22
22
|
|
|
23
23
|
const TESTOMATIO_URL = process.env.TESTOMATIO_URL || 'https://app.testomat.io';
|
|
24
|
-
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN } =
|
|
24
|
+
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN, TESTOMATIO_MARK_DETACHED } =
|
|
25
|
+
process.env;
|
|
25
26
|
|
|
26
27
|
const options = {
|
|
27
28
|
ignoreDeclaration: true,
|
|
@@ -41,6 +42,7 @@ class XmlReader {
|
|
|
41
42
|
title: TESTOMATIO_TITLE,
|
|
42
43
|
env: TESTOMATIO_ENV,
|
|
43
44
|
group_title: TESTOMATIO_RUNGROUP_TITLE,
|
|
45
|
+
detach: !!TESTOMATIO_MARK_DETACHED,
|
|
44
46
|
// batch uploading is implemented for xml already
|
|
45
47
|
isBatchEnabled: false,
|
|
46
48
|
};
|
|
@@ -424,6 +426,7 @@ class XmlReader {
|
|
|
424
426
|
|
|
425
427
|
const dataString = {
|
|
426
428
|
...this.stats,
|
|
429
|
+
detach: this.requestParams.detach,
|
|
427
430
|
api_key: this.requestParams.apiKey,
|
|
428
431
|
status: 'finished',
|
|
429
432
|
tests: this.tests,
|