@piwitests/reporter 0.58.0 → 0.59.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.
package/dist/index.js CHANGED
@@ -634,6 +634,7 @@ function toWireTestCase(tc) {
634
634
  timeout: rest.timeout ?? null,
635
635
  error: rest.error,
636
636
  retries: rest.retries,
637
+ repeatEachIndex: rest.repeatEachIndex ?? 0,
637
638
  attempts: rest.attempts || null,
638
639
  workerIndex: rest.workerIndex ?? null,
639
640
  shardIndex: rest.shardIndex ?? null,
@@ -686,7 +687,13 @@ function isFailingStatus(status) {
686
687
  function computeDistinctRunCounts(testCases) {
687
688
  const final = /* @__PURE__ */ new Map();
688
689
  for (const tc of testCases) {
689
- const key = tc.testId ?? [tc.title, tc.location, (tc.suitePath ?? []).join(""), browserProjectName(tc.browser)].join("\0");
690
+ const key = tc.testId ?? [
691
+ tc.title,
692
+ tc.location,
693
+ (tc.suitePath ?? []).join(""),
694
+ browserProjectName(tc.browser),
695
+ tc.repeatEachIndex ?? 0
696
+ ].join("\0");
690
697
  const status = tc.status ?? "";
691
698
  const retries = tc.retries ?? 0;
692
699
  const prev = final.get(key);
@@ -844,6 +851,7 @@ var Uploader = class {
844
851
  title: testCase.title,
845
852
  location: testCase.location,
846
853
  retries: testCase.retries ?? 0,
854
+ repeatEachIndex: testCase.repeatEachIndex ?? 0,
847
855
  suitePath: testCase.suitePath ?? null
848
856
  })
849
857
  );
@@ -9541,6 +9549,7 @@ function caseLocateUrl(serverUrl, runId, test) {
9541
9549
  `retry=${test.retry}`
9542
9550
  ];
9543
9551
  if (test.browser) params.push(`browser=${encodeURIComponent(test.browser)}`);
9552
+ if (test.repeatEachIndex) params.push(`repeat=${test.repeatEachIndex}`);
9544
9553
  return `${serverUrl.replace(/\/+$/, "")}/test-runs/${runId}/locate?${params.join("&")}`;
9545
9554
  }
9546
9555
  function formatFailureLine(link) {
@@ -11039,6 +11048,7 @@ var PiwiDashboardReporter = class _PiwiDashboardReporter {
11039
11048
  type: "begin",
11040
11049
  title: test.title,
11041
11050
  location: testLocation(test, this.testPathBase),
11051
+ repeatEachIndex: test.repeatEachIndex,
11042
11052
  workerIndex: workerIndexOf(result),
11043
11053
  shardIndex: this.shardInfo?.current ?? null,
11044
11054
  browser: this.metadataCollector.getBrowserConfig(test) || void 0,
@@ -11146,6 +11156,8 @@ var PiwiDashboardReporter = class _PiwiDashboardReporter {
11146
11156
  // Playwright reports the same line, so synthesize it.
11147
11157
  error: expectedFailureError(result.status, annotations) ?? buildErrorText(result, this.testPathBase),
11148
11158
  retries: result.retry,
11159
+ // Each `--repeat-each` repetition is a test of its own, with its own attempts.
11160
+ repeatEachIndex: test.repeatEachIndex,
11149
11161
  attempts: attempts.map((a) => ({ ...a })),
11150
11162
  workerIndex: workerIndexOf(result),
11151
11163
  shardIndex: this.shardInfo?.current ?? null,
@@ -11232,6 +11244,7 @@ var PiwiDashboardReporter = class _PiwiDashboardReporter {
11232
11244
  file: testFile(test, this.testPathBase),
11233
11245
  retry: result.retry,
11234
11246
  browser: typeof testCase.browser?.projectName === "string" ? testCase.browser.projectName : null,
11247
+ repeatEachIndex: test.repeatEachIndex,
11235
11248
  headline: failureHeadline(testCase.error, testCase.performanceMetrics?.steps)
11236
11249
  });
11237
11250
  }
@@ -11350,6 +11363,7 @@ var PiwiDashboardReporter = class _PiwiDashboardReporter {
11350
11363
  timeout: test.timeout ?? null,
11351
11364
  error: null,
11352
11365
  retries: 0,
11366
+ repeatEachIndex: test.repeatEachIndex,
11353
11367
  workerIndex: null,
11354
11368
  shardIndex: this.shardInfo?.current ?? null,
11355
11369
  startedAt: null,
@@ -11874,10 +11888,11 @@ function installPickerOverlay(arg) {
11874
11888
  }
11875
11889
  const Z = 2147483600;
11876
11890
  const testIdAttr = arg.testIdAttribute || "data-testid";
11891
+ const enterReaches = arg.transport === "global";
11877
11892
  const words = {
11878
11893
  banner: "Piwi inspector \u2014 click any element to generate locators for it",
11879
- keys: "\u2191 parent \xB7 \u2193 child \xB7 Esc skip",
11880
- keysHovering: "click to pick \xB7 \u2191 parent \xB7 \u2193 child \xB7 Esc skip",
11894
+ keys: enterReaches ? "Enter picks the focused element \xB7 \u2191 parent \xB7 \u2193 child \xB7 Esc skip" : "\u2191 parent \xB7 \u2193 child \xB7 Esc skip",
11895
+ keysHovering: enterReaches ? "click or Enter to pick \xB7 \u2191 parent \xB7 \u2193 child \xB7 Esc skip" : "click to pick \xB7 \u2191 parent \xB7 \u2193 child \xB7 Esc skip",
11881
11896
  analyzing: "Analyzing element\u2026"
11882
11897
  };
11883
11898
  for (const key of Object.keys(words)) {
@@ -11972,6 +11987,16 @@ function installPickerOverlay(arg) {
11972
11987
  const cls = (el.getAttribute && el.getAttribute("class") || "").split(/\s+/).find((c) => c.length > 1);
11973
11988
  return cls ? `locator(${quoteJs(`.${cls}`)})` : tag;
11974
11989
  };
11990
+ const parentOf = (node) => {
11991
+ if (node.parentElement) return node.parentElement;
11992
+ const root = node.getRootNode ? node.getRootNode() : null;
11993
+ return root && root !== doc && root.host ? root.host : null;
11994
+ };
11995
+ const targetOf = (e) => {
11996
+ const path32 = typeof e.composedPath === "function" ? e.composedPath() : null;
11997
+ const first = path32 && path32.length ? path32[0] : null;
11998
+ return first && first.nodeType === 1 ? first : e.target;
11999
+ };
11975
12000
  const buildChain = (raw) => {
11976
12001
  const chain2 = [];
11977
12002
  let node = raw;
@@ -11979,7 +12004,7 @@ function installPickerOverlay(arg) {
11979
12004
  const tag = (node.tagName || "").toLowerCase();
11980
12005
  if (tag === "body" || tag === "html") break;
11981
12006
  chain2.push(node);
11982
- node = node.parentElement;
12007
+ node = parentOf(node);
11983
12008
  }
11984
12009
  return chain2.length ? chain2 : [raw];
11985
12010
  };
@@ -12070,10 +12095,14 @@ function installPickerOverlay(arg) {
12070
12095
  }
12071
12096
  };
12072
12097
  const onMove = (e) => {
12073
- const raw = e.target;
12098
+ const raw = targetOf(e);
12074
12099
  if (!raw || isOwn(raw)) {
12100
+ chain = [];
12101
+ idx = 0;
12102
+ lastRaw = null;
12075
12103
  highlight.style.display = "none";
12076
12104
  label.style.display = "none";
12105
+ foot.textContent = words.keys;
12077
12106
  return;
12078
12107
  }
12079
12108
  if (raw !== lastRaw) {
@@ -12152,17 +12181,30 @@ function installPickerOverlay(arg) {
12152
12181
  const onClick = (e) => {
12153
12182
  stop(e);
12154
12183
  const el = current();
12155
- if (!el || isOwn(e.target)) return;
12184
+ if (!el || isOwn(targetOf(e))) return;
12156
12185
  reportPicked(el);
12157
12186
  };
12187
+ const focused = () => {
12188
+ let el = doc.activeElement;
12189
+ while (el && el.shadowRoot && el.shadowRoot.activeElement) el = el.shadowRoot.activeElement;
12190
+ const tag = (el && el.tagName || "").toLowerCase();
12191
+ return !el || tag === "body" || tag === "html" || isOwn(el) ? null : el;
12192
+ };
12158
12193
  const onKey = (e) => {
12159
12194
  if (e.key === "Escape") {
12160
12195
  stop(e);
12161
12196
  reportSkipped();
12162
12197
  return;
12163
12198
  }
12199
+ if (e.key === "Enter" && !e.isComposing) {
12200
+ stop(e);
12201
+ const el = current() || focused();
12202
+ if (el) reportPicked(el);
12203
+ return;
12204
+ }
12164
12205
  if (e.key === "ArrowUp" || e.key === "ArrowDown") {
12165
12206
  stop(e);
12207
+ if (!chain.length) return;
12166
12208
  if (e.key === "ArrowUp") idx = Math.min(idx + 1, chain.length - 1);
12167
12209
  else idx = Math.max(idx - 1, 0);
12168
12210
  refresh();
@@ -12236,12 +12278,43 @@ function showAnchorPicker(arg) {
12236
12278
  const al = n.getAttribute && n.getAttribute("aria-level");
12237
12279
  return al && /^\d+$/.test(al) ? Number(al) : null;
12238
12280
  };
12281
+ const parentOf = (n) => {
12282
+ if (n.parentElement) return n.parentElement;
12283
+ const root = n.getRootNode ? n.getRootNode() : null;
12284
+ return root && root !== doc && root.host ? root.host : null;
12285
+ };
12286
+ const shadowRoots = [];
12287
+ const collectRoots = (root) => {
12288
+ const all = root.querySelectorAll("*");
12289
+ for (let i = 0; i < all.length; i++) {
12290
+ const sr = all[i].shadowRoot;
12291
+ if (sr) {
12292
+ shadowRoots.push(sr);
12293
+ collectRoots(sr);
12294
+ }
12295
+ }
12296
+ };
12297
+ try {
12298
+ collectRoots(doc);
12299
+ } catch {
12300
+ shadowRoots.length = 0;
12301
+ }
12302
+ const within = (node2, scope) => {
12303
+ if (scope === doc) return true;
12304
+ for (let n = node2; n; n = parentOf(n)) if (n === scope) return true;
12305
+ return false;
12306
+ };
12307
+ const queryAll = (scope, sel) => {
12308
+ const found = Array.from(scope.querySelectorAll(sel));
12309
+ for (const sr of shadowRoots) if (within(sr.host, scope)) found.push(...Array.from(sr.querySelectorAll(sel)));
12310
+ return found;
12311
+ };
12239
12312
  const leafCountable = !!leafTestId || roleOf2(el) === leafRole;
12240
12313
  const leafMatches = (scope) => {
12241
12314
  try {
12242
- if (leafTestId) return scope.querySelectorAll(`[${testIdAttr}=${JSON.stringify(leafTestId)}]`).length;
12315
+ if (leafTestId) return queryAll(scope, `[${testIdAttr}=${JSON.stringify(leafTestId)}]`).length;
12243
12316
  if (!leafCountable) return -1;
12244
- const nodes = scope.querySelectorAll(roleSources);
12317
+ const nodes = queryAll(scope, roleSources);
12245
12318
  if (nodes.length > 2e3) return -1;
12246
12319
  let matched = 0;
12247
12320
  for (let i = 0; i < nodes.length; i++) {
@@ -12257,20 +12330,20 @@ function showAnchorPicker(arg) {
12257
12330
  };
12258
12331
  let roleNodes = [];
12259
12332
  try {
12260
- const all = doc.querySelectorAll(roleSources);
12261
- if (all.length <= 4e3) roleNodes = Array.from(all);
12333
+ const all = queryAll(doc, roleSources);
12334
+ if (all.length <= 4e3) roleNodes = all;
12262
12335
  } catch {
12263
12336
  roleNodes = [];
12264
12337
  }
12265
12338
  const count = (sel) => {
12266
12339
  try {
12267
- return doc.querySelectorAll(sel).length;
12340
+ return queryAll(doc, sel).length;
12268
12341
  } catch {
12269
12342
  return void 0;
12270
12343
  }
12271
12344
  };
12272
12345
  const rows = [];
12273
- let node = el.parentElement;
12346
+ let node = parentOf(el);
12274
12347
  let depth = 0;
12275
12348
  while (node && depth < 12) {
12276
12349
  depth++;
@@ -12307,7 +12380,7 @@ function showAnchorPicker(arg) {
12307
12380
  hookLabel,
12308
12381
  selectable: !!(testId || id || role && (ariaLabel || info.roleCount === 1))
12309
12382
  });
12310
- node = node.parentElement;
12383
+ node = parentOf(node);
12311
12384
  }
12312
12385
  if (rows.length === 0) {
12313
12386
  g.__piwiAnchorState = "skipped";
@@ -12336,9 +12409,9 @@ function showAnchorPicker(arg) {
12336
12409
  footer.style.cssText = "margin:10px 0;font-weight:600;";
12337
12410
  const segMatches = (scope, info) => {
12338
12411
  try {
12339
- if (info.testId) return Array.from(scope.querySelectorAll(`[${testIdAttr}=${JSON.stringify(info.testId)}]`));
12340
- if (info.id) return Array.from(scope.querySelectorAll(`#${doc.defaultView.CSS.escape(info.id)}`));
12341
- const nodes = Array.from(scope.querySelectorAll(roleSources));
12412
+ if (info.testId) return queryAll(scope, `[${testIdAttr}=${JSON.stringify(info.testId)}]`);
12413
+ if (info.id) return queryAll(scope, `#${doc.defaultView.CSS.escape(info.id)}`);
12414
+ const nodes = queryAll(scope, roleSources);
12342
12415
  if (nodes.length > 2e3) return [];
12343
12416
  return nodes.filter(
12344
12417
  (n) => roleOf2(n) === info.role && (!info.ariaLabel || n.getAttribute && n.getAttribute("aria-label") === info.ariaLabel)
@@ -412,10 +412,11 @@ function installPickerOverlay(arg) {
412
412
  }
413
413
  const Z = 2147483600;
414
414
  const testIdAttr = arg.testIdAttribute || "data-testid";
415
+ const enterReaches = arg.transport === "global";
415
416
  const words = {
416
417
  banner: "Piwi inspector \u2014 click any element to generate locators for it",
417
- keys: "\u2191 parent \xB7 \u2193 child \xB7 Esc skip",
418
- keysHovering: "click to pick \xB7 \u2191 parent \xB7 \u2193 child \xB7 Esc skip",
418
+ keys: enterReaches ? "Enter picks the focused element \xB7 \u2191 parent \xB7 \u2193 child \xB7 Esc skip" : "\u2191 parent \xB7 \u2193 child \xB7 Esc skip",
419
+ keysHovering: enterReaches ? "click or Enter to pick \xB7 \u2191 parent \xB7 \u2193 child \xB7 Esc skip" : "click to pick \xB7 \u2191 parent \xB7 \u2193 child \xB7 Esc skip",
419
420
  analyzing: "Analyzing element\u2026"
420
421
  };
421
422
  for (const key of Object.keys(words)) {
@@ -510,6 +511,16 @@ function installPickerOverlay(arg) {
510
511
  const cls = (el.getAttribute && el.getAttribute("class") || "").split(/\s+/).find((c) => c.length > 1);
511
512
  return cls ? `locator(${quoteJs(`.${cls}`)})` : tag;
512
513
  };
514
+ const parentOf = (node) => {
515
+ if (node.parentElement) return node.parentElement;
516
+ const root = node.getRootNode ? node.getRootNode() : null;
517
+ return root && root !== doc && root.host ? root.host : null;
518
+ };
519
+ const targetOf = (e) => {
520
+ const path13 = typeof e.composedPath === "function" ? e.composedPath() : null;
521
+ const first = path13 && path13.length ? path13[0] : null;
522
+ return first && first.nodeType === 1 ? first : e.target;
523
+ };
513
524
  const buildChain = (raw) => {
514
525
  const chain2 = [];
515
526
  let node = raw;
@@ -517,7 +528,7 @@ function installPickerOverlay(arg) {
517
528
  const tag = (node.tagName || "").toLowerCase();
518
529
  if (tag === "body" || tag === "html") break;
519
530
  chain2.push(node);
520
- node = node.parentElement;
531
+ node = parentOf(node);
521
532
  }
522
533
  return chain2.length ? chain2 : [raw];
523
534
  };
@@ -608,10 +619,14 @@ function installPickerOverlay(arg) {
608
619
  }
609
620
  };
610
621
  const onMove = (e) => {
611
- const raw = e.target;
622
+ const raw = targetOf(e);
612
623
  if (!raw || isOwn(raw)) {
624
+ chain = [];
625
+ idx = 0;
626
+ lastRaw = null;
613
627
  highlight.style.display = "none";
614
628
  label.style.display = "none";
629
+ foot.textContent = words.keys;
615
630
  return;
616
631
  }
617
632
  if (raw !== lastRaw) {
@@ -690,17 +705,30 @@ function installPickerOverlay(arg) {
690
705
  const onClick = (e) => {
691
706
  stop(e);
692
707
  const el = current();
693
- if (!el || isOwn(e.target)) return;
708
+ if (!el || isOwn(targetOf(e))) return;
694
709
  reportPicked(el);
695
710
  };
711
+ const focused = () => {
712
+ let el = doc.activeElement;
713
+ while (el && el.shadowRoot && el.shadowRoot.activeElement) el = el.shadowRoot.activeElement;
714
+ const tag = (el && el.tagName || "").toLowerCase();
715
+ return !el || tag === "body" || tag === "html" || isOwn(el) ? null : el;
716
+ };
696
717
  const onKey = (e) => {
697
718
  if (e.key === "Escape") {
698
719
  stop(e);
699
720
  reportSkipped();
700
721
  return;
701
722
  }
723
+ if (e.key === "Enter" && !e.isComposing) {
724
+ stop(e);
725
+ const el = current() || focused();
726
+ if (el) reportPicked(el);
727
+ return;
728
+ }
702
729
  if (e.key === "ArrowUp" || e.key === "ArrowDown") {
703
730
  stop(e);
731
+ if (!chain.length) return;
704
732
  if (e.key === "ArrowUp") idx = Math.min(idx + 1, chain.length - 1);
705
733
  else idx = Math.max(idx - 1, 0);
706
734
  refresh();
@@ -774,12 +802,43 @@ function showAnchorPicker(arg) {
774
802
  const al = n.getAttribute && n.getAttribute("aria-level");
775
803
  return al && /^\d+$/.test(al) ? Number(al) : null;
776
804
  };
805
+ const parentOf = (n) => {
806
+ if (n.parentElement) return n.parentElement;
807
+ const root = n.getRootNode ? n.getRootNode() : null;
808
+ return root && root !== doc && root.host ? root.host : null;
809
+ };
810
+ const shadowRoots = [];
811
+ const collectRoots = (root) => {
812
+ const all = root.querySelectorAll("*");
813
+ for (let i = 0; i < all.length; i++) {
814
+ const sr = all[i].shadowRoot;
815
+ if (sr) {
816
+ shadowRoots.push(sr);
817
+ collectRoots(sr);
818
+ }
819
+ }
820
+ };
821
+ try {
822
+ collectRoots(doc);
823
+ } catch {
824
+ shadowRoots.length = 0;
825
+ }
826
+ const within = (node2, scope) => {
827
+ if (scope === doc) return true;
828
+ for (let n = node2; n; n = parentOf(n)) if (n === scope) return true;
829
+ return false;
830
+ };
831
+ const queryAll = (scope, sel) => {
832
+ const found = Array.from(scope.querySelectorAll(sel));
833
+ for (const sr of shadowRoots) if (within(sr.host, scope)) found.push(...Array.from(sr.querySelectorAll(sel)));
834
+ return found;
835
+ };
777
836
  const leafCountable = !!leafTestId || roleOf2(el) === leafRole;
778
837
  const leafMatches = (scope) => {
779
838
  try {
780
- if (leafTestId) return scope.querySelectorAll(`[${testIdAttr}=${JSON.stringify(leafTestId)}]`).length;
839
+ if (leafTestId) return queryAll(scope, `[${testIdAttr}=${JSON.stringify(leafTestId)}]`).length;
781
840
  if (!leafCountable) return -1;
782
- const nodes = scope.querySelectorAll(roleSources);
841
+ const nodes = queryAll(scope, roleSources);
783
842
  if (nodes.length > 2e3) return -1;
784
843
  let matched = 0;
785
844
  for (let i = 0; i < nodes.length; i++) {
@@ -795,20 +854,20 @@ function showAnchorPicker(arg) {
795
854
  };
796
855
  let roleNodes = [];
797
856
  try {
798
- const all = doc.querySelectorAll(roleSources);
799
- if (all.length <= 4e3) roleNodes = Array.from(all);
857
+ const all = queryAll(doc, roleSources);
858
+ if (all.length <= 4e3) roleNodes = all;
800
859
  } catch {
801
860
  roleNodes = [];
802
861
  }
803
862
  const count = (sel) => {
804
863
  try {
805
- return doc.querySelectorAll(sel).length;
864
+ return queryAll(doc, sel).length;
806
865
  } catch {
807
866
  return void 0;
808
867
  }
809
868
  };
810
869
  const rows = [];
811
- let node = el.parentElement;
870
+ let node = parentOf(el);
812
871
  let depth = 0;
813
872
  while (node && depth < 12) {
814
873
  depth++;
@@ -845,7 +904,7 @@ function showAnchorPicker(arg) {
845
904
  hookLabel,
846
905
  selectable: !!(testId || id || role && (ariaLabel || info.roleCount === 1))
847
906
  });
848
- node = node.parentElement;
907
+ node = parentOf(node);
849
908
  }
850
909
  if (rows.length === 0) {
851
910
  g.__piwiAnchorState = "skipped";
@@ -874,9 +933,9 @@ function showAnchorPicker(arg) {
874
933
  footer.style.cssText = "margin:10px 0;font-weight:600;";
875
934
  const segMatches = (scope, info) => {
876
935
  try {
877
- if (info.testId) return Array.from(scope.querySelectorAll(`[${testIdAttr}=${JSON.stringify(info.testId)}]`));
878
- if (info.id) return Array.from(scope.querySelectorAll(`#${doc.defaultView.CSS.escape(info.id)}`));
879
- const nodes = Array.from(scope.querySelectorAll(roleSources));
936
+ if (info.testId) return queryAll(scope, `[${testIdAttr}=${JSON.stringify(info.testId)}]`);
937
+ if (info.id) return queryAll(scope, `#${doc.defaultView.CSS.escape(info.id)}`);
938
+ const nodes = queryAll(scope, roleSources);
880
939
  if (nodes.length > 2e3) return [];
881
940
  return nodes.filter(
882
941
  (n) => roleOf2(n) === info.role && (!info.ariaLabel || n.getAttribute && n.getAttribute("aria-label") === info.ariaLabel)
@@ -379,10 +379,11 @@ function installPickerOverlay(arg) {
379
379
  }
380
380
  const Z = 2147483600;
381
381
  const testIdAttr = arg.testIdAttribute || "data-testid";
382
+ const enterReaches = arg.transport === "global";
382
383
  const words = {
383
384
  banner: "Piwi inspector \u2014 click any element to generate locators for it",
384
- keys: "\u2191 parent \xB7 \u2193 child \xB7 Esc skip",
385
- keysHovering: "click to pick \xB7 \u2191 parent \xB7 \u2193 child \xB7 Esc skip",
385
+ keys: enterReaches ? "Enter picks the focused element \xB7 \u2191 parent \xB7 \u2193 child \xB7 Esc skip" : "\u2191 parent \xB7 \u2193 child \xB7 Esc skip",
386
+ keysHovering: enterReaches ? "click or Enter to pick \xB7 \u2191 parent \xB7 \u2193 child \xB7 Esc skip" : "click to pick \xB7 \u2191 parent \xB7 \u2193 child \xB7 Esc skip",
386
387
  analyzing: "Analyzing element\u2026"
387
388
  };
388
389
  for (const key of Object.keys(words)) {
@@ -477,6 +478,16 @@ function installPickerOverlay(arg) {
477
478
  const cls = (el.getAttribute && el.getAttribute("class") || "").split(/\s+/).find((c) => c.length > 1);
478
479
  return cls ? `locator(${quoteJs(`.${cls}`)})` : tag;
479
480
  };
481
+ const parentOf = (node) => {
482
+ if (node.parentElement) return node.parentElement;
483
+ const root = node.getRootNode ? node.getRootNode() : null;
484
+ return root && root !== doc && root.host ? root.host : null;
485
+ };
486
+ const targetOf = (e) => {
487
+ const path3 = typeof e.composedPath === "function" ? e.composedPath() : null;
488
+ const first = path3 && path3.length ? path3[0] : null;
489
+ return first && first.nodeType === 1 ? first : e.target;
490
+ };
480
491
  const buildChain = (raw) => {
481
492
  const chain2 = [];
482
493
  let node = raw;
@@ -484,7 +495,7 @@ function installPickerOverlay(arg) {
484
495
  const tag = (node.tagName || "").toLowerCase();
485
496
  if (tag === "body" || tag === "html") break;
486
497
  chain2.push(node);
487
- node = node.parentElement;
498
+ node = parentOf(node);
488
499
  }
489
500
  return chain2.length ? chain2 : [raw];
490
501
  };
@@ -575,10 +586,14 @@ function installPickerOverlay(arg) {
575
586
  }
576
587
  };
577
588
  const onMove = (e) => {
578
- const raw = e.target;
589
+ const raw = targetOf(e);
579
590
  if (!raw || isOwn(raw)) {
591
+ chain = [];
592
+ idx = 0;
593
+ lastRaw = null;
580
594
  highlight.style.display = "none";
581
595
  label.style.display = "none";
596
+ foot.textContent = words.keys;
582
597
  return;
583
598
  }
584
599
  if (raw !== lastRaw) {
@@ -657,17 +672,30 @@ function installPickerOverlay(arg) {
657
672
  const onClick = (e) => {
658
673
  stop(e);
659
674
  const el = current();
660
- if (!el || isOwn(e.target)) return;
675
+ if (!el || isOwn(targetOf(e))) return;
661
676
  reportPicked(el);
662
677
  };
678
+ const focused = () => {
679
+ let el = doc.activeElement;
680
+ while (el && el.shadowRoot && el.shadowRoot.activeElement) el = el.shadowRoot.activeElement;
681
+ const tag = (el && el.tagName || "").toLowerCase();
682
+ return !el || tag === "body" || tag === "html" || isOwn(el) ? null : el;
683
+ };
663
684
  const onKey = (e) => {
664
685
  if (e.key === "Escape") {
665
686
  stop(e);
666
687
  reportSkipped();
667
688
  return;
668
689
  }
690
+ if (e.key === "Enter" && !e.isComposing) {
691
+ stop(e);
692
+ const el = current() || focused();
693
+ if (el) reportPicked(el);
694
+ return;
695
+ }
669
696
  if (e.key === "ArrowUp" || e.key === "ArrowDown") {
670
697
  stop(e);
698
+ if (!chain.length) return;
671
699
  if (e.key === "ArrowUp") idx = Math.min(idx + 1, chain.length - 1);
672
700
  else idx = Math.max(idx - 1, 0);
673
701
  refresh();
@@ -741,12 +769,43 @@ function showAnchorPicker(arg) {
741
769
  const al = n.getAttribute && n.getAttribute("aria-level");
742
770
  return al && /^\d+$/.test(al) ? Number(al) : null;
743
771
  };
772
+ const parentOf = (n) => {
773
+ if (n.parentElement) return n.parentElement;
774
+ const root = n.getRootNode ? n.getRootNode() : null;
775
+ return root && root !== doc && root.host ? root.host : null;
776
+ };
777
+ const shadowRoots = [];
778
+ const collectRoots = (root) => {
779
+ const all = root.querySelectorAll("*");
780
+ for (let i = 0; i < all.length; i++) {
781
+ const sr = all[i].shadowRoot;
782
+ if (sr) {
783
+ shadowRoots.push(sr);
784
+ collectRoots(sr);
785
+ }
786
+ }
787
+ };
788
+ try {
789
+ collectRoots(doc);
790
+ } catch {
791
+ shadowRoots.length = 0;
792
+ }
793
+ const within = (node2, scope) => {
794
+ if (scope === doc) return true;
795
+ for (let n = node2; n; n = parentOf(n)) if (n === scope) return true;
796
+ return false;
797
+ };
798
+ const queryAll = (scope, sel) => {
799
+ const found = Array.from(scope.querySelectorAll(sel));
800
+ for (const sr of shadowRoots) if (within(sr.host, scope)) found.push(...Array.from(sr.querySelectorAll(sel)));
801
+ return found;
802
+ };
744
803
  const leafCountable = !!leafTestId || roleOf(el) === leafRole;
745
804
  const leafMatches = (scope) => {
746
805
  try {
747
- if (leafTestId) return scope.querySelectorAll(`[${testIdAttr}=${JSON.stringify(leafTestId)}]`).length;
806
+ if (leafTestId) return queryAll(scope, `[${testIdAttr}=${JSON.stringify(leafTestId)}]`).length;
748
807
  if (!leafCountable) return -1;
749
- const nodes = scope.querySelectorAll(roleSources);
808
+ const nodes = queryAll(scope, roleSources);
750
809
  if (nodes.length > 2e3) return -1;
751
810
  let matched = 0;
752
811
  for (let i = 0; i < nodes.length; i++) {
@@ -762,20 +821,20 @@ function showAnchorPicker(arg) {
762
821
  };
763
822
  let roleNodes = [];
764
823
  try {
765
- const all = doc.querySelectorAll(roleSources);
766
- if (all.length <= 4e3) roleNodes = Array.from(all);
824
+ const all = queryAll(doc, roleSources);
825
+ if (all.length <= 4e3) roleNodes = all;
767
826
  } catch {
768
827
  roleNodes = [];
769
828
  }
770
829
  const count = (sel) => {
771
830
  try {
772
- return doc.querySelectorAll(sel).length;
831
+ return queryAll(doc, sel).length;
773
832
  } catch {
774
833
  return void 0;
775
834
  }
776
835
  };
777
836
  const rows = [];
778
- let node = el.parentElement;
837
+ let node = parentOf(el);
779
838
  let depth = 0;
780
839
  while (node && depth < 12) {
781
840
  depth++;
@@ -812,7 +871,7 @@ function showAnchorPicker(arg) {
812
871
  hookLabel,
813
872
  selectable: !!(testId || id || role && (ariaLabel || info.roleCount === 1))
814
873
  });
815
- node = node.parentElement;
874
+ node = parentOf(node);
816
875
  }
817
876
  if (rows.length === 0) {
818
877
  g.__piwiAnchorState = "skipped";
@@ -841,9 +900,9 @@ function showAnchorPicker(arg) {
841
900
  footer.style.cssText = "margin:10px 0;font-weight:600;";
842
901
  const segMatches = (scope, info) => {
843
902
  try {
844
- if (info.testId) return Array.from(scope.querySelectorAll(`[${testIdAttr}=${JSON.stringify(info.testId)}]`));
845
- if (info.id) return Array.from(scope.querySelectorAll(`#${doc.defaultView.CSS.escape(info.id)}`));
846
- const nodes = Array.from(scope.querySelectorAll(roleSources));
903
+ if (info.testId) return queryAll(scope, `[${testIdAttr}=${JSON.stringify(info.testId)}]`);
904
+ if (info.id) return queryAll(scope, `#${doc.defaultView.CSS.escape(info.id)}`);
905
+ const nodes = queryAll(scope, roleSources);
847
906
  if (nodes.length > 2e3) return [];
848
907
  return nodes.filter(
849
908
  (n) => roleOf(n) === info.role && (!info.ariaLabel || n.getAttribute && n.getAttribute("aria-label") === info.ariaLabel)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piwitests/reporter",
3
- "version": "0.58.0",
3
+ "version": "0.59.0",
4
4
  "description": "Playwright reporter that streams results, traces and HTML reports to a Piwi Dashboard instance",
5
5
  "url": "https://github.com/PiwiTests/platform",
6
6
  "homepage": "https://piwitests.dev",