@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.
- package/README.md +1 -1
- package/lib/adapter/codecept.js +2 -3
- package/lib/bin/reportXml.js +4 -1
- package/lib/client.js +1 -1
- package/lib/junit-adapter/csharp.d.ts +0 -1
- package/lib/junit-adapter/csharp.js +36 -7
- package/lib/junit-adapter/nunit-parser.d.ts +82 -0
- package/lib/junit-adapter/nunit-parser.js +357 -0
- package/lib/pipe/debug.js +1 -1
- package/lib/pipe/testomatio.js +19 -14
- package/lib/uploader.js +4 -0
- package/lib/utils/utils.d.ts +0 -1
- package/lib/utils/utils.js +35 -19
- package/lib/xmlReader.d.ts +11 -26
- package/lib/xmlReader.js +50 -1
- package/package.json +1 -1
- package/src/adapter/codecept.js +3 -5
- package/src/bin/reportXml.js +4 -1
- package/src/client.js +2 -2
- package/src/junit-adapter/csharp.js +40 -6
- package/src/junit-adapter/nunit-parser.js +391 -0
- package/src/pipe/debug.js +2 -3
- package/src/pipe/testomatio.js +76 -80
- package/src/uploader.js +5 -0
- package/src/utils/utils.js +35 -17
- package/src/xmlReader.js +62 -1
package/lib/uploader.js
CHANGED
|
@@ -170,6 +170,10 @@ 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
|
+
}
|
|
173
177
|
const data = { rid, file: filePath, uploaded };
|
|
174
178
|
const jsonLine = `${JSON.stringify(data)}\n`;
|
|
175
179
|
fs_1.default.appendFileSync(tempFilePath, jsonLine);
|
package/lib/utils/utils.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ export function getPackageVersion(): any;
|
|
|
2
2
|
export const TEST_ID_REGEX: RegExp;
|
|
3
3
|
export const SUITE_ID_REGEX: RegExp;
|
|
4
4
|
export function ansiRegExp(): RegExp;
|
|
5
|
-
export function truncate(s: any, size?: number): any;
|
|
6
5
|
export function cleanLatestRunId(): any;
|
|
7
6
|
export function isSameTest(test: any, t: any): boolean;
|
|
8
7
|
export function fetchSourceCode(contents: any, opts?: {}): string;
|
package/lib/utils/utils.js
CHANGED
|
@@ -38,7 +38,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.validateSuiteId = exports.testRunnerHelper = exports.specificTestInfo = exports.parseSuite = exports.isValidUrl = exports.humanize = exports.getTestomatIdFromTestTitle = exports.getCurrentDateTime = exports.foundedTestLog = exports.fileSystem = exports.fetchFilesFromStackTrace = exports.fetchIdFromOutput = exports.fetchIdFromCode = exports.fetchSourceCodeFromStackTrace = exports.fetchSourceCode = exports.isSameTest = exports.ansiRegExp = exports.SUITE_ID_REGEX = exports.TEST_ID_REGEX = void 0;
|
|
40
40
|
exports.getPackageVersion = getPackageVersion;
|
|
41
|
-
exports.truncate = truncate;
|
|
42
41
|
exports.cleanLatestRunId = cleanLatestRunId;
|
|
43
42
|
exports.formatStep = formatStep;
|
|
44
43
|
exports.readLatestRunId = readLatestRunId;
|
|
@@ -122,12 +121,8 @@ const fetchFilesFromStackTrace = (stack = '', checkExists = true) => {
|
|
|
122
121
|
.map(f => f[1].trim())
|
|
123
122
|
.map(f => f.replace(/^\/+/, '/').replace(/^\/([A-Za-z]:)/, '$1')) // Remove extra slashes, handle Windows paths
|
|
124
123
|
.map(f => {
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
-
// Convert Windows path to Linux equivalent for test scenarios
|
|
128
|
-
return f.replace(/^[A-Za-z]:[\\\/]/, '/').replace(/\\/g, '/');
|
|
129
|
-
}
|
|
130
|
-
return f;
|
|
124
|
+
// Normalize path separators for cross-platform compatibility
|
|
125
|
+
return f.replace(/\\/g, '/');
|
|
131
126
|
});
|
|
132
127
|
debug('Found files in stack trace: ', files);
|
|
133
128
|
return files.filter(f => {
|
|
@@ -174,6 +169,8 @@ exports.fetchSourceCodeFromStackTrace = fetchSourceCodeFromStackTrace;
|
|
|
174
169
|
exports.TEST_ID_REGEX = /@T([\w\d]{8})/;
|
|
175
170
|
exports.SUITE_ID_REGEX = /@S([\w\d]{8})/;
|
|
176
171
|
const fetchIdFromCode = (code, opts = {}) => {
|
|
172
|
+
if (!code)
|
|
173
|
+
return null;
|
|
177
174
|
const comments = code
|
|
178
175
|
.split('\n')
|
|
179
176
|
.map(l => l.trim())
|
|
@@ -216,10 +213,29 @@ const fetchSourceCode = (contents, opts = {}) => {
|
|
|
216
213
|
lineIndex = lines.findIndex(l => l.includes(`${title}(`));
|
|
217
214
|
}
|
|
218
215
|
else if (opts.lang === 'csharp') {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if (lineIndex === -1)
|
|
216
|
+
// Enhanced C# method detection for NUnit tests
|
|
217
|
+
lineIndex = lines.findIndex(l => l.includes(`public void ${title}(`));
|
|
218
|
+
if (lineIndex === -1) {
|
|
219
|
+
lineIndex = lines.findIndex(l => l.includes(`public async Task ${title}(`));
|
|
220
|
+
}
|
|
221
|
+
if (lineIndex === -1) {
|
|
222
222
|
lineIndex = lines.findIndex(l => l.includes(`${title}(`));
|
|
223
|
+
}
|
|
224
|
+
// Look for TestCase or Test attributes above the method
|
|
225
|
+
if (lineIndex === -1) {
|
|
226
|
+
const testAttributeIndex = lines.findIndex((l, index) => {
|
|
227
|
+
if (l.includes('[TestCase') || l.includes('[Test')) {
|
|
228
|
+
// Check next few lines for the method
|
|
229
|
+
const nextLines = lines.slice(index, Math.min(lines.length, index + 5));
|
|
230
|
+
const hasMethod = nextLines.some(nextLine => nextLine.includes(`${title}(`));
|
|
231
|
+
return hasMethod;
|
|
232
|
+
}
|
|
233
|
+
return false;
|
|
234
|
+
});
|
|
235
|
+
if (testAttributeIndex !== -1) {
|
|
236
|
+
lineIndex = testAttributeIndex;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
223
239
|
}
|
|
224
240
|
else {
|
|
225
241
|
lineIndex = lines.findIndex(l => l.includes(title));
|
|
@@ -228,7 +244,7 @@ const fetchSourceCode = (contents, opts = {}) => {
|
|
|
228
244
|
if (opts.prepend) {
|
|
229
245
|
lineIndex -= opts.prepend;
|
|
230
246
|
}
|
|
231
|
-
if (lineIndex) {
|
|
247
|
+
if (lineIndex !== -1 && lineIndex !== undefined) {
|
|
232
248
|
const result = [];
|
|
233
249
|
for (let i = lineIndex; i < lineIndex + limit; i++) {
|
|
234
250
|
if (lines[i] === undefined)
|
|
@@ -271,6 +287,14 @@ const fetchSourceCode = (contents, opts = {}) => {
|
|
|
271
287
|
break;
|
|
272
288
|
if (opts.lang === 'java' && lines[i].includes(' class '))
|
|
273
289
|
break;
|
|
290
|
+
if (opts.lang === 'csharp' && lines[i].trim().match(/^\[Test/))
|
|
291
|
+
break;
|
|
292
|
+
if (opts.lang === 'csharp' && lines[i].includes(' public void '))
|
|
293
|
+
break;
|
|
294
|
+
if (opts.lang === 'csharp' && lines[i].includes(' public async Task '))
|
|
295
|
+
break;
|
|
296
|
+
if (opts.lang === 'csharp' && lines[i].includes(' class ') && lines[i].includes('public'))
|
|
297
|
+
break;
|
|
274
298
|
}
|
|
275
299
|
result.push(lines[i]);
|
|
276
300
|
}
|
|
@@ -470,17 +494,9 @@ function transformEnvVarToBoolean(value) {
|
|
|
470
494
|
// if not recognized, return truthy if any value is set
|
|
471
495
|
return Boolean(value);
|
|
472
496
|
}
|
|
473
|
-
function truncate(s, size = 255) {
|
|
474
|
-
if (s.toString().trim().length < size) {
|
|
475
|
-
return s.toString();
|
|
476
|
-
}
|
|
477
|
-
return `${s.toString().substring(0, size)}...`;
|
|
478
|
-
}
|
|
479
497
|
|
|
480
498
|
module.exports.getPackageVersion = getPackageVersion;
|
|
481
499
|
|
|
482
|
-
module.exports.truncate = truncate;
|
|
483
|
-
|
|
484
500
|
module.exports.cleanLatestRunId = cleanLatestRunId;
|
|
485
501
|
|
|
486
502
|
module.exports.formatStep = formatStep;
|
package/lib/xmlReader.d.ts
CHANGED
|
@@ -19,25 +19,11 @@ declare class XmlReader {
|
|
|
19
19
|
tests: any[];
|
|
20
20
|
stats: {};
|
|
21
21
|
uploader: S3Uploader;
|
|
22
|
+
enhancedNunit: boolean;
|
|
23
|
+
groupParameterized: boolean;
|
|
22
24
|
version: any;
|
|
23
25
|
connectAdapter(): import("./junit-adapter/adapter.js").default;
|
|
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
|
-
};
|
|
26
|
+
parse(fileName: any): any;
|
|
41
27
|
processJUnit(jsonSuite: any): {
|
|
42
28
|
create_tests: boolean;
|
|
43
29
|
duration: number;
|
|
@@ -49,15 +35,14 @@ declare class XmlReader {
|
|
|
49
35
|
tests: any[];
|
|
50
36
|
tests_count: number;
|
|
51
37
|
};
|
|
52
|
-
processNUnit(jsonSuite: any):
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
};
|
|
38
|
+
processNUnit(jsonSuite: any): any;
|
|
39
|
+
/**
|
|
40
|
+
* Check if the XML is actually NUnit format (has test-suite hierarchy)
|
|
41
|
+
* @param {Object} jsonSuite - Parsed XML suite object
|
|
42
|
+
* @returns {boolean} - True if this is NUnit XML format
|
|
43
|
+
*/
|
|
44
|
+
isNUnitXml(jsonSuite: any): boolean;
|
|
45
|
+
processNUnitEnhanced(jsonSuite: any): any;
|
|
61
46
|
processTRX(jsonSuite: any): {
|
|
62
47
|
status: string;
|
|
63
48
|
create_tests: boolean;
|
package/lib/xmlReader.js
CHANGED
|
@@ -11,6 +11,7 @@ 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");
|
|
14
15
|
const utils_js_1 = require("./utils/utils.js");
|
|
15
16
|
const index_js_1 = require("./pipe/index.js");
|
|
16
17
|
const index_js_2 = __importDefault(require("./junit-adapter/index.js"));
|
|
@@ -54,6 +55,9 @@ class XmlReader {
|
|
|
54
55
|
this.stats = {};
|
|
55
56
|
this.stats.language = opts.lang?.toLowerCase();
|
|
56
57
|
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
|
|
57
61
|
// @ts-ignore
|
|
58
62
|
const packageJsonPath = path_1.default.resolve(__dirname, '..', 'package.json');
|
|
59
63
|
this.version = JSON.parse(fs_1.default.readFileSync(packageJsonPath).toString()).version;
|
|
@@ -102,7 +106,8 @@ class XmlReader {
|
|
|
102
106
|
return this.processJUnit(jsonSuite);
|
|
103
107
|
}
|
|
104
108
|
processJUnit(jsonSuite) {
|
|
105
|
-
const { testsuite, name,
|
|
109
|
+
const { testsuite, name, failures, errors } = jsonSuite;
|
|
110
|
+
const tests = testsuite?.tests || jsonSuite.tests;
|
|
106
111
|
reduceOptions.preferClassname = this.stats.language === 'python';
|
|
107
112
|
const resultTests = processTestSuite(testsuite);
|
|
108
113
|
const hasFailures = resultTests.filter(t => t.status === 'failed').length > 0;
|
|
@@ -128,6 +133,13 @@ class XmlReader {
|
|
|
128
133
|
};
|
|
129
134
|
}
|
|
130
135
|
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');
|
|
131
143
|
const { result, total, passed, failed, inconclusive, skipped } = jsonSuite;
|
|
132
144
|
reduceOptions.preferClassname = this.stats.language === 'python';
|
|
133
145
|
const resultTests = processTestSuite(jsonSuite['test-suite']);
|
|
@@ -142,6 +154,43 @@ class XmlReader {
|
|
|
142
154
|
tests: resultTests,
|
|
143
155
|
};
|
|
144
156
|
}
|
|
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
|
+
}
|
|
145
194
|
processTRX(jsonSuite) {
|
|
146
195
|
let defs = jsonSuite?.TestRun?.TestDefinitions?.UnitTest;
|
|
147
196
|
if (!Array.isArray(defs))
|
package/package.json
CHANGED
package/src/adapter/codecept.js
CHANGED
|
@@ -2,7 +2,7 @@ import createDebugMessages from 'debug';
|
|
|
2
2
|
import pc from 'picocolors';
|
|
3
3
|
import TestomatClient from '../client.js';
|
|
4
4
|
import { STATUS, APP_PREFIX, TESTOMAT_TMP_STORAGE_DIR } from '../constants.js';
|
|
5
|
-
import { getTestomatIdFromTestTitle,
|
|
5
|
+
import { getTestomatIdFromTestTitle, fileSystem } from '../utils/utils.js';
|
|
6
6
|
import { services } from '../services/index.js';
|
|
7
7
|
import { dataStorage } from '../data-storage.js';
|
|
8
8
|
import codeceptjs from 'codeceptjs';
|
|
@@ -441,7 +441,7 @@ function formatCodeceptStep(step) {
|
|
|
441
441
|
if (!step) return null;
|
|
442
442
|
|
|
443
443
|
const category = step.constructor.name === 'HelperStep' ? 'framework' : 'user';
|
|
444
|
-
const title =
|
|
444
|
+
const title = step.toString(); // Use built-in toString
|
|
445
445
|
const duration = step.duration || 0; // Use built-in duration
|
|
446
446
|
|
|
447
447
|
const formattedStep = {
|
|
@@ -469,11 +469,10 @@ function formatHookStep(step) {
|
|
|
469
469
|
if (step.actor && step.name) {
|
|
470
470
|
title = `${step.actor} ${step.name}`;
|
|
471
471
|
if (step.args && step.args.length > 0) {
|
|
472
|
-
const argsStr = step.args.map(arg =>
|
|
472
|
+
const argsStr = step.args.map(arg => JSON.stringify(arg)).join(', ');
|
|
473
473
|
title += ` ${argsStr}`;
|
|
474
474
|
}
|
|
475
475
|
}
|
|
476
|
-
title = truncate(title);
|
|
477
476
|
|
|
478
477
|
return {
|
|
479
478
|
category: 'hook',
|
|
@@ -482,6 +481,5 @@ function formatHookStep(step) {
|
|
|
482
481
|
};
|
|
483
482
|
}
|
|
484
483
|
|
|
485
|
-
|
|
486
484
|
export { CodeceptReporter };
|
|
487
485
|
export default CodeceptReporter;
|
package/src/bin/reportXml.js
CHANGED
|
@@ -34,7 +34,10 @@ 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({
|
|
37
|
+
const runReader = new XmlReader({
|
|
38
|
+
javaTests,
|
|
39
|
+
lang,
|
|
40
|
+
});
|
|
38
41
|
const files = glob.sync(pattern, { cwd: opts.dir || process.cwd() });
|
|
39
42
|
if (!files.length) {
|
|
40
43
|
console.log(APP_PREFIX, `Report can't be created. No XML files found 😥`);
|
package/src/client.js
CHANGED
|
@@ -10,7 +10,7 @@ import { glob } from 'glob';
|
|
|
10
10
|
import path, { sep } from 'path';
|
|
11
11
|
import { fileURLToPath } from 'node:url';
|
|
12
12
|
import { S3Uploader } from './uploader.js';
|
|
13
|
-
import { formatStep,
|
|
13
|
+
import { formatStep, readLatestRunId, storeRunId, validateSuiteId } from './utils/utils.js';
|
|
14
14
|
import { filesize as prettyBytes } from 'filesize';
|
|
15
15
|
|
|
16
16
|
const debug = createDebugMessages('@testomatio/reporter:client');
|
|
@@ -387,7 +387,7 @@ class Client {
|
|
|
387
387
|
*/
|
|
388
388
|
formatLogs({ error, steps, logs }) {
|
|
389
389
|
error = error?.trim();
|
|
390
|
-
logs = logs?.trim()
|
|
390
|
+
logs = logs?.trim();
|
|
391
391
|
|
|
392
392
|
if (Array.isArray(steps)) {
|
|
393
393
|
steps = steps
|
|
@@ -3,18 +3,45 @@ import Adapter from './adapter.js';
|
|
|
3
3
|
|
|
4
4
|
class CSharpAdapter extends Adapter {
|
|
5
5
|
formatTest(t) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
if (
|
|
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
|
+
|
|
9
18
|
const suite = t.suite_title.split('.');
|
|
10
19
|
t.suite_title = suite.pop();
|
|
11
20
|
t.file = namespaceToFileName(t.file);
|
|
12
|
-
t.title = title.trim();
|
|
13
21
|
return t;
|
|
14
22
|
}
|
|
15
23
|
|
|
16
24
|
getFilePath(t) {
|
|
17
|
-
|
|
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);
|
|
18
45
|
return fileName;
|
|
19
46
|
}
|
|
20
47
|
}
|
|
@@ -22,7 +49,14 @@ class CSharpAdapter extends Adapter {
|
|
|
22
49
|
export default CSharpAdapter;
|
|
23
50
|
|
|
24
51
|
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
|
+
|
|
25
59
|
const fileParts = fileName.split('.');
|
|
26
60
|
fileParts[fileParts.length - 1] = fileParts[fileParts.length - 1]?.replace(/\$.*/, '');
|
|
27
|
-
return `${fileParts.join(
|
|
61
|
+
return `${fileParts.join('/')}.cs`;
|
|
28
62
|
}
|