@testomatio/reporter 2.3.6-beta.1-truncate-steps → 2.3.7-beta.1-xml-import

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.
@@ -83,12 +83,8 @@ const fetchFilesFromStackTrace = (stack = '', checkExists = true) => {
83
83
  .map(f => f[1].trim())
84
84
  .map(f => f.replace(/^\/+/, '/').replace(/^\/([A-Za-z]:)/, '$1')) // Remove extra slashes, handle Windows paths
85
85
  .map(f => {
86
- // Convert Windows paths to Linux paths for testing purposes
87
- if (f.match(/^[A-Za-z]:[\\\/]/)) {
88
- // Convert Windows path to Linux equivalent for test scenarios
89
- return f.replace(/^[A-Za-z]:[\\\/]/, '/').replace(/\\/g, '/');
90
- }
91
- return f;
86
+ // Normalize path separators for cross-platform compatibility
87
+ return f.replace(/\\/g, '/');
92
88
  });
93
89
 
94
90
  debug('Found files in stack trace: ', files);
@@ -139,6 +135,8 @@ export const TEST_ID_REGEX = /@T([\w\d]{8})/;
139
135
  export const SUITE_ID_REGEX = /@S([\w\d]{8})/;
140
136
 
141
137
  const fetchIdFromCode = (code, opts = {}) => {
138
+ if (!code) return null;
139
+
142
140
  const comments = code
143
141
  .split('\n')
144
142
  .map(l => l.trim())
@@ -180,8 +178,32 @@ const fetchSourceCode = (contents, opts = {}) => {
180
178
  if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`public void ${title}`));
181
179
  if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`${title}(`));
182
180
  } else if (opts.lang === 'csharp') {
183
- if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`public void ${title}`));
184
- if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`${title}(`));
181
+ // Enhanced C# method detection for NUnit tests
182
+ lineIndex = lines.findIndex(l => l.includes(`public void ${title}(`));
183
+
184
+ if (lineIndex === -1) {
185
+ lineIndex = lines.findIndex(l => l.includes(`public async Task ${title}(`));
186
+ }
187
+
188
+ if (lineIndex === -1) {
189
+ lineIndex = lines.findIndex(l => l.includes(`${title}(`));
190
+ }
191
+
192
+ // Look for TestCase or Test attributes above the method
193
+ if (lineIndex === -1) {
194
+ const testAttributeIndex = lines.findIndex((l, index) => {
195
+ if (l.includes('[TestCase') || l.includes('[Test')) {
196
+ // Check next few lines for the method
197
+ const nextLines = lines.slice(index, Math.min(lines.length, index + 5));
198
+ const hasMethod = nextLines.some(nextLine => nextLine.includes(`${title}(`));
199
+ return hasMethod;
200
+ }
201
+ return false;
202
+ });
203
+ if (testAttributeIndex !== -1) {
204
+ lineIndex = testAttributeIndex;
205
+ }
206
+ }
185
207
  } else {
186
208
  lineIndex = lines.findIndex(l => l.includes(title));
187
209
  }
@@ -191,7 +213,7 @@ const fetchSourceCode = (contents, opts = {}) => {
191
213
  lineIndex -= opts.prepend;
192
214
  }
193
215
 
194
- if (lineIndex) {
216
+ if (lineIndex !== -1 && lineIndex !== undefined) {
195
217
  const result = [];
196
218
  for (let i = lineIndex; i < lineIndex + limit; i++) {
197
219
  if (lines[i] === undefined) continue;
@@ -216,6 +238,10 @@ const fetchSourceCode = (contents, opts = {}) => {
216
238
  if (opts.lang === 'java' && lines[i].trim().match(/^@\w+/)) break;
217
239
  if (opts.lang === 'java' && lines[i].includes(' public void ')) break;
218
240
  if (opts.lang === 'java' && lines[i].includes(' class ')) break;
241
+ if (opts.lang === 'csharp' && lines[i].trim().match(/^\[Test/)) break;
242
+ if (opts.lang === 'csharp' && lines[i].includes(' public void ')) break;
243
+ if (opts.lang === 'csharp' && lines[i].includes(' public async Task ')) break;
244
+ if (opts.lang === 'csharp' && lines[i].includes(' class ') && lines[i].includes('public')) break;
219
245
  }
220
246
  result.push(lines[i]);
221
247
  }
@@ -428,16 +454,8 @@ function transformEnvVarToBoolean(value) {
428
454
  return Boolean(value);
429
455
  }
430
456
 
431
- function truncate(s, size = 255) {
432
- if (s.toString().trim().length < size) {
433
- return s.toString();
434
- }
435
- return `${s.toString().substring(0, size)}...`;
436
- }
437
-
438
457
  export {
439
458
  ansiRegExp,
440
- truncate,
441
459
  cleanLatestRunId,
442
460
  isSameTest,
443
461
  fetchSourceCode,
package/src/xmlReader.js CHANGED
@@ -6,6 +6,7 @@ import { XMLParser } from 'fast-xml-parser';
6
6
  import { APP_PREFIX, STATUS } from './constants.js';
7
7
  import { randomUUID } from 'crypto';
8
8
  import { fileURLToPath } from 'url';
9
+ import { NUnitXmlParser } from './junit-adapter/nunit-parser.js';
9
10
  import {
10
11
  fetchFilesFromStackTrace,
11
12
  fetchIdFromOutput,
@@ -75,6 +76,10 @@ class XmlReader {
75
76
  this.stats.language = opts.lang?.toLowerCase();
76
77
  this.uploader = new S3Uploader();
77
78
 
79
+ // Enhanced NUnit parsing - enabled by default for NUnit XML
80
+ this.enhancedNunit = opts.enhancedNunit !== false; // Default true, can be disabled
81
+ this.groupParameterized = opts.groupParameterized !== false; // Default true, can be disabled
82
+
78
83
  // @ts-ignore
79
84
  const packageJsonPath = path.resolve(__dirname, '..', 'package.json');
80
85
  this.version = JSON.parse(fs.readFileSync(packageJsonPath).toString()).version;
@@ -126,7 +131,8 @@ class XmlReader {
126
131
  }
127
132
 
128
133
  processJUnit(jsonSuite) {
129
- const { testsuite, name, tests, failures, errors } = jsonSuite;
134
+ const { testsuite, name, failures, errors } = jsonSuite;
135
+ const tests = testsuite?.tests || jsonSuite.tests;
130
136
 
131
137
  reduceOptions.preferClassname = this.stats.language === 'python';
132
138
  const resultTests = processTestSuite(testsuite);
@@ -157,6 +163,14 @@ class XmlReader {
157
163
  }
158
164
 
159
165
  processNUnit(jsonSuite) {
166
+ // Use enhanced NUnit parser if enabled and this is actually NUnit XML
167
+ if (this.enhancedNunit && this.isNUnitXml(jsonSuite)) {
168
+ debug('Using enhanced NUnit parser');
169
+ return this.processNUnitEnhanced(jsonSuite);
170
+ }
171
+
172
+ // Fallback to legacy parser for backward compatibility
173
+ debug('Using legacy NUnit parser');
160
174
  const { result, total, passed, failed, inconclusive, skipped } = jsonSuite;
161
175
 
162
176
  reduceOptions.preferClassname = this.stats.language === 'python';
@@ -175,6 +189,53 @@ class XmlReader {
175
189
  };
176
190
  }
177
191
 
192
+ /**
193
+ * Check if the XML is actually NUnit format (has test-suite hierarchy)
194
+ * @param {Object} jsonSuite - Parsed XML suite object
195
+ * @returns {boolean} - True if this is NUnit XML format
196
+ */
197
+ isNUnitXml(jsonSuite) {
198
+ // NUnit XML has test-suite elements with type attributes
199
+ if (jsonSuite['test-suite']) {
200
+ const testSuite = Array.isArray(jsonSuite['test-suite']) ? jsonSuite['test-suite'][0] : jsonSuite['test-suite'];
201
+
202
+ // Check for NUnit-specific test-suite types
203
+ return (
204
+ testSuite &&
205
+ testSuite.type &&
206
+ ['Assembly', 'TestSuite', 'TestFixture', 'ParameterizedMethod'].includes(testSuite.type)
207
+ );
208
+ }
209
+ return false;
210
+ }
211
+
212
+ processNUnitEnhanced(jsonSuite) {
213
+ debug('Processing NUnit XML with enhanced parser');
214
+
215
+ try {
216
+ const nunitParser = new NUnitXmlParser({
217
+ groupParameterized: this.groupParameterized,
218
+ ...this.opts,
219
+ });
220
+
221
+ const result = nunitParser.parseTestRun(jsonSuite);
222
+
223
+ // Add parsed tests to our collection
224
+ this.tests = this.tests.concat(result.tests);
225
+
226
+ debug(`Enhanced NUnit parser processed ${result.tests.length} tests`);
227
+
228
+ return result;
229
+ } catch (error) {
230
+ debug('Enhanced NUnit parser failed, falling back to legacy parser:', error.message);
231
+ console.warn(`${APP_PREFIX} Enhanced NUnit parsing failed, using legacy parser: ${error.message}`);
232
+
233
+ // Fallback to legacy parser
234
+ this.enhancedNunit = false;
235
+ return this.processNUnit(jsonSuite);
236
+ }
237
+ }
238
+
178
239
  processTRX(jsonSuite) {
179
240
  let defs = jsonSuite?.TestRun?.TestDefinitions?.UnitTest;
180
241
  if (!Array.isArray(defs)) defs = [defs].filter(d => !!d);