@testomatio/reporter 0.8.1 → 0.8.3

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.
@@ -5,7 +5,7 @@ const path = require('path');
5
5
  const fs = require('fs');
6
6
  const { APP_PREFIX, STATUS: Status } = require('../constants');
7
7
  const TestomatioClient = require('../client');
8
- const upload = require('../fileUploader');
8
+ const { isArtifactsEnabled } = require('../fileUploader');
9
9
  const { parseTest } = require('../util');
10
10
 
11
11
 
@@ -13,7 +13,7 @@ class TestomatioReporter {
13
13
  constructor(config = {}) {
14
14
  this.client = new TestomatioClient({ apiKey: config?.apiKey });
15
15
 
16
- this.videos = [];
16
+ this.uploads = [];
17
17
  }
18
18
 
19
19
  onBegin(_config, suite) {
@@ -37,32 +37,15 @@ class TestomatioReporter {
37
37
  appendStep(step, steps);
38
38
  }
39
39
 
40
- const files = [];
41
-
42
- for (const attachment of result.attachments) {
43
- if (!attachment.body && !attachment.path) {
44
- continue;
45
- }
46
- if (attachment.contentType && attachment.contentType.startsWith('video')) {
47
- // video is post-processed
48
- this.videos.push({ testId, attachment, title, suite_title });
49
- continue;
50
- }
51
-
52
- let fileName = attachment.path;
53
- if (attachment.body) {
54
- fileName = tmpFile();
55
- fs.writeFileSync(fileName, attachment.body);
56
- }
57
- files.push({ path: fileName, type: attachment.contentType });
58
- }
40
+ this.uploads.push({
41
+ testId, title, suite_title, files: result.attachments.filter((a) => a.body || a.path)
42
+ })
59
43
 
60
44
  this.client.addTestRun(checkStatus(result.status), {
61
45
  error,
62
46
  test_id: testId,
63
47
  suite_title,
64
48
  title,
65
- files,
66
49
  steps: steps.join('\n'),
67
50
  time: duration,
68
51
  });
@@ -71,19 +54,30 @@ class TestomatioReporter {
71
54
  async onEnd(result) {
72
55
  if (!this.client) return;
73
56
 
74
- if (this.videos.length && upload.isArtifactsEnabled()) {
75
- console.log(APP_PREFIX, `🎞️ Uploading ${this.videos.length} videos...`);
57
+ if (this.uploads.length && isArtifactsEnabled()) {
58
+ console.log(APP_PREFIX, `🎞️ Uploading ${this.uploads.length} files...`);
76
59
 
77
60
  const promises = [];
78
- for (const video of this.videos) {
79
- const { testId, title, attachment, suite_title } = video;
80
- const file = { path: attachment.path, title, type: attachment.contentType };
61
+
62
+ for (const upload of this.uploads) {
63
+
64
+ const { title, testId, suite_title } = upload;
65
+
66
+ const files = upload.files.map((attachment) => {
67
+ if (attachment.body) {
68
+ const fileName = tmpFile();
69
+ fs.writeFileSync(fileName, attachment.body);
70
+ }
71
+ return { path: attachment.path, title, type: attachment.contentType };
72
+ });
73
+
74
+
81
75
  promises.push(
82
76
  this.client.addTestRun(undefined, {
83
77
  test_id: testId,
84
78
  title,
85
79
  suite_title,
86
- files: [file],
80
+ files,
87
81
  }),
88
82
  );
89
83
  }
@@ -1,6 +1,7 @@
1
1
  const debug = require('debug')('@testomatio/reporter:file-uploader');
2
2
  const { S3 } = require('@aws-sdk/client-s3');
3
3
  const { Upload } = require('@aws-sdk/lib-storage');
4
+
4
5
  const fs = require('fs');
5
6
  const path = require('path');
6
7
  const chalk = require('chalk');
@@ -27,10 +28,14 @@ function getConfig() {
27
28
  acc[key] = process.env[key];
28
29
  return acc;
29
30
  }, {});
30
- debug('Config', config);
31
31
  return config;
32
32
  }
33
33
 
34
+ function getMaskedConfig() {
35
+ return Object.fromEntries(Object.entries(getConfig())
36
+ .map(([key, value]) => [key, key === 'S3_SECRET_ACCESS_KEY' || key === 'S3_ACCESS_KEY_ID' ? '***' : value]));
37
+ }
38
+
34
39
  let isEnabled;
35
40
 
36
41
  const isArtifactsEnabled = () => {
@@ -89,27 +94,31 @@ const uploadUsingS3 = async (filePath, runId) => {
89
94
  }
90
95
 
91
96
  const {
92
- S3_REGION, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_ENDPOINT, TESTOMATIO_PRIVATE_ARTIFACTS, S3_BUCKET
97
+ TESTOMATIO_PRIVATE_ARTIFACTS, S3_BUCKET
93
98
  } = getConfig();
99
+
100
+ debug('S3 config', getMaskedConfig());
101
+ debug('Uploading', filePath, 'to', S3_BUCKET);
94
102
 
95
- const file = fs.readFileSync(filePath);
103
+ const fileData = fs.readFileSync(filePath);
96
104
 
97
105
  Key = `${runId}/${randomUUID()}-${Key || path.basename(filePath)}`;
98
106
  const ACL = TESTOMATIO_PRIVATE_ARTIFACTS ? 'private' : 'public-read';
99
107
 
100
108
  const s3 = new S3(_getS3Config());
101
109
 
110
+ const params = {
111
+ Bucket: S3_BUCKET,
112
+ Key,
113
+ Body: fileData,
114
+ ContentType,
115
+ ACL,
116
+ };
117
+
102
118
  try {
103
119
  const out = new Upload({
104
120
  client: s3,
105
-
106
- params: {
107
- Bucket: S3_BUCKET,
108
- Key,
109
- Body: file,
110
- ContentType,
111
- ACL,
112
- }
121
+ params
113
122
  });
114
123
 
115
124
  await out.done();
@@ -117,14 +126,8 @@ const uploadUsingS3 = async (filePath, runId) => {
117
126
 
118
127
  return out.singleUploadResult.Location;
119
128
  } catch (e) {
120
- console.log(APP_PREFIX, chalk.bold.red(`Failed uploading '${Key}'. Please check S3 credentials`), {
121
- accessKeyId: S3_ACCESS_KEY_ID,
122
- secretAccessKey: S3_SECRET_ACCESS_KEY ? '**** (hidden) ***' : '(empty)',
123
- region: S3_REGION,
124
- bucket: S3_BUCKET,
125
- acl: ACL,
126
- endpoint: S3_ENDPOINT,
127
- });
129
+ console.log(e);
130
+ console.log(APP_PREFIX, chalk.red(`Failed uploading '${Key}'. Please check S3 credentials`), getMaskedConfig());
128
131
 
129
132
  console.log(APP_PREFIX, `To ${chalk.bold('disable')} artifact uploads set: TESTOMATIO_DISABLE_ARTIFACTS=1`);
130
133
  if (!TESTOMATIO_PRIVATE_ARTIFACTS) {
@@ -136,7 +139,7 @@ const uploadUsingS3 = async (filePath, runId) => {
136
139
  );
137
140
  }
138
141
  console.log(APP_PREFIX, '---------------');
139
- }
142
+ }
140
143
  };
141
144
 
142
145
  const uploadUsingS3AsBuffer = async (buffer, fileName, runId) => {
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) {
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.1",
3
+ "version": "0.8.3",
4
4
  "description": "Testomatio Reporter Client",
5
5
  "main": "./lib/reporter.js",
6
6
  "typings": "typings/index.d.ts",