@sudobility/testomniac_runner_service 0.1.58 → 0.1.60

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 (35) hide show
  1. package/dist/analyzer/page-analyzer/generators/content.js +4 -4
  2. package/dist/analyzer/page-analyzer/generators/content.js.map +1 -1
  3. package/dist/analyzer/page-analyzer/generators/dialogs.d.ts.map +1 -1
  4. package/dist/analyzer/page-analyzer/generators/dialogs.js +3 -3
  5. package/dist/analyzer/page-analyzer/generators/dialogs.js.map +1 -1
  6. package/dist/analyzer/page-analyzer/generators/e2e.js +2 -2
  7. package/dist/analyzer/page-analyzer/generators/e2e.js.map +1 -1
  8. package/dist/analyzer/page-analyzer/generators/forms.js +8 -8
  9. package/dist/analyzer/page-analyzer/generators/forms.js.map +1 -1
  10. package/dist/analyzer/page-analyzer/generators/hover-follow-up.d.ts.map +1 -1
  11. package/dist/analyzer/page-analyzer/generators/hover-follow-up.js +3 -3
  12. package/dist/analyzer/page-analyzer/generators/hover-follow-up.js.map +1 -1
  13. package/dist/analyzer/page-analyzer/generators/keyboard-disclosure.js +2 -2
  14. package/dist/analyzer/page-analyzer/generators/keyboard-disclosure.js.map +1 -1
  15. package/dist/analyzer/page-analyzer/generators/navigation.js +4 -4
  16. package/dist/analyzer/page-analyzer/generators/navigation.js.map +1 -1
  17. package/dist/analyzer/page-analyzer/generators/render.js +1 -1
  18. package/dist/analyzer/page-analyzer/generators/render.js.map +1 -1
  19. package/dist/analyzer/page-analyzer/generators/scaffolds.js +10 -10
  20. package/dist/analyzer/page-analyzer/generators/scaffolds.js.map +1 -1
  21. package/dist/analyzer/page-analyzer/generators/semantic-journeys.d.ts.map +1 -1
  22. package/dist/analyzer/page-analyzer/generators/semantic-journeys.js +2 -2
  23. package/dist/analyzer/page-analyzer/generators/semantic-journeys.js.map +1 -1
  24. package/dist/analyzer/page-analyzer/generators/variants.js +2 -2
  25. package/dist/analyzer/page-analyzer/generators/variants.js.map +1 -1
  26. package/dist/analyzer/page-analyzer/index.d.ts +9 -1
  27. package/dist/analyzer/page-analyzer/index.d.ts.map +1 -1
  28. package/dist/analyzer/page-analyzer/index.js +92 -8
  29. package/dist/analyzer/page-analyzer/index.js.map +1 -1
  30. package/dist/orchestrator/test-element-executor.d.ts.map +1 -1
  31. package/dist/orchestrator/test-element-executor.js +21 -2
  32. package/dist/orchestrator/test-element-executor.js.map +1 -1
  33. package/dist/orchestrator/types.d.ts +1 -0
  34. package/dist/orchestrator/types.d.ts.map +1 -1
  35. package/package.json +3 -3
@@ -85,7 +85,7 @@ export class PageAnalyzer {
85
85
  if (!surface)
86
86
  return;
87
87
  const existing = await context.api.getTestElementsByTestSurface(surface.id, true);
88
- const desiredTitles = new Set(params.desiredTitles);
88
+ const desiredKeys = new Set(params.desiredKeys.map(key => key.trim()).filter(Boolean));
89
89
  const obsoleteIds = existing
90
90
  .filter(testElement => {
91
91
  const isGenerated = Boolean(testElement.isGenerated);
@@ -98,7 +98,8 @@ export class PageAnalyzer {
98
98
  (params.dependencyTestElementId ?? null)) {
99
99
  return false;
100
100
  }
101
- return !desiredTitles.has(testElement.title);
101
+ const existingKey = this.getPersistedGeneratedKey(testElement);
102
+ return !existingKey || !desiredKeys.has(existingKey);
102
103
  })
103
104
  .map(testElement => testElement.id);
104
105
  if (obsoleteIds.length === 0)
@@ -172,10 +173,11 @@ export class PageAnalyzer {
172
173
  ],
173
174
  globalExpectations: [],
174
175
  uid,
176
+ generatedKey: this.buildGeneratedKey("navigation", startingPageStateId, path),
175
177
  };
176
178
  }
177
179
  buildHoverTestElement(item, startingPath, sizeClass, uid, startingPageStateId, dependencyTestElementId) {
178
- const label = item.accessibleName || item.textContent || item.selector;
180
+ const label = this.describeActionableItem(item);
179
181
  return {
180
182
  title: `Hover over ${label}`,
181
183
  type: "interaction",
@@ -200,10 +202,11 @@ export class PageAnalyzer {
200
202
  ],
201
203
  globalExpectations: [],
202
204
  uid,
205
+ generatedKey: this.buildGeneratedKey("hover", startingPageStateId, dependencyTestElementId, item.stableKey ?? item.selector ?? label),
203
206
  };
204
207
  }
205
208
  buildClickTestElement(item, startingPath, sizeClass, uid, startingPageStateId, dependencyTestElementId) {
206
- const label = item.accessibleName || item.textContent || item.selector;
209
+ const label = this.describeActionableItem(item);
207
210
  return {
208
211
  title: `Click ${label}`,
209
212
  type: "interaction",
@@ -228,6 +231,7 @@ export class PageAnalyzer {
228
231
  ],
229
232
  globalExpectations: [],
230
233
  uid,
234
+ generatedKey: this.buildGeneratedKey("click", startingPageStateId, dependencyTestElementId, item.stableKey ?? item.selector ?? label),
231
235
  };
232
236
  }
233
237
  isHoverOnly(testElement) {
@@ -245,6 +249,49 @@ export class PageAnalyzer {
245
249
  const selector = item.selector;
246
250
  return stableKey ?? selector ?? null;
247
251
  }
252
+ withGeneratedKey(testElement, ...parts) {
253
+ return {
254
+ ...testElement,
255
+ generatedKey: this.buildGeneratedKey(...parts),
256
+ };
257
+ }
258
+ getGeneratedKey(testElement) {
259
+ return (testElement.generatedKey?.trim() || testElement.title).trim();
260
+ }
261
+ getPersistedGeneratedKey(testElement) {
262
+ const generatedKey = testElement.generatedKey?.trim();
263
+ if (generatedKey)
264
+ return generatedKey;
265
+ const title = testElement.title?.trim();
266
+ return title || null;
267
+ }
268
+ buildGeneratedKey(...parts) {
269
+ const normalized = parts
270
+ .map(part => (part == null ? "" : String(part).trim()))
271
+ .filter(Boolean);
272
+ const raw = normalized.join("||");
273
+ const digest = createHash("sha1").update(raw).digest("hex").slice(0, 16);
274
+ const prefix = normalized
275
+ .slice(0, 3)
276
+ .map(part => part
277
+ .toLowerCase()
278
+ .replace(/[^a-z0-9]+/g, "-")
279
+ .replace(/^-+|-+$/g, ""))
280
+ .filter(Boolean)
281
+ .join(":")
282
+ .slice(0, 80);
283
+ return prefix ? `${prefix}:${digest}` : digest;
284
+ }
285
+ buildStepSignature(steps) {
286
+ return steps
287
+ .map(step => [
288
+ step.action?.actionType ?? "",
289
+ step.action?.path ?? "",
290
+ step.action?.value ?? "",
291
+ step.action?.description ?? "",
292
+ ].join("|"))
293
+ .join("||");
294
+ }
248
295
  async ensureTargetPageState(context) {
249
296
  const scaffoldIdsBySelector = await this.ensureScaffolds(context);
250
297
  for (const item of context.actionableItems) {
@@ -402,6 +449,7 @@ export class PageAnalyzer {
402
449
  ],
403
450
  globalExpectations: this.defaultFlowExpectations("Render page without runtime errors"),
404
451
  uid,
452
+ generatedKey: this.buildGeneratedKey("render", startingPageStateId, pageId, currentPath),
405
453
  };
406
454
  }
407
455
  buildFormTestElement(form, formLabel, formType, currentPath, sizeClass, uid, startingPageStateId, validValues) {
@@ -446,6 +494,7 @@ export class PageAnalyzer {
446
494
  this.makeExpectation("field_error_clears_after_fix", `Validation errors on ${formLabel} should clear once the fields are corrected`),
447
495
  ],
448
496
  uid,
497
+ generatedKey: this.buildGeneratedKey("form-positive", startingPageStateId, form.selector, formType),
449
498
  };
450
499
  }
451
500
  buildNegativeFormTestElement(form, formLabel, formType, omittedField, currentPath, sizeClass, uid, startingPageStateId, validValues) {
@@ -468,6 +517,7 @@ export class PageAnalyzer {
468
517
  }),
469
518
  ],
470
519
  uid,
520
+ generatedKey: this.buildGeneratedKey("form-negative", startingPageStateId, form.selector, formType, omittedField.selector),
471
521
  };
472
522
  }
473
523
  buildFormCorrectionTestElement(form, formLabel, formType, correctedField, currentPath, sizeClass, uid, startingPageStateId, validValues) {
@@ -512,6 +562,7 @@ export class PageAnalyzer {
512
562
  this.makeExpectation("feedback_not_duplicated", `Form ${formLabel} should not show duplicate feedback after correction`),
513
563
  ],
514
564
  uid,
565
+ generatedKey: this.buildGeneratedKey("form-correction", startingPageStateId, form.selector, formType, correctedField.selector),
515
566
  };
516
567
  }
517
568
  buildPasswordTestElements(form, formLabel, formType, currentPath, sizeClass, uid, startingPageStateId, validValues, passwordRequirements) {
@@ -547,6 +598,7 @@ export class PageAnalyzer {
547
598
  },
548
599
  ],
549
600
  uid,
601
+ generatedKey: this.buildGeneratedKey("password", startingPageStateId, form.selector, formType, variant.description, variant.password),
550
602
  };
551
603
  });
552
604
  }
@@ -565,6 +617,7 @@ export class PageAnalyzer {
565
617
  })),
566
618
  globalExpectations: this.defaultFlowExpectations(`Journey to ${currentPath} should complete without runtime errors`),
567
619
  uid,
620
+ generatedKey: this.buildGeneratedKey("dependency-journey", startingPageStateId, currentPath, this.buildStepSignature(journeySteps)),
568
621
  };
569
622
  }
570
623
  buildFormSteps(form, valuesBySelector, omittedSelector, postSubmitExpectations = []) {
@@ -845,6 +898,7 @@ export class PageAnalyzer {
845
898
  this.makeExpectation(ExpectationType.LoadingCompletes, `Search results for ${formLabel} should finish loading`),
846
899
  ],
847
900
  uid,
901
+ generatedKey: this.buildGeneratedKey("search", startingPageStateId, form.selector, searchField.selector),
848
902
  },
849
903
  {
850
904
  title: `Search Empty State — ${formLabel}`,
@@ -867,6 +921,7 @@ export class PageAnalyzer {
867
921
  this.makeExpectation(ExpectationType.LoadingCompletes, `No-result search for ${formLabel} should finish loading`),
868
922
  ],
869
923
  uid,
924
+ generatedKey: this.buildGeneratedKey("search-empty", startingPageStateId, form.selector, searchField.selector),
870
925
  },
871
926
  {
872
927
  title: `Search Recovery — ${formLabel}`,
@@ -890,6 +945,7 @@ export class PageAnalyzer {
890
945
  this.makeExpectation(ExpectationType.LoadingCompletes, `Search recovery for ${formLabel} should finish loading`),
891
946
  ],
892
947
  uid,
948
+ generatedKey: this.buildGeneratedKey("search-recovery", startingPageStateId, form.selector, searchField.selector),
893
949
  },
894
950
  ];
895
951
  const clearAction = actionableItems.find(item => this.isSearchClearItem(item));
@@ -916,6 +972,7 @@ export class PageAnalyzer {
916
972
  this.makeExpectation(ExpectationType.ResultsRestored, `Clearing ${formLabel} should restore the initial results baseline`),
917
973
  ],
918
974
  uid,
975
+ generatedKey: this.buildGeneratedKey("search-clear", startingPageStateId, form.selector, searchField.selector, clearAction.selector),
919
976
  });
920
977
  }
921
978
  return tests;
@@ -1252,6 +1309,7 @@ export class PageAnalyzer {
1252
1309
  steps,
1253
1310
  globalExpectations: this.defaultFlowExpectations(`${title} should complete without runtime errors`),
1254
1311
  uid: context.uid,
1312
+ generatedKey: this.buildGeneratedKey("semantic-journey", context.currentPageStateId, context.currentPath, this.buildStepSignature(steps)),
1255
1313
  };
1256
1314
  }
1257
1315
  buildJourneyAction(item, description, expectations) {
@@ -1450,6 +1508,7 @@ export class PageAnalyzer {
1450
1508
  ],
1451
1509
  globalExpectations: this.defaultFlowExpectations("Variant selection should complete without runtime errors"),
1452
1510
  uid: context.uid,
1511
+ generatedKey: this.buildGeneratedKey("variant-selection", context.currentPageStateId, item.selector, plannedValue),
1453
1512
  };
1454
1513
  }
1455
1514
  buildVariantPurchaseJourney(item, purchaseAction, context) {
@@ -1521,6 +1580,7 @@ export class PageAnalyzer {
1521
1580
  ],
1522
1581
  globalExpectations: this.defaultFlowExpectations(`Variant purchase journey for ${label} should execute cleanly`),
1523
1582
  uid: context.uid,
1583
+ generatedKey: this.buildGeneratedKey("variant-purchase", context.currentPageStateId, item.selector, plannedValue, purchaseAction.selector),
1524
1584
  };
1525
1585
  }
1526
1586
  buildRequiredVariantGuardTestElement(item, requiredField, purchaseAction, context) {
@@ -1544,6 +1604,7 @@ export class PageAnalyzer {
1544
1604
  ],
1545
1605
  globalExpectations: this.defaultFlowExpectations(`${label} should be enforced before ${purchaseLabel}`),
1546
1606
  uid: context.uid,
1607
+ generatedKey: this.buildGeneratedKey("variant-guard", context.currentPageStateId, item.selector, requiredField.selector, purchaseAction.selector),
1547
1608
  };
1548
1609
  }
1549
1610
  findRequiredVariantField(item, forms) {
@@ -1585,6 +1646,7 @@ export class PageAnalyzer {
1585
1646
  ],
1586
1647
  globalExpectations: this.defaultFlowExpectations("Disclosure interaction should complete without runtime errors"),
1587
1648
  uid,
1649
+ generatedKey: this.buildGeneratedKey("disclosure-click", startingPageStateId, item.stableKey ?? item.selector ?? label),
1588
1650
  };
1589
1651
  }
1590
1652
  buildKeyboardActivateTestElement(item, key, titlePrefix, expectations, startingPath, sizeClass, uid, startingPageStateId) {
@@ -1630,6 +1692,7 @@ export class PageAnalyzer {
1630
1692
  ],
1631
1693
  globalExpectations: this.defaultFlowExpectations("Keyboard activation should complete without runtime errors"),
1632
1694
  uid,
1695
+ generatedKey: this.buildGeneratedKey("keyboard", startingPageStateId, key === " " ? "space" : key, item.stableKey ?? item.selector ?? label),
1633
1696
  };
1634
1697
  }
1635
1698
  buildDialogCloseTestElement(item, startingPath, sizeClass, uid, startingPageStateId) {
@@ -1660,6 +1723,7 @@ export class PageAnalyzer {
1660
1723
  ],
1661
1724
  globalExpectations: this.defaultFlowExpectations("Dialog should close cleanly"),
1662
1725
  uid,
1726
+ generatedKey: this.buildGeneratedKey("dialog-close", startingPageStateId, item.stableKey ?? item.selector ?? label),
1663
1727
  };
1664
1728
  }
1665
1729
  buildEscapeDialogTestElement(startingPath, sizeClass, uid, startingPageStateId) {
@@ -1689,6 +1753,7 @@ export class PageAnalyzer {
1689
1753
  ],
1690
1754
  globalExpectations: this.defaultFlowExpectations("Dialog should close on Escape without runtime errors"),
1691
1755
  uid,
1756
+ generatedKey: this.buildGeneratedKey("dialog-escape", startingPageStateId, startingPath),
1692
1757
  };
1693
1758
  }
1694
1759
  shouldUseDirectControlInteraction(item) {
@@ -1706,7 +1771,7 @@ export class PageAnalyzer {
1706
1771
  inputType === "checkbox");
1707
1772
  }
1708
1773
  buildControlInteractionTestElement(item, startingPath, sizeClass, uid, startingPageStateId) {
1709
- const label = item.accessibleName || item.textContent || item.selector;
1774
+ const label = this.describeActionableItem(item);
1710
1775
  const role = (item.role ?? "").toLowerCase();
1711
1776
  const inputType = (item.inputType ?? "").toLowerCase();
1712
1777
  const isTab = role === "tab";
@@ -1791,6 +1856,7 @@ export class PageAnalyzer {
1791
1856
  ? `${label} should remain non-interactive despite appearing disabled`
1792
1857
  : `${label} should react without runtime errors`),
1793
1858
  uid,
1859
+ generatedKey: this.buildGeneratedKey("control", startingPageStateId, isImmutable ? "immutable" : "enabled", role || inputType || item.actionKind || "control", item.stableKey ?? item.selector ?? label),
1794
1860
  };
1795
1861
  }
1796
1862
  buildImmutableControlExpectations(item, label) {
@@ -1873,12 +1939,30 @@ export class PageAnalyzer {
1873
1939
  value.includes("disabled"));
1874
1940
  }
1875
1941
  describeActionableItem(item) {
1876
- return (item.accessibleName ||
1942
+ const described = (item.accessibleName ||
1877
1943
  item.textContent ||
1878
1944
  String(item.attributes?._containerTitle ?? "") ||
1879
1945
  String(item.attributes?.labelText ?? "") ||
1880
- String(item.attributes?.placeholder ?? "") ||
1881
- item.selector);
1946
+ String(item.attributes?.placeholder ?? "")).trim();
1947
+ if (described)
1948
+ return described;
1949
+ const selector = item.selector ?? "";
1950
+ if (selector.includes("data-tmnc-id")) {
1951
+ const role = item.role?.trim();
1952
+ const inputType = item.inputType?.trim();
1953
+ if (role)
1954
+ return role;
1955
+ if (inputType)
1956
+ return inputType;
1957
+ if (item.actionKind === "navigate")
1958
+ return "link";
1959
+ if (item.actionKind === "fill")
1960
+ return "input";
1961
+ if (item.actionKind === "select")
1962
+ return "select";
1963
+ return "control";
1964
+ }
1965
+ return selector || item.actionKind || "control";
1882
1966
  }
1883
1967
  semanticText(item) {
1884
1968
  return [