@testomatio/reporter 2.3.7-beta.1-xml-import → 2.3.7-beta.11-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.
@@ -1,357 +0,0 @@
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.NUnitXmlParser = void 0;
7
- const debug_1 = __importDefault(require("debug"));
8
- const constants_js_1 = require("../constants.js");
9
- const debug = (0, debug_1.default)('@testomatio/reporter:nunit-parser');
10
- /**
11
- * Enhanced NUnit XML Parser that properly handles test-suite hierarchy
12
- * and parameterized tests
13
- */
14
- class NUnitXmlParser {
15
- constructor(options = {}) {
16
- this.options = options;
17
- this.tests = [];
18
- this.stats = {
19
- total: 0,
20
- passed: 0,
21
- failed: 0,
22
- skipped: 0,
23
- inconclusive: 0,
24
- };
25
- }
26
- /**
27
- * Parse NUnit XML test-run structure
28
- * @param {Object} testRun - Parsed XML test-run object
29
- * @returns {Object} - Parsed test results
30
- */
31
- parseTestRun(testRun) {
32
- debug('Parsing NUnit test-run');
33
- // Extract run-level statistics
34
- this.stats = {
35
- total: parseInt(testRun.total || 0, 10),
36
- passed: parseInt(testRun.passed || 0, 10),
37
- failed: parseInt(testRun.failed || 0, 10),
38
- skipped: parseInt(testRun.skipped || 0, 10),
39
- inconclusive: parseInt(testRun.inconclusive || 0, 10),
40
- };
41
- // Process the root test-suite
42
- if (testRun['test-suite']) {
43
- this.parseTestSuite(testRun['test-suite'], []);
44
- }
45
- debug(`Parsed ${this.tests.length} tests from NUnit XML`);
46
- return {
47
- status: testRun.result?.toLowerCase() || 'unknown',
48
- create_tests: true,
49
- tests_count: this.tests.length,
50
- passed_count: this.tests.filter(t => t.status === constants_js_1.STATUS.PASSED).length,
51
- failed_count: this.tests.filter(t => t.status === constants_js_1.STATUS.FAILED).length,
52
- skipped_count: this.tests.filter(t => t.status === constants_js_1.STATUS.SKIPPED).length,
53
- tests: this.tests,
54
- };
55
- }
56
- /**
57
- * Recursively parse test-suite elements based on their type
58
- * @param {Object|Array} testSuite - Test suite object or array
59
- * @param {Array} parentPath - Current path in the hierarchy
60
- */
61
- parseTestSuite(testSuite, parentPath = []) {
62
- if (!testSuite)
63
- return;
64
- // Handle arrays of test suites
65
- if (Array.isArray(testSuite)) {
66
- testSuite.forEach(suite => this.parseTestSuite(suite, parentPath));
67
- return;
68
- }
69
- const suiteType = testSuite.type;
70
- const suiteName = testSuite.name;
71
- const fullName = testSuite.fullname;
72
- debug(`Processing test-suite: type=${suiteType}, name=${suiteName}`);
73
- switch (suiteType) {
74
- case 'Assembly':
75
- // Assembly level - ignore the name, just process children
76
- debug('Processing Assembly level - ignoring name, processing children');
77
- this.processChildren(testSuite, parentPath);
78
- break;
79
- case 'TestSuite':
80
- // Namespace/grouping level - add to path but don't create test
81
- debug(`Processing TestSuite level - adding '${suiteName}' to path`);
82
- const newPath = [...parentPath, suiteName];
83
- this.processChildren(testSuite, newPath);
84
- break;
85
- case 'TestFixture':
86
- // Test class level - add to path and process test cases
87
- debug(`Processing TestFixture level - test class '${suiteName}'`);
88
- const testFixturePath = [...parentPath, suiteName];
89
- this.processChildren(testSuite, testFixturePath);
90
- break;
91
- default:
92
- debug(`Unknown test-suite type: ${suiteType}, treating as TestSuite`);
93
- const unknownPath = [...parentPath, suiteName];
94
- this.processChildren(testSuite, unknownPath);
95
- break;
96
- }
97
- }
98
- /**
99
- * Process child elements of a test suite
100
- * @param {Object} testSuite - Test suite object
101
- * @param {Array} currentPath - Current path in hierarchy
102
- */
103
- processChildren(testSuite, currentPath) {
104
- // Process nested test-suites
105
- if (testSuite['test-suite']) {
106
- this.parseTestSuite(testSuite['test-suite'], currentPath);
107
- }
108
- // Process test-cases
109
- if (testSuite['test-case']) {
110
- this.parseTestCases(testSuite['test-case'], currentPath, testSuite);
111
- }
112
- }
113
- /**
114
- * Parse test-case elements (actual tests)
115
- * @param {Object|Array} testCases - Test case object or array
116
- * @param {Array} suitePath - Path to the test suite
117
- * @param {Object} parentSuite - Parent test suite for context
118
- */
119
- parseTestCases(testCases, suitePath, parentSuite) {
120
- if (!testCases)
121
- return;
122
- // Handle arrays of test cases
123
- if (!Array.isArray(testCases)) {
124
- testCases = [testCases];
125
- }
126
- testCases.forEach(testCase => {
127
- const parsedTest = this.parseTestCase(testCase, suitePath, parentSuite);
128
- if (parsedTest) {
129
- this.tests.push(parsedTest);
130
- }
131
- });
132
- }
133
- /**
134
- * Parse individual test case
135
- * @param {Object} testCase - Test case object
136
- * @param {Array} suitePath - Path to the test suite
137
- * @param {Object} parentSuite - Parent test suite for context
138
- * @returns {Object|null} - Parsed test object
139
- */
140
- parseTestCase(testCase, suitePath, parentSuite) {
141
- if (!testCase || !testCase.name) {
142
- debug('Skipping test case without name');
143
- return null;
144
- }
145
- const testName = testCase.name;
146
- const fullName = testCase.fullname;
147
- const methodName = testCase.methodname || this.extractMethodName(testName);
148
- const className = testCase.classname || parentSuite?.name;
149
- debug(`Parsing test case: ${testName}`);
150
- // Extract parameters if this is a parameterized test
151
- const { baseMethodName, parameters, isParameterized } = this.extractParameters(testName);
152
- // Determine test status
153
- let status = constants_js_1.STATUS.PASSED;
154
- if (testCase.result) {
155
- switch (testCase.result.toLowerCase()) {
156
- case 'passed':
157
- status = constants_js_1.STATUS.PASSED;
158
- break;
159
- case 'failed':
160
- status = constants_js_1.STATUS.FAILED;
161
- break;
162
- case 'skipped':
163
- case 'ignored':
164
- status = constants_js_1.STATUS.SKIPPED;
165
- break;
166
- case 'inconclusive':
167
- status = constants_js_1.STATUS.SKIPPED; // Treat inconclusive as skipped
168
- break;
169
- default:
170
- status = constants_js_1.STATUS.PASSED;
171
- }
172
- }
173
- // Extract error information
174
- let message = '';
175
- let stack = '';
176
- if (testCase.failure) {
177
- message = testCase.failure.message || '';
178
- stack = testCase.failure['stack-trace'] || testCase.failure['#text'] || '';
179
- }
180
- if (testCase.output && testCase.output['#text']) {
181
- stack = `${stack}\n\n${testCase.output['#text']}`.trim();
182
- }
183
- // Extract test ID from properties
184
- let testId = null;
185
- if (testCase.properties && testCase.properties.property) {
186
- const properties = Array.isArray(testCase.properties.property)
187
- ? testCase.properties.property
188
- : [testCase.properties.property];
189
- const idProperty = properties.find(p => p.name === 'ID');
190
- if (idProperty) {
191
- testId = idProperty.value;
192
- // Remove @ and T prefixes if present
193
- if (testId.startsWith('@'))
194
- testId = testId.slice(1);
195
- if (testId.startsWith('T'))
196
- testId = testId.slice(1);
197
- }
198
- }
199
- // Build file path from suite path and class name
200
- const filePath = this.buildFilePath(suitePath, className, parentSuite);
201
- return {
202
- title: isParameterized ? testName : methodName || testName,
203
- methodName: baseMethodName || methodName || testName,
204
- fullName: fullName,
205
- suitePath: suitePath,
206
- suite_title: className || suitePath[suitePath.length - 1] || 'Unknown',
207
- file: filePath,
208
- status: status,
209
- message: message,
210
- stack: stack,
211
- run_time: parseFloat(testCase.duration || testCase.time || 0) * 1000,
212
- test_id: testId,
213
- create: true,
214
- retry: false,
215
- // Parameterized test metadata
216
- isParameterized: isParameterized,
217
- parameters: parameters,
218
- baseMethodName: baseMethodName,
219
- };
220
- }
221
- /**
222
- * Extract method name and parameters from test name
223
- * @param {string} testName - Full test name
224
- * @returns {Object} - Extracted information
225
- */
226
- extractParameters(testName) {
227
- const paramMatch = testName.match(/^(.+?)\((.+)\)$/);
228
- if (paramMatch) {
229
- const baseMethodName = paramMatch[1].trim();
230
- const paramString = paramMatch[2];
231
- // Parse parameters - handle quoted strings and nested structures
232
- const parameters = this.parseParameterString(paramString);
233
- return {
234
- baseMethodName: baseMethodName,
235
- parameters: parameters,
236
- isParameterized: true,
237
- };
238
- }
239
- return {
240
- baseMethodName: testName,
241
- parameters: [],
242
- isParameterized: false,
243
- };
244
- }
245
- /**
246
- * Parse parameter string into array of parameters
247
- * @param {string} paramString - Parameter string
248
- * @returns {Array} - Array of parameters
249
- */
250
- parseParameterString(paramString) {
251
- const parameters = [];
252
- let current = '';
253
- let inQuotes = false;
254
- let quoteChar = null;
255
- let depth = 0;
256
- for (let i = 0; i < paramString.length; i++) {
257
- const char = paramString[i];
258
- if (!inQuotes && (char === '"' || char === "'")) {
259
- inQuotes = true;
260
- quoteChar = char;
261
- current += char;
262
- }
263
- else if (inQuotes && char === quoteChar) {
264
- inQuotes = false;
265
- quoteChar = null;
266
- current += char;
267
- }
268
- else if (!inQuotes && char === '(') {
269
- depth++;
270
- current += char;
271
- }
272
- else if (!inQuotes && char === ')') {
273
- depth--;
274
- current += char;
275
- }
276
- else if (!inQuotes && char === ',' && depth === 0) {
277
- parameters.push(current.trim());
278
- current = '';
279
- }
280
- else {
281
- current += char;
282
- }
283
- }
284
- if (current.trim()) {
285
- parameters.push(current.trim());
286
- }
287
- // Clean up parameters - remove quotes if they wrap the entire parameter
288
- return parameters.map(param => {
289
- param = param.trim();
290
- if ((param.startsWith('"') && param.endsWith('"')) || (param.startsWith("'") && param.endsWith("'"))) {
291
- return param.slice(1, -1);
292
- }
293
- return param;
294
- });
295
- }
296
- /**
297
- * Extract method name from test name (fallback)
298
- * @param {string} testName - Test name
299
- * @returns {string} - Method name
300
- */
301
- extractMethodName(testName) {
302
- // Remove parameters if present
303
- const paramMatch = testName.match(/^(.+?)\(/);
304
- return paramMatch ? paramMatch[1].trim() : testName;
305
- }
306
- /**
307
- * Build file path from suite path and class name
308
- * @param {Array} suitePath - Suite path array
309
- * @param {string} className - Class name
310
- * @param {Object} parentSuite - Parent suite for context
311
- * @returns {string} - File path
312
- */
313
- buildFilePath(suitePath, className, parentSuite) {
314
- // Try to get file path from parent suite
315
- if (parentSuite && parentSuite.filepath) {
316
- return parentSuite.filepath;
317
- }
318
- // Build path from suite hierarchy
319
- const pathParts = [...suitePath];
320
- if (className && !pathParts.includes(className)) {
321
- pathParts.push(className);
322
- }
323
- // Convert to file path format
324
- return pathParts.join('/') + '.cs'; // Assume C# for NUnit
325
- }
326
- /**
327
- * Group parameterized tests by base method name
328
- * @param {Array} tests - Array of parsed tests
329
- * @returns {Object} - Grouped tests
330
- */
331
- groupParameterizedTests(tests) {
332
- const grouped = {};
333
- tests.forEach(test => {
334
- const key = test.isParameterized
335
- ? `${test.suitePath.join('.')}.${test.baseMethodName}`
336
- : `${test.suitePath.join('.')}.${test.title}`;
337
- if (!grouped[key]) {
338
- grouped[key] = {
339
- baseTest: {
340
- name: test.baseMethodName || test.title,
341
- suitePath: test.suitePath,
342
- suite_title: test.suite_title,
343
- file: test.file,
344
- isParameterized: test.isParameterized,
345
- },
346
- variations: [],
347
- };
348
- }
349
- grouped[key].variations.push(test);
350
- });
351
- return grouped;
352
- }
353
- }
354
- exports.NUnitXmlParser = NUnitXmlParser;
355
- module.exports = NUnitXmlParser;
356
-
357
- module.exports.NUnitXmlParser = NUnitXmlParser;