@testomatio/reporter 2.7.1 → 2.7.2-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/README.md +2 -1
- package/lib/adapter/codecept.js +81 -26
- package/lib/adapter/playwright.d.ts +1 -1
- package/lib/adapter/playwright.js +54 -34
- package/lib/adapter/utils/step-formatter.d.ts +134 -0
- package/lib/adapter/utils/step-formatter.js +237 -0
- package/lib/bin/cli.js +28 -31
- package/lib/bin/reportXml.js +5 -6
- package/lib/bin/uploadArtifacts.js +6 -6
- package/lib/client.d.ts +8 -0
- package/lib/client.js +71 -10
- package/lib/constants.d.ts +1 -0
- package/lib/constants.js +7 -1
- package/lib/pipe/bitbucket.js +2 -1
- package/lib/pipe/coverage.js +16 -15
- package/lib/pipe/debug.js +3 -3
- package/lib/pipe/github.js +3 -2
- package/lib/pipe/gitlab.js +2 -1
- package/lib/pipe/index.js +5 -5
- package/lib/pipe/testomatio.js +21 -24
- package/lib/uploader.js +3 -2
- package/lib/utils/log.d.ts +45 -0
- package/lib/utils/log.js +98 -0
- package/lib/utils/pipe_utils.js +5 -5
- package/lib/utils/utils.d.ts +10 -0
- package/lib/utils/utils.js +16 -1
- package/lib/xmlReader.js +5 -4
- package/package.json +1 -1
- package/src/adapter/codecept.js +99 -29
- package/src/adapter/playwright.js +64 -39
- package/src/adapter/utils/step-formatter.js +232 -0
- package/src/bin/cli.js +34 -31
- package/src/bin/reportXml.js +5 -6
- package/src/bin/uploadArtifacts.js +6 -6
- package/src/client.js +76 -26
- package/src/constants.js +4 -0
- package/src/pipe/bitbucket.js +2 -1
- package/src/pipe/coverage.js +16 -15
- package/src/pipe/debug.js +3 -3
- package/src/pipe/github.js +4 -3
- package/src/pipe/gitlab.js +2 -1
- package/src/pipe/index.js +5 -7
- package/src/pipe/testomatio.js +32 -25
- package/src/uploader.js +3 -2
- package/src/utils/log.js +87 -0
- package/src/utils/pipe_utils.js +5 -5
- package/src/utils/utils.js +14 -0
- package/src/xmlReader.js +5 -4
- package/types/types.d.ts +3 -0
package/lib/client.js
CHANGED
|
@@ -14,8 +14,10 @@ const path_1 = __importDefault(require("path"));
|
|
|
14
14
|
const node_url_1 = require("node:url");
|
|
15
15
|
const uploader_js_1 = require("./uploader.js");
|
|
16
16
|
const utils_js_1 = require("./utils/utils.js");
|
|
17
|
+
const step_formatter_js_1 = require("./adapter/utils/step-formatter.js");
|
|
17
18
|
const filesize_1 = require("filesize");
|
|
18
19
|
const log_formatter_js_1 = require("./utils/log-formatter.js");
|
|
20
|
+
const log_js_1 = require("./utils/log.js");
|
|
19
21
|
const debug = (0, debug_1.default)('@testomatio/reporter:client');
|
|
20
22
|
// removed __dirname usage, because:
|
|
21
23
|
// 1. replaced with ESM syntax (import.meta.url), but it throws an error on tsc compilation;
|
|
@@ -39,7 +41,7 @@ class Client {
|
|
|
39
41
|
const pathToPackageJSON = path_1.default.join(__dirname, '../package.json');
|
|
40
42
|
try {
|
|
41
43
|
this.version = JSON.parse(fs_1.default.readFileSync(pathToPackageJSON).toString()).version;
|
|
42
|
-
|
|
44
|
+
log_js_1.log.info(`Testomatio Reporter v${this.version}`);
|
|
43
45
|
}
|
|
44
46
|
catch (e) {
|
|
45
47
|
// do nothing
|
|
@@ -67,7 +69,7 @@ class Client {
|
|
|
67
69
|
const { pipe, pipeOptions } = params;
|
|
68
70
|
// ❗ Validation: pipe is required
|
|
69
71
|
if (!pipe || !pipeOptions) {
|
|
70
|
-
|
|
72
|
+
log_js_1.log.warn(`❗ No valid pipe found in filter cmd. Expected format: <pipe>:<options>
|
|
71
73
|
Examples:
|
|
72
74
|
--filter "testomatio:tag-name=frontend"
|
|
73
75
|
--filter "coverage:file=coverage.yml"
|
|
@@ -84,7 +86,7 @@ class Client {
|
|
|
84
86
|
const p = this.pipes.find(p => p.constructor.name.toLowerCase() === `${pipe.toLowerCase()}pipe`);
|
|
85
87
|
// const p = this.pipes.find(p => p.id === `${pipe.toLowerCase()}`); TODO: as future updates
|
|
86
88
|
if (!p?.isEnabled) {
|
|
87
|
-
|
|
89
|
+
log_js_1.log.warn('🚫 No active pipes were found in the system. Execution aborted!');
|
|
88
90
|
return;
|
|
89
91
|
}
|
|
90
92
|
// Run only the selected pipe
|
|
@@ -94,7 +96,7 @@ class Client {
|
|
|
94
96
|
return result;
|
|
95
97
|
}
|
|
96
98
|
catch (err) {
|
|
97
|
-
|
|
99
|
+
log_js_1.log.error(err);
|
|
98
100
|
}
|
|
99
101
|
}
|
|
100
102
|
/**
|
|
@@ -111,7 +113,7 @@ class Client {
|
|
|
111
113
|
return Promise.resolve();
|
|
112
114
|
this.queue = this.queue
|
|
113
115
|
.then(() => Promise.all(this.pipes.map(p => p.createRun(params))))
|
|
114
|
-
.catch(err =>
|
|
116
|
+
.catch(err => log_js_1.log.info(err))
|
|
115
117
|
.then(() => {
|
|
116
118
|
const runId = this.pipeStore?.runId;
|
|
117
119
|
if (runId)
|
|
@@ -123,6 +125,58 @@ class Client {
|
|
|
123
125
|
// debug('Run', this.queue);
|
|
124
126
|
return this.queue;
|
|
125
127
|
}
|
|
128
|
+
/**
|
|
129
|
+
* Recursively uploads artifacts from steps
|
|
130
|
+
*
|
|
131
|
+
* @param {*} steps - Steps payload (validated inside function)
|
|
132
|
+
* @param {string} testRid - Test/result ID
|
|
133
|
+
* @returns {Promise<void>}
|
|
134
|
+
*/
|
|
135
|
+
async uploadStepArtifacts(steps, testRid) {
|
|
136
|
+
if (!steps || !Array.isArray(steps))
|
|
137
|
+
return;
|
|
138
|
+
if (!this.uploader.isEnabled || !constants_js_1.SCREENSHOTS_ON_STEPS)
|
|
139
|
+
return;
|
|
140
|
+
try {
|
|
141
|
+
for (const step of steps) {
|
|
142
|
+
if (!(step.artifacts && Array.isArray(step.artifacts))) {
|
|
143
|
+
if (step.steps) {
|
|
144
|
+
await this.uploadStepArtifacts(step.steps, testRid);
|
|
145
|
+
}
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const uploadedArtifacts = [];
|
|
149
|
+
for (const artifact of step.artifacts) {
|
|
150
|
+
if (typeof artifact === 'string' && !(0, utils_js_1.isHttpUrl)(artifact)) {
|
|
151
|
+
const filename = (0, step_formatter_js_1.generateShortFilename)(artifact);
|
|
152
|
+
try {
|
|
153
|
+
const uploadResult = await this.uploader.uploadFileByPath(artifact, [this.runId, testRid, 'steps', filename]);
|
|
154
|
+
if (uploadResult) {
|
|
155
|
+
uploadedArtifacts.push(uploadResult);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
uploadedArtifacts.push(artifact);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch (uploadErr) {
|
|
162
|
+
uploadedArtifacts.push(artifact);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
uploadedArtifacts.push(artifact);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
step.artifacts = uploadedArtifacts;
|
|
170
|
+
if (step.steps) {
|
|
171
|
+
await this.uploadStepArtifacts(step.steps, testRid);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
console.error(constants_js_1.APP_PREFIX, 'Error in uploadStepArtifacts for testRid', testRid, ':', err);
|
|
177
|
+
throw err;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
126
180
|
/**
|
|
127
181
|
* Updates test status and its data
|
|
128
182
|
*
|
|
@@ -145,6 +199,13 @@ class Client {
|
|
|
145
199
|
*/
|
|
146
200
|
const { rid, error = null, steps: originalSteps, title, suite_title } = testData;
|
|
147
201
|
let steps = originalSteps;
|
|
202
|
+
// Upload artifacts from steps
|
|
203
|
+
try {
|
|
204
|
+
await this.uploadStepArtifacts(steps, rid);
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
console.log(constants_js_1.APP_PREFIX, 'Failed to upload step artifacts:', err);
|
|
208
|
+
}
|
|
148
209
|
const uploadedFiles = [];
|
|
149
210
|
const stackArtifactsEnabled = (0, utils_js_1.transformEnvVarToBoolean)(process.env.TESTOMATIO_STACK_ARTIFACTS);
|
|
150
211
|
const { time = 0, example = null, files = [], filesBuffers = [], code = null, file, suite_id, test_id, timestamp, links, manuallyAttachedArtifacts, overwrite, tags, } = testData;
|
|
@@ -237,7 +298,7 @@ class Client {
|
|
|
237
298
|
return { pipe: pipe.toString(), result };
|
|
238
299
|
}
|
|
239
300
|
catch (err) {
|
|
240
|
-
|
|
301
|
+
log_js_1.log.info(pipe.toString(), err);
|
|
241
302
|
}
|
|
242
303
|
})));
|
|
243
304
|
// @ts-ignore
|
|
@@ -276,7 +337,7 @@ class Client {
|
|
|
276
337
|
});
|
|
277
338
|
}
|
|
278
339
|
if (this.uploader.failedUploads.length) {
|
|
279
|
-
|
|
340
|
+
log_js_1.log.info(`🗄️ ${this.uploader.failedUploads.length} artifacts 🔴${picocolors_1.default.bold('failed')} to upload`);
|
|
280
341
|
const failedUploads = this.uploader.failedUploads.map(file => ({
|
|
281
342
|
relativePath: file.path.replace(process.cwd(), ''),
|
|
282
343
|
sizePretty: file.size == null ? 'unknown' : (0, filesize_1.filesize)(file.size, { round: 0 }).toString(),
|
|
@@ -287,7 +348,7 @@ class Client {
|
|
|
287
348
|
});
|
|
288
349
|
}
|
|
289
350
|
if (this.uploader.skippedUploads.length) {
|
|
290
|
-
|
|
351
|
+
log_js_1.log.info(`🗄️ ${picocolors_1.default.bold(this.uploader.skippedUploads.length)} artifacts uploading 🟡${picocolors_1.default.bold('skipped')}`);
|
|
291
352
|
const skippedUploads = this.uploader.skippedUploads.map(file => ({
|
|
292
353
|
relativePath: file.path.replace(process.cwd(), ''),
|
|
293
354
|
sizePretty: file.size === null ? 'unknown' : (0, filesize_1.filesize)(file.size, { round: 0 }).toString(),
|
|
@@ -300,11 +361,11 @@ class Client {
|
|
|
300
361
|
if (this.uploader.skippedUploads.length || this.uploader.failedUploads.length) {
|
|
301
362
|
const command = `TESTOMATIO=<your_api_key> TESTOMATIO_RUN=${this.runId} npx @testomatio/reporter upload-artifacts`;
|
|
302
363
|
const numberOfNotUploadedArtifacts = this.uploader.skippedUploads.length + this.uploader.failedUploads.length;
|
|
303
|
-
|
|
364
|
+
log_js_1.log.info(`${numberOfNotUploadedArtifacts} artifacts were not uploaded.
|
|
304
365
|
Run "${picocolors_1.default.magenta(command)}" with valid S3 credentials to upload skipped & failed artifacts`);
|
|
305
366
|
}
|
|
306
367
|
})
|
|
307
|
-
.catch(err =>
|
|
368
|
+
.catch(err => log_js_1.log.info(err));
|
|
308
369
|
return this.queue;
|
|
309
370
|
}
|
|
310
371
|
}
|
package/lib/constants.d.ts
CHANGED
package/lib/constants.js
CHANGED
|
@@ -3,10 +3,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.REPORTER_REQUEST_RETRIES = exports.testomatLogoURL = exports.AXIOS_TIMEOUT = exports.HTML_REPORT = exports.STATUS = exports.CSV_HEADERS = exports.TESTOMAT_TMP_STORAGE_DIR = exports.APP_PREFIX = void 0;
|
|
6
|
+
exports.SCREENSHOTS_ON_STEPS = exports.REPORTER_REQUEST_RETRIES = exports.testomatLogoURL = exports.AXIOS_TIMEOUT = exports.HTML_REPORT = exports.STATUS = exports.CSV_HEADERS = exports.TESTOMAT_TMP_STORAGE_DIR = exports.APP_PREFIX = void 0;
|
|
7
7
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
8
8
|
const os_1 = __importDefault(require("os"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const utils_js_1 = require("./utils/utils.js");
|
|
10
11
|
const APP_PREFIX = picocolors_1.default.gray('[TESTOMATIO]');
|
|
11
12
|
exports.APP_PREFIX = APP_PREFIX;
|
|
12
13
|
const TESTOMATIO_REQUEST_TIMEOUT = parseInt(process.env.TESTOMATIO_REQUEST_TIMEOUT, 10);
|
|
@@ -15,6 +16,9 @@ if (TESTOMATIO_REQUEST_TIMEOUT) {
|
|
|
15
16
|
}
|
|
16
17
|
const AXIOS_TIMEOUT = TESTOMATIO_REQUEST_TIMEOUT || 20 * 1000;
|
|
17
18
|
exports.AXIOS_TIMEOUT = AXIOS_TIMEOUT;
|
|
19
|
+
const SCREENSHOTS_ON_STEPS = process.env.TESTOMATIO_SCREENSHOTS_ON_STEPS == null
|
|
20
|
+
|| (0, utils_js_1.transformEnvVarToBoolean)(process.env.TESTOMATIO_SCREENSHOTS_ON_STEPS);
|
|
21
|
+
exports.SCREENSHOTS_ON_STEPS = SCREENSHOTS_ON_STEPS;
|
|
18
22
|
const TESTOMAT_TMP_STORAGE_DIR = path_1.default.join(os_1.default.tmpdir(), 'testomatio_tmp');
|
|
19
23
|
exports.TESTOMAT_TMP_STORAGE_DIR = TESTOMAT_TMP_STORAGE_DIR;
|
|
20
24
|
const CSV_HEADERS = [
|
|
@@ -53,6 +57,8 @@ module.exports.APP_PREFIX = APP_PREFIX;
|
|
|
53
57
|
|
|
54
58
|
module.exports.AXIOS_TIMEOUT = AXIOS_TIMEOUT;
|
|
55
59
|
|
|
60
|
+
module.exports.SCREENSHOTS_ON_STEPS = SCREENSHOTS_ON_STEPS;
|
|
61
|
+
|
|
56
62
|
module.exports.TESTOMAT_TMP_STORAGE_DIR = TESTOMAT_TMP_STORAGE_DIR;
|
|
57
63
|
|
|
58
64
|
module.exports.CSV_HEADERS = CSV_HEADERS;
|
package/lib/pipe/bitbucket.js
CHANGED
|
@@ -46,6 +46,7 @@ const humanize_duration_1 = __importDefault(require("humanize-duration"));
|
|
|
46
46
|
const lodash_merge_1 = __importDefault(require("lodash.merge"));
|
|
47
47
|
const path_1 = __importDefault(require("path"));
|
|
48
48
|
const debug_1 = __importDefault(require("debug"));
|
|
49
|
+
const log_js_1 = require("../utils/log.js");
|
|
49
50
|
const debug = (0, debug_1.default)('@testomatio/reporter:pipe:bitbucket');
|
|
50
51
|
//! BITBUCKET_ACCESS_TOKEN environment variable is required for this functionality to work
|
|
51
52
|
//! and your pipeline trigger should be a pull request
|
|
@@ -190,7 +191,7 @@ class BitbucketPipe {
|
|
|
190
191
|
const commentID = addCommentResponse.data.id;
|
|
191
192
|
// eslint-disable-next-line max-len
|
|
192
193
|
const commentURL = `https://bitbucket.org/${this.ENV.BITBUCKET_WORKSPACE}/${this.ENV.BITBUCKET_REPO_SLUG}/pull-requests/${this.ENV.BITBUCKET_PR_ID}#comment-${commentID}`;
|
|
193
|
-
|
|
194
|
+
log_js_1.log.info(picocolors_1.default.yellow('Bitbucket'), `Report created: ${picocolors_1.default.magenta(commentURL)}`);
|
|
194
195
|
}
|
|
195
196
|
catch (err) {
|
|
196
197
|
console.error(constants_js_1.APP_PREFIX, picocolors_1.default.yellow('Bitbucket'), `Couldn't create Bitbucket report\n${err}.
|
package/lib/pipe/coverage.js
CHANGED
|
@@ -14,6 +14,7 @@ const pipe_utils_js_1 = require("../utils/pipe_utils.js");
|
|
|
14
14
|
const pipe_utils_js_2 = require("../utils/pipe_utils.js");
|
|
15
15
|
const config_js_1 = require("../config.js");
|
|
16
16
|
const debug_1 = __importDefault(require("debug"));
|
|
17
|
+
const log_js_1 = require("../utils/log.js");
|
|
17
18
|
const debug = (0, debug_1.default)('@testomatio/reporter:pipe:csv');
|
|
18
19
|
// Example of use 'coverage:file=coverage/coverage.yml,diff=master' cmd:
|
|
19
20
|
// | Option | Git command | Notes |
|
|
@@ -121,10 +122,10 @@ class CoveragePipe {
|
|
|
121
122
|
// Step 2: Extract all available tests and compare with coverage file
|
|
122
123
|
const lines = await this.extractRelevantTestsFromChanges();
|
|
123
124
|
if (this.store?.filterList && lines.size > 0) {
|
|
124
|
-
|
|
125
|
+
log_js_1.log.info(`Matched files: ${[...lines].join(', ')}`);
|
|
125
126
|
}
|
|
126
127
|
if (lines.size === 0) {
|
|
127
|
-
|
|
128
|
+
log_js_1.log.info('ℹ️ No matching entries in coverage file for provided Git changes.');
|
|
128
129
|
return [];
|
|
129
130
|
}
|
|
130
131
|
// Step 3: Handle tag labels tests from the server
|
|
@@ -141,7 +142,7 @@ class CoveragePipe {
|
|
|
141
142
|
}
|
|
142
143
|
}
|
|
143
144
|
if (this.tests.size === 0 && this.suiteIds.size === 0) {
|
|
144
|
-
|
|
145
|
+
log_js_1.log.info('ℹ️ No tests found for execution based on Git changes.');
|
|
145
146
|
return [];
|
|
146
147
|
}
|
|
147
148
|
this.results = [...this.tests, ...this.suiteIds];
|
|
@@ -200,7 +201,7 @@ class CoveragePipe {
|
|
|
200
201
|
...q,
|
|
201
202
|
});
|
|
202
203
|
if (!Array.isArray(resp.data?.tests) && resp.data?.tests?.length === 0) {
|
|
203
|
-
|
|
204
|
+
log_js_1.log.info(`🔍 No test by ${type}=${id} were found on the Testomat.io server side!`);
|
|
204
205
|
return undefined;
|
|
205
206
|
}
|
|
206
207
|
return resp.data.tests;
|
|
@@ -232,7 +233,7 @@ class CoveragePipe {
|
|
|
232
233
|
const errorMessage = err.message || '';
|
|
233
234
|
// Git edge: Not a git repository or other error
|
|
234
235
|
if (errorMessage.includes('Not a git repository')) {
|
|
235
|
-
|
|
236
|
+
log_js_1.log.error('❌ Error: This folder is not a Git repository.');
|
|
236
237
|
}
|
|
237
238
|
else {
|
|
238
239
|
throw new Error(`❌ Git command failed ("${cmd}"):\n`, errorMessage);
|
|
@@ -272,10 +273,10 @@ class CoveragePipe {
|
|
|
272
273
|
cmd = this.#buildGitCommand();
|
|
273
274
|
}
|
|
274
275
|
catch (err) {
|
|
275
|
-
|
|
276
|
+
log_js_1.log.error(err.message);
|
|
276
277
|
return undefined;
|
|
277
278
|
}
|
|
278
|
-
|
|
279
|
+
log_js_1.log.error(`ℹ️ We will use '${cmd}' Git command.`);
|
|
279
280
|
try {
|
|
280
281
|
// For clear unit testing process -> Like test_defaultGitChangedFile = todomvc-tests/edit-todos_test.js
|
|
281
282
|
if (this.isDefaultGitChanges) {
|
|
@@ -290,11 +291,11 @@ class CoveragePipe {
|
|
|
290
291
|
}
|
|
291
292
|
}
|
|
292
293
|
catch (err) {
|
|
293
|
-
|
|
294
|
-
|
|
294
|
+
log_js_1.log.error(err.message);
|
|
295
|
+
log_js_1.log.error("🔍 Pls, check this Git command manually to understand the original problem.");
|
|
295
296
|
return undefined;
|
|
296
297
|
}
|
|
297
|
-
|
|
298
|
+
log_js_1.log.info(`📑 GIT changed files:\n - ${this.changedFiles.join('\n - ')}`);
|
|
298
299
|
return this;
|
|
299
300
|
}
|
|
300
301
|
/**
|
|
@@ -313,18 +314,18 @@ class CoveragePipe {
|
|
|
313
314
|
validateCoverageFile() {
|
|
314
315
|
// Validate the presence of the coverage filepath
|
|
315
316
|
if (!fs_1.default.existsSync(this.coverageFilePath)) {
|
|
316
|
-
|
|
317
|
+
log_js_1.log.info('❌ Coverage file not found:', this.coverageFilePath);
|
|
317
318
|
return undefined;
|
|
318
319
|
}
|
|
319
320
|
// Ensure the given path is a file (not a directory or other type)
|
|
320
321
|
const stat = fs_1.default.statSync(this.coverageFilePath);
|
|
321
322
|
if (!stat.isFile()) {
|
|
322
|
-
|
|
323
|
+
log_js_1.log.info('❌ Provided coverage path is not a file:', this.coverageFilePath);
|
|
323
324
|
return undefined;
|
|
324
325
|
}
|
|
325
326
|
// Validate the file extension to be ".yml" to ensure it's a YAML file
|
|
326
327
|
if (path_1.default.extname(this.coverageFilePath) !== ".yml") {
|
|
327
|
-
|
|
328
|
+
log_js_1.log.info('❌ Coverage file must have a .yml extension:', this.coverageFilePath);
|
|
328
329
|
return undefined;
|
|
329
330
|
}
|
|
330
331
|
debug('Coverage file validation is OK!');
|
|
@@ -346,11 +347,11 @@ class CoveragePipe {
|
|
|
346
347
|
const rawYml = fs_1.default.readFileSync(this.coverageFilePath, 'utf8');
|
|
347
348
|
this.parsedCoverage = js_yaml_1.default.load(rawYml) || {};
|
|
348
349
|
debug(`Coverage filepath = ${this.coverageFilePath})`);
|
|
349
|
-
|
|
350
|
+
log_js_1.log.info(`✅ Coverage file parsed successfully: ${this.coverageFilePath}`);
|
|
350
351
|
return this;
|
|
351
352
|
}
|
|
352
353
|
catch (err) {
|
|
353
|
-
|
|
354
|
+
log_js_1.log.error('❌ Failed to parse YAML:', err.message);
|
|
354
355
|
return undefined;
|
|
355
356
|
}
|
|
356
357
|
}
|
package/lib/pipe/debug.js
CHANGED
|
@@ -8,8 +8,8 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const os_1 = __importDefault(require("os"));
|
|
10
10
|
const debug_1 = __importDefault(require("debug"));
|
|
11
|
-
const constants_js_1 = require("../constants.js");
|
|
12
11
|
const pretty_ms_1 = __importDefault(require("pretty-ms"));
|
|
12
|
+
const log_js_1 = require("../utils/log.js");
|
|
13
13
|
const debug = (0, debug_1.default)('@testomatio/reporter:pipe:debug');
|
|
14
14
|
class DebugPipe {
|
|
15
15
|
constructor(params, store) {
|
|
@@ -41,7 +41,7 @@ class DebugPipe {
|
|
|
41
41
|
catch (err) {
|
|
42
42
|
debug('Failed to create symlink:', err.message);
|
|
43
43
|
}
|
|
44
|
-
|
|
44
|
+
log_js_1.log.info('🪲 Debug file created');
|
|
45
45
|
this.testomatioEnvVars = Object.keys(process.env)
|
|
46
46
|
.filter(key => key.startsWith('TESTOMATIO_'))
|
|
47
47
|
.reduce((acc, key) => {
|
|
@@ -118,7 +118,7 @@ class DebugPipe {
|
|
|
118
118
|
if (this.batch.intervalFunction)
|
|
119
119
|
clearInterval(this.batch.intervalFunction);
|
|
120
120
|
this.logToFile({ action: 'finishRun', params });
|
|
121
|
-
|
|
121
|
+
log_js_1.log.info('🪲 Debug Saved to', this.logFilePath);
|
|
122
122
|
}
|
|
123
123
|
async sync() {
|
|
124
124
|
if (!this.isEnabled)
|
package/lib/pipe/github.js
CHANGED
|
@@ -44,6 +44,7 @@ const lodash_merge_1 = __importDefault(require("lodash.merge"));
|
|
|
44
44
|
const constants_js_1 = require("../constants.js");
|
|
45
45
|
const utils_js_1 = require("../utils/utils.js");
|
|
46
46
|
const pipe_utils_js_1 = require("../utils/pipe_utils.js");
|
|
47
|
+
const log_js_1 = require("../utils/log.js");
|
|
47
48
|
const debug = (0, debug_1.default)('@testomatio/reporter:pipe:github');
|
|
48
49
|
/**
|
|
49
50
|
* @typedef {import('../../types/types.js').Pipe} Pipe
|
|
@@ -197,10 +198,10 @@ class GitHubPipe {
|
|
|
197
198
|
const url = resp.data?.html_url;
|
|
198
199
|
debug('Comment URL:', url);
|
|
199
200
|
this.store.githubUrl = url;
|
|
200
|
-
|
|
201
|
+
log_js_1.log.info(picocolors_1.default.yellow('GitHub'), `Report created: ${picocolors_1.default.magenta(url)}`);
|
|
201
202
|
}
|
|
202
203
|
catch (err) {
|
|
203
|
-
|
|
204
|
+
log_js_1.log.info(picocolors_1.default.yellow('GitHub'), `Couldn't create GitHub report ${err}`);
|
|
204
205
|
}
|
|
205
206
|
}
|
|
206
207
|
async sync() {
|
package/lib/pipe/gitlab.js
CHANGED
|
@@ -12,6 +12,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
12
12
|
const constants_js_1 = require("../constants.js");
|
|
13
13
|
const utils_js_1 = require("../utils/utils.js");
|
|
14
14
|
const pipe_utils_js_1 = require("../utils/pipe_utils.js");
|
|
15
|
+
const log_js_1 = require("../utils/log.js");
|
|
15
16
|
const debug = (0, debug_1.default)('@testomatio/reporter:pipe:gitlab');
|
|
16
17
|
//! GITLAB_PAT environment variable is required for this functionality to work
|
|
17
18
|
//! and your pipeline trigger should be merge_request
|
|
@@ -147,7 +148,7 @@ class GitLabPipe {
|
|
|
147
148
|
const commentID = addCommentResponse.data.id;
|
|
148
149
|
// eslint-disable-next-line max-len
|
|
149
150
|
const commentURL = `${this.ENV.CI_PROJECT_URL}/-/merge_requests/${this.ENV.CI_MERGE_REQUEST_IID}#note_${commentID}`;
|
|
150
|
-
|
|
151
|
+
log_js_1.log.info(picocolors_1.default.yellow('GitLab'), `Report created: ${picocolors_1.default.magenta(commentURL)}`);
|
|
151
152
|
}
|
|
152
153
|
catch (err) {
|
|
153
154
|
console.error(constants_js_1.APP_PREFIX, picocolors_1.default.yellow('GitLab'), `Couldn't create GitLab report\n${err}.
|
package/lib/pipe/index.js
CHANGED
|
@@ -40,7 +40,6 @@ exports.pipesFactory = pipesFactory;
|
|
|
40
40
|
const fs_1 = __importDefault(require("fs"));
|
|
41
41
|
const path_1 = __importDefault(require("path"));
|
|
42
42
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
43
|
-
const constants_js_1 = require("../constants.js");
|
|
44
43
|
const testomatio_js_1 = __importDefault(require("./testomatio.js"));
|
|
45
44
|
const github_js_1 = __importDefault(require("./github.js"));
|
|
46
45
|
const gitlab_js_1 = __importDefault(require("./gitlab.js"));
|
|
@@ -49,6 +48,7 @@ const html_js_1 = __importDefault(require("./html.js"));
|
|
|
49
48
|
const coverage_js_1 = __importDefault(require("./coverage.js"));
|
|
50
49
|
const bitbucket_js_1 = require("./bitbucket.js");
|
|
51
50
|
const debug_js_1 = require("./debug.js");
|
|
51
|
+
const log_js_1 = require("../utils/log.js");
|
|
52
52
|
async function pipesFactory(params, opts) {
|
|
53
53
|
const extraPipes = [];
|
|
54
54
|
// Add extra pipes into package.json file:
|
|
@@ -65,14 +65,14 @@ async function pipesFactory(params, opts) {
|
|
|
65
65
|
PipeClass = await Promise.resolve(`${pipeDef}`).then(s => __importStar(require(s)));
|
|
66
66
|
}
|
|
67
67
|
catch (err) {
|
|
68
|
-
|
|
68
|
+
log_js_1.log.info(`Can't load module Testomatio pipe module from ${pipeDef}`);
|
|
69
69
|
continue;
|
|
70
70
|
}
|
|
71
71
|
try {
|
|
72
72
|
extraPipes.push(new PipeClass(params, opts));
|
|
73
73
|
}
|
|
74
74
|
catch (err) {
|
|
75
|
-
|
|
75
|
+
log_js_1.log.info(`Can't instantiate Testomatio for ${pipeDef}`, err);
|
|
76
76
|
continue;
|
|
77
77
|
}
|
|
78
78
|
}
|
|
@@ -89,9 +89,9 @@ async function pipesFactory(params, opts) {
|
|
|
89
89
|
...extraPipes,
|
|
90
90
|
];
|
|
91
91
|
const pipesEnabled = pipes.filter(p => p.isEnabled);
|
|
92
|
-
|
|
92
|
+
log_js_1.log.info(picocolors_1.default.cyan('Pipes:'), picocolors_1.default.cyan(pipesEnabled.map(p => p.toString()).join(', ') || 'No pipes enabled'));
|
|
93
93
|
if (!pipesEnabled.length) {
|
|
94
|
-
|
|
94
|
+
log_js_1.log.info(picocolors_1.default.dim('If you want to use Testomatio reporter, pass your token as TESTOMATIO env variable'));
|
|
95
95
|
}
|
|
96
96
|
return pipes;
|
|
97
97
|
}
|
package/lib/pipe/testomatio.js
CHANGED
|
@@ -11,6 +11,7 @@ const constants_js_1 = require("../constants.js");
|
|
|
11
11
|
const utils_js_1 = require("../utils/utils.js");
|
|
12
12
|
const pipe_utils_js_1 = require("../utils/pipe_utils.js");
|
|
13
13
|
const config_js_1 = require("../config.js");
|
|
14
|
+
const log_js_1 = require("../utils/log.js");
|
|
14
15
|
const debug = (0, debug_1.default)('@testomatio/reporter:pipe:testomatio');
|
|
15
16
|
if (process.env.TESTOMATIO_RUN)
|
|
16
17
|
process.env.runId = process.env.TESTOMATIO_RUN;
|
|
@@ -57,11 +58,10 @@ class TestomatioPipe {
|
|
|
57
58
|
const sha = (0, utils_js_1.getGitCommitSha)();
|
|
58
59
|
if (sha) {
|
|
59
60
|
this.title = `Shared Run - ${sha}`;
|
|
60
|
-
|
|
61
|
+
log_js_1.log.info(`🔄 Auto-generated title for shared run: ${this.title}`);
|
|
61
62
|
}
|
|
62
63
|
else {
|
|
63
|
-
|
|
64
|
-
console.log(constants_js_1.APP_PREFIX, 'Please run the tests inside a Git repository or set TESTOMATIO_TITLE explicitly.');
|
|
64
|
+
log_js_1.log.warn(picocolors_1.default.red('Failed to resolve git commit SHA for shared run title.'), 'Please run the tests inside a Git repository or set TESTOMATIO_TITLE explicitly.');
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
this.groupTitle = params.groupTitle || process.env.TESTOMATIO_RUNGROUP_TITLE;
|
|
@@ -97,7 +97,7 @@ class TestomatioPipe {
|
|
|
97
97
|
this.requestFailures = 0;
|
|
98
98
|
if (!(0, utils_js_1.isValidUrl)(this.url.trim())) {
|
|
99
99
|
this.isEnabled = false;
|
|
100
|
-
|
|
100
|
+
log_js_1.log.error(picocolors_1.default.red(`Error creating report on Testomat.io, report url '${this.url}' is invalid`));
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
/**
|
|
@@ -107,7 +107,8 @@ class TestomatioPipe {
|
|
|
107
107
|
*/
|
|
108
108
|
#formatData(data) {
|
|
109
109
|
data.api_key = this.apiKey;
|
|
110
|
-
data.create
|
|
110
|
+
if (data.create === undefined)
|
|
111
|
+
data.create = this.createNewTests;
|
|
111
112
|
// add test ID + run ID
|
|
112
113
|
if (data.rid)
|
|
113
114
|
data.rid = `${this.runId}-${data.rid}`;
|
|
@@ -155,10 +156,10 @@ class TestomatioPipe {
|
|
|
155
156
|
(0, utils_js_1.foundedTestLog)(constants_js_1.APP_PREFIX, resp.data.tests);
|
|
156
157
|
return resp.data.tests;
|
|
157
158
|
}
|
|
158
|
-
|
|
159
|
+
log_js_1.log.warn(`⛔ No tests found for your --filter --> ${type}=${id}`);
|
|
159
160
|
}
|
|
160
161
|
catch (err) {
|
|
161
|
-
|
|
162
|
+
log_js_1.log.error(`🚩 Error getting Testomat.io test grepList: ${err}`);
|
|
162
163
|
}
|
|
163
164
|
}
|
|
164
165
|
/**
|
|
@@ -234,8 +235,8 @@ class TestomatioPipe {
|
|
|
234
235
|
this.runPublicUrl = resp.data.public_url;
|
|
235
236
|
this.store.runUrl = this.runUrl;
|
|
236
237
|
this.store.runPublicUrl = this.runPublicUrl;
|
|
237
|
-
|
|
238
|
-
|
|
238
|
+
log_js_1.log.info('📊 Using existing run. Report ID:', this.runId);
|
|
239
|
+
log_js_1.log.info('📊 Report URL:', picocolors_1.default.magenta(this.runUrl));
|
|
239
240
|
}
|
|
240
241
|
return;
|
|
241
242
|
}
|
|
@@ -256,7 +257,7 @@ class TestomatioPipe {
|
|
|
256
257
|
this.store.runUrl = this.runUrl;
|
|
257
258
|
this.store.runPublicUrl = this.runPublicUrl;
|
|
258
259
|
this.store.runId = this.runId;
|
|
259
|
-
|
|
260
|
+
log_js_1.log.info('📊 Report created. Report ID:', this.runId);
|
|
260
261
|
process.env.runId = this.runId;
|
|
261
262
|
debug('Run created', this.runId);
|
|
262
263
|
}
|
|
@@ -323,7 +324,7 @@ class TestomatioPipe {
|
|
|
323
324
|
printCreateIssue();
|
|
324
325
|
}
|
|
325
326
|
else {
|
|
326
|
-
|
|
327
|
+
log_js_1.log.info(picocolors_1.default.blue(data?.title || ''), "Report couldn't be processed", err);
|
|
327
328
|
}
|
|
328
329
|
});
|
|
329
330
|
};
|
|
@@ -376,7 +377,7 @@ class TestomatioPipe {
|
|
|
376
377
|
printCreateIssue();
|
|
377
378
|
}
|
|
378
379
|
else {
|
|
379
|
-
|
|
380
|
+
log_js_1.log.info("Report couldn't be processed", err);
|
|
380
381
|
}
|
|
381
382
|
});
|
|
382
383
|
};
|
|
@@ -389,7 +390,7 @@ class TestomatioPipe {
|
|
|
389
390
|
return;
|
|
390
391
|
this.runId = this.runId || process.env.runId || this.store.runId || (0, utils_js_1.readLatestRunId)();
|
|
391
392
|
if (!this.runId) {
|
|
392
|
-
|
|
393
|
+
log_js_1.log.warn(picocolors_1.default.red('Run ID is not set, skipping test reporting'));
|
|
393
394
|
return;
|
|
394
395
|
}
|
|
395
396
|
this.#formatData(data);
|
|
@@ -456,31 +457,27 @@ class TestomatioPipe {
|
|
|
456
457
|
},
|
|
457
458
|
});
|
|
458
459
|
if (this.runUrl) {
|
|
459
|
-
|
|
460
|
+
log_js_1.log.warn('📊 Report URL:', picocolors_1.default.magenta(this.runUrl));
|
|
460
461
|
}
|
|
461
462
|
if (this.runPublicUrl) {
|
|
462
|
-
|
|
463
|
+
log_js_1.log.info('🌟 Public URL:', picocolors_1.default.magenta(this.runPublicUrl));
|
|
463
464
|
}
|
|
464
465
|
}
|
|
465
466
|
if (this.runUrl && this.proceed) {
|
|
466
467
|
const notFinishedMessage = picocolors_1.default.yellow(picocolors_1.default.bold('Run was not finished because of $TESTOMATIO_PROCEED'));
|
|
467
|
-
|
|
468
|
-
|
|
468
|
+
log_js_1.log.warn(`📊 ${notFinishedMessage}. Report URL: ${picocolors_1.default.magenta(this.runUrl)}`);
|
|
469
|
+
log_js_1.log.warn(`🛬 Run to finish it: TESTOMATIO_RUN=${this.runId} npx @testomatio/reporter finish`);
|
|
469
470
|
}
|
|
470
471
|
if (this.hasUnmatchedTests) {
|
|
471
472
|
console.log('');
|
|
472
|
-
|
|
473
|
+
log_js_1.log.warn(picocolors_1.default.yellow(picocolors_1.default.bold('⚠️ Some reported tests were not found in Testomat.io project')));
|
|
473
474
|
console.log(constants_js_1.APP_PREFIX, `If you use Testomat.io as a reporter only, please re-run tests using ${picocolors_1.default.bold('TESTOMATIO_CREATE=1')}`);
|
|
474
475
|
console.log(constants_js_1.APP_PREFIX, `But to keep your tests consistent it is recommended to ${picocolors_1.default.bold('import tests first')}`);
|
|
475
|
-
|
|
476
|
-
console.log(constants_js_1.APP_PREFIX, 'You can do that automatically via command line tools:');
|
|
477
|
-
console.log(constants_js_1.APP_PREFIX, picocolors_1.default.bold('npx check-tests ... --update-ids'), 'See: https://bit.ly/js-update-ids');
|
|
478
|
-
console.log(constants_js_1.APP_PREFIX, 'or for Cucumber:');
|
|
479
|
-
console.log(constants_js_1.APP_PREFIX, picocolors_1.default.bold('npx check-cucumber ... --update-ids'), 'See: https://bit.ly/bdd-update-ids');
|
|
476
|
+
log_js_1.log.info('If tests were imported but still not matched, assign test IDs to your tests.', 'You can do that automatically via command line tools:', picocolors_1.default.bold('npx check-tests ... --update-ids'), 'See: https://bit.ly/js-update-ids', 'or for Cucumber:', picocolors_1.default.bold('npx check-cucumber ... --update-ids'), 'See: https://bit.ly/bdd-update-ids');
|
|
480
477
|
}
|
|
481
478
|
}
|
|
482
479
|
catch (err) {
|
|
483
|
-
|
|
480
|
+
log_js_1.log.info('Error updating status, skipping...', err);
|
|
484
481
|
if (process.env.DEBUG || process.env.TESTOMATIO_DEBUG)
|
|
485
482
|
this.#logFailedResponse(err);
|
|
486
483
|
printCreateIssue();
|
package/lib/uploader.js
CHANGED
|
@@ -14,6 +14,7 @@ const promise_retry_1 = __importDefault(require("promise-retry"));
|
|
|
14
14
|
const picocolors_1 = __importDefault(require("picocolors"));
|
|
15
15
|
const constants_js_1 = require("./constants.js");
|
|
16
16
|
const filesize_1 = require("filesize");
|
|
17
|
+
const log_js_1 = require("./utils/log.js");
|
|
17
18
|
const debug = (0, debug_1.default)('@testomatio/reporter:file-uploader');
|
|
18
19
|
class S3Uploader {
|
|
19
20
|
constructor() {
|
|
@@ -119,7 +120,7 @@ class S3Uploader {
|
|
|
119
120
|
catch (e) {
|
|
120
121
|
this.failedUploads.push({ path: file.path, size: file.size });
|
|
121
122
|
debug('S3 uploading error:', e);
|
|
122
|
-
|
|
123
|
+
log_js_1.log.info('Upload failed:', e.message, '\nConfig:\n', this.getMaskedConfig());
|
|
123
124
|
}
|
|
124
125
|
}
|
|
125
126
|
/**
|
|
@@ -142,7 +143,7 @@ class S3Uploader {
|
|
|
142
143
|
const diffHours = diff / 1000 / 60 / 60;
|
|
143
144
|
debug('Diff hours:', diffHours);
|
|
144
145
|
if (diffHours > 3) {
|
|
145
|
-
|
|
146
|
+
log_js_1.log.info("Artifacts file is too old, can't process artifacts. Please re-run the tests.");
|
|
146
147
|
return [];
|
|
147
148
|
}
|
|
148
149
|
const data = fs_1.default.readFileSync(tempFilePath, 'utf8');
|