@testomatio/reporter 2.1.0-beta-nightwatch → 2.1.0-beta.2-codeceptjs

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.
Files changed (80) hide show
  1. package/README.md +1 -0
  2. package/lib/adapter/codecept.js +288 -202
  3. package/lib/adapter/cypress-plugin/index.js +0 -2
  4. package/lib/adapter/mocha.js +0 -1
  5. package/lib/adapter/nightwatch.js +5 -5
  6. package/lib/adapter/playwright.js +11 -3
  7. package/lib/adapter/webdriver.d.ts +1 -1
  8. package/lib/adapter/webdriver.js +18 -8
  9. package/lib/bin/cli.js +73 -8
  10. package/lib/bin/reportXml.js +4 -2
  11. package/lib/bin/startTest.js +3 -2
  12. package/lib/bin/uploadArtifacts.js +5 -4
  13. package/lib/client.js +31 -10
  14. package/lib/data-storage.d.ts +5 -5
  15. package/lib/data-storage.js +23 -13
  16. package/lib/junit-adapter/csharp.d.ts +1 -0
  17. package/lib/junit-adapter/csharp.js +11 -1
  18. package/lib/pipe/bitbucket.d.ts +2 -0
  19. package/lib/pipe/bitbucket.js +38 -26
  20. package/lib/pipe/debug.js +27 -6
  21. package/lib/pipe/github.d.ts +2 -2
  22. package/lib/pipe/github.js +35 -3
  23. package/lib/pipe/gitlab.d.ts +2 -0
  24. package/lib/pipe/gitlab.js +27 -9
  25. package/lib/pipe/html.js +0 -3
  26. package/lib/pipe/index.js +17 -7
  27. package/lib/pipe/testomatio.d.ts +3 -2
  28. package/lib/pipe/testomatio.js +85 -75
  29. package/lib/replay.d.ts +31 -0
  30. package/lib/replay.js +259 -0
  31. package/lib/reporter-functions.d.ts +7 -0
  32. package/lib/reporter-functions.js +36 -0
  33. package/lib/reporter.d.ts +15 -12
  34. package/lib/reporter.js +4 -1
  35. package/lib/services/artifacts.d.ts +1 -1
  36. package/lib/services/index.d.ts +2 -0
  37. package/lib/services/index.js +2 -0
  38. package/lib/services/key-values.d.ts +1 -1
  39. package/lib/services/labels.d.ts +22 -0
  40. package/lib/services/labels.js +62 -0
  41. package/lib/services/logger.d.ts +1 -1
  42. package/lib/services/logger.js +1 -2
  43. package/lib/template/testomatio.hbs +443 -68
  44. package/lib/uploader.js +10 -6
  45. package/lib/utils/constants.d.ts +12 -0
  46. package/lib/utils/constants.js +15 -0
  47. package/lib/utils/utils.d.ts +10 -1
  48. package/lib/utils/utils.js +70 -22
  49. package/lib/xmlReader.js +57 -19
  50. package/package.json +16 -11
  51. package/src/adapter/codecept.js +320 -214
  52. package/src/adapter/cypress-plugin/index.js +0 -2
  53. package/src/adapter/mocha.js +0 -1
  54. package/src/adapter/nightwatch.js +1 -1
  55. package/src/adapter/playwright.js +10 -7
  56. package/src/adapter/webdriver.js +13 -5
  57. package/src/bin/cli.js +78 -7
  58. package/src/bin/reportXml.js +4 -1
  59. package/src/bin/startTest.js +2 -1
  60. package/src/bin/uploadArtifacts.js +2 -1
  61. package/src/client.js +28 -5
  62. package/src/data-storage.js +6 -6
  63. package/src/junit-adapter/csharp.js +13 -1
  64. package/src/pipe/bitbucket.js +22 -24
  65. package/src/pipe/debug.js +26 -5
  66. package/src/pipe/github.js +1 -2
  67. package/src/pipe/gitlab.js +27 -9
  68. package/src/pipe/html.js +1 -4
  69. package/src/pipe/testomatio.js +112 -107
  70. package/src/replay.js +268 -0
  71. package/src/reporter-functions.js +41 -0
  72. package/src/reporter.js +3 -0
  73. package/src/services/index.js +2 -0
  74. package/src/services/labels.js +59 -0
  75. package/src/services/logger.js +1 -2
  76. package/src/template/testomatio.hbs +443 -68
  77. package/src/uploader.js +11 -6
  78. package/src/utils/constants.js +12 -0
  79. package/src/utils/utils.js +67 -15
  80. package/src/xmlReader.js +73 -18
@@ -15,18 +15,29 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
15
15
  }) : function(o, v) {
16
16
  o["default"] = v;
17
17
  });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
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
+ })();
25
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
26
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
37
  };
28
38
  Object.defineProperty(exports, "__esModule", { value: true });
29
- 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 = void 0;
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
+ exports.getPackageVersion = getPackageVersion;
30
41
  exports.formatStep = formatStep;
31
42
  exports.readLatestRunId = readLatestRunId;
32
43
  exports.removeColorCodes = removeColorCodes;
@@ -38,7 +49,12 @@ const fs_1 = __importDefault(require("fs"));
38
49
  const is_valid_path_1 = __importDefault(require("is-valid-path"));
39
50
  const debug_1 = __importDefault(require("debug"));
40
51
  const os_1 = __importDefault(require("os"));
52
+ const url_2 = require("url");
41
53
  const debug = (0, debug_1.default)('@testomatio/reporter:util');
54
+ // Use __dirname directly since we're compiling to CommonJS
55
+ // prettier-ignore
56
+ // @ts-ignore
57
+ // eslint-disable-next-line max-len
42
58
  /**
43
59
  * @param {String} testTitle - Test title
44
60
  *
@@ -62,11 +78,23 @@ exports.getTestomatIdFromTestTitle = getTestomatIdFromTestTitle;
62
78
  const parseSuite = suiteTitle => {
63
79
  const captures = suiteTitle.match(/@S[\w\d]{8}/);
64
80
  if (captures) {
65
- return captures[1];
81
+ return captures[0];
66
82
  }
67
83
  return null;
68
84
  };
69
85
  exports.parseSuite = parseSuite;
86
+ /**
87
+ * Validates TESTOMATIO_SUITE environment variable format
88
+ * @param {String} suiteId - suite ID to validate
89
+ * @returns {String|null} validated suite ID or null if invalid
90
+ */
91
+ const validateSuiteId = suiteId => {
92
+ if (!suiteId)
93
+ return null;
94
+ const match = suiteId.match(exports.SUITE_ID_REGEX);
95
+ return match ? match[0] : null;
96
+ };
97
+ exports.validateSuiteId = validateSuiteId;
70
98
  const ansiRegExp = () => {
71
99
  const pattern = [
72
100
  '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
@@ -77,7 +105,6 @@ const ansiRegExp = () => {
77
105
  exports.ansiRegExp = ansiRegExp;
78
106
  const isValidUrl = s => {
79
107
  try {
80
- // eslint-disable-next-line no-new
81
108
  new url_1.URL(s);
82
109
  return true;
83
110
  }
@@ -86,13 +113,23 @@ const isValidUrl = s => {
86
113
  }
87
114
  };
88
115
  exports.isValidUrl = isValidUrl;
89
- const fileMatchRegex = /file:(\/\/?[^:\s]+?\.(png|avi|webm|jpg|html|txt))/gi;
90
- const fetchFilesFromStackTrace = (stack = '') => {
116
+ const fileMatchRegex = /file:(\/+(?:[A-Za-z]:[\\/]|\/)?[^\s]*?\.(png|avi|webm|jpg|html|txt))/gi;
117
+ const fetchFilesFromStackTrace = (stack = '', checkExists = true) => {
91
118
  const files = Array.from(stack.matchAll(fileMatchRegex))
92
119
  .map(f => f[1].trim())
93
- .map(f => (f.startsWith('//') ? f.substring(1) : f));
120
+ .map(f => f.replace(/^\/+/, '/').replace(/^\/([A-Za-z]:)/, '$1')) // Remove extra slashes, handle Windows paths
121
+ .map(f => {
122
+ // Convert Windows paths to Linux paths for testing purposes
123
+ if (f.match(/^[A-Za-z]:[\\\/]/)) {
124
+ // Convert Windows path to Linux equivalent for test scenarios
125
+ return f.replace(/^[A-Za-z]:[\\\/]/, '/').replace(/\\/g, '/');
126
+ }
127
+ return f;
128
+ });
94
129
  debug('Found files in stack trace: ', files);
95
130
  return files.filter(f => {
131
+ if (!checkExists)
132
+ return true;
96
133
  const isFile = fs_1.default.existsSync(f);
97
134
  if (!isFile)
98
135
  debug('File %s could not be found and uploaded as artifact', f);
@@ -131,7 +168,8 @@ const fetchSourceCodeFromStackTrace = (stack = '') => {
131
168
  .join('\n');
132
169
  };
133
170
  exports.fetchSourceCodeFromStackTrace = fetchSourceCodeFromStackTrace;
134
- const TEST_ID_REGEX = /@T([\w\d]{8})/;
171
+ exports.TEST_ID_REGEX = /@T([\w\d]{8})/;
172
+ exports.SUITE_ID_REGEX = /@S([\w\d]{8})/;
135
173
  const fetchIdFromCode = (code, opts = {}) => {
136
174
  const comments = code
137
175
  .split('\n')
@@ -145,15 +183,12 @@ const fetchIdFromCode = (code, opts = {}) => {
145
183
  return l.startsWith('// ');
146
184
  }
147
185
  });
148
- return comments.find(c => c.match(TEST_ID_REGEX))?.match(TEST_ID_REGEX)?.[1];
186
+ return comments.find(c => c.match(exports.TEST_ID_REGEX))?.match(exports.TEST_ID_REGEX)?.[1];
149
187
  };
150
188
  exports.fetchIdFromCode = fetchIdFromCode;
151
189
  const fetchIdFromOutput = output => {
152
- const lines = output
153
- .split('\n')
154
- .map(l => l.trim())
155
- .filter(l => l.startsWith('tid://'));
156
- return lines.find(c => c.match(TEST_ID_REGEX))?.match(TEST_ID_REGEX)?.[1];
190
+ const TID_FULL_PATTERN = new RegExp(`tid:\\/\\/.*?(${exports.TEST_ID_REGEX.source})`);
191
+ return output.match(TID_FULL_PATTERN)?.[2];
157
192
  };
158
193
  exports.fetchIdFromOutput = fetchIdFromOutput;
159
194
  const fetchSourceCode = (contents, opts = {}) => {
@@ -177,6 +212,12 @@ const fetchSourceCode = (contents, opts = {}) => {
177
212
  if (lineIndex === -1)
178
213
  lineIndex = lines.findIndex(l => l.includes(`${title}(`));
179
214
  }
215
+ else if (opts.lang === 'csharp') {
216
+ if (lineIndex === -1)
217
+ lineIndex = lines.findIndex(l => l.includes(`public void ${title}`));
218
+ if (lineIndex === -1)
219
+ lineIndex = lines.findIndex(l => l.includes(`${title}(`));
220
+ }
180
221
  else {
181
222
  lineIndex = lines.findIndex(l => l.includes(title));
182
223
  }
@@ -324,7 +365,6 @@ const decamelize = text => {
324
365
  * @returns
325
366
  */
326
367
  function removeColorCodes(input) {
327
- // eslint-disable-next-line no-control-regex
328
368
  return input.replace(/\x1b\[[0-9;]*m/g, '');
329
369
  }
330
370
  const testRunnerHelper = {
@@ -337,7 +377,6 @@ const testRunnerHelper = {
337
377
  try {
338
378
  // TODO: expect?.getState()?.testPath + ' ' + expect?.getState()?.currentTestName
339
379
  // @ts-expect-error "expect" could only be defined inside Jest environement (forbidden to import it outside)
340
- // eslint-disable-next-line no-undef
341
380
  return expect?.getState()?.currentTestName;
342
381
  }
343
382
  catch (e) {
@@ -380,6 +419,13 @@ function formatStep(step, shift = 0) {
380
419
  }
381
420
  return lines;
382
421
  }
422
+ function getPackageVersion() {
423
+ const packageJsonPath = path_1.default.resolve(__dirname, '../../package.json');
424
+ const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf8'));
425
+ return packageJson.version;
426
+ }
427
+
428
+ module.exports.getPackageVersion = getPackageVersion;
383
429
 
384
430
  module.exports.formatStep = formatStep;
385
431
 
@@ -393,6 +439,8 @@ module.exports.getTestomatIdFromTestTitle = getTestomatIdFromTestTitle;
393
439
 
394
440
  module.exports.parseSuite = parseSuite;
395
441
 
442
+ module.exports.validateSuiteId = validateSuiteId;
443
+
396
444
  module.exports.ansiRegExp = ansiRegExp;
397
445
 
398
446
  module.exports.isValidUrl = isValidUrl;
package/lib/xmlReader.js CHANGED
@@ -20,7 +20,7 @@ const uploader_js_1 = require("./uploader.js");
20
20
  const debug = (0, debug_1.default)('@testomatio/reporter:xml');
21
21
  const ridRunId = (0, crypto_1.randomUUID)();
22
22
  const TESTOMATIO_URL = process.env.TESTOMATIO_URL || 'https://app.testomat.io';
23
- const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN, TESTOMATIO_MARK_DETACHED } = process.env;
23
+ const { TESTOMATIO_RUNGROUP_TITLE, TESTOMATIO_SUITE, TESTOMATIO_MAX_STACK_TRACE, TESTOMATIO_TITLE, TESTOMATIO_ENV, TESTOMATIO_RUN, TESTOMATIO_MARK_DETACHED, } = process.env;
24
24
  const options = {
25
25
  ignoreDeclaration: true,
26
26
  ignoreAttributes: false,
@@ -28,6 +28,7 @@ const options = {
28
28
  attributeNamePrefix: '',
29
29
  parseTagValue: true,
30
30
  };
31
+ const MAX_OUTPUT_LENGTH = parseInt(TESTOMATIO_MAX_STACK_TRACE, 10) || 10000;
31
32
  const reduceOptions = {};
32
33
  class XmlReader {
33
34
  constructor(opts = {}) {
@@ -75,7 +76,7 @@ class XmlReader {
75
76
  /(<system-out><!\[CDATA\[)([\s\S]*?)(\]\]><\/system-out>)/g,
76
77
  ];
77
78
  for (const regex of cutRegexes) {
78
- xmlData = xmlData.replace(regex, (_, p1, p2, p3) => `${p1}${p2.substring(0, 5000)}${p3}`);
79
+ xmlData = xmlData.replace(regex, (_, p1, p2, p3) => `${p1}${p2.substring(0, MAX_OUTPUT_LENGTH)}${p3}`);
79
80
  }
80
81
  const jsonResult = this.parser.parse(xmlData);
81
82
  let jsonSuite;
@@ -185,6 +186,7 @@ class XmlReader {
185
186
  if (test.file)
186
187
  r.file = test.file;
187
188
  r.create = true;
189
+ r.overwrite = true;
188
190
  if (r.status === 'Passed')
189
191
  r.status = constants_js_1.STATUS.PASSED;
190
192
  if (r.status === 'Failed')
@@ -202,7 +204,7 @@ class XmlReader {
202
204
  this.tests = results.filter(t => !!t.title);
203
205
  return {
204
206
  status,
205
- create_tests: true,
207
+ create_tests: !process.env.IGNORE_NEW_TESTS,
206
208
  tests_count: parseInt(counters.total, 10),
207
209
  passed_count: parseInt(counters.passed, 10),
208
210
  skipped_count: parseInt(counters.notExecuted, 10),
@@ -253,6 +255,7 @@ class XmlReader {
253
255
  title,
254
256
  suite_title,
255
257
  run_time,
258
+ retry: false,
256
259
  });
257
260
  });
258
261
  });
@@ -313,6 +316,8 @@ class XmlReader {
313
316
  this.stats.language = 'js';
314
317
  if (file.endsWith('.ts'))
315
318
  this.stats.language = 'ts';
319
+ if (file.endsWith('.cs'))
320
+ this.stats.language = 'csharp';
316
321
  }
317
322
  if (!fs_1.default.existsSync(file)) {
318
323
  debug('Failed to open file with the source code', file);
@@ -359,13 +364,13 @@ class XmlReader {
359
364
  async uploadArtifacts() {
360
365
  for (const test of this.tests.filter(t => !!t.stack)) {
361
366
  let files = [];
362
- if (test.files?.length)
363
- files = test.files.map(f => path_1.default.join(process.cwd(), f));
364
- files = [...files, ...(0, utils_js_1.fetchFilesFromStackTrace)(test.stack)];
367
+ if (!test.files?.length)
368
+ continue;
369
+ files = test.files.map(f => (path_1.default.isAbsolute(f) ? f : path_1.default.join(process.cwd(), f)));
365
370
  if (!files.length)
366
371
  continue;
367
372
  const runId = this.runId || this.store.runId || Date.now().toString();
368
- test.artifacts = await Promise.all(files.map(f => this.uploader.uploadFileByPath(f, [runId])));
373
+ test.artifacts = await Promise.all(files.map(f => this.uploader.uploadFileByPath(f, [runId, path_1.default.basename(f)])));
369
374
  console.log(constants_js_1.APP_PREFIX, `🗄️ Uploaded ${picocolors_1.default.bold(`${files.length} artifacts`)} for test ${test.title}`);
370
375
  }
371
376
  }
@@ -415,14 +420,17 @@ function reduceTestCases(prev, item) {
415
420
  testCases = [testCases];
416
421
  }
417
422
  // suite inside test case
418
- if (item['test-suite'] && item['test-suite']['test-case'])
419
- testCases.push(...item['test-suite']['test-case']);
423
+ const testCase = item['test-suite']?.['test-case'];
424
+ if (testCase) {
425
+ const nestedCases = Array.isArray(testCase) ? testCase : [testCase];
426
+ testCases.push(...nestedCases);
427
+ }
420
428
  const suiteOutput = item['system-out'] || item.output || item.log || '';
421
429
  const suiteErr = item['system-err'] || item.output || item.log || '';
422
430
  testCases
423
431
  .filter(t => !!t)
424
432
  .forEach(testCaseItem => {
425
- const file = testCaseItem.file || item.filepath || '';
433
+ const file = testCaseItem.file || item.filepath || item.fullname || item.package || '';
426
434
  let stack = '';
427
435
  let message = '';
428
436
  if (testCaseItem.error)
@@ -444,7 +452,7 @@ function reduceTestCases(prev, item) {
444
452
  const isParametrized = item.type === 'ParameterizedMethod';
445
453
  const preferClassname = reduceOptions.preferClassname || isParametrized;
446
454
  // SpecFlow config
447
- let { title, tags } = fetchProperties(isParametrized ? item : testCaseItem);
455
+ let { title, tags, testId } = fetchProperties(isParametrized ? item : testCaseItem);
448
456
  let example = null;
449
457
  const suiteTitle = preferClassname ? testCaseItem.classname : item.name || testCaseItem.classname;
450
458
  title ||= testCaseItem.name || testCaseItem.methodname || testCaseItem.classname;
@@ -454,17 +462,38 @@ function reduceTestCases(prev, item) {
454
462
  example = { ...exampleMatches[1].split(',').map(v => v.trim().replace(/[^\w\s-]/g, '')) };
455
463
  title = title.replace(/\(.*?\)/, '').trim();
456
464
  }
457
- // eslint-disable-next-line
458
465
  stack = `${testCaseItem['system-out'] || testCaseItem.output || testCaseItem.log || ''}\n\n${stack}\n\n${suiteOutput}\n\n${suiteErr}`.trim();
459
- const testId = (0, utils_js_1.fetchIdFromOutput)(stack);
466
+ if (!testId)
467
+ testId = (0, utils_js_1.fetchIdFromOutput)(stack);
468
+ if (tags?.length && !testId) {
469
+ testId = tags
470
+ .filter(t => t.startsWith('T'))
471
+ .map(t => `@${t}`)
472
+ .find(t => t.match(utils_js_1.TEST_ID_REGEX))
473
+ ?.slice(2);
474
+ }
460
475
  let status = constants_js_1.STATUS.PASSED.toString();
461
476
  if ('failure' in testCaseItem || 'error' in testCaseItem)
462
477
  status = constants_js_1.STATUS.FAILED;
463
478
  if ('skipped' in testCaseItem)
464
479
  status = constants_js_1.STATUS.SKIPPED;
480
+ if (testCaseItem.result && Object.values(constants_js_1.STATUS).includes(testCaseItem.result.toLowerCase())) {
481
+ status = testCaseItem.result.toLowerCase();
482
+ }
465
483
  let rid = null;
466
484
  if (testCaseItem.id)
467
485
  rid = `${ridRunId}-${testCaseItem.id}`;
486
+ // Extract attachments
487
+ let files = [];
488
+ if (testCaseItem.attachments) {
489
+ const attachments = Array.isArray(testCaseItem.attachments.attachment)
490
+ ? testCaseItem.attachments.attachment
491
+ : [testCaseItem.attachments.attachment];
492
+ files = attachments.filter(a => a && a.filePath).map(a => a.filePath);
493
+ }
494
+ // Extract files from stack trace using existing utility
495
+ const stackFiles = (0, utils_js_1.fetchFilesFromStackTrace)(stack);
496
+ files = [...new Set([...files, ...stackFiles])]; // Remove duplicates
468
497
  prev.push({
469
498
  rid,
470
499
  file,
@@ -479,7 +508,10 @@ function reduceTestCases(prev, item) {
479
508
  run_time: parseFloat(testCaseItem.time || testCaseItem.duration) * 1000,
480
509
  status,
481
510
  title,
511
+ root_suite_id: TESTOMATIO_SUITE,
482
512
  suite_title: suiteTitle,
513
+ files,
514
+ retry: false,
483
515
  });
484
516
  });
485
517
  return prev;
@@ -503,12 +535,18 @@ function fetchProperties(item) {
503
535
  let title = '';
504
536
  if (!item.properties)
505
537
  return {};
506
- const prop = [item.properties?.property].flat().find(p => p.name === 'Description');
538
+ // Handle both single property and array of properties
539
+ const properties = Array.isArray(item.properties.property)
540
+ ? item.properties.property
541
+ : [item.properties.property].filter(Boolean);
542
+ const prop = properties.find(p => p.name === 'Description');
507
543
  if (prop)
508
544
  title = prop.value;
509
- [item.properties?.property]
510
- .flat()
511
- .filter(p => p.name === 'Category')
512
- .forEach(p => tags.push(p.value));
513
- return { title, tags };
545
+ let testId = properties.find(p => p.name === 'ID')?.value;
546
+ if (testId?.startsWith('@'))
547
+ testId = testId.slice(1);
548
+ if (testId?.startsWith('T'))
549
+ testId = testId.slice(1);
550
+ properties.filter(p => p.name === 'Category').forEach(p => tags.push(p.value));
551
+ return { title, tags, testId };
514
552
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/reporter",
3
- "version": "2.1.0-beta-nightwatch",
3
+ "version": "2.1.0-beta.2-codeceptjs",
4
4
  "description": "Testomatio Reporter Client",
5
5
  "engines": {
6
6
  "node": ">=18"
@@ -14,10 +14,9 @@
14
14
  "@aws-sdk/client-s3": "^3.279.0",
15
15
  "@aws-sdk/lib-storage": "^3.279.0",
16
16
  "@cucumber/cucumber": "^10.9.0",
17
- "@octokit/rest": "^19.0.5",
17
+ "@octokit/rest": "^21.1.1",
18
18
  "aws-sdk": "^2.1072.0",
19
- "axios": "^1.6.2",
20
- "axios-retry": "^3.9.1",
19
+ "gaxios": ">=6.0 || >=7.0.0-rc.4 || <8",
21
20
  "callsite-record": "^4.1.4",
22
21
  "commander": "^12",
23
22
  "cross-spawn": "^7.0.3",
@@ -49,17 +48,23 @@
49
48
  "testcafe"
50
49
  ],
51
50
  "scripts": {
52
- "@cucumber/cucumber": "^10.9.0",
53
51
  "clear-exportdir": "rm -rf export/",
54
52
  "pretty": "npx prettier --check .",
55
53
  "pretty:fix": "prettier --write .",
56
54
  "lint": "eslint src",
57
55
  "lint:fix": "eslint src --fix",
58
56
  "format": "npm run lint:fix && npm run pretty:fix",
59
- "test": "mocha tests/**",
57
+ "test": "mocha tests/unit/**/*_test.js",
58
+ "test:playwright": "mocha tests/adapter/playwright.test.js",
59
+ "test:codecept": "mocha tests/adapter/codecept.test.js tests/adapter/codecept_comprehensive.test.js tests/adapter/codecept_steps_sections.test.js",
60
+ "test:frameworks": "npm run test:playwright && npm run test:codecept",
61
+ "test:all": "npm run test && npm run test:frameworks",
62
+ "test:adapters": "mocha tests/adapter/*.test.js",
63
+ "test:codecept:bug948": "mocha tests/adapter/codecept_aftersuite_failure.test.js",
64
+ "test:codecept:steps": "mocha tests/adapter/codecept_steps_sections.test.js",
65
+ "install-example-deps": "cd example/playwright && npm install && cd ../codecept && npm install",
60
66
  "init": "cd ./tests/adapter/examples/cucumber && npm i",
61
67
  "test:adapter": "node node_modules/mocha/bin/mocha './tests/adapter/index.test.js'",
62
- "test:pipes": "mocha './tests/pipes/*_test.js'",
63
68
  "test:adapter:jest:example": "jest './tests/adapter/examples/jest/index.test.js' --config='./tests/adapter/examples/jest/jest.config.js'",
64
69
  "test:adapter:mocha:example": "mocha './tests/adapter/examples/mocha/index.test.js' --config='./tests/adapter/examples/mocha/mocha.config.cjs'",
65
70
  "test:adapter:jasmine:example": "jasmine './tests/adapter/examples/jasmine/index.test.js' --reporter=./lib/adapter/jasmine.js",
@@ -68,8 +73,9 @@
68
73
  "test:adapter:playwright:example": "npx playwright test --config='./tests/adapter/examples/playwright/playwright.config.ts'",
69
74
  "test:adapter:vitest:example": "npx vitest --config='./tests/adapter/examples/vitest/vitest.config.ts'",
70
75
  "test:storage": "npx mocha tests-storage/artifact-storage.test.js && npx mocha tests-storage/data-storage.test.js && TESTOMATIO_INTERCEPT_CONSOLE_LOGS=true npx mocha tests-storage/logger.test.js && npx mocha tests-storage/logger-2.test.js && npx mocha tests-storage/reporter-functions.test.js",
71
- "//": "builds code from /src (esm) into /lib (commonjs)",
72
- "build": "rm -rf ./cjs && tsc --module commonjs && npx tsx build/scripts/edit-js-files.js && npx tsx build/scripts/edit-package-json.js && chmod +x ./build/scripts/copy-tesmplate.sh && ./build/scripts/copy-tesmplate.sh"
76
+ "build": "rm -rf ./cjs && tsc --module commonjs && npx tsx build/scripts/edit-js-files.js && npx tsx build/scripts/edit-package-json.js && chmod +x ./build/scripts/copy-tesmplate.sh && ./build/scripts/copy-tesmplate.sh",
77
+ "build:bun": "rm -rf ./cjs && bunx tsc --module commonjs && npx tsx build/scripts/edit-js-files.js && npx tsx build/scripts/edit-package-json.js && chmod +x ./build/scripts/copy-tesmplate.sh && ./build/scripts/copy-tesmplate.sh",
78
+ "build:watch:bun": "rm -rf ./cjs && bun build ./src/bin/reportXml.js ./src/bin/startTest.js ./src/bin/uploadArtifacts.js --outdir ./cjs --target node --watch --onSuccess \"build/scripts/post-build.js\""
73
79
  },
74
80
  "devDependencies": {
75
81
  "@playwright/test": "^1.49.1",
@@ -81,8 +87,7 @@
81
87
  "chai": "^4.3.6",
82
88
  "codeceptjs": "^3.6.5",
83
89
  "cucumber": "^6.0.7",
84
- "eslint": "^8.57.0",
85
- "eslint-config-airbnb-base": "^15.0.0",
90
+ "eslint": "^9.24.0",
86
91
  "eslint-config-prettier": "^8.3.0",
87
92
  "eslint-plugin-import": "^2.25.4",
88
93
  "jasmine": "^5.2.0",