@testomatio/reporter 2.0.0-beta-esm → 2.0.1-beta-esm
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/lib/adapter/codecept.d.ts +2 -0
- package/lib/adapter/codecept.js +31 -24
- package/lib/adapter/cucumber/current.d.ts +14 -0
- package/lib/adapter/cucumber/legacy.d.ts +0 -0
- package/lib/adapter/cucumber.d.ts +2 -0
- package/lib/adapter/cypress-plugin/index.d.ts +2 -0
- package/lib/adapter/cypress-plugin/index.js +11 -9
- package/lib/adapter/jasmine.d.ts +11 -0
- package/lib/adapter/jest.d.ts +13 -0
- package/lib/adapter/mocha.d.ts +2 -0
- package/lib/adapter/mocha.js +4 -3
- package/lib/adapter/playwright.d.ts +14 -0
- package/lib/adapter/playwright.js +58 -33
- package/lib/adapter/vitest.d.ts +35 -0
- package/lib/adapter/vitest.js +6 -6
- package/lib/adapter/webdriver.d.ts +24 -0
- package/lib/adapter/webdriver.js +34 -6
- package/lib/bin/cli.d.ts +2 -0
- package/lib/bin/cli.js +228 -0
- package/lib/bin/reportXml.d.ts +2 -0
- package/lib/bin/reportXml.js +11 -9
- package/lib/bin/startTest.d.ts +2 -0
- package/lib/bin/startTest.js +9 -5
- package/lib/bin/uploadArtifacts.d.ts +2 -0
- package/lib/bin/uploadArtifacts.js +81 -0
- package/lib/client.d.ts +76 -0
- package/lib/client.js +111 -45
- package/lib/config.d.ts +1 -0
- package/lib/constants.d.ts +25 -0
- package/lib/constants.js +5 -1
- package/lib/data-storage.d.ts +34 -0
- package/lib/data-storage.js +2 -2
- package/lib/junit-adapter/adapter.d.ts +9 -0
- package/lib/junit-adapter/csharp.d.ts +4 -0
- package/lib/junit-adapter/index.d.ts +3 -0
- package/lib/junit-adapter/java.d.ts +5 -0
- package/lib/junit-adapter/javascript.d.ts +4 -0
- package/lib/junit-adapter/python.d.ts +5 -0
- package/lib/junit-adapter/ruby.d.ts +4 -0
- package/lib/output.d.ts +11 -0
- package/lib/package.json +3 -1
- package/lib/pipe/bitbucket.d.ts +23 -0
- package/lib/pipe/bitbucket.js +2 -2
- package/lib/pipe/csv.d.ts +47 -0
- package/lib/pipe/csv.js +2 -2
- package/lib/pipe/debug.d.ts +29 -0
- package/lib/pipe/debug.js +108 -0
- package/lib/pipe/github.d.ts +30 -0
- package/lib/pipe/github.js +2 -2
- package/lib/pipe/gitlab.d.ts +23 -0
- package/lib/pipe/gitlab.js +2 -2
- package/lib/pipe/html.d.ts +34 -0
- package/lib/pipe/html.js +8 -1
- package/lib/pipe/index.d.ts +1 -0
- package/lib/pipe/index.js +3 -3
- package/lib/pipe/testomatio.d.ts +70 -0
- package/lib/pipe/testomatio.js +50 -30
- package/lib/reporter-functions.d.ts +34 -0
- package/lib/reporter-functions.js +17 -7
- package/lib/reporter.d.ts +232 -0
- package/lib/reporter.js +19 -33
- package/lib/services/artifacts.d.ts +33 -0
- package/lib/services/index.d.ts +9 -0
- package/lib/services/key-values.d.ts +27 -0
- package/lib/services/key-values.js +1 -1
- package/lib/services/logger.d.ts +64 -0
- package/lib/template/testomatio.hbs +651 -1366
- package/lib/uploader.d.ts +60 -0
- package/lib/uploader.js +312 -0
- package/lib/utils/pipe_utils.d.ts +41 -0
- package/lib/utils/pipe_utils.js +3 -5
- package/lib/utils/utils.d.ts +45 -0
- package/lib/utils/utils.js +69 -2
- package/lib/xmlReader.d.ts +92 -0
- package/lib/xmlReader.js +22 -12
- package/package.json +15 -9
- package/src/adapter/codecept.js +30 -24
- package/src/adapter/cypress-plugin/index.js +5 -3
- package/src/adapter/mocha.cjs +1 -1
- package/src/adapter/mocha.js +4 -3
- package/src/adapter/playwright.js +59 -31
- package/src/adapter/vitest.js +6 -6
- package/src/adapter/webdriver.js +41 -10
- package/src/bin/cli.js +280 -0
- package/src/bin/reportXml.js +15 -8
- package/src/bin/startTest.js +7 -3
- package/src/bin/uploadArtifacts.js +90 -0
- package/src/client.js +137 -56
- package/src/constants.js +5 -1
- package/src/data-storage.js +2 -2
- package/src/pipe/bitbucket.js +2 -2
- package/src/pipe/csv.js +3 -3
- package/src/pipe/debug.js +104 -0
- package/src/pipe/github.js +2 -3
- package/src/pipe/gitlab.js +6 -6
- package/src/pipe/html.js +11 -3
- package/src/pipe/index.js +5 -7
- package/src/pipe/testomatio.js +72 -67
- package/src/reporter-functions.js +18 -7
- package/src/reporter.cjs_decprecated +21 -0
- package/src/reporter.js +20 -11
- package/src/services/key-values.js +1 -1
- package/src/services/logger.js +4 -2
- package/src/template/testomatio.hbs +651 -1366
- package/src/uploader.js +371 -0
- package/src/utils/pipe_utils.js +4 -12
- package/src/utils/utils.js +48 -6
- package/src/xmlReader.js +26 -15
- package/lib/adapter/jasmine/jasmine.js +0 -63
- package/lib/adapter/mocha/mocha.js +0 -125
- package/lib/fileUploader.js +0 -245
- package/lib/utils/chalk.js +0 -10
- package/src/fileUploader.js +0 -307
- package/src/reporter.cjs +0 -22
- package/src/utils/chalk.js +0 -13
package/lib/client.js
CHANGED
|
@@ -33,32 +33,34 @@ const minimatch_1 = require("minimatch");
|
|
|
33
33
|
const fs_1 = __importDefault(require("fs"));
|
|
34
34
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
35
35
|
const crypto_1 = require("crypto");
|
|
36
|
-
const fileUploader_js_1 = require("./fileUploader.js");
|
|
37
36
|
const constants_js_1 = require("./constants.js");
|
|
38
37
|
const index_js_1 = require("./pipe/index.js");
|
|
39
38
|
const glob_1 = require("glob");
|
|
40
39
|
const path_1 = __importStar(require("path"));
|
|
41
40
|
const node_url_1 = require("node:url");
|
|
41
|
+
const uploader_js_1 = require("./uploader.js");
|
|
42
|
+
const utils_js_1 = require("./utils/utils.js");
|
|
43
|
+
const filesize_1 = require("filesize");
|
|
42
44
|
const debug = (0, debug_1.default)('@testomatio/reporter:client');
|
|
43
45
|
// removed __dirname usage, because:
|
|
44
46
|
// 1. replaced with ESM syntax (import.meta.url), but it throws an error on tsc compilation;
|
|
45
|
-
// 2. got error "__dirname already defined" in compiles js code (cjs dir)
|
|
47
|
+
// 2. got error "__dirname already defined" in compiles js code (cjs dir)
|
|
46
48
|
let listOfTestFilesToExcludeFromReport = null;
|
|
47
49
|
/**
|
|
48
|
-
* @typedef {import('../types').TestData} TestData
|
|
49
|
-
* @typedef {import('../types').PipeResult} PipeResult
|
|
50
|
+
* @typedef {import('../types/types.js').TestData} TestData
|
|
51
|
+
* @typedef {import('../types/types.js').PipeResult} PipeResult
|
|
50
52
|
*/
|
|
51
53
|
class Client {
|
|
52
54
|
/**
|
|
53
55
|
* Create a Testomat client instance
|
|
54
56
|
* @returns
|
|
55
57
|
*/
|
|
56
|
-
// eslint-disable-next-line
|
|
58
|
+
// eslint-disable-next-line
|
|
57
59
|
constructor(params = {}) {
|
|
58
|
-
this.
|
|
60
|
+
this.paramsForPipesFactory = params;
|
|
61
|
+
this.pipeStore = {};
|
|
62
|
+
this.runId = (0, crypto_1.randomUUID)(); // will be replaced by real run id
|
|
59
63
|
this.queue = Promise.resolve();
|
|
60
|
-
this.totalUploaded = 0;
|
|
61
|
-
this.failedToUpload = 0;
|
|
62
64
|
// @ts-ignore this line will be removed in compiled code, because __dirname is defined in commonjs
|
|
63
65
|
const pathToPackageJSON = path_1.default.join(__dirname, '../package.json');
|
|
64
66
|
try {
|
|
@@ -69,6 +71,7 @@ class Client {
|
|
|
69
71
|
// do nothing
|
|
70
72
|
}
|
|
71
73
|
this.executionList = Promise.resolve();
|
|
74
|
+
this.uploader = new uploader_js_1.S3Uploader();
|
|
72
75
|
}
|
|
73
76
|
/**
|
|
74
77
|
* Asynchronously prepares the execution list for running tests through various pipes.
|
|
@@ -87,8 +90,7 @@ class Client {
|
|
|
87
90
|
* or resolves to undefined if no valid results are found or if all pipes are disabled.
|
|
88
91
|
*/
|
|
89
92
|
async prepareRun(params) {
|
|
90
|
-
|
|
91
|
-
this.pipes = await (0, index_js_1.pipesFactory)(params, store);
|
|
93
|
+
this.pipes = await (0, index_js_1.pipesFactory)(params || this.paramsForPipesFactory || {}, this.pipeStore);
|
|
92
94
|
const { pipe, pipeOptions } = params;
|
|
93
95
|
// all pipes disabled, skipping
|
|
94
96
|
if (!this.pipes.some(p => p.isEnabled)) {
|
|
@@ -120,7 +122,7 @@ class Client {
|
|
|
120
122
|
*/
|
|
121
123
|
async createRun(params) {
|
|
122
124
|
if (!this.pipes || !this.pipes.length)
|
|
123
|
-
this.pipes = await (0, index_js_1.pipesFactory)(params || {},
|
|
125
|
+
this.pipes = await (0, index_js_1.pipesFactory)(params || this.paramsForPipesFactory || {}, this.pipeStore);
|
|
124
126
|
debug('Creating run...');
|
|
125
127
|
// all pipes disabled, skipping
|
|
126
128
|
if (!this.pipes?.filter(p => p.isEnabled).length)
|
|
@@ -128,6 +130,13 @@ class Client {
|
|
|
128
130
|
this.queue = this.queue
|
|
129
131
|
.then(() => Promise.all(this.pipes.map(p => p.createRun())))
|
|
130
132
|
.catch(err => console.log(constants_js_1.APP_PREFIX, err))
|
|
133
|
+
.then(() => {
|
|
134
|
+
const runId = this.pipeStore?.runId;
|
|
135
|
+
if (runId)
|
|
136
|
+
this.runId = runId;
|
|
137
|
+
(0, utils_js_1.storeRunId)(this.runId);
|
|
138
|
+
})
|
|
139
|
+
.then(() => this.uploader.checkEnabled())
|
|
131
140
|
.then(() => undefined); // fixes return type
|
|
132
141
|
// debug('Run', this.queue);
|
|
133
142
|
return this.queue;
|
|
@@ -157,8 +166,45 @@ class Client {
|
|
|
157
166
|
/**
|
|
158
167
|
* @type {TestData}
|
|
159
168
|
*/
|
|
160
|
-
const { rid, error = null, time = 0, example = null, files = [], filesBuffers = [], steps, code = null, title, file, suite_title, suite_id, test_id, manuallyAttachedArtifacts,
|
|
161
|
-
let { message = '' } = testData;
|
|
169
|
+
const { rid, error = null, time = 0, example = null, files = [], filesBuffers = [], steps, code = null, title, file, suite_title, suite_id, test_id, manuallyAttachedArtifacts, } = testData;
|
|
170
|
+
let { message = '', meta = {} } = testData;
|
|
171
|
+
// stringify meta values and limit keys and values length to 255
|
|
172
|
+
meta = Object.entries(meta)
|
|
173
|
+
.filter(([, value]) => value !== null && value !== undefined)
|
|
174
|
+
.map(([key, value]) => {
|
|
175
|
+
try {
|
|
176
|
+
if (typeof value === 'object') {
|
|
177
|
+
value = JSON.stringify(value);
|
|
178
|
+
}
|
|
179
|
+
else if (typeof value !== 'string') {
|
|
180
|
+
try {
|
|
181
|
+
value = value.toString();
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
console.warn(constants_js_1.APP_PREFIX, `Can't convert meta value to string`, err);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (value?.length > 255) {
|
|
188
|
+
value = value.substring(0, 255);
|
|
189
|
+
debug(constants_js_1.APP_PREFIX, `Meta info value "${value}" is too long, trimmed to 255 characters`);
|
|
190
|
+
}
|
|
191
|
+
if (key?.length > 255) {
|
|
192
|
+
const newKey = key.substring(0, 255);
|
|
193
|
+
debug(constants_js_1.APP_PREFIX, `Meta info key "${key}" is too long, trimmed to 255 characters`);
|
|
194
|
+
return [newKey, value];
|
|
195
|
+
}
|
|
196
|
+
return [key, value];
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
199
|
+
debug(constants_js_1.APP_PREFIX, `Error while processing meta info key ${key}`, err);
|
|
200
|
+
return [null, null];
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
.reduce((acc, [key, value]) => {
|
|
204
|
+
if (key)
|
|
205
|
+
acc[key] = value;
|
|
206
|
+
return acc;
|
|
207
|
+
}, {});
|
|
162
208
|
let errorFormatted = '';
|
|
163
209
|
if (error) {
|
|
164
210
|
errorFormatted += this.formatError(error) || '';
|
|
@@ -170,19 +216,21 @@ class Client {
|
|
|
170
216
|
if (manuallyAttachedArtifacts?.length)
|
|
171
217
|
files.push(...manuallyAttachedArtifacts);
|
|
172
218
|
const uploadedFiles = [];
|
|
173
|
-
for (
|
|
174
|
-
|
|
219
|
+
for (let f of files) {
|
|
220
|
+
if (!f)
|
|
221
|
+
continue; // f === null
|
|
222
|
+
if (typeof f === 'object') {
|
|
223
|
+
if (!f.path)
|
|
224
|
+
continue;
|
|
225
|
+
f = f.path;
|
|
226
|
+
}
|
|
227
|
+
uploadedFiles.push(this.uploader.uploadFileByPath(f, [this.runId, rid, path_1.default.basename(f)]));
|
|
175
228
|
}
|
|
176
229
|
for (const [idx, buffer] of filesBuffers.entries()) {
|
|
177
230
|
const fileName = `${idx + 1}-${title.replace(/\s+/g, '-')}`;
|
|
178
|
-
uploadedFiles.push(
|
|
231
|
+
uploadedFiles.push(this.uploader.uploadFileAsBuffer(buffer, [this.runId, rid, fileName]));
|
|
179
232
|
}
|
|
180
233
|
const artifacts = (await Promise.all(uploadedFiles)).filter(n => !!n);
|
|
181
|
-
if (artifacts.length < uploadedFiles.length) {
|
|
182
|
-
const failedUploading = uploadedFiles.length - artifacts.length;
|
|
183
|
-
this.failedToUpload += failedUploading;
|
|
184
|
-
}
|
|
185
|
-
this.totalUploaded += artifacts.length;
|
|
186
234
|
const data = {
|
|
187
235
|
rid,
|
|
188
236
|
files,
|
|
@@ -232,15 +280,47 @@ class Client {
|
|
|
232
280
|
this.queue = this.queue
|
|
233
281
|
.then(() => Promise.all(this.pipes.map(p => p.finishRun(runParams))))
|
|
234
282
|
.then(() => {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
if (this.
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
283
|
+
if (!this.uploader.isEnabled)
|
|
284
|
+
return;
|
|
285
|
+
const filesizeStrMaxLength = 7;
|
|
286
|
+
if (this.uploader.successfulUploads.length) {
|
|
287
|
+
debug('\n', constants_js_1.APP_PREFIX, `🗄️ ${this.uploader.successfulUploads.length} artifacts uploaded to S3 bucket`);
|
|
288
|
+
const uploadedArtifacts = this.uploader.successfulUploads.map(file => ({
|
|
289
|
+
relativePath: file.path.replace(process.cwd(), ''),
|
|
290
|
+
link: file.link,
|
|
291
|
+
sizePretty: (0, filesize_1.filesize)(file.size, { round: 0 }).toString(),
|
|
292
|
+
}));
|
|
293
|
+
uploadedArtifacts.forEach(upload => {
|
|
294
|
+
debug(`🟢Uploaded artifact`, `${upload.relativePath},`, 'size:', `${upload.sizePretty},`, 'link:', `${upload.link}`);
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
if (this.uploader.failedUploads.length) {
|
|
298
|
+
console.log(constants_js_1.APP_PREFIX, `🗄️ ${this.uploader.failedUploads.length} artifacts 🔴${picocolors_1.default.bold('failed')} to upload`);
|
|
299
|
+
const failedUploads = this.uploader.failedUploads.map(file => ({
|
|
300
|
+
relativePath: file.path.replace(process.cwd(), ''),
|
|
301
|
+
sizePretty: (0, filesize_1.filesize)(file.size, { round: 0 }).toString(),
|
|
302
|
+
}));
|
|
303
|
+
const pathPadding = Math.max(...failedUploads.map(upload => upload.relativePath.length)) + 1;
|
|
304
|
+
failedUploads.forEach(upload => {
|
|
305
|
+
console.log(` ${picocolors_1.default.gray('|')} 🔴 ${upload.relativePath.padEnd(pathPadding)} ${picocolors_1.default.gray(`| ${upload.sizePretty.padStart(filesizeStrMaxLength)} |`)}`);
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
if (this.uploader.skippedUploads.length) {
|
|
309
|
+
console.log('\n', constants_js_1.APP_PREFIX, `🗄️ ${picocolors_1.default.bold(this.uploader.skippedUploads.length)} artifacts uploading 🟡${picocolors_1.default.bold('skipped')}`);
|
|
310
|
+
const skippedUploads = this.uploader.skippedUploads.map(file => ({
|
|
311
|
+
relativePath: file.path.replace(process.cwd(), ''),
|
|
312
|
+
sizePretty: file.size === null ? 'unknown' : (0, filesize_1.filesize)(file.size, { round: 0 }).toString(),
|
|
313
|
+
}));
|
|
314
|
+
const pathPadding = Math.max(...skippedUploads.map(upload => upload.relativePath.length)) + 1;
|
|
315
|
+
skippedUploads.forEach(upload => {
|
|
316
|
+
console.log(` ${picocolors_1.default.gray('|')} 🟡 ${upload.relativePath.padEnd(pathPadding)} ${picocolors_1.default.gray(`| ${upload.sizePretty.padStart(filesizeStrMaxLength)} |`)}`);
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
if (this.uploader.skippedUploads.length || this.uploader.failedUploads.length) {
|
|
320
|
+
const command = `TESTOMATIO=<your_api_key> TESTOMATIO_RUN=${this.runId} npx @testomatio/reporter upload-artifacts`;
|
|
321
|
+
const numberOfNotUploadedArtifacts = this.uploader.skippedUploads.length + this.uploader.failedUploads.length;
|
|
322
|
+
console.log(constants_js_1.APP_PREFIX, `${numberOfNotUploadedArtifacts} artifacts were not uploaded.
|
|
323
|
+
Run "${picocolors_1.default.magenta(command)}" with valid S3 credentials to upload skipped & failed artifacts`);
|
|
244
324
|
}
|
|
245
325
|
})
|
|
246
326
|
.catch(err => console.log(constants_js_1.APP_PREFIX, err));
|
|
@@ -255,7 +335,7 @@ class Client {
|
|
|
255
335
|
logs = logs?.trim();
|
|
256
336
|
if (Array.isArray(steps)) {
|
|
257
337
|
steps = steps
|
|
258
|
-
.map(step => formatStep(step))
|
|
338
|
+
.map(step => (0, utils_js_1.formatStep)(step))
|
|
259
339
|
.flat()
|
|
260
340
|
.join('\n');
|
|
261
341
|
}
|
|
@@ -327,20 +407,6 @@ function isNotInternalFrame(frame) {
|
|
|
327
407
|
!frame.getFileName().includes('node_modules') &&
|
|
328
408
|
!frame.getFileName().includes('internal'));
|
|
329
409
|
}
|
|
330
|
-
function formatStep(step, shift = 0) {
|
|
331
|
-
const prefix = ' '.repeat(shift);
|
|
332
|
-
const lines = [];
|
|
333
|
-
if (step.error) {
|
|
334
|
-
lines.push(`${prefix}${picocolors_1.default.red(step.title)} ${picocolors_1.default.gray(`${step.duration}ms`)}`);
|
|
335
|
-
}
|
|
336
|
-
else {
|
|
337
|
-
lines.push(`${prefix}${step.title} ${picocolors_1.default.gray(`${step.duration}ms`)}`);
|
|
338
|
-
}
|
|
339
|
-
for (const child of step.steps || []) {
|
|
340
|
-
lines.push(...formatStep(child, shift + 2));
|
|
341
|
-
}
|
|
342
|
-
return lines;
|
|
343
|
-
}
|
|
344
410
|
/**
|
|
345
411
|
*
|
|
346
412
|
* @param {TestData} testData
|
package/lib/config.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const config: NodeJS.ProcessEnv;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export const APP_PREFIX: string;
|
|
2
|
+
export const TESTOMAT_TMP_STORAGE_DIR: string;
|
|
3
|
+
export const CSV_HEADERS: {
|
|
4
|
+
id: string;
|
|
5
|
+
title: string;
|
|
6
|
+
}[];
|
|
7
|
+
export namespace STATUS {
|
|
8
|
+
let PASSED: string;
|
|
9
|
+
let FAILED: string;
|
|
10
|
+
let SKIPPED: string;
|
|
11
|
+
let FINISHED: string;
|
|
12
|
+
}
|
|
13
|
+
export namespace HTML_REPORT {
|
|
14
|
+
let FOLDER: string;
|
|
15
|
+
let REPORT_DEFAULT_NAME: string;
|
|
16
|
+
let TEMPLATE_NAME: string;
|
|
17
|
+
}
|
|
18
|
+
export const AXIOS_TIMEOUT: number;
|
|
19
|
+
export const testomatLogoURL: "https://avatars.githubusercontent.com/u/59105116?s=36&v=4";
|
|
20
|
+
export namespace REPORTER_REQUEST_RETRIES {
|
|
21
|
+
let retryTimeout: number;
|
|
22
|
+
let retriesPerRequest: number;
|
|
23
|
+
let maxTotalRetries: number;
|
|
24
|
+
let withinTimeSeconds: number;
|
|
25
|
+
}
|
package/lib/constants.js
CHANGED
|
@@ -9,7 +9,11 @@ const os_1 = __importDefault(require("os"));
|
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
10
|
const APP_PREFIX = picocolors_1.default.gray('[TESTOMATIO]');
|
|
11
11
|
exports.APP_PREFIX = APP_PREFIX;
|
|
12
|
-
const
|
|
12
|
+
const TESTOMATIO_REQUEST_TIMEOUT = parseInt(process.env.TESTOMATIO_REQUEST_TIMEOUT, 10);
|
|
13
|
+
if (TESTOMATIO_REQUEST_TIMEOUT) {
|
|
14
|
+
console.log(`${APP_PREFIX} Request timeout is set to ${TESTOMATIO_REQUEST_TIMEOUT / 1000}s`);
|
|
15
|
+
}
|
|
16
|
+
const AXIOS_TIMEOUT = TESTOMATIO_REQUEST_TIMEOUT || 20 * 1000;
|
|
13
17
|
exports.AXIOS_TIMEOUT = AXIOS_TIMEOUT;
|
|
14
18
|
const TESTOMAT_TMP_STORAGE_DIR = path_1.default.join(os_1.default.tmpdir(), 'testomatio_tmp');
|
|
15
19
|
exports.TESTOMAT_TMP_STORAGE_DIR = TESTOMAT_TMP_STORAGE_DIR;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export const dataStorage: DataStorage;
|
|
2
|
+
declare class DataStorage {
|
|
3
|
+
static "__#11@#instance": any;
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @returns {DataStorage}
|
|
7
|
+
*/
|
|
8
|
+
static getInstance(): DataStorage;
|
|
9
|
+
context: any;
|
|
10
|
+
setContext(context: any): void;
|
|
11
|
+
isFileStorage: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Puts any data to storage (file or global variable).
|
|
14
|
+
* If file: stores data as text, if global variable – stores as array of data.
|
|
15
|
+
* @param {'log' | 'artifact' | 'keyvalue'} dataType
|
|
16
|
+
* @param {*} data anything you want to store (string, object, array, etc)
|
|
17
|
+
* @param {*} context could be testId or any context (test name, suite name, including their IDs etc)
|
|
18
|
+
* suite name + test name is used by default
|
|
19
|
+
* @returns
|
|
20
|
+
*/
|
|
21
|
+
putData(dataType: "log" | "artifact" | "keyvalue", data: any, context?: any): void;
|
|
22
|
+
/**
|
|
23
|
+
* Returns data, stored for specific test/context (or data which was stored without test id specified).
|
|
24
|
+
* This method will get data from global variable and/or from from file (previosly saved with put method).
|
|
25
|
+
*
|
|
26
|
+
* @param {'log' | 'artifact' | 'keyvalue'} dataType
|
|
27
|
+
* @param {string} context
|
|
28
|
+
* @returns {any []} array of data (any type), null (if no data found for context) or string (if data type is log)
|
|
29
|
+
*/
|
|
30
|
+
getData(dataType: "log" | "artifact" | "keyvalue", context: string): any[];
|
|
31
|
+
#private;
|
|
32
|
+
}
|
|
33
|
+
export function stringToMD5Hash(str: any): string;
|
|
34
|
+
export {};
|
package/lib/data-storage.js
CHANGED
|
@@ -76,7 +76,7 @@ class DataStorage {
|
|
|
76
76
|
return;
|
|
77
77
|
context = context || this.context || utils_js_1.testRunnerHelper.getNameOfCurrentlyRunningTest();
|
|
78
78
|
if (!context) {
|
|
79
|
-
debug(`No context provided for "${dataType}" data:`, data);
|
|
79
|
+
// debug(`No context provided for "${dataType}" data:`, data);
|
|
80
80
|
return;
|
|
81
81
|
}
|
|
82
82
|
const contextHash = stringToMD5Hash(context);
|
|
@@ -119,7 +119,7 @@ class DataStorage {
|
|
|
119
119
|
if (testDataFromFile.length) {
|
|
120
120
|
return testDataFromFile;
|
|
121
121
|
}
|
|
122
|
-
debug(`No "${dataType}" data for context "${contextHash}" in both file and global variable`);
|
|
122
|
+
// debug(`No "${dataType}" data for context "${contextHash}" in both file and global variable`);
|
|
123
123
|
// in case no data found for context
|
|
124
124
|
return null;
|
|
125
125
|
}
|
package/lib/output.d.ts
ADDED
package/lib/package.json
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @class BitbucketPipe
|
|
3
|
+
* @typedef {import('../../types/types.js').Pipe} Pipe
|
|
4
|
+
* @typedef {import('../../types/types.js').TestData} TestData
|
|
5
|
+
*/
|
|
6
|
+
export class BitbucketPipe {
|
|
7
|
+
constructor(params: any, store?: {});
|
|
8
|
+
isEnabled: boolean;
|
|
9
|
+
ENV: NodeJS.ProcessEnv;
|
|
10
|
+
store: {};
|
|
11
|
+
tests: any[];
|
|
12
|
+
token: any;
|
|
13
|
+
hiddenCommentData: string;
|
|
14
|
+
cleanLog(log: any): Promise<string>;
|
|
15
|
+
prepareRun(): Promise<void>;
|
|
16
|
+
createRun(): Promise<void>;
|
|
17
|
+
addTest(test: any): void;
|
|
18
|
+
finishRun(runParams: any): Promise<void>;
|
|
19
|
+
toString(): string;
|
|
20
|
+
updateRun(): void;
|
|
21
|
+
}
|
|
22
|
+
export type Pipe = import("../../types/types.js").Pipe;
|
|
23
|
+
export type TestData = import("../../types/types.js").TestData;
|
package/lib/pipe/bitbucket.js
CHANGED
|
@@ -41,8 +41,8 @@ const debug = (0, debug_1.default)('@testomatio/reporter:pipe:bitbucket');
|
|
|
41
41
|
//! and your pipeline trigger should be a pull request
|
|
42
42
|
/**
|
|
43
43
|
* @class BitbucketPipe
|
|
44
|
-
* @typedef {import('../../types').Pipe} Pipe
|
|
45
|
-
* @typedef {import('../../types').TestData} TestData
|
|
44
|
+
* @typedef {import('../../types/types.js').Pipe} Pipe
|
|
45
|
+
* @typedef {import('../../types/types.js').TestData} TestData
|
|
46
46
|
*/
|
|
47
47
|
class BitbucketPipe {
|
|
48
48
|
constructor(params, store = {}) {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export default CsvPipe;
|
|
2
|
+
export type Pipe = import("../../types/types.js").Pipe;
|
|
3
|
+
export type TestData = import("../../types/types.js").TestData;
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {import('../../types/types.js').Pipe} Pipe
|
|
6
|
+
* @typedef {import('../../types/types.js').TestData} TestData
|
|
7
|
+
* @class CsvPipe
|
|
8
|
+
* @implements {Pipe}
|
|
9
|
+
*/
|
|
10
|
+
declare class CsvPipe implements Pipe {
|
|
11
|
+
constructor(params: any, store: any);
|
|
12
|
+
store: any;
|
|
13
|
+
title: any;
|
|
14
|
+
results: any[];
|
|
15
|
+
outputDir: string;
|
|
16
|
+
defaultReportName: string;
|
|
17
|
+
csvFilename: string;
|
|
18
|
+
isEnabled: boolean;
|
|
19
|
+
outputFile: string;
|
|
20
|
+
prepareRun(): Promise<void>;
|
|
21
|
+
createRun(): Promise<void>;
|
|
22
|
+
updateRun(): void;
|
|
23
|
+
/**
|
|
24
|
+
* Create a folder that will contain the exported files
|
|
25
|
+
*/
|
|
26
|
+
checkExportDir(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Save data to the csv file.
|
|
29
|
+
* @param {Object} data - data that will be added to the CSV file.
|
|
30
|
+
* Example: [{suite_title: "Suite #1", test: "Test-case-1", message: "Test msg"}]
|
|
31
|
+
* @param {Object} headers - csv file headers. Example: [{ id: 'suite_title', title: 'Suite_title' }]
|
|
32
|
+
*/
|
|
33
|
+
saveToCsv(data: any, headers: any): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Add test data to the result array for saving. As a result of this function, we get a result object to save.
|
|
36
|
+
* @param {Object} test - object which includes each test entry.
|
|
37
|
+
*/
|
|
38
|
+
addTest(test: any): void;
|
|
39
|
+
/**
|
|
40
|
+
* @param {{ tests?: TestData[] }} runParams
|
|
41
|
+
* @returns {Promise<void>}
|
|
42
|
+
*/
|
|
43
|
+
finishRun(runParams: {
|
|
44
|
+
tests?: TestData[];
|
|
45
|
+
}): Promise<void>;
|
|
46
|
+
toString(): string;
|
|
47
|
+
}
|
package/lib/pipe/csv.js
CHANGED
|
@@ -13,8 +13,8 @@ const utils_js_1 = require("../utils/utils.js");
|
|
|
13
13
|
const constants_js_1 = require("../constants.js");
|
|
14
14
|
const debug = (0, debug_1.default)('@testomatio/reporter:pipe:csv');
|
|
15
15
|
/**
|
|
16
|
-
* @typedef {import('../../types').Pipe} Pipe
|
|
17
|
-
* @typedef {import('../../types').TestData} TestData
|
|
16
|
+
* @typedef {import('../../types/types.js').Pipe} Pipe
|
|
17
|
+
* @typedef {import('../../types/types.js').TestData} TestData
|
|
18
18
|
* @class CsvPipe
|
|
19
19
|
* @implements {Pipe}
|
|
20
20
|
*/
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export class DebugPipe {
|
|
2
|
+
constructor(params: any, store: any);
|
|
3
|
+
params: any;
|
|
4
|
+
store: any;
|
|
5
|
+
isEnabled: boolean;
|
|
6
|
+
batch: {
|
|
7
|
+
isEnabled: any;
|
|
8
|
+
intervalFunction: any;
|
|
9
|
+
intervalTime: number;
|
|
10
|
+
tests: any[];
|
|
11
|
+
batchIndex: number;
|
|
12
|
+
};
|
|
13
|
+
logFilePath: string;
|
|
14
|
+
testomatioEnvVars: {};
|
|
15
|
+
batchUpload(): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Logs data to a file if logging is enabled.
|
|
18
|
+
*
|
|
19
|
+
* @param {Object} logData - The data to be logged.
|
|
20
|
+
* @returns {Promise<void>} A promise that resolves when the log data has been appended to the file.
|
|
21
|
+
*/
|
|
22
|
+
logToFile(logData: any): Promise<void>;
|
|
23
|
+
lastActionTimestamp: number;
|
|
24
|
+
prepareRun(opts: any): Promise<any[]>;
|
|
25
|
+
createRun(params?: {}): Promise<{}>;
|
|
26
|
+
addTest(data: any): Promise<void>;
|
|
27
|
+
finishRun(params: any): Promise<void>;
|
|
28
|
+
toString(): string;
|
|
29
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DebugPipe = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const os_1 = __importDefault(require("os"));
|
|
10
|
+
const debug_1 = __importDefault(require("debug"));
|
|
11
|
+
const constants_js_1 = require("../constants.js");
|
|
12
|
+
const pretty_ms_1 = __importDefault(require("pretty-ms"));
|
|
13
|
+
const debug = (0, debug_1.default)('@testomatio/reporter:pipe:debug');
|
|
14
|
+
class DebugPipe {
|
|
15
|
+
constructor(params, store) {
|
|
16
|
+
this.params = params || {};
|
|
17
|
+
this.store = store || {};
|
|
18
|
+
this.isEnabled = !!process.env.TESTOMATIO_DEBUG || !!process.env.DEBUG;
|
|
19
|
+
if (this.isEnabled) {
|
|
20
|
+
this.batch = {
|
|
21
|
+
isEnabled: this.params.isBatchEnabled ?? !process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD ?? true,
|
|
22
|
+
intervalFunction: null,
|
|
23
|
+
intervalTime: 5000,
|
|
24
|
+
tests: [],
|
|
25
|
+
batchIndex: 0,
|
|
26
|
+
};
|
|
27
|
+
this.logFilePath = path_1.default.join(os_1.default.tmpdir(), `testomatio.debug.${Date.now()}.json`);
|
|
28
|
+
debug('Creating debug file:', this.logFilePath);
|
|
29
|
+
fs_1.default.writeFileSync(this.logFilePath, '');
|
|
30
|
+
console.log(constants_js_1.APP_PREFIX, '🪲. Debug created:');
|
|
31
|
+
this.testomatioEnvVars = Object.keys(process.env)
|
|
32
|
+
.filter(key => key.startsWith('TESTOMATIO_'))
|
|
33
|
+
.reduce((acc, key) => {
|
|
34
|
+
acc[key] = process.env[key];
|
|
35
|
+
return acc;
|
|
36
|
+
}, {});
|
|
37
|
+
this.logToFile({ datetime: new Date().toISOString(), timestamp: Date.now() });
|
|
38
|
+
this.logToFile({ data: 'variables', testomatioEnvVars: this.testomatioEnvVars });
|
|
39
|
+
this.logToFile({ data: 'store', store: this.store || {} });
|
|
40
|
+
// Bind batchUpload to the instance
|
|
41
|
+
this.batchUpload = this.batchUpload.bind(this);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Logs data to a file if logging is enabled.
|
|
46
|
+
*
|
|
47
|
+
* @param {Object} logData - The data to be logged.
|
|
48
|
+
* @returns {Promise<void>} A promise that resolves when the log data has been appended to the file.
|
|
49
|
+
*/
|
|
50
|
+
logToFile(logData) {
|
|
51
|
+
if (!this.isEnabled)
|
|
52
|
+
return;
|
|
53
|
+
const timePassedFromLastAction = Date.now() - (this.lastActionTimestamp || Date.now());
|
|
54
|
+
this.lastActionTimestamp = Date.now();
|
|
55
|
+
const logLine = JSON.stringify({ t: `+${(0, pretty_ms_1.default)(timePassedFromLastAction)}`, ...logData });
|
|
56
|
+
fs_1.default.appendFileSync(this.logFilePath, `${logLine}\n`);
|
|
57
|
+
}
|
|
58
|
+
async prepareRun(opts) {
|
|
59
|
+
if (!this.isEnabled)
|
|
60
|
+
return [];
|
|
61
|
+
this.logToFile({ action: 'prepareRun', data: opts });
|
|
62
|
+
}
|
|
63
|
+
async createRun(params = {}) {
|
|
64
|
+
if (!this.isEnabled)
|
|
65
|
+
return;
|
|
66
|
+
if (params.isBatchEnabled === true || params.isBatchEnabled === false)
|
|
67
|
+
this.batch.isEnabled = params.isBatchEnabled;
|
|
68
|
+
if (!this.isEnabled)
|
|
69
|
+
return {};
|
|
70
|
+
if (this.batch.isEnabled)
|
|
71
|
+
this.batch.intervalFunction = setInterval(this.batchUpload, this.batch.intervalTime);
|
|
72
|
+
this.logToFile({ action: 'createRun', params });
|
|
73
|
+
}
|
|
74
|
+
async addTest(data) {
|
|
75
|
+
if (!this.isEnabled)
|
|
76
|
+
return;
|
|
77
|
+
if (!this.batch.isEnabled)
|
|
78
|
+
this.logToFile({ action: 'addTest', testId: data });
|
|
79
|
+
else
|
|
80
|
+
this.batch.tests.push(data);
|
|
81
|
+
if (!this.batch.intervalFunction)
|
|
82
|
+
await this.batchUpload();
|
|
83
|
+
}
|
|
84
|
+
async batchUpload() {
|
|
85
|
+
this.batch.batchIndex++;
|
|
86
|
+
if (!this.batch.isEnabled)
|
|
87
|
+
return;
|
|
88
|
+
if (!this.batch.tests.length)
|
|
89
|
+
return;
|
|
90
|
+
const testsToSend = this.batch.tests.splice(0);
|
|
91
|
+
this.logToFile({ action: 'addTestsBatch', tests: testsToSend });
|
|
92
|
+
}
|
|
93
|
+
async finishRun(params) {
|
|
94
|
+
if (!this.isEnabled)
|
|
95
|
+
return;
|
|
96
|
+
this.logToFile({ actions: 'finishRun', params });
|
|
97
|
+
await this.batchUpload();
|
|
98
|
+
if (this.batch.intervalFunction)
|
|
99
|
+
clearInterval(this.batch.intervalFunction);
|
|
100
|
+
console.log(constants_js_1.APP_PREFIX, '🪲. Debug Saved to', this.logFilePath);
|
|
101
|
+
}
|
|
102
|
+
toString() {
|
|
103
|
+
return 'Debug Reporter';
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
exports.DebugPipe = DebugPipe;
|
|
107
|
+
|
|
108
|
+
module.exports.DebugPipe = DebugPipe;
|