@testomatio/reporter 2.3.8 β†’ 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)_
@@ -129,7 +129,6 @@ Bring this reporter on CI and never lose test results again!
129
129
  - [CSV](./docs/pipes/csv.md)
130
130
  - [HTML report](./docs/pipes/html.md)
131
131
  - [Bitbucket](./docs/pipes/bitbucket.md)
132
- - πŸ”— [Linking Tests](./docs/linking-tests.md)
133
132
  - πŸ““ [JUnit](./docs/junit.md)
134
133
  - πŸ—„οΈ [Artifacts](./docs/artifacts.md)
135
134
  - πŸ”‚ [Workflows](./docs/workflows.md)
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();
@@ -155,7 +145,7 @@ program
155
145
  .option('--lang <lang>', 'Language used (python, ruby, java)')
156
146
  .option('--timelimit <time>', 'default time limit in seconds to kill a stuck process')
157
147
  .action(async (pattern, opts) => {
158
- if (!pattern.endsWith('.xml') && !pattern.includes('*')) {
148
+ if (!pattern.endsWith('.xml')) {
159
149
  pattern += '.xml';
160
150
  }
161
151
  let { javaTests, lang } = opts;
@@ -25,7 +25,7 @@ program
25
25
  .option('--timelimit <time>', 'default time limit in seconds to kill a stuck process')
26
26
  .option('--env-file <envfile>', 'Load environment variables from env file')
27
27
  .action(async (pattern, opts) => {
28
- if (!pattern.endsWith('.xml') && !pattern.includes('*')) {
28
+ if (!pattern.endsWith('.xml')) {
29
29
  pattern += '.xml';
30
30
  }
31
31
  let { javaTests, lang } = opts;
@@ -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 πŸ˜₯`);
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 => {
@@ -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,57 +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
- // Extract example from title if not already present
11
- if (!t.example) {
12
- const exampleMatch = t.title.match(/\((.*?)\)/);
13
- if (exampleMatch) {
14
- // Extract parameters as object with numeric keys for API
15
- const params = exampleMatch[1].split(',').map(param => param.trim()).filter(param => param !== '');
16
- t.example = {};
17
- params.forEach((param, index) => {
18
- t.example[index] = param;
19
- });
20
- }
21
- }
22
- // Remove parameters from title to avoid duplicates in Test Suite
23
- // The example field will be used for grouping on import
24
- t.title = t.title.replace(/\(.*?\)/, '').trim();
10
+ const title = t.title.replace(/\(.*?\)/, '').trim();
11
+ const example = t.title.match(/\((.*?)\)/);
12
+ if (example)
13
+ t.example = { ...example[1].split(',') };
25
14
  const suite = t.suite_title.split('.');
26
15
  t.suite_title = suite.pop();
27
16
  t.file = namespaceToFileName(t.file);
17
+ t.title = title.trim();
28
18
  return t;
29
19
  }
30
20
  getFilePath(t) {
31
- if (!t.file)
32
- return null;
33
- // Normalize path separators for cross-platform compatibility
34
- let filePath = t.file.replace(/\\/g, '/');
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_1.default.isAbsolute(filePath)) {
39
- // Try to find project-relative path
40
- const cwd = process.cwd().replace(/\\/g, '/');
41
- if (filePath.startsWith(cwd)) {
42
- filePath = path_1.default.relative(cwd, filePath).replace(/\\/g, '/');
43
- }
44
- }
45
- return filePath;
46
- }
47
- // Convert namespace path to file path
48
- const fileName = namespaceToFileName(filePath);
21
+ const fileName = namespaceToFileName(t.file);
49
22
  return fileName;
50
23
  }
51
24
  }
52
25
  module.exports = CSharpAdapter;
53
26
  function namespaceToFileName(fileName) {
54
- if (!fileName)
55
- return '';
56
- // If already a .cs file path, clean it up
57
- if (fileName.endsWith('.cs')) {
58
- return fileName.replace(/\\/g, '/');
59
- }
60
27
  const fileParts = fileName.split('.');
61
28
  fileParts[fileParts.length - 1] = fileParts[fileParts.length - 1]?.replace(/\$.*/, '');
62
- return `${fileParts.join('/')}.cs`;
29
+ return `${fileParts.join(path_1.default.sep)}.cs`;
63
30
  }
@@ -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
  };
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);