@testomatio/reporter 2.0.0-beta.2-xml → 2.0.0-beta.3-gaxios

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.
@@ -57,7 +57,6 @@ class TestomatioPipe {
57
57
  timeout: constants_js_1.AXIOS_TIMEOUT,
58
58
  proxy: proxy ? proxy.toString() : undefined,
59
59
  retry: true,
60
- agent: new (require('https').Agent)({ rejectUnauthorized: false, keepAlive: false }),
61
60
  retryConfig: {
62
61
  retry: constants_js_1.REPORTER_REQUEST_RETRIES.retriesPerRequest,
63
62
  retryDelay: constants_js_1.REPORTER_REQUEST_RETRIES.retryTimeout,
@@ -439,9 +438,9 @@ function printCreateIssue(err) {
439
438
  if (!err.config)
440
439
  return;
441
440
  const time = new Date().toUTCString();
442
- const { body, url, baseURL, method } = err?.config || {};
441
+ const { data, url, baseURL, method } = err?.config || {};
443
442
  console.log('```js');
444
- console.log({ body: body?.replace(/"(tstmt_[^"]+)"/g, 'tstmt_*'), url, baseURL, method, time });
443
+ console.log({ data: data?.replace(/"(tstmt_[^"]+)"/g, 'tstmt_*'), url, baseURL, method, time });
445
444
  console.log('```');
446
445
  });
447
446
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/reporter",
3
- "version": "2.0.0-beta.2-xml",
3
+ "version": "2.0.0-beta.3-gaxios",
4
4
  "description": "Testomatio Reporter Client",
5
5
  "engines": {
6
6
  "node": ">=18"
@@ -16,8 +16,7 @@
16
16
  "@cucumber/cucumber": "^10.9.0",
17
17
  "@octokit/rest": "^21.1.1",
18
18
  "aws-sdk": "^2.1072.0",
19
- "axios": "^1.6.2",
20
- "axios-retry": "^3.9.1",
19
+ "gaxios": "7.0.0-rc.4",
21
20
  "callsite-record": "^4.1.4",
22
21
  "commander": "^12",
23
22
  "cross-spawn": "^7.0.3",
@@ -49,6 +48,7 @@
49
48
  "testcafe"
50
49
  ],
51
50
  "scripts": {
51
+ "@cucumber/cucumber": "^10.9.0",
52
52
  "clear-exportdir": "rm -rf export/",
53
53
  "pretty": "npx prettier --check .",
54
54
  "pretty:fix": "prettier --write .",
@@ -67,9 +67,8 @@
67
67
  "test:adapter:playwright:example": "npx playwright test --config='./tests/adapter/examples/playwright/playwright.config.ts'",
68
68
  "test:adapter:vitest:example": "npx vitest --config='./tests/adapter/examples/vitest/vitest.config.ts'",
69
69
  "test:storage": "npx mocha tests-storage/artifact-storage.test.js && npx mocha tests-storage/data-storage.test.js && TESTOMATIO_INTERCEPT_CONSOLE_LOGS=true npx mocha tests-storage/logger.test.js && npx mocha tests-storage/logger-2.test.js && npx mocha tests-storage/reporter-functions.test.js",
70
- "build": "rm -rf ./cjs && tsc --module commonjs && npx tsx build/scripts/edit-js-files.js && npx tsx build/scripts/edit-package-json.js && chmod +x ./build/scripts/copy-tesmplate.sh && ./build/scripts/copy-tesmplate.sh",
71
- "build:bun": "rm -rf ./cjs && bun build ./src/reporter.js ./src/bin/reportXml.js ./src/bin/startTest.js ./src/bin/uploadArtifacts.js --outdir ./cjs --target node",
72
- "build:watch:bun": "rm -rf ./cjs && bun build ./src/bin/reportXml.js ./src/bin/startTest.js ./src/bin/uploadArtifacts.js --outdir ./cjs --target node --watch --onSuccess \"build/scripts/post-build.js\""
70
+ "//": "builds code from /src (esm) into /lib (commonjs)",
71
+ "build": "rm -rf ./cjs && tsc --module commonjs && npx tsx build/scripts/edit-js-files.js && npx tsx build/scripts/edit-package-json.js && chmod +x ./build/scripts/copy-tesmplate.sh && ./build/scripts/copy-tesmplate.sh"
73
72
  },
74
73
  "devDependencies": {
75
74
  "@playwright/test": "^1.49.1",
@@ -1,6 +1,6 @@
1
1
  import TestomatClient from '../client.js';
2
2
  import { config } from '../config.js';
3
- import { STATUS } from '../constants.js';
3
+ import { STATUS } from '../constants';
4
4
  import { getTestomatIdFromTestTitle } from '../utils/utils.js';
5
5
 
6
6
  const apiKey = config.TESTOMATIO;
@@ -1,4 +1,4 @@
1
- import { default as WDIOReporter, RunnerStats } from '@wdio/reporter';
1
+ import WDIOReporter, { RunnerStats } from '@wdio/reporter';
2
2
  import TestomatClient from '../client.js';
3
3
  import { getTestomatIdFromTestTitle, fileSystem } from '../utils/utils.js';
4
4
  import { services } from '../services/index.js';
package/src/bin/cli.js CHANGED
@@ -7,7 +7,7 @@ import createDebugMessages from 'debug';
7
7
  import TestomatClient from '../client.js';
8
8
  import XmlReader from '../xmlReader.js';
9
9
  import { APP_PREFIX, STATUS } from '../constants.js';
10
- import { getPackageVersion } from '../utils/utils.js';
10
+ import { version } from '../../package.json';
11
11
  import { config } from '../config.js';
12
12
  import { readLatestRunId } from '../utils/utils.js';
13
13
  import pc from 'picocolors';
@@ -15,7 +15,6 @@ import { filesize as prettyBytes } from 'filesize';
15
15
  import dotenv from 'dotenv';
16
16
 
17
17
  const debug = createDebugMessages('@testomatio/reporter:xml-cli');
18
- const version = getPackageVersion();
19
18
  console.log(pc.cyan(pc.bold(` 🤩 Testomat.io Reporter v${version}`)));
20
19
  const program = new Command();
21
20
 
@@ -5,11 +5,8 @@ import { glob } from 'glob';
5
5
  import createDebugMessages from 'debug';
6
6
  import { APP_PREFIX } from '../constants.js';
7
7
  import XmlReader from '../xmlReader.js';
8
- import { getPackageVersion } from '../utils/utils.js';
8
+ import { version } from '../../package.json';
9
9
  import dotenv from 'dotenv';
10
- import path from 'path';
11
-
12
- const version = getPackageVersion();
13
10
 
14
11
  const debug = createDebugMessages('@testomatio/reporter:xml-cli');
15
12
  console.log(pc.cyan(pc.bold(` 🤩 Testomat.io XML Reporter v${version}`)));
@@ -4,11 +4,10 @@ import { Command } from 'commander';
4
4
  import pc from 'picocolors';
5
5
  import TestomatClient from '../client.js';
6
6
  import { APP_PREFIX, STATUS } from '../constants.js';
7
- import { getPackageVersion } from '../utils/utils.js';
7
+ import { version } from '../../package.json';
8
8
  import { config } from '../config.js';
9
9
  import dotenv from 'dotenv';
10
10
 
11
- const version = getPackageVersion();
12
11
  console.log(pc.cyan(pc.bold(` 🤩 Testomat.io Reporter v${version}`)));
13
12
  const program = new Command();
14
13
 
@@ -5,13 +5,12 @@ import pc from 'picocolors';
5
5
  import createDebugMessages from 'debug';
6
6
  import TestomatClient from '../client.js';
7
7
  import { APP_PREFIX } from '../constants.js';
8
- import { getPackageVersion } from '../utils/utils.js';
8
+ import { version } from '../../package.json';
9
9
  import { config } from '../config.js';
10
10
  import { readLatestRunId } from '../utils/utils.js';
11
11
  import dotenv from 'dotenv';
12
12
 
13
13
  const debug = createDebugMessages('@testomatio/reporter:upload-cli');
14
- const version = getPackageVersion();
15
14
  console.log(pc.cyan(pc.bold(` 🤩 Testomat.io Reporter v${version}`)));
16
15
  const program = new Command();
17
16
 
@@ -1,4 +1,3 @@
1
- import path from 'path';
2
1
  import Adapter from './adapter.js';
3
2
 
4
3
  class CSharpAdapter extends Adapter {
@@ -8,21 +7,10 @@ class CSharpAdapter extends Adapter {
8
7
  if (example) t.example = { ...example[1].split(',') };
9
8
  const suite = t.suite_title.split('.');
10
9
  t.suite_title = suite.pop();
11
- t.file = namespaceToFileName(t.file);
10
+ t.file = suite.join('/');
12
11
  t.title = title.trim();
13
12
  return t;
14
13
  }
15
-
16
- getFilePath(t) {
17
- const fileName = namespaceToFileName(t.file);
18
- return fileName;
19
- }
20
14
  }
21
15
 
22
16
  export default CSharpAdapter;
23
-
24
- function namespaceToFileName(fileName) {
25
- const fileParts = fileName.split('.');
26
- fileParts[fileParts.length - 1] = fileParts[fileParts.length - 1]?.replace(/\$.*/, '');
27
- return `${fileParts.join(path.sep)}.cs`;
28
- }
@@ -1,7 +1,7 @@
1
1
  import { APP_PREFIX, testomatLogoURL } from '../constants.js';
2
2
  import { ansiRegExp, isSameTest } from '../utils/utils.js';
3
3
  import { statusEmoji, fullName } from '../utils/pipe_utils.js';
4
- import axios from 'axios';
4
+ import { Gaxios } from 'gaxios';
5
5
  import pc from 'picocolors';
6
6
  import humanizeDuration from 'humanize-duration';
7
7
  import merge from 'lodash.merge';
@@ -40,6 +40,13 @@ export class BitbucketPipe {
40
40
  }
41
41
 
42
42
  this.isEnabled = true;
43
+ this.client = new Gaxios({
44
+ baseURL: 'https://api.bitbucket.org/2.0',
45
+ headers: {
46
+ 'Content-Type': 'application/json',
47
+ 'Authorization': `Bearer ${this.token}`
48
+ }
49
+ });
43
50
 
44
51
  debug('Bitbucket Pipe: Enabled');
45
52
  }
@@ -166,26 +173,21 @@ export class BitbucketPipe {
166
173
 
167
174
  // Construct Bitbucket API URL for comments
168
175
  // eslint-disable-next-line max-len
169
- const commentsRequestURL = `https://api.bitbucket.org/2.0/repositories/${this.ENV.BITBUCKET_WORKSPACE}/${this.ENV.BITBUCKET_REPO_SLUG}/pullrequests/${this.ENV.BITBUCKET_PR_ID}/comments`;
176
+ const commentsRequestURL = `/repositories/${this.ENV.BITBUCKET_WORKSPACE}/${this.ENV.BITBUCKET_REPO_SLUG}/pullrequests/${this.ENV.BITBUCKET_PR_ID}/comments`;
170
177
 
171
178
  // Delete previous report
172
- await deletePreviousReport(axios, commentsRequestURL, this.hiddenCommentData, this.token);
179
+ await deletePreviousReport(this.client, commentsRequestURL, this.hiddenCommentData);
173
180
 
174
181
  // Add current report
175
182
  debug(`Adding comment via URL: ${commentsRequestURL}`);
176
183
  debug(`Final Bitbucket API call body: ${body}`);
177
184
 
178
185
  try {
179
- const addCommentResponse = await axios.post(
180
- commentsRequestURL,
181
- { content: { raw: body } },
182
- {
183
- headers: {
184
- Authorization: `Bearer ${this.token}`,
185
- 'Content-Type': 'application/json',
186
- },
187
- },
188
- );
186
+ const addCommentResponse = await this.client.request({
187
+ method: 'POST',
188
+ url: commentsRequestURL,
189
+ data: { content: { raw: body } }
190
+ });
189
191
 
190
192
  const commentID = addCommentResponse.data.id;
191
193
  // eslint-disable-next-line max-len
@@ -210,18 +212,16 @@ export class BitbucketPipe {
210
212
  updateRun() {}
211
213
  }
212
214
 
213
- async function deletePreviousReport(axiosInstance, commentsRequestURL, hiddenCommentData, token) {
215
+ async function deletePreviousReport(client, commentsRequestURL, hiddenCommentData) {
214
216
  if (process.env.BITBUCKET_KEEP_OUTDATED_REPORTS) return;
215
217
 
216
218
  // Get comments
217
219
  let comments = [];
218
220
 
219
221
  try {
220
- const response = await axiosInstance.get(commentsRequestURL, {
221
- headers: {
222
- Authorization: `Bearer ${token}`,
223
- 'Content-Type': 'application/json',
224
- },
222
+ const response = await client.request({
223
+ method: 'GET',
224
+ url: commentsRequestURL
225
225
  });
226
226
  comments = response.data.values;
227
227
  } catch (e) {
@@ -236,11 +236,9 @@ async function deletePreviousReport(axiosInstance, commentsRequestURL, hiddenCom
236
236
  try {
237
237
  // Delete previous comment
238
238
  const deleteCommentURL = `${commentsRequestURL}/${comment.id}`;
239
- await axiosInstance.delete(deleteCommentURL, {
240
- headers: {
241
- Authorization: `Bearer ${token}`,
242
- 'Content-Type': 'application/json',
243
- },
239
+ await client.request({
240
+ method: 'DELETE',
241
+ url: deleteCommentURL
244
242
  });
245
243
  } catch (e) {
246
244
  console.warn(`Can't delete previously added comment with testomat.io report. Ignored.`);
@@ -1,5 +1,5 @@
1
1
  import createDebugMessages from 'debug';
2
- import axios from 'axios';
2
+ import { Gaxios } from 'gaxios';
3
3
  import pc from 'picocolors';
4
4
  import humanizeDuration from 'humanize-duration';
5
5
  import merge from 'lodash.merge';
@@ -45,6 +45,12 @@ class GitLabPipe {
45
45
  }
46
46
 
47
47
  this.isEnabled = true;
48
+ this.client = new Gaxios({
49
+ baseURL: 'https://gitlab.com/api/v4',
50
+ headers: {
51
+ 'Content-Type': 'application/json',
52
+ }
53
+ });
48
54
 
49
55
  debug('GitLab Pipe: Enabled');
50
56
  }
@@ -157,16 +163,21 @@ class GitLabPipe {
157
163
  }
158
164
 
159
165
  // eslint-disable-next-line max-len
160
- const commentsRequestURL = `https://gitlab.com/api/v4/projects/${this.ENV.CI_PROJECT_ID}/merge_requests/${this.ENV.CI_MERGE_REQUEST_IID}/notes`;
166
+ const commentsRequestURL = `/projects/${this.ENV.CI_PROJECT_ID}/merge_requests/${this.ENV.CI_MERGE_REQUEST_IID}/notes`;
161
167
 
162
168
  // delete previous report
163
- await deletePreviousReport(axios, commentsRequestURL, this.hiddenCommentData, this.token);
169
+ await deletePreviousReport(this.client, commentsRequestURL, this.hiddenCommentData, this.token);
164
170
 
165
171
  // add current report
166
172
  debug(`Adding comment via url: ${commentsRequestURL}`);
167
173
 
168
174
  try {
169
- const addCommentResponse = await axios.post(`${commentsRequestURL}?access_token=${this.token}`, { body });
175
+ const addCommentResponse = await this.client.request({
176
+ method: 'POST',
177
+ url: commentsRequestURL,
178
+ params: { access_token: this.token },
179
+ data: { body }
180
+ });
170
181
 
171
182
  const commentID = addCommentResponse.data.id;
172
183
  // eslint-disable-next-line max-len
@@ -191,14 +202,18 @@ class GitLabPipe {
191
202
  updateRun() {}
192
203
  }
193
204
 
194
- async function deletePreviousReport(axiosInstance, commentsRequestURL, hiddenCommentData, token) {
205
+ async function deletePreviousReport(client, commentsRequestURL, hiddenCommentData, token) {
195
206
  if (process.env.GITLAB_KEEP_OUTDATED_REPORTS) return;
196
207
 
197
208
  // get comments
198
209
  let comments = [];
199
210
 
200
211
  try {
201
- const response = await axiosInstance.get(`${commentsRequestURL}?access_token=${token}`);
212
+ const response = await client.request({
213
+ method: 'GET',
214
+ url: commentsRequestURL,
215
+ params: { access_token: token }
216
+ });
202
217
  comments = response.data;
203
218
  } catch (e) {
204
219
  console.error('Error while attempt to retrieve comments on GitLab Merge Request:\n', e);
@@ -211,8 +226,12 @@ async function deletePreviousReport(axiosInstance, commentsRequestURL, hiddenCom
211
226
  if (comment.body.includes(hiddenCommentData)) {
212
227
  try {
213
228
  // delete previous comment
214
- const deleteCommentURL = `${commentsRequestURL}/${comment.id}?access_token=${token}`;
215
- await axiosInstance.delete(deleteCommentURL);
229
+ const deleteCommentURL = `${commentsRequestURL}/${comment.id}`;
230
+ await client.request({
231
+ method: 'DELETE',
232
+ url: deleteCommentURL,
233
+ params: { access_token: token }
234
+ });
216
235
  } catch (e) {
217
236
  console.warn(`Can't delete previously added comment with testomat.io report. Ignore.`);
218
237
  }
@@ -1,12 +1,6 @@
1
1
  import createDebugMessages from 'debug';
2
2
  import pc from 'picocolors';
3
-
4
- // Retry interceptor function
5
- import axiosRetry from 'axios-retry';
6
-
7
- // Default axios instance
8
- import axios from 'axios';
9
-
3
+ import { Gaxios } from 'gaxios';
10
4
  import JsonCycle from 'json-cycle';
11
5
  import { APP_PREFIX, STATUS, AXIOS_TIMEOUT, REPORTER_REQUEST_RETRIES } from '../constants.js';
12
6
  import { isValidUrl, foundedTestLog } from '../utils/utils.js';
@@ -57,43 +51,30 @@ class TestomatioPipe {
57
51
  this.groupTitle = params.groupTitle || process.env.TESTOMATIO_RUNGROUP_TITLE;
58
52
  this.env = process.env.TESTOMATIO_ENV;
59
53
  this.label = process.env.TESTOMATIO_LABEL;
60
- // Create a new instance of axios with a custom config
61
- this.axios = axios.create({
54
+
55
+ // Create a new instance of gaxios with a custom config
56
+ this.client = new Gaxios({
62
57
  baseURL: `${this.url.trim()}`,
63
58
  timeout: AXIOS_TIMEOUT,
64
- proxy: proxy
65
- ? {
66
- host: proxy.hostname,
67
- port: parseInt(proxy.port, 10),
68
- protocol: proxy.protocol,
59
+ proxy: proxy ? proxy.toString() : undefined,
60
+ retry: true,
61
+ retryConfig: {
62
+ retry: REPORTER_REQUEST_RETRIES.retriesPerRequest,
63
+ retryDelay: REPORTER_REQUEST_RETRIES.retryTimeout,
64
+ shouldRetry: (error) => {
65
+ if (!error.response) return false;
66
+ switch (error.response?.status) {
67
+ case 400: // Bad request (probably wrong API key)
68
+ case 404: // Test not matched
69
+ case 429: // Rate limit exceeded
70
+ case 500: // Internal server error
71
+ return false;
72
+ default:
73
+ break;
69
74
  }
70
- : false,
71
- });
72
-
73
- // Pass the axios instance to the retry function
74
- axiosRetry(this.axios, {
75
- // do not use retries for unit tests
76
- retries: REPORTER_REQUEST_RETRIES.retriesPerRequest, // Number of retries
77
- shouldResetTimeout: true,
78
- retryCondition: error => {
79
- if (!error.response) return false;
80
- switch (error.response?.status) {
81
- case 400: // Bad request (probably wrong API key)
82
- case 404: // Test not matched
83
- case 429: // Rate limit exceeded
84
- case 500: // Internal server error
85
- return false;
86
- default:
87
- break;
75
+ return error.response?.status >= 401; // Retry on 401+ and 5xx
88
76
  }
89
- return error.response?.status >= 401; // Retry on 401+ and 5xx
90
- },
91
- retryDelay: () => REPORTER_REQUEST_RETRIES.retryTimeout, // sum = 15sec
92
- onRetry: async (retryCount, error) => {
93
- this.retriesTimestamps.push(Date.now());
94
-
95
- debug(`${error.message || `Request failed ${error.status}`}. Retry #${retryCount} ...`);
96
- },
77
+ }
97
78
  });
98
79
 
99
80
  this.isEnabled = true;
@@ -134,12 +115,15 @@ class TestomatioPipe {
134
115
  return;
135
116
  }
136
117
 
137
- const resp = await this.axios.get('/api/test_grep', q);
138
- const { data } = resp;
118
+ const resp = await this.client.request({
119
+ method: 'GET',
120
+ url: '/api/test_grep',
121
+ params: q
122
+ });
139
123
 
140
- if (Array.isArray(data?.tests) && data?.tests?.length > 0) {
141
- foundedTestLog(APP_PREFIX, data.tests);
142
- return data.tests;
124
+ if (Array.isArray(resp.data?.tests) && resp.data?.tests?.length > 0) {
125
+ foundedTestLog(APP_PREFIX, resp.data.tests);
126
+ return resp.data.tests;
143
127
  }
144
128
 
145
129
  console.log(APP_PREFIX, `⛔ No tests found for your --filter --> ${type}=${id}`);
@@ -163,7 +147,6 @@ class TestomatioPipe {
163
147
 
164
148
  // GitHub Actions Url
165
149
  if (!buildUrl && process.env.GITHUB_RUN_ID) {
166
- // eslint-disable-next-line max-len
167
150
  buildUrl = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
168
151
  }
169
152
 
@@ -199,16 +182,23 @@ class TestomatioPipe {
199
182
  if (this.runId) {
200
183
  this.store.runId = this.runId;
201
184
  debug(`Run with id ${this.runId} already created, updating...`);
202
- const resp = await this.axios.put(`/api/reporter/${this.runId}`, runParams);
185
+ const resp = await this.client.request({
186
+ method: 'PUT',
187
+ url: `/api/reporter/${this.runId}`,
188
+ data: runParams
189
+ });
203
190
  if (resp.data.artifacts) setS3Credentials(resp.data.artifacts);
204
191
  return;
205
192
  }
206
193
 
207
194
  debug('Creating run...');
208
195
  try {
209
- const resp = await this.axios.post(`/api/reporter`, runParams, {
196
+ const resp = await this.client.request({
197
+ method: 'POST',
198
+ url: '/api/reporter',
199
+ data: runParams,
210
200
  maxContentLength: Infinity,
211
- maxBodyLength: Infinity,
201
+ responseType: 'json'
212
202
  });
213
203
 
214
204
  this.runId = resp.data.uid;
@@ -225,6 +215,7 @@ class TestomatioPipe {
225
215
  debug('Run created', this.runId);
226
216
  } catch (err) {
227
217
  const errorText = err.response?.data?.message || err.message;
218
+ debug('Error creating run', err);
228
219
  console.log(errorText || err);
229
220
  if (!this.apiKey) console.error('Testomat.io API key is not set');
230
221
  if (!this.apiKey?.startsWith('tstmt')) console.error('Testomat.io API key is invalid');
@@ -271,7 +262,15 @@ class TestomatioPipe {
271
262
 
272
263
  debug('Adding test', json);
273
264
 
274
- return this.axios.post(`/api/reporter/${this.runId}/testrun`, json, axiosAddTestrunRequestConfig).catch(err => {
265
+ return this.client.request({
266
+ method: 'POST',
267
+ url: `/api/reporter/${this.runId}/testrun`,
268
+ data: json,
269
+ headers: {
270
+ 'Content-Type': 'application/json',
271
+ },
272
+ maxContentLength: Infinity
273
+ }).catch(err => {
275
274
  this.requestFailures++;
276
275
  this.notReportedTestsCount++;
277
276
  if (err.response) {
@@ -323,38 +322,43 @@ class TestomatioPipe {
323
322
  const testsToSend = this.batch.tests.splice(0);
324
323
  debug('📨 Batch upload', testsToSend.length, 'tests');
325
324
 
326
- return this.axios
327
- .post(
328
- `/api/reporter/${this.runId}/testrun`,
329
- { api_key: this.apiKey, tests: testsToSend, batch_index: this.batch.batchIndex },
330
- axiosAddTestrunRequestConfig,
331
- )
332
- .catch(err => {
333
- this.requestFailures++;
334
- this.notReportedTestsCount += testsToSend.length;
335
- if (err.response) {
336
- if (err.response.status >= 400) {
337
- const responseData = err.response.data || { message: '' };
338
- console.log(
339
- APP_PREFIX,
340
- pc.yellow(`Warning: ${responseData.message} (${err.response.status})`),
341
- // pc.grey(data?.title || ''),
342
- );
343
- if (err.response?.data?.message?.includes('could not be matched')) {
344
- this.hasUnmatchedTests = true;
345
- }
346
- return;
347
- }
325
+ return this.client.request({
326
+ method: 'POST',
327
+ url: `/api/reporter/${this.runId}/testrun`,
328
+ data: {
329
+ api_key: this.apiKey,
330
+ tests: testsToSend,
331
+ batch_index: this.batch.batchIndex
332
+ },
333
+ headers: {
334
+ 'Content-Type': 'application/json',
335
+ },
336
+ maxContentLength: Infinity
337
+ }).catch(err => {
338
+ this.requestFailures++;
339
+ this.notReportedTestsCount += testsToSend.length;
340
+ if (err.response) {
341
+ if (err.response.status >= 400) {
342
+ const responseData = err.response.data || { message: '' };
348
343
  console.log(
349
344
  APP_PREFIX,
350
- pc.yellow(`Warning: (${err.response?.status})`),
351
- `Report couldn't be processed: ${err?.response?.data?.message}`,
345
+ pc.yellow(`Warning: ${responseData.message} (${err.response.status})`),
352
346
  );
353
- printCreateIssue(err);
354
- } else {
355
- console.log(APP_PREFIX, "Report couldn't be processed", err);
347
+ if (err.response?.data?.message?.includes('could not be matched')) {
348
+ this.hasUnmatchedTests = true;
349
+ }
350
+ return;
356
351
  }
357
- });
352
+ console.log(
353
+ APP_PREFIX,
354
+ pc.yellow(`Warning: (${err.response?.status})`),
355
+ `Report couldn't be processed: ${err?.response?.data?.message}`,
356
+ );
357
+ printCreateIssue(err);
358
+ } else {
359
+ console.log(APP_PREFIX, "Report couldn't be processed", err);
360
+ }
361
+ });
358
362
  };
359
363
 
360
364
  /**
@@ -413,12 +417,16 @@ class TestomatioPipe {
413
417
 
414
418
  try {
415
419
  if (this.runId && !this.proceed) {
416
- await this.axios.put(`/api/reporter/${this.runId}`, {
417
- api_key: this.apiKey,
418
- duration: params.duration,
419
- status_event,
420
- detach: params.detach,
421
- tests: params.tests,
420
+ await this.client.request({
421
+ method: 'PUT',
422
+ url: `/api/reporter/${this.runId}`,
423
+ data: {
424
+ api_key: this.apiKey,
425
+ duration: params.duration,
426
+ status_event,
427
+ detach: params.detach,
428
+ tests: params.tests,
429
+ }
422
430
  });
423
431
  if (this.runUrl) {
424
432
  console.log(APP_PREFIX, '📊 Report Saved. Report URL:', pc.magenta(this.runUrl));
@@ -485,13 +493,4 @@ function printCreateIssue(err) {
485
493
  });
486
494
  }
487
495
 
488
- const axiosAddTestrunRequestConfig = {
489
- maxContentLength: Infinity,
490
- maxBodyLength: Infinity,
491
- headers: {
492
- // Overwrite Axios's automatically set Content-Type
493
- 'Content-Type': 'application/json',
494
- },
495
- };
496
-
497
496
  export default TestomatioPipe;
@@ -5,13 +5,9 @@ import fs from 'fs';
5
5
  import isValid from 'is-valid-path';
6
6
  import createDebugMessages from 'debug';
7
7
  import os from 'os';
8
- import { fileURLToPath } from 'url';
9
8
 
10
9
  const debug = createDebugMessages('@testomatio/reporter:util');
11
10
 
12
- // Use __dirname directly since we're compiling to CommonJS
13
- const __dirname = path.resolve();
14
-
15
11
  /**
16
12
  * @param {String} testTitle - Test title
17
13
  *
@@ -111,7 +107,7 @@ const fetchSourceCodeFromStackTrace = (stack = '') => {
111
107
  .join('\n');
112
108
  };
113
109
 
114
- export const TEST_ID_REGEX = /@T([\w\d]{8})/;
110
+ const TEST_ID_REGEX = /@T([\w\d]{8})/;
115
111
 
116
112
  const fetchIdFromCode = (code, opts = {}) => {
117
113
  const comments = code
@@ -154,9 +150,6 @@ const fetchSourceCode = (contents, opts = {}) => {
154
150
  if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`@DisplayName("${title}`));
155
151
  if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`public void ${title}`));
156
152
  if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`${title}(`));
157
- } else if (opts.lang === 'csharp') {
158
- if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`public void ${title}`));
159
- if (lineIndex === -1) lineIndex = lines.findIndex(l => l.includes(`${title}(`));
160
153
  } else {
161
154
  lineIndex = lines.findIndex(l => l.includes(title));
162
155
  }
@@ -360,12 +353,6 @@ function formatStep(step, shift = 0) {
360
353
  return lines;
361
354
  }
362
355
 
363
- export function getPackageVersion() {
364
- const packageJsonPath = path.resolve(__dirname, '../../package.json');
365
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
366
- return packageJson.version;
367
- }
368
-
369
356
  export {
370
357
  ansiRegExp,
371
358
  isSameTest,
package/src/xmlReader.js CHANGED
@@ -13,7 +13,6 @@ import {
13
13
  fetchSourceCodeFromStackTrace,
14
14
  fetchIdFromCode,
15
15
  humanize,
16
- TEST_ID_REGEX,
17
16
  } from './utils/utils.js';
18
17
  import { pipesFactory } from './pipe/index.js';
19
18
  import adapterFactory from './junit-adapter/index.js';
@@ -27,9 +26,8 @@ const debug = createDebugMessages('@testomatio/reporter:xml');
27
26
  const ridRunId = randomUUID();
28
27
 
29
28
  const TESTOMATIO_URL = process.env.TESTOMATIO_URL || 'https://app.testomat.io';
30
- const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_SUITE,
31
- TESTOMATIO_MAX_STACK_TRACE, TESTOMATIO_TITLE, TESTOMATIO_ENV,
32
- TESTOMATIO_RUN, TESTOMATIO_MARK_DETACHED } = process.env;
29
+ const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN, TESTOMATIO_MARK_DETACHED } =
30
+ process.env;
33
31
 
34
32
  const options = {
35
33
  ignoreDeclaration: true,
@@ -39,8 +37,6 @@ const options = {
39
37
  parseTagValue: true,
40
38
  };
41
39
 
42
- const MAX_OUTPUT_LENGTH = parseInt(TESTOMATIO_MAX_STACK_TRACE, 10) || 10000;
43
-
44
40
  const reduceOptions = {};
45
41
 
46
42
  class XmlReader {
@@ -95,7 +91,7 @@ class XmlReader {
95
91
  ];
96
92
 
97
93
  for (const regex of cutRegexes) {
98
- xmlData = xmlData.replace(regex, (_, p1, p2, p3) => `${p1}${p2.substring(0, MAX_OUTPUT_LENGTH)}${p3}`);
94
+ xmlData = xmlData.replace(regex, (_, p1, p2, p3) => `${p1}${p2.substring(0, 5000)}${p3}`);
99
95
  }
100
96
 
101
97
  const jsonResult = this.parser.parse(xmlData);
@@ -345,7 +341,6 @@ class XmlReader {
345
341
  if (file.endsWith('.rb')) this.stats.language = 'ruby';
346
342
  if (file.endsWith('.js')) this.stats.language = 'js';
347
343
  if (file.endsWith('.ts')) this.stats.language = 'ts';
348
- if (file.endsWith('.cs')) this.stats.language = 'csharp';
349
344
  }
350
345
 
351
346
  if (!fs.existsSync(file)) {
@@ -399,14 +394,13 @@ class XmlReader {
399
394
  async uploadArtifacts() {
400
395
  for (const test of this.tests.filter(t => !!t.stack)) {
401
396
  let files = [];
402
- if (!test.files?.length) continue;
403
-
404
- files = test.files.map(f => path.isAbsolute(f) ? f : path.join(process.cwd(), f));
397
+ if (test.files?.length) files = test.files.map(f => path.join(process.cwd(), f));
398
+ files = [...files, ...fetchFilesFromStackTrace(test.stack)];
405
399
 
406
400
  if (!files.length) continue;
407
401
 
408
402
  const runId = this.runId || this.store.runId || Date.now().toString();
409
- test.artifacts = await Promise.all(files.map(f => this.uploader.uploadFileByPath(f, [runId, path.basename(f)])));
403
+ test.artifacts = await Promise.all(files.map(f => this.uploader.uploadFileByPath(f, [runId])));
410
404
  console.log(APP_PREFIX, `🗄️ Uploaded ${pc.bold(`${files.length} artifacts`)} for test ${test.title}`);
411
405
  }
412
406
  }
@@ -477,7 +471,7 @@ function reduceTestCases(prev, item) {
477
471
  testCases
478
472
  .filter(t => !!t)
479
473
  .forEach(testCaseItem => {
480
- const file = testCaseItem.file || item.filepath || item.fullname || '';
474
+ const file = testCaseItem.file || item.filepath || '';
481
475
 
482
476
  let stack = '';
483
477
  let message = '';
@@ -511,38 +505,15 @@ function reduceTestCases(prev, item) {
511
505
  stack = `${
512
506
  testCaseItem['system-out'] || testCaseItem.output || testCaseItem.log || ''
513
507
  }\n\n${stack}\n\n${suiteOutput}\n\n${suiteErr}`.trim();
514
- let testId = fetchIdFromOutput(stack);
515
-
516
- if (tags?.length && !testId) {
517
- testId = tags.filter(t => t.startsWith('T')).map(t => `@${t}`).find(t => t.match(TEST_ID_REGEX))?.slice(2);
518
- }
508
+ const testId = fetchIdFromOutput(stack);
519
509
 
520
510
  let status = STATUS.PASSED.toString();
521
511
  if ('failure' in testCaseItem || 'error' in testCaseItem) status = STATUS.FAILED;
522
512
  if ('skipped' in testCaseItem) status = STATUS.SKIPPED;
523
- if (testCaseItem.result && Object.values(STATUS).includes(testCaseItem.result.toLowerCase())) {
524
- status = testCaseItem.result.toLowerCase();
525
- }
526
513
 
527
514
  let rid = null;
528
515
  if (testCaseItem.id) rid = `${ridRunId}-${testCaseItem.id}`;
529
516
 
530
- // Extract attachments
531
- let files = [];
532
- if (testCaseItem.attachments) {
533
- const attachments = Array.isArray(testCaseItem.attachments.attachment)
534
- ? testCaseItem.attachments.attachment
535
- : [testCaseItem.attachments.attachment];
536
-
537
- files = attachments
538
- .filter(a => a && a.filePath)
539
- .map(a => a.filePath);
540
- }
541
-
542
- // Extract files from stack trace using existing utility
543
- const stackFiles = fetchFilesFromStackTrace(stack);
544
- files = [...new Set([...files, ...stackFiles])]; // Remove duplicates
545
-
546
517
  prev.push({
547
518
  rid,
548
519
  file,
@@ -557,9 +528,7 @@ function reduceTestCases(prev, item) {
557
528
  run_time: parseFloat(testCaseItem.time || testCaseItem.duration) * 1000,
558
529
  status,
559
530
  title,
560
- root_suite_id: TESTOMATIO_SUITE,
561
531
  suite_title: suiteTitle,
562
- files,
563
532
  });
564
533
  });
565
534
  return prev;
@@ -586,15 +555,10 @@ function fetchProperties(item) {
586
555
 
587
556
  if (!item.properties) return {};
588
557
 
589
- // Handle both single property and array of properties
590
- const properties = Array.isArray(item.properties.property)
591
- ? item.properties.property
592
- : [item.properties.property].filter(Boolean);
593
-
594
- const prop = properties.find(p => p.name === 'Description');
558
+ const prop = [item.properties?.property].flat().find(p => p.name === 'Description');
595
559
  if (prop) title = prop.value;
596
-
597
- properties
560
+ [item.properties?.property]
561
+ .flat()
598
562
  .filter(p => p.name === 'Category')
599
563
  .forEach(p => tags.push(p.value));
600
564
  return { title, tags };