@testomatio/reporter 2.1.0-beta-nightwatch → 2.1.0-beta.2-codeceptjs
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 +1 -0
- package/lib/adapter/codecept.js +288 -202
- package/lib/adapter/cypress-plugin/index.js +0 -2
- package/lib/adapter/mocha.js +0 -1
- package/lib/adapter/nightwatch.js +5 -5
- package/lib/adapter/playwright.js +11 -3
- package/lib/adapter/webdriver.d.ts +1 -1
- package/lib/adapter/webdriver.js +18 -8
- package/lib/bin/cli.js +73 -8
- package/lib/bin/reportXml.js +4 -2
- package/lib/bin/startTest.js +3 -2
- package/lib/bin/uploadArtifacts.js +5 -4
- package/lib/client.js +31 -10
- package/lib/data-storage.d.ts +5 -5
- package/lib/data-storage.js +23 -13
- package/lib/junit-adapter/csharp.d.ts +1 -0
- package/lib/junit-adapter/csharp.js +11 -1
- package/lib/pipe/bitbucket.d.ts +2 -0
- package/lib/pipe/bitbucket.js +38 -26
- package/lib/pipe/debug.js +27 -6
- package/lib/pipe/github.d.ts +2 -2
- package/lib/pipe/github.js +35 -3
- package/lib/pipe/gitlab.d.ts +2 -0
- package/lib/pipe/gitlab.js +27 -9
- package/lib/pipe/html.js +0 -3
- package/lib/pipe/index.js +17 -7
- package/lib/pipe/testomatio.d.ts +3 -2
- package/lib/pipe/testomatio.js +85 -75
- package/lib/replay.d.ts +31 -0
- package/lib/replay.js +259 -0
- package/lib/reporter-functions.d.ts +7 -0
- package/lib/reporter-functions.js +36 -0
- package/lib/reporter.d.ts +15 -12
- package/lib/reporter.js +4 -1
- package/lib/services/artifacts.d.ts +1 -1
- package/lib/services/index.d.ts +2 -0
- package/lib/services/index.js +2 -0
- package/lib/services/key-values.d.ts +1 -1
- package/lib/services/labels.d.ts +22 -0
- package/lib/services/labels.js +62 -0
- package/lib/services/logger.d.ts +1 -1
- package/lib/services/logger.js +1 -2
- package/lib/template/testomatio.hbs +443 -68
- package/lib/uploader.js +10 -6
- package/lib/utils/constants.d.ts +12 -0
- package/lib/utils/constants.js +15 -0
- package/lib/utils/utils.d.ts +10 -1
- package/lib/utils/utils.js +70 -22
- package/lib/xmlReader.js +57 -19
- package/package.json +16 -11
- package/src/adapter/codecept.js +320 -214
- package/src/adapter/cypress-plugin/index.js +0 -2
- package/src/adapter/mocha.js +0 -1
- package/src/adapter/nightwatch.js +1 -1
- package/src/adapter/playwright.js +10 -7
- package/src/adapter/webdriver.js +13 -5
- package/src/bin/cli.js +78 -7
- package/src/bin/reportXml.js +4 -1
- package/src/bin/startTest.js +2 -1
- package/src/bin/uploadArtifacts.js +2 -1
- package/src/client.js +28 -5
- package/src/data-storage.js +6 -6
- package/src/junit-adapter/csharp.js +13 -1
- package/src/pipe/bitbucket.js +22 -24
- package/src/pipe/debug.js +26 -5
- package/src/pipe/github.js +1 -2
- package/src/pipe/gitlab.js +27 -9
- package/src/pipe/html.js +1 -4
- package/src/pipe/testomatio.js +112 -107
- package/src/replay.js +268 -0
- package/src/reporter-functions.js +41 -0
- package/src/reporter.js +3 -0
- package/src/services/index.js +2 -0
- package/src/services/labels.js +59 -0
- package/src/services/logger.js +1 -2
- package/src/template/testomatio.hbs +443 -68
- package/src/uploader.js +11 -6
- package/src/utils/constants.js +12 -0
- package/src/utils/utils.js +67 -15
- package/src/xmlReader.js +73 -18
package/lib/utils/utils.js
CHANGED
|
@@ -15,18 +15,29 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
15
15
|
}) : function(o, v) {
|
|
16
16
|
o["default"] = v;
|
|
17
17
|
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
25
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
37
|
};
|
|
28
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.testRunnerHelper = exports.specificTestInfo = exports.parseSuite = exports.isValidUrl = exports.humanize = exports.getTestomatIdFromTestTitle = exports.getCurrentDateTime = exports.foundedTestLog = exports.fileSystem = exports.fetchFilesFromStackTrace = exports.fetchIdFromOutput = exports.fetchIdFromCode = exports.fetchSourceCodeFromStackTrace = exports.fetchSourceCode = exports.isSameTest = exports.ansiRegExp = void 0;
|
|
39
|
+
exports.validateSuiteId = exports.testRunnerHelper = exports.specificTestInfo = exports.parseSuite = exports.isValidUrl = exports.humanize = exports.getTestomatIdFromTestTitle = exports.getCurrentDateTime = exports.foundedTestLog = exports.fileSystem = exports.fetchFilesFromStackTrace = exports.fetchIdFromOutput = exports.fetchIdFromCode = exports.fetchSourceCodeFromStackTrace = exports.fetchSourceCode = exports.isSameTest = exports.ansiRegExp = exports.SUITE_ID_REGEX = exports.TEST_ID_REGEX = void 0;
|
|
40
|
+
exports.getPackageVersion = getPackageVersion;
|
|
30
41
|
exports.formatStep = formatStep;
|
|
31
42
|
exports.readLatestRunId = readLatestRunId;
|
|
32
43
|
exports.removeColorCodes = removeColorCodes;
|
|
@@ -38,7 +49,12 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
38
49
|
const is_valid_path_1 = __importDefault(require("is-valid-path"));
|
|
39
50
|
const debug_1 = __importDefault(require("debug"));
|
|
40
51
|
const os_1 = __importDefault(require("os"));
|
|
52
|
+
const url_2 = require("url");
|
|
41
53
|
const debug = (0, debug_1.default)('@testomatio/reporter:util');
|
|
54
|
+
// Use __dirname directly since we're compiling to CommonJS
|
|
55
|
+
// prettier-ignore
|
|
56
|
+
// @ts-ignore
|
|
57
|
+
// eslint-disable-next-line max-len
|
|
42
58
|
/**
|
|
43
59
|
* @param {String} testTitle - Test title
|
|
44
60
|
*
|
|
@@ -62,11 +78,23 @@ exports.getTestomatIdFromTestTitle = getTestomatIdFromTestTitle;
|
|
|
62
78
|
const parseSuite = suiteTitle => {
|
|
63
79
|
const captures = suiteTitle.match(/@S[\w\d]{8}/);
|
|
64
80
|
if (captures) {
|
|
65
|
-
return captures[
|
|
81
|
+
return captures[0];
|
|
66
82
|
}
|
|
67
83
|
return null;
|
|
68
84
|
};
|
|
69
85
|
exports.parseSuite = parseSuite;
|
|
86
|
+
/**
|
|
87
|
+
* Validates TESTOMATIO_SUITE environment variable format
|
|
88
|
+
* @param {String} suiteId - suite ID to validate
|
|
89
|
+
* @returns {String|null} validated suite ID or null if invalid
|
|
90
|
+
*/
|
|
91
|
+
const validateSuiteId = suiteId => {
|
|
92
|
+
if (!suiteId)
|
|
93
|
+
return null;
|
|
94
|
+
const match = suiteId.match(exports.SUITE_ID_REGEX);
|
|
95
|
+
return match ? match[0] : null;
|
|
96
|
+
};
|
|
97
|
+
exports.validateSuiteId = validateSuiteId;
|
|
70
98
|
const ansiRegExp = () => {
|
|
71
99
|
const pattern = [
|
|
72
100
|
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
|
|
@@ -77,7 +105,6 @@ const ansiRegExp = () => {
|
|
|
77
105
|
exports.ansiRegExp = ansiRegExp;
|
|
78
106
|
const isValidUrl = s => {
|
|
79
107
|
try {
|
|
80
|
-
// eslint-disable-next-line no-new
|
|
81
108
|
new url_1.URL(s);
|
|
82
109
|
return true;
|
|
83
110
|
}
|
|
@@ -86,13 +113,23 @@ const isValidUrl = s => {
|
|
|
86
113
|
}
|
|
87
114
|
};
|
|
88
115
|
exports.isValidUrl = isValidUrl;
|
|
89
|
-
const fileMatchRegex = /file:(
|
|
90
|
-
const fetchFilesFromStackTrace = (stack = '') => {
|
|
116
|
+
const fileMatchRegex = /file:(\/+(?:[A-Za-z]:[\\/]|\/)?[^\s]*?\.(png|avi|webm|jpg|html|txt))/gi;
|
|
117
|
+
const fetchFilesFromStackTrace = (stack = '', checkExists = true) => {
|
|
91
118
|
const files = Array.from(stack.matchAll(fileMatchRegex))
|
|
92
119
|
.map(f => f[1].trim())
|
|
93
|
-
.map(f =>
|
|
120
|
+
.map(f => f.replace(/^\/+/, '/').replace(/^\/([A-Za-z]:)/, '$1')) // Remove extra slashes, handle Windows paths
|
|
121
|
+
.map(f => {
|
|
122
|
+
// Convert Windows paths to Linux paths for testing purposes
|
|
123
|
+
if (f.match(/^[A-Za-z]:[\\\/]/)) {
|
|
124
|
+
// Convert Windows path to Linux equivalent for test scenarios
|
|
125
|
+
return f.replace(/^[A-Za-z]:[\\\/]/, '/').replace(/\\/g, '/');
|
|
126
|
+
}
|
|
127
|
+
return f;
|
|
128
|
+
});
|
|
94
129
|
debug('Found files in stack trace: ', files);
|
|
95
130
|
return files.filter(f => {
|
|
131
|
+
if (!checkExists)
|
|
132
|
+
return true;
|
|
96
133
|
const isFile = fs_1.default.existsSync(f);
|
|
97
134
|
if (!isFile)
|
|
98
135
|
debug('File %s could not be found and uploaded as artifact', f);
|
|
@@ -131,7 +168,8 @@ const fetchSourceCodeFromStackTrace = (stack = '') => {
|
|
|
131
168
|
.join('\n');
|
|
132
169
|
};
|
|
133
170
|
exports.fetchSourceCodeFromStackTrace = fetchSourceCodeFromStackTrace;
|
|
134
|
-
|
|
171
|
+
exports.TEST_ID_REGEX = /@T([\w\d]{8})/;
|
|
172
|
+
exports.SUITE_ID_REGEX = /@S([\w\d]{8})/;
|
|
135
173
|
const fetchIdFromCode = (code, opts = {}) => {
|
|
136
174
|
const comments = code
|
|
137
175
|
.split('\n')
|
|
@@ -145,15 +183,12 @@ const fetchIdFromCode = (code, opts = {}) => {
|
|
|
145
183
|
return l.startsWith('// ');
|
|
146
184
|
}
|
|
147
185
|
});
|
|
148
|
-
return comments.find(c => c.match(TEST_ID_REGEX))?.match(TEST_ID_REGEX)?.[1];
|
|
186
|
+
return comments.find(c => c.match(exports.TEST_ID_REGEX))?.match(exports.TEST_ID_REGEX)?.[1];
|
|
149
187
|
};
|
|
150
188
|
exports.fetchIdFromCode = fetchIdFromCode;
|
|
151
189
|
const fetchIdFromOutput = output => {
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
.map(l => l.trim())
|
|
155
|
-
.filter(l => l.startsWith('tid://'));
|
|
156
|
-
return lines.find(c => c.match(TEST_ID_REGEX))?.match(TEST_ID_REGEX)?.[1];
|
|
190
|
+
const TID_FULL_PATTERN = new RegExp(`tid:\\/\\/.*?(${exports.TEST_ID_REGEX.source})`);
|
|
191
|
+
return output.match(TID_FULL_PATTERN)?.[2];
|
|
157
192
|
};
|
|
158
193
|
exports.fetchIdFromOutput = fetchIdFromOutput;
|
|
159
194
|
const fetchSourceCode = (contents, opts = {}) => {
|
|
@@ -177,6 +212,12 @@ const fetchSourceCode = (contents, opts = {}) => {
|
|
|
177
212
|
if (lineIndex === -1)
|
|
178
213
|
lineIndex = lines.findIndex(l => l.includes(`${title}(`));
|
|
179
214
|
}
|
|
215
|
+
else if (opts.lang === 'csharp') {
|
|
216
|
+
if (lineIndex === -1)
|
|
217
|
+
lineIndex = lines.findIndex(l => l.includes(`public void ${title}`));
|
|
218
|
+
if (lineIndex === -1)
|
|
219
|
+
lineIndex = lines.findIndex(l => l.includes(`${title}(`));
|
|
220
|
+
}
|
|
180
221
|
else {
|
|
181
222
|
lineIndex = lines.findIndex(l => l.includes(title));
|
|
182
223
|
}
|
|
@@ -324,7 +365,6 @@ const decamelize = text => {
|
|
|
324
365
|
* @returns
|
|
325
366
|
*/
|
|
326
367
|
function removeColorCodes(input) {
|
|
327
|
-
// eslint-disable-next-line no-control-regex
|
|
328
368
|
return input.replace(/\x1b\[[0-9;]*m/g, '');
|
|
329
369
|
}
|
|
330
370
|
const testRunnerHelper = {
|
|
@@ -337,7 +377,6 @@ const testRunnerHelper = {
|
|
|
337
377
|
try {
|
|
338
378
|
// TODO: expect?.getState()?.testPath + ' ' + expect?.getState()?.currentTestName
|
|
339
379
|
// @ts-expect-error "expect" could only be defined inside Jest environement (forbidden to import it outside)
|
|
340
|
-
// eslint-disable-next-line no-undef
|
|
341
380
|
return expect?.getState()?.currentTestName;
|
|
342
381
|
}
|
|
343
382
|
catch (e) {
|
|
@@ -380,6 +419,13 @@ function formatStep(step, shift = 0) {
|
|
|
380
419
|
}
|
|
381
420
|
return lines;
|
|
382
421
|
}
|
|
422
|
+
function getPackageVersion() {
|
|
423
|
+
const packageJsonPath = path_1.default.resolve(__dirname, '../../package.json');
|
|
424
|
+
const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf8'));
|
|
425
|
+
return packageJson.version;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
module.exports.getPackageVersion = getPackageVersion;
|
|
383
429
|
|
|
384
430
|
module.exports.formatStep = formatStep;
|
|
385
431
|
|
|
@@ -393,6 +439,8 @@ module.exports.getTestomatIdFromTestTitle = getTestomatIdFromTestTitle;
|
|
|
393
439
|
|
|
394
440
|
module.exports.parseSuite = parseSuite;
|
|
395
441
|
|
|
442
|
+
module.exports.validateSuiteId = validateSuiteId;
|
|
443
|
+
|
|
396
444
|
module.exports.ansiRegExp = ansiRegExp;
|
|
397
445
|
|
|
398
446
|
module.exports.isValidUrl = isValidUrl;
|
package/lib/xmlReader.js
CHANGED
|
@@ -20,7 +20,7 @@ const uploader_js_1 = require("./uploader.js");
|
|
|
20
20
|
const debug = (0, debug_1.default)('@testomatio/reporter:xml');
|
|
21
21
|
const ridRunId = (0, crypto_1.randomUUID)();
|
|
22
22
|
const TESTOMATIO_URL = process.env.TESTOMATIO_URL || 'https://app.testomat.io';
|
|
23
|
-
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN, TESTOMATIO_MARK_DETACHED } = process.env;
|
|
23
|
+
const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_SUITE, TESTOMATIO_MAX_STACK_TRACE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN, TESTOMATIO_MARK_DETACHED, } = process.env;
|
|
24
24
|
const options = {
|
|
25
25
|
ignoreDeclaration: true,
|
|
26
26
|
ignoreAttributes: false,
|
|
@@ -28,6 +28,7 @@ const options = {
|
|
|
28
28
|
attributeNamePrefix: '',
|
|
29
29
|
parseTagValue: true,
|
|
30
30
|
};
|
|
31
|
+
const MAX_OUTPUT_LENGTH = parseInt(TESTOMATIO_MAX_STACK_TRACE, 10) || 10000;
|
|
31
32
|
const reduceOptions = {};
|
|
32
33
|
class XmlReader {
|
|
33
34
|
constructor(opts = {}) {
|
|
@@ -75,7 +76,7 @@ class XmlReader {
|
|
|
75
76
|
/(<system-out><!\[CDATA\[)([\s\S]*?)(\]\]><\/system-out>)/g,
|
|
76
77
|
];
|
|
77
78
|
for (const regex of cutRegexes) {
|
|
78
|
-
xmlData = xmlData.replace(regex, (_, p1, p2, p3) => `${p1}${p2.substring(0,
|
|
79
|
+
xmlData = xmlData.replace(regex, (_, p1, p2, p3) => `${p1}${p2.substring(0, MAX_OUTPUT_LENGTH)}${p3}`);
|
|
79
80
|
}
|
|
80
81
|
const jsonResult = this.parser.parse(xmlData);
|
|
81
82
|
let jsonSuite;
|
|
@@ -185,6 +186,7 @@ class XmlReader {
|
|
|
185
186
|
if (test.file)
|
|
186
187
|
r.file = test.file;
|
|
187
188
|
r.create = true;
|
|
189
|
+
r.overwrite = true;
|
|
188
190
|
if (r.status === 'Passed')
|
|
189
191
|
r.status = constants_js_1.STATUS.PASSED;
|
|
190
192
|
if (r.status === 'Failed')
|
|
@@ -202,7 +204,7 @@ class XmlReader {
|
|
|
202
204
|
this.tests = results.filter(t => !!t.title);
|
|
203
205
|
return {
|
|
204
206
|
status,
|
|
205
|
-
create_tests:
|
|
207
|
+
create_tests: !process.env.IGNORE_NEW_TESTS,
|
|
206
208
|
tests_count: parseInt(counters.total, 10),
|
|
207
209
|
passed_count: parseInt(counters.passed, 10),
|
|
208
210
|
skipped_count: parseInt(counters.notExecuted, 10),
|
|
@@ -253,6 +255,7 @@ class XmlReader {
|
|
|
253
255
|
title,
|
|
254
256
|
suite_title,
|
|
255
257
|
run_time,
|
|
258
|
+
retry: false,
|
|
256
259
|
});
|
|
257
260
|
});
|
|
258
261
|
});
|
|
@@ -313,6 +316,8 @@ class XmlReader {
|
|
|
313
316
|
this.stats.language = 'js';
|
|
314
317
|
if (file.endsWith('.ts'))
|
|
315
318
|
this.stats.language = 'ts';
|
|
319
|
+
if (file.endsWith('.cs'))
|
|
320
|
+
this.stats.language = 'csharp';
|
|
316
321
|
}
|
|
317
322
|
if (!fs_1.default.existsSync(file)) {
|
|
318
323
|
debug('Failed to open file with the source code', file);
|
|
@@ -359,13 +364,13 @@ class XmlReader {
|
|
|
359
364
|
async uploadArtifacts() {
|
|
360
365
|
for (const test of this.tests.filter(t => !!t.stack)) {
|
|
361
366
|
let files = [];
|
|
362
|
-
if (test.files?.length)
|
|
363
|
-
|
|
364
|
-
files =
|
|
367
|
+
if (!test.files?.length)
|
|
368
|
+
continue;
|
|
369
|
+
files = test.files.map(f => (path_1.default.isAbsolute(f) ? f : path_1.default.join(process.cwd(), f)));
|
|
365
370
|
if (!files.length)
|
|
366
371
|
continue;
|
|
367
372
|
const runId = this.runId || this.store.runId || Date.now().toString();
|
|
368
|
-
test.artifacts = await Promise.all(files.map(f => this.uploader.uploadFileByPath(f, [runId])));
|
|
373
|
+
test.artifacts = await Promise.all(files.map(f => this.uploader.uploadFileByPath(f, [runId, path_1.default.basename(f)])));
|
|
369
374
|
console.log(constants_js_1.APP_PREFIX, `🗄️ Uploaded ${picocolors_1.default.bold(`${files.length} artifacts`)} for test ${test.title}`);
|
|
370
375
|
}
|
|
371
376
|
}
|
|
@@ -415,14 +420,17 @@ function reduceTestCases(prev, item) {
|
|
|
415
420
|
testCases = [testCases];
|
|
416
421
|
}
|
|
417
422
|
// suite inside test case
|
|
418
|
-
|
|
419
|
-
|
|
423
|
+
const testCase = item['test-suite']?.['test-case'];
|
|
424
|
+
if (testCase) {
|
|
425
|
+
const nestedCases = Array.isArray(testCase) ? testCase : [testCase];
|
|
426
|
+
testCases.push(...nestedCases);
|
|
427
|
+
}
|
|
420
428
|
const suiteOutput = item['system-out'] || item.output || item.log || '';
|
|
421
429
|
const suiteErr = item['system-err'] || item.output || item.log || '';
|
|
422
430
|
testCases
|
|
423
431
|
.filter(t => !!t)
|
|
424
432
|
.forEach(testCaseItem => {
|
|
425
|
-
const file = testCaseItem.file || item.filepath || '';
|
|
433
|
+
const file = testCaseItem.file || item.filepath || item.fullname || item.package || '';
|
|
426
434
|
let stack = '';
|
|
427
435
|
let message = '';
|
|
428
436
|
if (testCaseItem.error)
|
|
@@ -444,7 +452,7 @@ function reduceTestCases(prev, item) {
|
|
|
444
452
|
const isParametrized = item.type === 'ParameterizedMethod';
|
|
445
453
|
const preferClassname = reduceOptions.preferClassname || isParametrized;
|
|
446
454
|
// SpecFlow config
|
|
447
|
-
let { title, tags } = fetchProperties(isParametrized ? item : testCaseItem);
|
|
455
|
+
let { title, tags, testId } = fetchProperties(isParametrized ? item : testCaseItem);
|
|
448
456
|
let example = null;
|
|
449
457
|
const suiteTitle = preferClassname ? testCaseItem.classname : item.name || testCaseItem.classname;
|
|
450
458
|
title ||= testCaseItem.name || testCaseItem.methodname || testCaseItem.classname;
|
|
@@ -454,17 +462,38 @@ function reduceTestCases(prev, item) {
|
|
|
454
462
|
example = { ...exampleMatches[1].split(',').map(v => v.trim().replace(/[^\w\s-]/g, '')) };
|
|
455
463
|
title = title.replace(/\(.*?\)/, '').trim();
|
|
456
464
|
}
|
|
457
|
-
// eslint-disable-next-line
|
|
458
465
|
stack = `${testCaseItem['system-out'] || testCaseItem.output || testCaseItem.log || ''}\n\n${stack}\n\n${suiteOutput}\n\n${suiteErr}`.trim();
|
|
459
|
-
|
|
466
|
+
if (!testId)
|
|
467
|
+
testId = (0, utils_js_1.fetchIdFromOutput)(stack);
|
|
468
|
+
if (tags?.length && !testId) {
|
|
469
|
+
testId = tags
|
|
470
|
+
.filter(t => t.startsWith('T'))
|
|
471
|
+
.map(t => `@${t}`)
|
|
472
|
+
.find(t => t.match(utils_js_1.TEST_ID_REGEX))
|
|
473
|
+
?.slice(2);
|
|
474
|
+
}
|
|
460
475
|
let status = constants_js_1.STATUS.PASSED.toString();
|
|
461
476
|
if ('failure' in testCaseItem || 'error' in testCaseItem)
|
|
462
477
|
status = constants_js_1.STATUS.FAILED;
|
|
463
478
|
if ('skipped' in testCaseItem)
|
|
464
479
|
status = constants_js_1.STATUS.SKIPPED;
|
|
480
|
+
if (testCaseItem.result && Object.values(constants_js_1.STATUS).includes(testCaseItem.result.toLowerCase())) {
|
|
481
|
+
status = testCaseItem.result.toLowerCase();
|
|
482
|
+
}
|
|
465
483
|
let rid = null;
|
|
466
484
|
if (testCaseItem.id)
|
|
467
485
|
rid = `${ridRunId}-${testCaseItem.id}`;
|
|
486
|
+
// Extract attachments
|
|
487
|
+
let files = [];
|
|
488
|
+
if (testCaseItem.attachments) {
|
|
489
|
+
const attachments = Array.isArray(testCaseItem.attachments.attachment)
|
|
490
|
+
? testCaseItem.attachments.attachment
|
|
491
|
+
: [testCaseItem.attachments.attachment];
|
|
492
|
+
files = attachments.filter(a => a && a.filePath).map(a => a.filePath);
|
|
493
|
+
}
|
|
494
|
+
// Extract files from stack trace using existing utility
|
|
495
|
+
const stackFiles = (0, utils_js_1.fetchFilesFromStackTrace)(stack);
|
|
496
|
+
files = [...new Set([...files, ...stackFiles])]; // Remove duplicates
|
|
468
497
|
prev.push({
|
|
469
498
|
rid,
|
|
470
499
|
file,
|
|
@@ -479,7 +508,10 @@ function reduceTestCases(prev, item) {
|
|
|
479
508
|
run_time: parseFloat(testCaseItem.time || testCaseItem.duration) * 1000,
|
|
480
509
|
status,
|
|
481
510
|
title,
|
|
511
|
+
root_suite_id: TESTOMATIO_SUITE,
|
|
482
512
|
suite_title: suiteTitle,
|
|
513
|
+
files,
|
|
514
|
+
retry: false,
|
|
483
515
|
});
|
|
484
516
|
});
|
|
485
517
|
return prev;
|
|
@@ -503,12 +535,18 @@ function fetchProperties(item) {
|
|
|
503
535
|
let title = '';
|
|
504
536
|
if (!item.properties)
|
|
505
537
|
return {};
|
|
506
|
-
|
|
538
|
+
// Handle both single property and array of properties
|
|
539
|
+
const properties = Array.isArray(item.properties.property)
|
|
540
|
+
? item.properties.property
|
|
541
|
+
: [item.properties.property].filter(Boolean);
|
|
542
|
+
const prop = properties.find(p => p.name === 'Description');
|
|
507
543
|
if (prop)
|
|
508
544
|
title = prop.value;
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
545
|
+
let testId = properties.find(p => p.name === 'ID')?.value;
|
|
546
|
+
if (testId?.startsWith('@'))
|
|
547
|
+
testId = testId.slice(1);
|
|
548
|
+
if (testId?.startsWith('T'))
|
|
549
|
+
testId = testId.slice(1);
|
|
550
|
+
properties.filter(p => p.name === 'Category').forEach(p => tags.push(p.value));
|
|
551
|
+
return { title, tags, testId };
|
|
514
552
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testomatio/reporter",
|
|
3
|
-
"version": "2.1.0-beta-
|
|
3
|
+
"version": "2.1.0-beta.2-codeceptjs",
|
|
4
4
|
"description": "Testomatio Reporter Client",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=18"
|
|
@@ -14,10 +14,9 @@
|
|
|
14
14
|
"@aws-sdk/client-s3": "^3.279.0",
|
|
15
15
|
"@aws-sdk/lib-storage": "^3.279.0",
|
|
16
16
|
"@cucumber/cucumber": "^10.9.0",
|
|
17
|
-
"@octokit/rest": "^
|
|
17
|
+
"@octokit/rest": "^21.1.1",
|
|
18
18
|
"aws-sdk": "^2.1072.0",
|
|
19
|
-
"
|
|
20
|
-
"axios-retry": "^3.9.1",
|
|
19
|
+
"gaxios": ">=6.0 || >=7.0.0-rc.4 || <8",
|
|
21
20
|
"callsite-record": "^4.1.4",
|
|
22
21
|
"commander": "^12",
|
|
23
22
|
"cross-spawn": "^7.0.3",
|
|
@@ -49,17 +48,23 @@
|
|
|
49
48
|
"testcafe"
|
|
50
49
|
],
|
|
51
50
|
"scripts": {
|
|
52
|
-
"@cucumber/cucumber": "^10.9.0",
|
|
53
51
|
"clear-exportdir": "rm -rf export/",
|
|
54
52
|
"pretty": "npx prettier --check .",
|
|
55
53
|
"pretty:fix": "prettier --write .",
|
|
56
54
|
"lint": "eslint src",
|
|
57
55
|
"lint:fix": "eslint src --fix",
|
|
58
56
|
"format": "npm run lint:fix && npm run pretty:fix",
|
|
59
|
-
"test": "mocha tests
|
|
57
|
+
"test": "mocha tests/unit/**/*_test.js",
|
|
58
|
+
"test:playwright": "mocha tests/adapter/playwright.test.js",
|
|
59
|
+
"test:codecept": "mocha tests/adapter/codecept.test.js tests/adapter/codecept_comprehensive.test.js tests/adapter/codecept_steps_sections.test.js",
|
|
60
|
+
"test:frameworks": "npm run test:playwright && npm run test:codecept",
|
|
61
|
+
"test:all": "npm run test && npm run test:frameworks",
|
|
62
|
+
"test:adapters": "mocha tests/adapter/*.test.js",
|
|
63
|
+
"test:codecept:bug948": "mocha tests/adapter/codecept_aftersuite_failure.test.js",
|
|
64
|
+
"test:codecept:steps": "mocha tests/adapter/codecept_steps_sections.test.js",
|
|
65
|
+
"install-example-deps": "cd example/playwright && npm install && cd ../codecept && npm install",
|
|
60
66
|
"init": "cd ./tests/adapter/examples/cucumber && npm i",
|
|
61
67
|
"test:adapter": "node node_modules/mocha/bin/mocha './tests/adapter/index.test.js'",
|
|
62
|
-
"test:pipes": "mocha './tests/pipes/*_test.js'",
|
|
63
68
|
"test:adapter:jest:example": "jest './tests/adapter/examples/jest/index.test.js' --config='./tests/adapter/examples/jest/jest.config.js'",
|
|
64
69
|
"test:adapter:mocha:example": "mocha './tests/adapter/examples/mocha/index.test.js' --config='./tests/adapter/examples/mocha/mocha.config.cjs'",
|
|
65
70
|
"test:adapter:jasmine:example": "jasmine './tests/adapter/examples/jasmine/index.test.js' --reporter=./lib/adapter/jasmine.js",
|
|
@@ -68,8 +73,9 @@
|
|
|
68
73
|
"test:adapter:playwright:example": "npx playwright test --config='./tests/adapter/examples/playwright/playwright.config.ts'",
|
|
69
74
|
"test:adapter:vitest:example": "npx vitest --config='./tests/adapter/examples/vitest/vitest.config.ts'",
|
|
70
75
|
"test:storage": "npx mocha tests-storage/artifact-storage.test.js && npx mocha tests-storage/data-storage.test.js && TESTOMATIO_INTERCEPT_CONSOLE_LOGS=true npx mocha tests-storage/logger.test.js && npx mocha tests-storage/logger-2.test.js && npx mocha tests-storage/reporter-functions.test.js",
|
|
71
|
-
"
|
|
72
|
-
"build": "rm -rf ./cjs && tsc --module commonjs && npx tsx build/scripts/edit-js-files.js && npx tsx build/scripts/edit-package-json.js && chmod +x ./build/scripts/copy-tesmplate.sh && ./build/scripts/copy-tesmplate.sh"
|
|
76
|
+
"build": "rm -rf ./cjs && tsc --module commonjs && npx tsx build/scripts/edit-js-files.js && npx tsx build/scripts/edit-package-json.js && chmod +x ./build/scripts/copy-tesmplate.sh && ./build/scripts/copy-tesmplate.sh",
|
|
77
|
+
"build:bun": "rm -rf ./cjs && bunx tsc --module commonjs && npx tsx build/scripts/edit-js-files.js && npx tsx build/scripts/edit-package-json.js && chmod +x ./build/scripts/copy-tesmplate.sh && ./build/scripts/copy-tesmplate.sh",
|
|
78
|
+
"build:watch:bun": "rm -rf ./cjs && bun build ./src/bin/reportXml.js ./src/bin/startTest.js ./src/bin/uploadArtifacts.js --outdir ./cjs --target node --watch --onSuccess \"build/scripts/post-build.js\""
|
|
73
79
|
},
|
|
74
80
|
"devDependencies": {
|
|
75
81
|
"@playwright/test": "^1.49.1",
|
|
@@ -81,8 +87,7 @@
|
|
|
81
87
|
"chai": "^4.3.6",
|
|
82
88
|
"codeceptjs": "^3.6.5",
|
|
83
89
|
"cucumber": "^6.0.7",
|
|
84
|
-
"eslint": "^
|
|
85
|
-
"eslint-config-airbnb-base": "^15.0.0",
|
|
90
|
+
"eslint": "^9.24.0",
|
|
86
91
|
"eslint-config-prettier": "^8.3.0",
|
|
87
92
|
"eslint-plugin-import": "^2.25.4",
|
|
88
93
|
"jasmine": "^5.2.0",
|