@unotest/web 0.9.1 → 0.11.0

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.
@@ -520,12 +520,9 @@ function semanticDomAlgorithm(options) {
520
520
  return count;
521
521
  }
522
522
  __name(countInteractiveInSubtree, "countInteractiveInSubtree");
523
- for (let i = 0; i < nodes.length; i++) {
524
- const node = nodes[i];
525
- if (node.children.length < 3) continue;
526
- if (collectionMemberSet.has(i)) continue;
523
+ function tryDetectCollection(parentIdx, candidates) {
527
524
  const sigGroups = /* @__PURE__ */ new Map();
528
- for (const ci of node.children) {
525
+ for (const ci of candidates) {
529
526
  const sig = nodes[ci].depth2Signature || "";
530
527
  if (!sigGroups.has(sig)) sigGroups.set(sig, []);
531
528
  sigGroups.get(sig).push(ci);
@@ -538,7 +535,7 @@ function semanticDomAlgorithm(options) {
538
535
  dominantGroup = group;
539
536
  }
540
537
  }
541
- if (dominantGroup.length < 3) continue;
538
+ if (dominantGroup.length < 3) return;
542
539
  const dominantTag = nodes[dominantGroup[0]].tag;
543
540
  const dominantChildTags = nodes[dominantGroup[0]].children.map((ci) => nodes[ci].tag);
544
541
  let fuzzyCount = 0;
@@ -557,11 +554,11 @@ function semanticDomAlgorithm(options) {
557
554
  fuzzyCount++;
558
555
  }
559
556
  }
560
- if (allItems.length < 3) continue;
557
+ if (allItems.length < 3) return;
561
558
  const avgInteractive = allItems.reduce((sum, ci) => sum + countInteractiveInSubtree(ci), 0) / allItems.length;
562
- if (avgInteractive < 3) continue;
559
+ if (avgInteractive < 3) return;
563
560
  detectedCollections.push({
564
- parentIdx: i,
561
+ parentIdx,
565
562
  itemIndices: allItems,
566
563
  dominantSignature: dominantSig,
567
564
  fuzzyCount
@@ -572,6 +569,13 @@ function semanticDomAlgorithm(options) {
572
569
  addDescendantsToSet(ci, collectionMemberSet);
573
570
  }
574
571
  }
572
+ __name(tryDetectCollection, "tryDetectCollection");
573
+ for (let i = 0; i < nodes.length; i++) {
574
+ const node = nodes[i];
575
+ if (node.children.length < 3) continue;
576
+ if (collectionMemberSet.has(i)) continue;
577
+ tryDetectCollection(i, node.children);
578
+ }
575
579
  for (let i = 0; i < nodes.length; i++) {
576
580
  const node = nodes[i];
577
581
  if (node.children.length < 3) continue;
@@ -587,53 +591,7 @@ function semanticDomAlgorithm(options) {
587
591
  grandchildIndices.push(child.children[0]);
588
592
  }
589
593
  if (!valid || grandchildIndices.length < 3) continue;
590
- const sigGroups = /* @__PURE__ */ new Map();
591
- for (const gi of grandchildIndices) {
592
- const sig = nodes[gi].depth2Signature || "";
593
- if (!sigGroups.has(sig)) sigGroups.set(sig, []);
594
- sigGroups.get(sig).push(gi);
595
- }
596
- let dominantSig = "";
597
- let dominantGroup = [];
598
- for (const [sig, group] of sigGroups) {
599
- if (group.length > dominantGroup.length) {
600
- dominantSig = sig;
601
- dominantGroup = group;
602
- }
603
- }
604
- if (dominantGroup.length < 3) continue;
605
- const dominantTag = nodes[dominantGroup[0]].tag;
606
- const dominantChildTags = nodes[dominantGroup[0]].children.map((ci) => nodes[ci].tag);
607
- let fuzzyCount = 0;
608
- const allItems = [...dominantGroup];
609
- for (const [sig, group] of sigGroups) {
610
- if (sig === dominantSig) continue;
611
- for (const gi of group) {
612
- const candidate = nodes[gi];
613
- if (candidate.tag !== dominantTag) continue;
614
- const candidateChildTags = candidate.children.map((ci) => nodes[ci].tag);
615
- const jaccard = jaccardMultiset(dominantChildTags, candidateChildTags);
616
- if (jaccard < 0.7) continue;
617
- const iOverlap = interactiveOverlap(nodes[dominantGroup[0]].children, candidate.children);
618
- if (iOverlap < 0.5) continue;
619
- allItems.push(gi);
620
- fuzzyCount++;
621
- }
622
- }
623
- if (allItems.length < 3) continue;
624
- const avgInteractive = allItems.reduce((sum, ci) => sum + countInteractiveInSubtree(ci), 0) / allItems.length;
625
- if (avgInteractive < 3) continue;
626
- detectedCollections.push({
627
- parentIdx: i,
628
- itemIndices: allItems,
629
- dominantSignature: dominantSig,
630
- fuzzyCount
631
- });
632
- totalFuzzyMatched += fuzzyCount;
633
- for (const ci of allItems) {
634
- collectionMemberSet.add(ci);
635
- addDescendantsToSet(ci, collectionMemberSet);
636
- }
594
+ tryDetectCollection(i, grandchildIndices);
637
595
  }
638
596
  const LANDMARK_SELECTOR = 'header, nav, main, aside, footer, form, section[aria-label], section[aria-labelledby], article, dialog, [role="banner"], [role="navigation"], [role="main"], [role="contentinfo"], [role="search"], [role="region"], [role="complementary"], [role="dialog"], [role="alertdialog"]';
639
597
  function landmarkKind(landmark) {
@@ -1229,87 +1187,71 @@ function semanticDomAlgorithm(options) {
1229
1187
  return action;
1230
1188
  }
1231
1189
  __name(nodeToAction, "nodeToAction");
1232
- function groupLinksByHrefPattern(linkNodes) {
1233
- const pathGroups = /* @__PURE__ */ new Map();
1190
+ function groupNodes(nodeList, min, keyOf) {
1191
+ const buckets = /* @__PURE__ */ new Map();
1234
1192
  const ungrouped = [];
1235
- for (const node of linkNodes) {
1236
- if (!node.href) {
1237
- ungrouped.push(node);
1238
- continue;
1239
- }
1240
- const segments = node.href.split("/").filter(Boolean);
1241
- const patternKey = segments.length >= 1 ? `/${segments[0]}/` : null;
1242
- if (patternKey) {
1243
- if (!pathGroups.has(patternKey)) pathGroups.set(patternKey, []);
1244
- pathGroups.get(patternKey).push(node);
1193
+ for (const node of nodeList) {
1194
+ const key = keyOf(node);
1195
+ if (key) {
1196
+ if (!buckets.has(key)) buckets.set(key, []);
1197
+ buckets.get(key).push(node);
1245
1198
  } else {
1246
1199
  ungrouped.push(node);
1247
1200
  }
1248
1201
  }
1249
- const grouped = /* @__PURE__ */ new Map();
1250
- for (const [key, ns] of pathGroups) {
1251
- if (ns.length >= 3) {
1252
- grouped.set(key, ns);
1202
+ const groups = /* @__PURE__ */ new Map();
1203
+ for (const [key, ns] of buckets) {
1204
+ if (ns.length >= min) {
1205
+ groups.set(key, ns);
1253
1206
  } else {
1254
1207
  ungrouped.push(...ns);
1255
1208
  }
1256
1209
  }
1257
- return { grouped, ungrouped };
1210
+ return { groups, ungrouped };
1211
+ }
1212
+ __name(groupNodes, "groupNodes");
1213
+ function groupLinksByHrefPattern(linkNodes) {
1214
+ const { groups, ungrouped } = groupNodes(linkNodes, 3, (node) => {
1215
+ if (!node.href) return null;
1216
+ const segments = node.href.split("/").filter(Boolean);
1217
+ return segments.length >= 1 ? `/${segments[0]}/` : null;
1218
+ });
1219
+ return { grouped: groups, ungrouped };
1258
1220
  }
1259
1221
  __name(groupLinksByHrefPattern, "groupLinksByHrefPattern");
1260
1222
  function groupByTestId(nodeList) {
1261
- const groups = /* @__PURE__ */ new Map();
1262
- const ungrouped = [];
1263
- for (const node of nodeList) {
1264
- const key = node.testId ? `testid:${node.testId}` : node.qa ? `qa:${node.qa}` : null;
1265
- if (key) {
1266
- if (!groups.has(key)) groups.set(key, []);
1267
- groups.get(key).push(node);
1268
- } else {
1269
- ungrouped.push(node);
1270
- }
1271
- }
1272
- const validGroups = /* @__PURE__ */ new Map();
1273
- for (const [key, ns] of groups) {
1274
- if (ns.length >= 3) {
1275
- validGroups.set(key, ns);
1276
- } else {
1277
- ungrouped.push(...ns);
1278
- }
1279
- }
1280
- return { groups: validGroups, ungrouped };
1223
+ return groupNodes(
1224
+ nodeList,
1225
+ 3,
1226
+ (node) => node.testId ? `testid:${node.testId}` : node.qa ? `qa:${node.qa}` : null
1227
+ );
1281
1228
  }
1282
1229
  __name(groupByTestId, "groupByTestId");
1283
1230
  function groupByTagWithType(nodeList) {
1284
- const groups = /* @__PURE__ */ new Map();
1285
- const ungrouped = [];
1286
- for (const node of nodeList) {
1287
- if (node.tag === "input") {
1288
- const key = `input:${node.type || "text"}`;
1289
- if (!groups.has(key)) groups.set(key, []);
1290
- groups.get(key).push(node);
1291
- } else if (node.tag.includes("-")) {
1292
- if (!groups.has(node.tag)) groups.set(node.tag, []);
1293
- groups.get(node.tag).push(node);
1294
- } else {
1295
- ungrouped.push(node);
1296
- }
1297
- }
1298
- const validGroups = /* @__PURE__ */ new Map();
1299
- for (const [key, ns] of groups) {
1300
- if (ns.length >= 5) {
1301
- validGroups.set(key, ns);
1302
- } else {
1303
- ungrouped.push(...ns);
1304
- }
1305
- }
1306
- return { groups: validGroups, ungrouped };
1231
+ return groupNodes(nodeList, 5, (node) => {
1232
+ if (node.tag === "input") return `input:${node.type || "text"}`;
1233
+ if (node.tag.includes("-")) return node.tag;
1234
+ return null;
1235
+ });
1307
1236
  }
1308
1237
  __name(groupByTagWithType, "groupByTagWithType");
1309
1238
  const TIER2_CAP_PER_ZONE = 15;
1310
1239
  const regions = {};
1311
1240
  const textLines = [];
1312
1241
  for (const zoneName of allZoneNames) {
1242
+ let pushTagGroupPatterns2 = function(tagGroups) {
1243
+ for (const [tagKey, ns] of tagGroups) {
1244
+ const displayName = tagKey.includes(":") ? `${tagKey.split(":")[0]}[type=${tagKey.split(":")[1]}]` : tagKey;
1245
+ patterns.push({
1246
+ pattern: displayName,
1247
+ selectorPattern: tagKey.includes("-") ? tagKey : void 0,
1248
+ count: ns.length,
1249
+ samples: pickPatternSamples(ns, ns.length)
1250
+ });
1251
+ }
1252
+ };
1253
+ var pushTagGroupPatterns = pushTagGroupPatterns2;
1254
+ __name(pushTagGroupPatterns2, "pushTagGroupPatterns");
1313
1255
  const zoneNodes = zoneElements.get(zoneName) || [];
1314
1256
  const zoneCollIndices = zoneCollections.get(zoneName) || [];
1315
1257
  const meta = zoneMeta.get(zoneName) || { kind: "unknown" };
@@ -1433,15 +1375,7 @@ function semanticDomAlgorithm(options) {
1433
1375
  });
1434
1376
  }
1435
1377
  const { groups: t2TagGroups, ungrouped: t2Ungrouped } = groupByTagWithType(tier2Nodes);
1436
- for (const [tagKey, ns] of t2TagGroups) {
1437
- const displayName = tagKey.includes(":") ? `${tagKey.split(":")[0]}[type=${tagKey.split(":")[1]}]` : tagKey;
1438
- patterns.push({
1439
- pattern: displayName,
1440
- selectorPattern: tagKey.includes("-") ? tagKey : void 0,
1441
- count: ns.length,
1442
- samples: pickPatternSamples(ns, ns.length)
1443
- });
1444
- }
1378
+ pushTagGroupPatterns2(t2TagGroups);
1445
1379
  const cappedTier2 = t2Ungrouped.slice(0, TIER2_CAP_PER_ZONE).map(nodeToAction);
1446
1380
  const tier2Overflow = Math.max(0, t2Ungrouped.length - TIER2_CAP_PER_ZONE);
1447
1381
  const { groups: testidGroups, ungrouped: noTestidTier3 } = groupByTestId(tier3);
@@ -1457,15 +1391,7 @@ function semanticDomAlgorithm(options) {
1457
1391
  });
1458
1392
  }
1459
1393
  const { groups: tier3TagGroups, ungrouped: ungroupedTier3 } = groupByTagWithType(noTestidTier3);
1460
- for (const [tagKey, ns] of tier3TagGroups) {
1461
- const displayName = tagKey.includes(":") ? `${tagKey.split(":")[0]}[type=${tagKey.split(":")[1]}]` : tagKey;
1462
- patterns.push({
1463
- pattern: displayName,
1464
- selectorPattern: tagKey.includes("-") ? tagKey : void 0,
1465
- count: ns.length,
1466
- samples: pickPatternSamples(ns, ns.length)
1467
- });
1468
- }
1394
+ pushTagGroupPatterns2(tier3TagGroups);
1469
1395
  const otherCount = ungroupedTier3.length + tier2Overflow;
1470
1396
  const actions = [...tier1, ...cappedTier2];
1471
1397
  const region = { kind, total: totalInZone };
@@ -1643,6 +1569,9 @@ function semanticDomAlgorithm(options) {
1643
1569
  }
1644
1570
  __name(semanticDomAlgorithm, "semanticDomAlgorithm");
1645
1571
 
1572
+ // src/inspection/outline-capture.ts
1573
+ import { composeInjectedScript } from "@unotest/grounder-client/dom-walker";
1574
+
1646
1575
  // src/util/debug-log.ts
1647
1576
  import {
1648
1577
  appendFileSync as fsAppend,
@@ -1661,25 +1590,7 @@ var debugStore = new AsyncLocalStorage();
1661
1590
 
1662
1591
  // src/inspection/outline-capture.ts
1663
1592
  import { outlineAlgorithm as outlineAlgorithmInjectable } from "@unotest/web/internal/page-inject";
1664
- function outlineAlgorithm(assignRefs) {
1665
- const INTERACTIVE_ARIA_ROLES = /* @__PURE__ */ new Set([
1666
- "button",
1667
- "link",
1668
- "checkbox",
1669
- "radio",
1670
- "menuitem",
1671
- "menuitemcheckbox",
1672
- "menuitemradio",
1673
- "tab",
1674
- "option",
1675
- "switch",
1676
- "combobox",
1677
- "listbox",
1678
- "slider",
1679
- "spinbutton",
1680
- "searchbox",
1681
- "textbox"
1682
- ]);
1593
+ function outlineAlgorithm(h, assignRefs) {
1683
1594
  const LANDMARK_TAGS = {
1684
1595
  header: "banner",
1685
1596
  nav: "navigation",
@@ -1706,65 +1617,11 @@ function outlineAlgorithm(assignRefs) {
1706
1617
  "article"
1707
1618
  ]);
1708
1619
  const GENERIC_ROLES = /* @__PURE__ */ new Set(["generic", "none", "presentation"]);
1709
- function* deepWalk(root) {
1710
- const children = root.children;
1711
- for (let i = 0; i < children.length; i++) {
1712
- const el = children[i];
1713
- yield el;
1714
- const sr = el.shadowRoot;
1715
- if (sr) yield* deepWalk(sr);
1716
- yield* deepWalk(el);
1717
- }
1718
- }
1719
- __name(deepWalk, "deepWalk");
1720
- function parentOrHost(node) {
1721
- const parent = node.parentNode;
1722
- if (!parent) return null;
1723
- if (parent instanceof ShadowRoot) return parent.host;
1724
- return parent instanceof Element ? parent : null;
1725
- }
1726
- __name(parentOrHost, "parentOrHost");
1727
- function rootForScope(el) {
1728
- const root = el.getRootNode();
1729
- return root instanceof ShadowRoot ? root : document;
1730
- }
1731
- __name(rootForScope, "rootForScope");
1732
- function getElementByIdInScope(el, id) {
1733
- return rootForScope(el).getElementById(id);
1734
- }
1735
- __name(getElementByIdInScope, "getElementByIdInScope");
1736
- function isVisible(el) {
1737
- if (!el.getClientRects || el.getClientRects().length === 0) return false;
1738
- const cs = window.getComputedStyle(el);
1739
- if (cs.display === "none" || cs.visibility === "hidden") return false;
1740
- if (Number.parseFloat(cs.opacity || "1") < 0.01) return false;
1741
- for (let p = el; p; p = parentOrHost(p)) {
1742
- if (p.getAttribute && p.getAttribute("aria-hidden") === "true") return false;
1743
- }
1744
- const r = el.getBoundingClientRect();
1745
- if (r.width === 0 && r.height === 0) return false;
1746
- return true;
1747
- }
1748
- __name(isVisible, "isVisible");
1749
1620
  function implicitRole(el) {
1750
1621
  const tag = el.tagName.toLowerCase();
1751
1622
  if (tag === "a" && el.hasAttribute("href")) return "link";
1752
1623
  if (tag === "button") return "button";
1753
- if (tag === "textarea") return "textbox";
1754
- if (tag === "select") {
1755
- return el.multiple || el.size > 1 ? "listbox" : "combobox";
1756
- }
1757
- if (tag === "input") {
1758
- const t = (el.type || "text").toLowerCase();
1759
- if (t === "button" || t === "submit" || t === "reset" || t === "image") return "button";
1760
- if (t === "checkbox") return "checkbox";
1761
- if (t === "radio") return "radio";
1762
- if (t === "range") return "slider";
1763
- if (t === "number") return "spinbutton";
1764
- if (t === "search") return "searchbox";
1765
- if (t === "hidden") return null;
1766
- return "textbox";
1767
- }
1624
+ if (tag === "textarea" || tag === "select" || tag === "input") return formFieldRole(el, tag);
1768
1625
  if (tag === "h1" || tag === "h2" || tag === "h3" || tag === "h4" || tag === "h5" || tag === "h6") {
1769
1626
  return "heading";
1770
1627
  }
@@ -1784,111 +1641,32 @@ function outlineAlgorithm(assignRefs) {
1784
1641
  }
1785
1642
  __name(computeRole, "computeRole");
1786
1643
  function isInteractive(el) {
1787
- const tag = el.tagName.toLowerCase();
1788
- if (tag === "iframe") return true;
1789
- const role = computeRole(el);
1790
- if (role && INTERACTIVE_ARIA_ROLES.has(role)) return true;
1791
- if (el.hasAttribute("onclick")) return true;
1792
- const tiRaw = el.getAttribute("tabindex");
1793
- const ti = tiRaw === null ? null : Number.parseInt(tiRaw, 10);
1794
- if (ti !== null && ti >= 0) return true;
1795
- if (tag === "div" || tag === "span") {
1796
- }
1797
- return false;
1644
+ return h.isInteractive(el, computeRole(el));
1798
1645
  }
1799
1646
  __name(isInteractive, "isInteractive");
1800
- const MAX_NAME = 80;
1801
- function trim(s) {
1802
- return (s ?? "").replace(/\s+/g, " ").trim();
1803
- }
1804
- __name(trim, "trim");
1805
- function cap(s) {
1806
- return s.length > MAX_NAME ? s.slice(0, MAX_NAME - 1) + "\u2026" : s;
1807
- }
1808
- __name(cap, "cap");
1647
+ const {
1648
+ trim,
1649
+ cap,
1650
+ textContentVisible,
1651
+ parentOrHost,
1652
+ deepWalk,
1653
+ isVisible,
1654
+ stableDataAttr,
1655
+ clickAffordance,
1656
+ testIdOf,
1657
+ formFieldRole,
1658
+ nameFromLabelledby,
1659
+ svgAccname,
1660
+ makeAccname,
1661
+ ariaStateFlags,
1662
+ headingLevel
1663
+ } = h;
1809
1664
  function svgIconName(host, seen) {
1810
1665
  const svg = host.querySelector("svg");
1811
- if (!svg) return "";
1812
- const labelledby = svg.getAttribute("aria-labelledby");
1813
- if (labelledby) {
1814
- const parts = [];
1815
- for (const id of labelledby.split(/\s+/)) {
1816
- if (!id) continue;
1817
- const ref = getElementByIdInScope(svg, id);
1818
- if (ref && !seen.has(ref)) {
1819
- const n = accname(ref, seen);
1820
- if (n) parts.push(n);
1821
- }
1822
- }
1823
- const joined = trim(parts.join(" "));
1824
- if (joined) return cap(joined);
1825
- }
1826
- const ariaLabel = trim(svg.getAttribute("aria-label"));
1827
- if (ariaLabel) return cap(ariaLabel);
1828
- for (const child of Array.from(svg.children)) {
1829
- if (child.tagName.toLowerCase() === "title") {
1830
- const t = trim(child.textContent);
1831
- if (t) return cap(t);
1832
- break;
1833
- }
1834
- }
1835
- return "";
1666
+ return svg ? svgAccname(svg, seen, accname) : "";
1836
1667
  }
1837
1668
  __name(svgIconName, "svgIconName");
1838
- function accname(el, visited) {
1839
- const seen = visited ?? /* @__PURE__ */ new Set();
1840
- if (seen.has(el)) return "";
1841
- seen.add(el);
1842
- const labelledby = el.getAttribute("aria-labelledby");
1843
- if (labelledby) {
1844
- const parts = [];
1845
- for (const id of labelledby.split(/\s+/)) {
1846
- if (!id) continue;
1847
- const ref = getElementByIdInScope(el, id);
1848
- if (ref && !seen.has(ref)) {
1849
- const n = accname(ref, seen);
1850
- if (n) parts.push(n);
1851
- }
1852
- }
1853
- const joined = trim(parts.join(" "));
1854
- if (joined) return cap(joined);
1855
- }
1856
- const ariaLabel = trim(el.getAttribute("aria-label"));
1857
- if (ariaLabel) return cap(ariaLabel);
1858
- const tag = el.tagName.toLowerCase();
1859
- if (tag === "input" || tag === "textarea" || tag === "select") {
1860
- const id = el.id;
1861
- if (id) {
1862
- const lbl = rootForScope(el).querySelector(
1863
- `label[for="${cssEscape(id)}"]`
1864
- );
1865
- if (lbl) {
1866
- const n = trim(textContentVisible(lbl));
1867
- if (n) return cap(n);
1868
- }
1869
- }
1870
- let p = parentOrHost(el);
1871
- while (p) {
1872
- if (p.tagName.toLowerCase() === "label") {
1873
- const n = trim(textContentVisible(p));
1874
- if (n) return cap(n);
1875
- break;
1876
- }
1877
- p = parentOrHost(p);
1878
- }
1879
- if (tag === "input") {
1880
- const it = el.type?.toLowerCase();
1881
- if (it === "button" || it === "submit" || it === "reset") {
1882
- const v = trim(el.value);
1883
- if (v) return cap(v);
1884
- }
1885
- const ph = trim(el.placeholder);
1886
- if (ph) return cap(ph);
1887
- }
1888
- const t2 = trim(el.getAttribute("title"));
1889
- if (t2) return cap(t2);
1890
- return "";
1891
- }
1669
+ const accname = makeAccname((el, tag, seen) => {
1892
1670
  if (tag === "button" || tag === "a" || tag === "summary") {
1893
1671
  const txt = trim(textContentVisible(el));
1894
1672
  if (txt) return cap(txt);
@@ -1920,32 +1698,15 @@ function outlineAlgorithm(assignRefs) {
1920
1698
  if (inner) return cap(inner);
1921
1699
  const t = trim(el.getAttribute("title"));
1922
1700
  return cap(t);
1923
- }
1924
- __name(accname, "accname");
1701
+ });
1925
1702
  function srcBasename(src) {
1926
1703
  const noQuery = src.split(/[?#]/)[0] ?? "";
1927
1704
  const segments = noQuery.split("/").filter((s) => s.length > 0);
1928
1705
  return segments[segments.length - 1] ?? "";
1929
1706
  }
1930
1707
  __name(srcBasename, "srcBasename");
1931
- function textContentVisible(el) {
1932
- const ht = el;
1933
- if (typeof ht.innerText === "string") return ht.innerText;
1934
- return el.textContent ?? "";
1935
- }
1936
- __name(textContentVisible, "textContentVisible");
1937
- function cssEscape(s) {
1938
- return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
1939
- }
1940
- __name(cssEscape, "cssEscape");
1941
1708
  const MAX_HREF = 60;
1942
- const pageOrigin = (() => {
1943
- try {
1944
- return window.location.origin;
1945
- } catch {
1946
- return "";
1947
- }
1948
- })();
1709
+ const pageOrigin = h.pageOrigin;
1949
1710
  function shortenedHref(rawHref) {
1950
1711
  if (!rawHref) return "";
1951
1712
  const t = trim(rawHref);
@@ -1975,37 +1736,9 @@ function outlineAlgorithm(assignRefs) {
1975
1736
  if (formDisabled || el.getAttribute("aria-disabled") === "true") {
1976
1737
  state.disabled = true;
1977
1738
  }
1978
- const ariaChecked = el.getAttribute("aria-checked");
1979
- if (ariaChecked === "true") {
1980
- state.checked = true;
1981
- } else if (ariaChecked === "mixed") {
1982
- state.checked = "mixed";
1983
- } else if (tag === "input" && (el.type === "checkbox" || el.type === "radio") && el.checked) {
1984
- state.checked = true;
1985
- }
1986
- const ariaPressed = el.getAttribute("aria-pressed");
1987
- if (ariaPressed === "true") state.pressed = true;
1988
- else if (ariaPressed === "mixed") state.pressed = "mixed";
1989
- if (el.getAttribute("aria-expanded") === "true") state.expanded = true;
1990
- if (el.getAttribute("aria-selected") === "true") {
1991
- state.selected = true;
1992
- } else if (tag === "option" && el.selected) {
1993
- state.selected = true;
1994
- }
1995
- if (rootForScope(el).activeElement === el) {
1996
- state.active = true;
1997
- }
1998
- if (tag === "h1") state.level = 1;
1999
- else if (tag === "h2") state.level = 2;
2000
- else if (tag === "h3") state.level = 3;
2001
- else if (tag === "h4") state.level = 4;
2002
- else if (tag === "h5") state.level = 5;
2003
- else if (tag === "h6") state.level = 6;
2004
- const ariaLevel = el.getAttribute("aria-level");
2005
- if (ariaLevel) {
2006
- const n = Number.parseInt(ariaLevel, 10);
2007
- if (Number.isFinite(n) && n > 0) state.level = n;
2008
- }
1739
+ Object.assign(state, ariaStateFlags(el));
1740
+ const level = headingLevel(el);
1741
+ if (level !== void 0) state.level = level;
2009
1742
  return Object.keys(state).length > 0 ? state : void 0;
2010
1743
  }
2011
1744
  __name(collectState, "collectState");
@@ -2033,17 +1766,12 @@ function outlineAlgorithm(assignRefs) {
2033
1766
  function landmarkLabel(landmark) {
2034
1767
  const aria = trim(landmark.getAttribute("aria-label"));
2035
1768
  if (aria) return cap(aria);
2036
- const labelledby = landmark.getAttribute("aria-labelledby");
2037
- if (labelledby) {
2038
- const parts = [];
2039
- for (const id of labelledby.split(/\s+/)) {
2040
- if (!id) continue;
2041
- const ref = getElementByIdInScope(landmark, id);
2042
- if (ref) parts.push(trim(textContentVisible(ref)));
2043
- }
2044
- const joined = trim(parts.join(" "));
2045
- if (joined) return cap(joined);
2046
- }
1769
+ const fromLabelledby = nameFromLabelledby(
1770
+ landmark,
1771
+ /* @__PURE__ */ new Set(),
1772
+ (ref) => trim(textContentVisible(ref))
1773
+ );
1774
+ if (fromLabelledby) return fromLabelledby;
2047
1775
  const heading = landmark.querySelector("h1, h2, h3, h4, h5, h6");
2048
1776
  if (heading) {
2049
1777
  const txt = trim(textContentVisible(heading));
@@ -2052,33 +1780,6 @@ function outlineAlgorithm(assignRefs) {
2052
1780
  return "";
2053
1781
  }
2054
1782
  __name(landmarkLabel, "landmarkLabel");
2055
- function testIdOf(el) {
2056
- const t = el.getAttribute("data-testid");
2057
- if (t) return t;
2058
- const qa = el.getAttribute("data-qa");
2059
- if (qa) return qa;
2060
- return void 0;
2061
- }
2062
- __name(testIdOf, "testIdOf");
2063
- function idStable(id) {
2064
- if (!id || id.length > 40) return false;
2065
- if (id.startsWith(":") || id.startsWith("__")) return false;
2066
- const digits = id.replace(/\D/g, "").length;
2067
- return !(id.length > 8 && digits * 2 > id.length);
2068
- }
2069
- __name(idStable, "idStable");
2070
- function stableDataAttr(el) {
2071
- const skip = /^data-(unotest|v-|react|ng-|svelte|qa|testid|sort|shadow|tooltip|toggle|target|bs-|original-title)/;
2072
- const idLike = /(^|[-_])(id|guid|uid|key|code|ref|num|no)$/;
2073
- for (const a of Array.from(el.attributes)) {
2074
- if (!a.name.startsWith("data-") || skip.test(a.name) || !idLike.test(a.name)) continue;
2075
- const v = (a.value ?? "").trim();
2076
- if (!v || v.length > 64) continue;
2077
- if (/^\d+$/.test(v) || idStable(v)) return { name: a.name, value: v };
2078
- }
2079
- return void 0;
2080
- }
2081
- __name(stableDataAttr, "stableDataAttr");
2082
1783
  function tooltipLabel(el) {
2083
1784
  for (const a of Array.from(el.attributes)) {
2084
1785
  if (!a.name.startsWith("data-") || a.name.startsWith("data-unotest")) continue;
@@ -2095,19 +1796,6 @@ function outlineAlgorithm(assignRefs) {
2095
1796
  return t.length > 120 ? t.slice(0, 119) + "\u2026" : t;
2096
1797
  }
2097
1798
  __name(tooltipText, "tooltipText");
2098
- function clickAffordance(el) {
2099
- if (el.hasAttribute("onclick")) return true;
2100
- const cls = el.getAttribute("class") ?? "";
2101
- if (/(^|\s)hover:/.test(cls) || /\bcursor-pointer\b/.test(cls) || /\bclickable\b/.test(cls)) {
2102
- return true;
2103
- }
2104
- try {
2105
- return window.getComputedStyle(el).cursor === "pointer";
2106
- } catch {
2107
- return false;
2108
- }
2109
- }
2110
- __name(clickAffordance, "clickAffordance");
2111
1799
  const viewport = { width: window.innerWidth, height: window.innerHeight };
2112
1800
  function classifyViewport(b) {
2113
1801
  if (viewport.width <= 0 || viewport.height <= 0) return void 0;
@@ -2168,18 +1856,11 @@ function outlineAlgorithm(assignRefs) {
2168
1856
  clickableEls.add(el);
2169
1857
  }
2170
1858
  }
2171
- const refRegex = /^e\d+$/;
2172
1859
  const previouslyRefed = /* @__PURE__ */ new Map();
2173
1860
  for (const el of deepWalk(document)) {
2174
1861
  const v = el.getAttribute("data-unotest-ref");
2175
- if (v && refRegex.test(v)) previouslyRefed.set(el, v);
2176
- }
2177
- const html = document.documentElement;
2178
- let refCounter = Number.parseInt(
2179
- html.getAttribute("data-unotest-ref-counter") || "0",
2180
- 10
2181
- );
2182
- if (!Number.isFinite(refCounter) || refCounter < 0) refCounter = 0;
1862
+ if (v && h.refRegex.test(v)) previouslyRefed.set(el, v);
1863
+ }
2183
1864
  const refByElement = /* @__PURE__ */ new Map();
2184
1865
  if (!assignRefs) {
2185
1866
  for (const el of interactiveElements) {
@@ -2189,19 +1870,9 @@ function outlineAlgorithm(assignRefs) {
2189
1870
  } else {
2190
1871
  const kept = /* @__PURE__ */ new Set();
2191
1872
  for (const el of interactiveElements) {
2192
- const existing = previouslyRefed.get(el);
2193
- if (existing) {
2194
- refByElement.set(el, existing);
2195
- el.setAttribute("data-unotest-ref-kind", "i");
2196
- kept.add(el);
2197
- } else {
2198
- refCounter += 1;
2199
- const ref = "e" + refCounter;
2200
- el.setAttribute("data-unotest-ref", ref);
2201
- el.setAttribute("data-unotest-ref-kind", "i");
2202
- refByElement.set(el, ref);
2203
- kept.add(el);
2204
- }
1873
+ refByElement.set(el, h.ensureRef(el, "i"));
1874
+ el.setAttribute("data-unotest-ref-kind", "i");
1875
+ kept.add(el);
2205
1876
  }
2206
1877
  for (const [el] of previouslyRefed) {
2207
1878
  if (kept.has(el)) continue;
@@ -2211,7 +1882,7 @@ function outlineAlgorithm(assignRefs) {
2211
1882
  el.removeAttribute("data-unotest-ref-role");
2212
1883
  el.removeAttribute("data-unotest-ref-name");
2213
1884
  }
2214
- html.setAttribute("data-unotest-ref-counter", String(refCounter));
1885
+ h.persistRefCounter();
2215
1886
  }
2216
1887
  const MAX_TEXT = 80;
2217
1888
  const entries = [];
@@ -2226,8 +1897,8 @@ function outlineAlgorithm(assignRefs) {
2226
1897
  if (!name) name = tooltipLabel(el) ?? "";
2227
1898
  let href;
2228
1899
  if (!name && tag === "a") {
2229
- const h = shortenedHref(el.getAttribute("href"));
2230
- if (h) href = h;
1900
+ const h2 = shortenedHref(el.getAttribute("href"));
1901
+ if (h2) href = h2;
2231
1902
  }
2232
1903
  const role = computeRole(el) ?? (tag === "tr" ? "row" : tag === "li" ? "listitem" : null);
2233
1904
  if (assignRefs && refByElement.has(el)) {
@@ -2314,31 +1985,22 @@ function outlineAlgorithm(assignRefs) {
2314
1985
  runRows.push({ tr: nextTr, start: scan, end: nextEnd, sig: nextSig });
2315
1986
  scan = nextEnd;
2316
1987
  }
2317
- if (runRows.length >= ROW_COLLAPSE_MIN) {
2318
- for (let r = 0; r < ROW_KEEP_HEAD; r++) {
2319
- const g = runRows[r];
2320
- for (let k = g.start; k < g.end; k++) {
2321
- out.push({ kind: "entry", entry: entries[k] });
2322
- }
1988
+ const pushRow = /* @__PURE__ */ __name((g) => {
1989
+ for (let k = g.start; k < g.end; k++) {
1990
+ out.push({ kind: "entry", entry: entries[k] });
2323
1991
  }
1992
+ }, "pushRow");
1993
+ if (runRows.length >= ROW_COLLAPSE_MIN) {
1994
+ for (let r = 0; r < ROW_KEEP_HEAD; r++) pushRow(runRows[r]);
2324
1995
  const omitted = runRows.length - ROW_KEEP_HEAD - ROW_KEEP_TAIL;
2325
1996
  out.push({
2326
1997
  kind: "summary",
2327
1998
  text: `\u2026 ${omitted} similar rows \u2026`,
2328
1999
  landmark: entries[runRows[0].start].landmark
2329
2000
  });
2330
- for (let r = runRows.length - ROW_KEEP_TAIL; r < runRows.length; r++) {
2331
- const g = runRows[r];
2332
- for (let k = g.start; k < g.end; k++) {
2333
- out.push({ kind: "entry", entry: entries[k] });
2334
- }
2335
- }
2001
+ for (let r = runRows.length - ROW_KEEP_TAIL; r < runRows.length; r++) pushRow(runRows[r]);
2336
2002
  } else {
2337
- for (const g of runRows) {
2338
- for (let k = g.start; k < g.end; k++) {
2339
- out.push({ kind: "entry", entry: entries[k] });
2340
- }
2341
- }
2003
+ for (const g of runRows) pushRow(g);
2342
2004
  }
2343
2005
  i = scan;
2344
2006
  }
@@ -2405,66 +2067,9 @@ function outlineAlgorithm(assignRefs) {
2405
2067
  __name(outlineAlgorithm, "outlineAlgorithm");
2406
2068
 
2407
2069
  // src/inspection/aria-snapshot-capture.ts
2070
+ import { composeInjectedScript as composeInjectedScript2 } from "@unotest/grounder-client/dom-walker";
2408
2071
  import { ariaSnapshotAlgorithm as ariaSnapshotAlgorithmInjectable } from "@unotest/web/internal/page-inject";
2409
- function ariaSnapshotAlgorithm() {
2410
- function* deepWalk(root) {
2411
- const children = root.children;
2412
- for (let i = 0; i < children.length; i++) {
2413
- const el = children[i];
2414
- yield el;
2415
- const sr = el.shadowRoot;
2416
- if (sr) yield* deepWalk(sr);
2417
- yield* deepWalk(el);
2418
- }
2419
- }
2420
- __name(deepWalk, "deepWalk");
2421
- function parentOrHost(node) {
2422
- const parent = node.parentNode;
2423
- if (!parent) return null;
2424
- if (parent instanceof ShadowRoot) return parent.host;
2425
- return parent instanceof Element ? parent : null;
2426
- }
2427
- __name(parentOrHost, "parentOrHost");
2428
- function rootForScope(el) {
2429
- const root = el.getRootNode();
2430
- return root instanceof ShadowRoot ? root : document;
2431
- }
2432
- __name(rootForScope, "rootForScope");
2433
- function getElementByIdInScope(el, id) {
2434
- return rootForScope(el).getElementById(id);
2435
- }
2436
- __name(getElementByIdInScope, "getElementByIdInScope");
2437
- function isVisible(el) {
2438
- if (!el.getClientRects || el.getClientRects().length === 0) return false;
2439
- const cs = window.getComputedStyle(el);
2440
- if (cs.display === "none" || cs.visibility === "hidden") return false;
2441
- if (Number.parseFloat(cs.opacity || "1") < 0.01) return false;
2442
- for (let p = el; p; p = parentOrHost(p)) {
2443
- if (p.getAttribute && p.getAttribute("aria-hidden") === "true") return false;
2444
- }
2445
- const r = el.getBoundingClientRect();
2446
- if (r.width === 0 && r.height === 0) return false;
2447
- return true;
2448
- }
2449
- __name(isVisible, "isVisible");
2450
- const INTERACTIVE_ROLES = /* @__PURE__ */ new Set([
2451
- "button",
2452
- "link",
2453
- "checkbox",
2454
- "radio",
2455
- "menuitem",
2456
- "menuitemcheckbox",
2457
- "menuitemradio",
2458
- "tab",
2459
- "option",
2460
- "switch",
2461
- "combobox",
2462
- "listbox",
2463
- "slider",
2464
- "spinbutton",
2465
- "searchbox",
2466
- "textbox"
2467
- ]);
2072
+ function ariaSnapshotAlgorithm(h) {
2468
2073
  const NAMED_STRUCTURAL_ROLES = /* @__PURE__ */ new Set([
2469
2074
  "heading",
2470
2075
  "list",
@@ -2495,337 +2100,12 @@ function ariaSnapshotAlgorithm() {
2495
2100
  "alertdialog",
2496
2101
  "article"
2497
2102
  ]);
2498
- function implicitRole(el) {
2499
- const tag = el.tagName.toLowerCase();
2500
- if (tag === "a") {
2501
- return el.hasAttribute("href") ? "link" : null;
2502
- }
2503
- if (tag === "button") return "button";
2504
- if (tag === "summary") return "button";
2505
- if (tag === "textarea") return "textbox";
2506
- if (tag === "select") {
2507
- const s = el;
2508
- return s.multiple || s.size > 1 ? "listbox" : "combobox";
2509
- }
2510
- if (tag === "input") {
2511
- const t = (el.type || "text").toLowerCase();
2512
- if (t === "button" || t === "submit" || t === "reset" || t === "image") return "button";
2513
- if (t === "checkbox") return "checkbox";
2514
- if (t === "radio") return "radio";
2515
- if (t === "range") return "slider";
2516
- if (t === "number") return "spinbutton";
2517
- if (t === "search") return "searchbox";
2518
- if (t === "hidden") return null;
2519
- return "textbox";
2520
- }
2521
- if (tag === "h1" || tag === "h2" || tag === "h3" || tag === "h4" || tag === "h5" || tag === "h6") {
2522
- return "heading";
2523
- }
2524
- if (tag === "nav") return "navigation";
2525
- if (tag === "header") return "banner";
2526
- if (tag === "main") return "main";
2527
- if (tag === "aside") return "complementary";
2528
- if (tag === "footer") return "contentinfo";
2529
- if (tag === "form") return "form";
2530
- if (tag === "article") return "article";
2531
- if (tag === "dialog") return "dialog";
2532
- if (tag === "section") {
2533
- if (el.hasAttribute("aria-label") || el.hasAttribute("aria-labelledby")) {
2534
- return "region";
2535
- }
2536
- return null;
2537
- }
2538
- if (tag === "ul" || tag === "ol" || tag === "menu") return "list";
2539
- if (tag === "li") return "listitem";
2540
- if (tag === "table") return "table";
2541
- if (tag === "tr") return "row";
2542
- if (tag === "td") return "cell";
2543
- if (tag === "th") {
2544
- const scope = (el.getAttribute("scope") || "").toLowerCase();
2545
- return scope === "row" ? "rowheader" : "columnheader";
2546
- }
2547
- if (tag === "p") return "paragraph";
2548
- if (tag === "figure") return "figure";
2549
- if (tag === "figcaption") return "caption";
2550
- if (tag === "img") {
2551
- const alt = el.getAttribute("alt");
2552
- if (alt === "") return null;
2553
- return "img";
2554
- }
2555
- if (tag === "code") return "code";
2556
- if (tag === "pre") return "code";
2557
- if (tag === "blockquote") return "blockquote";
2558
- if (tag === "hr") return "separator";
2559
- if (tag === "details") return "group";
2560
- if (tag === "fieldset") return "group";
2561
- if (tag === "iframe") return "iframe";
2562
- return null;
2563
- }
2564
- __name(implicitRole, "implicitRole");
2565
- function computeRole(el) {
2566
- const explicit = el.getAttribute("role");
2567
- if (explicit) return explicit.split(/\s+/)[0].toLowerCase();
2568
- return implicitRole(el);
2569
- }
2570
- __name(computeRole, "computeRole");
2571
- const MAX_NAME = 80;
2572
- function trim(s) {
2573
- return (s ?? "").replace(/\s+/g, " ").trim();
2574
- }
2575
- __name(trim, "trim");
2576
- function cap(s) {
2577
- return s.length > MAX_NAME ? s.slice(0, MAX_NAME - 1) + "\u2026" : s;
2578
- }
2579
- __name(cap, "cap");
2580
- function cssEscape(s) {
2581
- return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
2582
- }
2583
- __name(cssEscape, "cssEscape");
2584
- function textContentVisible(el) {
2585
- const ht = el;
2586
- if (typeof ht.innerText === "string") return ht.innerText;
2587
- return el.textContent ?? "";
2588
- }
2589
- __name(textContentVisible, "textContentVisible");
2590
- function composedAccname(el, seen) {
2591
- const parts = [];
2592
- for (const child of Array.from(el.childNodes)) {
2593
- if (child.nodeType === 3) {
2594
- const t = trim(child.nodeValue);
2595
- if (t) parts.push(t);
2596
- continue;
2597
- }
2598
- if (child.nodeType !== 1) continue;
2599
- const ce = child;
2600
- if (!isVisible(ce)) continue;
2601
- if (seen.has(ce)) continue;
2602
- seen.add(ce);
2603
- const tag = ce.tagName.toLowerCase();
2604
- if (tag === "img") {
2605
- const alt = trim(ce.getAttribute("alt"));
2606
- if (alt) parts.push(alt);
2607
- continue;
2608
- }
2609
- if (tag === "svg") {
2610
- const sn = svgAccname(ce, seen);
2611
- if (sn) parts.push(sn);
2612
- continue;
2613
- }
2614
- const sub = composedAccname(ce, seen);
2615
- if (sub) parts.push(sub);
2616
- }
2617
- return trim(parts.join(" "));
2618
- }
2619
- __name(composedAccname, "composedAccname");
2620
- function svgAccname(svg, seen) {
2621
- const labelledby = svg.getAttribute("aria-labelledby");
2622
- if (labelledby) {
2623
- const parts = [];
2624
- for (const id of labelledby.split(/\s+/)) {
2625
- if (!id) continue;
2626
- const ref = getElementByIdInScope(svg, id);
2627
- if (ref && !seen.has(ref)) {
2628
- const n = accname(ref, seen);
2629
- if (n) parts.push(n);
2630
- }
2631
- }
2632
- const joined = trim(parts.join(" "));
2633
- if (joined) return cap(joined);
2634
- }
2635
- const ariaLabel = trim(svg.getAttribute("aria-label"));
2636
- if (ariaLabel) return cap(ariaLabel);
2637
- for (const child of Array.from(svg.children)) {
2638
- if (child.tagName.toLowerCase() === "title") {
2639
- const t = trim(child.textContent);
2640
- if (t) return cap(t);
2641
- break;
2642
- }
2643
- }
2644
- return "";
2645
- }
2646
- __name(svgAccname, "svgAccname");
2647
- function accname(el, visited) {
2648
- const seen = visited ?? /* @__PURE__ */ new Set();
2649
- if (seen.has(el)) return "";
2650
- seen.add(el);
2651
- const labelledby = el.getAttribute("aria-labelledby");
2652
- if (labelledby) {
2653
- const parts = [];
2654
- for (const id of labelledby.split(/\s+/)) {
2655
- if (!id) continue;
2656
- const ref = getElementByIdInScope(el, id);
2657
- if (ref && !seen.has(ref)) {
2658
- const n = accname(ref, seen);
2659
- if (n) parts.push(n);
2660
- }
2661
- }
2662
- const joined = trim(parts.join(" "));
2663
- if (joined) return cap(joined);
2664
- }
2665
- const ariaLabel = trim(el.getAttribute("aria-label"));
2666
- if (ariaLabel) return cap(ariaLabel);
2667
- const tag = el.tagName.toLowerCase();
2668
- if (tag === "input" || tag === "textarea" || tag === "select") {
2669
- const id = el.id;
2670
- if (id) {
2671
- const lbl = rootForScope(el).querySelector(
2672
- `label[for="${cssEscape(id)}"]`
2673
- );
2674
- if (lbl) {
2675
- const n = trim(textContentVisible(lbl));
2676
- if (n) return cap(n);
2677
- }
2678
- }
2679
- let p = parentOrHost(el);
2680
- while (p) {
2681
- if (p.tagName.toLowerCase() === "label") {
2682
- const n = trim(textContentVisible(p));
2683
- if (n) return cap(n);
2684
- break;
2685
- }
2686
- p = parentOrHost(p);
2687
- }
2688
- if (tag === "input") {
2689
- const it = el.type?.toLowerCase();
2690
- if (it === "button" || it === "submit" || it === "reset") {
2691
- const v = trim(el.value);
2692
- if (v) return cap(v);
2693
- }
2694
- const ph = trim(el.placeholder);
2695
- if (ph) return cap(ph);
2696
- }
2697
- const t2 = trim(el.getAttribute("title"));
2698
- if (t2) return cap(t2);
2699
- return "";
2700
- }
2701
- if (tag === "button" || tag === "a" || tag === "summary") {
2702
- const composed = composedAccname(el, seen);
2703
- if (composed) return cap(composed);
2704
- const t2 = trim(el.getAttribute("title"));
2705
- if (t2) return cap(t2);
2706
- return "";
2707
- }
2708
- if (tag === "h1" || tag === "h2" || tag === "h3" || tag === "h4" || tag === "h5" || tag === "h6") {
2709
- return cap(trim(textContentVisible(el)));
2710
- }
2711
- if (tag === "img") {
2712
- return cap(trim(el.getAttribute("alt")));
2713
- }
2714
- if (tag === "iframe") {
2715
- const t2 = trim(el.getAttribute("title"));
2716
- if (t2) return cap(t2);
2717
- return "";
2718
- }
2719
- if (tag === "figure") {
2720
- for (const child of Array.from(el.children)) {
2721
- if (child.tagName.toLowerCase() === "figcaption") {
2722
- const t2 = trim(textContentVisible(child));
2723
- if (t2) return cap(t2);
2724
- break;
2725
- }
2726
- }
2727
- return "";
2728
- }
2729
- if (tag === "fieldset") {
2730
- for (const child of Array.from(el.children)) {
2731
- if (child.tagName.toLowerCase() === "legend") {
2732
- const t2 = trim(textContentVisible(child));
2733
- if (t2) return cap(t2);
2734
- break;
2735
- }
2736
- }
2737
- return "";
2738
- }
2739
- if (tag === "form" || tag === "nav" || tag === "header" || tag === "main" || tag === "aside" || tag === "footer" || tag === "section" || tag === "article" || tag === "dialog") {
2740
- return "";
2741
- }
2742
- if (tag === "ul" || tag === "ol" || tag === "menu" || tag === "li" || tag === "table" || tag === "tr" || tag === "td" || tag === "th" || tag === "p" || tag === "code" || tag === "pre" || tag === "blockquote" || tag === "details") {
2743
- return "";
2744
- }
2745
- const inner = trim(textContentVisible(el));
2746
- if (inner) return cap(inner);
2747
- const t = trim(el.getAttribute("title"));
2748
- return cap(t);
2749
- }
2750
- __name(accname, "accname");
2751
- function collectState(el, role) {
2752
- const state = {};
2753
- const tag = el.tagName.toLowerCase();
2754
- if (el.getAttribute("aria-disabled") === "true" || el.disabled) {
2755
- state.disabled = true;
2756
- }
2757
- const ariaChecked = el.getAttribute("aria-checked");
2758
- if (ariaChecked === "true") state.checked = true;
2759
- else if (ariaChecked === "mixed") state.checked = "mixed";
2760
- else if (tag === "input" && (el.type === "checkbox" || el.type === "radio") && el.checked) {
2761
- state.checked = true;
2762
- }
2763
- const ariaPressed = el.getAttribute("aria-pressed");
2764
- if (ariaPressed === "true") state.pressed = true;
2765
- else if (ariaPressed === "mixed") state.pressed = "mixed";
2766
- if (el.getAttribute("aria-expanded") === "true") state.expanded = true;
2767
- if (el.getAttribute("aria-selected") === "true") state.selected = true;
2768
- else if (tag === "option" && el.selected) {
2769
- state.selected = true;
2770
- }
2771
- if (rootForScope(el).activeElement === el) state.active = true;
2772
- if (role === "heading") {
2773
- if (tag === "h1") state.level = 1;
2774
- else if (tag === "h2") state.level = 2;
2775
- else if (tag === "h3") state.level = 3;
2776
- else if (tag === "h4") state.level = 4;
2777
- else if (tag === "h5") state.level = 5;
2778
- else if (tag === "h6") state.level = 6;
2779
- const ariaLevel = el.getAttribute("aria-level");
2780
- if (ariaLevel) {
2781
- const n = Number.parseInt(ariaLevel, 10);
2782
- if (Number.isFinite(n) && n > 0) state.level = n;
2783
- }
2784
- }
2785
- return Object.keys(state).length > 0 ? state : void 0;
2786
- }
2787
- __name(collectState, "collectState");
2788
- const pageOrigin = (() => {
2789
- try {
2790
- return window.location.origin;
2791
- } catch {
2792
- return "";
2793
- }
2794
- })();
2795
- function normaliseHref(raw) {
2796
- if (!raw) return void 0;
2797
- let href = raw.trim();
2798
- if (!href) return void 0;
2799
- if (pageOrigin && href.startsWith(pageOrigin)) {
2800
- href = href.slice(pageOrigin.length) || "/";
2801
- }
2802
- return href;
2803
- }
2804
- __name(normaliseHref, "normaliseHref");
2805
- const REF_REGEX = /^e\d+$/;
2806
- const html = document.documentElement;
2807
- let refCounter = Number.parseInt(
2808
- html.getAttribute("data-unotest-ref-counter") || "0",
2809
- 10
2810
- );
2811
- if (!Number.isFinite(refCounter) || refCounter < 0) refCounter = 0;
2812
- function ensureRef(el, role) {
2813
- const existing = el.getAttribute("data-unotest-ref");
2814
- if (existing && REF_REGEX.test(existing)) return existing;
2815
- refCounter += 1;
2816
- const ref = "e" + refCounter;
2817
- el.setAttribute("data-unotest-ref", ref);
2818
- const interactive = INTERACTIVE_ROLES.has(role);
2819
- el.setAttribute("data-unotest-ref-kind", interactive ? "i" : "n");
2820
- return ref;
2821
- }
2822
- __name(ensureRef, "ensureRef");
2823
2103
  let totalNodes = 0;
2824
2104
  let totalRefs = 0;
2825
2105
  let frameCounter = 0;
2826
2106
  function shouldInclude(role) {
2827
2107
  if (!role) return false;
2828
- if (INTERACTIVE_ROLES.has(role)) return true;
2108
+ if (h.interactiveRoles.has(role)) return true;
2829
2109
  if (NAMED_STRUCTURAL_ROLES.has(role)) return true;
2830
2110
  if (LANDMARK_ROLES.has(role)) return true;
2831
2111
  if (role === "caption") return true;
@@ -2833,7 +2113,7 @@ function ariaSnapshotAlgorithm() {
2833
2113
  }
2834
2114
  __name(shouldInclude, "shouldInclude");
2835
2115
  function makeRef(el, role, name, refPrefix) {
2836
- const localRef = ensureRef(el, role);
2116
+ const localRef = h.ensureRef(el, h.interactiveRoles.has(role) ? "i" : "n");
2837
2117
  el.setAttribute("data-unotest-ref-role", role);
2838
2118
  el.setAttribute("data-unotest-ref-name", name);
2839
2119
  totalRefs += 1;
@@ -2853,7 +2133,7 @@ function ariaSnapshotAlgorithm() {
2853
2133
  function descendInto(parent, bucket, refPrefix, parentName) {
2854
2134
  const buf = { value: "" };
2855
2135
  function flush() {
2856
- const t = trim(buf.value);
2136
+ const t = h.trim(buf.value);
2857
2137
  if (t) pushText(t, bucket, parentName);
2858
2138
  buf.value = "";
2859
2139
  }
@@ -2865,7 +2145,7 @@ function ariaSnapshotAlgorithm() {
2865
2145
  }
2866
2146
  if (node.nodeType !== 1) return;
2867
2147
  const el = node;
2868
- if (!isVisible(el)) return;
2148
+ if (!h.isVisible(el)) return;
2869
2149
  const tag = el.tagName.toLowerCase();
2870
2150
  if (tag === "script" || tag === "style" || tag === "noscript") return;
2871
2151
  if (tag === "iframe") {
@@ -2873,7 +2153,7 @@ function ariaSnapshotAlgorithm() {
2873
2153
  visit(el, bucket, refPrefix, parentName);
2874
2154
  return;
2875
2155
  }
2876
- const role = computeRole(el);
2156
+ const role = h.computeRole(el);
2877
2157
  if (shouldInclude(role)) {
2878
2158
  flush();
2879
2159
  visit(el, bucket, refPrefix, parentName);
@@ -2897,7 +2177,7 @@ function ariaSnapshotAlgorithm() {
2897
2177
  function visit(el, parentBucket, refPrefix, _parentName) {
2898
2178
  const tag = el.tagName.toLowerCase();
2899
2179
  if (tag === "iframe") {
2900
- const name2 = accname(el);
2180
+ const name2 = h.accname(el);
2901
2181
  const ref = makeRef(el, "iframe", name2, refPrefix);
2902
2182
  const node2 = { role: "iframe", name: name2, ref, children: [] };
2903
2183
  let inner = null;
@@ -2915,23 +2195,23 @@ function ariaSnapshotAlgorithm() {
2915
2195
  parentBucket.push(node2);
2916
2196
  return;
2917
2197
  }
2918
- const role = computeRole(el);
2919
- const name = accname(el);
2198
+ const role = h.computeRole(el);
2199
+ const name = h.accname(el);
2920
2200
  const node = { role, name, children: [] };
2921
2201
  node.ref = makeRef(el, role, name, refPrefix);
2922
2202
  if (role === "link") {
2923
- const url = normaliseHref(
2203
+ const url = h.normaliseHref(
2924
2204
  el.getAttribute("href")
2925
2205
  );
2926
2206
  if (url) node.url = url;
2927
2207
  }
2928
- const state = collectState(el, role);
2208
+ const state = h.collectState(el, role);
2929
2209
  if (state) node.state = state;
2930
2210
  totalNodes += 1;
2931
2211
  parentBucket.push(node);
2932
2212
  if (role === "paragraph" || role === "code") {
2933
2213
  if (!node.name) {
2934
- const t = trim(textContentVisible(el));
2214
+ const t = h.trim(h.textContentVisible(el));
2935
2215
  if (t) node.name = t;
2936
2216
  }
2937
2217
  return;
@@ -3004,7 +2284,7 @@ function ariaSnapshotAlgorithm() {
3004
2284
  if (body) {
3005
2285
  descendInto(body, rootBucket, "", "");
3006
2286
  }
3007
- html.setAttribute("data-unotest-ref-counter", String(refCounter));
2287
+ h.persistRefCounter();
3008
2288
  for (const r of rootBucket) collapseRepetitiveRows(r);
3009
2289
  const recounted = recountTree(rootBucket);
3010
2290
  totalNodes = recounted.nodes;
@@ -3095,12 +2375,31 @@ function pickerOverlayAlgorithm(op) {
3095
2375
  }, "onScroll")
3096
2376
  };
3097
2377
  const norm = /* @__PURE__ */ __name((s) => (s ?? "").replace(/\s+/g, " ").trim(), "norm");
2378
+ const deepElementFromPoint = /* @__PURE__ */ __name((x, y) => {
2379
+ let el = document.elementFromPoint(x, y);
2380
+ while (el && el.shadowRoot) {
2381
+ const inner = el.shadowRoot.elementFromPoint(x, y);
2382
+ if (!inner || inner === el) break;
2383
+ el = inner;
2384
+ }
2385
+ return el;
2386
+ }, "deepElementFromPoint");
2387
+ const closestRef = /* @__PURE__ */ __name((el, sel) => {
2388
+ let node = el;
2389
+ while (node) {
2390
+ const found = node.closest(sel);
2391
+ if (found) return found;
2392
+ const root = node.getRootNode();
2393
+ node = root instanceof ShadowRoot ? root.host : null;
2394
+ }
2395
+ return null;
2396
+ }, "closestRef");
3098
2397
  const targetFrom = /* @__PURE__ */ __name((el) => {
3099
2398
  if (!el) return null;
3100
2399
  const sel = state.mode === "locator" ? `[${KIND_ATTR}="i"]` : `[${REF_ATTR}]`;
3101
- return el.closest(sel);
2400
+ return closestRef(el, sel);
3102
2401
  }, "targetFrom");
3103
- const refOf = /* @__PURE__ */ __name((el) => el instanceof Element ? el.closest(`[${REF_ATTR}]`) : null, "refOf");
2402
+ const refOf = /* @__PURE__ */ __name((el) => el instanceof Element ? closestRef(el, `[${REF_ATTR}]`) : null, "refOf");
3104
2403
  const push = /* @__PURE__ */ __name((node, action, extra) => {
3105
2404
  const ref = node.getAttribute(REF_ATTR);
3106
2405
  if (!ref) return;
@@ -3145,7 +2444,7 @@ function pickerOverlayAlgorithm(op) {
3145
2444
  }, "clearHighlight");
3146
2445
  state.onMove = (e) => {
3147
2446
  if (!e.isTrusted) return;
3148
- const node = targetFrom(document.elementFromPoint(e.clientX, e.clientY));
2447
+ const node = targetFrom(deepElementFromPoint(e.clientX, e.clientY));
3149
2448
  if (!node) {
3150
2449
  clearHighlight();
3151
2450
  return;
@@ -3153,7 +2452,7 @@ function pickerOverlayAlgorithm(op) {
3153
2452
  state.current = node;
3154
2453
  showHighlight(node);
3155
2454
  };
3156
- const overTarget = /* @__PURE__ */ __name((e) => targetFrom(document.elementFromPoint(e.clientX, e.clientY)), "overTarget");
2455
+ const overTarget = /* @__PURE__ */ __name((e) => targetFrom(deepElementFromPoint(e.clientX, e.clientY)), "overTarget");
3157
2456
  const swallow = /* @__PURE__ */ __name((e) => {
3158
2457
  e.preventDefault();
3159
2458
  e.stopPropagation();
@@ -3342,175 +2641,9 @@ function serializeRuntimeDom() {
3342
2641
  __name(serializeRuntimeDom, "serializeRuntimeDom");
3343
2642
 
3344
2643
  // src/inspection/find-element.ts
2644
+ import { composeInjectedScript as composeInjectedScript3 } from "@unotest/grounder-client/dom-walker";
3345
2645
  import { findElementAlgorithm as findElementAlgorithmInjectable } from "@unotest/web/internal/page-inject";
3346
- function findElementAlgorithm(args) {
3347
- function* deepWalk(root) {
3348
- const children = root.children;
3349
- for (let i = 0; i < children.length; i++) {
3350
- const el = children[i];
3351
- yield el;
3352
- const sr = el.shadowRoot;
3353
- if (sr) yield* deepWalk(sr);
3354
- yield* deepWalk(el);
3355
- }
3356
- }
3357
- __name(deepWalk, "deepWalk");
3358
- function parentOrHost(node) {
3359
- const parent = node.parentNode;
3360
- if (!parent) return null;
3361
- if (parent instanceof ShadowRoot) return parent.host;
3362
- return parent instanceof Element ? parent : null;
3363
- }
3364
- __name(parentOrHost, "parentOrHost");
3365
- function rootForScope(el) {
3366
- const root = el.getRootNode();
3367
- return root instanceof ShadowRoot ? root : document;
3368
- }
3369
- __name(rootForScope, "rootForScope");
3370
- function getElementByIdInScope(el, id) {
3371
- return rootForScope(el).getElementById(id);
3372
- }
3373
- __name(getElementByIdInScope, "getElementByIdInScope");
3374
- function isVisible(el) {
3375
- if (!el.getClientRects || el.getClientRects().length === 0) return false;
3376
- const cs = window.getComputedStyle(el);
3377
- if (cs.display === "none" || cs.visibility === "hidden") return false;
3378
- if (Number.parseFloat(cs.opacity || "1") < 0.01) return false;
3379
- for (let p = el; p; p = parentOrHost(p)) {
3380
- if (p.getAttribute && p.getAttribute("aria-hidden") === "true") return false;
3381
- }
3382
- const r = el.getBoundingClientRect();
3383
- if (r.width === 0 && r.height === 0) return false;
3384
- return true;
3385
- }
3386
- __name(isVisible, "isVisible");
3387
- function implicitRole(el) {
3388
- const tag = el.tagName.toLowerCase();
3389
- if (tag === "a") return el.hasAttribute("href") ? "link" : null;
3390
- if (tag === "button") return "button";
3391
- if (tag === "summary") return "button";
3392
- if (tag === "textarea") return "textbox";
3393
- if (tag === "select") {
3394
- const s = el;
3395
- return s.multiple || s.size > 1 ? "listbox" : "combobox";
3396
- }
3397
- if (tag === "input") {
3398
- const t = (el.type || "text").toLowerCase();
3399
- if (t === "button" || t === "submit" || t === "reset" || t === "image") return "button";
3400
- if (t === "checkbox") return "checkbox";
3401
- if (t === "radio") return "radio";
3402
- if (t === "range") return "slider";
3403
- if (t === "number") return "spinbutton";
3404
- if (t === "search") return "searchbox";
3405
- if (t === "hidden") return null;
3406
- return "textbox";
3407
- }
3408
- if (/^h[1-6]$/.test(tag)) return "heading";
3409
- if (tag === "nav") return "navigation";
3410
- if (tag === "header") return "banner";
3411
- if (tag === "main") return "main";
3412
- if (tag === "aside") return "complementary";
3413
- if (tag === "footer") return "contentinfo";
3414
- if (tag === "form") return "form";
3415
- if (tag === "article") return "article";
3416
- if (tag === "dialog") return "dialog";
3417
- if (tag === "section") {
3418
- return el.hasAttribute("aria-label") || el.hasAttribute("aria-labelledby") ? "region" : null;
3419
- }
3420
- if (tag === "ul" || tag === "ol" || tag === "menu") return "list";
3421
- if (tag === "li") return "listitem";
3422
- if (tag === "table") return "table";
3423
- if (tag === "tr") return "row";
3424
- if (tag === "td") return "cell";
3425
- if (tag === "th") {
3426
- const scope = (el.getAttribute("scope") || "").toLowerCase();
3427
- return scope === "row" ? "rowheader" : "columnheader";
3428
- }
3429
- if (tag === "p") return "paragraph";
3430
- if (tag === "figure") return "figure";
3431
- if (tag === "img") {
3432
- const alt = el.getAttribute("alt");
3433
- if (alt === "") return null;
3434
- return "img";
3435
- }
3436
- if (tag === "details" || tag === "fieldset") return "group";
3437
- if (tag === "iframe") return "iframe";
3438
- return null;
3439
- }
3440
- __name(implicitRole, "implicitRole");
3441
- function computeRole(el) {
3442
- const explicit = el.getAttribute("role");
3443
- if (explicit) return explicit.split(/\s+/)[0].toLowerCase();
3444
- return implicitRole(el);
3445
- }
3446
- __name(computeRole, "computeRole");
3447
- function trim(s) {
3448
- return (s ?? "").replace(/\s+/g, " ").trim();
3449
- }
3450
- __name(trim, "trim");
3451
- function textContentVisible(el) {
3452
- const ht = el;
3453
- if (typeof ht.innerText === "string") return ht.innerText;
3454
- return el.textContent ?? "";
3455
- }
3456
- __name(textContentVisible, "textContentVisible");
3457
- const MAX_NAME = 200;
3458
- function cap(s) {
3459
- return s.length > MAX_NAME ? s.slice(0, MAX_NAME - 1) + "\u2026" : s;
3460
- }
3461
- __name(cap, "cap");
3462
- function accname(el, visited) {
3463
- const seen = visited ?? /* @__PURE__ */ new Set();
3464
- if (seen.has(el)) return "";
3465
- seen.add(el);
3466
- const labelledby = el.getAttribute("aria-labelledby");
3467
- if (labelledby) {
3468
- const parts = [];
3469
- for (const id of labelledby.split(/\s+/)) {
3470
- if (!id) continue;
3471
- const ref = getElementByIdInScope(el, id);
3472
- if (ref && !seen.has(ref)) {
3473
- const n = accname(ref, seen);
3474
- if (n) parts.push(n);
3475
- }
3476
- }
3477
- const joined = trim(parts.join(" "));
3478
- if (joined) return cap(joined);
3479
- }
3480
- const ariaLabel = trim(el.getAttribute("aria-label"));
3481
- if (ariaLabel) return cap(ariaLabel);
3482
- const tag = el.tagName.toLowerCase();
3483
- if (tag === "input" || tag === "textarea") {
3484
- const ph = trim(el.placeholder);
3485
- if (ph) return cap(ph);
3486
- const t = trim(el.getAttribute("title"));
3487
- if (t) return cap(t);
3488
- return "";
3489
- }
3490
- if (tag === "img") return cap(trim(el.getAttribute("alt")));
3491
- if (tag === "form" || tag === "nav" || tag === "header" || tag === "main" || tag === "aside" || tag === "footer" || tag === "section" || tag === "article" || tag === "dialog" || tag === "ul" || tag === "ol" || tag === "menu" || tag === "li" || tag === "table" || tag === "tr" || tag === "td" || tag === "th" || tag === "p" || tag === "code" || tag === "pre" || tag === "blockquote" || tag === "fieldset" || tag === "details") {
3492
- return "";
3493
- }
3494
- return cap(trim(textContentVisible(el)));
3495
- }
3496
- __name(accname, "accname");
3497
- const REF_REGEX = /^e\d+$/;
3498
- const html = document.documentElement;
3499
- let refCounter = Number.parseInt(
3500
- html.getAttribute("data-unotest-ref-counter") || "0",
3501
- 10
3502
- );
3503
- if (!Number.isFinite(refCounter) || refCounter < 0) refCounter = 0;
3504
- function ensureRef(el) {
3505
- const existing = el.getAttribute("data-unotest-ref");
3506
- if (existing && REF_REGEX.test(existing)) return existing;
3507
- refCounter += 1;
3508
- const ref = "e" + refCounter;
3509
- el.setAttribute("data-unotest-ref", ref);
3510
- el.setAttribute("data-unotest-ref-kind", "n");
3511
- return ref;
3512
- }
3513
- __name(ensureRef, "ensureRef");
2646
+ function findElementAlgorithm(h, args) {
3514
2647
  let scopeRoot = document;
3515
2648
  if (args.near) {
3516
2649
  const scoped = document.querySelector(
@@ -3521,12 +2654,12 @@ function findElementAlgorithm(args) {
3521
2654
  const wantName = args.name ? args.name.toLowerCase() : null;
3522
2655
  const hits = [];
3523
2656
  function visitOne(el) {
3524
- if (!isVisible(el)) return;
3525
- const role = computeRole(el);
2657
+ if (!h.isVisible(el)) return;
2658
+ const role = h.computeRole(el);
3526
2659
  if (role === args.role) {
3527
2660
  if (wantName === null) {
3528
2661
  hits.push(el);
3529
- } else if (accname(el).toLowerCase().includes(wantName)) {
2662
+ } else if (h.accname(el).toLowerCase().includes(wantName)) {
3530
2663
  hits.push(el);
3531
2664
  }
3532
2665
  }
@@ -3535,10 +2668,10 @@ function findElementAlgorithm(args) {
3535
2668
  if (scopeRoot !== document) {
3536
2669
  const sr = scopeRoot.shadowRoot;
3537
2670
  if (sr) {
3538
- for (const el of deepWalk(sr)) visitOne(el);
2671
+ for (const el of h.deepWalk(sr)) visitOne(el);
3539
2672
  }
3540
2673
  }
3541
- for (const el of deepWalk(scopeRoot)) visitOne(el);
2674
+ for (const el of h.deepWalk(scopeRoot)) visitOne(el);
3542
2675
  const totalCount = hits.length;
3543
2676
  let selected = hits;
3544
2677
  if (args.nth !== void 0) {
@@ -3549,28 +2682,28 @@ function findElementAlgorithm(args) {
3549
2682
  }
3550
2683
  function namedAncestors(el) {
3551
2684
  const chain = [];
3552
- let p = parentOrHost(el);
2685
+ let p = h.parentOrHost(el);
3553
2686
  while (p) {
3554
- const role = computeRole(p);
2687
+ const role = h.computeRole(p);
3555
2688
  if (role) {
3556
- const name = accname(p);
2689
+ const name = h.accname(p);
3557
2690
  if (role !== "generic" || name) {
3558
- chain.push({ role, name, ref: ensureRef(p) });
2691
+ chain.push({ role, name, ref: h.ensureRef(p, "n") });
3559
2692
  }
3560
2693
  }
3561
- p = parentOrHost(p);
2694
+ p = h.parentOrHost(p);
3562
2695
  }
3563
2696
  return chain.reverse().slice(-4);
3564
2697
  }
3565
2698
  __name(namedAncestors, "namedAncestors");
3566
2699
  const matches = selected.map((el, idx) => ({
3567
- ref: ensureRef(el),
2700
+ ref: h.ensureRef(el, "n"),
3568
2701
  role: args.role,
3569
- name: accname(el),
2702
+ name: h.accname(el),
3570
2703
  index: args.nth !== void 0 ? args.nth : idx,
3571
2704
  ancestors: namedAncestors(el)
3572
2705
  }));
3573
- html.setAttribute("data-unotest-ref-counter", String(refCounter));
2706
+ h.persistRefCounter();
3574
2707
  const query = { role: args.role };
3575
2708
  if (args.name !== void 0) query.name = args.name;
3576
2709
  if (args.near !== void 0) query.near = args.near;
@@ -3653,9 +2786,13 @@ function inspectElementRaw(el) {
3653
2786
  "colgroup",
3654
2787
  "col"
3655
2788
  ]);
3656
- const rawText = STRUCTURAL_TAGS.has(tag) ? "" : (e.textContent ?? "").replace(/\s+/g, " ").trim().slice(0, 200);
2789
+ const structural = STRUCTURAL_TAGS.has(tag);
2790
+ const rawText = structural ? "" : (e.textContent ?? "").replace(/\s+/g, " ").trim().slice(0, 200);
3657
2791
  const text = rawText || void 0;
3658
2792
  if (!name && text) name = text;
2793
+ const CONTAINER_TEXT_CAP = 120;
2794
+ const containerFull = structural && !name ? (e.textContent ?? "").replace(/\s+/g, " ").trim() : "";
2795
+ const containerText = containerFull && containerFull.length <= CONTAINER_TEXT_CAP ? containerFull : void 0;
3659
2796
  const href = tag === "a" ? e.getAttribute("href") ?? void 0 : void 0;
3660
2797
  const placeholder = e.placeholder?.trim() || void 0;
3661
2798
  const alt = e.getAttribute("alt")?.trim() || void 0;
@@ -3735,6 +2872,7 @@ function inspectElementRaw(el) {
3735
2872
  alt,
3736
2873
  titleAttr,
3737
2874
  text,
2875
+ containerText,
3738
2876
  href,
3739
2877
  elementId,
3740
2878
  nameAttr,
@@ -3763,6 +2901,9 @@ function inspectElementRaw(el) {
3763
2901
  if (tag2 === "footer") return "contentinfo";
3764
2902
  if (tag2 === "aside") return "complementary";
3765
2903
  if (tag2 === "form") return "form";
2904
+ if (tag2 === "li") return "listitem";
2905
+ if (tag2 === "tr") return "row";
2906
+ if (tag2 === "article") return "article";
3766
2907
  return void 0;
3767
2908
  }
3768
2909
  __name(inferRole, "inferRole");