@testomatio/reporter 2.3.1-beta.1-dependency → 2.3.2-beta.3-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/data-storage.d.ts +1 -1
- package/lib/junit-adapter/csharp.d.ts +0 -1
- package/lib/junit-adapter/csharp.js +36 -7
- package/lib/pipe/debug.js +1 -1
- package/lib/pipe/testomatio.js +14 -18
- package/lib/reporter.d.ts +12 -12
- package/lib/services/artifacts.d.ts +1 -1
- package/lib/services/key-values.d.ts +1 -1
- package/lib/services/links.d.ts +1 -1
- package/lib/services/logger.d.ts +1 -1
- package/lib/utils/utils.js +33 -4
- package/lib/xmlReader.d.ts +8 -1
- package/lib/xmlReader.js +317 -13
- package/package.json +28 -37
- package/src/junit-adapter/csharp.js +40 -6
- package/src/pipe/debug.js +3 -2
- package/src/pipe/testomatio.js +73 -74
- package/src/utils/utils.js +34 -4
- package/src/xmlReader.js +367 -13
package/src/pipe/testomatio.js
CHANGED
|
@@ -20,7 +20,7 @@ if (process.env.TESTOMATIO_RUN) process.env.runId = process.env.TESTOMATIO_RUN;
|
|
|
20
20
|
class TestomatioPipe {
|
|
21
21
|
constructor(params, store) {
|
|
22
22
|
this.batch = {
|
|
23
|
-
isEnabled: params?.isBatchEnabled ?? !process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD,
|
|
23
|
+
isEnabled: params?.isBatchEnabled ?? !process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD ?? true,
|
|
24
24
|
intervalFunction: null, // will be created in createRun by setInterval function
|
|
25
25
|
intervalTime: 5000, // how often tests are sent
|
|
26
26
|
tests: [], // array of tests in batch
|
|
@@ -60,8 +60,8 @@ class TestomatioPipe {
|
|
|
60
60
|
retryConfig: {
|
|
61
61
|
retry: REPORTER_REQUEST_RETRIES.retriesPerRequest,
|
|
62
62
|
retryDelay: REPORTER_REQUEST_RETRIES.retryTimeout,
|
|
63
|
-
httpMethodsToRetry: ['GET',
|
|
64
|
-
shouldRetry: error => {
|
|
63
|
+
httpMethodsToRetry: ['GET','PUT','HEAD','OPTIONS','DELETE','POST'],
|
|
64
|
+
shouldRetry: (error) => {
|
|
65
65
|
if (!error.response) return false;
|
|
66
66
|
switch (error.response?.status) {
|
|
67
67
|
case 400: // Bad request (probably wrong API key)
|
|
@@ -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
|
|
|
80
80
|
this.isEnabled = true;
|
|
@@ -186,7 +186,7 @@ class TestomatioPipe {
|
|
|
186
186
|
method: 'PUT',
|
|
187
187
|
url: `/api/reporter/${this.runId}`,
|
|
188
188
|
data: runParams,
|
|
189
|
-
responseType: 'json'
|
|
189
|
+
responseType: 'json'
|
|
190
190
|
});
|
|
191
191
|
if (resp.data.artifacts) setS3Credentials(resp.data.artifacts);
|
|
192
192
|
return;
|
|
@@ -199,7 +199,7 @@ class TestomatioPipe {
|
|
|
199
199
|
url: '/api/reporter',
|
|
200
200
|
data: runParams,
|
|
201
201
|
maxContentLength: Infinity,
|
|
202
|
-
responseType: 'json'
|
|
202
|
+
responseType: 'json'
|
|
203
203
|
});
|
|
204
204
|
|
|
205
205
|
this.runId = resp.data.uid;
|
|
@@ -263,42 +263,40 @@ class TestomatioPipe {
|
|
|
263
263
|
|
|
264
264
|
debug('Adding test', json);
|
|
265
265
|
|
|
266
|
-
return this.client
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
.
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
if (err.response.status >= 400) {
|
|
281
|
-
const responseData = err.response.data || { message: '' };
|
|
282
|
-
console.log(
|
|
283
|
-
APP_PREFIX,
|
|
284
|
-
pc.yellow(`Warning: ${responseData.message} (${err.response.status})`),
|
|
285
|
-
pc.gray(data?.title || ''),
|
|
286
|
-
);
|
|
287
|
-
if (err.response?.data?.message?.includes('could not be matched')) {
|
|
288
|
-
this.hasUnmatchedTests = true;
|
|
289
|
-
}
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
266
|
+
return this.client.request({
|
|
267
|
+
method: 'POST',
|
|
268
|
+
url: `/api/reporter/${this.runId}/testrun`,
|
|
269
|
+
data: json,
|
|
270
|
+
headers: {
|
|
271
|
+
'Content-Type': 'application/json',
|
|
272
|
+
},
|
|
273
|
+
maxContentLength: Infinity
|
|
274
|
+
}).catch(err => {
|
|
275
|
+
this.requestFailures++;
|
|
276
|
+
this.notReportedTestsCount++;
|
|
277
|
+
if (err.response) {
|
|
278
|
+
if (err.response.status >= 400) {
|
|
279
|
+
const responseData = err.response.data || { message: '' };
|
|
292
280
|
console.log(
|
|
293
281
|
APP_PREFIX,
|
|
294
|
-
pc.yellow(`Warning: ${
|
|
295
|
-
|
|
282
|
+
pc.yellow(`Warning: ${responseData.message} (${err.response.status})`),
|
|
283
|
+
pc.gray(data?.title || ''),
|
|
296
284
|
);
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
285
|
+
if (err.response?.data?.message?.includes('could not be matched')) {
|
|
286
|
+
this.hasUnmatchedTests = true;
|
|
287
|
+
}
|
|
288
|
+
return;
|
|
300
289
|
}
|
|
301
|
-
|
|
290
|
+
console.log(
|
|
291
|
+
APP_PREFIX,
|
|
292
|
+
pc.yellow(`Warning: ${data?.title || ''} (${err.response?.status})`),
|
|
293
|
+
`Report couldn't be processed: ${err?.response?.data?.message}`,
|
|
294
|
+
);
|
|
295
|
+
printCreateIssue(err);
|
|
296
|
+
} else {
|
|
297
|
+
console.log(APP_PREFIX, pc.blue(data?.title || ''), "Report couldn't be processed", err);
|
|
298
|
+
}
|
|
299
|
+
});
|
|
302
300
|
};
|
|
303
301
|
|
|
304
302
|
/**
|
|
@@ -325,42 +323,43 @@ class TestomatioPipe {
|
|
|
325
323
|
const testsToSend = this.batch.tests.splice(0);
|
|
326
324
|
debug('📨 Batch upload', testsToSend.length, 'tests');
|
|
327
325
|
|
|
328
|
-
return this.client
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
.
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
if (err.response.status >= 400) {
|
|
347
|
-
const responseData = err.response.data || { message: '' };
|
|
348
|
-
console.log(APP_PREFIX, pc.yellow(`Warning: ${responseData.message} (${err.response.status})`));
|
|
349
|
-
if (err.response?.data?.message?.includes('could not be matched')) {
|
|
350
|
-
this.hasUnmatchedTests = true;
|
|
351
|
-
}
|
|
352
|
-
return;
|
|
353
|
-
}
|
|
326
|
+
return this.client.request({
|
|
327
|
+
method: 'POST',
|
|
328
|
+
url: `/api/reporter/${this.runId}/testrun`,
|
|
329
|
+
data: {
|
|
330
|
+
api_key: this.apiKey,
|
|
331
|
+
tests: testsToSend,
|
|
332
|
+
batch_index: this.batch.batchIndex
|
|
333
|
+
},
|
|
334
|
+
headers: {
|
|
335
|
+
'Content-Type': 'application/json',
|
|
336
|
+
},
|
|
337
|
+
maxContentLength: Infinity
|
|
338
|
+
}).catch(err => {
|
|
339
|
+
this.requestFailures++;
|
|
340
|
+
this.notReportedTestsCount += testsToSend.length;
|
|
341
|
+
if (err.response) {
|
|
342
|
+
if (err.response.status >= 400) {
|
|
343
|
+
const responseData = err.response.data || { message: '' };
|
|
354
344
|
console.log(
|
|
355
345
|
APP_PREFIX,
|
|
356
|
-
pc.yellow(`Warning: (${err.response
|
|
357
|
-
`Report couldn't be processed: ${err?.response?.data?.message}`,
|
|
346
|
+
pc.yellow(`Warning: ${responseData.message} (${err.response.status})`),
|
|
358
347
|
);
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
348
|
+
if (err.response?.data?.message?.includes('could not be matched')) {
|
|
349
|
+
this.hasUnmatchedTests = true;
|
|
350
|
+
}
|
|
351
|
+
return;
|
|
362
352
|
}
|
|
363
|
-
|
|
353
|
+
console.log(
|
|
354
|
+
APP_PREFIX,
|
|
355
|
+
pc.yellow(`Warning: (${err.response?.status})`),
|
|
356
|
+
`Report couldn't be processed: ${err?.response?.data?.message}`,
|
|
357
|
+
);
|
|
358
|
+
printCreateIssue(err);
|
|
359
|
+
} else {
|
|
360
|
+
console.log(APP_PREFIX, "Report couldn't be processed", err);
|
|
361
|
+
}
|
|
362
|
+
});
|
|
364
363
|
};
|
|
365
364
|
|
|
366
365
|
/**
|
|
@@ -386,9 +385,9 @@ class TestomatioPipe {
|
|
|
386
385
|
else this.batch.tests.push(data);
|
|
387
386
|
|
|
388
387
|
// if test is added after run which is already finished
|
|
389
|
-
|
|
388
|
+
if (!this.batch.intervalFunction) uploading = this.#batchUpload();
|
|
390
389
|
|
|
391
|
-
|
|
390
|
+
// return promise to be able to wait for it
|
|
392
391
|
return uploading;
|
|
393
392
|
}
|
|
394
393
|
|
|
@@ -437,7 +436,7 @@ class TestomatioPipe {
|
|
|
437
436
|
status_event,
|
|
438
437
|
detach: params.detach,
|
|
439
438
|
tests: params.tests,
|
|
440
|
-
}
|
|
439
|
+
}
|
|
441
440
|
});
|
|
442
441
|
if (this.runUrl) {
|
|
443
442
|
console.log(APP_PREFIX, '📊 Report Saved. Report URL:', pc.magenta(this.runUrl));
|
package/src/utils/utils.js
CHANGED
|
@@ -139,6 +139,8 @@ export const TEST_ID_REGEX = /@T([\w\d]{8})/;
|
|
|
139
139
|
export const SUITE_ID_REGEX = /@S([\w\d]{8})/;
|
|
140
140
|
|
|
141
141
|
const fetchIdFromCode = (code, opts = {}) => {
|
|
142
|
+
if (!code) return null;
|
|
143
|
+
|
|
142
144
|
const comments = code
|
|
143
145
|
.split('\n')
|
|
144
146
|
.map(l => l.trim())
|
|
@@ -180,8 +182,32 @@ const fetchSourceCode = (contents, opts = {}) => {
|
|
|
180
182
|
if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`public void ${title}`));
|
|
181
183
|
if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`${title}(`));
|
|
182
184
|
} else if (opts.lang === 'csharp') {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
+
// Enhanced C# method detection for NUnit tests
|
|
186
|
+
lineIndex = lines.findIndex(l => l.includes(`public void ${title}(`));
|
|
187
|
+
|
|
188
|
+
if (lineIndex === -1) {
|
|
189
|
+
lineIndex = lines.findIndex(l => l.includes(`public async Task ${title}(`));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (lineIndex === -1) {
|
|
193
|
+
lineIndex = lines.findIndex(l => l.includes(`${title}(`));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Look for TestCase or Test attributes above the method
|
|
197
|
+
if (lineIndex === -1) {
|
|
198
|
+
const testAttributeIndex = lines.findIndex((l, index) => {
|
|
199
|
+
if (l.includes('[TestCase') || l.includes('[Test')) {
|
|
200
|
+
// Check next few lines for the method
|
|
201
|
+
const nextLines = lines.slice(index, Math.min(lines.length, index + 5));
|
|
202
|
+
const hasMethod = nextLines.some(nextLine => nextLine.includes(`${title}(`));
|
|
203
|
+
return hasMethod;
|
|
204
|
+
}
|
|
205
|
+
return false;
|
|
206
|
+
});
|
|
207
|
+
if (testAttributeIndex !== -1) {
|
|
208
|
+
lineIndex = testAttributeIndex;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
185
211
|
} else {
|
|
186
212
|
lineIndex = lines.findIndex(l => l.includes(title));
|
|
187
213
|
}
|
|
@@ -191,7 +217,7 @@ const fetchSourceCode = (contents, opts = {}) => {
|
|
|
191
217
|
lineIndex -= opts.prepend;
|
|
192
218
|
}
|
|
193
219
|
|
|
194
|
-
if (lineIndex) {
|
|
220
|
+
if (lineIndex !== -1 && lineIndex !== undefined) {
|
|
195
221
|
const result = [];
|
|
196
222
|
for (let i = lineIndex; i < lineIndex + limit; i++) {
|
|
197
223
|
if (lines[i] === undefined) continue;
|
|
@@ -216,6 +242,10 @@ const fetchSourceCode = (contents, opts = {}) => {
|
|
|
216
242
|
if (opts.lang === 'java' && lines[i].trim().match(/^@\w+/)) break;
|
|
217
243
|
if (opts.lang === 'java' && lines[i].includes(' public void ')) break;
|
|
218
244
|
if (opts.lang === 'java' && lines[i].includes(' class ')) break;
|
|
245
|
+
if (opts.lang === 'csharp' && lines[i].trim().match(/^\[Test/)) break;
|
|
246
|
+
if (opts.lang === 'csharp' && lines[i].includes(' public void ')) break;
|
|
247
|
+
if (opts.lang === 'csharp' && lines[i].includes(' public async Task ')) break;
|
|
248
|
+
if (opts.lang === 'csharp' && lines[i].includes(' class ') && lines[i].includes('public')) break;
|
|
219
249
|
}
|
|
220
250
|
result.push(lines[i]);
|
|
221
251
|
}
|
|
@@ -361,7 +391,7 @@ function readLatestRunId() {
|
|
|
361
391
|
try {
|
|
362
392
|
const filePath = path.join(os.tmpdir(), `testomatio.latest.run`);
|
|
363
393
|
if (!fs.existsSync(filePath)) return null;
|
|
364
|
-
|
|
394
|
+
|
|
365
395
|
const stats = fs.statSync(filePath);
|
|
366
396
|
const diff = +new Date() - +stats.mtime;
|
|
367
397
|
const diffHours = diff / 1000 / 60 / 60;
|