touchpress 0.2.0 → 0.2.1

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.
@@ -1,2 +1,2 @@
1
- import { A as normalizeText, C as parseScreen, D as deviceNameForSlot, E as TOUCHPRESS_DEFAULTS, M as TouchpressError, O as parseDeviceOptions, S as silentSink, T as resolve, a as sizeOf, d as createScrollSearch, f as directionToward, g as evaluate, h as describeCheck, i as relativeTo, j as textMatch, k as describeQuery, l as captureEvidence, m as probe, n as compareScreenshot, o as toPixelBox, p as formatFailure, r as cropScreenshot, t as preflight, u as createDevice, v as openSession, w as renderScreen, x as renderTitle, y as sessionName } from "../preflight-sFz236o3.mjs";
1
+ import { A as normalizeText, C as parseScreen, D as deviceNameForSlot, E as TOUCHPRESS_DEFAULTS, M as TouchpressError, O as parseDeviceOptions, S as silentSink, T as resolve, a as sizeOf, d as createScrollSearch, f as directionToward, g as evaluate, h as describeCheck, i as relativeTo, j as textMatch, k as describeQuery, l as captureEvidence, m as probe, n as compareScreenshot, o as toPixelBox, p as formatFailure, r as cropScreenshot, t as preflight, u as createDevice, v as openSession, w as renderScreen, x as renderTitle, y as sessionName } from "../preflight-D1aux7ic.mjs";
2
2
  export { TOUCHPRESS_DEFAULTS, TouchpressError, captureEvidence, compareScreenshot, createDevice, createScrollSearch, cropScreenshot, describeCheck, describeQuery, deviceNameForSlot, directionToward, evaluate, formatFailure, normalizeText, openSession, parseDeviceOptions, parseScreen, preflight, probe, relativeTo, renderScreen, renderTitle, resolve, sessionName, silentSink, sizeOf, textMatch, toPixelBox };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { C as parseScreen, E as TOUCHPRESS_DEFAULTS, M as TouchpressError, O as parseDeviceOptions, S as silentSink, T as resolve, _ as createQueue, a as sizeOf, b as sleep, c as createClient, i as relativeTo, j as textMatch, l as captureEvidence, n as compareScreenshot, o as toPixelBox, r as cropScreenshot, s as createAgentDeviceDriver, t as preflight, u as createDevice, v as openSession, w as renderScreen, x as renderTitle } from "./preflight-sFz236o3.mjs";
1
+ import { C as parseScreen, E as TOUCHPRESS_DEFAULTS, M as TouchpressError, O as parseDeviceOptions, S as silentSink, T as resolve, _ as createQueue, a as sizeOf, b as sleep, c as createClient, i as relativeTo, j as textMatch, l as captureEvidence, n as compareScreenshot, o as toPixelBox, r as cropScreenshot, s as createAgentDeviceDriver, t as preflight, u as createDevice, v as openSession, w as renderScreen, x as renderTitle } from "./preflight-D1aux7ic.mjs";
2
2
  import { expect as expect$1, test as test$1 } from "@playwright/test";
3
3
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
4
  import { dirname } from "node:path";
@@ -51,11 +51,11 @@ function formatError(info) {
51
51
  info.screen
52
52
  ].join("\n");
53
53
  case "fill-unconfirmed": return [
54
- `fill left the field holding something else after ${String(info.attempts)} attempts in ${String(info.timeoutMs)}ms.`,
54
+ info.actual === null ? `fill lost the field after ${String(info.attempts)} attempts in ${String(info.timeoutMs)}ms.` : `fill left the field holding something else after ${String(info.attempts)} attempts in ${String(info.timeoutMs)}ms.`,
55
55
  ``,
56
56
  `Locator: ${info.locator}`,
57
57
  `Expected value: ${describeValue(info.expected)}${info.expected.kind === "masked" ? " (a secure field reports a mask, not its contents)" : ""}`,
58
- `Actual value: ${info.actual === null ? "the locator stopped resolving" : describeValue(info.actual)}`,
58
+ `Actual value: ${info.actual === null ? "unknown, the field no longer resolved after the write" : describeValue(info.actual)}`,
59
59
  ``,
60
60
  `Screen:`,
61
61
  info.screen
@@ -1388,6 +1388,7 @@ function perform(session, sink, record, options, dispatch, write) {
1388
1388
  let retriedStaleRef = false;
1389
1389
  let attempts = 0;
1390
1390
  let target = record.query;
1391
+ let written = null;
1391
1392
  let lastActual = null;
1392
1393
  let screen = await device.capture();
1393
1394
  const unconfirmed = (expected) => new TouchpressError({
@@ -1426,6 +1427,7 @@ function perform(session, sink, record, options, dispatch, write) {
1426
1427
  typed: typedOf(resolution.node.role, write)
1427
1428
  }), () => Promise.resolve(), { box: true });
1428
1429
  target = identityOf(screen, resolution.node);
1430
+ written = confirmation;
1429
1431
  screen = await device.capture();
1430
1432
  lastActual = valueAt(screen, target);
1431
1433
  if (lastActual !== null && holds(confirmation, lastActual)) {
@@ -1442,13 +1444,16 @@ function perform(session, sink, record, options, dispatch, write) {
1442
1444
  continue;
1443
1445
  }
1444
1446
  const remaining = deadline - Date.now();
1445
- if (remaining <= 0) throw new TouchpressError({
1446
- kind: "not-found",
1447
- locator,
1448
- timeoutMs: timeout,
1449
- screen: renderScreen(screen),
1450
- scrolled: search.trail()
1451
- });
1447
+ if (remaining <= 0) {
1448
+ if (written !== null) throw unconfirmed(expectedOf(written));
1449
+ throw new TouchpressError({
1450
+ kind: "not-found",
1451
+ locator,
1452
+ timeoutMs: timeout,
1453
+ screen: renderScreen(screen),
1454
+ scrolled: search.trail()
1455
+ });
1456
+ }
1452
1457
  if (attempts === 0 && await search.step(screen, target, remaining)) {
1453
1458
  screen = await device.capture();
1454
1459
  continue;
@@ -1630,18 +1635,29 @@ function valueAt(screen, target) {
1630
1635
  /**
1631
1636
  * How the written node is found again on the next snapshot.
1632
1637
  *
1633
- * The driver copies an ancestor's identifier onto every descendant that
1634
- * inherits it, so a testId names this node alone only when it resolves to this
1635
- * node alone. Two inheriting siblings would otherwise turn a landed fill into a
1636
- * strict-mode failure. Position in the tree is the fallback.
1638
+ * A testId rarely names one node. SwiftUI puts one identifier on a field, its
1639
+ * placeholder, and the button under it, and the driver copies an ancestor's
1640
+ * identifier onto every inheriting descendant. The role narrows those to the
1641
+ * field, and failing that the field's position among nodes of its role stands
1642
+ * in. Never the raw tree index: the software keyboard inserts its windows at the
1643
+ * top of the tree, which shifts every index but adds no text field.
1637
1644
  */
1638
1645
  function identityOf(screen, node) {
1639
1646
  if (node.testId !== null) {
1640
- const byTestId = { testId: textMatch(node.testId, true) };
1647
+ const byTestId = {
1648
+ role: node.role,
1649
+ testId: textMatch(node.testId, true)
1650
+ };
1641
1651
  const resolution = resolve(screen, byTestId);
1642
1652
  if (resolution.outcome === "one" && resolution.node === node) return byTestId;
1643
1653
  }
1644
- return { where: (other) => other.index === node.index };
1654
+ const byRole = { role: node.role };
1655
+ const index = matchesOf(screen, byRole).indexOf(node);
1656
+ if (index === -1) throw new Error(`${node.ref} does not resolve under its own role`);
1657
+ return {
1658
+ ...byRole,
1659
+ index
1660
+ };
1645
1661
  }
1646
1662
  //#endregion
1647
1663
  //#region src/core/evidence.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "touchpress",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "End-to-end testing for mobile apps.",
5
5
  "homepage": "https://github.com/wobsoriano/touchpress#readme",
6
6
  "bugs": {