@testomatio/reporter 2.1.3-beta.1-xml-import → 2.1.3-beta.2-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.
package/lib/xmlReader.js CHANGED
@@ -283,16 +283,60 @@ class XmlReader {
283
283
  const fqn = this.generateNormalizedFQN(test);
284
284
  if (fqnMap.has(fqn)) {
285
285
  const existingTest = fqnMap.get(fqn);
286
- // Merge test properties, prioritizing Test Explorer structure but updating with IDs
287
- if (test.test_id && !existingTest.test_id) {
288
- existingTest.test_id = test.test_id;
289
- }
290
- // Keep the most complete test data
291
- if (test.stack && !existingTest.stack) {
292
- existingTest.stack = test.stack;
286
+ // For parameterized tests, merge as Examples
287
+ if (test.example) {
288
+ // Initialize examples array if it doesn't exist
289
+ if (!existingTest.examples) {
290
+ existingTest.examples = [];
291
+ // Add the existing test's example as the first item
292
+ if (existingTest.example) {
293
+ existingTest.examples.push({
294
+ parameters: existingTest.example,
295
+ status: existingTest.status,
296
+ run_time: existingTest.run_time,
297
+ message: existingTest.message,
298
+ stack: existingTest.stack
299
+ });
300
+ }
301
+ }
302
+ // Add this test's execution as an example
303
+ existingTest.examples.push({
304
+ parameters: test.example,
305
+ status: test.status,
306
+ run_time: test.run_time,
307
+ message: test.message,
308
+ stack: test.stack
309
+ });
310
+ // Update the main test status to reflect the worst status
311
+ if (test.status === 'failed' || existingTest.status === 'failed') {
312
+ existingTest.status = 'failed';
313
+ }
314
+ else if (test.status === 'skipped' && existingTest.status !== 'failed') {
315
+ existingTest.status = 'skipped';
316
+ }
317
+ // Update total run time
318
+ existingTest.run_time = (existingTest.run_time || 0) + (test.run_time || 0);
319
+ // Merge stack traces if they're different
320
+ if (test.stack && test.stack !== existingTest.stack) {
321
+ existingTest.stack = existingTest.stack + '\n\n---\n\n' + test.stack;
322
+ }
323
+ // Merge messages if they're different
324
+ if (test.message && test.message !== existingTest.message) {
325
+ existingTest.message = existingTest.message + '; ' + test.message;
326
+ }
293
327
  }
294
- if (test.message && !existingTest.message) {
295
- existingTest.message = test.message;
328
+ else {
329
+ // Merge test properties for non-parameterized tests, prioritizing Test Explorer structure
330
+ if (test.test_id && !existingTest.test_id) {
331
+ existingTest.test_id = test.test_id;
332
+ }
333
+ // Keep the most complete test data
334
+ if (test.stack && !existingTest.stack) {
335
+ existingTest.stack = test.stack;
336
+ }
337
+ if (test.message && !existingTest.message) {
338
+ existingTest.message = test.message;
339
+ }
296
340
  }
297
341
  // Prefer Test Explorer structure (longer, more complete suite_title)
298
342
  if (test.suite_title && test.suite_title.length > existingTest.suite_title.length) {
@@ -322,7 +366,7 @@ class XmlReader {
322
366
  }
323
367
  generateNormalizedFQN(test) {
324
368
  // Generate normalized FQN for deduplication by extracting the core namespace.class.method
325
- // This normalizes different representations of the same test
369
+ // For parameterized tests, we want the SAME FQN so they merge into one test with multiple Examples
326
370
  const fullClassName = test.suite_title || '';
327
371
  const methodName = test.title;
328
372
  // Extract the most specific namespace.class pattern
@@ -579,9 +623,13 @@ function reduceTestCases(prev, item) {
579
623
  const suiteTitle = extractTestExplorerSuiteTitle(testCaseItem, item);
580
624
  title ||= testCaseItem.name || testCaseItem.methodname || testCaseItem.classname;
581
625
  tags ||= [];
582
- const exampleMatches = testCaseItem.name?.match(/\S\((.*?)\)/);
626
+ // Store original test name for parameter extraction
627
+ const originalTestName = testCaseItem.name || testCaseItem.methodname;
628
+ const exampleMatches = originalTestName?.match(/\((.*?)\)$/);
583
629
  if (exampleMatches) {
584
- example = { ...exampleMatches[1].split(',').map(v => v.trim().replace(/[^\w\s-]/g, '')) };
630
+ // Extract and store parameters as Examples
631
+ const parameterValues = exampleMatches[1].split(',').map(v => v.trim().replace(/['"]/g, ''));
632
+ example = { ...parameterValues };
585
633
  title = title.replace(/\(.*?\)/, '').trim();
586
634
  }
587
635
  stack = `${testCaseItem['system-out'] || testCaseItem.output || testCaseItem.log || ''}\n\n${stack}\n\n${suiteOutput}\n\n${suiteErr}`.trim();
@@ -630,6 +678,7 @@ function reduceTestCases(prev, item) {
630
678
  run_time: parseFloat(testCaseItem.time || testCaseItem.duration) * 1000,
631
679
  status,
632
680
  title,
681
+ originalTestName, // Store original name for parameter-aware FQN generation
633
682
  root_suite_id: TESTOMATIO_SUITE,
634
683
  suite_title: suiteTitle,
635
684
  files,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/reporter",
3
- "version": "2.1.3-beta.1-xml-import",
3
+ "version": "2.1.3-beta.2-xml-import",
4
4
  "description": "Testomatio Reporter Client",
5
5
  "engines": {
6
6
  "node": ">=18"
package/src/xmlReader.js CHANGED
@@ -330,17 +330,66 @@ class XmlReader {
330
330
 
331
331
  if (fqnMap.has(fqn)) {
332
332
  const existingTest = fqnMap.get(fqn);
333
- // Merge test properties, prioritizing Test Explorer structure but updating with IDs
334
- if (test.test_id && !existingTest.test_id) {
335
- existingTest.test_id = test.test_id;
336
- }
337
- // Keep the most complete test data
338
- if (test.stack && !existingTest.stack) {
339
- existingTest.stack = test.stack;
340
- }
341
- if (test.message && !existingTest.message) {
342
- existingTest.message = test.message;
333
+
334
+ // For parameterized tests, merge as Examples
335
+ if (test.example) {
336
+ // Initialize examples array if it doesn't exist
337
+ if (!existingTest.examples) {
338
+ existingTest.examples = [];
339
+ // Add the existing test's example as the first item
340
+ if (existingTest.example) {
341
+ existingTest.examples.push({
342
+ parameters: existingTest.example,
343
+ status: existingTest.status,
344
+ run_time: existingTest.run_time,
345
+ message: existingTest.message,
346
+ stack: existingTest.stack
347
+ });
348
+ }
349
+ }
350
+
351
+ // Add this test's execution as an example
352
+ existingTest.examples.push({
353
+ parameters: test.example,
354
+ status: test.status,
355
+ run_time: test.run_time,
356
+ message: test.message,
357
+ stack: test.stack
358
+ });
359
+
360
+ // Update the main test status to reflect the worst status
361
+ if (test.status === 'failed' || existingTest.status === 'failed') {
362
+ existingTest.status = 'failed';
363
+ } else if (test.status === 'skipped' && existingTest.status !== 'failed') {
364
+ existingTest.status = 'skipped';
365
+ }
366
+
367
+ // Update total run time
368
+ existingTest.run_time = (existingTest.run_time || 0) + (test.run_time || 0);
369
+
370
+ // Merge stack traces if they're different
371
+ if (test.stack && test.stack !== existingTest.stack) {
372
+ existingTest.stack = existingTest.stack + '\n\n---\n\n' + test.stack;
373
+ }
374
+
375
+ // Merge messages if they're different
376
+ if (test.message && test.message !== existingTest.message) {
377
+ existingTest.message = existingTest.message + '; ' + test.message;
378
+ }
379
+ } else {
380
+ // Merge test properties for non-parameterized tests, prioritizing Test Explorer structure
381
+ if (test.test_id && !existingTest.test_id) {
382
+ existingTest.test_id = test.test_id;
383
+ }
384
+ // Keep the most complete test data
385
+ if (test.stack && !existingTest.stack) {
386
+ existingTest.stack = test.stack;
387
+ }
388
+ if (test.message && !existingTest.message) {
389
+ existingTest.message = test.message;
390
+ }
343
391
  }
392
+
344
393
  // Prefer Test Explorer structure (longer, more complete suite_title)
345
394
  if (test.suite_title && test.suite_title.length > existingTest.suite_title.length) {
346
395
  existingTest.suite_title = test.suite_title;
@@ -373,7 +422,7 @@ class XmlReader {
373
422
 
374
423
  generateNormalizedFQN(test) {
375
424
  // Generate normalized FQN for deduplication by extracting the core namespace.class.method
376
- // This normalizes different representations of the same test
425
+ // For parameterized tests, we want the SAME FQN so they merge into one test with multiple Examples
377
426
 
378
427
  const fullClassName = test.suite_title || '';
379
428
  const methodName = test.title;
@@ -654,9 +703,14 @@ function reduceTestCases(prev, item) {
654
703
  title ||= testCaseItem.name || testCaseItem.methodname || testCaseItem.classname;
655
704
  tags ||= [];
656
705
 
657
- const exampleMatches = testCaseItem.name?.match(/\S\((.*?)\)/);
706
+ // Store original test name for parameter extraction
707
+ const originalTestName = testCaseItem.name || testCaseItem.methodname;
708
+
709
+ const exampleMatches = originalTestName?.match(/\((.*?)\)$/);
658
710
  if (exampleMatches) {
659
- example = { ...exampleMatches[1].split(',').map(v => v.trim().replace(/[^\w\s-]/g, '')) };
711
+ // Extract and store parameters as Examples
712
+ const parameterValues = exampleMatches[1].split(',').map(v => v.trim().replace(/['"]/g, ''));
713
+ example = { ...parameterValues };
660
714
  title = title.replace(/\(.*?\)/, '').trim();
661
715
  }
662
716
 
@@ -712,6 +766,7 @@ function reduceTestCases(prev, item) {
712
766
  run_time: parseFloat(testCaseItem.time || testCaseItem.duration) * 1000,
713
767
  status,
714
768
  title,
769
+ originalTestName, // Store original name for parameter-aware FQN generation
715
770
  root_suite_id: TESTOMATIO_SUITE,
716
771
  suite_title: suiteTitle,
717
772
  files,