@testomatio/reporter 2.3.8 → 2.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20,11 +20,14 @@ if (!global.codeceptjs) {
20
20
  }
21
21
  // @ts-ignore
22
22
  const { event, recorder, codecept, output } = global.codeceptjs;
23
- const [, MAJOR_VERSION, MINOR_VERSION] = codecept.version().match(/(\d+)\.(\d+)/).map(Number);
23
+ const [, MAJOR_VERSION, MINOR_VERSION] = codecept
24
+ .version()
25
+ .match(/(\d+)\.(\d+)/)
26
+ .map(Number);
24
27
  // Constants for hook execution order
25
28
  const HOOK_EXECUTION_ORDER = {
26
29
  PRE_TEST: ['BeforeSuiteHook', 'BeforeHook'],
27
- POST_TEST: ['AfterHook', 'AfterSuiteHook']
30
+ POST_TEST: ['AfterHook', 'AfterSuiteHook'],
28
31
  };
29
32
  // codeceptjs workers are self-contained
30
33
  data_storage_js_1.dataStorage.isFileStorage = false;
@@ -92,7 +95,7 @@ function CodeceptReporter(config) {
92
95
  global.testomatioDataStore = {};
93
96
  });
94
97
  // Hook event listeners
95
- event.dispatcher.on(event.hook.started, (hook) => {
98
+ event.dispatcher.on(event.hook.started, hook => {
96
99
  output.stepShift = 2;
97
100
  currentHook = hook.name;
98
101
  let title = hook.hookName;
@@ -281,7 +284,7 @@ function captureHookStep(step, currentHook, hookSteps) {
281
284
  status: step.status,
282
285
  startTime,
283
286
  endTime,
284
- helperMethod: step.helperMethod
287
+ helperMethod: step.helperMethod,
285
288
  });
286
289
  hookSteps.set(currentHook, hookStepsArray);
287
290
  }
@@ -369,7 +372,7 @@ function createSectionStep(metaStep) {
369
372
  category: 'user',
370
373
  title: metaStep.toString(), // Use built-in toString method
371
374
  duration: metaStep.duration || 0, // Use built-in duration
372
- steps: []
375
+ steps: [],
373
376
  };
374
377
  }
375
378
  function createHookSection(hookName, steps) {
@@ -379,7 +382,7 @@ function createHookSection(hookName, steps) {
379
382
  category: 'hook',
380
383
  title: formatHookName(hookName),
381
384
  duration: 0,
382
- steps: []
385
+ steps: [],
383
386
  };
384
387
  for (const step of steps) {
385
388
  const formattedStep = formatHookStep(step);
@@ -403,13 +406,13 @@ function formatCodeceptStep(step) {
403
406
  const formattedStep = {
404
407
  category,
405
408
  title,
406
- duration
409
+ duration,
407
410
  };
408
411
  // Add error if step failed
409
412
  if (step.status === 'failed' && step.err) {
410
413
  formattedStep.error = {
411
414
  message: step.err.message || 'Step failed',
412
- stack: step.err.stack || ''
415
+ stack: step.err.stack || '',
413
416
  };
414
417
  }
415
418
  return formattedStep;
@@ -430,7 +433,7 @@ function formatHookStep(step) {
430
433
  return {
431
434
  category: 'hook',
432
435
  title,
433
- duration: step.duration || 0
436
+ duration: step.duration || 0,
434
437
  };
435
438
  }
436
439
  module.exports = CodeceptReporter;
package/lib/client.d.ts CHANGED
@@ -61,15 +61,5 @@ export class Client {
61
61
  * @returns {Promise<any>} - A Promise that resolves when finishes the run.
62
62
  */
63
63
  updateRunStatus(status: "passed" | "failed" | "skipped" | "finished"): Promise<any>;
64
- /**
65
- * Returns the formatted stack including the stack trace, steps, and logs.
66
- * @returns {string}
67
- */
68
- formatLogs({ error, steps, logs }: {
69
- error: any;
70
- steps: any;
71
- logs: any;
72
- }): string;
73
- formatError(error: any, message: any): string;
74
64
  }
75
65
  import { S3Uploader } from './uploader.js';
package/lib/client.js CHANGED
@@ -1,59 +1,22 @@
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
6
  exports.Client = void 0;
40
7
  const debug_1 = __importDefault(require("debug"));
41
- const callsite_record_1 = __importDefault(require("callsite-record"));
42
- const minimatch_1 = require("minimatch");
43
8
  const fs_1 = __importDefault(require("fs"));
44
9
  const picocolors_1 = __importDefault(require("picocolors"));
45
- const crypto_1 = require("crypto");
46
10
  const constants_js_1 = require("./constants.js");
47
11
  const index_js_1 = require("./pipe/index.js");
48
12
  const glob_1 = require("glob");
49
- const path_1 = __importStar(require("path"));
13
+ const path_1 = __importDefault(require("path"));
50
14
  const node_url_1 = require("node:url");
51
15
  const uploader_js_1 = require("./uploader.js");
52
16
  const utils_js_1 = require("./utils/utils.js");
53
17
  const filesize_1 = require("filesize");
54
- const util_1 = require("util");
18
+ const log_formatter_js_1 = require("./utils/log-formatter.js");
55
19
  const debug = (0, debug_1.default)('@testomatio/reporter:client');
56
- const stripColors = util_1.stripVTControlCharacters || ((str) => str?.replace(/\x1b\[[0-9;]*m/g, '') || '');
57
20
  // removed __dirname usage, because:
58
21
  // 1. replaced with ESM syntax (import.meta.url), but it throws an error on tsc compilation;
59
22
  // 2. got error "__dirname already defined" in compiles js code (cjs dir)
@@ -172,7 +135,7 @@ class Client {
172
135
  /**
173
136
  * @type {TestData}
174
137
  */
175
- const { rid, error = null, steps: originalSteps, title, suite_title, } = testData;
138
+ const { rid, error = null, steps: originalSteps, title, suite_title } = testData;
176
139
  let steps = originalSteps;
177
140
  const uploadedFiles = [];
178
141
  const stackArtifactsEnabled = (0, utils_js_1.transformEnvVarToBoolean)(process.env.TESTOMATIO_STACK_ARTIFACTS);
@@ -188,12 +151,16 @@ class Client {
188
151
  const testContext = suite_title ? `${suite_title} ${title}` : title;
189
152
  let errorFormatted = '';
190
153
  if (error) {
191
- errorFormatted += this.formatError(error) || '';
154
+ errorFormatted += (0, log_formatter_js_1.formatError)(error) || '';
192
155
  message = error?.message;
193
156
  }
194
- let fullLogs = this.formatLogs({ error: errorFormatted, steps, logs: testData.logs });
157
+ let fullLogs = (0, log_formatter_js_1.formatLogs)({ error: errorFormatted, steps, logs: testData.logs });
195
158
  if (stackArtifactsEnabled && fullLogs?.trim()?.length > 0) {
196
- uploadedFiles.push(this.uploader.uploadFileAsBuffer(Buffer.from(stripColors(fullLogs), 'utf8'), [this.runId, rid, `logs_${+new Date}.log`]));
159
+ uploadedFiles.push(this.uploader.uploadFileAsBuffer(Buffer.from((0, log_formatter_js_1.stripColors)(fullLogs), 'utf8'), [
160
+ this.runId,
161
+ rid,
162
+ `logs_${+new Date()}.log`,
163
+ ]));
197
164
  fullLogs = '';
198
165
  steps = null;
199
166
  }
@@ -205,7 +172,7 @@ class Client {
205
172
  }
206
173
  return [];
207
174
  }
208
- if (isTestShouldBeExculedFromReport(testData))
175
+ if (isTestShouldBeExcludedFromReport(testData))
209
176
  return [];
210
177
  if (status === constants_js_1.STATUS.SKIPPED && process.env.TESTOMATIO_EXCLUDE_SKIPPED) {
211
178
  debug('Skipping test from report', testData?.title);
@@ -332,93 +299,14 @@ class Client {
332
299
  .catch(err => console.log(constants_js_1.APP_PREFIX, err));
333
300
  return this.queue;
334
301
  }
335
- /**
336
- * Returns the formatted stack including the stack trace, steps, and logs.
337
- * @returns {string}
338
- */
339
- formatLogs({ error, steps, logs }) {
340
- error = error?.trim();
341
- logs = logs?.trim().split('\n').map(l => (0, utils_js_1.truncate)(l)).join('\n');
342
- if (Array.isArray(steps)) {
343
- steps = steps
344
- .map(step => (0, utils_js_1.formatStep)(step))
345
- .flat()
346
- .join('\n');
347
- }
348
- let testLogs = '';
349
- if (steps)
350
- testLogs += `${picocolors_1.default.bold(picocolors_1.default.blue('################[ Steps ]################'))}\n${steps}\n\n`;
351
- if (logs)
352
- testLogs += `${picocolors_1.default.bold(picocolors_1.default.gray('################[ Logs ]################'))}\n${logs}\n\n`;
353
- if (error)
354
- testLogs += `${picocolors_1.default.bold(picocolors_1.default.red('################[ Failure ]################'))}\n${error}`;
355
- return testLogs;
356
- }
357
- formatError(error, message) {
358
- if (!message)
359
- message = error.message;
360
- if (error.inspect)
361
- message = error.inspect() || '';
362
- let stack = '';
363
- if (error.name)
364
- stack += `${picocolors_1.default.red(error.name)}`;
365
- if (error.operator)
366
- stack += ` (${picocolors_1.default.red(error.operator)})`;
367
- // add new line if something was added to stack
368
- if (stack)
369
- stack += ': ';
370
- stack += `${message}\n`;
371
- if (error.diff) {
372
- // diff for vitest
373
- stack += error.diff;
374
- stack += '\n\n';
375
- }
376
- else if (error.actual && error.expected && error.actual !== error.expected) {
377
- // diffs for mocha, cypress, codeceptjs style
378
- stack += `\n\n${picocolors_1.default.bold(picocolors_1.default.green('+ expected'))} ${picocolors_1.default.bold(picocolors_1.default.red('- actual'))}`;
379
- stack += `\n${picocolors_1.default.green(`+ ${error.expected.toString().split('\n').join('\n+ ')}`)}`;
380
- stack += `\n${picocolors_1.default.red(`- ${error.actual.toString().split('\n').join('\n- ')}`)}`;
381
- stack += '\n\n';
382
- }
383
- const customFilter = process.env.TESTOMATIO_STACK_IGNORE;
384
- try {
385
- let hasFrame = false;
386
- const record = (0, callsite_record_1.default)({
387
- forError: error,
388
- isCallsiteFrame: frame => {
389
- if (customFilter && (0, minimatch_1.minimatch)(frame.fileName, customFilter))
390
- return false;
391
- if (hasFrame)
392
- return false;
393
- if (isNotInternalFrame(frame))
394
- hasFrame = true;
395
- return hasFrame;
396
- },
397
- });
398
- // @ts-ignore
399
- if (record && !record.filename.startsWith('http')) {
400
- stack += record.renderSync({ stackFilter: isNotInternalFrame });
401
- }
402
- return stack;
403
- }
404
- catch (e) {
405
- console.log(e);
406
- }
407
- }
408
302
  }
409
303
  exports.Client = Client;
410
- function isNotInternalFrame(frame) {
411
- return (frame.getFileName() &&
412
- frame.getFileName().includes(path_1.sep) &&
413
- !frame.getFileName().includes('node_modules') &&
414
- !frame.getFileName().includes('internal'));
415
- }
416
304
  /**
417
305
  *
418
306
  * @param {TestData} testData
419
307
  * @returns boolean
420
308
  */
421
- function isTestShouldBeExculedFromReport(testData) {
309
+ function isTestShouldBeExcludedFromReport(testData) {
422
310
  // const fileName = path.basename(test.location?.file || '');
423
311
  const globExcludeFilesPattern = process.env.TESTOMATIO_EXCLUDE_FILES_FROM_REPORT_GLOB_PATTERN;
424
312
  if (!globExcludeFilesPattern)
@@ -427,11 +315,11 @@ function isTestShouldBeExculedFromReport(testData) {
427
315
  debug('No "file" property found for test ', testData.title);
428
316
  return false;
429
317
  }
430
- const excludeParretnsList = globExcludeFilesPattern.split(';');
318
+ const excludePatternsList = globExcludeFilesPattern.split(';');
431
319
  // as scanning files is time consuming operation, just save the result in variable to avoid multiple scans
432
320
  if (!listOfTestFilesToExcludeFromReport) {
433
321
  // list of files with relative paths
434
- listOfTestFilesToExcludeFromReport = glob_1.glob.sync(excludeParretnsList, { ignore: '**/node_modules/**' });
322
+ listOfTestFilesToExcludeFromReport = glob_1.glob.sync(excludePatternsList, { ignore: '**/node_modules/**' });
435
323
  debug('Tests from next files will not be reported:', listOfTestFilesToExcludeFromReport);
436
324
  }
437
325
  const testFileRelativePath = path_1.default.relative(process.cwd(), testData.file);
@@ -12,7 +12,10 @@ class CSharpAdapter extends adapter_js_1.default {
12
12
  const exampleMatch = t.title.match(/\((.*?)\)/);
13
13
  if (exampleMatch) {
14
14
  // Extract parameters as object with numeric keys for API
15
- const params = exampleMatch[1].split(',').map(param => param.trim()).filter(param => param !== '');
15
+ const params = exampleMatch[1]
16
+ .split(',')
17
+ .map(param => param.trim())
18
+ .filter(param => param !== '');
16
19
  t.example = {};
17
20
  params.forEach((param, index) => {
18
21
  t.example[index] = param;
@@ -359,13 +359,15 @@ class NUnitXmlParser {
359
359
  parameters.push(current.trim());
360
360
  }
361
361
  // Clean up parameters - remove quotes if they wrap the entire parameter and filter empty ones
362
- return parameters.map(param => {
362
+ return parameters
363
+ .map(param => {
363
364
  param = param.trim();
364
365
  if ((param.startsWith('"') && param.endsWith('"')) || (param.startsWith("'") && param.endsWith("'"))) {
365
366
  return param.slice(1, -1);
366
367
  }
367
368
  return param;
368
- }).filter(p => !!p);
369
+ })
370
+ .filter(p => !!p);
369
371
  }
370
372
  /**
371
373
  * Extract method name from test name (fallback)
@@ -73,8 +73,8 @@ class BitbucketPipe {
73
73
  baseURL: 'https://api.bitbucket.org/2.0',
74
74
  headers: {
75
75
  'Content-Type': 'application/json',
76
- 'Authorization': `Bearer ${this.token}`
77
- }
76
+ Authorization: `Bearer ${this.token}`,
77
+ },
78
78
  });
79
79
  debug('Bitbucket Pipe: Enabled');
80
80
  }
@@ -185,7 +185,7 @@ class BitbucketPipe {
185
185
  const addCommentResponse = await this.client.request({
186
186
  method: 'POST',
187
187
  url: commentsRequestURL,
188
- data: { content: { raw: body } }
188
+ data: { content: { raw: body } },
189
189
  });
190
190
  const commentID = addCommentResponse.data.id;
191
191
  // eslint-disable-next-line max-len
@@ -212,7 +212,7 @@ async function deletePreviousReport(client, commentsRequestURL, hiddenCommentDat
212
212
  try {
213
213
  const response = await client.request({
214
214
  method: 'GET',
215
- url: commentsRequestURL
215
+ url: commentsRequestURL,
216
216
  });
217
217
  comments = response.data.values;
218
218
  }
@@ -229,7 +229,7 @@ async function deletePreviousReport(client, commentsRequestURL, hiddenCommentDat
229
229
  const deleteCommentURL = `${commentsRequestURL}/${comment.id}`;
230
230
  await client.request({
231
231
  method: 'DELETE',
232
- url: deleteCommentURL
232
+ url: deleteCommentURL,
233
233
  });
234
234
  }
235
235
  catch (e) {
@@ -43,7 +43,7 @@ class GitLabPipe {
43
43
  baseURL: 'https://gitlab.com/api/v4',
44
44
  headers: {
45
45
  'Content-Type': 'application/json',
46
- }
46
+ },
47
47
  });
48
48
  debug('GitLab Pipe: Enabled');
49
49
  }
@@ -142,7 +142,7 @@ class GitLabPipe {
142
142
  method: 'POST',
143
143
  url: commentsRequestURL,
144
144
  params: { access_token: this.token },
145
- data: { body }
145
+ data: { body },
146
146
  });
147
147
  const commentID = addCommentResponse.data.id;
148
148
  // eslint-disable-next-line max-len
@@ -169,7 +169,7 @@ async function deletePreviousReport(client, commentsRequestURL, hiddenCommentDat
169
169
  const response = await client.request({
170
170
  method: 'GET',
171
171
  url: commentsRequestURL,
172
- params: { access_token: token }
172
+ params: { access_token: token },
173
173
  });
174
174
  comments = response.data;
175
175
  }
@@ -187,7 +187,7 @@ async function deletePreviousReport(client, commentsRequestURL, hiddenCommentDat
187
187
  await client.request({
188
188
  method: 'DELETE',
189
189
  url: deleteCommentURL,
190
- params: { access_token: token }
190
+ params: { access_token: token },
191
191
  });
192
192
  }
193
193
  catch (e) {
@@ -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)
@@ -194,7 +194,7 @@ class TestomatioPipe {
194
194
  method: 'PUT',
195
195
  url: `/api/reporter/${this.runId}`,
196
196
  data: runParams,
197
- responseType: 'json'
197
+ responseType: 'json',
198
198
  });
199
199
  if (resp.data.artifacts)
200
200
  (0, pipe_utils_js_1.setS3Credentials)(resp.data.artifacts);
@@ -207,7 +207,7 @@ class TestomatioPipe {
207
207
  url: '/api/reporter',
208
208
  data: runParams,
209
209
  maxContentLength: Infinity,
210
- responseType: 'json'
210
+ responseType: 'json',
211
211
  });
212
212
  this.runId = resp.data.uid;
213
213
  this.runUrl = `${this.url}/${resp.data.url.split('/').splice(3).join('/')}`;
@@ -260,15 +260,17 @@ class TestomatioPipe {
260
260
  this.#formatData(data);
261
261
  const json = json_cycle_1.default.stringify(data);
262
262
  debug('Adding test', json);
263
- return this.client.request({
263
+ return this.client
264
+ .request({
264
265
  method: 'POST',
265
266
  url: `/api/reporter/${this.runId}/testrun`,
266
267
  data: json,
267
268
  headers: {
268
269
  'Content-Type': 'application/json',
269
270
  },
270
- maxContentLength: Infinity
271
- }).catch(err => {
271
+ maxContentLength: Infinity,
272
+ })
273
+ .catch(err => {
272
274
  this.requestFailures++;
273
275
  this.notReportedTestsCount++;
274
276
  if (err.response) {
@@ -313,19 +315,21 @@ class TestomatioPipe {
313
315
  // get tests from batch and clear batch
314
316
  const testsToSend = this.batch.tests.splice(0);
315
317
  debug('📨 Batch upload', testsToSend.length, 'tests');
316
- return this.client.request({
318
+ return this.client
319
+ .request({
317
320
  method: 'POST',
318
321
  url: `/api/reporter/${this.runId}/testrun`,
319
322
  data: {
320
323
  api_key: this.apiKey,
321
324
  tests: testsToSend,
322
- batch_index: this.batch.batchIndex
325
+ batch_index: this.batch.batchIndex,
323
326
  },
324
327
  headers: {
325
328
  'Content-Type': 'application/json',
326
329
  },
327
- maxContentLength: Infinity
328
- }).catch(err => {
330
+ maxContentLength: Infinity,
331
+ })
332
+ .catch(err => {
329
333
  this.requestFailures++;
330
334
  this.notReportedTestsCount += testsToSend.length;
331
335
  if (err.response) {
@@ -409,7 +413,7 @@ class TestomatioPipe {
409
413
  status_event,
410
414
  detach: params.detach,
411
415
  tests: params.tests,
412
- }
416
+ },
413
417
  });
414
418
  if (this.runUrl) {
415
419
  console.log(constants_js_1.APP_PREFIX, '📊 Report Saved. Report URL:', picocolors_1.default.magenta(this.runUrl));
@@ -59,9 +59,7 @@ function setLabel(key, value = null) {
59
59
  if (Array.isArray(value)) {
60
60
  return value.forEach(label => setLabel(key, label));
61
61
  }
62
- const labelObject = value !== null && value !== undefined && value !== ''
63
- ? { label: `${key}:${value}` }
64
- : { label: key };
62
+ const labelObject = value !== null && value !== undefined && value !== '' ? { label: `${key}:${value}` } : { label: key };
65
63
  index_js_1.services.links.put([labelObject]);
66
64
  }
67
65
  /**
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Returns the formatted stack including the stack trace, steps, and logs.
3
+ * @param {Object} params - Parameters for formatting logs
4
+ * @param {string} params.error - Error message
5
+ * @param {Array|any} params.steps - Test steps (array or other types)
6
+ * @param {string} params.logs - Test logs
7
+ * @returns {string}
8
+ */
9
+ export function formatLogs({ error, steps, logs }: {
10
+ error: string;
11
+ steps: any[] | any;
12
+ logs: string;
13
+ }): string;
14
+ /**
15
+ * Formats an error with stack trace and diff information
16
+ * @param {Error & {inspect?: () => string, operator?: string, diff?: string, actual?: any, expected?: any}} error
17
+ * The error object to format
18
+ * @param {string} [message] - Optional error message override
19
+ * @returns {string}
20
+ */
21
+ export function formatError(error: Error & {
22
+ inspect?: () => string;
23
+ operator?: string;
24
+ diff?: string;
25
+ actual?: any;
26
+ expected?: any;
27
+ }, message?: string): string;
28
+ export function stripColors(str: string): string;
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.stripColors = void 0;
7
+ exports.formatLogs = formatLogs;
8
+ exports.formatError = formatError;
9
+ const callsite_record_1 = __importDefault(require("callsite-record"));
10
+ const minimatch_1 = require("minimatch");
11
+ const picocolors_1 = __importDefault(require("picocolors"));
12
+ const util_1 = require("util");
13
+ const path_1 = require("path");
14
+ const utils_js_1 = require("./utils.js");
15
+ const stripColors = util_1.stripVTControlCharacters || (str => str?.replace(/\x1b\[[0-9;]*m/g, '') || '');
16
+ exports.stripColors = stripColors;
17
+ /**
18
+ * Returns the formatted stack including the stack trace, steps, and logs.
19
+ * @param {Object} params - Parameters for formatting logs
20
+ * @param {string} params.error - Error message
21
+ * @param {Array|any} params.steps - Test steps (array or other types)
22
+ * @param {string} params.logs - Test logs
23
+ * @returns {string}
24
+ */
25
+ function formatLogs({ error, steps, logs }) {
26
+ error = error?.trim();
27
+ logs = logs
28
+ ?.trim()
29
+ .split('\n')
30
+ .map(l => (0, utils_js_1.truncate)(l))
31
+ .join('\n');
32
+ if (Array.isArray(steps)) {
33
+ steps = steps
34
+ .map(step => (0, utils_js_1.formatStep)(step))
35
+ .flat()
36
+ .join('\n');
37
+ }
38
+ else {
39
+ steps = null;
40
+ }
41
+ let testLogs = '';
42
+ if (steps)
43
+ testLogs += `${picocolors_1.default.bold(picocolors_1.default.blue('################[ Steps ]################'))}\n${steps}\n\n`;
44
+ if (logs)
45
+ testLogs += `${picocolors_1.default.bold(picocolors_1.default.gray('################[ Logs ]################'))}\n${logs}\n\n`;
46
+ if (error)
47
+ testLogs += `${picocolors_1.default.bold(picocolors_1.default.red('################[ Failure ]################'))}\n${error}`;
48
+ return testLogs;
49
+ }
50
+ /**
51
+ * Formats an error with stack trace and diff information
52
+ * @param {Error & {inspect?: () => string, operator?: string, diff?: string, actual?: any, expected?: any}} error
53
+ * The error object to format
54
+ * @param {string} [message] - Optional error message override
55
+ * @returns {string}
56
+ */
57
+ function formatError(error, message) {
58
+ if (!message)
59
+ message = error.message;
60
+ // @ts-ignore - inspect is a custom property added by some testing frameworks
61
+ if (error.inspect)
62
+ message = error.inspect() || '';
63
+ let stack = '';
64
+ if (error.name)
65
+ stack += `${picocolors_1.default.red(error.name)}`;
66
+ // @ts-ignore - operator is a custom property added by assertion libraries
67
+ if (error.operator)
68
+ stack += ` (${picocolors_1.default.red(error.operator)})`;
69
+ // add new line if something was added to stack
70
+ if (stack)
71
+ stack += ': ';
72
+ stack += `${message}\n`;
73
+ // @ts-ignore - diff is a custom property added by vitest
74
+ if (error.diff) {
75
+ // diff for vitest
76
+ stack += error.diff;
77
+ stack += '\n\n';
78
+ }
79
+ else if (error.actual && error.expected && error.actual !== error.expected) {
80
+ // diffs for mocha, cypress, codeceptjs style
81
+ stack += `\n\n${picocolors_1.default.bold(picocolors_1.default.green('+ expected'))} ${picocolors_1.default.bold(picocolors_1.default.red('- actual'))}`;
82
+ stack += `\n${picocolors_1.default.green(`+ ${error.expected.toString().split('\n').join('\n+ ')}`)}`;
83
+ stack += `\n${picocolors_1.default.red(`- ${error.actual.toString().split('\n').join('\n- ')}`)}`;
84
+ stack += '\n\n';
85
+ }
86
+ const customFilter = process.env.TESTOMATIO_STACK_IGNORE;
87
+ try {
88
+ let hasFrame = false;
89
+ const record = (0, callsite_record_1.default)({
90
+ forError: error,
91
+ isCallsiteFrame: frame => {
92
+ if (customFilter && (0, minimatch_1.minimatch)(frame.fileName, customFilter))
93
+ return false;
94
+ if (hasFrame)
95
+ return false;
96
+ if (isNotInternalFrame(frame))
97
+ hasFrame = true;
98
+ return hasFrame;
99
+ },
100
+ });
101
+ // @ts-ignore
102
+ if (record && !record.filename.startsWith('http')) {
103
+ stack += record.renderSync({ stackFilter: isNotInternalFrame });
104
+ }
105
+ return stack;
106
+ }
107
+ catch (e) {
108
+ console.log(e);
109
+ }
110
+ }
111
+ /**
112
+ * Checks if a stack frame is not an internal frame (node_modules or internal)
113
+ * @param {Object} frame - Stack frame object
114
+ * @returns {boolean}
115
+ */
116
+ function isNotInternalFrame(frame) {
117
+ return (frame.getFileName() &&
118
+ frame.getFileName().includes(path_1.sep) &&
119
+ !frame.getFileName().includes('node_modules') &&
120
+ !frame.getFileName().includes('internal'));
121
+ }
122
+
123
+ module.exports.formatLogs = formatLogs;
124
+
125
+ module.exports.formatError = formatError;
126
+
127
+ module.exports.stripColors = stripColors;