@testomatio/reporter 1.6.0-beta-2-artifacts → 1.6.0-beta-4-artifacts

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.
@@ -301,9 +301,11 @@ function CodeceptReporter(config) {
301
301
  }
302
302
 
303
303
  async function uploadAttachments(client, attachments, messagePrefix, attachmentType) {
304
- if (!attachments?.length) return;
304
+ if (!attachments?.length) return;
305
305
 
306
- if (client.uploader.isEnabled) console.log(APP_PREFIX, `Attachments: ${messagePrefix} ${attachments.length} ${attachmentType} ...`);
306
+ if (client.uploader.isEnabled) {
307
+ console.log(APP_PREFIX, `Attachments: ${messagePrefix} ${attachments.length} ${attachmentType} ...`);
308
+ }
307
309
 
308
310
  const promises = attachments.map(async attachment => {
309
311
  const { rid, title, path, type } = attachment;
@@ -106,7 +106,7 @@ class PlaywrightReporter {
106
106
  await Promise.all(reportTestPromises);
107
107
 
108
108
  if (!this.uploads.length) return;
109
-
109
+
110
110
  if (this.client.uploader.isEnabled) console.log(APP_PREFIX, `🎞️ Uploading ${this.uploads.length} files...`);
111
111
 
112
112
  const promises = [];
@@ -135,7 +135,6 @@ class PlaywrightReporter {
135
135
  );
136
136
  }
137
137
  await Promise.all(promises);
138
-
139
138
 
140
139
  await this.client.updateRunStatus(checkStatus(result.status));
141
140
  }
package/lib/bin/cli.js CHANGED
@@ -9,13 +9,14 @@ const XmlReader = require('../xmlReader');
9
9
  const { APP_PREFIX, STATUS } = require('../constants');
10
10
  const { version } = require('../../package.json');
11
11
  const config = require('../config');
12
+ const { readLatestRunId } = require('../utils/utils');
12
13
 
13
14
  console.log(chalk.cyan.bold(` 🤩 Testomat.io Reporter v${version}`));
14
15
 
15
16
  program
16
17
  .version(version)
17
18
  .option('--env-file <envfile>', 'Load environment variables from env file')
18
- .hook('preAction', (thisCommand) => {
19
+ .hook('preAction', thisCommand => {
19
20
  const opts = thisCommand.opts();
20
21
  if (opts.envFile) {
21
22
  require('dotenv').config({ path: opts.envFile });
@@ -42,8 +43,10 @@ program
42
43
  .command('finish')
43
44
  .description('Finish Run by its ID')
44
45
  .action(async () => {
46
+ process.env.TESTOMATIO_RUN ||= readLatestRunId();
47
+
45
48
  if (!process.env.TESTOMATIO_RUN) {
46
- console.log('TESTOMATIO_RUN environment variable must be set.');
49
+ console.log('TESTOMATIO_RUN environment variable must be set or restored from a previous run.');
47
50
  return process.exit(1);
48
51
  }
49
52
 
@@ -140,10 +143,15 @@ program
140
143
 
141
144
  let timeoutTimer;
142
145
  if (opts.timelimit) {
143
- timeoutTimer = setTimeout(() => {
144
- console.log(`⚠️ Reached timeout of ${opts.timelimit}s. Exiting... (Exit code is 0 to not fail the pipeline)`);
145
- process.exit(0);
146
- }, parseInt(opts.timelimit, 10) * 1000);
146
+ timeoutTimer = setTimeout(
147
+ () => {
148
+ console.log(
149
+ `⚠️ Reached timeout of ${opts.timelimit}s. Exiting... (Exit code is 0 to not fail the pipeline)`,
150
+ );
151
+ process.exit(0);
152
+ },
153
+ parseInt(opts.timelimit, 10) * 1000,
154
+ );
147
155
  }
148
156
 
149
157
  try {
@@ -160,13 +168,20 @@ program
160
168
  .command('upload-artifacts')
161
169
  .description('Upload artifacts to Testomat.io')
162
170
  .option('--force', 'Re-upload artifacts even if they were uploaded before')
163
- .action(async (opts) => {
171
+ .action(async opts => {
164
172
  const apiKey = config.TESTOMATIO;
165
173
 
166
174
  process.env.TESTOMATIO_DISABLE_ARTIFACTS = '';
175
+ const runId = process.env.TESTOMATIO_RUN || process.env.runId || readLatestRunId();
176
+
177
+ if (!runId) {
178
+ console.log('TESTOMATIO_RUN environment variable must be set or restored from a previous run.');
179
+ return process.exit(1);
180
+ }
167
181
 
168
182
  const client = new TestomatClient({
169
183
  apiKey,
184
+ runId,
170
185
  isBatchEnabled: false,
171
186
  });
172
187
 
@@ -203,9 +218,9 @@ program
203
218
  await client.addTestRun(undefined, { rid, files });
204
219
  }
205
220
 
206
- console.log(APP_PREFIX, client.uploader.totalUploaded, 'artifacts uploaded');
207
- if (client.uploader.failedUpload) {
208
- console.log(APP_PREFIX, client.uploader.failedUpload, 'artifacts failed to upload');
221
+ console.log(APP_PREFIX, client.uploader.totalUploadsCount, 'artifacts uploaded');
222
+ if (client.uploader.failedUploadsCount) {
223
+ console.log(APP_PREFIX, client.uploader.failedUploadsCount, 'artifacts failed to upload');
209
224
  }
210
225
  });
211
226
 
@@ -44,10 +44,15 @@ program
44
44
 
45
45
  let timeoutTimer;
46
46
  if (opts.timelimit) {
47
- timeoutTimer = setTimeout(() => {
48
- console.log(`⚠️ Reached timeout of ${opts.timelimit}s. Exiting... (Exit code is 0 to not fail the pipeline)`);
49
- process.exit(0);
50
- }, parseInt(opts.timelimit, 10) * 1000);
47
+ timeoutTimer = setTimeout(
48
+ () => {
49
+ console.log(
50
+ `⚠️ Reached timeout of ${opts.timelimit}s. Exiting... (Exit code is 0 to not fail the pipeline)`,
51
+ );
52
+ process.exit(0);
53
+ },
54
+ parseInt(opts.timelimit, 10) * 1000,
55
+ );
51
56
  }
52
57
 
53
58
  try {
@@ -1,11 +1,12 @@
1
1
  #!/usr/bin/env node
2
- const program = require('commander');
2
+ const { program } = require('commander');
3
3
  const chalk = require('chalk');
4
4
  const debug = require('debug')('@testomatio/reporter:upload-cli');
5
5
  const TestomatClient = require('../client');
6
6
  const { APP_PREFIX } = require('../constants');
7
7
  const { version } = require('../../package.json');
8
8
  const config = require('../config');
9
+ const { readLatestRunId } = require('../utils/utils');
9
10
 
10
11
  console.log(chalk.cyan.bold(` 🤩 Testomat.io Reporter v${version}`));
11
12
 
@@ -13,7 +14,6 @@ program
13
14
  .option('--env-file <envfile>', 'Load environment variables from env file')
14
15
  .option('--force', 'Re-upload artifacts even if they were uploaded before')
15
16
  .action(async opts => {
16
-
17
17
  if (opts.envFile) {
18
18
  require('dotenv').config(opts.envFile); // eslint-disable-line
19
19
  } else {
@@ -22,15 +22,19 @@ program
22
22
  }
23
23
 
24
24
  const apiKey = config.TESTOMATIO;
25
-
26
25
  process.env.TESTOMATIO_DISABLE_ARTIFACTS = '';
27
- // process.env.TESTOMATIO_ARTIFACTS_SIZE = null;
26
+ const runId = process.env.TESTOMATIO_RUN || process.env.runId || readLatestRunId();
27
+
28
+ if (!runId) {
29
+ console.log('TESTOMATIO_RUN environment variable must be set or restored from a previous run.');
30
+ return process.exit(1);
31
+ }
28
32
 
29
33
  const client = new TestomatClient({
30
34
  apiKey,
35
+ runId,
31
36
  isBatchEnabled: false,
32
- });
33
-
37
+ });
34
38
  let testruns = client.uploader.readUploadedFiles(process.env.TESTOMATIO_RUN);
35
39
 
36
40
  const numTotalArtifacts = testruns.length;
@@ -56,8 +60,6 @@ program
56
60
  return acc;
57
61
  }, {});
58
62
 
59
- let numArtifacts = 0;
60
-
61
63
  // we need to obtain S3 credentials
62
64
  await client.createRun();
63
65
 
@@ -66,16 +68,15 @@ program
66
68
 
67
69
  for (const rid in testrunsByRid) {
68
70
  const files = testrunsByRid[rid];
69
- numArtifacts += files.length;
70
71
  await client.addTestRun(undefined, {
71
72
  rid,
72
73
  files,
73
74
  });
74
75
  }
75
76
 
76
- console.log(APP_PREFIX, client.uploader.totalUploaded, 'artifacts uploaded');
77
- if (client.uploader.failedUpload) {
78
- console.log(APP_PREFIX, client.uploader.failedUpload, 'artifacts failed to upload');
77
+ console.log(APP_PREFIX, client.uploader.totalUploadsCount, 'artifacts uploaded');
78
+ if (client.uploader.failedUploadsCount) {
79
+ console.log(APP_PREFIX, client.uploader.failedUploadsCount, 'artifacts failed to upload');
79
80
  }
80
81
  });
81
82
 
package/lib/client.js CHANGED
@@ -10,6 +10,7 @@ const { APP_PREFIX, STATUS } = require('./constants');
10
10
  const pipesFactory = require('./pipe');
11
11
  const { glob } = require('glob');
12
12
  const path = require('path');
13
+ const { storeRunId } = require('./utils/utils');
13
14
 
14
15
  let listOfTestFilesToExcludeFromReport = null;
15
16
 
@@ -107,6 +108,8 @@ class Client {
107
108
  .then(() => {
108
109
  const runId = this.pipeStore?.runId;
109
110
  if (runId) this.runId = runId;
111
+ storeRunId(this.runId);
112
+
110
113
  this.uploader.checkEnabled();
111
114
  })
112
115
  .then(() => undefined); // fixes return type
@@ -182,12 +185,7 @@ class Client {
182
185
  f = f.path;
183
186
  }
184
187
 
185
- uploadedFiles.push(this.uploader.uploadFileByPath(f, [
186
- this.runId,
187
- rid,
188
- path.basename(f)
189
- ]));
190
-
188
+ uploadedFiles.push(this.uploader.uploadFileByPath(f, [this.runId, rid, path.basename(f)]));
191
189
  }
192
190
 
193
191
  for (const [idx, buffer] of filesBuffers.entries()) {
@@ -252,30 +250,37 @@ class Client {
252
250
  this.queue = this.queue
253
251
  .then(() => Promise.all(this.pipes.map(p => p.finishRun(runParams))))
254
252
  .then(() => {
255
- debug('TOTAL artifacts', this.uploader.totalUploaded);
256
- debug(`${this.uploader.skippedUpload} artifacts skipped`);
253
+ debug('TOTAL artifacts', this.uploader.totalUploadsCount);
254
+ debug(`${this.uploader.skippedUploadsCount} artifacts skipped`);
257
255
 
258
- if (this.uploader.totalUploaded && this.uploader.isEnabled) {
256
+ if (this.uploader.totalUploadsCount && this.uploader.isEnabled) {
259
257
  console.log(
260
258
  APP_PREFIX,
261
- `🗄️ ${this.uploader.totalUploaded} artifacts ${
259
+ `🗄️ ${this.uploader.totalUploadsCount} artifacts ${
262
260
  process.env.TESTOMATIO_PRIVATE_ARTIFACTS ? 'privately' : chalk.bold('publicly')
263
261
  } uploaded to S3 bucket`,
264
- );
262
+ );
265
263
  }
266
264
 
267
- if (this.uploader.failedUpload) {
268
- console.log(APP_PREFIX, `${this.uploader.failedUpload} artifacts failed to upload`);
265
+ if (this.uploader.failedUploadsCount) {
266
+ console.log(
267
+ APP_PREFIX,
268
+ chalk.gray('[CLIENT]'),
269
+ `${this.uploader.failedUploadsCount} artifacts failed to upload`,
270
+ );
269
271
  }
270
272
 
271
- if (this.uploader.isEnabled && this.uploader.skippedUpload) {
272
- console.log(APP_PREFIX, `${chalk.bold(this.uploader.skippedUpload)} artifacts skipped to upload`);
273
+ if (this.uploader.isEnabled && this.uploader.skippedUploadsCount) {
274
+ console.log(APP_PREFIX, `${chalk.bold(this.uploader.skippedUploadsCount)} artifacts skipped to upload`);
273
275
  }
274
276
 
275
- if (this.uploader.skippedUpload || this.uploader.failedUpload) {
276
- console.log(APP_PREFIX, `Run "${chalk.magenta(`TESTOMATIO_RUN=${this.runId} npx upload-artifacts`)}" with valid S3 credentials to upload skipped & failed artifacts`);
277
+ if (this.uploader.skippedUploadsCount || this.uploader.failedUploadsCount) {
278
+ const command = `TESTOMATIO_RUN=${this.runId} npx @testomatio/reporter upload-artifacts`;
279
+ console.log(
280
+ APP_PREFIX,
281
+ `Run "${chalk.magenta(command)}" with valid S3 credentials to upload skipped & failed artifacts`,
282
+ );
277
283
  }
278
-
279
284
  })
280
285
  .catch(err => console.log(APP_PREFIX, err));
281
286
 
@@ -79,13 +79,13 @@ class GitLabPipe {
79
79
  let summary = `${this.hiddenCommentData}
80
80
 
81
81
  | [![Testomat.io Report](${testomatLogoURL})](https://testomat.io) | ${statusEmoji(
82
- runParams.status,
83
- )} ${runParams.status.toUpperCase()} ${statusEmoji(runParams.status)} |
82
+ runParams.status,
83
+ )} ${runParams.status.toUpperCase()} ${statusEmoji(runParams.status)} |
84
84
  | --- | --- |
85
85
  | Tests | ✔️ **${this.tests.length}** tests run |
86
86
  | Summary | ${statusEmoji('failed')} **${failedCount}** failed; ${statusEmoji(
87
- 'passed',
88
- )} **${passedCount}** passed; **${statusEmoji('skipped')}** ${skippedCount} skipped |
87
+ 'passed',
88
+ )} **${passedCount}** passed; **${statusEmoji('skipped')}** ${skippedCount} skipped |
89
89
  | Duration | 🕐 **${humanizeDuration(
90
90
  parseInt(
91
91
  this.tests.reduce((a, t) => a + (t.run_time || 0), 0),
@@ -214,7 +214,7 @@ class TestomatioPipe {
214
214
  this.store.runPublicUrl = this.runPublicUrl;
215
215
  this.store.runId = this.runId;
216
216
  console.log(APP_PREFIX, '📊 Report created. Report ID:', this.runId);
217
- process.env.runId = this.runId;
217
+ process.env.runId = this.runId;
218
218
  debug('Run created', this.runId);
219
219
  } catch (err) {
220
220
  console.error(
package/lib/uploader.js CHANGED
@@ -15,9 +15,9 @@ class S3Uploader {
15
15
  this.config = undefined;
16
16
 
17
17
  // counters
18
- this.skippedUpload = 0;
19
- this.failedUpload = 0;
20
- this.totalUploaded = 0;
18
+ this.skippedUploadsCount = 0;
19
+ this.failedUploadsCount = 0;
20
+ this.totalUploadsCount = 0;
21
21
 
22
22
  this.succesfulUploads = {};
23
23
 
@@ -31,7 +31,7 @@ class S3Uploader {
31
31
  'S3_FORCE_PATH_STYLE',
32
32
  'TESTOMATIO_DISABLE_ARTIFACTS',
33
33
  'TESTOMATIO_PRIVATE_ARTIFACTS',
34
- 'TESTOMATIO_ARTIFACTS_SIZE'
34
+ 'TESTOMATIO_ARTIFACT_MAX_SIZE_MB',
35
35
  ];
36
36
  }
37
37
 
@@ -84,7 +84,11 @@ class S3Uploader {
84
84
  const ACL = TESTOMATIO_PRIVATE_ARTIFACTS ? 'private' : 'public-read';
85
85
 
86
86
  if (!S3_BUCKET || !Body) {
87
- console.log(APP_PREFIX, chalk.bold.red(`Failed uploading '${Key}'. Please check S3 credentials`), this.getMaskedConfig());
87
+ console.log(
88
+ APP_PREFIX,
89
+ chalk.bold.red(`Failed uploading '${Key}'. Please check S3 credentials`),
90
+ this.getMaskedConfig(),
91
+ );
88
92
  return;
89
93
  }
90
94
 
@@ -104,17 +108,17 @@ class S3Uploader {
104
108
  });
105
109
 
106
110
  const link = await this.getS3LocationLink(upload);
107
- this.totalUploaded++;
111
+ this.totalUploadsCount++;
108
112
  this.succesfulUploads[Key] = link;
109
113
  return link;
110
114
  } catch (e) {
111
- this.failedUpload++;
115
+ this.failedUploadsCount++;
112
116
  debug('S3 uploading error:', e);
113
- console.log(APP_PREFIX, "Upload failed:", e.message, this.getMaskedConfig());
117
+ console.log(APP_PREFIX, 'Upload failed:', e.message, this.getMaskedConfig());
114
118
  }
115
119
  }
116
120
 
117
- readUploadedFiles(runId = null) {
121
+ readUploadedFiles(runId) {
118
122
  const tempFilePath = this.#getUploadFilePath(runId);
119
123
 
120
124
  debug('Reading file', tempFilePath);
@@ -125,10 +129,10 @@ class S3Uploader {
125
129
  }
126
130
 
127
131
  const stats = fs.statSync(tempFilePath);
128
- const diff = (+new Date()) - (+stats.mtime);
132
+ const diff = +new Date() - +stats.mtime;
129
133
  const diffHours = diff / 1000 / 60 / 60;
130
134
  if (diffHours > 3) {
131
- console.log(APP_PREFIX, 'Artifacts file is too old, can\'t process artifacts. Please re-run the tests.');
135
+ console.log(APP_PREFIX, "Artifacts file is too old, can't process artifacts. Please re-run the tests.");
132
136
  return [];
133
137
  }
134
138
 
@@ -137,22 +141,11 @@ class S3Uploader {
137
141
  return lines.map(line => JSON.parse(line));
138
142
  }
139
143
 
140
- #getUploadFilePath(runId = null, forceCreate = false) {
141
- if (!runId && !forceCreate) {
142
- return path.join(os.tmpdir(), 'testomatio.run.latest.jsonl');
143
- }
144
-
144
+ #getUploadFilePath(runId, forceCreate = false) {
145
145
  const tempFilePath = path.join(os.tmpdir(), `testomatio.run.${runId}.jsonl`);
146
146
  if (!fs.existsSync(tempFilePath) || forceCreate) {
147
147
  debug('Creating artifacts file:', tempFilePath);
148
148
  fs.writeFileSync(tempFilePath, '');
149
- // make symlink to 'testomatio.run.latest.jsonl' file
150
- const latestFilePath = path.join(os.tmpdir(), 'testomatio.run.latest.jsonl');
151
- if (fs.existsSync(latestFilePath)) {
152
- fs.unlinkSync(latestFilePath);
153
- }
154
- fs.symlinkSync(tempFilePath, latestFilePath);
155
-
156
149
  }
157
150
  return tempFilePath;
158
151
  }
@@ -160,40 +153,38 @@ class S3Uploader {
160
153
  storeUploadedFile(filePath, runId, rid, uploaded = false) {
161
154
  if (!this.storeEnabled) return;
162
155
 
163
- if (!filePath || !runId || !rid ) return;
156
+ if (!filePath || !runId || !rid) return;
164
157
 
165
158
  const tempFilePath = this.#getUploadFilePath(runId);
166
159
 
167
160
  const data = { rid, file: filePath, uploaded };
168
- const jsonLine = JSON.stringify(data) + '\n';
161
+ const jsonLine = `${JSON.stringify(data)}\n`;
169
162
 
170
163
  fs.appendFileSync(tempFilePath, jsonLine);
171
164
  }
172
165
 
173
166
  getskippedUpload() {
174
- return this.skippedUpload;
167
+ return this.skippedUploadsCount;
175
168
  }
176
169
 
177
170
  async uploadFileByPath(filePath, pathInS3) {
178
171
  const [runId, rid] = pathInS3;
179
172
 
173
+ if (!filePath) return;
174
+
180
175
  if (!this.isEnabled) {
181
176
  this.storeUploadedFile(filePath, runId, rid, false);
182
- this.skippedUpload++;
177
+ this.skippedUploadsCount++;
183
178
  return;
184
179
  }
185
180
 
186
- const {
187
- S3_BUCKET,
188
- TESTOMATIO_ARTIFACTS_SIZE,
189
- } = this.getConfig();
181
+ const { S3_BUCKET, TESTOMATIO_ARTIFACT_MAX_SIZE_MB } = this.getConfig();
190
182
 
191
183
  debug('Started upload', filePath, 'to', S3_BUCKET);
192
184
 
193
185
  const isFileExist = await this.checkFileExists(filePath, 20, 500);
194
186
 
195
187
  if (!isFileExist) {
196
- this.failedUpload++;
197
188
  console.error(chalk.yellow(`Artifacts file ${filePath} does not exist. Skipping...`));
198
189
  return;
199
190
  }
@@ -201,8 +192,8 @@ class S3Uploader {
201
192
  const fileSize = fs.statSync(filePath).size;
202
193
  const fileSizeInMb = fileSize / (1024 * 1024);
203
194
 
204
- if (TESTOMATIO_ARTIFACTS_SIZE && fileSizeInMb > parseInt(TESTOMATIO_ARTIFACTS_SIZE)) {
205
- this.skippedUpload++;
195
+ if (TESTOMATIO_ARTIFACT_MAX_SIZE_MB && fileSizeInMb > parseInt(TESTOMATIO_ARTIFACT_MAX_SIZE_MB, 10)) {
196
+ this.skippedUploadsCount++;
206
197
  console.error(chalk.yellow(`Artifacts file ${filePath} exceeds the maximum allowed size. Skipping...`));
207
198
  return;
208
199
  }
@@ -242,7 +233,9 @@ class S3Uploader {
242
233
  return false;
243
234
  }
244
235
  debug(`File not found, retrying (attempt ${number}/${attempts})`);
245
- await new Promise((resolve) => setTimeout(resolve, intervalMs));
236
+ await new Promise(resolve => {
237
+ setTimeout(resolve, intervalMs);
238
+ });
246
239
  retry(err);
247
240
  }
248
241
  },
@@ -250,7 +243,7 @@ class S3Uploader {
250
243
  retries: attempts,
251
244
  minTimeout: intervalMs,
252
245
  maxTimeout: intervalMs,
253
- }
246
+ },
254
247
  );
255
248
  }
256
249
 
@@ -3,6 +3,8 @@ const { sep, basename } = require('path');
3
3
  const chalk = require('chalk');
4
4
  const fs = require('fs');
5
5
  const isValid = require('is-valid-path');
6
+ const path = require('path');
7
+ const os = require('os');
6
8
  const debug = require('debug')('@testomatio/reporter:util');
7
9
 
8
10
  /**
@@ -318,7 +320,28 @@ const testRunnerHelper = {
318
320
  },
319
321
  };
320
322
 
323
+ function storeRunId(runId) {
324
+ const filePath = path.join(os.tmpdir(), `testomatio.latest.run`);
325
+ fs.writeFileSync(filePath, runId);
326
+ }
327
+
328
+ function readLatestRunId() {
329
+ try {
330
+ const filePath = path.join(os.tmpdir(), `testomatio.latest.run`);
331
+ const stats = fs.statSync(filePath);
332
+ const diff = +new Date() - +stats.mtime;
333
+ const diffHours = diff / 1000 / 60 / 60;
334
+ if (diffHours > 1) return;
335
+
336
+ return fs.readFileSync(filePath)?.toString()?.trim();
337
+ } catch (e) {
338
+ return null;
339
+ }
340
+ }
341
+
321
342
  module.exports = {
343
+ storeRunId,
344
+ readLatestRunId,
322
345
  isSameTest,
323
346
  fetchSourceCode,
324
347
  fetchSourceCodeFromStackTrace,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/reporter",
3
- "version": "1.6.0-beta-2-artifacts",
3
+ "version": "1.6.0-beta-4-artifacts",
4
4
  "description": "Testomatio Reporter Client",
5
5
  "main": "./lib/reporter.js",
6
6
  "typings": "typings/index.d.ts",