testomatio-reporter-cli 2.9.1-beta.1-allure-chunking → 2.9.1-beta.3-allure-steps

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testomatio-reporter-cli",
3
- "version": "2.9.1-beta.1-allure-chunking",
3
+ "version": "2.9.1-beta.3-allure-steps",
4
4
  "description": "Yarn Berry compatible standalone CLI for @testomatio/reporter",
5
5
  "type": "module",
6
6
  "bin": {
@@ -193,6 +193,13 @@ class AllureReader {
193
193
  overwrite: true,
194
194
  };
195
195
 
196
+ // Use the @TmsLink / Testomat.io link as the test id so reported tests MATCH
197
+ // existing cases instead of creating duplicates on every run.
198
+ const testId = this.extractTestId(result);
199
+ if (testId) {
200
+ test.test_id = testId;
201
+ }
202
+
196
203
  // Add description if present
197
204
  if (result.description) {
198
205
  test.description = result.description;
@@ -233,6 +240,29 @@ class AllureReader {
233
240
  return statusMap[status] || 'failed';
234
241
  }
235
242
 
243
+ /**
244
+ * Map an Allure step status to the Testomat.io Step status enum
245
+ * (`passed | failed | none | custom`, see testomat-api-definition.yml).
246
+ *
247
+ * Allure marks a step `broken` when it threw an unexpected error — that is a
248
+ * failure for reporting purposes, matching how `mapStatus` treats tests.
249
+ * `skipped` and anything unknown/absent become `none` (the neutral value),
250
+ * since the step enum has no `skipped`.
251
+ *
252
+ * @param {string} status - Allure step status
253
+ * @returns {'passed'|'failed'|'none'} Testomat.io step status
254
+ */
255
+ mapStepStatus(status) {
256
+ const statusMap = {
257
+ passed: 'passed',
258
+ failed: 'failed',
259
+ broken: 'failed',
260
+ skipped: 'none',
261
+ pending: 'none',
262
+ };
263
+ return statusMap[status] || 'none';
264
+ }
265
+
236
266
  extractSuiteTitle(result) {
237
267
  const labels = result.labels || [];
238
268
 
@@ -346,6 +376,58 @@ class AllureReader {
346
376
  return links.length > 0 ? links : undefined;
347
377
  }
348
378
 
379
+ /**
380
+ * Extract a Testomat.io test id from Allure links so reported tests match
381
+ * existing cases instead of creating duplicates.
382
+ *
383
+ * Allure's `@TmsLink("T1a2b3c4d")` produces a link with `type: "tms"`. Some exporters
384
+ * omit the type but still point the link URL at a Testomat.io test page; both are
385
+ * accepted. The link `name` is used as the id (falling back to the last URL segment).
386
+ *
387
+ * @param {object} result - Parsed Allure result JSON
388
+ * @returns {string|null} Normalized test id, or null when no usable link exists
389
+ */
390
+ extractTestId(result) {
391
+ const links = result.links || [];
392
+ if (!links.length) return null;
393
+
394
+ const isTmsLink = l => typeof l?.type === 'string' && l.type.toLowerCase() === 'tms';
395
+ const isTestomatioLink = l => typeof l?.url === 'string' && /testomat\.io\/[^\s]*\/test\//i.test(l.url);
396
+
397
+ const link = links.find(isTmsLink) || links.find(isTestomatioLink);
398
+ if (!link) return null;
399
+
400
+ // Prefer the explicit link name; fall back to the id segment of a Testomat.io URL.
401
+ let id = this.normalizeTestId(link.name);
402
+ if (!id && typeof link.url === 'string') {
403
+ const fromUrl = link.url.match(/\/test\/([\w\d]{8})(?=$|[/?#])/i);
404
+ if (fromUrl) id = fromUrl[1];
405
+ }
406
+
407
+ return id;
408
+ }
409
+
410
+ /**
411
+ * Normalize a value into a Testomat.io test id.
412
+ *
413
+ * Testomat.io test ids are exactly **8 word characters**. The value may arrive bare
414
+ * (`1a2b3c4d`), or carrying the `T` / `@T` markers Testomat uses in code and titles
415
+ * (`T1a2b3c4d`, `@T1a2b3c4d`). The markers are removed only when doing so still leaves
416
+ * a valid 8-char id, so a real id that happens to start with `T` is preserved.
417
+ *
418
+ * Anything that does not resolve to a valid 8-char id — a numeric Allure TestOps id
419
+ * like `12345`, a JIRA key, a 6-digit TMS number — is rejected (returns null) so we
420
+ * never send an unmatchable id that would create duplicates.
421
+ *
422
+ * @param {string|number|null|undefined} value
423
+ * @returns {string|null} The bare 8-char id, or null when the value is not a valid id
424
+ */
425
+ normalizeTestId(value) {
426
+ if (value === null || value === undefined) return null;
427
+ const match = value.toString().trim().match(/^@?T?([\w\d]{8})$/);
428
+ return match ? match[1] : null;
429
+ }
430
+
349
431
  convertSteps(steps, depth = 0) {
350
432
  if (depth >= 10) return null;
351
433
 
@@ -354,6 +436,7 @@ class AllureReader {
354
436
  const convertedStep = {
355
437
  category: 'user',
356
438
  title: step.name || step.title || 'Unknown step',
439
+ status: this.mapStepStatus(step.status),
357
440
  duration: this.calculateRunTime(step),
358
441
  steps: this.convertSteps(step.steps || [], depth + 1),
359
442
  };
@@ -482,8 +565,10 @@ class AllureReader {
482
565
  debug('Fetched code for test %s', t.title);
483
566
  }
484
567
 
568
+ // Don't override an id already taken from a @TmsLink — the link is the
569
+ // explicit, source-independent match key the client maintains.
485
570
  const testId = fetchIdFromCode(contents, { lang: this.getLanguage() });
486
- if (testId) {
571
+ if (testId && !t.test_id) {
487
572
  t.test_id = testId;
488
573
  debug('Fetched test id %s for test %s', testId, t.title);
489
574
  }