@testomatio/reporter 2.3.5-beta-6-xml-import → 2.3.5-beta.8-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
@@ -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_SUITE, TESTOMATIO_MAX_STACK_TRACE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN, TESTOMATIO_MARK_DETACHED, TESTOMATIO_DISABLE_SOURCE_CODE, } = 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,
@@ -47,10 +47,6 @@ class XmlReader {
47
47
  if (!this.adapter)
48
48
  throw new Error('XML adapter for this format not found');
49
49
  this.opts = opts || {};
50
- // Check if source code fetching should be disabled
51
- this.disableSourceCodeFetching = opts.disableSourceCodeFetching || TESTOMATIO_DISABLE_SOURCE_CODE;
52
- // Control suite organization strategy: 'classname' (default) or 'fullpath'
53
- this.suiteOrganization = opts.suiteOrganization || process.env.TESTOMATIO_SUITE_ORGANIZATION || 'classname';
54
50
  this.store = {};
55
51
  this.pipesPromise = (0, index_js_1.pipesFactory)(opts, this.store);
56
52
  this.parser = new fast_xml_parser_1.XMLParser(options);
@@ -62,15 +58,6 @@ class XmlReader {
62
58
  const packageJsonPath = path_1.default.resolve(__dirname, '..', 'package.json');
63
59
  this.version = JSON.parse(fs_1.default.readFileSync(packageJsonPath).toString()).version;
64
60
  console.log(constants_js_1.APP_PREFIX, `Testomatio Reporter v${this.version}`);
65
- if (this.disableSourceCodeFetching) {
66
- console.log(constants_js_1.APP_PREFIX, '🚫 Source code fetching is disabled');
67
- }
68
- if (this.suiteOrganization === 'fullpath') {
69
- console.log(constants_js_1.APP_PREFIX, '📁 Using fullpath suite organization (may create nested structure)');
70
- }
71
- else {
72
- console.log(constants_js_1.APP_PREFIX, '📋 Using classname suite organization (avoids duplicates)');
73
- }
74
61
  }
75
62
  connectAdapter() {
76
63
  if (this.opts.javaTests) {
@@ -143,23 +130,8 @@ class XmlReader {
143
130
  processNUnit(jsonSuite) {
144
131
  const { result, total, passed, failed, inconclusive, skipped } = jsonSuite;
145
132
  reduceOptions.preferClassname = this.stats.language === 'python';
146
- reduceOptions.suiteOrganization = this.suiteOrganization;
147
133
  const resultTests = processTestSuite(jsonSuite['test-suite']);
148
- debug('Raw tests extracted from NUnit XML:', resultTests.length);
149
- debug('Raw tests:', resultTests.map(t => ({ title: t.title, example: t.example, file: t.file })));
150
- // Optional deduplication for complex NUnit scenarios - can be enabled via options
151
- let finalTests = resultTests;
152
- if (this.opts.enableNUnitDeduplication) {
153
- finalTests = this.deduplicateTestsByFQN(resultTests);
154
- debug('Tests after deduplication:', finalTests.length);
155
- debug('Deduplicated tests:', finalTests.map(t => ({
156
- title: t.title,
157
- examples: t.examples,
158
- example: t.example,
159
- file: t.file,
160
- })));
161
- }
162
- this.tests = this.tests.concat(finalTests);
134
+ this.tests = this.tests.concat(resultTests);
163
135
  return {
164
136
  status: result?.toLowerCase(),
165
137
  create_tests: true,
@@ -167,7 +139,7 @@ class XmlReader {
167
139
  passed_count: parseInt(passed, 10),
168
140
  failed_count: parseInt(failed, 10),
169
141
  skipped_count: parseInt(inconclusive + skipped, 10),
170
- tests: finalTests,
142
+ tests: resultTests,
171
143
  };
172
144
  }
173
145
  processTRX(jsonSuite) {
@@ -303,171 +275,6 @@ class XmlReader {
303
275
  tests,
304
276
  };
305
277
  }
306
- deduplicateTestsByFQN(tests) {
307
- const fqnMap = new Map();
308
- tests.forEach(test => {
309
- const fqn = this.generateNormalizedFQN(test);
310
- if (fqnMap.has(fqn)) {
311
- const existingTest = fqnMap.get(fqn);
312
- // For parameterized tests, merge as Examples
313
- if (test.example && Array.isArray(test.example) && test.example.length > 0) {
314
- // Initialize examples array if it doesn't exist
315
- if (!existingTest.examples) {
316
- existingTest.examples = [];
317
- // Add the existing test's example as the first item if it has parameters
318
- if (existingTest.example && Array.isArray(existingTest.example) && existingTest.example.length > 0) {
319
- existingTest.examples.push({
320
- parameters: existingTest.example,
321
- status: existingTest.status,
322
- run_time: existingTest.run_time,
323
- message: existingTest.message,
324
- stack: existingTest.stack,
325
- });
326
- // Clear the main test's example since it's now in examples array
327
- delete existingTest.example;
328
- }
329
- }
330
- // Add this test's execution as an example
331
- existingTest.examples.push({
332
- parameters: test.example,
333
- status: test.status,
334
- run_time: test.run_time,
335
- message: test.message,
336
- stack: test.stack,
337
- });
338
- // Update the main test status to reflect the worst status
339
- if (test.status === 'failed' || existingTest.status === 'failed') {
340
- existingTest.status = 'failed';
341
- }
342
- else if (test.status === 'skipped' && existingTest.status !== 'failed') {
343
- existingTest.status = 'skipped';
344
- }
345
- // Update total run time
346
- existingTest.run_time = (existingTest.run_time || 0) + (test.run_time || 0);
347
- }
348
- else {
349
- // Merge test properties for non-parameterized tests, prioritizing Test Explorer structure
350
- if (test.test_id && !existingTest.test_id) {
351
- existingTest.test_id = test.test_id;
352
- }
353
- // Keep the most complete test data
354
- if (test.stack && !existingTest.stack) {
355
- existingTest.stack = test.stack;
356
- }
357
- if (test.message && !existingTest.message) {
358
- existingTest.message = test.message;
359
- }
360
- }
361
- // Prefer Test Explorer structure (longer, more complete suite_title)
362
- if (test.suite_title && test.suite_title.length > existingTest.suite_title.length) {
363
- existingTest.suite_title = test.suite_title;
364
- }
365
- // Always use the source file path if available
366
- if (test.file && test.file.endsWith('.cs')) {
367
- existingTest.file = test.file;
368
- }
369
- else if (!existingTest.file || !existingTest.file.endsWith('.cs')) {
370
- existingTest.file = this.extractCsFileFromPath(test);
371
- }
372
- }
373
- else {
374
- // Fix file path to use proper .cs file names from source paths
375
- if (!test.file || !test.file.endsWith('.cs')) {
376
- test.file = this.extractCsFileFromPath(test);
377
- }
378
- fqnMap.set(fqn, test);
379
- }
380
- });
381
- return Array.from(fqnMap.values());
382
- }
383
- generateFQN(test) {
384
- // Generate Fully Qualified Name: Namespace + Class + Method (standard .NET FQN)
385
- // Don't include assembly as it can vary between different test structures
386
- const namespace = this.extractNamespace(test);
387
- const className = this.extractClassName(test);
388
- const methodName = test.title;
389
- // Use the most complete namespace.class structure available
390
- if (test.suite_title && test.suite_title.includes('.')) {
391
- return `${test.suite_title}.${methodName}`;
392
- }
393
- return `${namespace}.${className}.${methodName}`;
394
- }
395
- generateNormalizedFQN(test) {
396
- // Generate normalized FQN for deduplication by extracting the core namespace.class.method
397
- // For parameterized tests, we want the SAME FQN so they merge into one test with multiple Examples
398
- const fullClassName = test.suite_title || '';
399
- const methodName = test.title;
400
- // Extract the most specific namespace.class pattern
401
- if (fullClassName.includes('.')) {
402
- const parts = fullClassName.split('.');
403
- if (parts.length >= 2) {
404
- const className = parts[parts.length - 1];
405
- // Look for common .NET namespace patterns and normalize them:
406
- // TestProject.Tests.MyClass -> Tests.MyClass
407
- // Tests.MyClass -> Tests.MyClass
408
- // MyProject.SubNamespace.Tests.MyClass -> Tests.MyClass
409
- let normalizedNamespace = '';
410
- for (let i = parts.length - 2; i >= 0; i--) {
411
- const part = parts[i];
412
- // Build namespace from right to left, excluding project names
413
- if (part === 'Tests' || part.endsWith('Tests') || part.includes('Test')) {
414
- // Found a test namespace, use it as the normalized namespace
415
- normalizedNamespace = part;
416
- break;
417
- }
418
- else if (i === parts.length - 2) {
419
- // If no test namespace found, use the immediate parent as namespace
420
- normalizedNamespace = part;
421
- }
422
- }
423
- return `${normalizedNamespace}.${className}.${methodName}`;
424
- }
425
- }
426
- // Fallback for simple class names
427
- return `${fullClassName}.${methodName}`;
428
- }
429
- extractAssemblyName(test) {
430
- // Extract assembly name from file path or use default
431
- if (test.file) {
432
- const parts = test.file.split(/[/\\]/);
433
- return parts[0] || 'DefaultAssembly';
434
- }
435
- return 'DefaultAssembly';
436
- }
437
- extractNamespace(test) {
438
- // Extract namespace from suite_title or classname
439
- if (test.suite_title && test.suite_title.includes('.')) {
440
- const parts = test.suite_title.split('.');
441
- return parts.slice(0, -1).join('.');
442
- }
443
- return test.suite_title || 'DefaultNamespace';
444
- }
445
- extractClassName(test) {
446
- // Extract class name from suite_title
447
- if (test.suite_title && test.suite_title.includes('.')) {
448
- const parts = test.suite_title.split('.');
449
- return parts[parts.length - 1];
450
- }
451
- return test.suite_title || 'DefaultClass';
452
- }
453
- extractCsFileFromPath(test) {
454
- // Extract .cs file name from source file path, not namespace
455
- if (test.file) {
456
- // Look for actual .cs file path patterns
457
- const csFileMatch = test.file.match(/([^/\\]+\.cs)$/);
458
- if (csFileMatch) {
459
- return test.file;
460
- }
461
- // If no .cs extension, assume it's a namespace path and convert to likely file name
462
- const className = this.extractClassName(test);
463
- const pathParts = test.file.split(/[/\\]/);
464
- pathParts[pathParts.length - 1] = `${className}.cs`;
465
- return pathParts.join('/');
466
- }
467
- // Fallback to class name
468
- const className = this.extractClassName(test);
469
- return `${className}.cs`;
470
- }
471
278
  calculateStats() {
472
279
  this.stats = {
473
280
  ...this.stats,
@@ -491,11 +298,6 @@ class XmlReader {
491
298
  return this.stats;
492
299
  }
493
300
  fetchSourceCode() {
494
- // Skip source code fetching if disabled
495
- if (this.disableSourceCodeFetching) {
496
- debug('Source code fetching is disabled');
497
- return;
498
- }
499
301
  this.tests.forEach(t => {
500
302
  try {
501
303
  const file = this.adapter.getFilePath(t);
@@ -522,9 +324,7 @@ class XmlReader {
522
324
  return;
523
325
  }
524
326
  const contents = fs_1.default.readFileSync(file).toString();
525
- // Try original test name first (for parameterized tests), fallback to regular title
526
- const titleForLookup = t.originalTestName ? t.originalTestName.replace(/\(.*?\)/, '').trim() : t.title;
527
- t.code = (0, utils_js_1.fetchSourceCode)(contents, { ...t, title: titleForLookup, lang: this.stats.language });
327
+ t.code = (0, utils_js_1.fetchSourceCode)(contents, { ...t, lang: this.stats.language });
528
328
  if (t.code)
529
329
  debug('Fetched code for test %s', t.title);
530
330
  t.test_id = (0, utils_js_1.fetchIdFromCode)(t.code, { lang: this.stats.language });
@@ -630,12 +430,7 @@ function reduceTestCases(prev, item) {
630
430
  testCases
631
431
  .filter(t => !!t)
632
432
  .forEach(testCaseItem => {
633
- // Simple file extraction (version 2.1.1 approach) with fallback to enhanced extraction
634
- let file = testCaseItem.file || item.filepath || item.fullname || item.package || '';
635
- // If no file found with simple approach and we have enhanced extraction enabled, use it
636
- if (!file && item.filepath) {
637
- file = extractSourceFilePath(testCaseItem, item);
638
- }
433
+ const file = testCaseItem.file || item.filepath || item.fullname || item.package || '';
639
434
  let stack = '';
640
435
  let message = '';
641
436
  if (testCaseItem.error)
@@ -659,28 +454,13 @@ function reduceTestCases(prev, item) {
659
454
  // SpecFlow config
660
455
  let { title, tags, testId } = fetchProperties(isParametrized ? item : testCaseItem);
661
456
  let example = null;
662
- // Smart suite title extraction to avoid duplicates
663
- const suiteTitle = getSuiteTitle(testCaseItem, item, isParametrized, reduceOptions.suiteOrganization);
457
+ const suiteTitle = preferClassname ? testCaseItem.classname : item.name || testCaseItem.classname;
664
458
  title ||= testCaseItem.name || testCaseItem.methodname || testCaseItem.classname;
665
459
  tags ||= [];
666
- // Store original test name for enhanced parameter extraction
667
- const originalTestName = testCaseItem.name || testCaseItem.methodname;
668
- // Enhanced NUnit-style arguments from <arguments> element
669
- if (testCaseItem.arguments && testCaseItem.arguments.arg) {
670
- const args = Array.isArray(testCaseItem.arguments.arg)
671
- ? testCaseItem.arguments.arg
672
- : [testCaseItem.arguments.arg];
673
- example = args; // Store as array instead of object
674
- // Remove parameters from title for NUnit tests
675
- title = (testCaseItem.methodname || title).replace(/\(.*?\)/, '').trim();
676
- }
677
- else {
678
- // Simple parameter extraction (version 2.1.1 approach)
679
- const exampleMatches = testCaseItem.name?.match(/\S\((.*?)\)/);
680
- if (exampleMatches) {
681
- example = { ...exampleMatches[1].split(',').map(v => v.trim().replace(/[^\w\s-]/g, '')) };
682
- title = title.replace(/\(.*?\)/, '').trim();
683
- }
460
+ const exampleMatches = testCaseItem.name?.match(/\S\((.*?)\)/);
461
+ if (exampleMatches) {
462
+ example = { ...exampleMatches[1].split(',').map(v => v.trim().replace(/[^\w\s-]/g, '')) };
463
+ title = title.replace(/\(.*?\)/, '').trim();
684
464
  }
685
465
  stack = `${testCaseItem['system-out'] || testCaseItem.output || testCaseItem.log || ''}\n\n${stack}\n\n${suiteOutput}\n\n${suiteErr}`.trim();
686
466
  if (!testId)
@@ -728,7 +508,6 @@ function reduceTestCases(prev, item) {
728
508
  run_time: parseFloat(testCaseItem.time || testCaseItem.duration) * 1000,
729
509
  status,
730
510
  title,
731
- originalTestName, // Store original name for enhanced features
732
511
  root_suite_id: TESTOMATIO_SUITE,
733
512
  suite_title: suiteTitle,
734
513
  files,
@@ -737,102 +516,6 @@ function reduceTestCases(prev, item) {
737
516
  });
738
517
  return prev;
739
518
  }
740
- function extractSourceFilePath(testCaseItem, item) {
741
- // Priority order for file path extraction to match Test Explorer structure:
742
- // 1. filepath attribute (direct .cs file path from NUnit)
743
- // 2. fullname (contains full project path)
744
- // 3. file attribute from test case
745
- // 4. package (fallback)
746
- // NUnit provides filepath attribute with actual .cs file path - use this first
747
- if (item.filepath) {
748
- // Clean up Windows/Unix path separators and ensure proper format
749
- let filePath = item.filepath.replace(/\\/g, '/');
750
- // Make relative to current working directory if absolute
751
- if (path_1.default.isAbsolute(item.filepath)) {
752
- const cwd = process.cwd().replace(/\\/g, '/');
753
- if (filePath.startsWith(cwd)) {
754
- filePath = path_1.default.relative(cwd, item.filepath).replace(/\\/g, '/');
755
- }
756
- else {
757
- // Try to extract relative path from common patterns
758
- const commonPatterns = ['/Tests/', '/test/', '/src/', '/Test/'];
759
- for (const pattern of commonPatterns) {
760
- const index = filePath.lastIndexOf(pattern);
761
- if (index !== -1) {
762
- filePath = filePath.substring(index + 1);
763
- break;
764
- }
765
- }
766
- }
767
- }
768
- return filePath;
769
- }
770
- if (testCaseItem.file) {
771
- let filePath = testCaseItem.file.replace(/\\/g, '/');
772
- // Make relative to current working directory if absolute
773
- if (path_1.default.isAbsolute(testCaseItem.file)) {
774
- const cwd = process.cwd().replace(/\\/g, '/');
775
- if (filePath.startsWith(cwd)) {
776
- filePath = path_1.default.relative(cwd, testCaseItem.file).replace(/\\/g, '/');
777
- }
778
- else {
779
- // Try to extract relative path from common patterns
780
- const commonPatterns = ['/Tests/', '/test/', '/src/', '/Test/'];
781
- for (const pattern of commonPatterns) {
782
- const index = filePath.lastIndexOf(pattern);
783
- if (index !== -1) {
784
- filePath = filePath.substring(index + 1);
785
- break;
786
- }
787
- }
788
- }
789
- }
790
- return filePath;
791
- }
792
- if (item.fullname) {
793
- // Extract actual file path from fullname if it contains path separators
794
- const fullnameParts = item.fullname.split('.');
795
- if (fullnameParts.length > 2) {
796
- // For ParameterizedMethod, get the class name (not method name)
797
- // Example: "NUnit_sample_test.Tests.SampleTests.TestBooleanValue" -> "Tests/SampleTests.cs"
798
- let namespaceParts, className;
799
- if (item.type === 'ParameterizedMethod') {
800
- // For parameterized methods, the last part is the method name, second-to-last is class
801
- namespaceParts = fullnameParts.slice(1, -2); // Skip project name and method name
802
- className = fullnameParts[fullnameParts.length - 2]; // Get class name
803
- }
804
- else {
805
- // For regular classes/fixtures
806
- namespaceParts = fullnameParts.slice(1, -1); // Skip project name
807
- className = fullnameParts[fullnameParts.length - 1];
808
- }
809
- return `${namespaceParts.join('/')}/${className}.cs`;
810
- }
811
- }
812
- if (item.package)
813
- return item.package.replace(/\\/g, '/');
814
- // Fallback: construct from classname
815
- if (testCaseItem.classname) {
816
- const parts = testCaseItem.classname.split('.');
817
- const className = parts[parts.length - 1];
818
- const namespacePath = parts.slice(0, -1).join('/');
819
- return `${namespacePath}/${className}.cs`;
820
- }
821
- return '';
822
- }
823
- function extractTestExplorerSuiteTitle(testCaseItem, item) {
824
- // Extract suite title to match Test Explorer structure (Project/Namespace hierarchy)
825
- // Priority: fullname > classname > name
826
- if (item.fullname) {
827
- // Use fullname to maintain Test Explorer structure
828
- return item.fullname;
829
- }
830
- if (testCaseItem.classname) {
831
- return testCaseItem.classname;
832
- }
833
- // Fallback to item name but prefer classname structure
834
- return item.name || testCaseItem.classname || 'UnknownClass';
835
- }
836
519
  function processTestSuite(testsuite) {
837
520
  if (!testsuite)
838
521
  return [];
@@ -844,38 +527,9 @@ function processTestSuite(testsuite) {
844
527
  if (!Array.isArray(testsuite)) {
845
528
  suites = [testsuite];
846
529
  }
847
- // Simple approach from version 2.1.1 with enhanced processing for complex scenarios
848
- const subSuites = suites.filter(s => s['test-suite'] && !s['test-case']);
530
+ const subSuites = suites.filter(s => s['test-suite'] && !testsuite['test-case']);
849
531
  return [...subSuites.map(s => processTestSuite(s['test-suite'])), ...suites.reduce(reduceTestCases, [])].flat();
850
532
  }
851
- function getSuiteTitle(testCaseItem, item, isParametrized, suiteOrganization = 'classname') {
852
- let suiteTitle;
853
- if (suiteOrganization === 'fullpath') {
854
- // Use full namespace path (old behavior that creates detailed structure)
855
- if (item.fullname) {
856
- return item.fullname;
857
- }
858
- suiteTitle = testCaseItem.classname || item.name;
859
- }
860
- else {
861
- // Use classname approach (default - avoids duplicates)
862
- if (isParametrized) {
863
- // For parameterized tests, use the class name to group them
864
- suiteTitle = item.name || testCaseItem.classname;
865
- }
866
- else {
867
- // For regular tests, prefer classname over fullname to avoid long paths
868
- suiteTitle = testCaseItem.classname || item.name;
869
- }
870
- // If still no suite title and we have fullname, extract just the class name
871
- if (!suiteTitle && item.fullname) {
872
- const fullnameParts = item.fullname.split('.');
873
- suiteTitle = fullnameParts[fullnameParts.length - 1]; // Just the class name
874
- }
875
- }
876
- // Fallback
877
- return suiteTitle || 'UnknownClass';
878
- }
879
533
  function fetchProperties(item) {
880
534
  const tags = [];
881
535
  let title = '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/reporter",
3
- "version": "2.3.5-beta-6-xml-import",
3
+ "version": "2.3.5-beta.8-xml-import",
4
4
  "description": "Testomatio Reporter Client",
5
5
  "engines": {
6
6
  "node": ">=18"
@@ -3,45 +3,18 @@ import Adapter from './adapter.js';
3
3
 
4
4
  class CSharpAdapter extends Adapter {
5
5
  formatTest(t) {
6
- // Don't override example if it already exists from NUnit XML processing
7
- // The xmlReader.js already extracts parameters correctly from <arguments>
8
- if (!t.example) {
9
- const title = t.title.replace(/\(.*?\)/, '').trim();
10
- const exampleMatch = t.title.match(/\((.*?)\)/);
11
- if (exampleMatch) {
12
- // Keep as array for consistency with NUnit XML processing
13
- t.example = exampleMatch[1].split(',').map(param => param.trim());
14
- }
15
- t.title = title.trim();
16
- }
17
-
6
+ const title = t.title.replace(/\(.*?\)/, '').trim();
7
+ const example = t.title.match(/\((.*?)\)/);
8
+ if (example) t.example = { ...example[1].split(',') };
18
9
  const suite = t.suite_title.split('.');
19
10
  t.suite_title = suite.pop();
20
11
  t.file = namespaceToFileName(t.file);
12
+ t.title = title.trim();
21
13
  return t;
22
14
  }
23
15
 
24
16
  getFilePath(t) {
25
- if (!t.file) return null;
26
-
27
- // Normalize path separators for cross-platform compatibility
28
- let filePath = t.file.replace(/\\/g, '/');
29
-
30
- // If file already has .cs extension, use it directly
31
- if (filePath.endsWith('.cs')) {
32
- // Make relative path if it's absolute
33
- if (path.isAbsolute(filePath)) {
34
- // Try to find project-relative path
35
- const cwd = process.cwd().replace(/\\/g, '/');
36
- if (filePath.startsWith(cwd)) {
37
- filePath = path.relative(cwd, filePath).replace(/\\/g, '/');
38
- }
39
- }
40
- return filePath;
41
- }
42
-
43
- // Convert namespace path to file path
44
- const fileName = namespaceToFileName(filePath);
17
+ const fileName = namespaceToFileName(t.file);
45
18
  return fileName;
46
19
  }
47
20
  }
@@ -49,14 +22,7 @@ class CSharpAdapter extends Adapter {
49
22
  export default CSharpAdapter;
50
23
 
51
24
  function namespaceToFileName(fileName) {
52
- if (!fileName) return '';
53
-
54
- // If already a .cs file path, clean it up
55
- if (fileName.endsWith('.cs')) {
56
- return fileName.replace(/\\/g, '/');
57
- }
58
-
59
25
  const fileParts = fileName.split('.');
60
26
  fileParts[fileParts.length - 1] = fileParts[fileParts.length - 1]?.replace(/\$.*/, '');
61
- return `${fileParts.join('/')}.cs`;
27
+ return `${fileParts.join(path.sep)}.cs`;
62
28
  }
package/src/pipe/debug.js CHANGED
@@ -15,7 +15,7 @@ export class DebugPipe {
15
15
  this.isEnabled = !!process.env.TESTOMATIO_DEBUG || !!process.env.DEBUG;
16
16
  if (this.isEnabled) {
17
17
  this.batch = {
18
- isEnabled: this.params.isBatchEnabled ?? (process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD ? false : true),
18
+ isEnabled: this.params.isBatchEnabled ?? !process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD ?? true,
19
19
  intervalFunction: null,
20
20
  intervalTime: 5000,
21
21
  tests: [],
@@ -93,7 +93,8 @@ export class DebugPipe {
93
93
  const logData = { action: 'addTest', testId: data };
94
94
  if (this.store.runId) logData.runId = this.store.runId;
95
95
  this.logToFile(logData);
96
- } else this.batch.tests.push(data);
96
+ }
97
+ else this.batch.tests.push(data);
97
98
 
98
99
  if (!this.batch.intervalFunction) await this.batchUpload();
99
100
  }