@testomatio/reporter 2.3.8-rc.1 β†’ 2.3.9-beta-bin-fix

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)_
package/lib/bin/cli.js CHANGED
@@ -37,17 +37,12 @@ program
37
37
  program
38
38
  .command('start')
39
39
  .description('Start a new run and return its ID')
40
- .option('--kind <type>', 'Specify run type: automated, manual, or mixed')
41
- .action(async (opts) => {
40
+ .action(async () => {
42
41
  (0, utils_js_1.cleanLatestRunId)();
43
42
  console.log('Starting a new Run on Testomat.io...');
44
43
  const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || config_js_1.config.TESTOMATIO;
45
44
  const client = new client_js_1.default({ apiKey });
46
- const createRunParams = {};
47
- if (opts.kind) {
48
- createRunParams.kind = opts.kind;
49
- }
50
- client.createRun(createRunParams).then(() => {
45
+ client.createRun().then(() => {
51
46
  console.log(process.env.runId);
52
47
  process.exit(0);
53
48
  });
@@ -75,7 +70,6 @@ program
75
70
  .description('Run tests with the specified command')
76
71
  .argument('<command>', 'Test runner command')
77
72
  .option('--filter <filter>', 'Additional execution filter')
78
- .option('--kind <type>', 'Specify run type: automated, manual, or mixed')
79
73
  .action(async (command, opts) => {
80
74
  const apiKey = process.env['INPUT_TESTOMATIO-KEY'] || config_js_1.config.TESTOMATIO;
81
75
  const title = process.env.TESTOMATIO_TITLE;
@@ -114,12 +108,8 @@ program
114
108
  process.exit(code);
115
109
  });
116
110
  };
117
- const createRunParams = {};
118
- if (opts.kind) {
119
- createRunParams.kind = opts.kind;
120
- }
121
111
  if (apiKey) {
122
- await client.createRun(createRunParams).then(runTests);
112
+ await client.createRun().then(runTests);
123
113
  }
124
114
  else {
125
115
  await runTests();
File without changes
File without changes
File without changes
package/lib/client.d.ts CHANGED
@@ -44,7 +44,7 @@ export class Client {
44
44
  *
45
45
  * @returns {Promise<any>} - resolves to Run id which should be used to update / add test
46
46
  */
47
- createRun(params?: {}): Promise<any>;
47
+ createRun(params: any): Promise<any>;
48
48
  /**
49
49
  * Updates test status and its data
50
50
  *
package/lib/client.js CHANGED
@@ -51,9 +51,7 @@ 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");
55
54
  const debug = (0, debug_1.default)('@testomatio/reporter:client');
56
- const stripColors = util_1.stripVTControlCharacters || ((str) => str?.replace(/\x1b\[[0-9;]*m/g, '') || '');
57
55
  // removed __dirname usage, because:
58
56
  // 1. replaced with ESM syntax (import.meta.url), but it throws an error on tsc compilation;
59
57
  // 2. got error "__dirname already defined" in compiles js code (cjs dir)
@@ -131,7 +129,7 @@ class Client {
131
129
  *
132
130
  * @returns {Promise<any>} - resolves to Run id which should be used to update / add test
133
131
  */
134
- async createRun(params = {}) {
132
+ async createRun(params) {
135
133
  if (!this.pipes || !this.pipes.length)
136
134
  this.pipes = await (0, index_js_1.pipesFactory)(params || this.paramsForPipesFactory || {}, this.pipeStore);
137
135
  debug('Creating run...');
@@ -139,7 +137,7 @@ class Client {
139
137
  if (!this.pipes?.filter(p => p.isEnabled).length)
140
138
  return Promise.resolve();
141
139
  this.queue = this.queue
142
- .then(() => Promise.all(this.pipes.map(p => p.createRun(params))))
140
+ .then(() => Promise.all(this.pipes.map(p => p.createRun())))
143
141
  .catch(err => console.log(constants_js_1.APP_PREFIX, err))
144
142
  .then(() => {
145
143
  const runId = this.pipeStore?.runId;
@@ -160,6 +158,17 @@ class Client {
160
158
  * @returns {Promise<PipeResult[]>}
161
159
  */
162
160
  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
+ }
163
172
  if (!testData)
164
173
  testData = {
165
174
  title: 'Unknown test',
@@ -172,12 +181,9 @@ class Client {
172
181
  /**
173
182
  * @type {TestData}
174
183
  */
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;
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;
180
185
  let { message = '', meta = {} } = testData;
186
+ // stringify meta values and limit keys and values length to 255
181
187
  meta = Object.entries(meta)
182
188
  .filter(([, value]) => value !== null && value !== undefined)
183
189
  .reduce((acc, [key, value]) => {
@@ -185,34 +191,19 @@ class Client {
185
191
  acc[key] = value;
186
192
  return acc;
187
193
  }, {});
194
+ // Get links from storage using the test context
188
195
  const testContext = suite_title ? `${suite_title} ${title}` : title;
189
196
  let errorFormatted = '';
190
197
  if (error) {
191
198
  errorFormatted += this.formatError(error) || '';
192
199
  message = error?.message;
193
200
  }
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
- }
201
+ // Attach logs
202
+ const fullLogs = this.formatLogs({ error: errorFormatted, steps, logs: testData.logs });
203
+ // add artifacts
214
204
  if (manuallyAttachedArtifacts?.length)
215
205
  files.push(...manuallyAttachedArtifacts);
206
+ const uploadedFiles = [];
216
207
  for (let f of files) {
217
208
  if (!f)
218
209
  continue; // f === null
@@ -294,7 +285,7 @@ class Client {
294
285
  const uploadedArtifacts = this.uploader.successfulUploads.map(file => ({
295
286
  relativePath: file.path.replace(process.cwd(), ''),
296
287
  link: file.link,
297
- sizePretty: file.size == null ? 'unknown' : (0, filesize_1.filesize)(file.size, { round: 0 }).toString(),
288
+ sizePretty: (0, filesize_1.filesize)(file.size, { round: 0 }).toString(),
298
289
  }));
299
290
  uploadedArtifacts.forEach(upload => {
300
291
  debug(`🟒Uploaded artifact`, `${upload.relativePath},`, 'size:', `${upload.sizePretty},`, 'link:', `${upload.link}`);
@@ -304,7 +295,7 @@ class Client {
304
295
  console.log(constants_js_1.APP_PREFIX, `πŸ—„οΈ ${this.uploader.failedUploads.length} artifacts πŸ”΄${picocolors_1.default.bold('failed')} to upload`);
305
296
  const failedUploads = this.uploader.failedUploads.map(file => ({
306
297
  relativePath: file.path.replace(process.cwd(), ''),
307
- sizePretty: file.size == null ? 'unknown' : (0, filesize_1.filesize)(file.size, { round: 0 }).toString(),
298
+ sizePretty: (0, filesize_1.filesize)(file.size, { round: 0 }).toString(),
308
299
  }));
309
300
  const pathPadding = Math.max(...failedUploads.map(upload => upload.relativePath.length)) + 1;
310
301
  failedUploads.forEach(upload => {
@@ -47,12 +47,11 @@ declare class TestomatioPipe implements Pipe {
47
47
  prepareRun(opts: any): Promise<string[]>;
48
48
  /**
49
49
  * Creates a new run on Testomat.io
50
- * @param {{isBatchEnabled?: boolean, kind?: string}} params
50
+ * @param {{isBatchEnabled?: boolean}} params
51
51
  * @returns Promise<void>
52
52
  */
53
53
  createRun(params?: {
54
54
  isBatchEnabled?: boolean;
55
- kind?: string;
56
55
  }): Promise<void>;
57
56
  runUrl: string;
58
57
  runPublicUrl: any;
@@ -148,7 +148,7 @@ class TestomatioPipe {
148
148
  }
149
149
  /**
150
150
  * Creates a new run on Testomat.io
151
- * @param {{isBatchEnabled?: boolean, kind?: string}} params
151
+ * @param {{isBatchEnabled?: boolean}} params
152
152
  * @returns Promise<void>
153
153
  */
154
154
  async createRun(params = {}) {
@@ -184,7 +184,6 @@ class TestomatioPipe {
184
184
  label: this.label,
185
185
  shared_run: this.sharedRun,
186
186
  shared_run_timeout: this.sharedRunTimeout,
187
- kind: params.kind,
188
187
  }).filter(([, value]) => !!value));
189
188
  debug(' >>>>>> Run params', JSON.stringify(runParams, null, 2));
190
189
  if (this.runId) {
package/lib/reporter.d.ts CHANGED
@@ -1,10 +1,3 @@
1
- export { Client };
2
- export const STATUS: {
3
- PASSED: string;
4
- FAILED: string;
5
- SKIPPED: string;
6
- FINISHED: string;
7
- };
8
1
  export const artifact: (data: string | {
9
2
  path: string;
10
3
  type: string;
@@ -87,7 +80,7 @@ export const label: (key: string, value?: string | null) => void;
87
80
  export const linkTest: (...testIds: string[]) => void;
88
81
  export const linkJira: (...jiraIds: string[]) => void;
89
82
  declare namespace _default {
90
- export let testomatioLogger: {
83
+ let testomatioLogger: {
91
84
  "__#13@#originalUserLogger": {
92
85
  assert(condition?: boolean, ...data: any[]): void;
93
86
  assert(value: any, message?: string, ...optionalParams: any[]): void;
@@ -155,13 +148,13 @@ declare namespace _default {
155
148
  }): void;
156
149
  prettyObjects: boolean;
157
150
  };
158
- export let artifact: (data: string | {
151
+ let artifact: (data: string | {
159
152
  path: string;
160
153
  type: string;
161
154
  name: string;
162
155
  }, context?: any) => void;
163
- export let log: (...args: any[]) => void;
164
- export let logger: {
156
+ let log: (...args: any[]) => void;
157
+ let logger: {
165
158
  "__#13@#originalUserLogger": {
166
159
  assert(condition?: boolean, ...data: any[]): void;
167
160
  assert(value: any, message?: string, ...optionalParams: any[]): void;
@@ -229,15 +222,13 @@ declare namespace _default {
229
222
  }): void;
230
223
  prettyObjects: boolean;
231
224
  };
232
- export let meta: (keyValue: {
225
+ let meta: (keyValue: {
233
226
  [key: string]: string;
234
227
  } | string, value?: string | null) => 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 };
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;
241
232
  }
242
233
  export default _default;
243
234
  export type ArtifactFunction = typeof import("./reporter-functions.js").default.artifact;
@@ -246,4 +237,3 @@ export type LoggerService = typeof import("./services/index.js").services.logger
246
237
  export type MetaFunction = typeof import("./reporter-functions.js").default.keyValue;
247
238
  export type StepFunction = typeof import("./reporter-functions.js").default.step;
248
239
  export type LabelFunction = typeof import("./reporter-functions.js").default.label;
249
- import Client from './client.js';
package/lib/reporter.js CHANGED
@@ -1,48 +1,13 @@
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
- })();
35
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
4
  };
38
5
  Object.defineProperty(exports, "__esModule", { value: true });
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"));
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';
43
9
  const index_js_1 = require("./services/index.js");
44
10
  const reporter_functions_js_1 = __importDefault(require("./reporter-functions.js"));
45
- exports.STATUS = TestomatioConstants.STATUS;
46
11
  exports.artifact = reporter_functions_js_1.default.artifact;
47
12
  exports.log = reporter_functions_js_1.default.log;
48
13
  exports.logger = index_js_1.services.logger;
@@ -72,6 +37,6 @@ module.exports = {
72
37
  label: reporter_functions_js_1.default.label,
73
38
  linkTest: reporter_functions_js_1.default.linkTest,
74
39
  linkJira: reporter_functions_js_1.default.linkJira,
75
- TestomatioClient: client_js_1.default,
76
- STATUS: exports.STATUS,
40
+ // TestomatClient,
41
+ // TRConstants,
77
42
  };
@@ -471,14 +471,10 @@ function transformEnvVarToBoolean(value) {
471
471
  return Boolean(value);
472
472
  }
473
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;
474
+ if (s.toString().trim().length < size) {
475
+ return s.toString();
480
476
  }
481
- return `${str.substring(0, size)}...`;
477
+ return `${s.toString().substring(0, size)}...`;
482
478
  }
483
479
 
484
480
  module.exports.getPackageVersion = getPackageVersion;
package/package.json CHANGED
@@ -1,13 +1,11 @@
1
1
  {
2
2
  "name": "@testomatio/reporter",
3
- "version": "2.3.8-rc.1",
3
+ "version": "2.3.9-beta-bin-fix",
4
4
  "description": "Testomatio Reporter Client",
5
5
  "engines": {
6
6
  "node": ">=18"
7
7
  },
8
- "main": "lib/reporter.js",
9
- "module": "src/reporter.js",
10
- "types": "types/types.d.ts",
8
+ "typings": "typings/index.d.ts",
11
9
  "repository": "git@github.com:testomatio/reporter.git",
12
10
  "author": "Michael Bodnarchuk <davert@testomat.io>,Koushik Mohan <koushikmohan1996@gmail.com>",
13
11
  "license": "MIT",
@@ -47,8 +45,7 @@
47
45
  "bin",
48
46
  "lib",
49
47
  "src",
50
- "testcafe",
51
- "types"
48
+ "testcafe"
52
49
  ],
53
50
  "scripts": {
54
51
  "clear-exportdir": "rm -rf export/",
@@ -60,8 +57,7 @@
60
57
  "test": "mocha 'tests/unit/**/*_test.js'",
61
58
  "test:playwright": "mocha tests/adapter/playwright.test.js",
62
59
  "test:codecept": "mocha tests/adapter/codecept.test.js tests/adapter/codecept_comprehensive.test.js tests/adapter/codecept_steps_sections.test.js",
63
- "test:vitest": "mocha tests/adapter/vitest.test.js",
64
- "test:frameworks": "npm run test:playwright && npm run test:codecept && npm run test:vitest",
60
+ "test:frameworks": "npm run test:playwright && npm run test:codecept",
65
61
  "test:all": "npm run test && npm run test:frameworks",
66
62
  "test:adapters": "mocha tests/adapter/*.test.js",
67
63
  "test:codecept:bug948": "mocha tests/adapter/codecept_aftersuite_failure.test.js",
@@ -106,7 +102,7 @@
106
102
  "vitest": "^1.6.0"
107
103
  },
108
104
  "bin": {
109
- "@testomatio/reporter": "./lib/bin/cli.js",
105
+ "testomatio/reporter": "./lib/bin/cli.js",
110
106
  "report-xml": "./lib/bin/reportXml.js",
111
107
  "start-test-run": "./lib/bin/startTest.js",
112
108
  "upload-artifacts": "./lib/bin/uploadArtifacts.js"
package/src/bin/cli.js CHANGED
@@ -158,7 +158,7 @@ program
158
158
  .option('--lang <lang>', 'Language used (python, ruby, java)')
159
159
  .option('--timelimit <time>', 'default time limit in seconds to kill a stuck process')
160
160
  .action(async (pattern, opts) => {
161
- if (!pattern.endsWith('.xml') && !pattern.includes('*')) {
161
+ if (!pattern.endsWith('.xml')) {
162
162
  pattern += '.xml';
163
163
  }
164
164
  let { javaTests, lang } = opts;
@@ -23,7 +23,7 @@ program
23
23
  .option('--timelimit <time>', 'default time limit in seconds to kill a stuck process')
24
24
  .option('--env-file <envfile>', 'Load environment variables from env file')
25
25
  .action(async (pattern, opts) => {
26
- if (!pattern.endsWith('.xml') && !pattern.includes('*')) {
26
+ if (!pattern.endsWith('.xml')) {
27
27
  pattern += '.xml';
28
28
  }
29
29
  let { javaTests, lang } = opts;
@@ -34,10 +34,7 @@ program
34
34
  }
35
35
  lang = lang?.toLowerCase();
36
36
  if (javaTests === true || (lang === 'java' && !javaTests)) javaTests = 'src/test/java';
37
- const runReader = new XmlReader({
38
- javaTests,
39
- lang,
40
- });
37
+ const runReader = new XmlReader({ javaTests, lang });
41
38
  const files = glob.sync(pattern, { cwd: opts.dir || process.cwd() });
42
39
  if (!files.length) {
43
40
  console.log(APP_PREFIX, `Report can't be created. No XML files found πŸ˜₯`);
package/src/client.js CHANGED
@@ -10,21 +10,11 @@ import { glob } from 'glob';
10
10
  import path, { sep } from 'path';
11
11
  import { fileURLToPath } from 'node:url';
12
12
  import { S3Uploader } from './uploader.js';
13
- import {
14
- formatStep,
15
- truncate,
16
- readLatestRunId,
17
- storeRunId,
18
- validateSuiteId,
19
- transformEnvVarToBoolean
20
- } from './utils/utils.js';
13
+ import { formatStep, truncate, readLatestRunId, storeRunId, validateSuiteId } from './utils/utils.js';
21
14
  import { filesize as prettyBytes } from 'filesize';
22
- import { stripVTControlCharacters } from 'util';
23
15
 
24
16
  const debug = createDebugMessages('@testomatio/reporter:client');
25
17
 
26
- const stripColors = stripVTControlCharacters || ((str) => str?.replace(/\x1b\[[0-9;]*m/g, '') || '');
27
-
28
18
  // removed __dirname usage, because:
29
19
  // 1. replaced with ESM syntax (import.meta.url), but it throws an error on tsc compilation;
30
20
  // 2. got error "__dirname already defined" in compiles js code (cjs dir)
@@ -149,6 +139,19 @@ class Client {
149
139
  * @returns {Promise<PipeResult[]>}
150
140
  */
151
141
  async addTestRun(status, testData) {
142
+ if (!this.pipes || !this.pipes.length)
143
+ this.pipes = await pipesFactory(this.paramsForPipesFactory || {}, this.pipeStore);
144
+
145
+ // all pipes disabled, skipping
146
+ if (!this.pipes?.filter(p => p.isEnabled).length) return [];
147
+
148
+ if (isTestShouldBeExculedFromReport(testData)) return [];
149
+
150
+ if (status === STATUS.SKIPPED && process.env.TESTOMATIO_EXCLUDE_SKIPPED) {
151
+ debug('Skipping test from report', testData?.title);
152
+ return []; // do not log skipped tests
153
+ }
154
+
152
155
  if (!testData)
153
156
  testData = {
154
157
  title: 'Unknown test',
@@ -166,23 +169,15 @@ class Client {
166
169
  const {
167
170
  rid,
168
171
  error = null,
169
- steps: originalSteps,
170
- title,
171
- suite_title,
172
- } = testData;
173
- let steps = originalSteps;
174
-
175
- const uploadedFiles = [];
176
- const stackArtifactsEnabled = transformEnvVarToBoolean(process.env.TESTOMATIO_STACK_ARTIFACTS);
177
-
178
-
179
- const {
180
172
  time = 0,
181
173
  example = null,
182
174
  files = [],
183
175
  filesBuffers = [],
176
+ steps,
184
177
  code = null,
178
+ title,
185
179
  file,
180
+ suite_title,
186
181
  suite_id,
187
182
  test_id,
188
183
  timestamp,
@@ -193,6 +188,7 @@ class Client {
193
188
  } = testData;
194
189
  let { message = '', meta = {} } = testData;
195
190
 
191
+ // stringify meta values and limit keys and values length to 255
196
192
  meta = Object.entries(meta)
197
193
  .filter(([, value]) => value !== null && value !== undefined)
198
194
  .reduce((acc, [key, value]) => {
@@ -200,6 +196,7 @@ class Client {
200
196
  return acc;
201
197
  }, {});
202
198
 
199
+ // Get links from storage using the test context
203
200
  const testContext = suite_title ? `${suite_title} ${title}` : title;
204
201
 
205
202
  let errorFormatted = '';
@@ -208,39 +205,14 @@ class Client {
208
205
  message = error?.message;
209
206
  }
210
207
 
211
- let fullLogs = this.formatLogs({ error: errorFormatted, steps, logs: testData.logs });
212
-
213
- if (stackArtifactsEnabled && fullLogs?.trim()?.length > 0) {
214
- uploadedFiles.push(
215
- this.uploader.uploadFileAsBuffer(
216
- Buffer.from(stripColors(fullLogs), 'utf8'),
217
- [this.runId, rid, `logs_${+new Date}.log`]
218
- )
219
- );
220
- fullLogs = '';
221
- steps = null;
222
- }
223
-
224
-
225
- if (!this.pipes || !this.pipes.length)
226
- this.pipes = await pipesFactory(this.paramsForPipesFactory || {}, this.pipeStore);
227
-
228
- if (!this.pipes?.filter(p => p.isEnabled).length) {
229
- if (uploadedFiles.length > 0) {
230
- await Promise.all(uploadedFiles);
231
- }
232
- return [];
233
- }
234
-
235
- if (isTestShouldBeExculedFromReport(testData)) return [];
236
-
237
- if (status === STATUS.SKIPPED && process.env.TESTOMATIO_EXCLUDE_SKIPPED) {
238
- debug('Skipping test from report', testData?.title);
239
- return [];
240
- }
208
+ // Attach logs
209
+ const fullLogs = this.formatLogs({ error: errorFormatted, steps, logs: testData.logs });
241
210
 
211
+ // add artifacts
242
212
  if (manuallyAttachedArtifacts?.length) files.push(...manuallyAttachedArtifacts);
243
213
 
214
+ const uploadedFiles = [];
215
+
244
216
  for (let f of files) {
245
217
  if (!f) continue; // f === null
246
218
  if (typeof f === 'object') {
@@ -336,7 +308,7 @@ class Client {
336
308
  const uploadedArtifacts = this.uploader.successfulUploads.map(file => ({
337
309
  relativePath: file.path.replace(process.cwd(), ''),
338
310
  link: file.link,
339
- sizePretty: file.size == null ? 'unknown' : prettyBytes(file.size, { round: 0 }).toString(),
311
+ sizePretty: prettyBytes(file.size, { round: 0 }).toString(),
340
312
  }));
341
313
 
342
314
  uploadedArtifacts.forEach(upload => {
@@ -358,7 +330,7 @@ class Client {
358
330
  );
359
331
  const failedUploads = this.uploader.failedUploads.map(file => ({
360
332
  relativePath: file.path.replace(process.cwd(), ''),
361
- sizePretty: file.size == null ? 'unknown' : prettyBytes(file.size, { round: 0 }).toString(),
333
+ sizePretty: prettyBytes(file.size, { round: 0 }).toString(),
362
334
  }));
363
335
 
364
336
  const pathPadding = Math.max(...failedUploads.map(upload => upload.relativePath.length)) + 1;
@@ -3,50 +3,18 @@ import Adapter from './adapter.js';
3
3
 
4
4
  class CSharpAdapter extends Adapter {
5
5
  formatTest(t) {
6
- // Extract example from title if not already present
7
- if (!t.example) {
8
- const exampleMatch = t.title.match(/\((.*?)\)/);
9
- if (exampleMatch) {
10
- // Extract parameters as object with numeric keys for API
11
- const params = exampleMatch[1].split(',').map(param => param.trim());
12
- t.example = {};
13
- params.forEach((param, index) => {
14
- t.example[index] = param;
15
- });
16
- }
17
- }
18
-
19
- // Remove parameters from title to avoid duplicates in Test Suite
20
- // The example field will be used for grouping on import
21
- t.title = t.title.replace(/\(.*?\)/, '').trim();
22
-
6
+ const title = t.title.replace(/\(.*?\)/, '').trim();
7
+ const example = t.title.match(/\((.*?)\)/);
8
+ if (example) t.example = { ...example[1].split(',') };
23
9
  const suite = t.suite_title.split('.');
24
10
  t.suite_title = suite.pop();
25
11
  t.file = namespaceToFileName(t.file);
12
+ t.title = title.trim();
26
13
  return t;
27
14
  }
28
15
 
29
16
  getFilePath(t) {
30
- if (!t.file) return null;
31
-
32
- // Normalize path separators for cross-platform compatibility
33
- let filePath = t.file.replace(/\\/g, '/');
34
-
35
- // If file already has .cs extension, use it directly
36
- if (filePath.endsWith('.cs')) {
37
- // Make relative path if it's absolute
38
- if (path.isAbsolute(filePath)) {
39
- // Try to find project-relative path
40
- const cwd = process.cwd().replace(/\\/g, '/');
41
- if (filePath.startsWith(cwd)) {
42
- filePath = path.relative(cwd, filePath).replace(/\\/g, '/');
43
- }
44
- }
45
- return filePath;
46
- }
47
-
48
- // Convert namespace path to file path
49
- const fileName = namespaceToFileName(filePath);
17
+ const fileName = namespaceToFileName(t.file);
50
18
  return fileName;
51
19
  }
52
20
  }
@@ -54,14 +22,7 @@ class CSharpAdapter extends Adapter {
54
22
  export default CSharpAdapter;
55
23
 
56
24
  function namespaceToFileName(fileName) {
57
- if (!fileName) return '';
58
-
59
- // If already a .cs file path, clean it up
60
- if (fileName.endsWith('.cs')) {
61
- return fileName.replace(/\\/g, '/');
62
- }
63
-
64
25
  const fileParts = fileName.split('.');
65
26
  fileParts[fileParts.length - 1] = fileParts[fileParts.length - 1]?.replace(/\$.*/, '');
66
- return `${fileParts.join('/')}.cs`;
27
+ return `${fileParts.join(path.sep)}.cs`;
67
28
  }