@testomatio/reporter 2.9.1-beta.1-allure-chunking → 2.9.1-beta.2-allure-tms-link

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.
@@ -52,6 +52,34 @@ declare class AllureReader {
52
52
  extractLinks(result: any): {
53
53
  label: string;
54
54
  }[];
55
+ /**
56
+ * Extract a Testomat.io test id from Allure links so reported tests match
57
+ * existing cases instead of creating duplicates.
58
+ *
59
+ * Allure's `@TmsLink("T1a2b3c4d")` produces a link with `type: "tms"`. Some exporters
60
+ * omit the type but still point the link URL at a Testomat.io test page; both are
61
+ * accepted. The link `name` is used as the id (falling back to the last URL segment).
62
+ *
63
+ * @param {object} result - Parsed Allure result JSON
64
+ * @returns {string|null} Normalized test id, or null when no usable link exists
65
+ */
66
+ extractTestId(result: object): string | null;
67
+ /**
68
+ * Normalize a value into a Testomat.io test id.
69
+ *
70
+ * Testomat.io test ids are exactly **8 word characters**. The value may arrive bare
71
+ * (`1a2b3c4d`), or carrying the `T` / `@T` markers Testomat uses in code and titles
72
+ * (`T1a2b3c4d`, `@T1a2b3c4d`). The markers are removed only when doing so still leaves
73
+ * a valid 8-char id, so a real id that happens to start with `T` is preserved.
74
+ *
75
+ * Anything that does not resolve to a valid 8-char id — a numeric Allure TestOps id
76
+ * like `12345`, a JIRA key, a 6-digit TMS number — is rejected (returns null) so we
77
+ * never send an unmatchable id that would create duplicates.
78
+ *
79
+ * @param {string|number|null|undefined} value
80
+ * @returns {string|null} The bare 8-char id, or null when the value is not a valid id
81
+ */
82
+ normalizeTestId(value: string | number | null | undefined): string | null;
55
83
  convertSteps(steps: any, depth?: number): any;
56
84
  calculateRunTime(item: any): number;
57
85
  convertParameters(parameters: any): {};
@@ -169,6 +169,12 @@ class AllureReader {
169
169
  create: true,
170
170
  overwrite: true,
171
171
  };
172
+ // Use the @TmsLink / Testomat.io link as the test id so reported tests MATCH
173
+ // existing cases instead of creating duplicates on every run.
174
+ const testId = this.extractTestId(result);
175
+ if (testId) {
176
+ test.test_id = testId;
177
+ }
172
178
  // Add description if present
173
179
  if (result.description) {
174
180
  test.description = result.description;
@@ -296,6 +302,56 @@ class AllureReader {
296
302
  }
297
303
  return links.length > 0 ? links : undefined;
298
304
  }
305
+ /**
306
+ * Extract a Testomat.io test id from Allure links so reported tests match
307
+ * existing cases instead of creating duplicates.
308
+ *
309
+ * Allure's `@TmsLink("T1a2b3c4d")` produces a link with `type: "tms"`. Some exporters
310
+ * omit the type but still point the link URL at a Testomat.io test page; both are
311
+ * accepted. The link `name` is used as the id (falling back to the last URL segment).
312
+ *
313
+ * @param {object} result - Parsed Allure result JSON
314
+ * @returns {string|null} Normalized test id, or null when no usable link exists
315
+ */
316
+ extractTestId(result) {
317
+ const links = result.links || [];
318
+ if (!links.length)
319
+ return null;
320
+ const isTmsLink = l => typeof l?.type === 'string' && l.type.toLowerCase() === 'tms';
321
+ const isTestomatioLink = l => typeof l?.url === 'string' && /testomat\.io\/[^\s]*\/test\//i.test(l.url);
322
+ const link = links.find(isTmsLink) || links.find(isTestomatioLink);
323
+ if (!link)
324
+ return null;
325
+ // Prefer the explicit link name; fall back to the id segment of a Testomat.io URL.
326
+ let id = this.normalizeTestId(link.name);
327
+ if (!id && typeof link.url === 'string') {
328
+ const fromUrl = link.url.match(/\/test\/([\w\d]{8})(?=$|[/?#])/i);
329
+ if (fromUrl)
330
+ id = fromUrl[1];
331
+ }
332
+ return id;
333
+ }
334
+ /**
335
+ * Normalize a value into a Testomat.io test id.
336
+ *
337
+ * Testomat.io test ids are exactly **8 word characters**. The value may arrive bare
338
+ * (`1a2b3c4d`), or carrying the `T` / `@T` markers Testomat uses in code and titles
339
+ * (`T1a2b3c4d`, `@T1a2b3c4d`). The markers are removed only when doing so still leaves
340
+ * a valid 8-char id, so a real id that happens to start with `T` is preserved.
341
+ *
342
+ * Anything that does not resolve to a valid 8-char id — a numeric Allure TestOps id
343
+ * like `12345`, a JIRA key, a 6-digit TMS number — is rejected (returns null) so we
344
+ * never send an unmatchable id that would create duplicates.
345
+ *
346
+ * @param {string|number|null|undefined} value
347
+ * @returns {string|null} The bare 8-char id, or null when the value is not a valid id
348
+ */
349
+ normalizeTestId(value) {
350
+ if (value === null || value === undefined)
351
+ return null;
352
+ const match = value.toString().trim().match(/^@?T?([\w\d]{8})$/);
353
+ return match ? match[1] : null;
354
+ }
299
355
  convertSteps(steps, depth = 0) {
300
356
  if (depth >= 10)
301
357
  return null;
@@ -413,8 +469,10 @@ class AllureReader {
413
469
  t.code = code;
414
470
  debug('Fetched code for test %s', t.title);
415
471
  }
472
+ // Don't override an id already taken from a @TmsLink — the link is the
473
+ // explicit, source-independent match key the client maintains.
416
474
  const testId = (0, utils_js_1.fetchIdFromCode)(contents, { lang: this.getLanguage() });
417
- if (testId) {
475
+ if (testId && !t.test_id) {
418
476
  t.test_id = testId;
419
477
  debug('Fetched test id %s for test %s', testId, t.title);
420
478
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@testomatio/reporter",
3
- "version": "2.9.1-beta.1-allure-chunking",
3
+ "version": "2.9.1-beta.2-allure-tms-link",
4
4
  "description": "Testomatio Reporter Client",
5
5
  "engines": {
6
6
  "node": ">=18"
@@ -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;
@@ -346,6 +353,58 @@ class AllureReader {
346
353
  return links.length > 0 ? links : undefined;
347
354
  }
348
355
 
356
+ /**
357
+ * Extract a Testomat.io test id from Allure links so reported tests match
358
+ * existing cases instead of creating duplicates.
359
+ *
360
+ * Allure's `@TmsLink("T1a2b3c4d")` produces a link with `type: "tms"`. Some exporters
361
+ * omit the type but still point the link URL at a Testomat.io test page; both are
362
+ * accepted. The link `name` is used as the id (falling back to the last URL segment).
363
+ *
364
+ * @param {object} result - Parsed Allure result JSON
365
+ * @returns {string|null} Normalized test id, or null when no usable link exists
366
+ */
367
+ extractTestId(result) {
368
+ const links = result.links || [];
369
+ if (!links.length) return null;
370
+
371
+ const isTmsLink = l => typeof l?.type === 'string' && l.type.toLowerCase() === 'tms';
372
+ const isTestomatioLink = l => typeof l?.url === 'string' && /testomat\.io\/[^\s]*\/test\//i.test(l.url);
373
+
374
+ const link = links.find(isTmsLink) || links.find(isTestomatioLink);
375
+ if (!link) return null;
376
+
377
+ // Prefer the explicit link name; fall back to the id segment of a Testomat.io URL.
378
+ let id = this.normalizeTestId(link.name);
379
+ if (!id && typeof link.url === 'string') {
380
+ const fromUrl = link.url.match(/\/test\/([\w\d]{8})(?=$|[/?#])/i);
381
+ if (fromUrl) id = fromUrl[1];
382
+ }
383
+
384
+ return id;
385
+ }
386
+
387
+ /**
388
+ * Normalize a value into a Testomat.io test id.
389
+ *
390
+ * Testomat.io test ids are exactly **8 word characters**. The value may arrive bare
391
+ * (`1a2b3c4d`), or carrying the `T` / `@T` markers Testomat uses in code and titles
392
+ * (`T1a2b3c4d`, `@T1a2b3c4d`). The markers are removed only when doing so still leaves
393
+ * a valid 8-char id, so a real id that happens to start with `T` is preserved.
394
+ *
395
+ * Anything that does not resolve to a valid 8-char id — a numeric Allure TestOps id
396
+ * like `12345`, a JIRA key, a 6-digit TMS number — is rejected (returns null) so we
397
+ * never send an unmatchable id that would create duplicates.
398
+ *
399
+ * @param {string|number|null|undefined} value
400
+ * @returns {string|null} The bare 8-char id, or null when the value is not a valid id
401
+ */
402
+ normalizeTestId(value) {
403
+ if (value === null || value === undefined) return null;
404
+ const match = value.toString().trim().match(/^@?T?([\w\d]{8})$/);
405
+ return match ? match[1] : null;
406
+ }
407
+
349
408
  convertSteps(steps, depth = 0) {
350
409
  if (depth >= 10) return null;
351
410
 
@@ -482,8 +541,10 @@ class AllureReader {
482
541
  debug('Fetched code for test %s', t.title);
483
542
  }
484
543
 
544
+ // Don't override an id already taken from a @TmsLink — the link is the
545
+ // explicit, source-independent match key the client maintains.
485
546
  const testId = fetchIdFromCode(contents, { lang: this.getLanguage() });
486
- if (testId) {
547
+ if (testId && !t.test_id) {
487
548
  t.test_id = testId;
488
549
  debug('Fetched test id %s for test %s', testId, t.title);
489
550
  }