@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 CHANGED
@@ -1,3 +1,9 @@
1
+ # 1.0.0
2
+
3
+ <!-- pending release updates -->
4
+
5
+ * Added `TESTOMATIO_SHARED_RUN` option to use a shared run for parallel executions
6
+
1
7
  # 0.7.6
2
8
 
3
9
  * Updated to use AWS S3 3.0 SDK for uploading
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`:
@@ -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
- const config = getConfig();
36
- return Object.fromEntries(Object.entries(config).map(([key, value]) => [key, key === 'S3_SECRET_ACCESS_KEY' || key === 'S3_ACCESS_KEY_ID' ? '***' : value]));
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.bold.red(`Failed uploading '${Key}'. Please check S3 credentials`), getMaskedConfig());
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) {
@@ -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(parseFloat(this.tests.reduce((a, t) => a + (t.run_time || 0), 0)))}** |
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
 
@@ -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(parseFloat(this.tests.reduce((a, t) => a + (t.run_time || 0), 0)))}** |
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) {
@@ -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
- return this.axios.put(`${this.url.trim()}/api/reporter/${this.runId}`, runParams);
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
- run_time: parseFloat(td.duration),
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.length) files = test.files.map(f => path.join(process.cwd(), f))
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
- run_time: parseFloat(testCaseItem.time || testCaseItem.duration),
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),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/reporter",
3
- "version": "0.8.2-beta.1",
3
+ "version": "1.0.0-beta.1",
4
4
  "description": "Testomatio Reporter Client",
5
5
  "main": "./lib/reporter.js",
6
6
  "typings": "typings/index.d.ts",