@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/README.md +1 -1
- package/lib/junit-adapter/csharp.d.ts +1 -0
- package/lib/junit-adapter/csharp.js +7 -36
- package/lib/pipe/debug.js +1 -1
- package/lib/pipe/testomatio.js +14 -18
- package/lib/utils/utils.js +4 -33
- package/lib/xmlReader.d.ts +0 -9
- package/lib/xmlReader.js +11 -357
- package/package.json +1 -1
- package/src/junit-adapter/csharp.js +6 -40
- package/src/pipe/debug.js +3 -2
- package/src/pipe/testomatio.js +80 -74
- package/src/utils/utils.js +3 -33
- package/src/xmlReader.js +10 -409
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ Testomat.io Reporter (this npm package) supports:
|
|
|
13
13
|
- π [Stack traces](./docs/stacktrace.md) and error messages
|
|
14
14
|
- π [GitHub](./docs/pipes/github.md), [GitLab](./docs/pipes/gitlab.md) & [Bitbucket](./docs/pipes/bitbucket.md) integration
|
|
15
15
|
- π
Realtime reports
|
|
16
|
-
- ποΈ Other test frameworks supported via [
|
|
16
|
+
- ποΈ Other test frameworks supported via [JUNit XML](./docs/junit.md)
|
|
17
17
|
- πΆββοΈ Steps _(work in progress)_
|
|
18
18
|
- π [Logger](./docs/logger.md) _(work in progress, supports Jest for now)_
|
|
19
19
|
- βοΈ Custom properties and metadata _(work in progress)_
|
|
@@ -7,53 +7,24 @@ const path_1 = __importDefault(require("path"));
|
|
|
7
7
|
const adapter_js_1 = __importDefault(require("./adapter.js"));
|
|
8
8
|
class CSharpAdapter extends adapter_js_1.default {
|
|
9
9
|
formatTest(t) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
if (
|
|
13
|
-
|
|
14
|
-
const exampleMatch = t.title.match(/\((.*?)\)/);
|
|
15
|
-
if (exampleMatch) {
|
|
16
|
-
// Keep as array for consistency with NUnit XML processing
|
|
17
|
-
t.example = exampleMatch[1].split(',').map(param => param.trim());
|
|
18
|
-
}
|
|
19
|
-
t.title = title.trim();
|
|
20
|
-
}
|
|
10
|
+
const title = t.title.replace(/\(.*?\)/, '').trim();
|
|
11
|
+
const example = t.title.match(/\((.*?)\)/);
|
|
12
|
+
if (example)
|
|
13
|
+
t.example = { ...example[1].split(',') };
|
|
21
14
|
const suite = t.suite_title.split('.');
|
|
22
15
|
t.suite_title = suite.pop();
|
|
23
16
|
t.file = namespaceToFileName(t.file);
|
|
17
|
+
t.title = title.trim();
|
|
24
18
|
return t;
|
|
25
19
|
}
|
|
26
20
|
getFilePath(t) {
|
|
27
|
-
|
|
28
|
-
return null;
|
|
29
|
-
// Normalize path separators for cross-platform compatibility
|
|
30
|
-
let filePath = t.file.replace(/\\/g, '/');
|
|
31
|
-
// If file already has .cs extension, use it directly
|
|
32
|
-
if (filePath.endsWith('.cs')) {
|
|
33
|
-
// Make relative path if it's absolute
|
|
34
|
-
if (path_1.default.isAbsolute(filePath)) {
|
|
35
|
-
// Try to find project-relative path
|
|
36
|
-
const cwd = process.cwd().replace(/\\/g, '/');
|
|
37
|
-
if (filePath.startsWith(cwd)) {
|
|
38
|
-
filePath = path_1.default.relative(cwd, filePath).replace(/\\/g, '/');
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return filePath;
|
|
42
|
-
}
|
|
43
|
-
// Convert namespace path to file path
|
|
44
|
-
const fileName = namespaceToFileName(filePath);
|
|
21
|
+
const fileName = namespaceToFileName(t.file);
|
|
45
22
|
return fileName;
|
|
46
23
|
}
|
|
47
24
|
}
|
|
48
25
|
module.exports = CSharpAdapter;
|
|
49
26
|
function namespaceToFileName(fileName) {
|
|
50
|
-
if (!fileName)
|
|
51
|
-
return '';
|
|
52
|
-
// If already a .cs file path, clean it up
|
|
53
|
-
if (fileName.endsWith('.cs')) {
|
|
54
|
-
return fileName.replace(/\\/g, '/');
|
|
55
|
-
}
|
|
56
27
|
const fileParts = fileName.split('.');
|
|
57
28
|
fileParts[fileParts.length - 1] = fileParts[fileParts.length - 1]?.replace(/\$.*/, '');
|
|
58
|
-
return `${fileParts.join(
|
|
29
|
+
return `${fileParts.join(path_1.default.sep)}.cs`;
|
|
59
30
|
}
|
package/lib/pipe/debug.js
CHANGED
|
@@ -18,7 +18,7 @@ class DebugPipe {
|
|
|
18
18
|
this.isEnabled = !!process.env.TESTOMATIO_DEBUG || !!process.env.DEBUG;
|
|
19
19
|
if (this.isEnabled) {
|
|
20
20
|
this.batch = {
|
|
21
|
-
isEnabled: this.params.isBatchEnabled ??
|
|
21
|
+
isEnabled: this.params.isBatchEnabled ?? !process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD ?? true,
|
|
22
22
|
intervalFunction: null,
|
|
23
23
|
intervalTime: 5000,
|
|
24
24
|
tests: [],
|
package/lib/pipe/testomatio.js
CHANGED
|
@@ -23,7 +23,7 @@ if (process.env.TESTOMATIO_RUN)
|
|
|
23
23
|
class TestomatioPipe {
|
|
24
24
|
constructor(params, store) {
|
|
25
25
|
this.batch = {
|
|
26
|
-
isEnabled: params?.isBatchEnabled ??
|
|
26
|
+
isEnabled: params?.isBatchEnabled ?? !process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD ?? true,
|
|
27
27
|
intervalFunction: null, // will be created in createRun by setInterval function
|
|
28
28
|
intervalTime: 5000, // how often tests are sent
|
|
29
29
|
tests: [], // array of tests in batch
|
|
@@ -60,7 +60,7 @@ class TestomatioPipe {
|
|
|
60
60
|
retry: constants_js_1.REPORTER_REQUEST_RETRIES.retriesPerRequest,
|
|
61
61
|
retryDelay: constants_js_1.REPORTER_REQUEST_RETRIES.retryTimeout,
|
|
62
62
|
httpMethodsToRetry: ['GET', 'PUT', 'HEAD', 'OPTIONS', 'DELETE', 'POST'],
|
|
63
|
-
shouldRetry: error => {
|
|
63
|
+
shouldRetry: (error) => {
|
|
64
64
|
if (!error.response)
|
|
65
65
|
return false;
|
|
66
66
|
switch (error.response?.status) {
|
|
@@ -73,8 +73,8 @@ class TestomatioPipe {
|
|
|
73
73
|
break;
|
|
74
74
|
}
|
|
75
75
|
return error.response?.status >= 401; // Retry on 401+ and 5xx
|
|
76
|
-
}
|
|
77
|
-
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
78
|
});
|
|
79
79
|
this.isEnabled = true;
|
|
80
80
|
// do not finish this run (for parallel testing)
|
|
@@ -193,7 +193,7 @@ class TestomatioPipe {
|
|
|
193
193
|
method: 'PUT',
|
|
194
194
|
url: `/api/reporter/${this.runId}`,
|
|
195
195
|
data: runParams,
|
|
196
|
-
responseType: 'json'
|
|
196
|
+
responseType: 'json'
|
|
197
197
|
});
|
|
198
198
|
if (resp.data.artifacts)
|
|
199
199
|
(0, pipe_utils_js_1.setS3Credentials)(resp.data.artifacts);
|
|
@@ -206,7 +206,7 @@ class TestomatioPipe {
|
|
|
206
206
|
url: '/api/reporter',
|
|
207
207
|
data: runParams,
|
|
208
208
|
maxContentLength: Infinity,
|
|
209
|
-
responseType: 'json'
|
|
209
|
+
responseType: 'json'
|
|
210
210
|
});
|
|
211
211
|
this.runId = resp.data.uid;
|
|
212
212
|
this.runUrl = `${this.url}/${resp.data.url.split('/').splice(3).join('/')}`;
|
|
@@ -259,17 +259,15 @@ class TestomatioPipe {
|
|
|
259
259
|
this.#formatData(data);
|
|
260
260
|
const json = json_cycle_1.default.stringify(data);
|
|
261
261
|
debug('Adding test', json);
|
|
262
|
-
return this.client
|
|
263
|
-
.request({
|
|
262
|
+
return this.client.request({
|
|
264
263
|
method: 'POST',
|
|
265
264
|
url: `/api/reporter/${this.runId}/testrun`,
|
|
266
265
|
data: json,
|
|
267
266
|
headers: {
|
|
268
267
|
'Content-Type': 'application/json',
|
|
269
268
|
},
|
|
270
|
-
maxContentLength: Infinity
|
|
271
|
-
})
|
|
272
|
-
.catch(err => {
|
|
269
|
+
maxContentLength: Infinity
|
|
270
|
+
}).catch(err => {
|
|
273
271
|
this.requestFailures++;
|
|
274
272
|
this.notReportedTestsCount++;
|
|
275
273
|
if (err.response) {
|
|
@@ -314,21 +312,19 @@ class TestomatioPipe {
|
|
|
314
312
|
// get tests from batch and clear batch
|
|
315
313
|
const testsToSend = this.batch.tests.splice(0);
|
|
316
314
|
debug('π¨ Batch upload', testsToSend.length, 'tests');
|
|
317
|
-
return this.client
|
|
318
|
-
.request({
|
|
315
|
+
return this.client.request({
|
|
319
316
|
method: 'POST',
|
|
320
317
|
url: `/api/reporter/${this.runId}/testrun`,
|
|
321
318
|
data: {
|
|
322
319
|
api_key: this.apiKey,
|
|
323
320
|
tests: testsToSend,
|
|
324
|
-
batch_index: this.batch.batchIndex
|
|
321
|
+
batch_index: this.batch.batchIndex
|
|
325
322
|
},
|
|
326
323
|
headers: {
|
|
327
324
|
'Content-Type': 'application/json',
|
|
328
325
|
},
|
|
329
|
-
maxContentLength: Infinity
|
|
330
|
-
})
|
|
331
|
-
.catch(err => {
|
|
326
|
+
maxContentLength: Infinity
|
|
327
|
+
}).catch(err => {
|
|
332
328
|
this.requestFailures++;
|
|
333
329
|
this.notReportedTestsCount += testsToSend.length;
|
|
334
330
|
if (err.response) {
|
|
@@ -412,7 +408,7 @@ class TestomatioPipe {
|
|
|
412
408
|
status_event,
|
|
413
409
|
detach: params.detach,
|
|
414
410
|
tests: params.tests,
|
|
415
|
-
}
|
|
411
|
+
}
|
|
416
412
|
});
|
|
417
413
|
console.log(constants_js_1.APP_PREFIX, 'β
Testrun finished');
|
|
418
414
|
if (this.runUrl) {
|
package/lib/utils/utils.js
CHANGED
|
@@ -173,8 +173,6 @@ exports.fetchSourceCodeFromStackTrace = fetchSourceCodeFromStackTrace;
|
|
|
173
173
|
exports.TEST_ID_REGEX = /@T([\w\d]{8})/;
|
|
174
174
|
exports.SUITE_ID_REGEX = /@S([\w\d]{8})/;
|
|
175
175
|
const fetchIdFromCode = (code, opts = {}) => {
|
|
176
|
-
if (!code)
|
|
177
|
-
return null;
|
|
178
176
|
const comments = code
|
|
179
177
|
.split('\n')
|
|
180
178
|
.map(l => l.trim())
|
|
@@ -217,29 +215,10 @@ const fetchSourceCode = (contents, opts = {}) => {
|
|
|
217
215
|
lineIndex = lines.findIndex(l => l.includes(`${title}(`));
|
|
218
216
|
}
|
|
219
217
|
else if (opts.lang === 'csharp') {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
if (lineIndex === -1)
|
|
223
|
-
lineIndex = lines.findIndex(l => l.includes(`public async Task ${title}(`));
|
|
224
|
-
}
|
|
225
|
-
if (lineIndex === -1) {
|
|
218
|
+
if (lineIndex === -1)
|
|
219
|
+
lineIndex = lines.findIndex(l => l.includes(`public void ${title}`));
|
|
220
|
+
if (lineIndex === -1)
|
|
226
221
|
lineIndex = lines.findIndex(l => l.includes(`${title}(`));
|
|
227
|
-
}
|
|
228
|
-
// Look for TestCase or Test attributes above the method
|
|
229
|
-
if (lineIndex === -1) {
|
|
230
|
-
const testAttributeIndex = lines.findIndex((l, index) => {
|
|
231
|
-
if (l.includes('[TestCase') || l.includes('[Test')) {
|
|
232
|
-
// Check next few lines for the method
|
|
233
|
-
const nextLines = lines.slice(index, Math.min(lines.length, index + 5));
|
|
234
|
-
const hasMethod = nextLines.some(nextLine => nextLine.includes(`${title}(`));
|
|
235
|
-
return hasMethod;
|
|
236
|
-
}
|
|
237
|
-
return false;
|
|
238
|
-
});
|
|
239
|
-
if (testAttributeIndex !== -1) {
|
|
240
|
-
lineIndex = testAttributeIndex;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
222
|
}
|
|
244
223
|
else {
|
|
245
224
|
lineIndex = lines.findIndex(l => l.includes(title));
|
|
@@ -248,7 +227,7 @@ const fetchSourceCode = (contents, opts = {}) => {
|
|
|
248
227
|
if (opts.prepend) {
|
|
249
228
|
lineIndex -= opts.prepend;
|
|
250
229
|
}
|
|
251
|
-
if (lineIndex
|
|
230
|
+
if (lineIndex) {
|
|
252
231
|
const result = [];
|
|
253
232
|
for (let i = lineIndex; i < lineIndex + limit; i++) {
|
|
254
233
|
if (lines[i] === undefined)
|
|
@@ -291,14 +270,6 @@ const fetchSourceCode = (contents, opts = {}) => {
|
|
|
291
270
|
break;
|
|
292
271
|
if (opts.lang === 'java' && lines[i].includes(' class '))
|
|
293
272
|
break;
|
|
294
|
-
if (opts.lang === 'csharp' && lines[i].trim().match(/^\[Test/))
|
|
295
|
-
break;
|
|
296
|
-
if (opts.lang === 'csharp' && lines[i].includes(' public void '))
|
|
297
|
-
break;
|
|
298
|
-
if (opts.lang === 'csharp' && lines[i].includes(' public async Task '))
|
|
299
|
-
break;
|
|
300
|
-
if (opts.lang === 'csharp' && lines[i].includes(' class ') && lines[i].includes('public'))
|
|
301
|
-
break;
|
|
302
273
|
}
|
|
303
274
|
result.push(lines[i]);
|
|
304
275
|
}
|
package/lib/xmlReader.d.ts
CHANGED
|
@@ -13,8 +13,6 @@ declare class XmlReader {
|
|
|
13
13
|
runId: any;
|
|
14
14
|
adapter: import("./junit-adapter/adapter.js").default;
|
|
15
15
|
opts: {};
|
|
16
|
-
disableSourceCodeFetching: any;
|
|
17
|
-
suiteOrganization: any;
|
|
18
16
|
store: {};
|
|
19
17
|
pipesPromise: Promise<any[]>;
|
|
20
18
|
parser: XMLParser;
|
|
@@ -79,13 +77,6 @@ declare class XmlReader {
|
|
|
79
77
|
skipped_count: number;
|
|
80
78
|
tests: any[];
|
|
81
79
|
};
|
|
82
|
-
deduplicateTestsByFQN(tests: any): any[];
|
|
83
|
-
generateFQN(test: any): string;
|
|
84
|
-
generateNormalizedFQN(test: any): string;
|
|
85
|
-
extractAssemblyName(test: any): any;
|
|
86
|
-
extractNamespace(test: any): any;
|
|
87
|
-
extractClassName(test: any): any;
|
|
88
|
-
extractCsFileFromPath(test: any): any;
|
|
89
80
|
calculateStats(): {};
|
|
90
81
|
fetchSourceCode(): void;
|
|
91
82
|
formatTests(): void;
|