@piwitests/reporter 0.48.0 → 0.49.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.
@@ -29,6 +29,8 @@ interface InspectionGate {
29
29
  /** Configured retry count for the project (`testInfo.project.retries`). */
30
30
  retries: number;
31
31
  }
32
+ /** True when this raw `CI` env value counts as "running under CI". */
33
+ declare function isCi(ci: string | undefined): boolean;
32
34
  /** Decide whether the failing page should be handed to the Inspector. */
33
35
  declare function shouldInspectOnFailure(gate: InspectionGate): boolean;
34
36
  /**
@@ -47,4 +49,4 @@ declare function environmentalSkipReason(gate: InspectionGate): string | null;
47
49
  */
48
50
  declare function inspectionGateFromTestInfo(testInfo: TestInfo, enabled?: string | undefined): InspectionGate;
49
51
 
50
- export { type InspectionGate, environmentalSkipReason, inspectionGateFromTestInfo, shouldInspectOnFailure };
52
+ export { type InspectionGate, environmentalSkipReason, inspectionGateFromTestInfo, isCi, shouldInspectOnFailure };
@@ -22,6 +22,7 @@ var inspect_on_failure_exports = {};
22
22
  __export(inspect_on_failure_exports, {
23
23
  environmentalSkipReason: () => environmentalSkipReason,
24
24
  inspectionGateFromTestInfo: () => inspectionGateFromTestInfo,
25
+ isCi: () => isCi,
25
26
  shouldInspectOnFailure: () => shouldInspectOnFailure
26
27
  });
27
28
  module.exports = __toCommonJS(inspect_on_failure_exports);
@@ -62,5 +63,6 @@ function inspectionGateFromTestInfo(testInfo, enabled = process.env.PIWI_INSPECT
62
63
  0 && (module.exports = {
63
64
  environmentalSkipReason,
64
65
  inspectionGateFromTestInfo,
66
+ isCi,
65
67
  shouldInspectOnFailure
66
68
  });
@@ -1,5 +1,5 @@
1
1
  import { TestInfo, Page } from '@playwright/test';
2
- import { ProbeArg, ProbedAttrs, PickedAnchorInfo } from '@piwitests/picker-dom';
2
+ import { PickedAnchorInfo, ProbeArg, ProbedAttrs } from '@piwitests/picker-dom';
3
3
  export { PickedAnchorInfo, PickedLeafInfo, generateAnchoredAlternatives, installPickerOverlay, mergeCandidates, showAnchorPicker, showPickerChoices } from '@piwitests/picker-dom';
4
4
  import { FailedLocatorInfo } from './locator-healing.js';
5
5
  import { RankedLocator, LocatorSnapshot } from '@piwitests/core/locator-healing-types';
@@ -55,13 +55,21 @@ declare function parseLeafLocatorExpression(rawExpr: string): {
55
55
  * when no locator can be identified. Exported for tests.
56
56
  */
57
57
  declare function deriveFailedLocator(testInfo: TestInfo): FailedLocatorInfo | null;
58
+ /** What the element → anchors → confirm flow yields: the confirmed locator, its alternatives and the element. */
59
+ type PickedLocator = Omit<UserPickResult, 'failing'>;
58
60
  /**
59
- * Drive the full pick flow on the failing page: element pick (snap +
60
- * tree-walk), optional stable-parent anchoring with live match counts, ranked
61
+ * Run the element → anchors → confirm flow on a live page: element pick (snap
62
+ * + tree-walk), optional stable-parent anchoring with live match counts, ranked
61
63
  * candidates (standard generation merged with anchor-scoped chains), and a
62
- * final confirmation. The test timeout is lifted while waiting. Returns null
63
- * when skipped or when anything breaks — the picker must never mask the
64
- * test's own failure.
64
+ * final confirmation. `failing` is the locator the pick replaces, rendered, for
65
+ * the overlay's headings; null to pick any element. Returns null when the
66
+ * human skips or no locator could be generated; throws when the page breaks.
67
+ */
68
+ declare function pickLocator(page: Page, probe: PickerProbe, failing: string | null): Promise<PickedLocator | null>;
69
+ /**
70
+ * Drive the full pick flow on the failing page (`pickLocator`), the test
71
+ * timeout lifted while waiting. Returns null when skipped or when anything
72
+ * breaks — the picker must never mask the test's own failure.
65
73
  */
66
74
  declare function runLocatorPicker(page: Page, testInfo: TestInfo, failed: FailedLocatorInfo | null, probe: PickerProbe): Promise<UserPickResult | null>;
67
75
  /**
@@ -80,4 +88,4 @@ declare function runLocatorPicker(page: Page, testInfo: TestInfo, failed: Failed
80
88
  */
81
89
  declare function applyPickToSnapshots(snapshots: LocatorSnapshot[], pick: UserPickResult): boolean;
82
90
 
83
- export { type PickerProbe, type UserPickResult, applyPickToSnapshots, deriveFailedLocator, parseLeafLocatorExpression, runLocatorPicker };
91
+ export { type PickedLocator, type PickerProbe, type UserPickResult, applyPickToSnapshots, deriveFailedLocator, parseLeafLocatorExpression, pickLocator, runLocatorPicker };
@@ -36,6 +36,7 @@ __export(pick_on_failure_exports, {
36
36
  installPickerOverlay: () => installPickerOverlay,
37
37
  mergeCandidates: () => mergeCandidates,
38
38
  parseLeafLocatorExpression: () => parseLeafLocatorExpression,
39
+ pickLocator: () => pickLocator,
39
40
  runLocatorPicker: () => runLocatorPicker,
40
41
  showAnchorPicker: () => showAnchorPicker,
41
42
  showPickerChoices: () => showPickerChoices
@@ -1544,25 +1545,16 @@ async function cleanupPicker(page) {
1544
1545
  } catch {
1545
1546
  }
1546
1547
  }
1547
- async function runLocatorPicker(page, testInfo, failed, probe) {
1548
+ async function pickLocator(page, probe, failing) {
1548
1549
  try {
1549
- testInfo.setTimeout(0);
1550
- const rendered = failed ? renderFailing(failed) : null;
1551
- console.log(
1552
- `
1553
- [piwi] "${testInfo.title}" ${testInfo.status} \u2014 ${rendered ? "locator picker" : "inspector"} open in the browser: ${rendered ? `click the element that should replace ${rendered}` : "click any element to generate locators for it"} (\u2191/\u2193 to select a parent/child, Esc to skip).`
1554
- );
1555
- const overlayArg = { transport: "global", failing: rendered };
1550
+ const overlayArg = { transport: "global", failing };
1556
1551
  await page.evaluate(installPickerOverlay, overlayArg);
1557
1552
  await page.waitForFunction(() => globalThis.__piwiPickState !== void 0, void 0, {
1558
1553
  timeout: 0,
1559
1554
  polling: 250
1560
1555
  });
1561
1556
  const state = await page.evaluate(() => globalThis.__piwiPickState);
1562
- if (state !== "picked") {
1563
- await cleanupPicker(page);
1564
- return null;
1565
- }
1557
+ if (state !== "picked") return null;
1566
1558
  const handle = await page.evaluateHandle(() => globalThis.__piwiPickedElement);
1567
1559
  const attrs = await handle.evaluate(probe.fn, probe.arg);
1568
1560
  await handle.dispose();
@@ -1600,11 +1592,10 @@ async function runLocatorPicker(page, testInfo, failed, probe) {
1600
1592
  );
1601
1593
  if (ranked.length === 0) {
1602
1594
  console.log("[piwi] No stable locator could be generated for the picked element \u2014 nothing recorded.");
1603
- await cleanupPicker(page);
1604
1595
  return null;
1605
1596
  }
1606
1597
  await page.evaluate(showPickerChoices, {
1607
- failing: rendered,
1598
+ failing,
1608
1599
  choices: ranked.map((r) => ({ locator: r.locator, score: r.score }))
1609
1600
  });
1610
1601
  await page.waitForFunction(() => globalThis.__piwiPickChoice !== void 0, void 0, {
@@ -1612,31 +1603,45 @@ async function runLocatorPicker(page, testInfo, failed, probe) {
1612
1603
  polling: 250
1613
1604
  });
1614
1605
  const choice = await page.evaluate(() => globalThis.__piwiPickChoice);
1615
- await cleanupPicker(page);
1616
1606
  if (typeof choice !== "number" || choice < 0 || choice >= ranked.length) return null;
1617
1607
  const picked = { ...ranked[choice], pickedByUser: true };
1618
- const alternatives = [picked, ...ranked.filter((_, i) => i !== choice)];
1619
- const element = {
1620
- tagName: attrs.tagName,
1621
- attributes: attrs.attributes,
1622
- textContent: attrs.textContent,
1623
- accessibleName,
1624
- center: attrs.center,
1625
- ...attrs.rolePosition ? { rolePosition: attrs.rolePosition } : {},
1626
- ...attrs.ancestors && attrs.ancestors.length > 0 ? { ancestors: attrs.ancestors } : {}
1608
+ return {
1609
+ picked,
1610
+ alternatives: [picked, ...ranked.filter((_, i) => i !== choice)],
1611
+ element: {
1612
+ tagName: attrs.tagName,
1613
+ attributes: attrs.attributes,
1614
+ textContent: attrs.textContent,
1615
+ accessibleName,
1616
+ center: attrs.center,
1617
+ ...attrs.rolePosition ? { rolePosition: attrs.rolePosition } : {},
1618
+ ...attrs.ancestors && attrs.ancestors.length > 0 ? { ancestors: attrs.ancestors } : {}
1619
+ },
1620
+ ...anchors.length > 0 ? { anchors } : {}
1627
1621
  };
1622
+ } finally {
1623
+ await cleanupPicker(page);
1624
+ }
1625
+ }
1626
+ async function runLocatorPicker(page, testInfo, failed, probe) {
1627
+ try {
1628
+ testInfo.setTimeout(0);
1629
+ const rendered = failed ? renderFailing(failed) : null;
1630
+ console.log(
1631
+ `
1632
+ [piwi] "${testInfo.title}" ${testInfo.status} \u2014 ${rendered ? "locator picker" : "inspector"} open in the browser: ${rendered ? `click the element that should replace ${rendered}` : "click any element to generate locators for it"} (\u2191/\u2193 to select a parent/child, Esc to skip).`
1633
+ );
1634
+ const pick = await pickLocator(page, probe, rendered);
1635
+ if (!pick) return null;
1628
1636
  const location = failed?.location ?? null;
1629
1637
  console.log(
1630
1638
  failed ? `[piwi] Replacement picked for ${rendered}${location ? ` at ${location}` : ""}:
1631
- [piwi] ${picked.locator}` : `[piwi] Locator picked while inspecting:
1632
- [piwi] ${picked.locator}`
1639
+ [piwi] ${pick.picked.locator}` : `[piwi] Locator picked while inspecting:
1640
+ [piwi] ${pick.picked.locator}`
1633
1641
  );
1634
1642
  return {
1635
1643
  failing: failed ? { method: failed.method, args: failed.args, rendered, location } : null,
1636
- picked,
1637
- alternatives,
1638
- element,
1639
- ...anchors.length > 0 ? { anchors } : {}
1644
+ ...pick
1640
1645
  };
1641
1646
  } catch {
1642
1647
  return null;
@@ -1676,6 +1681,7 @@ function applyPickToSnapshots(snapshots, pick) {
1676
1681
  installPickerOverlay,
1677
1682
  mergeCandidates,
1678
1683
  parseLeafLocatorExpression,
1684
+ pickLocator,
1679
1685
  runLocatorPicker,
1680
1686
  showAnchorPicker,
1681
1687
  showPickerChoices
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piwitests/reporter",
3
- "version": "0.48.0",
3
+ "version": "0.49.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",