@testomatio/reporter 2.3.7-beta.2-xml-import → 2.3.7-beta.4-stack-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.
- package/README.md +1 -1
- package/lib/bin/cli.js +1 -1
- package/lib/bin/reportXml.js +2 -5
- package/lib/bin/startTest.js +3 -3
- package/lib/client.js +48 -25
- package/lib/junit-adapter/csharp.d.ts +1 -0
- package/lib/junit-adapter/csharp.js +7 -40
- package/lib/pipe/debug.js +1 -1
- package/lib/pipe/testomatio.js +15 -19
- package/lib/reporter.d.ts +19 -9
- package/lib/reporter.js +40 -5
- package/lib/template/testomatio.hbs +1026 -1366
- package/lib/uploader.js +0 -4
- package/lib/utils/utils.js +11 -90
- package/lib/xmlReader.d.ts +26 -32
- package/lib/xmlReader.js +50 -106
- package/package.json +1 -1
- package/src/bin/cli.js +1 -1
- package/src/bin/reportXml.js +2 -5
- package/src/bin/startTest.js +5 -5
- package/src/client.js +80 -31
- package/src/junit-adapter/csharp.js +6 -45
- package/src/pipe/debug.js +3 -2
- package/src/pipe/testomatio.js +81 -75
- package/src/reporter.js +7 -4
- package/src/template/testomatio.hbs +1026 -1366
- package/src/uploader.js +0 -5
- package/src/utils/utils.js +9 -96
- package/src/xmlReader.js +45 -128
- package/lib/junit-adapter/nunit-parser.d.ts +0 -82
- package/lib/junit-adapter/nunit-parser.js +0 -369
- package/src/junit-adapter/nunit-parser.js +0 -404
package/lib/uploader.js
CHANGED
|
@@ -170,10 +170,6 @@ class S3Uploader {
|
|
|
170
170
|
if (typeof filePath === 'string' && !path_1.default.isAbsolute(filePath)) {
|
|
171
171
|
filePath = path_1.default.join(process.cwd(), filePath);
|
|
172
172
|
}
|
|
173
|
-
// Normalize path separators for cross-platform compatibility
|
|
174
|
-
if (typeof filePath === 'string') {
|
|
175
|
-
filePath = filePath.replace(/\\/g, '/');
|
|
176
|
-
}
|
|
177
173
|
const data = { rid, file: filePath, uploaded };
|
|
178
174
|
const jsonLine = `${JSON.stringify(data)}\n`;
|
|
179
175
|
fs_1.default.appendFileSync(tempFilePath, jsonLine);
|
package/lib/utils/utils.js
CHANGED
|
@@ -122,8 +122,12 @@ const fetchFilesFromStackTrace = (stack = '', checkExists = true) => {
|
|
|
122
122
|
.map(f => f[1].trim())
|
|
123
123
|
.map(f => f.replace(/^\/+/, '/').replace(/^\/([A-Za-z]:)/, '$1')) // Remove extra slashes, handle Windows paths
|
|
124
124
|
.map(f => {
|
|
125
|
-
//
|
|
126
|
-
|
|
125
|
+
// Convert Windows paths to Linux paths for testing purposes
|
|
126
|
+
if (f.match(/^[A-Za-z]:[\\\/]/)) {
|
|
127
|
+
// Convert Windows path to Linux equivalent for test scenarios
|
|
128
|
+
return f.replace(/^[A-Za-z]:[\\\/]/, '/').replace(/\\/g, '/');
|
|
129
|
+
}
|
|
130
|
+
return f;
|
|
127
131
|
});
|
|
128
132
|
debug('Found files in stack trace: ', files);
|
|
129
133
|
return files.filter(f => {
|
|
@@ -170,8 +174,6 @@ exports.fetchSourceCodeFromStackTrace = fetchSourceCodeFromStackTrace;
|
|
|
170
174
|
exports.TEST_ID_REGEX = /@T([\w\d]{8})/;
|
|
171
175
|
exports.SUITE_ID_REGEX = /@S([\w\d]{8})/;
|
|
172
176
|
const fetchIdFromCode = (code, opts = {}) => {
|
|
173
|
-
if (!code)
|
|
174
|
-
return null;
|
|
175
177
|
const comments = code
|
|
176
178
|
.split('\n')
|
|
177
179
|
.map(l => l.trim())
|
|
@@ -214,58 +216,10 @@ const fetchSourceCode = (contents, opts = {}) => {
|
|
|
214
216
|
lineIndex = lines.findIndex(l => l.includes(`${title}(`));
|
|
215
217
|
}
|
|
216
218
|
else if (opts.lang === 'csharp') {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
if (
|
|
220
|
-
|
|
221
|
-
}
|
|
222
|
-
if (methodLineIndex === -1) {
|
|
223
|
-
methodLineIndex = lines.findIndex(l => l.includes(`${title}(`));
|
|
224
|
-
}
|
|
225
|
-
// If found, scan upwards to find [TestCase], [Test] attributes and XML comments
|
|
226
|
-
if (methodLineIndex !== -1) {
|
|
227
|
-
lineIndex = methodLineIndex;
|
|
228
|
-
// Scan upwards to find the start of attributes and comments
|
|
229
|
-
for (let i = methodLineIndex - 1; i >= 0; i--) {
|
|
230
|
-
const trimmedLine = lines[i].trim();
|
|
231
|
-
// Include [TestCase], [Test], and other attributes
|
|
232
|
-
if (trimmedLine.startsWith('[')) {
|
|
233
|
-
lineIndex = i;
|
|
234
|
-
continue;
|
|
235
|
-
}
|
|
236
|
-
// Include XML documentation comments
|
|
237
|
-
if (trimmedLine.startsWith('///')) {
|
|
238
|
-
lineIndex = i;
|
|
239
|
-
continue;
|
|
240
|
-
}
|
|
241
|
-
// Stop at empty lines (with some tolerance)
|
|
242
|
-
if (trimmedLine === '') {
|
|
243
|
-
// Check if next non-empty line is an attribute or comment
|
|
244
|
-
let hasMoreAttributes = false;
|
|
245
|
-
for (let j = i - 1; j >= 0; j--) {
|
|
246
|
-
const nextTrimmed = lines[j].trim();
|
|
247
|
-
if (nextTrimmed === '')
|
|
248
|
-
continue;
|
|
249
|
-
if (nextTrimmed.startsWith('[') || nextTrimmed.startsWith('///')) {
|
|
250
|
-
hasMoreAttributes = true;
|
|
251
|
-
lineIndex = j;
|
|
252
|
-
}
|
|
253
|
-
break;
|
|
254
|
-
}
|
|
255
|
-
if (!hasMoreAttributes)
|
|
256
|
-
break;
|
|
257
|
-
continue;
|
|
258
|
-
}
|
|
259
|
-
// Stop at other method declarations or class-level elements
|
|
260
|
-
if (trimmedLine.includes('public ') ||
|
|
261
|
-
trimmedLine.includes('private ') ||
|
|
262
|
-
trimmedLine.includes('protected ') ||
|
|
263
|
-
trimmedLine.includes('internal ')) {
|
|
264
|
-
if (!trimmedLine.startsWith('['))
|
|
265
|
-
break;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
219
|
+
if (lineIndex === -1)
|
|
220
|
+
lineIndex = lines.findIndex(l => l.includes(`public void ${title}`));
|
|
221
|
+
if (lineIndex === -1)
|
|
222
|
+
lineIndex = lines.findIndex(l => l.includes(`${title}(`));
|
|
269
223
|
}
|
|
270
224
|
else {
|
|
271
225
|
lineIndex = lines.findIndex(l => l.includes(title));
|
|
@@ -274,28 +228,11 @@ const fetchSourceCode = (contents, opts = {}) => {
|
|
|
274
228
|
if (opts.prepend) {
|
|
275
229
|
lineIndex -= opts.prepend;
|
|
276
230
|
}
|
|
277
|
-
if (lineIndex
|
|
231
|
+
if (lineIndex) {
|
|
278
232
|
const result = [];
|
|
279
|
-
let braceDepth = 0; // Track brace depth for C# methods
|
|
280
|
-
let methodStartFound = false; // Flag to indicate we've found the method opening brace
|
|
281
233
|
for (let i = lineIndex; i < lineIndex + limit; i++) {
|
|
282
234
|
if (lines[i] === undefined)
|
|
283
235
|
continue;
|
|
284
|
-
// Track brace depth for C# to stop after method closes
|
|
285
|
-
if (opts.lang === 'csharp') {
|
|
286
|
-
const line = lines[i];
|
|
287
|
-
// Count opening and closing braces
|
|
288
|
-
const openBraces = (line.match(/\{/g) || []).length;
|
|
289
|
-
const closeBraces = (line.match(/\}/g) || []).length;
|
|
290
|
-
if (openBraces > 0)
|
|
291
|
-
methodStartFound = true;
|
|
292
|
-
braceDepth += openBraces - closeBraces;
|
|
293
|
-
// If we've started the method and depth returns to 0, method is complete
|
|
294
|
-
if (methodStartFound && braceDepth === 0 && closeBraces > 0) {
|
|
295
|
-
result.push(lines[i]);
|
|
296
|
-
break;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
236
|
if (i > lineIndex + 2 && !opts.prepend) {
|
|
300
237
|
// annotation
|
|
301
238
|
if (opts.lang === 'php' && lines[i].trim().startsWith('#['))
|
|
@@ -334,22 +271,6 @@ const fetchSourceCode = (contents, opts = {}) => {
|
|
|
334
271
|
break;
|
|
335
272
|
if (opts.lang === 'java' && lines[i].includes(' class '))
|
|
336
273
|
break;
|
|
337
|
-
// For C#, additional checks if brace tracking didn't stop us
|
|
338
|
-
if (opts.lang === 'csharp') {
|
|
339
|
-
const trimmed = lines[i].trim();
|
|
340
|
-
// Stop at attribute that marks beginning of next test
|
|
341
|
-
if (trimmed.match(/^\[(Test|TestCase|Theory|Fact)/))
|
|
342
|
-
break;
|
|
343
|
-
// Stop at XML documentation comments that belong to next method
|
|
344
|
-
if (trimmed.startsWith('///'))
|
|
345
|
-
break;
|
|
346
|
-
// Stop at another method declaration
|
|
347
|
-
if (trimmed.match(/^\s*(public|private|protected|internal)\s+(\w+|async\s+\w+)\s+\w+\s*\(/))
|
|
348
|
-
break;
|
|
349
|
-
// Stop at class declaration
|
|
350
|
-
if (trimmed.includes(' class ') && trimmed.includes('public'))
|
|
351
|
-
break;
|
|
352
|
-
}
|
|
353
274
|
}
|
|
354
275
|
result.push(lines[i]);
|
|
355
276
|
}
|
package/lib/xmlReader.d.ts
CHANGED
|
@@ -19,11 +19,25 @@ declare class XmlReader {
|
|
|
19
19
|
tests: any[];
|
|
20
20
|
stats: {};
|
|
21
21
|
uploader: S3Uploader;
|
|
22
|
-
enhancedNunit: boolean;
|
|
23
|
-
groupParameterized: boolean;
|
|
24
22
|
version: any;
|
|
25
23
|
connectAdapter(): import("./junit-adapter/adapter.js").default;
|
|
26
|
-
parse(fileName: any):
|
|
24
|
+
parse(fileName: any): {
|
|
25
|
+
status: string;
|
|
26
|
+
create_tests: boolean;
|
|
27
|
+
tests_count: number;
|
|
28
|
+
passed_count: number;
|
|
29
|
+
skipped_count: number;
|
|
30
|
+
failed_count: number;
|
|
31
|
+
tests: any;
|
|
32
|
+
} | {
|
|
33
|
+
status: any;
|
|
34
|
+
create_tests: boolean;
|
|
35
|
+
tests_count: number;
|
|
36
|
+
passed_count: number;
|
|
37
|
+
failed_count: number;
|
|
38
|
+
skipped_count: number;
|
|
39
|
+
tests: any[];
|
|
40
|
+
};
|
|
27
41
|
processJUnit(jsonSuite: any): {
|
|
28
42
|
create_tests: boolean;
|
|
29
43
|
duration: number;
|
|
@@ -35,14 +49,15 @@ declare class XmlReader {
|
|
|
35
49
|
tests: any[];
|
|
36
50
|
tests_count: number;
|
|
37
51
|
};
|
|
38
|
-
processNUnit(jsonSuite: any):
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
52
|
+
processNUnit(jsonSuite: any): {
|
|
53
|
+
status: any;
|
|
54
|
+
create_tests: boolean;
|
|
55
|
+
tests_count: number;
|
|
56
|
+
passed_count: number;
|
|
57
|
+
failed_count: number;
|
|
58
|
+
skipped_count: number;
|
|
59
|
+
tests: any[];
|
|
60
|
+
};
|
|
46
61
|
processTRX(jsonSuite: any): {
|
|
47
62
|
status: string;
|
|
48
63
|
create_tests: boolean;
|
|
@@ -52,27 +67,6 @@ declare class XmlReader {
|
|
|
52
67
|
failed_count: number;
|
|
53
68
|
tests: any;
|
|
54
69
|
};
|
|
55
|
-
_parseTRXTestDefinition(td: any): {
|
|
56
|
-
title: any;
|
|
57
|
-
example: any;
|
|
58
|
-
file: string;
|
|
59
|
-
description: any;
|
|
60
|
-
suite_title: any;
|
|
61
|
-
id: any;
|
|
62
|
-
};
|
|
63
|
-
_parseTRXTestResult(td: any, tests: any): {
|
|
64
|
-
suite_title: any;
|
|
65
|
-
title: any;
|
|
66
|
-
file: any;
|
|
67
|
-
description: any;
|
|
68
|
-
code: any;
|
|
69
|
-
run_time: number;
|
|
70
|
-
stack: any;
|
|
71
|
-
files: any;
|
|
72
|
-
create: boolean;
|
|
73
|
-
overwrite: boolean;
|
|
74
|
-
};
|
|
75
|
-
_mapTRXStatus(outcome: any): string;
|
|
76
70
|
processXUnit(assemblies: any): {
|
|
77
71
|
status: string;
|
|
78
72
|
create_tests: boolean;
|
package/lib/xmlReader.js
CHANGED
|
@@ -11,7 +11,6 @@ const fast_xml_parser_1 = require("fast-xml-parser");
|
|
|
11
11
|
const constants_js_1 = require("./constants.js");
|
|
12
12
|
const crypto_1 = require("crypto");
|
|
13
13
|
const url_1 = require("url");
|
|
14
|
-
const nunit_parser_js_1 = require("./junit-adapter/nunit-parser.js");
|
|
15
14
|
const utils_js_1 = require("./utils/utils.js");
|
|
16
15
|
const index_js_1 = require("./pipe/index.js");
|
|
17
16
|
const index_js_2 = __importDefault(require("./junit-adapter/index.js"));
|
|
@@ -55,9 +54,6 @@ class XmlReader {
|
|
|
55
54
|
this.stats = {};
|
|
56
55
|
this.stats.language = opts.lang?.toLowerCase();
|
|
57
56
|
this.uploader = new uploader_js_1.S3Uploader();
|
|
58
|
-
// Enhanced NUnit parsing - enabled by default for NUnit XML
|
|
59
|
-
this.enhancedNunit = opts.enhancedNunit !== false; // Default true, can be disabled
|
|
60
|
-
this.groupParameterized = opts.groupParameterized !== false; // Default true, can be disabled
|
|
61
57
|
// @ts-ignore
|
|
62
58
|
const packageJsonPath = path_1.default.resolve(__dirname, '..', 'package.json');
|
|
63
59
|
this.version = JSON.parse(fs_1.default.readFileSync(packageJsonPath).toString()).version;
|
|
@@ -106,8 +102,7 @@ class XmlReader {
|
|
|
106
102
|
return this.processJUnit(jsonSuite);
|
|
107
103
|
}
|
|
108
104
|
processJUnit(jsonSuite) {
|
|
109
|
-
const { testsuite, name, failures, errors } = jsonSuite;
|
|
110
|
-
const tests = testsuite?.tests || jsonSuite.tests;
|
|
105
|
+
const { testsuite, name, tests, failures, errors } = jsonSuite;
|
|
111
106
|
reduceOptions.preferClassname = this.stats.language === 'python';
|
|
112
107
|
const resultTests = processTestSuite(testsuite);
|
|
113
108
|
const hasFailures = resultTests.filter(t => t.status === 'failed').length > 0;
|
|
@@ -133,13 +128,6 @@ class XmlReader {
|
|
|
133
128
|
};
|
|
134
129
|
}
|
|
135
130
|
processNUnit(jsonSuite) {
|
|
136
|
-
// Use enhanced NUnit parser if enabled and this is actually NUnit XML
|
|
137
|
-
if (this.enhancedNunit && this.isNUnitXml(jsonSuite)) {
|
|
138
|
-
debug('Using enhanced NUnit parser');
|
|
139
|
-
return this.processNUnitEnhanced(jsonSuite);
|
|
140
|
-
}
|
|
141
|
-
// Fallback to legacy parser for backward compatibility
|
|
142
|
-
debug('Using legacy NUnit parser');
|
|
143
131
|
const { result, total, passed, failed, inconclusive, skipped } = jsonSuite;
|
|
144
132
|
reduceOptions.preferClassname = this.stats.language === 'python';
|
|
145
133
|
const resultTests = processTestSuite(jsonSuite['test-suite']);
|
|
@@ -154,58 +142,65 @@ class XmlReader {
|
|
|
154
142
|
tests: resultTests,
|
|
155
143
|
};
|
|
156
144
|
}
|
|
157
|
-
/**
|
|
158
|
-
* Check if the XML is actually NUnit format (has test-suite hierarchy)
|
|
159
|
-
* @param {Object} jsonSuite - Parsed XML suite object
|
|
160
|
-
* @returns {boolean} - True if this is NUnit XML format
|
|
161
|
-
*/
|
|
162
|
-
isNUnitXml(jsonSuite) {
|
|
163
|
-
// NUnit XML has test-suite elements with type attributes
|
|
164
|
-
if (jsonSuite['test-suite']) {
|
|
165
|
-
const testSuite = Array.isArray(jsonSuite['test-suite']) ? jsonSuite['test-suite'][0] : jsonSuite['test-suite'];
|
|
166
|
-
// Check for NUnit-specific test-suite types
|
|
167
|
-
return (testSuite &&
|
|
168
|
-
testSuite.type &&
|
|
169
|
-
['Assembly', 'TestSuite', 'TestFixture', 'ParameterizedMethod'].includes(testSuite.type));
|
|
170
|
-
}
|
|
171
|
-
return false;
|
|
172
|
-
}
|
|
173
|
-
processNUnitEnhanced(jsonSuite) {
|
|
174
|
-
debug('Processing NUnit XML with enhanced parser');
|
|
175
|
-
try {
|
|
176
|
-
const nunitParser = new nunit_parser_js_1.NUnitXmlParser({
|
|
177
|
-
groupParameterized: this.groupParameterized,
|
|
178
|
-
...this.opts,
|
|
179
|
-
});
|
|
180
|
-
const result = nunitParser.parseTestRun(jsonSuite);
|
|
181
|
-
// Add parsed tests to our collection
|
|
182
|
-
this.tests = this.tests.concat(result.tests);
|
|
183
|
-
debug(`Enhanced NUnit parser processed ${result.tests.length} tests`);
|
|
184
|
-
return result;
|
|
185
|
-
}
|
|
186
|
-
catch (error) {
|
|
187
|
-
debug('Enhanced NUnit parser failed, falling back to legacy parser:', error.message);
|
|
188
|
-
console.warn(`${constants_js_1.APP_PREFIX} Enhanced NUnit parsing failed, using legacy parser: ${error.message}`);
|
|
189
|
-
// Fallback to legacy parser
|
|
190
|
-
this.enhancedNunit = false;
|
|
191
|
-
return this.processNUnit(jsonSuite);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
145
|
processTRX(jsonSuite) {
|
|
195
146
|
let defs = jsonSuite?.TestRun?.TestDefinitions?.UnitTest;
|
|
196
147
|
if (!Array.isArray(defs))
|
|
197
148
|
defs = [defs].filter(d => !!d);
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
149
|
+
const tests = defs.map(td => {
|
|
150
|
+
const title = td.name.replace(/\(.*?\)/, '').trim();
|
|
151
|
+
let example = td.name.match(/\((.*?)\)/);
|
|
152
|
+
if (example)
|
|
153
|
+
example = { ...example[1].split(',') };
|
|
154
|
+
const suite = td.TestMethod.className.split(', ')[0].split('.');
|
|
155
|
+
const suite_title = suite.pop();
|
|
156
|
+
return {
|
|
157
|
+
title,
|
|
158
|
+
example,
|
|
159
|
+
file: suite.join('/'),
|
|
160
|
+
description: td.Description,
|
|
161
|
+
suite_title,
|
|
162
|
+
id: td.Execution.id,
|
|
163
|
+
};
|
|
164
|
+
}) || [];
|
|
201
165
|
let result = jsonSuite?.TestRun?.Results?.UnitTestResult;
|
|
202
166
|
if (!Array.isArray(result))
|
|
203
167
|
result = [result].filter(d => !!d);
|
|
204
|
-
const results = result.map(td =>
|
|
168
|
+
const results = result.map(td => ({
|
|
169
|
+
id: td.executionId,
|
|
170
|
+
// seconds are used in junit reports, but ms are used by testomatio
|
|
171
|
+
run_time: parseFloat(td.duration) * 1000,
|
|
172
|
+
status: td.outcome,
|
|
173
|
+
stack: td.Output.StdOut,
|
|
174
|
+
files: td?.ResultFiles?.ResultFile?.map(rf => rf.path),
|
|
175
|
+
}));
|
|
176
|
+
results.forEach(r => {
|
|
177
|
+
const test = tests.find(t => t.id === r.id) || {};
|
|
178
|
+
r.suite_title = test.suite_title;
|
|
179
|
+
r.title = test.title?.trim();
|
|
180
|
+
if (test.code)
|
|
181
|
+
r.code = test.code;
|
|
182
|
+
if (test.description)
|
|
183
|
+
r.description = test.description;
|
|
184
|
+
if (test.example)
|
|
185
|
+
r.example = test.example;
|
|
186
|
+
if (test.file)
|
|
187
|
+
r.file = test.file;
|
|
188
|
+
r.create = true;
|
|
189
|
+
r.overwrite = true;
|
|
190
|
+
if (r.status === 'Passed')
|
|
191
|
+
r.status = constants_js_1.STATUS.PASSED;
|
|
192
|
+
if (r.status === 'Failed')
|
|
193
|
+
r.status = constants_js_1.STATUS.FAILED;
|
|
194
|
+
if (r.status === 'Skipped')
|
|
195
|
+
r.status = constants_js_1.STATUS.SKIPPED;
|
|
196
|
+
delete r.id;
|
|
197
|
+
});
|
|
205
198
|
debug(results);
|
|
206
199
|
const counters = jsonSuite?.TestRun?.ResultSummary?.Counters || {};
|
|
207
200
|
const failed_count = parseInt(counters.failed, 10) + parseInt(counters.error, 10);
|
|
208
|
-
|
|
201
|
+
let status = constants_js_1.STATUS.PASSED.toString();
|
|
202
|
+
if (failed_count > 0)
|
|
203
|
+
status = constants_js_1.STATUS.FAILED;
|
|
209
204
|
this.tests = results.filter(t => !!t.title);
|
|
210
205
|
return {
|
|
211
206
|
status,
|
|
@@ -217,57 +212,6 @@ class XmlReader {
|
|
|
217
212
|
tests: results,
|
|
218
213
|
};
|
|
219
214
|
}
|
|
220
|
-
_parseTRXTestDefinition(td) {
|
|
221
|
-
const title = td.name.replace(/\(.*?\)/, '').trim();
|
|
222
|
-
const exampleMatch = td.name.match(/\((.*?)\)/);
|
|
223
|
-
const example = exampleMatch ? { ...exampleMatch[1].split(',') } : null;
|
|
224
|
-
const suite = td.TestMethod.className.split(', ')[0].split('.');
|
|
225
|
-
const suite_title = suite.pop();
|
|
226
|
-
// Convert namespace to file path for C#
|
|
227
|
-
const file = `${suite.join('/')}.cs`;
|
|
228
|
-
return {
|
|
229
|
-
title, // Base name without parameters for test import
|
|
230
|
-
example, // Parameters object for parameterized tests
|
|
231
|
-
file, // File path with .cs extension
|
|
232
|
-
description: td.Description,
|
|
233
|
-
suite_title,
|
|
234
|
-
id: td.Execution.id,
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
_parseTRXTestResult(td, tests) {
|
|
238
|
-
const test = tests.find(t => t.id === td.executionId) || {};
|
|
239
|
-
const result = {
|
|
240
|
-
suite_title: test.suite_title,
|
|
241
|
-
title: test.title?.trim(),
|
|
242
|
-
file: test.file,
|
|
243
|
-
description: test.description,
|
|
244
|
-
code: test.code,
|
|
245
|
-
run_time: parseFloat(td.duration) * 1000,
|
|
246
|
-
stack: td.Output?.StdOut || '',
|
|
247
|
-
files: td?.ResultFiles?.ResultFile?.map(rf => rf.path),
|
|
248
|
-
create: true,
|
|
249
|
-
overwrite: true,
|
|
250
|
-
};
|
|
251
|
-
// Add example for parameterized tests
|
|
252
|
-
if (test.example) {
|
|
253
|
-
result.example = test.example;
|
|
254
|
-
}
|
|
255
|
-
// Map TRX status to Testomat.io status
|
|
256
|
-
result.status = this._mapTRXStatus(td.outcome);
|
|
257
|
-
return result;
|
|
258
|
-
}
|
|
259
|
-
_mapTRXStatus(outcome) {
|
|
260
|
-
switch (outcome) {
|
|
261
|
-
case 'Passed':
|
|
262
|
-
return constants_js_1.STATUS.PASSED;
|
|
263
|
-
case 'Failed':
|
|
264
|
-
return constants_js_1.STATUS.FAILED;
|
|
265
|
-
case 'Skipped':
|
|
266
|
-
return constants_js_1.STATUS.SKIPPED;
|
|
267
|
-
default:
|
|
268
|
-
return constants_js_1.STATUS.PASSED;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
215
|
processXUnit(assemblies) {
|
|
272
216
|
const tests = [];
|
|
273
217
|
assemblies = Array.isArray(assemblies.assembly) ? assemblies.assembly : [assemblies.assembly];
|
package/package.json
CHANGED
package/src/bin/cli.js
CHANGED
|
@@ -158,7 +158,7 @@ program
|
|
|
158
158
|
.option('--lang <lang>', 'Language used (python, ruby, java)')
|
|
159
159
|
.option('--timelimit <time>', 'default time limit in seconds to kill a stuck process')
|
|
160
160
|
.action(async (pattern, opts) => {
|
|
161
|
-
if (!pattern.endsWith('.xml')
|
|
161
|
+
if (!pattern.endsWith('.xml')) {
|
|
162
162
|
pattern += '.xml';
|
|
163
163
|
}
|
|
164
164
|
let { javaTests, lang } = opts;
|
package/src/bin/reportXml.js
CHANGED
|
@@ -23,7 +23,7 @@ program
|
|
|
23
23
|
.option('--timelimit <time>', 'default time limit in seconds to kill a stuck process')
|
|
24
24
|
.option('--env-file <envfile>', 'Load environment variables from env file')
|
|
25
25
|
.action(async (pattern, opts) => {
|
|
26
|
-
if (!pattern.endsWith('.xml')
|
|
26
|
+
if (!pattern.endsWith('.xml')) {
|
|
27
27
|
pattern += '.xml';
|
|
28
28
|
}
|
|
29
29
|
let { javaTests, lang } = opts;
|
|
@@ -34,10 +34,7 @@ program
|
|
|
34
34
|
}
|
|
35
35
|
lang = lang?.toLowerCase();
|
|
36
36
|
if (javaTests === true || (lang === 'java' && !javaTests)) javaTests = 'src/test/java';
|
|
37
|
-
const runReader = new XmlReader({
|
|
38
|
-
javaTests,
|
|
39
|
-
lang,
|
|
40
|
-
});
|
|
37
|
+
const runReader = new XmlReader({ javaTests, lang });
|
|
41
38
|
const files = glob.sync(pattern, { cwd: opts.dir || process.cwd() });
|
|
42
39
|
if (!files.length) {
|
|
43
40
|
console.log(APP_PREFIX, `Report can't be created. No XML files found 😥`);
|
package/src/bin/startTest.js
CHANGED
|
@@ -18,7 +18,7 @@ const newArgs = ['run'];
|
|
|
18
18
|
let i = 0;
|
|
19
19
|
while (i < args.length) {
|
|
20
20
|
const arg = args[i];
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
if (arg === '-c' || arg === '--command') {
|
|
23
23
|
// Map -c/--command to positional argument for run command
|
|
24
24
|
i++;
|
|
@@ -33,7 +33,7 @@ while (i < args.length) {
|
|
|
33
33
|
// Map --launch to start command
|
|
34
34
|
newArgs[0] = 'start';
|
|
35
35
|
} else if (arg === '--finish') {
|
|
36
|
-
// Map --finish to finish command
|
|
36
|
+
// Map --finish to finish command
|
|
37
37
|
newArgs[0] = 'finish';
|
|
38
38
|
} else {
|
|
39
39
|
// Pass through other arguments
|
|
@@ -45,9 +45,9 @@ while (i < args.length) {
|
|
|
45
45
|
// Execute the main CLI with mapped arguments
|
|
46
46
|
|
|
47
47
|
const child = spawn(process.execPath, [cliPath, ...newArgs], {
|
|
48
|
-
stdio: 'inherit'
|
|
48
|
+
stdio: 'inherit'
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
-
child.on('exit', code => {
|
|
51
|
+
child.on('exit', (code) => {
|
|
52
52
|
process.exit(code);
|
|
53
|
-
});
|
|
53
|
+
});
|