@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.
@@ -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 keyValues = services.keyValues.get(fullTestTitle);
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: keyValues,
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
- } 🟢 uploaded to S3 bucket`,
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
- `🟢 Uploaded artifact`,
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(), ''),
@@ -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) clearInterval(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(`Artifacts file ${JSON.stringify(skippedArtifact)} exceeds the maximum allowed size. Skipping.`),
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
  }
@@ -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
- // 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 || '';
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 } = process.env;
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/reporter",
3
- "version": "1.6.6",
3
+ "version": "1.6.8",
4
4
  "description": "Testomatio Reporter Client",
5
5
  "main": "./lib/reporter.js",
6
6
  "typings": "typings/index.d.ts",