@testomatio/reporter 1.6.0-beta-1-artifacts → 1.6.0-beta-3-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,20 +168,22 @@ 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;
173
+
165
174
  process.env.TESTOMATIO_DISABLE_ARTIFACTS = '';
175
+ process.env.TESTOMATIO_RUN ||= readLatestRunId();
176
+
177
+ if (!process.env.TESTOMATIO_RUN) {
178
+ console.log('TESTOMATIO_RUN environment variable must be set or restored from a previous run.');
179
+ return process.exit(1);
180
+ }
166
181
 
167
182
  const client = new TestomatClient({
168
183
  apiKey,
169
184
  isBatchEnabled: false,
170
185
  });
171
186
 
172
- if (!process.env.TESTOMATIO_RUN) {
173
- console.log(APP_PREFIX, 'No run ID provided');
174
- process.exit(1);
175
- }
176
-
177
187
  let testruns = client.uploader.readUploadedFiles(process.env.TESTOMATIO_RUN);
178
188
  const numTotalArtifacts = testruns.length;
179
189
 
@@ -207,9 +217,9 @@ program
207
217
  await client.addTestRun(undefined, { rid, files });
208
218
  }
209
219
 
210
- console.log(APP_PREFIX, client.uploader.totalUploaded, 'artifacts uploaded');
211
- if (client.uploader.failedUpload) {
212
- console.log(APP_PREFIX, client.uploader.failedUpload, 'artifacts failed to upload');
220
+ console.log(APP_PREFIX, client.uploader.totalUploadsCount, 'artifacts uploaded');
221
+ if (client.uploader.failedUploadsCount) {
222
+ console.log(APP_PREFIX, client.uploader.failedUploadsCount, 'artifacts failed to upload');
213
223
  }
214
224
  });
215
225
 
@@ -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 {
@@ -13,7 +13,6 @@ program
13
13
  .option('--env-file <envfile>', 'Load environment variables from env file')
14
14
  .option('--force', 'Re-upload artifacts even if they were uploaded before')
15
15
  .action(async opts => {
16
-
17
16
  if (opts.envFile) {
18
17
  require('dotenv').config(opts.envFile); // eslint-disable-line
19
18
  } else {
@@ -24,17 +23,12 @@ program
24
23
  const apiKey = config.TESTOMATIO;
25
24
 
26
25
  process.env.TESTOMATIO_DISABLE_ARTIFACTS = '';
27
- // process.env.TESTOMATIO_ARTIFACTS_SIZE = null;
26
+ // process.env.TESTOMATIO_ARTIFACTS_SIZE = null;
28
27
 
29
- const client = new TestomatClient({
28
+ const client = new TestomatClient({
30
29
  apiKey,
31
30
  isBatchEnabled: false,
32
- });
33
-
34
- if (!process.env.TESTOMATIO_RUN) {
35
- console.log(APP_PREFIX, 'No run ID provided');
36
- process.exit(1);
37
- }
31
+ });
38
32
 
39
33
  let testruns = client.uploader.readUploadedFiles(process.env.TESTOMATIO_RUN);
40
34
 
@@ -61,8 +55,6 @@ program
61
55
  return acc;
62
56
  }, {});
63
57
 
64
- let numArtifacts = 0;
65
-
66
58
  // we need to obtain S3 credentials
67
59
  await client.createRun();
68
60
 
@@ -71,16 +63,15 @@ program
71
63
 
72
64
  for (const rid in testrunsByRid) {
73
65
  const files = testrunsByRid[rid];
74
- numArtifacts += files.length;
75
66
  await client.addTestRun(undefined, {
76
67
  rid,
77
68
  files,
78
69
  });
79
70
  }
80
71
 
81
- console.log(APP_PREFIX, client.uploader.totalUploaded, 'artifacts uploaded');
82
- if (client.uploader.failedUpload) {
83
- console.log(APP_PREFIX, client.uploader.failedUpload, 'artifacts failed to upload');
72
+ console.log(APP_PREFIX, client.uploader.totalUploadsCount, 'artifacts uploaded');
73
+ if (client.uploader.failedUploadsCount) {
74
+ console.log(APP_PREFIX, client.uploader.failedUploadsCount, 'artifacts failed to upload');
84
75
  }
85
76
  });
86
77
 
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,33 @@ 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(APP_PREFIX, `${this.uploader.failedUploadsCount} artifacts failed to upload`);
269
267
  }
270
268
 
271
- if (this.uploader.isEnabled && this.uploader.skippedUpload) {
272
- console.log(APP_PREFIX, `${chalk.bold(this.uploader.skippedUpload)} artifacts skipped to upload`);
269
+ if (this.uploader.isEnabled && this.uploader.skippedUploadsCount) {
270
+ console.log(APP_PREFIX, `${chalk.bold(this.uploader.skippedUploadsCount)} artifacts skipped to upload`);
273
271
  }
274
272
 
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`);
273
+ if (this.uploader.skippedUploadsCount || this.uploader.failedUploadsCount) {
274
+ const command = `TESTOMATIO_RUN=${this.runId} npx upload-artifacts`;
275
+ console.log(
276
+ APP_PREFIX,
277
+ `Run "${chalk.magenta(command)}" with valid S3 credentials to upload skipped & failed artifacts`,
278
+ );
277
279
  }
278
-
279
280
  })
280
281
  .catch(err => console.log(APP_PREFIX, err));
281
282
 
@@ -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,13 +108,13 @@ 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
 
@@ -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
 
@@ -142,13 +146,6 @@ class S3Uploader {
142
146
  if (!fs.existsSync(tempFilePath) || forceCreate) {
143
147
  debug('Creating artifacts file:', tempFilePath);
144
148
  fs.writeFileSync(tempFilePath, '');
145
- // make symlink to 'testomatio.run.latest.jsonl' file
146
- const latestFilePath = path.join(os.tmpdir(), 'testomatio.run.latest.jsonl');
147
- if (fs.existsSync(latestFilePath)) {
148
- fs.unlinkSync(latestFilePath);
149
- }
150
- fs.symlinkSync(tempFilePath, latestFilePath);
151
-
152
149
  }
153
150
  return tempFilePath;
154
151
  }
@@ -156,18 +153,18 @@ class S3Uploader {
156
153
  storeUploadedFile(filePath, runId, rid, uploaded = false) {
157
154
  if (!this.storeEnabled) return;
158
155
 
159
- if (!filePath || !runId || !rid ) return;
156
+ if (!filePath || !runId || !rid) return;
160
157
 
161
158
  const tempFilePath = this.#getUploadFilePath(runId);
162
159
 
163
160
  const data = { rid, file: filePath, uploaded };
164
- const jsonLine = JSON.stringify(data) + '\n';
161
+ const jsonLine = `${JSON.stringify(data)}\n`;
165
162
 
166
163
  fs.appendFileSync(tempFilePath, jsonLine);
167
164
  }
168
165
 
169
166
  getskippedUpload() {
170
- return this.skippedUpload;
167
+ return this.skippedUploadsCount;
171
168
  }
172
169
 
173
170
  async uploadFileByPath(filePath, pathInS3) {
@@ -175,21 +172,18 @@ class S3Uploader {
175
172
 
176
173
  if (!this.isEnabled) {
177
174
  this.storeUploadedFile(filePath, runId, rid, false);
178
- this.skippedUpload++;
175
+ this.skippedUploadsCount++;
179
176
  return;
180
177
  }
181
178
 
182
- const {
183
- S3_BUCKET,
184
- TESTOMATIO_ARTIFACTS_SIZE,
185
- } = this.getConfig();
179
+ const { S3_BUCKET, TESTOMATIO_ARTIFACT_MAX_SIZE_MB } = this.getConfig();
186
180
 
187
181
  debug('Started upload', filePath, 'to', S3_BUCKET);
188
182
 
189
183
  const isFileExist = await this.checkFileExists(filePath, 20, 500);
190
184
 
191
185
  if (!isFileExist) {
192
- this.failedUpload++;
186
+ this.failedUploadsCount++;
193
187
  console.error(chalk.yellow(`Artifacts file ${filePath} does not exist. Skipping...`));
194
188
  return;
195
189
  }
@@ -197,8 +191,8 @@ class S3Uploader {
197
191
  const fileSize = fs.statSync(filePath).size;
198
192
  const fileSizeInMb = fileSize / (1024 * 1024);
199
193
 
200
- if (TESTOMATIO_ARTIFACTS_SIZE && fileSizeInMb > parseInt(TESTOMATIO_ARTIFACTS_SIZE)) {
201
- this.skippedUpload++;
194
+ if (TESTOMATIO_ARTIFACT_MAX_SIZE_MB && fileSizeInMb > parseInt(TESTOMATIO_ARTIFACT_MAX_SIZE_MB, 10)) {
195
+ this.skippedUploadsCount++;
202
196
  console.error(chalk.yellow(`Artifacts file ${filePath} exceeds the maximum allowed size. Skipping...`));
203
197
  return;
204
198
  }
@@ -238,7 +232,9 @@ class S3Uploader {
238
232
  return false;
239
233
  }
240
234
  debug(`File not found, retrying (attempt ${number}/${attempts})`);
241
- await new Promise((resolve) => setTimeout(resolve, intervalMs));
235
+ await new Promise(resolve => {
236
+ setTimeout(resolve, intervalMs);
237
+ });
242
238
  retry(err);
243
239
  }
244
240
  },
@@ -246,7 +242,7 @@ class S3Uploader {
246
242
  retries: attempts,
247
243
  minTimeout: intervalMs,
248
244
  maxTimeout: intervalMs,
249
- }
245
+ },
250
246
  );
251
247
  }
252
248
 
@@ -1,4 +1,3 @@
1
- const { resetConfig } = require('../uploader');
2
1
  const { APP_PREFIX } = require('../constants');
3
2
 
4
3
  /**
@@ -17,7 +16,6 @@ function setS3Credentials(artifacts) {
17
16
  if (artifacts.ENDPOINT) process.env.S3_ENDPOINT = artifacts.ENDPOINT;
18
17
  if (artifacts.SESSION_TOKEN) process.env.S3_SESSION_TOKEN = artifacts.SESSION_TOKEN;
19
18
  if (artifacts.presign) process.env.TESTOMATIO_PRIVATE_ARTIFACTS = '1';
20
- resetConfig();
21
19
  }
22
20
  /**
23
21
  * Generates mode request parameters based on the input params.
@@ -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-1-artifacts",
3
+ "version": "1.6.0-beta-3-artifacts",
4
4
  "description": "Testomatio Reporter Client",
5
5
  "main": "./lib/reporter.js",
6
6
  "typings": "typings/index.d.ts",