rnxsim 0.1.366 → 0.1.367
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 +14 -2
- package/cli/bin.ts +18 -0
- package/cli/bridge-flow-runner.ts +14 -151
- package/cli/commands/assert.ts +1 -1
- package/cli/commands/box/checkout-plane.ts +126 -0
- package/cli/commands/box/rnx-command.ts +68 -0
- package/cli/commands/box.ts +200 -0
- package/cli/commands/control.ts +12 -32
- package/cli/commands/do-chain.ts +412 -0
- package/cli/commands/flow.ts +0 -10
- package/cli/commands/inspect/core.ts +302 -725
- package/cli/commands/inspect/describe.ts +3 -76
- package/cli/commands/inspect/find.ts +3 -6
- package/cli/commands/inspect/get-layout.ts +14 -1
- package/cli/commands/inspect/resolve-target.ts +41 -47
- package/cli/commands/inspect/shared.ts +28 -123
- package/cli/commands/inspect/tree.ts +6 -15
- package/cli/commands/inspect/wait-ready.ts +2 -2
- package/cli/commands/inspect/wait-selector.ts +4 -8
- package/cli/commands/inspect.ts +23 -17
- package/cli/commands/install-desktop.ts +44 -7
- package/cli/commands/maestro-generate.ts +0 -2
- package/cli/commands/platform.ts +68 -0
- package/cli/commands/state.ts +5 -2
- package/cli/outbound-endpoints.ts +6 -1
- package/cli/parse-args.ts +13 -2
- package/cli/ws-bridge.ts +30 -2
- package/detox/index.ts +34 -17
- package/dist-cli/bin.js +5 -5
- package/dist-cli/chunks/{agent-SO6IOPRK.js → agent-EKKIAKP5.js} +2 -2
- package/dist-cli/chunks/{agent-wrapper-PMYCB2KO.js → agent-wrapper-MHLQUGA6.js} +2 -2
- package/dist-cli/chunks/{app-fonts-LULQGFY7.js → app-fonts-HN6ADRTQ.js} +2 -2
- package/dist-cli/chunks/{assert-WGNCD4ZU.js → assert-IMR6QCN6.js} +3 -3
- package/dist-cli/chunks/{auth-G7CCHH3M.js → auth-27LPMQNH.js} +2 -2
- package/dist-cli/chunks/{beta-N4LXL5TO.js → beta-6RXBB5AI.js} +2 -2
- package/dist-cli/chunks/box-DORLF3GP.js +1709 -0
- package/dist-cli/chunks/{camera-4JHCYBQS.js → camera-2G7JZ5DL.js} +2 -2
- package/dist-cli/chunks/{chunk-2UXU53MG.js → chunk-3QZJLNYY.js} +1 -1
- package/dist-cli/chunks/{chunk-YXXKSL6R.js → chunk-3VBVEDDA.js} +1 -1
- package/dist-cli/chunks/{chunk-ZJIS3CTW.js → chunk-4BWGLTKU.js} +2 -2
- package/dist-cli/chunks/chunk-4XSNO374.js +423 -0
- package/dist-cli/chunks/{chunk-RYIMSKDO.js → chunk-5DGMFUZG.js} +2 -2
- package/dist-cli/chunks/{chunk-M3BNCF7E.js → chunk-6DBS5EGX.js} +2 -2
- package/dist-cli/chunks/{chunk-ONORMKWX.js → chunk-6SO6I3D5.js} +2 -2
- package/dist-cli/chunks/chunk-75EFOPXE.js +32 -0
- package/dist-cli/chunks/{chunk-5NMCF5VF.js → chunk-A5AJFVOF.js} +1 -1
- package/dist-cli/chunks/{chunk-N3UC7HIX.js → chunk-AM2ODYAN.js} +1 -1
- package/dist-cli/chunks/{chunk-I6KIM6PN.js → chunk-AVOROGA3.js} +1 -1
- package/dist-cli/chunks/chunk-AXMMLO7D.js +130 -0
- package/dist-cli/chunks/{chunk-ORWP2E7R.js → chunk-BSEXEYNX.js} +2 -2
- package/dist-cli/chunks/{chunk-NQFJOCSF.js → chunk-CM6ZKZW7.js} +2 -2
- package/dist-cli/chunks/{chunk-ZMJ75EDG.js → chunk-DNVABCEC.js} +2 -2
- package/dist-cli/chunks/{chunk-2S7BKUP2.js → chunk-E33A4AIR.js} +2 -2
- package/dist-cli/chunks/{chunk-GXAB52K7.js → chunk-EFMOXYDD.js} +2 -2
- package/dist-cli/chunks/{chunk-TIAEYO6I.js → chunk-EV4P5UBP.js} +2 -2
- package/dist-cli/chunks/{chunk-JHXETDVC.js → chunk-G5IUIGGC.js} +1 -1
- package/dist-cli/chunks/chunk-GMLOCV3E.js +312 -0
- package/dist-cli/chunks/{chunk-ZLVRYXZ7.js → chunk-GRN2FHVW.js} +1 -1
- package/dist-cli/chunks/{chunk-A5FXX5PJ.js → chunk-HFFXPUEP.js} +1 -1
- package/dist-cli/chunks/chunk-HG6T4OOR.js +4 -0
- package/dist-cli/chunks/{chunk-OW5XHAA4.js → chunk-HNT2OXPZ.js} +1 -1
- package/dist-cli/chunks/chunk-IPEURNLI.js +24 -0
- package/dist-cli/chunks/chunk-IR44SFBA.js +5 -0
- package/dist-cli/chunks/chunk-JMSLXZFW.js +4 -0
- package/dist-cli/chunks/{chunk-EFR5DVNP.js → chunk-JPXQKLNH.js} +1 -1
- package/dist-cli/chunks/chunk-K3EC6JDE.js +4 -0
- package/dist-cli/chunks/{chunk-YGMEDJKJ.js → chunk-KHX65HAU.js} +2 -2
- package/dist-cli/chunks/{chunk-EQK5MJCU.js → chunk-LWOVKHRC.js} +2 -2
- package/dist-cli/chunks/{chunk-5OK3LCEJ.js → chunk-MQN5DWTI.js} +2 -2
- package/dist-cli/chunks/{chunk-TTASWLKM.js → chunk-N3BXQEUF.js} +1 -1
- package/dist-cli/chunks/{chunk-UCHW7ZHR.js → chunk-N5OSV26B.js} +2 -2
- package/dist-cli/chunks/chunk-PHNURIH3.js +4 -0
- package/dist-cli/chunks/{chunk-AFV32WZT.js → chunk-PP2KNVXP.js} +3 -3
- package/dist-cli/chunks/{chunk-XJU2QIRY.js → chunk-PROKNJVY.js} +2 -2
- package/dist-cli/chunks/chunk-QXLLY4IM.js +7 -0
- package/dist-cli/chunks/{chunk-HT6GFZOZ.js → chunk-RNQTOGM7.js} +1 -1
- package/dist-cli/chunks/{chunk-CN6XYYEA.js → chunk-RPNKKVBN.js} +27 -27
- package/dist-cli/chunks/{chunk-LN3RNW6X.js → chunk-RSNLI2NH.js} +2 -2
- package/dist-cli/chunks/{chunk-YCCCPFWT.js → chunk-RZGHV2WI.js} +2 -2
- package/dist-cli/chunks/chunk-SGGL3AFV.js +497 -0
- package/dist-cli/chunks/chunk-SH5ETJKG.js +4 -0
- package/dist-cli/chunks/{chunk-77ZHQJZR.js → chunk-T6J4YP56.js} +2 -2
- package/dist-cli/chunks/{chunk-MGDSJZ37.js → chunk-TWUE7WF5.js} +2 -2
- package/dist-cli/chunks/{chunk-SA6XVI55.js → chunk-U44VEQRN.js} +1 -1
- package/dist-cli/chunks/{chunk-IZKHOM37.js → chunk-URLVQHC3.js} +1 -1
- package/dist-cli/chunks/{chunk-GAJQSITE.js → chunk-VANZFTUE.js} +1 -1
- package/dist-cli/chunks/{chunk-EGAGZHO6.js → chunk-VB2YBH46.js} +1 -1
- package/dist-cli/chunks/{chunk-H23KWIKA.js → chunk-VFU2SJ7M.js} +2 -2
- package/dist-cli/chunks/{chunk-34IQM22N.js → chunk-VUBJ6FXU.js} +15 -15
- package/dist-cli/chunks/{chunk-NM2L2QV4.js → chunk-W5PATILQ.js} +2 -2
- package/dist-cli/chunks/chunk-WI5V4TYG.js +38 -0
- package/dist-cli/chunks/{chunk-6PL2CCGP.js → chunk-XTRHQAOV.js} +2 -2
- package/dist-cli/chunks/chunk-XWUTJLBM.js +27 -0
- package/dist-cli/chunks/{chunk-5DAIA77C.js → chunk-XYRUQQUR.js} +2 -2
- package/dist-cli/chunks/chunk-YBFB3RM7.js +4 -0
- package/dist-cli/chunks/chunk-YGQVQ2VG.js +4 -0
- package/dist-cli/chunks/{chunk-CTG7HBCA.js → chunk-YINACV32.js} +1 -1
- package/dist-cli/chunks/chunk-YV7ERO2V.js +5 -0
- package/dist-cli/chunks/{cleanup-KRIATKFH.js → cleanup-GVFHQ3KH.js} +2 -2
- package/dist-cli/chunks/cli-version-DPX5MA2M.js +4 -0
- package/dist-cli/chunks/{compat-6PTQ3QEA.js → compat-AYRTXYAM.js} +2 -2
- package/dist-cli/chunks/{config-ETGDUANO.js → config-K7LOOV62.js} +2 -2
- package/dist-cli/chunks/connect-H3CNY4IO.js +4 -0
- package/dist-cli/chunks/control-SMHZ2CF5.js +4 -0
- package/dist-cli/chunks/daemon-TEZUNI73.js +4 -0
- package/dist-cli/chunks/{debug-WIIB6ZKC.js → debug-ELGHG57T.js} +3 -3
- package/dist-cli/chunks/{desktop-TD7Q5LNB.js → desktop-4G7FK3MP.js} +3 -3
- package/dist-cli/chunks/{detox-7GNR33VM.js → detox-JKXGEJIZ.js} +2 -2
- package/dist-cli/chunks/{device-QOR3PGUF.js → device-IV3AR6KG.js} +2 -2
- package/dist-cli/chunks/{diagnose-RRCBLP6E.js → diagnose-3ERGF7EW.js} +2 -2
- package/dist-cli/chunks/{disk-cleanup-MJUGJPXT.js → disk-cleanup-ALFEVZ4B.js} +2 -2
- package/dist-cli/chunks/do-chain-K2B4OHM7.js +5 -0
- package/dist-cli/chunks/drivers-KJYXA3M3.js +4 -0
- package/dist-cli/chunks/env-2KF6IYZE.js +4 -0
- package/dist-cli/chunks/{film-D7Y64LID.js → film-UQXJFAD5.js} +3 -3
- package/dist-cli/chunks/flow-ASTUH56E.js +4 -0
- package/dist-cli/chunks/help-SNNYFELA.js +4 -0
- package/dist-cli/chunks/{hidden-runtime-alias-JGURVR2Z.js → hidden-runtime-alias-VJCWCFVO.js} +2 -2
- package/dist-cli/chunks/{home-paths-4ICFIGXO.js → home-paths-EGUZZ6J6.js} +2 -2
- package/dist-cli/chunks/inspect-XANAADQT.js +4 -0
- package/dist-cli/chunks/install-desktop-JKQA7UY6.js +4 -0
- package/dist-cli/chunks/{login-RU33QR2G.js → login-6KQGD7HK.js} +2 -2
- package/dist-cli/chunks/{logout-2IUG3Y5J.js → logout-2FCOVLBC.js} +2 -2
- package/dist-cli/chunks/{maestro-2MXTGG7I.js → maestro-HPSSEVJD.js} +3 -3
- package/dist-cli/chunks/{maestro-generate-2PYIFGLR.js → maestro-generate-KQQ6JHMR.js} +5 -5
- package/dist-cli/chunks/{mode-3DAPG3HF.js → mode-SH7KRZET.js} +2 -2
- package/dist-cli/chunks/{optional-demo-registry-BWSDJF2Z.js → optional-demo-registry-NAMCAVFX.js} +2 -2
- package/dist-cli/chunks/{perf-2TKPWB4N.js → perf-LWKAHXUL.js} +2 -2
- package/dist-cli/chunks/{permissions-5CSMHYNA.js → permissions-EZTKWTXV.js} +2 -2
- package/dist-cli/chunks/platform-KSOJIVN5.js +6 -0
- package/dist-cli/chunks/privacy-EJULWP7L.js +4 -0
- package/dist-cli/chunks/{record-I2HJCFP3.js → record-2IC7BAQN.js} +3 -3
- package/dist-cli/chunks/{report-issue-A4FZWBOC.js → report-issue-MMX6HOQO.js} +2 -2
- package/dist-cli/chunks/reset-PKFZA4NC.js +4 -0
- package/dist-cli/chunks/resolve-target-AFKU3BRE.js +4 -0
- package/dist-cli/chunks/runtime-XHWWPOIN.js +4 -0
- package/dist-cli/chunks/{screenshot-command-UFW5BFHP.js → screenshot-command-JYUXDVSA.js} +3 -3
- package/dist-cli/chunks/{screenshot-layers-5BIAL4YN.js → screenshot-layers-OGRY6AA3.js} +2 -2
- package/dist-cli/chunks/{screenshots-capture-EHYS3BV2.js → screenshots-capture-QHDN65XS.js} +2 -2
- package/dist-cli/chunks/{serve-O4FDM3AC.js → serve-W4BKAG4J.js} +3 -3
- package/dist-cli/chunks/setup-WJPMPSAC.js +4 -0
- package/dist-cli/chunks/sim-client-UOQGFBPO.js +4 -0
- package/dist-cli/chunks/{skills-ZK55SY32.js → skills-OPPXOD63.js} +2 -2
- package/dist-cli/chunks/state-TDAN7BOM.js +14 -0
- package/dist-cli/chunks/{storage-WP33ISEM.js → storage-CFB2YGLY.js} +2 -2
- package/dist-cli/chunks/store-4TEED4ZU.js +4 -0
- package/dist-cli/chunks/telemetry-S6N7CV6G.js +4 -0
- package/dist-cli/chunks/{timeline-DKGW5S4I.js → timeline-ASOOVP4C.js} +2 -2
- package/dist-cli/chunks/{upgrade-LL7G4B7G.js → upgrade-6MVJISHG.js} +2 -2
- package/dist-cli/chunks/upload-CBNIRFEW.js +4 -0
- package/dist-cli/chunks/{version-YLWR5G4F.js → version-5GS7CWRL.js} +2 -2
- package/dist-cli/chunks/{web-QELZ4CP6.js → web-QRRMYMV4.js} +2 -2
- package/dist-cli/chunks/welcome-33FXMAMB.js +5 -0
- package/dist-cli/chunks/{what-happened-MIOMZWM5.js → what-happened-GAZJGKXX.js} +2 -2
- package/dist-cli/chunks/ws-bridge-DIXUAPLQ.js +4 -0
- 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 +336 -0
- package/dist-lib/bridge-contract-input.mjs +303 -0
- package/dist-lib/bridge-contract.cjs +29 -1
- package/dist-lib/bridge-contract.mjs +19 -0
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +32 -18
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +5 -1
- 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 +214 -594
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.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 +1 -1
- 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 +273 -657
- package/dist-lib/sdk.mjs +271 -652
- package/dist-lib/skills.cjs +190 -287
- package/dist-lib/vite.cjs +1 -1
- package/package.json +9 -6
- package/skills/contrast/SKILL.md +3 -0
- package/skills/rnx-debug/SKILL.md +8 -0
- package/skills/rnx-test/SKILL.md +21 -0
- package/src/bridge-contract-input.ts +359 -0
- package/src/bridge-contract.ts +121 -1
- package/src/host/bridge-host.ts +7 -12
- package/src/index.ts +7 -0
- package/src/sim-client.ts +12 -3
- package/dist-cli/chunks/chunk-3X6IBUBX.js +0 -38
- package/dist-cli/chunks/chunk-7B37IZBN.js +0 -15
- package/dist-cli/chunks/chunk-DMPF2D24.js +0 -4
- package/dist-cli/chunks/chunk-DW37EVX5.js +0 -767
- package/dist-cli/chunks/chunk-DWFIH7CB.js +0 -32
- package/dist-cli/chunks/chunk-GUI3PDH2.js +0 -27
- package/dist-cli/chunks/chunk-KLF3K6ZZ.js +0 -4
- package/dist-cli/chunks/chunk-M5QCGLXR.js +0 -944
- package/dist-cli/chunks/chunk-M6SKQGAN.js +0 -5
- package/dist-cli/chunks/chunk-NAYLPOC5.js +0 -24
- package/dist-cli/chunks/chunk-VIND4SP3.js +0 -124
- package/dist-cli/chunks/chunk-XXNLYR64.js +0 -5
- package/dist-cli/chunks/chunk-ZFM24DOX.js +0 -4
- package/dist-cli/chunks/cli-version-SYGNHGFR.js +0 -4
- package/dist-cli/chunks/control-RBTNOLQM.js +0 -4
- package/dist-cli/chunks/daemon-N3SC5DK6.js +0 -4
- package/dist-cli/chunks/drivers-W6E3BZH4.js +0 -4
- package/dist-cli/chunks/flow-7QAE2V5Y.js +0 -4
- package/dist-cli/chunks/help-NLD6KWVG.js +0 -4
- package/dist-cli/chunks/inspect-C347BG52.js +0 -4
- package/dist-cli/chunks/install-desktop-YR6TWZYK.js +0 -4
- package/dist-cli/chunks/privacy-PWWC2BXO.js +0 -4
- package/dist-cli/chunks/reset-Y3PBPFYA.js +0 -4
- package/dist-cli/chunks/runtime-IC6NVVGA.js +0 -4
- package/dist-cli/chunks/setup-Y4QGSJBM.js +0 -4
- package/dist-cli/chunks/state-KFLAZNEY.js +0 -14
- package/dist-cli/chunks/store-VEE22CAY.js +0 -4
- package/dist-cli/chunks/telemetry-PZV4BISJ.js +0 -4
- package/dist-cli/chunks/upload-Y4C3YZDK.js +0 -4
- package/dist-cli/chunks/welcome-ZPKA5PZJ.js +0 -5
- package/dist-lib/mcp.cjs +0 -673
- package/dist-lib/mcp.mjs +0 -641
- package/src/mcp.ts +0 -791
package/dist-lib/sdk.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.367 | (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;
|
|
@@ -26,9 +26,6 @@ __export(sdk_exports, {
|
|
|
26
26
|
LAYOUT_GET_NATIVE_EVAL: () => LAYOUT_GET_NATIVE_EVAL,
|
|
27
27
|
MERGED_CONSOLE_COUNT_EVAL: () => MERGED_CONSOLE_COUNT_EVAL,
|
|
28
28
|
NOISY_TIMELINE_KINDS: () => NOISY_TIMELINE_KINDS,
|
|
29
|
-
READY_CONTENT_NODE_FLOOR: () => READY_CONTENT_NODE_FLOOR,
|
|
30
|
-
READY_NODE_STABLE_MS: () => READY_NODE_STABLE_MS,
|
|
31
|
-
READY_PROBE_POLL_MS: () => READY_PROBE_POLL_MS,
|
|
32
29
|
SimClient: () => SimClient,
|
|
33
30
|
SimClientError: () => SimClientError,
|
|
34
31
|
SimConnectError: () => SimConnectError,
|
|
@@ -37,8 +34,6 @@ __export(sdk_exports, {
|
|
|
37
34
|
SimReadyTimeoutError: () => SimReadyTimeoutError,
|
|
38
35
|
SimResetError: () => SimResetError,
|
|
39
36
|
SimTapError: () => SimTapError,
|
|
40
|
-
WAIT_READY_PROBE: () => WAIT_READY_PROBE,
|
|
41
|
-
WAIT_READY_PROGRESS_INTERVAL_MS: () => WAIT_READY_PROGRESS_INTERVAL_MS,
|
|
42
37
|
buildSimOpenUrl: () => buildSimOpenUrl,
|
|
43
38
|
clearConsole: () => clearConsole,
|
|
44
39
|
clearLogs: () => clearLogs,
|
|
@@ -46,6 +41,7 @@ __export(sdk_exports, {
|
|
|
46
41
|
detectOpenNativeUI: () => detectOpenNativeUI,
|
|
47
42
|
filterLogEntries: () => filterLogEntries,
|
|
48
43
|
formatLayoutElements: () => formatLayoutElements,
|
|
44
|
+
formatSemanticTree: () => formatSemanticTree,
|
|
49
45
|
formatTimelineEvent: () => formatTimelineEvent,
|
|
50
46
|
formatTimelineEvents: () => formatTimelineEvents,
|
|
51
47
|
formatTimelinePayload: () => formatTimelinePayload,
|
|
@@ -78,6 +74,7 @@ __export(sdk_exports, {
|
|
|
78
74
|
isInspectModeActive: () => isInspectModeActive,
|
|
79
75
|
isInspectPickCommand: () => isInspectPickCommand,
|
|
80
76
|
isShellCommandUnavailable: () => isShellCommandUnavailable,
|
|
77
|
+
isSimSemanticTree: () => isSimSemanticTree,
|
|
81
78
|
isTapSuccess: () => isTapSuccess,
|
|
82
79
|
layoutGetNativeEval: () => layoutGetNativeEval,
|
|
83
80
|
rankInteractive: () => rankInteractive,
|
|
@@ -751,6 +748,15 @@ async function tapBest(bridge, query, opts = {}) {
|
|
|
751
748
|
});
|
|
752
749
|
}
|
|
753
750
|
|
|
751
|
+
// src/bridge-contract.ts
|
|
752
|
+
var SEMANTIC_READY_CONTENT_NODE_FLOOR = 100;
|
|
753
|
+
function readyProbeHasContent(probe) {
|
|
754
|
+
return probe.liveFrameActive || probe.targets > 0 || probe.nodes >= SEMANTIC_READY_CONTENT_NODE_FLOOR;
|
|
755
|
+
}
|
|
756
|
+
function readyProbeHasTargetContent(probe) {
|
|
757
|
+
return probe.liveFrameActive || probe.targets > 0;
|
|
758
|
+
}
|
|
759
|
+
|
|
754
760
|
// cli/commands/inspect/core.ts
|
|
755
761
|
function resolveMaxMsFlag(args, fallbackMs) {
|
|
756
762
|
const aliases = ["--max-ms", "--maxMs", "--maxms", "--max_ms"];
|
|
@@ -790,121 +796,116 @@ async function shouldSkipAutoSettleForInspectPick(bridge, subcommand) {
|
|
|
790
796
|
}
|
|
791
797
|
}
|
|
792
798
|
async function inspectNodeCount(bridge) {
|
|
793
|
-
const
|
|
794
|
-
type: "
|
|
795
|
-
|
|
799
|
+
const result = await bridge.send({
|
|
800
|
+
type: "query",
|
|
801
|
+
query: { kind: "count" }
|
|
796
802
|
});
|
|
797
|
-
return { nodes:
|
|
803
|
+
return { nodes: isSemanticQueryResult(result) ? result.total : 0 };
|
|
798
804
|
}
|
|
799
|
-
async function inspectTree(bridge, depth = 5
|
|
800
|
-
const
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
code: `(async () => await window.__sootsimTest.dumpTree(${depth}${optsArg}))()`
|
|
805
|
+
async function inspectTree(bridge, depth = 5) {
|
|
806
|
+
const result = await bridge.send({
|
|
807
|
+
type: "query",
|
|
808
|
+
query: { kind: "tree", depth }
|
|
804
809
|
});
|
|
805
|
-
|
|
806
|
-
try {
|
|
807
|
-
return { depth, tree: JSON.parse(tree) };
|
|
808
|
-
} catch {
|
|
809
|
-
return { depth, tree };
|
|
810
|
-
}
|
|
811
|
-
}
|
|
810
|
+
const tree = isSemanticQueryResult(result) ? result.nodes : [];
|
|
812
811
|
return { depth, tree };
|
|
813
812
|
}
|
|
814
813
|
async function inspectUrl(bridge) {
|
|
815
814
|
const url = await bridge.send({ type: "evaluate", code: "window.location.href" });
|
|
816
815
|
return { url: typeof url === "string" ? url : "" };
|
|
817
816
|
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
817
|
+
function isSemanticGeometry(value) {
|
|
818
|
+
return value !== null && typeof value === "object" && [
|
|
819
|
+
Reflect.get(value, "x"),
|
|
820
|
+
Reflect.get(value, "y"),
|
|
821
|
+
Reflect.get(value, "width"),
|
|
822
|
+
Reflect.get(value, "height")
|
|
823
|
+
].every(
|
|
824
|
+
(coordinate) => typeof coordinate === "number" ? Number.isFinite(coordinate) : false
|
|
825
|
+
);
|
|
826
|
+
}
|
|
827
|
+
function isSimSemanticTree(value) {
|
|
828
|
+
if (!Array.isArray(value)) return false;
|
|
829
|
+
return value.every((node) => {
|
|
830
|
+
if (node === null || typeof node !== "object" || !Number.isInteger(Reflect.get(node, "nodeId")) || typeof Reflect.get(node, "type") !== "string" || !isSemanticGeometry(Reflect.get(node, "geometry"))) {
|
|
831
|
+
return false;
|
|
832
|
+
}
|
|
833
|
+
for (const key of ["testID", "text", "role", "label"]) {
|
|
834
|
+
const value2 = Reflect.get(node, key);
|
|
835
|
+
if (value2 !== void 0 && typeof value2 !== "string") return false;
|
|
836
|
+
}
|
|
837
|
+
const pressable = Reflect.get(node, "pressable");
|
|
838
|
+
if (pressable !== void 0 && pressable !== true) return false;
|
|
839
|
+
const children = Reflect.get(node, "children");
|
|
840
|
+
return children === void 0 || isSimSemanticTree(children);
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
function isSemanticQueryResult(value) {
|
|
844
|
+
const total = value !== null && typeof value === "object" ? Reflect.get(value, "total") : void 0;
|
|
845
|
+
return value !== null && typeof value === "object" && typeof total === "number" && Number.isInteger(total) && total >= 0 && isSimSemanticTree(Reflect.get(value, "nodes"));
|
|
846
|
+
}
|
|
847
|
+
function globRegex(glob) {
|
|
848
|
+
const escaped = glob.replace(/[.+^$(){}|[\]\\]/g, "\\$&");
|
|
849
|
+
return new RegExp(`^${escaped.replace(/\*/g, ".*").replace(/\?/g, ".")}$`, "i");
|
|
850
|
+
}
|
|
851
|
+
function formatSemanticTree(nodes, opts = {}) {
|
|
852
|
+
const filter = opts.filter?.toLowerCase() ?? "";
|
|
853
|
+
const testIDRegex = opts.testIdLike ? globRegex(opts.testIdLike) : null;
|
|
854
|
+
const onlyRegex = opts.onlyGlob ? globRegex(opts.onlyGlob) : null;
|
|
855
|
+
const lines = [];
|
|
856
|
+
const visit = (node, indent) => {
|
|
857
|
+
const joined = [node.role, node.label, node.text, node.testID].filter((value) => typeof value === "string").join(" ");
|
|
858
|
+
const matches = (!filter || joined.toLowerCase().includes(filter)) && (!testIDRegex || testIDRegex.test(node.testID ?? "")) && (!onlyRegex || onlyRegex.test(joined));
|
|
859
|
+
if (matches) {
|
|
860
|
+
const parts = [
|
|
861
|
+
node.role ? `[${node.role}]` : node.pressable ? "[tap]" : `<${node.type}>`
|
|
862
|
+
];
|
|
863
|
+
if (node.text) {
|
|
864
|
+
const text = opts.fullText || node.text.length <= 50 ? node.text : `${node.text.slice(0, 49).trimEnd()}\u2026`;
|
|
865
|
+
parts.push(JSON.stringify(text));
|
|
847
866
|
}
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
if (!Array.isArray(list)) return null
|
|
852
|
-
const filter = typeof opts.filter === 'string' ? opts.filter.toLowerCase() : ''
|
|
853
|
-
const testIdRegex = opts.testIdLike ? globToRegex(opts.testIdLike) : null
|
|
854
|
-
const onlyRegex = opts.onlyGlob ? globToRegex(opts.onlyGlob) : null
|
|
855
|
-
const lines = []
|
|
856
|
-
let matched = 0
|
|
857
|
-
for (const node of list) {
|
|
858
|
-
const testID = node?.testID || node?.testId || ''
|
|
859
|
-
const role = node?.role || ''
|
|
860
|
-
const label = node?.accessibilityLabel || node?.label || ''
|
|
861
|
-
const text = node?.text || ''
|
|
862
|
-
if (testIdRegex && !testIdRegex.test(testID)) continue
|
|
863
|
-
const joined = [role, label, text, testID].filter(Boolean).join(' ')
|
|
864
|
-
if (onlyRegex && !onlyRegex.test(joined)) continue
|
|
865
|
-
if (filter && !joined.toLowerCase().includes(filter)) continue
|
|
866
|
-
matched++
|
|
867
|
-
|
|
868
|
-
const type = String(node?.type || 'node').toLowerCase()
|
|
869
|
-
const handlers = Array.isArray(node?.handlers) ? node.handlers : []
|
|
870
|
-
const parts = []
|
|
871
|
-
if (role) parts.push('[' + role + ']')
|
|
872
|
-
else if (handlers.length > 0) parts.push('[tap]')
|
|
873
|
-
else parts.push('<' + type + '>')
|
|
874
|
-
const visibleText = clipForDisplay(text, 50)
|
|
875
|
-
if (visibleText) parts.push(JSON.stringify(visibleText))
|
|
876
|
-
if (testID) parts.push('#' + testID)
|
|
877
|
-
if (label && label !== text) {
|
|
878
|
-
parts.push('label=' + JSON.stringify(clipForDisplay(label, 50)))
|
|
879
|
-
}
|
|
880
|
-
if (!opts.hideXy) {
|
|
881
|
-
const abs = node?.absolute || {}
|
|
882
|
-
const layout = node?.layout || {}
|
|
883
|
-
const x = Math.round(Number(abs.x) || 0)
|
|
884
|
-
const y = Math.round(Number(abs.y) || 0)
|
|
885
|
-
const w = Math.round(Number(layout.width) || 0)
|
|
886
|
-
const h = Math.round(Number(layout.height) || 0)
|
|
887
|
-
parts.push('@(' + x + ',' + y + ') ' + w + 'x' + h)
|
|
888
|
-
}
|
|
889
|
-
if (handlers.length > 0) parts.push('(tap)')
|
|
890
|
-
lines.push(' ' + parts.join(' '))
|
|
891
|
-
}
|
|
892
|
-
return { tree: lines.join('\\n'), matched }
|
|
867
|
+
if (node.testID) parts.push(`#${node.testID}`);
|
|
868
|
+
if (node.label && node.label !== node.text) {
|
|
869
|
+
parts.push(`label=${JSON.stringify(node.label)}`);
|
|
893
870
|
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
} else {
|
|
900
|
-
tree = await t.dumpTree(${dumpDepth}, opts)
|
|
871
|
+
if (!opts.hideXy) {
|
|
872
|
+
const geometry = node.geometry;
|
|
873
|
+
parts.push(
|
|
874
|
+
`@(${Math.round(geometry.x)},${Math.round(geometry.y)}) ${Math.round(geometry.width)}x${Math.round(geometry.height)}`
|
|
875
|
+
);
|
|
901
876
|
}
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
877
|
+
if (node.pressable) parts.push("(tap)");
|
|
878
|
+
lines.push(`${" ".repeat(indent + 1)}${parts.join(" ")}`);
|
|
879
|
+
}
|
|
880
|
+
for (const child of node.children ?? []) visit(child, indent + 1);
|
|
881
|
+
};
|
|
882
|
+
let roots = nodes;
|
|
883
|
+
if (opts.subtreeRoot) {
|
|
884
|
+
const pending = [...nodes];
|
|
885
|
+
let found;
|
|
886
|
+
while (pending.length > 0 && !found) {
|
|
887
|
+
const candidate = pending.shift();
|
|
888
|
+
if (!candidate) continue;
|
|
889
|
+
if (candidate.testID === opts.subtreeRoot) found = candidate;
|
|
890
|
+
else pending.push(...candidate.children ?? []);
|
|
891
|
+
}
|
|
892
|
+
if (!found) return `__SUBTREE_NOT_FOUND__:${opts.subtreeRoot}`;
|
|
893
|
+
roots = [found];
|
|
894
|
+
}
|
|
895
|
+
for (const root of roots) visit(root, 0);
|
|
896
|
+
return lines.join("\n");
|
|
897
|
+
}
|
|
898
|
+
async function inspectDescribe(bridge, dumpOpts) {
|
|
899
|
+
const dumpDepth = dumpOpts.maxDepth ?? 12;
|
|
900
|
+
const result = await bridge.send({
|
|
901
|
+
type: "query",
|
|
902
|
+
query: { kind: "tree", depth: dumpDepth }
|
|
903
|
+
});
|
|
904
|
+
if (!isSemanticQueryResult(result)) return {};
|
|
905
|
+
return {
|
|
906
|
+
tree: formatSemanticTree(result.nodes, dumpOpts),
|
|
907
|
+
nodeCount: result.total
|
|
908
|
+
};
|
|
908
909
|
}
|
|
909
910
|
function scoreLayoutTextContrast(input) {
|
|
910
911
|
const required = input.fontSize >= 18 || input.fontSize >= 14 && (input.fontWeight ?? 400) >= 700 ? 3 : 4.5;
|
|
@@ -1119,9 +1120,6 @@ function layoutGetNativeEval(opts = {}) {
|
|
|
1119
1120
|
type: typeof n.type === 'string' ? n.type : 'node',
|
|
1120
1121
|
text,
|
|
1121
1122
|
box: { x: round(abs.x), y: round(abs.y), w: round(layout.width), h: round(layout.height) },
|
|
1122
|
-
borderRadius: 0,
|
|
1123
|
-
padding: { t: 0, r: 0, b: 0, l: 0 },
|
|
1124
|
-
fontSize: 0,
|
|
1125
1123
|
}
|
|
1126
1124
|
if (!includeStyle) return base
|
|
1127
1125
|
// computedStyle has the resolved values; style is the curated subset \u2014
|
|
@@ -1192,6 +1190,32 @@ function layoutGetNativeEval(opts = {}) {
|
|
|
1192
1190
|
}
|
|
1193
1191
|
var LAYOUT_GET_NATIVE_EVAL = layoutGetNativeEval();
|
|
1194
1192
|
async function inspectGetLayout(bridge, opts = {}) {
|
|
1193
|
+
if (!opts.styling) {
|
|
1194
|
+
const result = await bridge.send({
|
|
1195
|
+
type: "query",
|
|
1196
|
+
query: { kind: "find", selector: { visible: true } }
|
|
1197
|
+
});
|
|
1198
|
+
if (!isSemanticQueryResult(result)) return [];
|
|
1199
|
+
const filter = opts.filter?.trim().toLowerCase() ?? "";
|
|
1200
|
+
return result.nodes.filter((node) => {
|
|
1201
|
+
if (!filter) return true;
|
|
1202
|
+
return [node.text, node.testID, node.role, node.type].some(
|
|
1203
|
+
(value) => typeof value === "string" && value.toLowerCase().includes(filter)
|
|
1204
|
+
);
|
|
1205
|
+
}).map((node) => ({
|
|
1206
|
+
nodeId: node.nodeId,
|
|
1207
|
+
...node.testID ? { testID: node.testID } : {},
|
|
1208
|
+
...node.role ? { role: node.role } : {},
|
|
1209
|
+
type: node.type,
|
|
1210
|
+
...node.text ? { text: node.text } : {},
|
|
1211
|
+
box: {
|
|
1212
|
+
x: Math.round(node.geometry.x),
|
|
1213
|
+
y: Math.round(node.geometry.y),
|
|
1214
|
+
w: Math.round(node.geometry.width),
|
|
1215
|
+
h: Math.round(node.geometry.height)
|
|
1216
|
+
}
|
|
1217
|
+
}));
|
|
1218
|
+
}
|
|
1195
1219
|
const raw = await bridge.send({
|
|
1196
1220
|
type: "evaluate",
|
|
1197
1221
|
code: layoutGetNativeEval(opts)
|
|
@@ -1203,13 +1227,17 @@ function formatLayoutElements(elements, opts) {
|
|
|
1203
1227
|
const lines = elements.map((el) => {
|
|
1204
1228
|
const ident = el.testID ? `#${el.testID}` : el.selector ? el.selector : el.role ? `[${el.role}]` : `<${el.type}>`;
|
|
1205
1229
|
const parts = [ident, `@(${el.box.x},${el.box.y})`, `${el.box.w}x${el.box.h}`];
|
|
1206
|
-
if (el.borderRadius
|
|
1230
|
+
if (el.borderRadius !== void 0 && el.borderRadius > 0) {
|
|
1231
|
+
parts.push(`radius:${el.borderRadius}`);
|
|
1232
|
+
}
|
|
1207
1233
|
const p = el.padding;
|
|
1208
|
-
if (p.t || p.r || p.b || p.l) {
|
|
1234
|
+
if (p && (p.t || p.r || p.b || p.l)) {
|
|
1209
1235
|
const uniform = p.t === p.r && p.r === p.b && p.b === p.l;
|
|
1210
1236
|
parts.push(uniform ? `pad:${p.t}` : `pad:${p.t},${p.r},${p.b},${p.l}`);
|
|
1211
1237
|
}
|
|
1212
|
-
if (el.fontSize
|
|
1238
|
+
if (el.fontSize !== void 0 && el.fontSize > 0) {
|
|
1239
|
+
parts.push(`font:${el.fontSize}`);
|
|
1240
|
+
}
|
|
1213
1241
|
if (opts?.styling) {
|
|
1214
1242
|
if (el.fontWeight) parts.push(`weight:${el.fontWeight}`);
|
|
1215
1243
|
if (el.color) parts.push(`color:${el.color}`);
|
|
@@ -1236,246 +1264,80 @@ function formatLayoutElements(elements, opts) {
|
|
|
1236
1264
|
});
|
|
1237
1265
|
return lines.join("\n");
|
|
1238
1266
|
}
|
|
1239
|
-
var INSPECT_A11Y_CODE = `(async () => {
|
|
1240
|
-
const t = window.__sootsimTest
|
|
1241
|
-
if (!t) return []
|
|
1242
|
-
|
|
1243
|
-
const readLayout = (n) => n?.layout || null
|
|
1244
|
-
const readAbs = (n) => n?.absolutePosition || n?.absolute || null
|
|
1245
|
-
const readRole = (n) => n?.accessibilityRole || n?.role || null
|
|
1246
|
-
const readLabel = (n) => n?.accessibilityLabel || n?.label || n?.text || null
|
|
1247
|
-
const readHint = (n) => n?.accessibilityHint || n?.hint || null
|
|
1248
|
-
const readState = (n) => n?.accessibilityState || n?.state || null
|
|
1249
|
-
const readTestId = (n) => n?.testID || n?.testId || null
|
|
1250
|
-
const readType = (n) => typeof n?.type === 'string' ? n.type.toLowerCase() : n?.type
|
|
1251
|
-
const readPressable = (n) => {
|
|
1252
|
-
if (n?.pressable) return true
|
|
1253
|
-
const handlers = Array.isArray(n?.handlers) ? n.handlers : []
|
|
1254
|
-
const role = readRole(n)
|
|
1255
|
-
return handlers.length > 0 || role === 'button' || role === 'link' || role === 'tab'
|
|
1256
|
-
}
|
|
1257
|
-
const isTextInput = (n) => n?.isTextInput === true
|
|
1258
|
-
const isTextNode = (n) => readType(n) === 'text'
|
|
1259
|
-
const isVisibleTarget = (n) => {
|
|
1260
|
-
const layout = readLayout(n)
|
|
1261
|
-
if (!layout || layout.width <= 0 || layout.height <= 0) return false
|
|
1262
|
-
const abs = readAbs(n)
|
|
1263
|
-
if (!abs) return true
|
|
1264
|
-
const device = n?.device || {}
|
|
1265
|
-
const screenW = Number(device.width) || window.innerWidth || 0
|
|
1266
|
-
const screenH = Number(device.height) || window.innerHeight || 0
|
|
1267
|
-
return (
|
|
1268
|
-
abs.x + layout.width > 0 &&
|
|
1269
|
-
abs.y + layout.height > 0 &&
|
|
1270
|
-
abs.x < screenW &&
|
|
1271
|
-
abs.y < screenH
|
|
1272
|
-
)
|
|
1273
|
-
}
|
|
1274
|
-
const hasAccessibleSignal = (n) => {
|
|
1275
|
-
const role = readRole(n)
|
|
1276
|
-
const label = readLabel(n)
|
|
1277
|
-
const hint = readHint(n)
|
|
1278
|
-
if (role) return true
|
|
1279
|
-
if (hint) return true
|
|
1280
|
-
if (isTextInput(n)) return true
|
|
1281
|
-
if (readPressable(n)) return true
|
|
1282
|
-
if (isTextNode(n) && n?.text) return true
|
|
1283
|
-
if (typeof label === 'string' && label.length <= 30 && label !== n?.text) return true
|
|
1284
|
-
return false
|
|
1285
|
-
}
|
|
1286
|
-
const normalize = (n) => {
|
|
1287
|
-
const layout = readLayout(n)
|
|
1288
|
-
const abs = readAbs(n)
|
|
1289
|
-
const role = readRole(n) || (readPressable(n) ? 'button' : isTextInput(n) ? 'textfield' : isTextNode(n) ? 'statictext' : 'none')
|
|
1290
|
-
return {
|
|
1291
|
-
role,
|
|
1292
|
-
label: readLabel(n),
|
|
1293
|
-
hint: readHint(n),
|
|
1294
|
-
state: readState(n),
|
|
1295
|
-
testID: readTestId(n),
|
|
1296
|
-
position: abs ? { x: Math.round(abs.x), y: Math.round(abs.y) } : null,
|
|
1297
|
-
size: layout ? { w: Math.round(layout.width), h: Math.round(layout.height) } : null,
|
|
1298
|
-
}
|
|
1299
|
-
}
|
|
1300
|
-
|
|
1301
|
-
if (typeof t.listInspectable === 'function') {
|
|
1302
|
-
const list = await t.listInspectable({})
|
|
1303
|
-
if (Array.isArray(list)) {
|
|
1304
|
-
return list.filter(n => isVisibleTarget(n) && hasAccessibleSignal(n)).map(normalize)
|
|
1305
|
-
}
|
|
1306
|
-
}
|
|
1307
|
-
|
|
1308
|
-
const all = await t.queryAll({ pruneHidden: true })
|
|
1309
|
-
return all
|
|
1310
|
-
.filter(n => isVisibleTarget(n) && hasAccessibleSignal(n))
|
|
1311
|
-
.map(normalize)
|
|
1312
|
-
})()`;
|
|
1313
1267
|
async function inspectAccessibilityTree(bridge) {
|
|
1314
|
-
const
|
|
1315
|
-
|
|
1316
|
-
}
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
Math.max(0, absolutePosition.x),
|
|
1337
|
-
),
|
|
1338
|
-
height: Math.max(
|
|
1339
|
-
0,
|
|
1340
|
-
Math.min(screenH, absolutePosition.y + layout.height) -
|
|
1341
|
-
Math.max(0, absolutePosition.y),
|
|
1342
|
-
),
|
|
1343
|
-
}
|
|
1344
|
-
: null
|
|
1345
|
-
const handlers = Array.isArray(n.handlers) ? n.handlers : []
|
|
1346
|
-
const pressable =
|
|
1347
|
-
handlers.length > 0 ||
|
|
1348
|
-
role === 'button' ||
|
|
1349
|
-
role === 'link' ||
|
|
1350
|
-
role === 'tab'
|
|
1351
|
-
return {
|
|
1352
|
-
type: typeof n.type === 'string' ? n.type.toLowerCase() : n.type,
|
|
1353
|
-
id: n.id ?? n.nodeId,
|
|
1354
|
-
nodeId: n.nodeId,
|
|
1355
|
-
testID: n.testID ?? n.testId,
|
|
1356
|
-
text: n.text,
|
|
1357
|
-
layout,
|
|
1358
|
-
absolutePosition,
|
|
1359
|
-
visibleFrame,
|
|
1360
|
-
style: n.computedStyle ?? n.style ?? {},
|
|
1361
|
-
childCount: n.childCount ?? 0,
|
|
1362
|
-
pressed: n.pressed ?? false,
|
|
1363
|
-
pressable,
|
|
1364
|
-
isTextInput: n.isTextInput ?? false,
|
|
1365
|
-
accessible: n.accessible ?? true,
|
|
1366
|
-
accessibilityLabel: label,
|
|
1367
|
-
accessibilityRole: role,
|
|
1368
|
-
accessibilityHint: n.accessibilityHint,
|
|
1369
|
-
accessibilityState: n.accessibilityState,
|
|
1370
|
-
accessibilityValue: n.accessibilityValue,
|
|
1371
|
-
}
|
|
1372
|
-
})
|
|
1373
|
-
}
|
|
1374
|
-
const isVisibleTarget = (n) => {
|
|
1375
|
-
if (!n?.layout) return false
|
|
1376
|
-
const frame = n.visibleFrame
|
|
1377
|
-
if (frame) return frame.width > 0 && frame.height > 0
|
|
1378
|
-
const abs = n.absolutePosition
|
|
1379
|
-
if (!abs) return n.layout.width > 0 && n.layout.height > 0
|
|
1380
|
-
const screenW = window.innerWidth || 0
|
|
1381
|
-
const screenH = window.innerHeight || 0
|
|
1382
|
-
return (
|
|
1383
|
-
abs.x + n.layout.width > 0 &&
|
|
1384
|
-
abs.y + n.layout.height > 0 &&
|
|
1385
|
-
abs.x < screenW &&
|
|
1386
|
-
abs.y < screenH
|
|
1387
|
-
)
|
|
1268
|
+
const result = await bridge.send({
|
|
1269
|
+
type: "query",
|
|
1270
|
+
query: { kind: "find", selector: { visible: true } }
|
|
1271
|
+
});
|
|
1272
|
+
if (!isSemanticQueryResult(result)) return [];
|
|
1273
|
+
return result.nodes.flatMap((node) => {
|
|
1274
|
+
if (!node.role && !node.label && !node.text && !node.pressable) return [];
|
|
1275
|
+
return [
|
|
1276
|
+
{
|
|
1277
|
+
role: node.role ?? (node.pressable ? "button" : node.type === "text" ? "statictext" : "none"),
|
|
1278
|
+
label: node.label ?? node.text ?? null,
|
|
1279
|
+
hint: null,
|
|
1280
|
+
state: null,
|
|
1281
|
+
testID: node.testID ?? null,
|
|
1282
|
+
position: {
|
|
1283
|
+
x: Math.round(node.geometry.x),
|
|
1284
|
+
y: Math.round(node.geometry.y)
|
|
1285
|
+
},
|
|
1286
|
+
size: {
|
|
1287
|
+
w: Math.round(node.geometry.width),
|
|
1288
|
+
h: Math.round(node.geometry.height)
|
|
1289
|
+
}
|
|
1388
1290
|
}
|
|
1389
|
-
|
|
1291
|
+
];
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1390
1294
|
function resolveFindMode(q) {
|
|
1391
1295
|
if (q.testId) {
|
|
1392
|
-
return {
|
|
1393
|
-
mode: "testid",
|
|
1394
|
-
code: `(async () => {
|
|
1395
|
-
const t = window.__sootsimTest
|
|
1396
|
-
if (!t) return null
|
|
1397
|
-
return (await t.findByTestId(${JSON.stringify(q.testId)})) || (await t.findById(${JSON.stringify(q.testId)}))
|
|
1398
|
-
})()`
|
|
1399
|
-
};
|
|
1296
|
+
return { mode: "testid", selector: { testID: q.testId } };
|
|
1400
1297
|
}
|
|
1401
1298
|
if (q.role) {
|
|
1402
|
-
return {
|
|
1403
|
-
mode: "role",
|
|
1404
|
-
code: `(async () => {
|
|
1405
|
-
const t = window.__sootsimTest
|
|
1406
|
-
if (!t) return []
|
|
1407
|
-
return await t.queryAll({ hasRole: ${JSON.stringify(q.role)}, pruneHidden: true })
|
|
1408
|
-
})()`
|
|
1409
|
-
};
|
|
1299
|
+
return { mode: "role", selector: { role: q.role } };
|
|
1410
1300
|
}
|
|
1411
1301
|
if (q.type) {
|
|
1412
|
-
return {
|
|
1413
|
-
mode: "type",
|
|
1414
|
-
code: `(async () => {
|
|
1415
|
-
const t = window.__sootsimTest
|
|
1416
|
-
if (!t) return []
|
|
1417
|
-
return await t.queryAll({ type: ${JSON.stringify(q.type)}, pruneHidden: true })
|
|
1418
|
-
})()`
|
|
1419
|
-
};
|
|
1302
|
+
return { mode: "type", selector: { type: q.type } };
|
|
1420
1303
|
}
|
|
1421
1304
|
if (q.pressable) {
|
|
1422
|
-
return {
|
|
1423
|
-
mode: "pressable",
|
|
1424
|
-
code: `(async () => {
|
|
1425
|
-
const t = window.__sootsimTest
|
|
1426
|
-
if (!t) return []
|
|
1427
|
-
${FIND_INSPECTABLE_TARGETS}
|
|
1428
|
-
const inspectable = await fromInspectable()
|
|
1429
|
-
if (inspectable) return inspectable.filter(n => n.pressable && isVisibleTarget(n))
|
|
1430
|
-
const all = await t.queryAll({ pruneHidden: true })
|
|
1431
|
-
return all.filter(n => n.pressable && isVisibleTarget(n))
|
|
1432
|
-
})()`
|
|
1433
|
-
};
|
|
1305
|
+
return { mode: "pressable", selector: { pressable: true } };
|
|
1434
1306
|
}
|
|
1435
1307
|
if (q.interactive) {
|
|
1436
|
-
return {
|
|
1437
|
-
mode: "interactive-targets",
|
|
1438
|
-
code: `(async () => {
|
|
1439
|
-
const t = window.__sootsimTest
|
|
1440
|
-
if (!t) return []
|
|
1441
|
-
${FIND_INSPECTABLE_TARGETS}
|
|
1442
|
-
const inspectable = await fromInspectable()
|
|
1443
|
-
if (inspectable) {
|
|
1444
|
-
return inspectable.filter(n => n.pressable && isVisibleTarget(n))
|
|
1445
|
-
}
|
|
1446
|
-
const all = await t.queryAll({ pruneHidden: true })
|
|
1447
|
-
return all.filter(n => n.pressable && isVisibleTarget(n))
|
|
1448
|
-
})()`
|
|
1449
|
-
};
|
|
1308
|
+
return { mode: "interactive-targets", selector: { pressable: true } };
|
|
1450
1309
|
}
|
|
1451
1310
|
if (q.visible) {
|
|
1452
|
-
return {
|
|
1453
|
-
mode: "visible",
|
|
1454
|
-
code: `(async () => {
|
|
1455
|
-
const t = window.__sootsimTest
|
|
1456
|
-
if (!t) return []
|
|
1457
|
-
const all = await t.queryAll({ pruneHidden: true })
|
|
1458
|
-
return all.filter(n => n.layout && n.layout.width > 0 && n.layout.height > 0)
|
|
1459
|
-
})()`
|
|
1460
|
-
};
|
|
1311
|
+
return { mode: "visible", selector: { visible: true } };
|
|
1461
1312
|
}
|
|
1462
1313
|
if (q.text) {
|
|
1463
|
-
return {
|
|
1464
|
-
mode: "text",
|
|
1465
|
-
code: `(async () => {
|
|
1466
|
-
const t = window.__sootsimTest
|
|
1467
|
-
if (!t) return null
|
|
1468
|
-
return await t.findByText(${JSON.stringify(q.text)})
|
|
1469
|
-
})()`
|
|
1470
|
-
};
|
|
1314
|
+
return { mode: "text", selector: { text: q.text } };
|
|
1471
1315
|
}
|
|
1472
1316
|
return null;
|
|
1473
1317
|
}
|
|
1474
1318
|
async function inspectFind(bridge, q) {
|
|
1475
1319
|
const resolved = resolveFindMode(q);
|
|
1476
1320
|
if (!resolved) return null;
|
|
1477
|
-
const result = await bridge.send({
|
|
1478
|
-
|
|
1321
|
+
const result = await bridge.send({
|
|
1322
|
+
type: "query",
|
|
1323
|
+
query: { kind: "find", selector: resolved.selector }
|
|
1324
|
+
});
|
|
1325
|
+
if (!isSemanticQueryResult(result)) return { mode: resolved.mode, result: [] };
|
|
1326
|
+
const nodes = result.nodes.map(
|
|
1327
|
+
(node) => ({
|
|
1328
|
+
type: node.type,
|
|
1329
|
+
nodeId: node.nodeId,
|
|
1330
|
+
...node.testID ? { testID: node.testID } : {},
|
|
1331
|
+
...node.text ? { text: node.text } : {},
|
|
1332
|
+
...node.pressable ? { pressable: true } : {},
|
|
1333
|
+
...node.role ? { accessibilityRole: node.role } : {},
|
|
1334
|
+
...node.label ? { accessibilityLabel: node.label } : {},
|
|
1335
|
+
absolutePosition: { x: node.geometry.x, y: node.geometry.y },
|
|
1336
|
+
layout: { width: node.geometry.width, height: node.geometry.height }
|
|
1337
|
+
})
|
|
1338
|
+
);
|
|
1339
|
+
const resultValue = resolved.mode === "testid" || resolved.mode === "text" ? nodes[0] ?? null : nodes;
|
|
1340
|
+
return { mode: resolved.mode, result: resultValue };
|
|
1479
1341
|
}
|
|
1480
1342
|
function rankInteractive(nodes) {
|
|
1481
1343
|
return [...nodes].sort((a, b) => scoreInteractive(b) - scoreInteractive(a));
|
|
@@ -1509,158 +1371,6 @@ function shellEscape(value) {
|
|
|
1509
1371
|
if (/^[A-Za-z0-9_./@:-]+$/.test(value)) return value;
|
|
1510
1372
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
1511
1373
|
}
|
|
1512
|
-
var WAIT_READY_PROBE = `(async () => {
|
|
1513
|
-
const t = window.__sootsimTest
|
|
1514
|
-
let nodes = 0
|
|
1515
|
-
try { nodes = (await t?.getNodeCount?.()) || 0 } catch {}
|
|
1516
|
-
let targets = 0
|
|
1517
|
-
let liveFrameActive = false
|
|
1518
|
-
let liveFrameChannels = 0
|
|
1519
|
-
let liveFramePublishes = 0
|
|
1520
|
-
let loadingText = ''
|
|
1521
|
-
const cleanText = (value) =>
|
|
1522
|
-
typeof value === 'string' ? value.replace(/\\s+/g, ' ').trim() : ''
|
|
1523
|
-
const loadingSignal = (value) => {
|
|
1524
|
-
const text = cleanText(value)
|
|
1525
|
-
if (!text) return ''
|
|
1526
|
-
return /^(opening app\\b|looking for your app\\b|building bundle\\b|preparing rnx runtime\\b|loading native bundle\\b|loading rnx stubs\\b|loading bundle loader\\b|loading native stubs\\b|loading compat bridge\\b|loading react-native shim\\b|loading react bridge\\b|loading reconciler\\b|loading replacement modules\\b|injecting rnx modules\\b|warming sqlite engine\\b|capturing root component\\b|reloading app\\b|loading(?:[.!\u2026]*)?$|connecting(?:[.!\u2026]*)?$)/i.test(text)
|
|
1527
|
-
? text
|
|
1528
|
-
: ''
|
|
1529
|
-
}
|
|
1530
|
-
try {
|
|
1531
|
-
const loading = window.__sootsimConnectRNLoading
|
|
1532
|
-
if (loading && loading.active === true) {
|
|
1533
|
-
loadingText = [loading.title, loading.detail].map(cleanText).filter(Boolean).join(' ')
|
|
1534
|
-
}
|
|
1535
|
-
} catch {}
|
|
1536
|
-
try {
|
|
1537
|
-
if (typeof t?.listInspectable === 'function') {
|
|
1538
|
-
const list = await t.listInspectable({})
|
|
1539
|
-
if (Array.isArray(list)) {
|
|
1540
|
-
const screenW = window.innerWidth || 0
|
|
1541
|
-
const screenH = window.innerHeight || 0
|
|
1542
|
-
const isVisible = (n) => {
|
|
1543
|
-
const layout = n?.layout
|
|
1544
|
-
if (!layout || layout.width <= 0 || layout.height <= 0) return false
|
|
1545
|
-
const abs = n?.absolutePosition || n?.absolute
|
|
1546
|
-
if (!abs) return true
|
|
1547
|
-
const device = n?.device || {}
|
|
1548
|
-
const w = Number(device.width) || screenW
|
|
1549
|
-
const h = Number(device.height) || screenH
|
|
1550
|
-
return (
|
|
1551
|
-
abs.x + layout.width > 0 &&
|
|
1552
|
-
abs.y + layout.height > 0 &&
|
|
1553
|
-
abs.x < w &&
|
|
1554
|
-
abs.y < h
|
|
1555
|
-
)
|
|
1556
|
-
}
|
|
1557
|
-
const hasContentSignal = (n) => {
|
|
1558
|
-
const role = n?.accessibilityRole || n?.role
|
|
1559
|
-
const label = cleanText(n?.accessibilityLabel || n?.label)
|
|
1560
|
-
const handlers = Array.isArray(n?.handlers) ? n.handlers : []
|
|
1561
|
-
const text = cleanText(n?.text)
|
|
1562
|
-
const testID = cleanText(n?.testID || n?.testId)
|
|
1563
|
-
const loading = loadingSignal(text) || loadingSignal(label) || loadingSignal(testID)
|
|
1564
|
-
if (!loadingText && loading) {
|
|
1565
|
-
loadingText = loading
|
|
1566
|
-
}
|
|
1567
|
-
if (handlers.length > 0) return true
|
|
1568
|
-
if (role) return true
|
|
1569
|
-
if (n?.isTextInput) return true
|
|
1570
|
-
if (text) return true
|
|
1571
|
-
if (label) return true
|
|
1572
|
-
if (testID && !/^splash|loading/i.test(testID)) return true
|
|
1573
|
-
return false
|
|
1574
|
-
}
|
|
1575
|
-
targets = list.filter(n => isVisible(n) && hasContentSignal(n)).length
|
|
1576
|
-
}
|
|
1577
|
-
}
|
|
1578
|
-
} catch {}
|
|
1579
|
-
try {
|
|
1580
|
-
const stats = await t?.getImageLoaderStats?.()
|
|
1581
|
-
const presentation = stats?.liveFramePresentation
|
|
1582
|
-
liveFrameChannels = Number(presentation?.activeChannels) || 0
|
|
1583
|
-
liveFramePublishes = Number(presentation?.publishes) || 0
|
|
1584
|
-
const previous = window.__sootsimWaitReadyLiveFramePublishes
|
|
1585
|
-
liveFrameActive =
|
|
1586
|
-
liveFrameChannels > 0 &&
|
|
1587
|
-
typeof previous === 'number' &&
|
|
1588
|
-
liveFramePublishes > previous
|
|
1589
|
-
window.__sootsimWaitReadyLiveFramePublishes = liveFramePublishes
|
|
1590
|
-
} catch {}
|
|
1591
|
-
let externalReady = null
|
|
1592
|
-
let externalStatus = ''
|
|
1593
|
-
let externalError = ''
|
|
1594
|
-
let suppressedEntryError = ''
|
|
1595
|
-
try {
|
|
1596
|
-
const getExternalAppState = t?.getExternalAppState
|
|
1597
|
-
const external = typeof getExternalAppState === 'function'
|
|
1598
|
-
? await Promise.race([
|
|
1599
|
-
getExternalAppState(),
|
|
1600
|
-
new Promise((resolve) => setTimeout(
|
|
1601
|
-
() => resolve({
|
|
1602
|
-
state: {
|
|
1603
|
-
ready: false,
|
|
1604
|
-
loading: true,
|
|
1605
|
-
status: 'waiting for tenant worker...',
|
|
1606
|
-
},
|
|
1607
|
-
}),
|
|
1608
|
-
1000,
|
|
1609
|
-
)),
|
|
1610
|
-
])
|
|
1611
|
-
: null
|
|
1612
|
-
const state = external?.state
|
|
1613
|
-
if (state && typeof state === 'object') {
|
|
1614
|
-
externalReady = state.ready === true
|
|
1615
|
-
if (typeof state.status === 'string') externalStatus = state.status
|
|
1616
|
-
if (typeof state.error === 'string') externalError = state.error
|
|
1617
|
-
}
|
|
1618
|
-
if (!externalError && Array.isArray(external?.entryErrors)) {
|
|
1619
|
-
// suppressed-and-continued entry errors (bundle-loader's patched __r
|
|
1620
|
-
// swallows entry throws and moves on) must NOT gate readiness \u2014 the
|
|
1621
|
-
// app frequently runs fine after one (e.g. metro's "Unknown named
|
|
1622
|
-
// module" probing noise). they surface separately as a diagnostic
|
|
1623
|
-
// for the timeout path.
|
|
1624
|
-
const isReal = (item) => item && typeof item.message === 'string' && item.message.trim()
|
|
1625
|
-
const fatal = external.entryErrors.find((item) => isReal(item) && item.suppressed !== true)
|
|
1626
|
-
if (fatal) externalError = fatal.message
|
|
1627
|
-
const suppressed = external.entryErrors.find((item) => isReal(item) && item.suppressed === true)
|
|
1628
|
-
if (suppressed) suppressedEntryError = suppressed.message
|
|
1629
|
-
}
|
|
1630
|
-
// once the app has errored its last status ("injecting sootsim
|
|
1631
|
-
// modules...") is stale \u2014 reporting it makes a dead app read as one that
|
|
1632
|
-
// is still booting.
|
|
1633
|
-
if (externalError) loadingText = ''
|
|
1634
|
-
else if (externalReady === false && externalStatus) loadingText = externalStatus
|
|
1635
|
-
} catch {}
|
|
1636
|
-
let errors = 0
|
|
1637
|
-
try { errors = window.__sootsimConsole?.count?.()?.errors ?? 0 } catch {}
|
|
1638
|
-
return {
|
|
1639
|
-
flag: (window).__sootsimExternalAppReady,
|
|
1640
|
-
at: (window).__sootsimExternalAppReadyAt || 0,
|
|
1641
|
-
nodes,
|
|
1642
|
-
targets,
|
|
1643
|
-
liveFrameActive,
|
|
1644
|
-
liveFrameChannels,
|
|
1645
|
-
liveFramePublishes,
|
|
1646
|
-
errors,
|
|
1647
|
-
loadingText,
|
|
1648
|
-
externalReady,
|
|
1649
|
-
externalStatus,
|
|
1650
|
-
externalError,
|
|
1651
|
-
suppressedEntryError,
|
|
1652
|
-
}
|
|
1653
|
-
})()`;
|
|
1654
|
-
var READY_CONTENT_NODE_FLOOR = 100;
|
|
1655
|
-
var READY_NODE_STABLE_MS = 750;
|
|
1656
|
-
var WAIT_READY_PROGRESS_INTERVAL_MS = 2e3;
|
|
1657
|
-
var READY_PROBE_POLL_MS = 150;
|
|
1658
|
-
function readyProbeHasContent(probe) {
|
|
1659
|
-
return probe.liveFrameActive || probe.targets > 0 || probe.nodes >= READY_CONTENT_NODE_FLOOR;
|
|
1660
|
-
}
|
|
1661
|
-
function readyProbeHasTargetContent(probe) {
|
|
1662
|
-
return probe.liveFrameActive || probe.targets > 0;
|
|
1663
|
-
}
|
|
1664
1374
|
function waitReadyReason(status) {
|
|
1665
1375
|
if (status.bridgeError) {
|
|
1666
1376
|
return `sim unreachable: ${status.bridgeError} \u2014 the target tab is closed or no sim is connected (check \`rnx list\`, reopen with \`rnx open\`)`;
|
|
@@ -1670,155 +1380,69 @@ function waitReadyReason(status) {
|
|
|
1670
1380
|
}
|
|
1671
1381
|
async function inspectWaitReady(bridge, timeoutMs = 2e4, options = {}) {
|
|
1672
1382
|
const start = Date.now();
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
liveFramePublishes: 0,
|
|
1687
|
-
errors: 0,
|
|
1688
|
-
loadingText: "",
|
|
1689
|
-
externalReady: null,
|
|
1690
|
-
externalStatus: "",
|
|
1691
|
-
externalError: "",
|
|
1692
|
-
suppressedEntryError: ""
|
|
1693
|
-
};
|
|
1694
|
-
while (Date.now() < deadline) {
|
|
1695
|
-
try {
|
|
1696
|
-
last = await bridge.send({ type: "evaluate", code: WAIT_READY_PROBE }) ?? last;
|
|
1697
|
-
bridgeError = "";
|
|
1698
|
-
} catch (error) {
|
|
1699
|
-
bridgeError = error instanceof Error ? error.message : String(error);
|
|
1700
|
-
if (bridgeError.startsWith("multiple sims are connected:") || bridgeError.startsWith("saved sim ")) {
|
|
1701
|
-
throw error;
|
|
1702
|
-
}
|
|
1383
|
+
let result;
|
|
1384
|
+
try {
|
|
1385
|
+
result = await bridge.send(
|
|
1386
|
+
{
|
|
1387
|
+
type: "waitFor",
|
|
1388
|
+
waitForOptions: { condition: { type: "ready" }, timeoutMs }
|
|
1389
|
+
},
|
|
1390
|
+
{ timeoutMs: timeoutMs + 1e3 }
|
|
1391
|
+
);
|
|
1392
|
+
} catch (error) {
|
|
1393
|
+
const bridgeError = error instanceof Error ? error.message : String(error);
|
|
1394
|
+
if (bridgeError.startsWith("multiple sims are connected:") || bridgeError.startsWith("saved sim ")) {
|
|
1395
|
+
throw error;
|
|
1703
1396
|
}
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
targets: last.targets,
|
|
1710
|
-
liveFrameActive: last.liveFrameActive,
|
|
1711
|
-
liveFrameChannels: last.liveFrameChannels,
|
|
1712
|
-
liveFramePublishes: last.liveFramePublishes,
|
|
1713
|
-
flag: last.flag,
|
|
1714
|
-
loadingText: last.loadingText,
|
|
1715
|
-
externalReady: last.externalReady,
|
|
1716
|
-
externalStatus: last.externalStatus,
|
|
1717
|
-
externalError: last.externalError,
|
|
1718
|
-
suppressedEntryError: last.suppressedEntryError,
|
|
1719
|
-
errors: last.errors,
|
|
1720
|
-
bridgeError
|
|
1397
|
+
result = {
|
|
1398
|
+
matched: false,
|
|
1399
|
+
elapsedMs: Date.now() - start,
|
|
1400
|
+
polls: 0,
|
|
1401
|
+
error: bridgeError
|
|
1721
1402
|
};
|
|
1722
|
-
if (last.nodes !== lastNodes) {
|
|
1723
|
-
lastNodes = last.nodes;
|
|
1724
|
-
nodeStableSince = now;
|
|
1725
|
-
}
|
|
1726
|
-
const hasReadySignal = last.flag === true && readyProbeHasContent(last);
|
|
1727
|
-
const hasRenderedContentWithoutReadySignal = last.flag !== true && readyProbeHasTargetContent(last);
|
|
1728
|
-
if ((hasReadySignal || hasRenderedContentWithoutReadySignal) && last.externalReady !== false && !last.externalError && !last.loadingText) {
|
|
1729
|
-
if (now - nodeStableSince >= READY_NODE_STABLE_MS) {
|
|
1730
|
-
return {
|
|
1731
|
-
...status2,
|
|
1732
|
-
ready: true
|
|
1733
|
-
};
|
|
1734
|
-
}
|
|
1735
|
-
}
|
|
1736
|
-
if (last.externalError) return status2;
|
|
1737
|
-
if (options.onProgress && progressIntervalMs > 0 && now >= nextProgressAt) {
|
|
1738
|
-
options.onProgress(status2);
|
|
1739
|
-
do {
|
|
1740
|
-
nextProgressAt += progressIntervalMs;
|
|
1741
|
-
} while (now >= nextProgressAt);
|
|
1742
|
-
}
|
|
1743
|
-
await new Promise((r) => setTimeout(r, READY_PROBE_POLL_MS));
|
|
1744
1403
|
}
|
|
1404
|
+
const probe = result.probe;
|
|
1745
1405
|
const status = {
|
|
1746
|
-
ready:
|
|
1747
|
-
elapsedMs:
|
|
1748
|
-
nodes:
|
|
1749
|
-
targets:
|
|
1750
|
-
liveFrameActive:
|
|
1751
|
-
liveFrameChannels:
|
|
1752
|
-
liveFramePublishes:
|
|
1753
|
-
flag:
|
|
1754
|
-
loadingText:
|
|
1755
|
-
externalReady:
|
|
1756
|
-
externalStatus:
|
|
1757
|
-
externalError:
|
|
1758
|
-
suppressedEntryError:
|
|
1759
|
-
errors:
|
|
1760
|
-
bridgeError
|
|
1406
|
+
ready: result.matched,
|
|
1407
|
+
elapsedMs: result.elapsedMs,
|
|
1408
|
+
nodes: probe?.nodes ?? 0,
|
|
1409
|
+
targets: probe?.targets ?? 0,
|
|
1410
|
+
liveFrameActive: probe?.liveFrameActive ?? false,
|
|
1411
|
+
liveFrameChannels: probe?.liveFrameChannels ?? 0,
|
|
1412
|
+
liveFramePublishes: probe?.liveFramePublishes ?? 0,
|
|
1413
|
+
flag: probe?.flag,
|
|
1414
|
+
loadingText: probe?.loadingText ?? "",
|
|
1415
|
+
externalReady: probe?.externalReady ?? null,
|
|
1416
|
+
externalStatus: probe?.externalStatus ?? "",
|
|
1417
|
+
externalError: probe?.externalError ?? "",
|
|
1418
|
+
suppressedEntryError: probe?.suppressedEntryError ?? "",
|
|
1419
|
+
errors: probe?.errors ?? 0,
|
|
1420
|
+
bridgeError: result.error ?? ""
|
|
1761
1421
|
};
|
|
1762
|
-
if (
|
|
1763
|
-
options.onProgress(status);
|
|
1764
|
-
}
|
|
1422
|
+
if (!status.ready && options.onProgress) options.onProgress(status);
|
|
1765
1423
|
return status;
|
|
1766
1424
|
}
|
|
1767
1425
|
async function inspectWaitSelector(bridge, testId, timeoutMs = 5e3, opts = {}) {
|
|
1768
1426
|
const gone = opts.gone === true;
|
|
1769
1427
|
const result = await bridge.send(
|
|
1770
1428
|
{
|
|
1771
|
-
type: "
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
const gone = ${gone}
|
|
1776
|
-
const ID = ${JSON.stringify(testId)}
|
|
1777
|
-
const present = (node) => !!(node && node.layout && node.layout.width > 0 && node.layout.height > 0)
|
|
1778
|
-
const find = async () => {
|
|
1779
|
-
const t = window.__sootsimTest
|
|
1780
|
-
if (!t) return undefined // bridge not ready \u2014 indeterminate, not "gone"
|
|
1781
|
-
try {
|
|
1782
|
-
// gone-mode confirms a gate/screen LEFT \u2014 it only needs structural
|
|
1783
|
-
// existence + layout, NOT the full-tree occlusion pass (pruneHidden).
|
|
1784
|
-
// that pass (computeOccludedInspectNodeIds) walks the whole tree and
|
|
1785
|
-
// does O(n\xB2)-ish rect-occlusion testing EVERY poll; on a large
|
|
1786
|
-
// post-login app tree it costs seconds per poll, so --gone can never
|
|
1787
|
-
// get two clean "absent" polls inside budget and falsely reports the
|
|
1788
|
-
// gate as still blocked \u2014 the root cause of the 3pc PR-preview outage.
|
|
1789
|
-
// presence-mode keeps pruneHidden so "appeared" still means
|
|
1790
|
-
// visibly-reachable. a departed gate is unmounted or zero-sized, so the
|
|
1791
|
-
// cheap structural+layout check is correct here and ~O(n) cheap.
|
|
1792
|
-
const matches = await t.queryAll?.(gone ? { hasId: ID } : { hasId: ID, pruneHidden: true })
|
|
1793
|
-
if (!Array.isArray(matches)) return undefined
|
|
1794
|
-
return matches.find(present) || null
|
|
1795
|
-
} catch {
|
|
1796
|
-
return undefined
|
|
1797
|
-
}
|
|
1798
|
-
}
|
|
1799
|
-
let goneStreak = 0
|
|
1800
|
-
while (Date.now() < deadline) {
|
|
1801
|
-
const node = await find()
|
|
1802
|
-
if (gone) {
|
|
1803
|
-
if (node === null) {
|
|
1804
|
-
goneStreak += 1
|
|
1805
|
-
if (goneStreak >= 2) return { found: true, elapsed: Date.now() - start }
|
|
1806
|
-
} else {
|
|
1807
|
-
goneStreak = 0
|
|
1808
|
-
}
|
|
1809
|
-
} else if (present(node)) {
|
|
1810
|
-
return { found: true, node, elapsed: Date.now() - start }
|
|
1811
|
-
}
|
|
1812
|
-
await new Promise((r) => setTimeout(r, 80))
|
|
1429
|
+
type: "waitFor",
|
|
1430
|
+
waitForOptions: {
|
|
1431
|
+
condition: { type: gone ? "absent" : "present", testID: testId },
|
|
1432
|
+
timeoutMs
|
|
1813
1433
|
}
|
|
1814
|
-
return { found: false, elapsed: Date.now() - start }
|
|
1815
|
-
})()`
|
|
1816
1434
|
},
|
|
1817
|
-
{
|
|
1818
|
-
timeoutMs: timeoutMs + 1e3
|
|
1819
|
-
}
|
|
1435
|
+
{ timeoutMs: timeoutMs + 1e3 }
|
|
1820
1436
|
);
|
|
1821
|
-
|
|
1437
|
+
if (result?.error) throw new Error(result.error);
|
|
1438
|
+
if (result?.node && !isSimSemanticTree([result.node])) {
|
|
1439
|
+
throw new Error("waitFor returned an invalid semantic node");
|
|
1440
|
+
}
|
|
1441
|
+
return {
|
|
1442
|
+
found: result?.matched === true,
|
|
1443
|
+
...result?.node ? { node: result.node } : {},
|
|
1444
|
+
elapsed: result?.elapsedMs ?? timeoutMs
|
|
1445
|
+
};
|
|
1822
1446
|
}
|
|
1823
1447
|
function consoleEntriesEval(level, limit) {
|
|
1824
1448
|
const getter = level === "error" ? "getErrors" : "getWarnings";
|
|
@@ -2281,38 +1905,30 @@ function formatTimelineEvents(events) {
|
|
|
2281
1905
|
|
|
2282
1906
|
// cli/commands/inspect/resolve-target.ts
|
|
2283
1907
|
async function resolveTargetCoords(bridge, target) {
|
|
2284
|
-
const arg = JSON.stringify(target.value);
|
|
2285
|
-
const findExpr = target.mode === "testid" ? `(await t.findByTestId(${arg})) || (await t.findById(${arg}))` : `await t.findByText(${arg})`;
|
|
2286
1908
|
const result = await bridge.send({
|
|
2287
|
-
type: "
|
|
2288
|
-
|
|
2289
|
-
const t = window.__sootsimTest
|
|
2290
|
-
if (!t) return null
|
|
2291
|
-
const n = ${findExpr}
|
|
2292
|
-
if (!n || !n.absolutePosition || !n.layout) return null
|
|
2293
|
-
const resolved =
|
|
2294
|
-
typeof n.nodeId === 'number' && typeof t.resolveTapTarget === 'function'
|
|
2295
|
-
? await t.resolveTapTarget(n.nodeId)
|
|
2296
|
-
: null
|
|
2297
|
-
const cx =
|
|
2298
|
-
typeof resolved?.cx === 'number'
|
|
2299
|
-
? resolved.cx
|
|
2300
|
-
: n.absolutePosition.x + (n.layout.width || 0) / 2
|
|
2301
|
-
const cy =
|
|
2302
|
-
typeof resolved?.cy === 'number'
|
|
2303
|
-
? resolved.cy
|
|
2304
|
-
: n.absolutePosition.y + (n.layout.height || 0) / 2
|
|
2305
|
-
return {
|
|
2306
|
-
x: cx,
|
|
2307
|
-
y: cy,
|
|
2308
|
-
id: n.id ?? null,
|
|
2309
|
-
testID: n.testID ?? null,
|
|
2310
|
-
text: ${JSON.stringify(target.mode === "text")} ? ${arg} : (n.text ?? n.accessibilityLabel ?? null),
|
|
2311
|
-
type: n.type ?? null,
|
|
2312
|
-
}
|
|
2313
|
-
})()`
|
|
1909
|
+
type: "resolve",
|
|
1910
|
+
selector: target.mode === "testid" ? { testID: target.value } : { text: target.value }
|
|
2314
1911
|
});
|
|
2315
|
-
|
|
1912
|
+
if (result === null) return null;
|
|
1913
|
+
if (typeof result !== "object") throw new Error("resolve returned an invalid result");
|
|
1914
|
+
const match = Reflect.get(result, "match");
|
|
1915
|
+
const resolvedTarget = Reflect.get(result, "target");
|
|
1916
|
+
const semanticNodes = [match, resolvedTarget];
|
|
1917
|
+
const point = Reflect.get(result, "point");
|
|
1918
|
+
const x = point !== null && typeof point === "object" ? Reflect.get(point, "x") : void 0;
|
|
1919
|
+
const y = point !== null && typeof point === "object" ? Reflect.get(point, "y") : void 0;
|
|
1920
|
+
if (!isSimSemanticTree(semanticNodes) || typeof x !== "number" || !Number.isFinite(x) || typeof y !== "number" || !Number.isFinite(y)) {
|
|
1921
|
+
throw new Error("resolve returned an invalid result");
|
|
1922
|
+
}
|
|
1923
|
+
const semanticMatch = semanticNodes[0];
|
|
1924
|
+
if (!semanticMatch) throw new Error("resolve returned an invalid result");
|
|
1925
|
+
return {
|
|
1926
|
+
x,
|
|
1927
|
+
y,
|
|
1928
|
+
testID: semanticMatch.testID ?? null,
|
|
1929
|
+
text: semanticMatch.text ?? (target.mode === "text" ? target.value : semanticMatch.label),
|
|
1930
|
+
type: semanticMatch.type
|
|
1931
|
+
};
|
|
2316
1932
|
}
|
|
2317
1933
|
|
|
2318
1934
|
// src/app-url.ts
|
|
@@ -2550,9 +2166,12 @@ var SimClient = class {
|
|
|
2550
2166
|
}
|
|
2551
2167
|
async tree(depth = 5) {
|
|
2552
2168
|
const { tree } = await inspectTree(this, depth);
|
|
2553
|
-
return tree;
|
|
2169
|
+
return Array.isArray(tree) ? tree : [];
|
|
2170
|
+
}
|
|
2171
|
+
resolve(selector) {
|
|
2172
|
+
return this.send({ type: "resolve", selector });
|
|
2554
2173
|
}
|
|
2555
|
-
/** compact visible
|
|
2174
|
+
/** compact visible semantic tree, formatted as the CLI prints it. */
|
|
2556
2175
|
describe(opts = {}) {
|
|
2557
2176
|
return inspectDescribe(this, {
|
|
2558
2177
|
describe: true,
|
|
@@ -2782,9 +2401,6 @@ var SimClient = class {
|
|
|
2782
2401
|
LAYOUT_GET_NATIVE_EVAL,
|
|
2783
2402
|
MERGED_CONSOLE_COUNT_EVAL,
|
|
2784
2403
|
NOISY_TIMELINE_KINDS,
|
|
2785
|
-
READY_CONTENT_NODE_FLOOR,
|
|
2786
|
-
READY_NODE_STABLE_MS,
|
|
2787
|
-
READY_PROBE_POLL_MS,
|
|
2788
2404
|
SimClient,
|
|
2789
2405
|
SimClientError,
|
|
2790
2406
|
SimConnectError,
|
|
@@ -2793,8 +2409,6 @@ var SimClient = class {
|
|
|
2793
2409
|
SimReadyTimeoutError,
|
|
2794
2410
|
SimResetError,
|
|
2795
2411
|
SimTapError,
|
|
2796
|
-
WAIT_READY_PROBE,
|
|
2797
|
-
WAIT_READY_PROGRESS_INTERVAL_MS,
|
|
2798
2412
|
buildSimOpenUrl,
|
|
2799
2413
|
clearConsole,
|
|
2800
2414
|
clearLogs,
|
|
@@ -2802,6 +2416,7 @@ var SimClient = class {
|
|
|
2802
2416
|
detectOpenNativeUI,
|
|
2803
2417
|
filterLogEntries,
|
|
2804
2418
|
formatLayoutElements,
|
|
2419
|
+
formatSemanticTree,
|
|
2805
2420
|
formatTimelineEvent,
|
|
2806
2421
|
formatTimelineEvents,
|
|
2807
2422
|
formatTimelinePayload,
|
|
@@ -2834,6 +2449,7 @@ var SimClient = class {
|
|
|
2834
2449
|
isInspectModeActive,
|
|
2835
2450
|
isInspectPickCommand,
|
|
2836
2451
|
isShellCommandUnavailable,
|
|
2452
|
+
isSimSemanticTree,
|
|
2837
2453
|
isTapSuccess,
|
|
2838
2454
|
layoutGetNativeEval,
|
|
2839
2455
|
rankInteractive,
|