peachsim 0.1.629 → 0.1.631
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/README.md +8 -4
- package/cli/app-fonts.ts +23 -4
- package/cli/bridge-flow-runner.ts +45 -31
- package/cli/cli-update.ts +2 -7
- package/cli/cloud-test-tunnel.ts +16 -3
- package/cli/command-registry.ts +1 -0
- package/cli/commands/compat.ts +11 -60
- package/cli/commands/config.ts +1 -1
- package/cli/commands/control.ts +40 -8
- package/cli/commands/daemon-mac-app.ts +4 -2
- package/cli/commands/daemon.ts +4 -4
- package/cli/commands/detect.ts +289 -26
- package/cli/commands/device.ts +2 -3
- package/cli/commands/inspect/actions.ts +125 -90
- package/cli/commands/inspect/core.ts +1 -1
- package/cli/commands/inspect.ts +10 -1
- package/cli/commands/maestro.ts +3 -1
- package/cli/commands/platform.ts +1 -1
- package/cli/commands/project-report.ts +386 -0
- package/cli/commands/projectReportContract.ts +42 -0
- package/cli/commands/report-issue.ts +38 -54
- package/cli/commands/screenshot-layers.ts +1 -1
- package/cli/commands/screenshot.ts +1 -1
- package/cli/commands/setup.ts +6 -4
- package/cli/commands/skills.ts +0 -31
- package/cli/commands/telemetry.ts +8 -3
- package/cli/commands/test.ts +347 -134
- package/cli/drivers/playwright-provisioning.ts +3 -2
- package/cli/drivers/playwright-sim-host.ts +25 -9
- package/cli/main.ts +19 -3
- package/cli/outbound-endpoints.ts +2 -5
- package/cli/privacy.ts +1 -2
- package/cli/runtime-diagnostics.ts +51 -0
- package/cli/telemetry.ts +78 -7
- package/detox/index.ts +77 -51
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.cjs +1 -1
- package/dist-lib/agent-sessions.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +1 -1
- package/dist-lib/backend-origin.cjs +1 -1
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +1 -1
- package/dist-lib/bridge-contract-input.cjs +1 -1
- package/dist-lib/bridge-contract-input.mjs +1 -1
- package/dist-lib/bridge-contract.cjs +1 -1
- package/dist-lib/bridge-contract.mjs +1 -1
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/cloud-contract.cjs +1 -1
- package/dist-lib/cloud-contract.mjs +1 -1
- package/dist-lib/cloud-test-args.cjs +1 -1
- package/dist-lib/cloud-test-args.mjs +1 -1
- package/dist-lib/cloud.cjs +1 -1
- package/dist-lib/cloud.mjs +1 -1
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +130 -33
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +8 -5
- package/dist-lib/host/bridge-host.cjs +353 -290
- package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
- package/dist-lib/host/replacement-module-handler.cjs +1 -1
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +257 -121
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/{vite-kotlin.cjs → kotlin-host.cjs} +130 -19
- package/dist-lib/menu.cjs +11 -1
- package/dist-lib/menu.mjs +11 -1
- package/dist-lib/metro-fingerprint-registry.cjs +1 -1
- package/dist-lib/metro-fingerprint-registry.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +1 -1
- package/dist-lib/metro-production-bundle.mjs +1 -1
- package/dist-lib/metro.cjs +132 -9
- package/dist-lib/native-app-identity.cjs +278 -0
- package/dist-lib/native-app-identity.mjs +245 -0
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +1 -1
- package/dist-lib/react-native-host-modules.cjs +1 -1
- package/dist-lib/react-native-host-modules.mjs +1 -1
- package/dist-lib/render-mode.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
- package/dist-lib/sdk.cjs +112 -87
- package/dist-lib/sdk.mjs +111 -87
- package/dist-lib/skills.cjs +1270 -709
- package/dist-lib/swift-compile.cjs +1 -1
- package/dist-lib/swift-compile.mjs +1 -1
- package/dist-lib/swift-package.cjs +1 -1
- package/dist-lib/swift-package.mjs +1 -1
- package/dist-lib/swift-submit.cjs +1 -1
- package/dist-lib/swift-submit.mjs +1 -1
- package/dist-lib/swift.cjs +672 -275
- package/dist-lib/vite-swift.cjs +1 -1
- package/dist-lib/vite.cjs +11 -28
- package/kotlin/README.md +32 -4
- package/kotlin/fixtures/counter/Counter.kt +26 -0
- package/kotlin/fixtures/counter/CounterScreen.kt +40 -0
- package/kotlin/fixtures/headless-counter/Headless.kt +228 -0
- package/kotlin/fixtures/runtime-state/RuntimeState.kt +200 -0
- package/kotlin/fork/LICENSE +178 -0
- package/kotlin/fork/NOTICE +25 -0
- package/kotlin/fork/README.md +59 -0
- package/kotlin/fork/build.sh +128 -0
- package/kotlin/fork/compose-worker-globals.patch +96 -0
- package/kotlin/fork/fork.json +33 -0
- package/kotlin/fork/publish.ts +77 -0
- package/kotlin/fork/skiko-worker-clock.patch +33 -0
- package/kotlin/toolchain.json +288 -0
- package/package.json +13 -5
- package/skills/contrast/SKILL.md +1 -0
- package/src/automatic-update-policy.ts +17 -0
- package/src/home-paths.ts +11 -9
- package/src/host/bridge-host.ts +3 -1
- package/src/{vite-plugin-kotlin.ts → kotlin-host.ts} +155 -12
- package/src/menu.ts +11 -0
- package/src/metro-plugin.ts +18 -0
- package/src/native-app-identity.ts +376 -0
- package/src/native-firebase-config.ts +90 -0
- package/src/screenshots/frame-compose.ts +1 -1
- package/src/screenshots/registry.ts +1 -1
- package/src/screenshots/schema.ts +2 -2
- package/src/vite-plugin-one.ts +11 -28
- package/src/vite-plugin.ts +6 -1
package/dist-lib/sdk.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.631 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -39,6 +39,7 @@ __export(sdk_exports, {
|
|
|
39
39
|
clearConsole: () => clearConsole,
|
|
40
40
|
clearLogs: () => clearLogs,
|
|
41
41
|
clearRequests: () => clearRequests,
|
|
42
|
+
consoleEntriesEval: () => consoleEntriesEval,
|
|
42
43
|
detectOpenNativeUI: () => detectOpenNativeUI,
|
|
43
44
|
filterLogEntries: () => filterLogEntries,
|
|
44
45
|
formatLayoutElements: () => formatLayoutElements,
|
|
@@ -1799,16 +1800,26 @@ function payloadFromResolved(resolved, match, extra = {}) {
|
|
|
1799
1800
|
...extra
|
|
1800
1801
|
};
|
|
1801
1802
|
}
|
|
1802
|
-
function collectTextCandidates(nodes, ancestors = [], pressableOwnerText = "") {
|
|
1803
|
+
function collectTextCandidates(nodes, ancestors = [], pressableOwnerText = "", actionableAncestor = false, actionableRole) {
|
|
1803
1804
|
const out = [];
|
|
1804
1805
|
for (const node of nodes) {
|
|
1805
1806
|
const text = nodeText(node);
|
|
1806
1807
|
const caption = !!text && text === pressableOwnerText && !node.pressable;
|
|
1807
|
-
|
|
1808
|
+
const actionable = !!node.pressable || actionableAncestor;
|
|
1809
|
+
const role = node.role ?? actionableRole;
|
|
1810
|
+
if (!caption) out.push({ node, ancestorTestIDs: ancestors, actionable, role });
|
|
1808
1811
|
if (node.children?.length) {
|
|
1809
1812
|
const next = node.testID ? [...ancestors, node.testID] : ancestors;
|
|
1810
1813
|
const owner = text ? node.pressable ? text : "" : pressableOwnerText;
|
|
1811
|
-
out.push(
|
|
1814
|
+
out.push(
|
|
1815
|
+
...collectTextCandidates(
|
|
1816
|
+
node.children,
|
|
1817
|
+
next,
|
|
1818
|
+
owner,
|
|
1819
|
+
actionable,
|
|
1820
|
+
node.pressable ? node.role : actionableRole
|
|
1821
|
+
)
|
|
1822
|
+
);
|
|
1812
1823
|
}
|
|
1813
1824
|
}
|
|
1814
1825
|
return out;
|
|
@@ -1832,85 +1843,96 @@ async function tapByText(bridge, query, options = {}, opts = {}) {
|
|
|
1832
1843
|
agent: opts.agent,
|
|
1833
1844
|
timing: opts.timing,
|
|
1834
1845
|
textFallback: query,
|
|
1835
|
-
resolve:
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
const ay = node2.geometry.y;
|
|
1849
|
-
if (options.minX != null && !(ax >= options.minX)) return false;
|
|
1850
|
-
if (options.maxX != null && !(ax <= options.maxX)) return false;
|
|
1851
|
-
if (options.minY != null && !(ay >= options.minY)) return false;
|
|
1852
|
-
if (options.maxY != null && !(ay <= options.maxY)) return false;
|
|
1853
|
-
return true;
|
|
1854
|
-
});
|
|
1855
|
-
if (options.near) {
|
|
1856
|
-
const { x, y } = options.near;
|
|
1857
|
-
candidates.sort(
|
|
1858
|
-
(a, b) => Math.hypot(a.node.geometry.x - x, a.node.geometry.y - y) - Math.hypot(b.node.geometry.x - x, b.node.geometry.y - y)
|
|
1859
|
-
);
|
|
1860
|
-
} else {
|
|
1861
|
-
candidates.sort((a, b) => {
|
|
1862
|
-
if (Math.abs(a.node.geometry.y - b.node.geometry.y) > 2) {
|
|
1863
|
-
return a.node.geometry.y - b.node.geometry.y;
|
|
1864
|
-
}
|
|
1865
|
-
return a.node.geometry.x - b.node.geometry.x;
|
|
1866
|
-
});
|
|
1867
|
-
}
|
|
1868
|
-
const total = candidates.length;
|
|
1869
|
-
if (total === 0) return { matched: 0, total: 0 };
|
|
1870
|
-
let idx = 0;
|
|
1871
|
-
if (options.nth != null) {
|
|
1872
|
-
idx = options.nth < 0 ? total + options.nth : options.nth;
|
|
1873
|
-
if (idx < 0 || idx >= total) {
|
|
1874
|
-
return { matched: 0, total, nthOutOfRange: true, nth: options.nth };
|
|
1875
|
-
}
|
|
1876
|
-
} else if (total > 1 && !options.first && !options.near) {
|
|
1877
|
-
return {
|
|
1878
|
-
ambiguous: true,
|
|
1879
|
-
total,
|
|
1880
|
-
candidates: candidates.slice(0, 10).map((candidate, index) => ({
|
|
1881
|
-
idx: index,
|
|
1882
|
-
nodeId: candidate.node.nodeId,
|
|
1883
|
-
type: candidate.node.type,
|
|
1884
|
-
testID: candidate.node.testID ?? null,
|
|
1885
|
-
text: nodeText(candidate.node).slice(0, 60),
|
|
1886
|
-
abs: { x: candidate.node.geometry.x, y: candidate.node.geometry.y },
|
|
1887
|
-
layout: {
|
|
1888
|
-
width: Math.round(candidate.node.geometry.width),
|
|
1889
|
-
height: Math.round(candidate.node.geometry.height)
|
|
1890
|
-
},
|
|
1891
|
-
ancestorTestIDs: candidate.ancestorTestIDs.slice(0, 5)
|
|
1892
|
-
}))
|
|
1893
|
-
};
|
|
1846
|
+
resolve: () => resolveTextTapTarget(bridge, query, options)
|
|
1847
|
+
});
|
|
1848
|
+
}
|
|
1849
|
+
async function resolveTextTapTarget(bridge, query, options = {}) {
|
|
1850
|
+
const { tree } = await inspectTree(bridge, 50);
|
|
1851
|
+
if (!Array.isArray(tree)) return { matched: 0, total: 0 };
|
|
1852
|
+
const needle = options.exact ? query : query.toLowerCase();
|
|
1853
|
+
let candidates = collectTextCandidates(tree).filter(
|
|
1854
|
+
({ node: node2, ancestorTestIDs, role }) => {
|
|
1855
|
+
const text = nodeText(node2);
|
|
1856
|
+
if (!text) return false;
|
|
1857
|
+
if (options.exact ? text !== needle : !text.toLowerCase().includes(needle)) {
|
|
1858
|
+
return false;
|
|
1894
1859
|
}
|
|
1895
|
-
|
|
1896
|
-
if (!
|
|
1897
|
-
const
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
if (!
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1860
|
+
if (options.role && role !== options.role) return false;
|
|
1861
|
+
if (options.within && !ancestorTestIDs.includes(options.within)) return false;
|
|
1862
|
+
const ax = node2.geometry.x;
|
|
1863
|
+
const ay = node2.geometry.y;
|
|
1864
|
+
if (options.minX != null && !(ax >= options.minX)) return false;
|
|
1865
|
+
if (options.maxX != null && !(ax <= options.maxX)) return false;
|
|
1866
|
+
if (options.minY != null && !(ay >= options.minY)) return false;
|
|
1867
|
+
if (options.maxY != null && !(ay <= options.maxY)) return false;
|
|
1868
|
+
return true;
|
|
1869
|
+
}
|
|
1870
|
+
);
|
|
1871
|
+
if (options.nth == null && !options.first && !options.near) {
|
|
1872
|
+
const actionable = candidates.filter((candidate) => candidate.actionable);
|
|
1873
|
+
if (actionable.length > 0) candidates = actionable;
|
|
1874
|
+
}
|
|
1875
|
+
if (options.near) {
|
|
1876
|
+
const { x, y } = options.near;
|
|
1877
|
+
candidates.sort(
|
|
1878
|
+
(a, b) => Math.hypot(a.node.geometry.x - x, a.node.geometry.y - y) - Math.hypot(b.node.geometry.x - x, b.node.geometry.y - y)
|
|
1879
|
+
);
|
|
1880
|
+
} else {
|
|
1881
|
+
candidates.sort((a, b) => {
|
|
1882
|
+
if (Math.abs(a.node.geometry.y - b.node.geometry.y) > 2) {
|
|
1883
|
+
return a.node.geometry.y - b.node.geometry.y;
|
|
1910
1884
|
}
|
|
1911
|
-
return
|
|
1885
|
+
return a.node.geometry.x - b.node.geometry.x;
|
|
1886
|
+
});
|
|
1887
|
+
}
|
|
1888
|
+
const total = candidates.length;
|
|
1889
|
+
if (total === 0) return { matched: 0, total: 0 };
|
|
1890
|
+
let idx = 0;
|
|
1891
|
+
if (options.nth != null) {
|
|
1892
|
+
idx = options.nth < 0 ? total + options.nth : options.nth;
|
|
1893
|
+
if (idx < 0 || idx >= total) {
|
|
1894
|
+
return { matched: 0, total, nthOutOfRange: true, nth: options.nth };
|
|
1912
1895
|
}
|
|
1896
|
+
} else if (total > 1 && !options.first && !options.near) {
|
|
1897
|
+
return {
|
|
1898
|
+
ambiguous: true,
|
|
1899
|
+
total,
|
|
1900
|
+
candidates: candidates.slice(0, 10).map((candidate, index) => ({
|
|
1901
|
+
idx: index,
|
|
1902
|
+
nodeId: candidate.node.nodeId,
|
|
1903
|
+
type: candidate.node.type,
|
|
1904
|
+
testID: candidate.node.testID ?? null,
|
|
1905
|
+
text: nodeText(candidate.node).slice(0, 60),
|
|
1906
|
+
abs: { x: candidate.node.geometry.x, y: candidate.node.geometry.y },
|
|
1907
|
+
layout: {
|
|
1908
|
+
width: Math.round(candidate.node.geometry.width),
|
|
1909
|
+
height: Math.round(candidate.node.geometry.height)
|
|
1910
|
+
},
|
|
1911
|
+
ancestorTestIDs: candidate.ancestorTestIDs.slice(0, 5)
|
|
1912
|
+
}))
|
|
1913
|
+
};
|
|
1914
|
+
}
|
|
1915
|
+
const picked = candidates[idx];
|
|
1916
|
+
if (!picked) return { matched: 0, total };
|
|
1917
|
+
const node = picked.node;
|
|
1918
|
+
const tap = await bridge.send({
|
|
1919
|
+
type: "call",
|
|
1920
|
+
path: "SootSim.bridges.test.resolveTapTarget",
|
|
1921
|
+
args: [node.nodeId]
|
|
1913
1922
|
});
|
|
1923
|
+
if (!isTapTargetInfo(tap)) return { matched: 0, total };
|
|
1924
|
+
return payloadFromResolved(
|
|
1925
|
+
{
|
|
1926
|
+
x: tap.cx,
|
|
1927
|
+
y: tap.cy,
|
|
1928
|
+
nodeId: tap.target.nodeId,
|
|
1929
|
+
testID: tap.target.testID,
|
|
1930
|
+
text: tap.target.text ?? tap.target.accessibilityLabel,
|
|
1931
|
+
type: tap.target.type
|
|
1932
|
+
},
|
|
1933
|
+
node,
|
|
1934
|
+
{ total, idx }
|
|
1935
|
+
);
|
|
1914
1936
|
}
|
|
1915
1937
|
var TAP_QUERY_FINDERS = {
|
|
1916
1938
|
label: "findByLabel",
|
|
@@ -1974,17 +1996,19 @@ async function tapBest(bridge, query, opts = {}) {
|
|
|
1974
1996
|
}
|
|
1975
1997
|
};
|
|
1976
1998
|
}
|
|
1977
|
-
const byText = await
|
|
1978
|
-
if (byText) {
|
|
1999
|
+
const byText = await resolveTextTapTarget(bridge, query);
|
|
2000
|
+
if ("ambiguous" in byText) {
|
|
2001
|
+
return {
|
|
2002
|
+
ambiguous: true,
|
|
2003
|
+
error: `multiple text matches for "${query}"; use tap-text with --nth, --within or --near`
|
|
2004
|
+
};
|
|
2005
|
+
}
|
|
2006
|
+
if ("cx" in byText && typeof byText.cx === "number") {
|
|
1979
2007
|
return {
|
|
1980
|
-
...
|
|
2008
|
+
...byText,
|
|
1981
2009
|
strategy: "text",
|
|
1982
2010
|
node: {
|
|
1983
|
-
|
|
1984
|
-
id: byText.testID ?? null,
|
|
1985
|
-
testID: byText.testID ?? null,
|
|
1986
|
-
type: byText.type ?? null,
|
|
1987
|
-
text: byText.text ?? query
|
|
2011
|
+
...byText.target
|
|
1988
2012
|
}
|
|
1989
2013
|
};
|
|
1990
2014
|
}
|
|
@@ -2491,6 +2515,7 @@ var SimClient = class {
|
|
|
2491
2515
|
clearConsole,
|
|
2492
2516
|
clearLogs,
|
|
2493
2517
|
clearRequests,
|
|
2518
|
+
consoleEntriesEval,
|
|
2494
2519
|
detectOpenNativeUI,
|
|
2495
2520
|
filterLogEntries,
|
|
2496
2521
|
formatLayoutElements,
|
package/dist-lib/sdk.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.631 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
4
4
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
@@ -1701,16 +1701,26 @@ function payloadFromResolved(resolved, match, extra = {}) {
|
|
|
1701
1701
|
...extra
|
|
1702
1702
|
};
|
|
1703
1703
|
}
|
|
1704
|
-
function collectTextCandidates(nodes, ancestors = [], pressableOwnerText = "") {
|
|
1704
|
+
function collectTextCandidates(nodes, ancestors = [], pressableOwnerText = "", actionableAncestor = false, actionableRole) {
|
|
1705
1705
|
const out = [];
|
|
1706
1706
|
for (const node of nodes) {
|
|
1707
1707
|
const text = nodeText(node);
|
|
1708
1708
|
const caption = !!text && text === pressableOwnerText && !node.pressable;
|
|
1709
|
-
|
|
1709
|
+
const actionable = !!node.pressable || actionableAncestor;
|
|
1710
|
+
const role = node.role ?? actionableRole;
|
|
1711
|
+
if (!caption) out.push({ node, ancestorTestIDs: ancestors, actionable, role });
|
|
1710
1712
|
if (node.children?.length) {
|
|
1711
1713
|
const next = node.testID ? [...ancestors, node.testID] : ancestors;
|
|
1712
1714
|
const owner = text ? node.pressable ? text : "" : pressableOwnerText;
|
|
1713
|
-
out.push(
|
|
1715
|
+
out.push(
|
|
1716
|
+
...collectTextCandidates(
|
|
1717
|
+
node.children,
|
|
1718
|
+
next,
|
|
1719
|
+
owner,
|
|
1720
|
+
actionable,
|
|
1721
|
+
node.pressable ? node.role : actionableRole
|
|
1722
|
+
)
|
|
1723
|
+
);
|
|
1714
1724
|
}
|
|
1715
1725
|
}
|
|
1716
1726
|
return out;
|
|
@@ -1734,85 +1744,96 @@ async function tapByText(bridge, query, options = {}, opts = {}) {
|
|
|
1734
1744
|
agent: opts.agent,
|
|
1735
1745
|
timing: opts.timing,
|
|
1736
1746
|
textFallback: query,
|
|
1737
|
-
resolve:
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
const ay = node2.geometry.y;
|
|
1751
|
-
if (options.minX != null && !(ax >= options.minX)) return false;
|
|
1752
|
-
if (options.maxX != null && !(ax <= options.maxX)) return false;
|
|
1753
|
-
if (options.minY != null && !(ay >= options.minY)) return false;
|
|
1754
|
-
if (options.maxY != null && !(ay <= options.maxY)) return false;
|
|
1755
|
-
return true;
|
|
1756
|
-
});
|
|
1757
|
-
if (options.near) {
|
|
1758
|
-
const { x, y } = options.near;
|
|
1759
|
-
candidates.sort(
|
|
1760
|
-
(a, b) => Math.hypot(a.node.geometry.x - x, a.node.geometry.y - y) - Math.hypot(b.node.geometry.x - x, b.node.geometry.y - y)
|
|
1761
|
-
);
|
|
1762
|
-
} else {
|
|
1763
|
-
candidates.sort((a, b) => {
|
|
1764
|
-
if (Math.abs(a.node.geometry.y - b.node.geometry.y) > 2) {
|
|
1765
|
-
return a.node.geometry.y - b.node.geometry.y;
|
|
1766
|
-
}
|
|
1767
|
-
return a.node.geometry.x - b.node.geometry.x;
|
|
1768
|
-
});
|
|
1769
|
-
}
|
|
1770
|
-
const total = candidates.length;
|
|
1771
|
-
if (total === 0) return { matched: 0, total: 0 };
|
|
1772
|
-
let idx = 0;
|
|
1773
|
-
if (options.nth != null) {
|
|
1774
|
-
idx = options.nth < 0 ? total + options.nth : options.nth;
|
|
1775
|
-
if (idx < 0 || idx >= total) {
|
|
1776
|
-
return { matched: 0, total, nthOutOfRange: true, nth: options.nth };
|
|
1777
|
-
}
|
|
1778
|
-
} else if (total > 1 && !options.first && !options.near) {
|
|
1779
|
-
return {
|
|
1780
|
-
ambiguous: true,
|
|
1781
|
-
total,
|
|
1782
|
-
candidates: candidates.slice(0, 10).map((candidate, index) => ({
|
|
1783
|
-
idx: index,
|
|
1784
|
-
nodeId: candidate.node.nodeId,
|
|
1785
|
-
type: candidate.node.type,
|
|
1786
|
-
testID: candidate.node.testID ?? null,
|
|
1787
|
-
text: nodeText(candidate.node).slice(0, 60),
|
|
1788
|
-
abs: { x: candidate.node.geometry.x, y: candidate.node.geometry.y },
|
|
1789
|
-
layout: {
|
|
1790
|
-
width: Math.round(candidate.node.geometry.width),
|
|
1791
|
-
height: Math.round(candidate.node.geometry.height)
|
|
1792
|
-
},
|
|
1793
|
-
ancestorTestIDs: candidate.ancestorTestIDs.slice(0, 5)
|
|
1794
|
-
}))
|
|
1795
|
-
};
|
|
1747
|
+
resolve: () => resolveTextTapTarget(bridge, query, options)
|
|
1748
|
+
});
|
|
1749
|
+
}
|
|
1750
|
+
async function resolveTextTapTarget(bridge, query, options = {}) {
|
|
1751
|
+
const { tree } = await inspectTree(bridge, 50);
|
|
1752
|
+
if (!Array.isArray(tree)) return { matched: 0, total: 0 };
|
|
1753
|
+
const needle = options.exact ? query : query.toLowerCase();
|
|
1754
|
+
let candidates = collectTextCandidates(tree).filter(
|
|
1755
|
+
({ node: node2, ancestorTestIDs, role }) => {
|
|
1756
|
+
const text = nodeText(node2);
|
|
1757
|
+
if (!text) return false;
|
|
1758
|
+
if (options.exact ? text !== needle : !text.toLowerCase().includes(needle)) {
|
|
1759
|
+
return false;
|
|
1796
1760
|
}
|
|
1797
|
-
|
|
1798
|
-
if (!
|
|
1799
|
-
const
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
if (!
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1761
|
+
if (options.role && role !== options.role) return false;
|
|
1762
|
+
if (options.within && !ancestorTestIDs.includes(options.within)) return false;
|
|
1763
|
+
const ax = node2.geometry.x;
|
|
1764
|
+
const ay = node2.geometry.y;
|
|
1765
|
+
if (options.minX != null && !(ax >= options.minX)) return false;
|
|
1766
|
+
if (options.maxX != null && !(ax <= options.maxX)) return false;
|
|
1767
|
+
if (options.minY != null && !(ay >= options.minY)) return false;
|
|
1768
|
+
if (options.maxY != null && !(ay <= options.maxY)) return false;
|
|
1769
|
+
return true;
|
|
1770
|
+
}
|
|
1771
|
+
);
|
|
1772
|
+
if (options.nth == null && !options.first && !options.near) {
|
|
1773
|
+
const actionable = candidates.filter((candidate) => candidate.actionable);
|
|
1774
|
+
if (actionable.length > 0) candidates = actionable;
|
|
1775
|
+
}
|
|
1776
|
+
if (options.near) {
|
|
1777
|
+
const { x, y } = options.near;
|
|
1778
|
+
candidates.sort(
|
|
1779
|
+
(a, b) => Math.hypot(a.node.geometry.x - x, a.node.geometry.y - y) - Math.hypot(b.node.geometry.x - x, b.node.geometry.y - y)
|
|
1780
|
+
);
|
|
1781
|
+
} else {
|
|
1782
|
+
candidates.sort((a, b) => {
|
|
1783
|
+
if (Math.abs(a.node.geometry.y - b.node.geometry.y) > 2) {
|
|
1784
|
+
return a.node.geometry.y - b.node.geometry.y;
|
|
1812
1785
|
}
|
|
1813
|
-
return
|
|
1786
|
+
return a.node.geometry.x - b.node.geometry.x;
|
|
1787
|
+
});
|
|
1788
|
+
}
|
|
1789
|
+
const total = candidates.length;
|
|
1790
|
+
if (total === 0) return { matched: 0, total: 0 };
|
|
1791
|
+
let idx = 0;
|
|
1792
|
+
if (options.nth != null) {
|
|
1793
|
+
idx = options.nth < 0 ? total + options.nth : options.nth;
|
|
1794
|
+
if (idx < 0 || idx >= total) {
|
|
1795
|
+
return { matched: 0, total, nthOutOfRange: true, nth: options.nth };
|
|
1814
1796
|
}
|
|
1797
|
+
} else if (total > 1 && !options.first && !options.near) {
|
|
1798
|
+
return {
|
|
1799
|
+
ambiguous: true,
|
|
1800
|
+
total,
|
|
1801
|
+
candidates: candidates.slice(0, 10).map((candidate, index) => ({
|
|
1802
|
+
idx: index,
|
|
1803
|
+
nodeId: candidate.node.nodeId,
|
|
1804
|
+
type: candidate.node.type,
|
|
1805
|
+
testID: candidate.node.testID ?? null,
|
|
1806
|
+
text: nodeText(candidate.node).slice(0, 60),
|
|
1807
|
+
abs: { x: candidate.node.geometry.x, y: candidate.node.geometry.y },
|
|
1808
|
+
layout: {
|
|
1809
|
+
width: Math.round(candidate.node.geometry.width),
|
|
1810
|
+
height: Math.round(candidate.node.geometry.height)
|
|
1811
|
+
},
|
|
1812
|
+
ancestorTestIDs: candidate.ancestorTestIDs.slice(0, 5)
|
|
1813
|
+
}))
|
|
1814
|
+
};
|
|
1815
|
+
}
|
|
1816
|
+
const picked = candidates[idx];
|
|
1817
|
+
if (!picked) return { matched: 0, total };
|
|
1818
|
+
const node = picked.node;
|
|
1819
|
+
const tap = await bridge.send({
|
|
1820
|
+
type: "call",
|
|
1821
|
+
path: "SootSim.bridges.test.resolveTapTarget",
|
|
1822
|
+
args: [node.nodeId]
|
|
1815
1823
|
});
|
|
1824
|
+
if (!isTapTargetInfo(tap)) return { matched: 0, total };
|
|
1825
|
+
return payloadFromResolved(
|
|
1826
|
+
{
|
|
1827
|
+
x: tap.cx,
|
|
1828
|
+
y: tap.cy,
|
|
1829
|
+
nodeId: tap.target.nodeId,
|
|
1830
|
+
testID: tap.target.testID,
|
|
1831
|
+
text: tap.target.text ?? tap.target.accessibilityLabel,
|
|
1832
|
+
type: tap.target.type
|
|
1833
|
+
},
|
|
1834
|
+
node,
|
|
1835
|
+
{ total, idx }
|
|
1836
|
+
);
|
|
1816
1837
|
}
|
|
1817
1838
|
var TAP_QUERY_FINDERS = {
|
|
1818
1839
|
label: "findByLabel",
|
|
@@ -1876,17 +1897,19 @@ async function tapBest(bridge, query, opts = {}) {
|
|
|
1876
1897
|
}
|
|
1877
1898
|
};
|
|
1878
1899
|
}
|
|
1879
|
-
const byText = await
|
|
1880
|
-
if (byText) {
|
|
1900
|
+
const byText = await resolveTextTapTarget(bridge, query);
|
|
1901
|
+
if ("ambiguous" in byText) {
|
|
1902
|
+
return {
|
|
1903
|
+
ambiguous: true,
|
|
1904
|
+
error: `multiple text matches for "${query}"; use tap-text with --nth, --within or --near`
|
|
1905
|
+
};
|
|
1906
|
+
}
|
|
1907
|
+
if ("cx" in byText && typeof byText.cx === "number") {
|
|
1881
1908
|
return {
|
|
1882
|
-
...
|
|
1909
|
+
...byText,
|
|
1883
1910
|
strategy: "text",
|
|
1884
1911
|
node: {
|
|
1885
|
-
|
|
1886
|
-
id: byText.testID ?? null,
|
|
1887
|
-
testID: byText.testID ?? null,
|
|
1888
|
-
type: byText.type ?? null,
|
|
1889
|
-
text: byText.text ?? query
|
|
1912
|
+
...byText.target
|
|
1890
1913
|
}
|
|
1891
1914
|
};
|
|
1892
1915
|
}
|
|
@@ -2392,6 +2415,7 @@ export {
|
|
|
2392
2415
|
clearConsole,
|
|
2393
2416
|
clearLogs,
|
|
2394
2417
|
clearRequests,
|
|
2418
|
+
consoleEntriesEval,
|
|
2395
2419
|
detectOpenNativeUI,
|
|
2396
2420
|
filterLogEntries,
|
|
2397
2421
|
formatLayoutElements,
|