@testomatio/reporter 2.3.4 → 2.3.5-beta-6-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.
@@ -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 ?? true,
23
+ isEnabled: params?.isBatchEnabled ?? (process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD ? false : 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','PUT','HEAD','OPTIONS','DELETE','POST'],
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;
@@ -92,6 +92,33 @@ class TestomatioPipe {
92
92
  }
93
93
  }
94
94
 
95
+ /**
96
+ * Prepares data for sending to Testomat.io.
97
+ * @param {*} data - The data to be formatted.
98
+ * @returns
99
+ */
100
+ #formatData(data) {
101
+ data.api_key = this.apiKey;
102
+ data.create = this.createNewTests;
103
+
104
+ // add test ID + run ID
105
+ if (data.rid) data.rid = `${this.runId}-${data.rid}`;
106
+
107
+ if (!process.env.TESTOMATIO_STACK_PASSED && data.status === STATUS.PASSED) {
108
+ data.stack = null;
109
+ }
110
+
111
+ if (!process.env.TESTOMATIO_STEPS_PASSED && data.status === STATUS.PASSED) {
112
+ data.steps = null;
113
+ }
114
+
115
+ if (process.env.TESTOMATIO_NO_STEPS) {
116
+ data.steps = null;
117
+ }
118
+
119
+ return data;
120
+ }
121
+
95
122
  /**
96
123
  * Asynchronously prepares and retrieves the Testomat.io test grepList based on the provided options.
97
124
  * @param {Object} opts - The options for preparing the test grepList.
@@ -186,7 +213,7 @@ class TestomatioPipe {
186
213
  method: 'PUT',
187
214
  url: `/api/reporter/${this.runId}`,
188
215
  data: runParams,
189
- responseType: 'json'
216
+ responseType: 'json',
190
217
  });
191
218
  if (resp.data.artifacts) setS3Credentials(resp.data.artifacts);
192
219
  return;
@@ -199,7 +226,7 @@ class TestomatioPipe {
199
226
  url: '/api/reporter',
200
227
  data: runParams,
201
228
  maxContentLength: Infinity,
202
- responseType: 'json'
229
+ responseType: 'json',
203
230
  });
204
231
 
205
232
  this.runId = resp.data.uid;
@@ -252,59 +279,48 @@ class TestomatioPipe {
252
279
  if (!this.runId) return;
253
280
  if (this.#cancelTestReportingInCaseOfTooManyReqFailures()) return;
254
281
 
255
- data.api_key = this.apiKey;
256
- data.create = this.createNewTests;
257
-
258
- if (!process.env.TESTOMATIO_STACK_PASSED && data.status === STATUS.PASSED) {
259
- data.stack = null;
260
- }
261
-
262
- if (!process.env.TESTOMATIO_STEPS_PASSED && data.status === STATUS.PASSED) {
263
- data.steps = null;
264
- }
265
-
266
- if (process.env.TESTOMATIO_NO_STEPS) {
267
- data.steps = null;
268
- }
282
+ this.#formatData(data);
269
283
 
270
284
  const json = JsonCycle.stringify(data);
271
285
 
272
286
  debug('Adding test', json);
273
287
 
274
- return this.client.request({
275
- method: 'POST',
276
- url: `/api/reporter/${this.runId}/testrun`,
277
- data: json,
278
- headers: {
279
- 'Content-Type': 'application/json',
280
- },
281
- maxContentLength: Infinity
282
- }).catch(err => {
283
- this.requestFailures++;
284
- this.notReportedTestsCount++;
285
- if (err.response) {
286
- if (err.response.status >= 400) {
287
- const responseData = err.response.data || { message: '' };
288
+ return this.client
289
+ .request({
290
+ method: 'POST',
291
+ url: `/api/reporter/${this.runId}/testrun`,
292
+ data: json,
293
+ headers: {
294
+ 'Content-Type': 'application/json',
295
+ },
296
+ maxContentLength: Infinity,
297
+ })
298
+ .catch(err => {
299
+ this.requestFailures++;
300
+ this.notReportedTestsCount++;
301
+ if (err.response) {
302
+ if (err.response.status >= 400) {
303
+ const responseData = err.response.data || { message: '' };
304
+ console.log(
305
+ APP_PREFIX,
306
+ pc.yellow(`Warning: ${responseData.message} (${err.response.status})`),
307
+ pc.gray(data?.title || ''),
308
+ );
309
+ if (err.response?.data?.message?.includes('could not be matched')) {
310
+ this.hasUnmatchedTests = true;
311
+ }
312
+ return;
313
+ }
288
314
  console.log(
289
315
  APP_PREFIX,
290
- pc.yellow(`Warning: ${responseData.message} (${err.response.status})`),
291
- pc.gray(data?.title || ''),
316
+ pc.yellow(`Warning: ${data?.title || ''} (${err.response?.status})`),
317
+ `Report couldn't be processed: ${err?.response?.data?.message}`,
292
318
  );
293
- if (err.response?.data?.message?.includes('could not be matched')) {
294
- this.hasUnmatchedTests = true;
295
- }
296
- return;
319
+ printCreateIssue(err);
320
+ } else {
321
+ console.log(APP_PREFIX, pc.blue(data?.title || ''), "Report couldn't be processed", err);
297
322
  }
298
- console.log(
299
- APP_PREFIX,
300
- pc.yellow(`Warning: ${data?.title || ''} (${err.response?.status})`),
301
- `Report couldn't be processed: ${err?.response?.data?.message}`,
302
- );
303
- printCreateIssue(err);
304
- } else {
305
- console.log(APP_PREFIX, pc.blue(data?.title || ''), "Report couldn't be processed", err);
306
- }
307
- });
323
+ });
308
324
  };
309
325
 
310
326
  /**
@@ -331,43 +347,42 @@ class TestomatioPipe {
331
347
  const testsToSend = this.batch.tests.splice(0);
332
348
  debug('📨 Batch upload', testsToSend.length, 'tests');
333
349
 
334
- return this.client.request({
335
- method: 'POST',
336
- url: `/api/reporter/${this.runId}/testrun`,
337
- data: {
338
- api_key: this.apiKey,
339
- tests: testsToSend,
340
- batch_index: this.batch.batchIndex
341
- },
342
- headers: {
343
- 'Content-Type': 'application/json',
344
- },
345
- maxContentLength: Infinity
346
- }).catch(err => {
347
- this.requestFailures++;
348
- this.notReportedTestsCount += testsToSend.length;
349
- if (err.response) {
350
- if (err.response.status >= 400) {
351
- const responseData = err.response.data || { message: '' };
350
+ return this.client
351
+ .request({
352
+ method: 'POST',
353
+ url: `/api/reporter/${this.runId}/testrun`,
354
+ data: {
355
+ api_key: this.apiKey,
356
+ tests: testsToSend,
357
+ batch_index: this.batch.batchIndex,
358
+ },
359
+ headers: {
360
+ 'Content-Type': 'application/json',
361
+ },
362
+ maxContentLength: Infinity,
363
+ })
364
+ .catch(err => {
365
+ this.requestFailures++;
366
+ this.notReportedTestsCount += testsToSend.length;
367
+ if (err.response) {
368
+ if (err.response.status >= 400) {
369
+ const responseData = err.response.data || { message: '' };
370
+ console.log(APP_PREFIX, pc.yellow(`Warning: ${responseData.message} (${err.response.status})`));
371
+ if (err.response?.data?.message?.includes('could not be matched')) {
372
+ this.hasUnmatchedTests = true;
373
+ }
374
+ return;
375
+ }
352
376
  console.log(
353
377
  APP_PREFIX,
354
- pc.yellow(`Warning: ${responseData.message} (${err.response.status})`),
378
+ pc.yellow(`Warning: (${err.response?.status})`),
379
+ `Report couldn't be processed: ${err?.response?.data?.message}`,
355
380
  );
356
- if (err.response?.data?.message?.includes('could not be matched')) {
357
- this.hasUnmatchedTests = true;
358
- }
359
- return;
381
+ printCreateIssue(err);
382
+ } else {
383
+ console.log(APP_PREFIX, "Report couldn't be processed", err);
360
384
  }
361
- console.log(
362
- APP_PREFIX,
363
- pc.yellow(`Warning: (${err.response?.status})`),
364
- `Report couldn't be processed: ${err?.response?.data?.message}`,
365
- );
366
- printCreateIssue(err);
367
- } else {
368
- console.log(APP_PREFIX, "Report couldn't be processed", err);
369
- }
370
- });
385
+ });
371
386
  };
372
387
 
373
388
  /**
@@ -383,19 +398,16 @@ class TestomatioPipe {
383
398
  return;
384
399
  }
385
400
 
386
- // add test ID + run ID
387
- if (data.rid) data.rid = `${this.runId}-${data.rid}`;
388
- data.api_key = this.apiKey;
389
- data.create = this.createNewTests;
401
+ this.#formatData(data);
390
402
 
391
403
  let uploading = null;
392
404
  if (!this.batch.isEnabled) uploading = this.#uploadSingleTest(data);
393
405
  else this.batch.tests.push(data);
394
406
 
395
407
  // if test is added after run which is already finished
396
- if (!this.batch.intervalFunction) uploading = this.#batchUpload();
408
+ if (!this.batch.intervalFunction) uploading = this.#batchUpload();
397
409
 
398
- // return promise to be able to wait for it
410
+ // return promise to be able to wait for it
399
411
  return uploading;
400
412
  }
401
413
 
@@ -444,7 +456,7 @@ class TestomatioPipe {
444
456
  status_event,
445
457
  detach: params.detach,
446
458
  tests: params.tests,
447
- }
459
+ },
448
460
  });
449
461
 
450
462
  console.log(APP_PREFIX, '✅ Testrun finished');
@@ -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
- if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`public void ${title}`));
184
- if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`${title}(`));
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
  }