@testomatio/reporter 2.3.7-beta.1-xml-import β†’ 2.3.7-beta.11-stack-artifacts

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 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 [JUnit XML](./docs/junit.md) with [XML import configuration](./docs/xml-imports.md)
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)_
@@ -110,6 +110,25 @@ function CodeceptReporter(config) {
110
110
  output.stepShift = 2;
111
111
  index_js_1.services.setContext(null);
112
112
  });
113
+ // mark as failed all tests inside the failed hook
114
+ event.dispatcher.on(event.hook.failed, hook => {
115
+ if (hook.name !== 'BeforeSuiteHook')
116
+ return;
117
+ const suite = hook.runnable.parent;
118
+ if (!suite)
119
+ return;
120
+ const error = hook?.ctx?.currentTest?.err;
121
+ for (const test of suite.tests) {
122
+ client.addTestRun('failed', {
123
+ ...stripExampleFromTitle(test.title),
124
+ rid: test.uid,
125
+ test_id: (0, utils_js_1.getTestomatIdFromTestTitle)(test.title),
126
+ suite_title: stripTagsFromTitle(suite.title),
127
+ error,
128
+ time: hook?.runnable?.duration,
129
+ });
130
+ }
131
+ });
113
132
  event.dispatcher.on(event.suite.before, suite => {
114
133
  data_storage_js_1.dataStorage.setContext(suite.fullTitle());
115
134
  });
@@ -379,7 +398,7 @@ function formatCodeceptStep(step) {
379
398
  if (!step)
380
399
  return null;
381
400
  const category = step.constructor.name === 'HelperStep' ? 'framework' : 'user';
382
- const title = step.toString(); // Use built-in toString
401
+ const title = (0, utils_js_1.truncate)(step); // Use built-in toString
383
402
  const duration = step.duration || 0; // Use built-in duration
384
403
  const formattedStep = {
385
404
  category,
@@ -403,10 +422,11 @@ function formatHookStep(step) {
403
422
  if (step.actor && step.name) {
404
423
  title = `${step.actor} ${step.name}`;
405
424
  if (step.args && step.args.length > 0) {
406
- const argsStr = step.args.map(arg => JSON.stringify(arg)).join(', ');
425
+ const argsStr = step.args.map(arg => (0, utils_js_1.truncate)(JSON.stringify(arg))).join(', ');
407
426
  title += ` ${argsStr}`;
408
427
  }
409
428
  }
429
+ title = (0, utils_js_1.truncate)(title);
410
430
  return {
411
431
  category: 'hook',
412
432
  title,
package/lib/bin/cli.js CHANGED
File without changes
@@ -37,10 +37,7 @@ program
37
37
  lang = lang?.toLowerCase();
38
38
  if (javaTests === true || (lang === 'java' && !javaTests))
39
39
  javaTests = 'src/test/java';
40
- const runReader = new xmlReader_js_1.default({
41
- javaTests,
42
- lang,
43
- });
40
+ const runReader = new xmlReader_js_1.default({ javaTests, lang });
44
41
  const files = glob_1.glob.sync(pattern, { cwd: opts.dir || process.cwd() });
45
42
  if (!files.length) {
46
43
  console.log(constants_js_1.APP_PREFIX, `Report can't be created. No XML files found πŸ˜₯`);
File without changes
File without changes
package/lib/client.js CHANGED
@@ -51,7 +51,9 @@ const node_url_1 = require("node:url");
51
51
  const uploader_js_1 = require("./uploader.js");
52
52
  const utils_js_1 = require("./utils/utils.js");
53
53
  const filesize_1 = require("filesize");
54
+ const util_1 = require("util");
54
55
  const debug = (0, debug_1.default)('@testomatio/reporter:client');
56
+ const stripColors = util_1.stripVTControlCharacters || ((str) => str?.replace(/\x1b\[[0-9;]*m/g, '') || '');
55
57
  // removed __dirname usage, because:
56
58
  // 1. replaced with ESM syntax (import.meta.url), but it throws an error on tsc compilation;
57
59
  // 2. got error "__dirname already defined" in compiles js code (cjs dir)
@@ -158,17 +160,6 @@ class Client {
158
160
  * @returns {Promise<PipeResult[]>}
159
161
  */
160
162
  async addTestRun(status, testData) {
161
- if (!this.pipes || !this.pipes.length)
162
- this.pipes = await (0, index_js_1.pipesFactory)(this.paramsForPipesFactory || {}, this.pipeStore);
163
- // all pipes disabled, skipping
164
- if (!this.pipes?.filter(p => p.isEnabled).length)
165
- return [];
166
- if (isTestShouldBeExculedFromReport(testData))
167
- return [];
168
- if (status === constants_js_1.STATUS.SKIPPED && process.env.TESTOMATIO_EXCLUDE_SKIPPED) {
169
- debug('Skipping test from report', testData?.title);
170
- return []; // do not log skipped tests
171
- }
172
163
  if (!testData)
173
164
  testData = {
174
165
  title: 'Unknown test',
@@ -181,9 +172,12 @@ class Client {
181
172
  /**
182
173
  * @type {TestData}
183
174
  */
184
- const { rid, error = null, time = 0, example = null, files = [], filesBuffers = [], steps, code = null, title, file, suite_title, suite_id, test_id, timestamp, links, manuallyAttachedArtifacts, overwrite, tags, } = testData;
175
+ const { rid, error = null, steps: originalSteps, title, suite_title, } = testData;
176
+ let steps = originalSteps;
177
+ const uploadedFiles = [];
178
+ const stackArtifactsEnabled = (0, utils_js_1.transformEnvVarToBoolean)(process.env.TESTOMATIO_STACK_ARTIFACTS);
179
+ const { time = 0, example = null, files = [], filesBuffers = [], code = null, file, suite_id, test_id, timestamp, links, manuallyAttachedArtifacts, overwrite, tags, } = testData;
185
180
  let { message = '', meta = {} } = testData;
186
- // stringify meta values and limit keys and values length to 255
187
181
  meta = Object.entries(meta)
188
182
  .filter(([, value]) => value !== null && value !== undefined)
189
183
  .reduce((acc, [key, value]) => {
@@ -191,19 +185,34 @@ class Client {
191
185
  acc[key] = value;
192
186
  return acc;
193
187
  }, {});
194
- // Get links from storage using the test context
195
188
  const testContext = suite_title ? `${suite_title} ${title}` : title;
196
189
  let errorFormatted = '';
197
190
  if (error) {
198
191
  errorFormatted += this.formatError(error) || '';
199
192
  message = error?.message;
200
193
  }
201
- // Attach logs
202
- const fullLogs = this.formatLogs({ error: errorFormatted, steps, logs: testData.logs });
203
- // add artifacts
194
+ let fullLogs = this.formatLogs({ error: errorFormatted, steps, logs: testData.logs });
195
+ if (stackArtifactsEnabled && fullLogs?.trim()?.length > 0) {
196
+ uploadedFiles.push(this.uploader.uploadFileAsBuffer(Buffer.from(stripColors(fullLogs), 'utf8'), [this.runId, rid, `logs_${+new Date}.log`]));
197
+ fullLogs = '';
198
+ steps = null;
199
+ }
200
+ if (!this.pipes || !this.pipes.length)
201
+ this.pipes = await (0, index_js_1.pipesFactory)(this.paramsForPipesFactory || {}, this.pipeStore);
202
+ if (!this.pipes?.filter(p => p.isEnabled).length) {
203
+ if (uploadedFiles.length > 0) {
204
+ await Promise.all(uploadedFiles);
205
+ }
206
+ return [];
207
+ }
208
+ if (isTestShouldBeExculedFromReport(testData))
209
+ return [];
210
+ if (status === constants_js_1.STATUS.SKIPPED && process.env.TESTOMATIO_EXCLUDE_SKIPPED) {
211
+ debug('Skipping test from report', testData?.title);
212
+ return [];
213
+ }
204
214
  if (manuallyAttachedArtifacts?.length)
205
215
  files.push(...manuallyAttachedArtifacts);
206
- const uploadedFiles = [];
207
216
  for (let f of files) {
208
217
  if (!f)
209
218
  continue; // f === null
@@ -285,7 +294,7 @@ class Client {
285
294
  const uploadedArtifacts = this.uploader.successfulUploads.map(file => ({
286
295
  relativePath: file.path.replace(process.cwd(), ''),
287
296
  link: file.link,
288
- sizePretty: (0, filesize_1.filesize)(file.size, { round: 0 }).toString(),
297
+ sizePretty: file.size == null ? 'unknown' : (0, filesize_1.filesize)(file.size, { round: 0 }).toString(),
289
298
  }));
290
299
  uploadedArtifacts.forEach(upload => {
291
300
  debug(`🟒Uploaded artifact`, `${upload.relativePath},`, 'size:', `${upload.sizePretty},`, 'link:', `${upload.link}`);
@@ -295,7 +304,7 @@ class Client {
295
304
  console.log(constants_js_1.APP_PREFIX, `πŸ—„οΈ ${this.uploader.failedUploads.length} artifacts πŸ”΄${picocolors_1.default.bold('failed')} to upload`);
296
305
  const failedUploads = this.uploader.failedUploads.map(file => ({
297
306
  relativePath: file.path.replace(process.cwd(), ''),
298
- sizePretty: (0, filesize_1.filesize)(file.size, { round: 0 }).toString(),
307
+ sizePretty: file.size == null ? 'unknown' : (0, filesize_1.filesize)(file.size, { round: 0 }).toString(),
299
308
  }));
300
309
  const pathPadding = Math.max(...failedUploads.map(upload => upload.relativePath.length)) + 1;
301
310
  failedUploads.forEach(upload => {
@@ -329,7 +338,7 @@ class Client {
329
338
  */
330
339
  formatLogs({ error, steps, logs }) {
331
340
  error = error?.trim();
332
- logs = logs?.trim();
341
+ logs = logs?.trim().split('\n').map(l => (0, utils_js_1.truncate)(l)).join('\n');
333
342
  if (Array.isArray(steps)) {
334
343
  steps = steps
335
344
  .map(step => (0, utils_js_1.formatStep)(step))
@@ -1,4 +1,5 @@
1
1
  export default CSharpAdapter;
2
2
  declare class CSharpAdapter extends Adapter {
3
+ getFilePath(t: any): string;
3
4
  }
4
5
  import Adapter from './adapter.js';
@@ -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
- // Don't override example if it already exists from NUnit XML processing
11
- // The xmlReader.js already extracts parameters correctly from <arguments>
12
- if (!t.example) {
13
- const title = t.title.replace(/\(.*?\)/, '').trim();
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
- if (!t.file)
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('/')}.cs`;
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 ?? (process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD ? false : true),
21
+ isEnabled: this.params.isBatchEnabled ?? !process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD ?? true,
22
22
  intervalFunction: null,
23
23
  intervalTime: 5000,
24
24
  tests: [],
@@ -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 ?? (process.env.TESTOMATIO_DISABLE_BATCH_UPLOAD ? false : true),
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,9 +408,8 @@ class TestomatioPipe {
412
408
  status_event,
413
409
  detach: params.detach,
414
410
  tests: params.tests,
415
- },
411
+ }
416
412
  });
417
- debug(constants_js_1.APP_PREFIX, 'βœ… Testrun finished');
418
413
  if (this.runUrl) {
419
414
  console.log(constants_js_1.APP_PREFIX, 'πŸ“Š Report Saved. Report URL:', picocolors_1.default.magenta(this.runUrl));
420
415
  }
package/lib/reporter.d.ts CHANGED
@@ -1,3 +1,10 @@
1
+ export { Client };
2
+ export const STATUS: {
3
+ PASSED: string;
4
+ FAILED: string;
5
+ SKIPPED: string;
6
+ FINISHED: string;
7
+ };
1
8
  export const artifact: (data: string | {
2
9
  path: string;
3
10
  type: string;
@@ -80,7 +87,7 @@ export const label: (key: string, value?: string | null) => void;
80
87
  export const linkTest: (...testIds: string[]) => void;
81
88
  export const linkJira: (...jiraIds: string[]) => void;
82
89
  declare namespace _default {
83
- let testomatioLogger: {
90
+ export let testomatioLogger: {
84
91
  "__#13@#originalUserLogger": {
85
92
  assert(condition?: boolean, ...data: any[]): void;
86
93
  assert(value: any, message?: string, ...optionalParams: any[]): void;
@@ -148,13 +155,13 @@ declare namespace _default {
148
155
  }): void;
149
156
  prettyObjects: boolean;
150
157
  };
151
- let artifact: (data: string | {
158
+ export let artifact: (data: string | {
152
159
  path: string;
153
160
  type: string;
154
161
  name: string;
155
162
  }, context?: any) => void;
156
- let log: (...args: any[]) => void;
157
- let logger: {
163
+ export let log: (...args: any[]) => void;
164
+ export let logger: {
158
165
  "__#13@#originalUserLogger": {
159
166
  assert(condition?: boolean, ...data: any[]): void;
160
167
  assert(value: any, message?: string, ...optionalParams: any[]): void;
@@ -222,13 +229,15 @@ declare namespace _default {
222
229
  }): void;
223
230
  prettyObjects: boolean;
224
231
  };
225
- let meta: (keyValue: {
232
+ export let meta: (keyValue: {
226
233
  [key: string]: string;
227
234
  } | string, value?: string | null) => void;
228
- let step: (message: string) => void;
229
- let label: (key: string, value?: string | null) => void;
230
- let linkTest: (...testIds: string[]) => void;
231
- let linkJira: (...jiraIds: string[]) => void;
235
+ export let step: (message: string) => void;
236
+ export let label: (key: string, value?: string | null) => void;
237
+ export let linkTest: (...testIds: string[]) => void;
238
+ export let linkJira: (...jiraIds: string[]) => void;
239
+ export { Client as TestomatioClient };
240
+ export { STATUS };
232
241
  }
233
242
  export default _default;
234
243
  export type ArtifactFunction = typeof import("./reporter-functions.js").default.artifact;
@@ -237,3 +246,4 @@ export type LoggerService = typeof import("./services/index.js").services.logger
237
246
  export type MetaFunction = typeof import("./reporter-functions.js").default.keyValue;
238
247
  export type StepFunction = typeof import("./reporter-functions.js").default.step;
239
248
  export type LabelFunction = typeof import("./reporter-functions.js").default.label;
249
+ import Client from './client.js';
package/lib/reporter.js CHANGED
@@ -1,13 +1,48 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
37
  };
5
38
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.linkJira = exports.linkTest = exports.label = exports.step = exports.meta = exports.logger = exports.log = exports.artifact = void 0;
7
- // import TestomatClient from './client.js';
8
- // import * as TRConstants from './constants.js';
39
+ exports.linkJira = exports.linkTest = exports.label = exports.step = exports.meta = exports.logger = exports.log = exports.artifact = exports.STATUS = exports.Client = void 0;
40
+ const client_js_1 = __importDefault(require("./client.js"));
41
+ exports.Client = client_js_1.default;
42
+ const TestomatioConstants = __importStar(require("./constants.js"));
9
43
  const index_js_1 = require("./services/index.js");
10
44
  const reporter_functions_js_1 = __importDefault(require("./reporter-functions.js"));
45
+ exports.STATUS = TestomatioConstants.STATUS;
11
46
  exports.artifact = reporter_functions_js_1.default.artifact;
12
47
  exports.log = reporter_functions_js_1.default.log;
13
48
  exports.logger = index_js_1.services.logger;
@@ -37,6 +72,6 @@ module.exports = {
37
72
  label: reporter_functions_js_1.default.label,
38
73
  linkTest: reporter_functions_js_1.default.linkTest,
39
74
  linkJira: reporter_functions_js_1.default.linkJira,
40
- // TestomatClient,
41
- // TRConstants,
75
+ TestomatioClient: client_js_1.default,
76
+ STATUS: exports.STATUS,
42
77
  };
package/lib/uploader.js CHANGED
@@ -170,10 +170,6 @@ 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
- }
177
173
  const data = { rid, file: filePath, uploaded };
178
174
  const jsonLine = `${JSON.stringify(data)}\n`;
179
175
  fs_1.default.appendFileSync(tempFilePath, jsonLine);
@@ -2,6 +2,7 @@ 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;
5
6
  export function cleanLatestRunId(): any;
6
7
  export function isSameTest(test: any, t: any): boolean;
7
8
  export function fetchSourceCode(contents: any, opts?: {}): string;
@@ -38,6 +38,7 @@ 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;
41
42
  exports.cleanLatestRunId = cleanLatestRunId;
42
43
  exports.formatStep = formatStep;
43
44
  exports.readLatestRunId = readLatestRunId;
@@ -121,8 +122,12 @@ const fetchFilesFromStackTrace = (stack = '', checkExists = true) => {
121
122
  .map(f => f[1].trim())
122
123
  .map(f => f.replace(/^\/+/, '/').replace(/^\/([A-Za-z]:)/, '$1')) // Remove extra slashes, handle Windows paths
123
124
  .map(f => {
124
- // Normalize path separators for cross-platform compatibility
125
- return f.replace(/\\/g, '/');
125
+ // Convert Windows paths to Linux paths for testing purposes
126
+ if (f.match(/^[A-Za-z]:[\\\/]/)) {
127
+ // Convert Windows path to Linux equivalent for test scenarios
128
+ return f.replace(/^[A-Za-z]:[\\\/]/, '/').replace(/\\/g, '/');
129
+ }
130
+ return f;
126
131
  });
127
132
  debug('Found files in stack trace: ', files);
128
133
  return files.filter(f => {
@@ -169,8 +174,6 @@ exports.fetchSourceCodeFromStackTrace = fetchSourceCodeFromStackTrace;
169
174
  exports.TEST_ID_REGEX = /@T([\w\d]{8})/;
170
175
  exports.SUITE_ID_REGEX = /@S([\w\d]{8})/;
171
176
  const fetchIdFromCode = (code, opts = {}) => {
172
- if (!code)
173
- return null;
174
177
  const comments = code
175
178
  .split('\n')
176
179
  .map(l => l.trim())
@@ -213,29 +216,10 @@ const fetchSourceCode = (contents, opts = {}) => {
213
216
  lineIndex = lines.findIndex(l => l.includes(`${title}(`));
214
217
  }
215
218
  else if (opts.lang === 'csharp') {
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) {
219
+ if (lineIndex === -1)
220
+ lineIndex = lines.findIndex(l => l.includes(`public void ${title}`));
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
- }
239
223
  }
240
224
  else {
241
225
  lineIndex = lines.findIndex(l => l.includes(title));
@@ -244,7 +228,7 @@ const fetchSourceCode = (contents, opts = {}) => {
244
228
  if (opts.prepend) {
245
229
  lineIndex -= opts.prepend;
246
230
  }
247
- if (lineIndex !== -1 && lineIndex !== undefined) {
231
+ if (lineIndex) {
248
232
  const result = [];
249
233
  for (let i = lineIndex; i < lineIndex + limit; i++) {
250
234
  if (lines[i] === undefined)
@@ -287,14 +271,6 @@ const fetchSourceCode = (contents, opts = {}) => {
287
271
  break;
288
272
  if (opts.lang === 'java' && lines[i].includes(' class '))
289
273
  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;
298
274
  }
299
275
  result.push(lines[i]);
300
276
  }
@@ -494,9 +470,21 @@ function transformEnvVarToBoolean(value) {
494
470
  // if not recognized, return truthy if any value is set
495
471
  return Boolean(value);
496
472
  }
473
+ function truncate(s, size = 255) {
474
+ if (s === undefined || s === null) {
475
+ return '';
476
+ }
477
+ const str = s.toString();
478
+ if (str.trim().length < size) {
479
+ return str;
480
+ }
481
+ return `${str.substring(0, size)}...`;
482
+ }
497
483
 
498
484
  module.exports.getPackageVersion = getPackageVersion;
499
485
 
486
+ module.exports.truncate = truncate;
487
+
500
488
  module.exports.cleanLatestRunId = cleanLatestRunId;
501
489
 
502
490
  module.exports.formatStep = formatStep;