execbro 2.3.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/build/__tests__/helpers/fake-cdp-server.d.ts +0 -12
- package/build/__tests__/helpers/fake-cdp-server.d.ts.map +1 -1
- package/build/__tests__/helpers/fake-cdp-server.js +0 -22
- package/build/__tests__/helpers/fake-cdp-server.js.map +1 -1
- package/build/__tests__/helpers/tap-test-helpers.d.ts +0 -23
- package/build/__tests__/helpers/tap-test-helpers.d.ts.map +1 -1
- package/build/__tests__/helpers/tap-test-helpers.js +2 -29
- package/build/__tests__/helpers/tap-test-helpers.js.map +1 -1
- package/build/__tests__/integration/auto-reconnect.test.js +4 -14
- package/build/__tests__/integration/auto-reconnect.test.js.map +1 -1
- package/build/__tests__/integration/connection-health.test.js +1 -10
- package/build/__tests__/integration/connection-health.test.js.map +1 -1
- package/build/__tests__/integration/execute-in-app.test.js +1 -11
- package/build/__tests__/integration/execute-in-app.test.js.map +1 -1
- package/build/__tests__/integration/native-logs.test.js +0 -5
- package/build/__tests__/integration/native-logs.test.js.map +1 -1
- package/build/__tests__/integration/per-call-timeout.test.js +1 -5
- package/build/__tests__/integration/per-call-timeout.test.js.map +1 -1
- package/build/__tests__/integration/socket-close-midflight.test.js +2 -12
- package/build/__tests__/integration/socket-close-midflight.test.js.map +1 -1
- package/build/__tests__/integration/stale-target-probe.test.js +2 -17
- package/build/__tests__/integration/stale-target-probe.test.js.map +1 -1
- package/build/__tests__/integration/tap-strategies.test.js +2 -36
- package/build/__tests__/integration/tap-strategies.test.js.map +1 -1
- package/build/__tests__/integration/text-input.test.js +0 -39
- package/build/__tests__/integration/text-input.test.js.map +1 -1
- package/build/__tests__/integration/tools.test.js +1 -7
- package/build/__tests__/integration/tools.test.js.map +1 -1
- package/build/__tests__/setup.js +0 -2
- package/build/__tests__/setup.js.map +1 -1
- package/build/__tests__/unit/androidEmulatorAvds.test.js +0 -10
- package/build/__tests__/unit/androidEmulatorAvds.test.js.map +1 -1
- package/build/__tests__/unit/androidPinch.test.d.ts +2 -0
- package/build/__tests__/unit/androidPinch.test.d.ts.map +1 -0
- package/build/__tests__/unit/androidPinch.test.js +119 -0
- package/build/__tests__/unit/androidPinch.test.js.map +1 -0
- package/build/__tests__/unit/appContext.test.js +0 -2
- package/build/__tests__/unit/appContext.test.js.map +1 -1
- package/build/__tests__/unit/appRequest.test.js +0 -3
- package/build/__tests__/unit/appRequest.test.js.map +1 -1
- package/build/__tests__/unit/buildToken.test.js +0 -6
- package/build/__tests__/unit/buildToken.test.js.map +1 -1
- package/build/__tests__/unit/burst.test.js +0 -1
- package/build/__tests__/unit/burst.test.js.map +1 -1
- package/build/__tests__/unit/clearLogsNative.test.js +0 -8
- package/build/__tests__/unit/clearLogsNative.test.js.map +1 -1
- package/build/__tests__/unit/componentFormatNaming.test.js +0 -13
- package/build/__tests__/unit/componentFormatNaming.test.js.map +1 -1
- package/build/__tests__/unit/componentSource.test.js +0 -4
- package/build/__tests__/unit/componentSource.test.js.map +1 -1
- package/build/__tests__/unit/componentSourceResolve.test.js +0 -1
- package/build/__tests__/unit/componentSourceResolve.test.js.map +1 -1
- package/build/__tests__/unit/contextWiring.test.js +0 -3
- package/build/__tests__/unit/contextWiring.test.js.map +1 -1
- package/build/__tests__/unit/coordinateMiss.test.js +0 -16
- package/build/__tests__/unit/coordinateMiss.test.js.map +1 -1
- package/build/__tests__/unit/deviceDiscovery.test.js +0 -4
- package/build/__tests__/unit/deviceDiscovery.test.js.map +1 -1
- package/build/__tests__/unit/deviceResolution.test.js +0 -2
- package/build/__tests__/unit/deviceResolution.test.js.map +1 -1
- package/build/__tests__/unit/deviceResolver.test.js +0 -1
- package/build/__tests__/unit/deviceResolver.test.js.map +1 -1
- package/build/__tests__/unit/deviceResolverStaleRetry.test.js +0 -8
- package/build/__tests__/unit/deviceResolverStaleRetry.test.js.map +1 -1
- package/build/__tests__/unit/emulatorBridge.test.d.ts +2 -0
- package/build/__tests__/unit/emulatorBridge.test.d.ts.map +1 -0
- package/build/__tests__/unit/emulatorBridge.test.js +76 -0
- package/build/__tests__/unit/emulatorBridge.test.js.map +1 -0
- package/build/__tests__/unit/emulatorGrpc.test.d.ts +2 -0
- package/build/__tests__/unit/emulatorGrpc.test.d.ts.map +1 -0
- package/build/__tests__/unit/emulatorGrpc.test.js +33 -0
- package/build/__tests__/unit/emulatorGrpc.test.js.map +1 -0
- package/build/__tests__/unit/epochRender.test.js +0 -3
- package/build/__tests__/unit/epochRender.test.js.map +1 -1
- package/build/__tests__/unit/errorCategorization.test.js +0 -18
- package/build/__tests__/unit/errorCategorization.test.js.map +1 -1
- package/build/__tests__/unit/exec.test.js +0 -1
- package/build/__tests__/unit/exec.test.js.map +1 -1
- package/build/__tests__/unit/executor.test.js +0 -15
- package/build/__tests__/unit/executor.test.js.map +1 -1
- package/build/__tests__/unit/findElementTreeReuse.test.js +0 -12
- package/build/__tests__/unit/findElementTreeReuse.test.js.map +1 -1
- package/build/__tests__/unit/fingerprint.test.js +0 -4
- package/build/__tests__/unit/fingerprint.test.js.map +1 -1
- package/build/__tests__/unit/focusedInputTools.test.js +0 -4
- package/build/__tests__/unit/focusedInputTools.test.js.map +1 -1
- package/build/__tests__/unit/getLogsEmptyContract.test.js +0 -30
- package/build/__tests__/unit/getLogsEmptyContract.test.js.map +1 -1
- package/build/__tests__/unit/getLogsNativeSource.test.js +0 -22
- package/build/__tests__/unit/getLogsNativeSource.test.js.map +1 -1
- package/build/__tests__/unit/graphqlOperation.test.d.ts +2 -0
- package/build/__tests__/unit/graphqlOperation.test.d.ts.map +1 -0
- package/build/__tests__/unit/graphqlOperation.test.js +107 -0
- package/build/__tests__/unit/graphqlOperation.test.js.map +1 -0
- package/build/__tests__/unit/inputTarget.test.js +0 -16
- package/build/__tests__/unit/inputTarget.test.js.map +1 -1
- package/build/__tests__/unit/inputTextTool.test.js +0 -3
- package/build/__tests__/unit/inputTextTool.test.js.map +1 -1
- package/build/__tests__/unit/jsLogEvents.test.js +0 -3
- package/build/__tests__/unit/jsLogEvents.test.js.map +1 -1
- package/build/__tests__/unit/keyboardMetrics.test.js +0 -2
- package/build/__tests__/unit/keyboardMetrics.test.js.map +1 -1
- package/build/__tests__/unit/keyboardRaise.test.js +0 -2
- package/build/__tests__/unit/keyboardRaise.test.js.map +1 -1
- package/build/__tests__/unit/licenseGrace.test.js +1 -4
- package/build/__tests__/unit/licenseGrace.test.js.map +1 -1
- package/build/__tests__/unit/logEventFormat.test.js +0 -27
- package/build/__tests__/unit/logEventFormat.test.js.map +1 -1
- package/build/__tests__/unit/logEvents.test.js +1 -5
- package/build/__tests__/unit/logEvents.test.js.map +1 -1
- package/build/__tests__/unit/logGrouping.test.js +0 -2
- package/build/__tests__/unit/logGrouping.test.js.map +1 -1
- package/build/__tests__/unit/logOwnership.test.js +0 -11
- package/build/__tests__/unit/logOwnership.test.js.map +1 -1
- package/build/__tests__/unit/logRelevance.test.js +0 -6
- package/build/__tests__/unit/logRelevance.test.js.map +1 -1
- package/build/__tests__/unit/logSourceAndroid.test.js +0 -4
- package/build/__tests__/unit/logSourceAndroid.test.js.map +1 -1
- package/build/__tests__/unit/logSourceIos.test.js +0 -5
- package/build/__tests__/unit/logSourceIos.test.js.map +1 -1
- package/build/__tests__/unit/logs.test.js +0 -1
- package/build/__tests__/unit/logs.test.js.map +1 -1
- package/build/__tests__/unit/measureComponentTools.test.js +0 -1
- package/build/__tests__/unit/measureComponentTools.test.js.map +1 -1
- package/build/__tests__/unit/metro.test.js +0 -2
- package/build/__tests__/unit/metro.test.js.map +1 -1
- package/build/__tests__/unit/nativeInputValue.test.js +0 -4
- package/build/__tests__/unit/nativeInputValue.test.js.map +1 -1
- package/build/__tests__/unit/nativeLogs.test.js +1 -23
- package/build/__tests__/unit/nativeLogs.test.js.map +1 -1
- package/build/__tests__/unit/navigateVerify.test.js +0 -6
- package/build/__tests__/unit/navigateVerify.test.js.map +1 -1
- package/build/__tests__/unit/navigation.test.js +0 -5
- package/build/__tests__/unit/navigation.test.js.map +1 -1
- package/build/__tests__/unit/networkInterceptor.test.js +0 -2
- package/build/__tests__/unit/networkInterceptor.test.js.map +1 -1
- package/build/__tests__/unit/overlayGuard.test.d.ts +2 -0
- package/build/__tests__/unit/overlayGuard.test.d.ts.map +1 -0
- package/build/__tests__/unit/overlayGuard.test.js +123 -0
- package/build/__tests__/unit/overlayGuard.test.js.map +1 -0
- package/build/__tests__/unit/pendingExecutions.test.js +0 -6
- package/build/__tests__/unit/pendingExecutions.test.js.map +1 -1
- package/build/__tests__/unit/pinchGeometry.test.d.ts +2 -0
- package/build/__tests__/unit/pinchGeometry.test.d.ts.map +1 -0
- package/build/__tests__/unit/pinchGeometry.test.js +139 -0
- package/build/__tests__/unit/pinchGeometry.test.js.map +1 -0
- package/build/__tests__/unit/pinchTool.test.d.ts +2 -0
- package/build/__tests__/unit/pinchTool.test.d.ts.map +1 -0
- package/build/__tests__/unit/pinchTool.test.js +39 -0
- package/build/__tests__/unit/pinchTool.test.js.map +1 -0
- package/build/__tests__/unit/probe.test.js +0 -1
- package/build/__tests__/unit/probe.test.js.map +1 -1
- package/build/__tests__/unit/projectMemory.test.js +6 -7
- package/build/__tests__/unit/projectMemory.test.js.map +1 -1
- package/build/__tests__/unit/projectStore.test.js +2 -2
- package/build/__tests__/unit/projectStore.test.js.map +1 -1
- package/build/__tests__/unit/reloadAppAutoConnect.test.js +0 -9
- package/build/__tests__/unit/reloadAppAutoConnect.test.js.map +1 -1
- package/build/__tests__/unit/rnGlobalsBootstrap.test.js +3 -3
- package/build/__tests__/unit/rnGlobalsBootstrap.test.js.map +1 -1
- package/build/__tests__/unit/routeHistory.test.d.ts +2 -0
- package/build/__tests__/unit/routeHistory.test.d.ts.map +1 -0
- package/build/__tests__/unit/routeHistory.test.js +136 -0
- package/build/__tests__/unit/routeHistory.test.js.map +1 -0
- package/build/__tests__/unit/screenSpace.test.js +30 -13
- package/build/__tests__/unit/screenSpace.test.js.map +1 -1
- package/build/__tests__/unit/screenState.test.js +3 -6
- package/build/__tests__/unit/screenState.test.js.map +1 -1
- package/build/__tests__/unit/screenStateInputValue.test.js +0 -7
- package/build/__tests__/unit/screenStateInputValue.test.js.map +1 -1
- package/build/__tests__/unit/screenStateKeyboard.test.js +0 -1
- package/build/__tests__/unit/screenStateKeyboard.test.js.map +1 -1
- package/build/__tests__/unit/screenshot-diff.test.js +0 -6
- package/build/__tests__/unit/screenshot-diff.test.js.map +1 -1
- package/build/__tests__/unit/sdkMirrorPoller.test.js +0 -8
- package/build/__tests__/unit/sdkMirrorPoller.test.js.map +1 -1
- package/build/__tests__/unit/settleAndDiff.test.js +0 -11
- package/build/__tests__/unit/settleAndDiff.test.js.map +1 -1
- package/build/__tests__/unit/signedVerdict.test.js +0 -5
- package/build/__tests__/unit/signedVerdict.test.js.map +1 -1
- package/build/__tests__/unit/swipeDirection.test.js +6 -9
- package/build/__tests__/unit/swipeDirection.test.js.map +1 -1
- package/build/__tests__/unit/symbolicate.test.js +0 -1
- package/build/__tests__/unit/symbolicate.test.js.map +1 -1
- package/build/__tests__/unit/tap.test.js +2 -40
- package/build/__tests__/unit/tap.test.js.map +1 -1
- package/build/__tests__/unit/textEntry.test.js +0 -27
- package/build/__tests__/unit/textEntry.test.js.map +1 -1
- package/build/__tests__/unit/toolDescriptions.test.js +0 -40
- package/build/__tests__/unit/toolDescriptions.test.js.map +1 -1
- package/build/__tests__/unit/toolsJson.test.js +0 -10
- package/build/__tests__/unit/toolsJson.test.js.map +1 -1
- package/build/__tests__/unit/transportClassify.test.js +0 -20
- package/build/__tests__/unit/transportClassify.test.js.map +1 -1
- package/build/__tests__/unit/truncate.test.js +0 -5
- package/build/__tests__/unit/truncate.test.js.map +1 -1
- package/build/__tests__/unit/usageCache.test.js +0 -3
- package/build/__tests__/unit/usageCache.test.js.map +1 -1
- package/build/__tests__/unit/usageGate.test.js +1 -1
- package/build/__tests__/unit/usageGate.test.js.map +1 -1
- package/build/__tests__/unit/visibility.test.js +0 -8
- package/build/__tests__/unit/visibility.test.js.map +1 -1
- package/build/__tests__/unit/waitForMeasurements.test.js +2 -7
- package/build/__tests__/unit/waitForMeasurements.test.js.map +1 -1
- package/build/bin.js +0 -18
- package/build/bin.js.map +1 -1
- package/build/core/android.d.ts +0 -120
- package/build/core/android.d.ts.map +1 -1
- package/build/core/android.js +4 -259
- package/build/core/android.js.map +1 -1
- package/build/core/androidPinch.d.ts +21 -0
- package/build/core/androidPinch.d.ts.map +1 -0
- package/build/core/androidPinch.js +72 -0
- package/build/core/androidPinch.js.map +1 -0
- package/build/core/androidSystemBars.d.ts +7 -0
- package/build/core/androidSystemBars.d.ts.map +1 -0
- package/build/core/androidSystemBars.js +21 -0
- package/build/core/androidSystemBars.js.map +1 -0
- package/build/core/appContext.d.ts +0 -50
- package/build/core/appContext.d.ts.map +1 -1
- package/build/core/appContext.js +0 -50
- package/build/core/appContext.js.map +1 -1
- package/build/core/appDetection.d.ts +0 -16
- package/build/core/appDetection.d.ts.map +1 -1
- package/build/core/appDetection.js +0 -36
- package/build/core/appDetection.js.map +1 -1
- package/build/core/appRequest.d.ts +0 -12
- package/build/core/appRequest.d.ts.map +1 -1
- package/build/core/appRequest.js +0 -15
- package/build/core/appRequest.js.map +1 -1
- package/build/core/bufferConfig.d.ts +0 -2
- package/build/core/bufferConfig.d.ts.map +1 -1
- package/build/core/bufferConfig.js +0 -2
- package/build/core/bufferConfig.js.map +1 -1
- package/build/core/bundle.js +0 -40
- package/build/core/bundle.js.map +1 -1
- package/build/core/componentSearch.d.ts +0 -19
- package/build/core/componentSearch.d.ts.map +1 -1
- package/build/core/componentSearch.js +1 -22
- package/build/core/componentSearch.js.map +1 -1
- package/build/core/componentSource.d.ts +0 -21
- package/build/core/componentSource.d.ts.map +1 -1
- package/build/core/componentSource.js +0 -27
- package/build/core/componentSource.js.map +1 -1
- package/build/core/componentTree.js +0 -12
- package/build/core/componentTree.js.map +1 -1
- package/build/core/config.d.ts +0 -8
- package/build/core/config.d.ts.map +1 -1
- package/build/core/config.js +0 -10
- package/build/core/config.js.map +1 -1
- package/build/core/connection.d.ts +0 -76
- package/build/core/connection.d.ts.map +1 -1
- package/build/core/connection.js +15 -340
- package/build/core/connection.js.map +1 -1
- package/build/core/connectionState.d.ts +0 -78
- package/build/core/connectionState.d.ts.map +1 -1
- package/build/core/connectionState.js +4 -92
- package/build/core/connectionState.js.map +1 -1
- package/build/core/debugGlobals.js +0 -68
- package/build/core/debugGlobals.js.map +1 -1
- package/build/core/deviceDiscovery.d.ts +0 -5
- package/build/core/deviceDiscovery.d.ts.map +1 -1
- package/build/core/deviceDiscovery.js +0 -12
- package/build/core/deviceDiscovery.js.map +1 -1
- package/build/core/deviceResolver.d.ts +0 -10
- package/build/core/deviceResolver.d.ts.map +1 -1
- package/build/core/deviceResolver.js +0 -54
- package/build/core/deviceResolver.js.map +1 -1
- package/build/core/emulatorBridge.d.ts +17 -0
- package/build/core/emulatorBridge.d.ts.map +1 -0
- package/build/core/emulatorBridge.js +78 -0
- package/build/core/emulatorBridge.js.map +1 -0
- package/build/core/emulatorGrpc.d.ts +14 -0
- package/build/core/emulatorGrpc.d.ts.map +1 -0
- package/build/core/emulatorGrpc.js +93 -0
- package/build/core/emulatorGrpc.js.map +1 -0
- package/build/core/epochRender.d.ts +0 -12
- package/build/core/epochRender.d.ts.map +1 -1
- package/build/core/epochRender.js +0 -12
- package/build/core/epochRender.js.map +1 -1
- package/build/core/errorScreenParser.d.ts +0 -21
- package/build/core/errorScreenParser.d.ts.map +1 -1
- package/build/core/errorScreenParser.js +0 -50
- package/build/core/errorScreenParser.js.map +1 -1
- package/build/core/errors.d.ts +0 -15
- package/build/core/errors.d.ts.map +1 -1
- package/build/core/errors.js +0 -15
- package/build/core/errors.js.map +1 -1
- package/build/core/escapeNonAscii.d.ts +0 -9
- package/build/core/escapeNonAscii.d.ts.map +1 -1
- package/build/core/escapeNonAscii.js +1 -43
- package/build/core/escapeNonAscii.js.map +1 -1
- package/build/core/exec.d.ts +0 -12
- package/build/core/exec.d.ts.map +1 -1
- package/build/core/exec.js +2 -14
- package/build/core/exec.js.map +1 -1
- package/build/core/executor.js +0 -3
- package/build/core/executor.js.map +1 -1
- package/build/core/fastRefreshRecorder.js +0 -4
- package/build/core/fastRefreshRecorder.js.map +1 -1
- package/build/core/fastRefreshTools.js +0 -3
- package/build/core/fastRefreshTools.js.map +1 -1
- package/build/core/feedback.js +0 -4
- package/build/core/feedback.js.map +1 -1
- package/build/core/graphqlOperation.d.ts +13 -0
- package/build/core/graphqlOperation.d.ts.map +1 -0
- package/build/core/graphqlOperation.js +80 -0
- package/build/core/graphqlOperation.js.map +1 -0
- package/build/core/guides.d.ts +0 -10
- package/build/core/guides.d.ts.map +1 -1
- package/build/core/guides.js +18 -12
- package/build/core/guides.js.map +1 -1
- package/build/core/iconSemantics.d.ts +0 -15
- package/build/core/iconSemantics.d.ts.map +1 -1
- package/build/core/iconSemantics.js +0 -31
- package/build/core/iconSemantics.js.map +1 -1
- package/build/core/index.d.ts +2 -0
- package/build/core/index.d.ts.map +1 -1
- package/build/core/index.js +5 -47
- package/build/core/index.js.map +1 -1
- package/build/core/injected/visibility.d.ts +0 -30
- package/build/core/injected/visibility.d.ts.map +1 -1
- package/build/core/injected/visibility.js +0 -33
- package/build/core/injected/visibility.js.map +1 -1
- package/build/core/injectedFilters.d.ts +0 -28
- package/build/core/injectedFilters.d.ts.map +1 -1
- package/build/core/injectedFilters.js +0 -28
- package/build/core/injectedFilters.js.map +1 -1
- package/build/core/inputTarget.d.ts +1 -34
- package/build/core/inputTarget.d.ts.map +1 -1
- package/build/core/inputTarget.js +0 -26
- package/build/core/inputTarget.js.map +1 -1
- package/build/core/inputTargetTools.d.ts +0 -7
- package/build/core/inputTargetTools.d.ts.map +1 -1
- package/build/core/inputTargetTools.js +0 -7
- package/build/core/inputTargetTools.js.map +1 -1
- package/build/core/inspector.d.ts +0 -33
- package/build/core/inspector.d.ts.map +1 -1
- package/build/core/inspector.js +27 -40
- package/build/core/inspector.js.map +1 -1
- package/build/core/ios.d.ts +11 -122
- package/build/core/ios.d.ts.map +1 -1
- package/build/core/ios.js +32 -299
- package/build/core/ios.js.map +1 -1
- package/build/core/jsExecute.d.ts +0 -100
- package/build/core/jsExecute.d.ts.map +1 -1
- package/build/core/jsExecute.js +0 -261
- package/build/core/jsExecute.js.map +1 -1
- package/build/core/jsLogEvents.d.ts +0 -8
- package/build/core/jsLogEvents.d.ts.map +1 -1
- package/build/core/jsLogEvents.js +0 -8
- package/build/core/jsLogEvents.js.map +1 -1
- package/build/core/keyboardMetrics.js +0 -8
- package/build/core/keyboardMetrics.js.map +1 -1
- package/build/core/keyboardRaise.d.ts +0 -8
- package/build/core/keyboardRaise.d.ts.map +1 -1
- package/build/core/keyboardRaise.js +0 -23
- package/build/core/keyboardRaise.js.map +1 -1
- package/build/core/license.d.ts +0 -4
- package/build/core/license.d.ts.map +1 -1
- package/build/core/license.js +2 -55
- package/build/core/license.js.map +1 -1
- package/build/core/logDiagnosis.d.ts +0 -34
- package/build/core/logDiagnosis.d.ts.map +1 -1
- package/build/core/logDiagnosis.js +0 -15
- package/build/core/logDiagnosis.js.map +1 -1
- package/build/core/logEventFormat.d.ts +0 -1
- package/build/core/logEventFormat.d.ts.map +1 -1
- package/build/core/logEventFormat.js +0 -30
- package/build/core/logEventFormat.js.map +1 -1
- package/build/core/logEvents.d.ts +0 -54
- package/build/core/logEvents.d.ts.map +1 -1
- package/build/core/logEvents.js +0 -37
- package/build/core/logEvents.js.map +1 -1
- package/build/core/logGrouping.d.ts +0 -9
- package/build/core/logGrouping.d.ts.map +1 -1
- package/build/core/logGrouping.js +0 -17
- package/build/core/logGrouping.js.map +1 -1
- package/build/core/logOwnership.d.ts +0 -17
- package/build/core/logOwnership.d.ts.map +1 -1
- package/build/core/logOwnership.js +0 -34
- package/build/core/logOwnership.js.map +1 -1
- package/build/core/logRelevance.d.ts +0 -8
- package/build/core/logRelevance.d.ts.map +1 -1
- package/build/core/logRelevance.js +0 -15
- package/build/core/logRelevance.js.map +1 -1
- package/build/core/logSourceAndroid.d.ts +0 -8
- package/build/core/logSourceAndroid.d.ts.map +1 -1
- package/build/core/logSourceAndroid.js +0 -11
- package/build/core/logSourceAndroid.js.map +1 -1
- package/build/core/logSourceIos.d.ts +0 -8
- package/build/core/logSourceIos.d.ts.map +1 -1
- package/build/core/logSourceIos.js +1 -24
- package/build/core/logSourceIos.js.map +1 -1
- package/build/core/logbox.d.ts +0 -30
- package/build/core/logbox.d.ts.map +1 -1
- package/build/core/logbox.js +0 -43
- package/build/core/logbox.js.map +1 -1
- package/build/core/logs.d.ts +0 -6
- package/build/core/logs.d.ts.map +1 -1
- package/build/core/logs.js +0 -19
- package/build/core/logs.js.map +1 -1
- package/build/core/metro.d.ts +0 -14
- package/build/core/metro.d.ts.map +1 -1
- package/build/core/metro.js +1 -32
- package/build/core/metro.js.map +1 -1
- package/build/core/moduleRegistry.d.ts +0 -17
- package/build/core/moduleRegistry.d.ts.map +1 -1
- package/build/core/moduleRegistry.js +0 -17
- package/build/core/moduleRegistry.js.map +1 -1
- package/build/core/nativeInputValue.d.ts +0 -29
- package/build/core/nativeInputValue.d.ts.map +1 -1
- package/build/core/nativeInputValue.js +0 -18
- package/build/core/nativeInputValue.js.map +1 -1
- package/build/core/nativeLogs.d.ts +0 -47
- package/build/core/nativeLogs.d.ts.map +1 -1
- package/build/core/nativeLogs.js +0 -82
- package/build/core/nativeLogs.js.map +1 -1
- package/build/core/nativeOnlyHints.js +0 -4
- package/build/core/nativeOnlyHints.js.map +1 -1
- package/build/core/navigate.d.ts +0 -15
- package/build/core/navigate.d.ts.map +1 -1
- package/build/core/navigate.js +0 -22
- package/build/core/navigate.js.map +1 -1
- package/build/core/navigation.d.ts +0 -60
- package/build/core/navigation.d.ts.map +1 -1
- package/build/core/navigation.js +0 -60
- package/build/core/navigation.js.map +1 -1
- package/build/core/network.d.ts +0 -5
- package/build/core/network.d.ts.map +1 -1
- package/build/core/network.js +10 -31
- package/build/core/network.js.map +1 -1
- package/build/core/networkInterceptor.d.ts +0 -19
- package/build/core/networkInterceptor.d.ts.map +1 -1
- package/build/core/networkInterceptor.js +3 -27
- package/build/core/networkInterceptor.js.map +1 -1
- package/build/core/ocr.d.ts +0 -31
- package/build/core/ocr.d.ts.map +1 -1
- package/build/core/ocr.js +1 -71
- package/build/core/ocr.js.map +1 -1
- package/build/core/paths.js +0 -1
- package/build/core/paths.js.map +1 -1
- package/build/core/pinchGeometry.d.ts +28 -0
- package/build/core/pinchGeometry.d.ts.map +1 -0
- package/build/core/pinchGeometry.js +97 -0
- package/build/core/pinchGeometry.js.map +1 -0
- package/build/core/pinchThresholds.d.ts +14 -0
- package/build/core/pinchThresholds.d.ts.map +1 -0
- package/build/core/pinchThresholds.js +9 -0
- package/build/core/pinchThresholds.js.map +1 -0
- package/build/core/posthog.js +0 -7
- package/build/core/posthog.js.map +1 -1
- package/build/core/pressables.d.ts +0 -25
- package/build/core/pressables.d.ts.map +1 -1
- package/build/core/pressables.js +0 -37
- package/build/core/pressables.js.map +1 -1
- package/build/core/probe.d.ts +0 -9
- package/build/core/probe.d.ts.map +1 -1
- package/build/core/probe.js +0 -9
- package/build/core/probe.js.map +1 -1
- package/build/core/projectMemory.d.ts +0 -2
- package/build/core/projectMemory.d.ts.map +1 -1
- package/build/core/projectMemory.js +2 -7
- package/build/core/projectMemory.js.map +1 -1
- package/build/core/projectStore.d.ts +0 -18
- package/build/core/projectStore.d.ts.map +1 -1
- package/build/core/projectStore.js +0 -19
- package/build/core/projectStore.js.map +1 -1
- package/build/core/promiseHandles.d.ts +0 -13
- package/build/core/promiseHandles.d.ts.map +1 -1
- package/build/core/promiseHandles.js +0 -25
- package/build/core/promiseHandles.js.map +1 -1
- package/build/core/register.js +0 -44
- package/build/core/register.js.map +1 -1
- package/build/core/rnGlobalsBootstrap.d.ts +0 -13
- package/build/core/rnGlobalsBootstrap.d.ts.map +1 -1
- package/build/core/rnGlobalsBootstrap.js +0 -22
- package/build/core/rnGlobalsBootstrap.js.map +1 -1
- package/build/core/routeHistory.d.ts +24 -0
- package/build/core/routeHistory.d.ts.map +1 -0
- package/build/core/routeHistory.js +186 -0
- package/build/core/routeHistory.js.map +1 -0
- package/build/core/screenLayout.d.ts +0 -11
- package/build/core/screenLayout.d.ts.map +1 -1
- package/build/core/screenLayout.js +24 -61
- package/build/core/screenLayout.js.map +1 -1
- package/build/core/screenSpace.d.ts +32 -44
- package/build/core/screenSpace.d.ts.map +1 -1
- package/build/core/screenSpace.js +80 -46
- package/build/core/screenSpace.js.map +1 -1
- package/build/core/screenSpaceDevice.d.ts +0 -8
- package/build/core/screenSpaceDevice.d.ts.map +1 -1
- package/build/core/screenSpaceDevice.js +31 -14
- package/build/core/screenSpaceDevice.js.map +1 -1
- package/build/core/screenState.d.ts +2 -74
- package/build/core/screenState.d.ts.map +1 -1
- package/build/core/screenState.js +204 -145
- package/build/core/screenState.js.map +1 -1
- package/build/core/sdkBridge.js +0 -8
- package/build/core/sdkBridge.js.map +1 -1
- package/build/core/sdkMirrorPoller.d.ts +0 -18
- package/build/core/sdkMirrorPoller.d.ts.map +1 -1
- package/build/core/sdkMirrorPoller.js +0 -47
- package/build/core/sdkMirrorPoller.js.map +1 -1
- package/build/core/selectionBuffer.d.ts +0 -6
- package/build/core/selectionBuffer.d.ts.map +1 -1
- package/build/core/selectionBuffer.js +0 -5
- package/build/core/selectionBuffer.js.map +1 -1
- package/build/core/signedVerdict.js +0 -6
- package/build/core/signedVerdict.js.map +1 -1
- package/build/core/state.d.ts +0 -31
- package/build/core/state.d.ts.map +1 -1
- package/build/core/state.js +0 -57
- package/build/core/state.js.map +1 -1
- package/build/core/symbolicate.js +0 -11
- package/build/core/symbolicate.js.map +1 -1
- package/build/core/telemetry.d.ts +0 -47
- package/build/core/telemetry.d.ts.map +1 -1
- package/build/core/telemetry.js +1 -173
- package/build/core/telemetry.js.map +1 -1
- package/build/core/textEntry.d.ts +0 -34
- package/build/core/textEntry.d.ts.map +1 -1
- package/build/core/textEntry.js +0 -73
- package/build/core/textEntry.js.map +1 -1
- package/build/core/toolHelpers.d.ts +0 -12
- package/build/core/toolHelpers.d.ts.map +1 -1
- package/build/core/toolHelpers.js +2 -47
- package/build/core/toolHelpers.js.map +1 -1
- package/build/core/truncate.d.ts +0 -17
- package/build/core/truncate.d.ts.map +1 -1
- package/build/core/truncate.js +0 -32
- package/build/core/truncate.js.map +1 -1
- package/build/core/types.d.ts +0 -6
- package/build/core/types.d.ts.map +1 -1
- package/build/core/usageCache.js +0 -12
- package/build/core/usageCache.js.map +1 -1
- package/build/index.js +0 -18
- package/build/index.js.map +1 -1
- package/build/pro/coordinateMiss.d.ts +0 -30
- package/build/pro/coordinateMiss.d.ts.map +1 -1
- package/build/pro/coordinateMiss.js +0 -17
- package/build/pro/coordinateMiss.js.map +1 -1
- package/build/pro/overlayGuard.d.ts +27 -0
- package/build/pro/overlayGuard.d.ts.map +1 -0
- package/build/pro/overlayGuard.js +103 -0
- package/build/pro/overlayGuard.js.map +1 -0
- package/build/pro/screenshot-diff.js +4 -6
- package/build/pro/screenshot-diff.js.map +1 -1
- package/build/pro/tap.d.ts +0 -55
- package/build/pro/tap.d.ts.map +1 -1
- package/build/pro/tap.js +72 -316
- package/build/pro/tap.js.map +1 -1
- package/build/pro/usageGate.js +0 -9
- package/build/pro/usageGate.js.map +1 -1
- package/build/pro/usageNotifications.js +1 -13
- package/build/pro/usageNotifications.js.map +1 -1
- package/build/pro/verifyAction.d.ts +0 -13
- package/build/pro/verifyAction.d.ts.map +1 -1
- package/build/pro/verifyAction.js +0 -45
- package/build/pro/verifyAction.js.map +1 -1
- package/build/tools/_deviceArg.d.ts +0 -35
- package/build/tools/_deviceArg.d.ts.map +1 -1
- package/build/tools/_deviceArg.js +0 -35
- package/build/tools/_deviceArg.js.map +1 -1
- package/build/tools/accountTools.js +2 -2
- package/build/tools/accountTools.js.map +1 -1
- package/build/tools/bundleTools.js +0 -16
- package/build/tools/bundleTools.js.map +1 -1
- package/build/tools/componentTools.d.ts.map +1 -1
- package/build/tools/componentTools.js +47 -53
- package/build/tools/componentTools.js.map +1 -1
- package/build/tools/connectionTools.js +1 -59
- package/build/tools/connectionTools.js.map +1 -1
- package/build/tools/deviceTools.js +0 -11
- package/build/tools/deviceTools.js.map +1 -1
- package/build/tools/executionTools.js +1 -18
- package/build/tools/executionTools.js.map +1 -1
- package/build/tools/interactionTools.d.ts +0 -10
- package/build/tools/interactionTools.d.ts.map +1 -1
- package/build/tools/interactionTools.js +190 -59
- package/build/tools/interactionTools.js.map +1 -1
- package/build/tools/logTools.d.ts +0 -13
- package/build/tools/logTools.d.ts.map +1 -1
- package/build/tools/logTools.js +2 -126
- package/build/tools/logTools.js.map +1 -1
- package/build/tools/metaTools.js +0 -10
- package/build/tools/metaTools.js.map +1 -1
- package/build/tools/networkTools.js +4 -43
- package/build/tools/networkTools.js.map +1 -1
- package/build/tools/requestTools.js +0 -5
- package/build/tools/requestTools.js.map +1 -1
- package/build/tools/screenshotTools.js +5 -111
- package/build/tools/screenshotTools.js.map +1 -1
- package/package.json +2 -2
- package/tools.json +16 -1
package/build/core/ios.js
CHANGED
|
@@ -8,13 +8,9 @@ import { getActiveSimulatorUdid } from "./state.js";
|
|
|
8
8
|
import { notifyDriverMissing } from "./logbox.js";
|
|
9
9
|
import { execAsync } from "./exec.js";
|
|
10
10
|
const execFileAsync = promisify(execFile);
|
|
11
|
-
// simctl command timeout in milliseconds
|
|
12
11
|
const SIMCTL_TIMEOUT = 30000;
|
|
13
|
-
// IDB command timeout in milliseconds
|
|
14
12
|
const IDB_TIMEOUT = 30000;
|
|
15
|
-
// AXe command timeout in milliseconds
|
|
16
13
|
const AXE_TIMEOUT = 30000;
|
|
17
|
-
// Valid button types for IDB ui button command
|
|
18
14
|
export const IOS_BUTTON_TYPES = [
|
|
19
15
|
"HOME",
|
|
20
16
|
"LOCK",
|
|
@@ -22,35 +18,19 @@ export const IOS_BUTTON_TYPES = [
|
|
|
22
18
|
"SIRI",
|
|
23
19
|
"APPLE_PAY",
|
|
24
20
|
];
|
|
25
|
-
// Track connected IDB simulators to avoid redundant connect calls
|
|
26
21
|
const connectedIdbSimulators = new Set();
|
|
27
|
-
/**
|
|
28
|
-
* Get the IDB executable path
|
|
29
|
-
* Supports IDB_PATH environment variable for custom installations
|
|
30
|
-
*/
|
|
31
22
|
function getIdbPath() {
|
|
32
23
|
return process.env.IDB_PATH || "idb";
|
|
33
24
|
}
|
|
34
|
-
/**
|
|
35
|
-
* Get the active iOS UI driver.
|
|
36
|
-
* Defaults to AXe; set IOS_DRIVER=idb in MCP server env to use IDB instead.
|
|
37
|
-
*/
|
|
38
25
|
function getIosDriver() {
|
|
39
26
|
const driver = process.env.IOS_DRIVER?.toLowerCase();
|
|
40
27
|
if (driver === "idb")
|
|
41
28
|
return "idb";
|
|
42
29
|
return "axe";
|
|
43
30
|
}
|
|
44
|
-
/**
|
|
45
|
-
* Get the AXe executable path.
|
|
46
|
-
* Supports AXE_PATH environment variable for custom installations.
|
|
47
|
-
*/
|
|
48
31
|
function getAxePath() {
|
|
49
32
|
return process.env.AXE_PATH || "axe";
|
|
50
33
|
}
|
|
51
|
-
/**
|
|
52
|
-
* Run AXe command with execFile (no shell) for proper argument handling.
|
|
53
|
-
*/
|
|
54
34
|
async function runAxe(...args) {
|
|
55
35
|
const axePath = getAxePath();
|
|
56
36
|
const { stdout, stderr } = await execFileAsync(axePath, args, {
|
|
@@ -61,10 +41,6 @@ async function runAxe(...args) {
|
|
|
61
41
|
stderr: stderr.trim(),
|
|
62
42
|
};
|
|
63
43
|
}
|
|
64
|
-
/**
|
|
65
|
-
* Run IDB command with execFile (no shell) for proper argument handling
|
|
66
|
-
* This matches the original ios-simulator-mcp implementation
|
|
67
|
-
*/
|
|
68
44
|
async function runIdb(...args) {
|
|
69
45
|
const idbPath = getIdbPath();
|
|
70
46
|
const { stdout, stderr } = await execFileAsync(idbPath, args, {
|
|
@@ -75,9 +51,6 @@ async function runIdb(...args) {
|
|
|
75
51
|
stderr: stderr.trim(),
|
|
76
52
|
};
|
|
77
53
|
}
|
|
78
|
-
/**
|
|
79
|
-
* Check if IDB is available
|
|
80
|
-
*/
|
|
81
54
|
export async function isIdbAvailable() {
|
|
82
55
|
try {
|
|
83
56
|
await runIdb("--help");
|
|
@@ -87,9 +60,6 @@ export async function isIdbAvailable() {
|
|
|
87
60
|
return false;
|
|
88
61
|
}
|
|
89
62
|
}
|
|
90
|
-
/**
|
|
91
|
-
* Check if AXe CLI is available
|
|
92
|
-
*/
|
|
93
63
|
export async function isAxeAvailable() {
|
|
94
64
|
try {
|
|
95
65
|
await runAxe("--version");
|
|
@@ -99,17 +69,10 @@ export async function isAxeAvailable() {
|
|
|
99
69
|
return false;
|
|
100
70
|
}
|
|
101
71
|
}
|
|
102
|
-
/**
|
|
103
|
-
* Check if the active iOS UI driver is available.
|
|
104
|
-
* Used by tap.ts for early fail-fast checks.
|
|
105
|
-
*/
|
|
106
72
|
export async function isUiDriverAvailable() {
|
|
107
73
|
const driver = getIosDriver();
|
|
108
74
|
return driver === "axe" ? isAxeAvailable() : isIdbAvailable();
|
|
109
75
|
}
|
|
110
|
-
/**
|
|
111
|
-
* Get install instructions for the active iOS UI driver.
|
|
112
|
-
*/
|
|
113
76
|
export function getUiDriverInstallHint() {
|
|
114
77
|
const driver = getIosDriver();
|
|
115
78
|
if (driver === "axe") {
|
|
@@ -117,9 +80,6 @@ export function getUiDriverInstallHint() {
|
|
|
117
80
|
}
|
|
118
81
|
return "IDB is not installed. Install with: brew install idb-companion\nTip: Consider switching to AXe (recommended) — brew install cameroncooke/axe/axe and set IOS_DRIVER=axe";
|
|
119
82
|
}
|
|
120
|
-
/**
|
|
121
|
-
* Map IDB button names (uppercase/underscores) to AXe button names (lowercase/hyphens).
|
|
122
|
-
*/
|
|
123
83
|
function toAxeButtonName(idbButton) {
|
|
124
84
|
const map = {
|
|
125
85
|
HOME: "home",
|
|
@@ -130,12 +90,7 @@ function toAxeButtonName(idbButton) {
|
|
|
130
90
|
};
|
|
131
91
|
return map[idbButton];
|
|
132
92
|
}
|
|
133
|
-
/**
|
|
134
|
-
* Ensure IDB is connected to the specified simulator
|
|
135
|
-
* IDB requires `idb connect <UDID>` before any UI commands work
|
|
136
|
-
*/
|
|
137
93
|
async function ensureIdbConnected(udid) {
|
|
138
|
-
// Skip if already connected in this session
|
|
139
94
|
if (connectedIdbSimulators.has(udid)) {
|
|
140
95
|
return { success: true };
|
|
141
96
|
}
|
|
@@ -146,7 +101,6 @@ async function ensureIdbConnected(udid) {
|
|
|
146
101
|
}
|
|
147
102
|
catch (error) {
|
|
148
103
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
149
|
-
// "Already connected" is not an error
|
|
150
104
|
if (errorMessage.includes("already connected") ||
|
|
151
105
|
errorMessage.includes("Connected")) {
|
|
152
106
|
connectedIdbSimulators.add(udid);
|
|
@@ -158,12 +112,6 @@ async function ensureIdbConnected(udid) {
|
|
|
158
112
|
};
|
|
159
113
|
}
|
|
160
114
|
}
|
|
161
|
-
/**
|
|
162
|
-
* Shared pre-flight check for all UI driver functions:
|
|
163
|
-
* 1. Check driver availability
|
|
164
|
-
* 2. Resolve target simulator UDID
|
|
165
|
-
* 3. Ensure IDB connection (IDB only)
|
|
166
|
-
*/
|
|
167
115
|
async function ensureUiDriverReady(udid) {
|
|
168
116
|
const driver = getIosDriver();
|
|
169
117
|
if (driver === "axe") {
|
|
@@ -205,19 +153,11 @@ async function ensureUiDriverReady(udid) {
|
|
|
205
153
|
}
|
|
206
154
|
return { ready: true, driver, targetUdid };
|
|
207
155
|
}
|
|
208
|
-
// Shared message for callers that hit a missing-Xcode preflight. Includes
|
|
209
|
-
// the actual install command so the agent (or user) can act without guessing.
|
|
210
156
|
export const XCODE_MISSING_ERROR = "Xcode command line tools are not installed or not selected. " +
|
|
211
157
|
"Run `xcode-select --install` for the CLT, or install full Xcode from the App Store " +
|
|
212
158
|
"and run `sudo xcode-select -s /Applications/Xcode.app/Contents/Developer`. " +
|
|
213
159
|
"simctl ships with full Xcode (the standalone CLT does not include it).";
|
|
214
|
-
// Memoized result — Xcode CLT presence does not change mid-session in
|
|
215
|
-
// practice, and `xcrun simctl help` adds 50–200 ms per ios_* call without
|
|
216
|
-
// caching. Re-resolve once per process; restart the MCP server after install.
|
|
217
160
|
let simctlAvailableCache = null;
|
|
218
|
-
/**
|
|
219
|
-
* Check if simctl is available (cached for the lifetime of the process).
|
|
220
|
-
*/
|
|
221
161
|
export async function isSimctlAvailable() {
|
|
222
162
|
if (simctlAvailableCache)
|
|
223
163
|
return simctlAvailableCache;
|
|
@@ -232,15 +172,9 @@ export async function isSimctlAvailable() {
|
|
|
232
172
|
})();
|
|
233
173
|
return simctlAvailableCache;
|
|
234
174
|
}
|
|
235
|
-
/**
|
|
236
|
-
* Reset the cached simctl availability check. Test-only.
|
|
237
|
-
*/
|
|
238
175
|
export function resetSimctlAvailableCache() {
|
|
239
176
|
simctlAvailableCache = null;
|
|
240
177
|
}
|
|
241
|
-
/**
|
|
242
|
-
* List iOS simulators
|
|
243
|
-
*/
|
|
244
178
|
export async function listIOSSimulators(onlyBooted = false) {
|
|
245
179
|
try {
|
|
246
180
|
const simctlAvailable = await isSimctlAvailable();
|
|
@@ -255,7 +189,6 @@ export async function listIOSSimulators(onlyBooted = false) {
|
|
|
255
189
|
});
|
|
256
190
|
const data = JSON.parse(stdout);
|
|
257
191
|
const simulators = [];
|
|
258
|
-
// Parse devices from each runtime
|
|
259
192
|
for (const [runtime, devices] of Object.entries(data.devices)) {
|
|
260
193
|
if (!Array.isArray(devices))
|
|
261
194
|
continue;
|
|
@@ -264,7 +197,6 @@ export async function listIOSSimulators(onlyBooted = false) {
|
|
|
264
197
|
continue;
|
|
265
198
|
if (onlyBooted && device.state !== "Booted")
|
|
266
199
|
continue;
|
|
267
|
-
// Extract iOS version from runtime string
|
|
268
200
|
const runtimeMatch = runtime.match(/iOS[- ](\d+[.-]\d+)/i);
|
|
269
201
|
const runtimeVersion = runtimeMatch
|
|
270
202
|
? `iOS ${runtimeMatch[1].replace("-", ".")}`
|
|
@@ -288,7 +220,6 @@ export async function listIOSSimulators(onlyBooted = false) {
|
|
|
288
220
|
simulators: [],
|
|
289
221
|
};
|
|
290
222
|
}
|
|
291
|
-
// Sort: Booted first, then by name
|
|
292
223
|
simulators.sort((a, b) => {
|
|
293
224
|
if (a.state === "Booted" && b.state !== "Booted")
|
|
294
225
|
return -1;
|
|
@@ -315,11 +246,6 @@ export async function listIOSSimulators(onlyBooted = false) {
|
|
|
315
246
|
};
|
|
316
247
|
}
|
|
317
248
|
}
|
|
318
|
-
/**
|
|
319
|
-
* Build the actionable "no booted simulator" error message. Lists up to 6
|
|
320
|
-
* recently-available shutdown simulators by name+UDID and points the agent
|
|
321
|
-
* at `ios_boot_simulator`, so it can recover instead of giving up.
|
|
322
|
-
*/
|
|
323
249
|
export async function buildNoSimulatorError() {
|
|
324
250
|
const base = "No iOS simulator is currently running. " +
|
|
325
251
|
"Boot one with `ios_boot_simulator` (pass udid or name), " +
|
|
@@ -350,9 +276,6 @@ export async function buildNoSimulatorError() {
|
|
|
350
276
|
return base;
|
|
351
277
|
}
|
|
352
278
|
}
|
|
353
|
-
/**
|
|
354
|
-
* Get the booted simulator UDID
|
|
355
|
-
*/
|
|
356
279
|
export async function getBootedSimulatorUdid() {
|
|
357
280
|
try {
|
|
358
281
|
const { stdout } = await execAsync("xcrun simctl list devices booted -j", {
|
|
@@ -374,10 +297,6 @@ export async function getBootedSimulatorUdid() {
|
|
|
374
297
|
return null;
|
|
375
298
|
}
|
|
376
299
|
}
|
|
377
|
-
/**
|
|
378
|
-
* Find a booted simulator's UDID by its device name
|
|
379
|
-
* Matches Metro's deviceName against simulator names from simctl
|
|
380
|
-
*/
|
|
381
300
|
export async function findSimulatorByName(deviceName) {
|
|
382
301
|
try {
|
|
383
302
|
const { stdout } = await execAsync("xcrun simctl list devices booted -j", {
|
|
@@ -392,11 +311,9 @@ export async function findSimulatorByName(deviceName) {
|
|
|
392
311
|
if (device.state !== "Booted")
|
|
393
312
|
continue;
|
|
394
313
|
const normalizedSimName = device.name.toLowerCase().trim();
|
|
395
|
-
// Exact match
|
|
396
314
|
if (normalizedSimName === normalizedDeviceName) {
|
|
397
315
|
return device.udid;
|
|
398
316
|
}
|
|
399
|
-
// Partial match (deviceName contains simulator name or vice versa)
|
|
400
317
|
if (normalizedSimName.includes(normalizedDeviceName) ||
|
|
401
318
|
normalizedDeviceName.includes(normalizedSimName)) {
|
|
402
319
|
return device.udid;
|
|
@@ -409,28 +326,16 @@ export async function findSimulatorByName(deviceName) {
|
|
|
409
326
|
return null;
|
|
410
327
|
}
|
|
411
328
|
}
|
|
412
|
-
/**
|
|
413
|
-
* Get the active simulator UDID (Metro-connected) or fall back to first booted simulator
|
|
414
|
-
* This enables automatic device scoping based on Metro connection
|
|
415
|
-
*/
|
|
416
329
|
export async function getActiveOrBootedSimulatorUdid() {
|
|
417
|
-
// First, check if there's an active Metro-connected simulator
|
|
418
330
|
const activeUdid = getActiveSimulatorUdid();
|
|
419
331
|
if (activeUdid) {
|
|
420
332
|
return activeUdid;
|
|
421
333
|
}
|
|
422
|
-
// Fall back to first booted simulator
|
|
423
334
|
return getBootedSimulatorUdid();
|
|
424
335
|
}
|
|
425
|
-
/**
|
|
426
|
-
* Build device selector for simctl command
|
|
427
|
-
*/
|
|
428
336
|
function buildDeviceArg(udid) {
|
|
429
337
|
return udid || "booted";
|
|
430
338
|
}
|
|
431
|
-
/**
|
|
432
|
-
* Take a screenshot from an iOS simulator
|
|
433
|
-
*/
|
|
434
339
|
export async function iosScreenshot(outputPath, udid) {
|
|
435
340
|
try {
|
|
436
341
|
const simctlAvailable = await isSimctlAvailable();
|
|
@@ -440,7 +345,6 @@ export async function iosScreenshot(outputPath, udid) {
|
|
|
440
345
|
error: XCODE_MISSING_ERROR,
|
|
441
346
|
};
|
|
442
347
|
}
|
|
443
|
-
// Resolve target UDID (prefer Metro-connected simulator)
|
|
444
348
|
const targetUdid = udid || (await getActiveOrBootedSimulatorUdid());
|
|
445
349
|
if (!targetUdid) {
|
|
446
350
|
return {
|
|
@@ -448,14 +352,11 @@ export async function iosScreenshot(outputPath, udid) {
|
|
|
448
352
|
error: await buildNoSimulatorError(),
|
|
449
353
|
};
|
|
450
354
|
}
|
|
451
|
-
// Generate output path if not provided
|
|
452
355
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
453
356
|
const finalOutputPath = outputPath || path.join(os.tmpdir(), `ios-screenshot-${timestamp}.png`);
|
|
454
357
|
await execAsync(`xcrun simctl io ${targetUdid} screenshot "${finalOutputPath}"`, {
|
|
455
358
|
timeout: SIMCTL_TIMEOUT,
|
|
456
359
|
});
|
|
457
|
-
// Resize image if needed (API limit: 2000px max for multi-image requests)
|
|
458
|
-
// Return scale factor so AI can convert image coords to device coords
|
|
459
360
|
const MAX_DIMENSION = 2000;
|
|
460
361
|
const image = sharp(finalOutputPath);
|
|
461
362
|
const metadata = await image.metadata();
|
|
@@ -464,7 +365,6 @@ export async function iosScreenshot(outputPath, udid) {
|
|
|
464
365
|
let imageData;
|
|
465
366
|
let scaleFactor = 1;
|
|
466
367
|
if (originalWidth > MAX_DIMENSION || originalHeight > MAX_DIMENSION) {
|
|
467
|
-
// Calculate scale to fit within MAX_DIMENSION
|
|
468
368
|
scaleFactor = Math.max(originalWidth, originalHeight) / MAX_DIMENSION;
|
|
469
369
|
imageData = await image
|
|
470
370
|
.resize(MAX_DIMENSION, MAX_DIMENSION, {
|
|
@@ -493,9 +393,6 @@ export async function iosScreenshot(outputPath, udid) {
|
|
|
493
393
|
};
|
|
494
394
|
}
|
|
495
395
|
}
|
|
496
|
-
/**
|
|
497
|
-
* Install an app on an iOS simulator
|
|
498
|
-
*/
|
|
499
396
|
export async function iosInstallApp(appPath, udid) {
|
|
500
397
|
try {
|
|
501
398
|
const simctlAvailable = await isSimctlAvailable();
|
|
@@ -505,14 +402,12 @@ export async function iosInstallApp(appPath, udid) {
|
|
|
505
402
|
error: XCODE_MISSING_ERROR,
|
|
506
403
|
};
|
|
507
404
|
}
|
|
508
|
-
// Verify app exists
|
|
509
405
|
if (!existsSync(appPath)) {
|
|
510
406
|
return {
|
|
511
407
|
success: false,
|
|
512
408
|
error: `App bundle not found: ${appPath}`,
|
|
513
409
|
};
|
|
514
410
|
}
|
|
515
|
-
// Resolve target UDID (prefer Metro-connected simulator)
|
|
516
411
|
const targetUdid = udid || (await getActiveOrBootedSimulatorUdid());
|
|
517
412
|
if (!targetUdid) {
|
|
518
413
|
return {
|
|
@@ -521,7 +416,7 @@ export async function iosInstallApp(appPath, udid) {
|
|
|
521
416
|
};
|
|
522
417
|
}
|
|
523
418
|
await execAsync(`xcrun simctl install ${targetUdid} "${appPath}"`, {
|
|
524
|
-
timeout: 120000,
|
|
419
|
+
timeout: 120000,
|
|
525
420
|
});
|
|
526
421
|
return {
|
|
527
422
|
success: true,
|
|
@@ -535,9 +430,6 @@ export async function iosInstallApp(appPath, udid) {
|
|
|
535
430
|
};
|
|
536
431
|
}
|
|
537
432
|
}
|
|
538
|
-
/**
|
|
539
|
-
* Launch an app on an iOS simulator
|
|
540
|
-
*/
|
|
541
433
|
export async function iosLaunchApp(bundleId, udid) {
|
|
542
434
|
try {
|
|
543
435
|
const simctlAvailable = await isSimctlAvailable();
|
|
@@ -547,7 +439,6 @@ export async function iosLaunchApp(bundleId, udid) {
|
|
|
547
439
|
error: XCODE_MISSING_ERROR,
|
|
548
440
|
};
|
|
549
441
|
}
|
|
550
|
-
// Resolve target UDID (prefer Metro-connected simulator)
|
|
551
442
|
const targetUdid = udid || (await getActiveOrBootedSimulatorUdid());
|
|
552
443
|
if (!targetUdid) {
|
|
553
444
|
return {
|
|
@@ -576,9 +467,6 @@ export async function iosLaunchApp(bundleId, udid) {
|
|
|
576
467
|
};
|
|
577
468
|
}
|
|
578
469
|
}
|
|
579
|
-
/**
|
|
580
|
-
* Open a URL in the iOS simulator
|
|
581
|
-
*/
|
|
582
470
|
export async function iosOpenUrl(url, udid) {
|
|
583
471
|
try {
|
|
584
472
|
const simctlAvailable = await isSimctlAvailable();
|
|
@@ -588,7 +476,6 @@ export async function iosOpenUrl(url, udid) {
|
|
|
588
476
|
error: XCODE_MISSING_ERROR,
|
|
589
477
|
};
|
|
590
478
|
}
|
|
591
|
-
// Resolve target UDID (prefer Metro-connected simulator)
|
|
592
479
|
const targetUdid = udid || (await getActiveOrBootedSimulatorUdid());
|
|
593
480
|
if (!targetUdid) {
|
|
594
481
|
return {
|
|
@@ -618,9 +505,6 @@ export async function iosOpenUrl(url, udid) {
|
|
|
618
505
|
};
|
|
619
506
|
}
|
|
620
507
|
}
|
|
621
|
-
/**
|
|
622
|
-
* Terminate an app on an iOS simulator
|
|
623
|
-
*/
|
|
624
508
|
export async function iosTerminateApp(bundleId, udid) {
|
|
625
509
|
try {
|
|
626
510
|
const simctlAvailable = await isSimctlAvailable();
|
|
@@ -630,7 +514,6 @@ export async function iosTerminateApp(bundleId, udid) {
|
|
|
630
514
|
error: XCODE_MISSING_ERROR,
|
|
631
515
|
};
|
|
632
516
|
}
|
|
633
|
-
// Resolve target UDID (prefer Metro-connected simulator)
|
|
634
517
|
const targetUdid = udid || (await getActiveOrBootedSimulatorUdid());
|
|
635
518
|
if (!targetUdid) {
|
|
636
519
|
return {
|
|
@@ -653,9 +536,6 @@ export async function iosTerminateApp(bundleId, udid) {
|
|
|
653
536
|
};
|
|
654
537
|
}
|
|
655
538
|
}
|
|
656
|
-
/**
|
|
657
|
-
* Boot an iOS simulator
|
|
658
|
-
*/
|
|
659
539
|
export async function iosBootSimulator(udid) {
|
|
660
540
|
try {
|
|
661
541
|
const simctlAvailable = await isSimctlAvailable();
|
|
@@ -666,14 +546,10 @@ export async function iosBootSimulator(udid) {
|
|
|
666
546
|
};
|
|
667
547
|
}
|
|
668
548
|
await execAsync(`xcrun simctl boot ${udid}`, {
|
|
669
|
-
timeout: 60000,
|
|
549
|
+
timeout: 60000,
|
|
670
550
|
});
|
|
671
|
-
// Open Simulator app
|
|
672
551
|
await execAsync("open -a Simulator", { timeout: 10000 }).catch(() => {
|
|
673
|
-
// Ignore if Simulator app doesn't open
|
|
674
552
|
});
|
|
675
|
-
// The device inventory just changed — drop the cached copy so the next
|
|
676
|
-
// resolve/tap sees this simulator as booted without waiting out the TTL.
|
|
677
553
|
const { resetDeviceDiscoveryCache } = await import("./deviceDiscovery.js");
|
|
678
554
|
resetDeviceDiscoveryCache();
|
|
679
555
|
return {
|
|
@@ -683,7 +559,6 @@ export async function iosBootSimulator(udid) {
|
|
|
683
559
|
}
|
|
684
560
|
catch (error) {
|
|
685
561
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
686
|
-
// Already booted is not an error
|
|
687
562
|
if (errorMessage.includes("Unable to boot device in current state: Booted")) {
|
|
688
563
|
return {
|
|
689
564
|
success: true,
|
|
@@ -696,16 +571,6 @@ export async function iosBootSimulator(udid) {
|
|
|
696
571
|
};
|
|
697
572
|
}
|
|
698
573
|
}
|
|
699
|
-
// ============================================================================
|
|
700
|
-
// iOS UI Interaction Tools
|
|
701
|
-
// These tools require an iOS UI driver to be installed:
|
|
702
|
-
// Recommended: AXe — brew install cameroncooke/axe/axe
|
|
703
|
-
// Alternative: IDB — brew install idb-companion
|
|
704
|
-
// AXe is the default driver. Set IOS_DRIVER=idb to fall back to IDB.
|
|
705
|
-
// ============================================================================
|
|
706
|
-
/**
|
|
707
|
-
* Tap at coordinates on an iOS simulator using IDB
|
|
708
|
-
*/
|
|
709
574
|
export async function iosTap(x, y, options) {
|
|
710
575
|
try {
|
|
711
576
|
const preflight = await ensureUiDriverReady(options?.udid);
|
|
@@ -716,7 +581,6 @@ export async function iosTap(x, y, options) {
|
|
|
716
581
|
const yRounded = Math.round(y);
|
|
717
582
|
if (driver === "axe") {
|
|
718
583
|
if (options?.duration !== undefined) {
|
|
719
|
-
// Long press: use axe touch with delay (convert ms to seconds)
|
|
720
584
|
const delaySecs = String(options.duration / 1000);
|
|
721
585
|
await runAxe("touch", "-x", String(xRounded), "-y", String(yRounded), "--down", "--up", "--delay", delaySecs, "--udid", targetUdid);
|
|
722
586
|
}
|
|
@@ -746,9 +610,6 @@ export async function iosTap(x, y, options) {
|
|
|
746
610
|
};
|
|
747
611
|
}
|
|
748
612
|
}
|
|
749
|
-
/**
|
|
750
|
-
* Swipe gesture on an iOS simulator
|
|
751
|
-
*/
|
|
752
613
|
export async function iosSwipe(startX, startY, endX, endY, options) {
|
|
753
614
|
try {
|
|
754
615
|
const preflight = await ensureUiDriverReady(options?.udid);
|
|
@@ -772,7 +633,6 @@ export async function iosSwipe(startX, startY, endX, endY, options) {
|
|
|
772
633
|
String(y2),
|
|
773
634
|
];
|
|
774
635
|
if (options?.duration !== undefined) {
|
|
775
|
-
// Both axe and idb expect duration in seconds (matches the MCP tool schema).
|
|
776
636
|
args.push("--duration", String(options.duration));
|
|
777
637
|
}
|
|
778
638
|
if (options?.delta !== undefined) {
|
|
@@ -806,9 +666,6 @@ export async function iosSwipe(startX, startY, endX, endY, options) {
|
|
|
806
666
|
};
|
|
807
667
|
}
|
|
808
668
|
}
|
|
809
|
-
/**
|
|
810
|
-
* Input text into the active field on an iOS simulator
|
|
811
|
-
*/
|
|
812
669
|
export async function iosInputText(text, udid) {
|
|
813
670
|
try {
|
|
814
671
|
const preflight = await ensureUiDriverReady(udid);
|
|
@@ -816,12 +673,6 @@ export async function iosInputText(text, udid) {
|
|
|
816
673
|
return preflight.error;
|
|
817
674
|
const { driver, targetUdid } = preflight;
|
|
818
675
|
if (driver === "axe") {
|
|
819
|
-
// Submit one HID event per chunk instead of AXe's default 200. Text
|
|
820
|
-
// arriving out of order has been observed on device ("Alice" landing as
|
|
821
|
-
// "Aicel", "CASEB" as "CSEBA" — in both cases the second keystroke last),
|
|
822
|
-
// and a single large submission is the plausible cause. Measured at the
|
|
823
|
-
// same latency as plain `axe type` (1432ms vs 1519ms for 19 chars), so
|
|
824
|
-
// the serialisation is free.
|
|
825
676
|
await runAxe("batch", "--udid", targetUdid, "--type-chunk-size", "1", "--step", `type '${text.replace(/'/g, "'\\''")}'`);
|
|
826
677
|
}
|
|
827
678
|
else {
|
|
@@ -841,12 +692,8 @@ export async function iosInputText(text, udid) {
|
|
|
841
692
|
};
|
|
842
693
|
}
|
|
843
694
|
}
|
|
844
|
-
/**
|
|
845
|
-
* Press a hardware button on an iOS simulator
|
|
846
|
-
*/
|
|
847
695
|
export async function iosButton(button, options) {
|
|
848
696
|
try {
|
|
849
|
-
// Validate button type
|
|
850
697
|
if (!IOS_BUTTON_TYPES.includes(button)) {
|
|
851
698
|
return {
|
|
852
699
|
success: false,
|
|
@@ -887,9 +734,6 @@ export async function iosButton(button, options) {
|
|
|
887
734
|
};
|
|
888
735
|
}
|
|
889
736
|
}
|
|
890
|
-
/**
|
|
891
|
-
* Send a key event to an iOS simulator
|
|
892
|
-
*/
|
|
893
737
|
export async function iosKeyEvent(keycode, options) {
|
|
894
738
|
try {
|
|
895
739
|
const preflight = await ensureUiDriverReady(options?.udid);
|
|
@@ -926,9 +770,6 @@ export async function iosKeyEvent(keycode, options) {
|
|
|
926
770
|
};
|
|
927
771
|
}
|
|
928
772
|
}
|
|
929
|
-
/**
|
|
930
|
-
* Send a sequence of key events to an iOS simulator
|
|
931
|
-
*/
|
|
932
773
|
export async function iosKeySequence(keycodes, udid) {
|
|
933
774
|
try {
|
|
934
775
|
if (!keycodes || keycodes.length === 0) {
|
|
@@ -968,9 +809,6 @@ export async function iosKeySequence(keycodes, udid) {
|
|
|
968
809
|
};
|
|
969
810
|
}
|
|
970
811
|
}
|
|
971
|
-
/**
|
|
972
|
-
* Format accessibility tree for human-readable output
|
|
973
|
-
*/
|
|
974
812
|
function formatAccessibilityTree(elements, depth = 0) {
|
|
975
813
|
const lines = [];
|
|
976
814
|
const indent = " ".repeat(depth);
|
|
@@ -997,25 +835,15 @@ function formatAccessibilityTree(elements, depth = 0) {
|
|
|
997
835
|
}
|
|
998
836
|
return lines.join("\n");
|
|
999
837
|
}
|
|
1000
|
-
/**
|
|
1001
|
-
* Probe the iOS root accessibility element for the top safe-area inset in points.
|
|
1002
|
-
* React-native-screens modal/sheet presentations cause measureInWindow to return y
|
|
1003
|
-
* relative to the content origin (below the inset) instead of the window. Knowing
|
|
1004
|
-
* the inset lets us detect and correct the shifted coordinate space.
|
|
1005
|
-
*
|
|
1006
|
-
* Cached so repeated taps don't each incur a describe-ui call. The probe costs
|
|
1007
|
-
* ~200ms and ran on essentially every fiber+native tap under the old 5s TTL;
|
|
1008
|
-
* the inset itself only changes on rotation, so a longer TTL is safe (a rotation
|
|
1009
|
-
* mid-session costs at most one stale correction until the entry expires).
|
|
1010
|
-
*/
|
|
1011
838
|
const SAFE_AREA_CACHE_TTL_MS = 30_000;
|
|
1012
839
|
const safeAreaTopCache = new Map();
|
|
1013
|
-
|
|
840
|
+
async function probeIOSRootFrame(udid) {
|
|
1014
841
|
const cacheKey = udid || "__default__";
|
|
1015
842
|
const cached = safeAreaTopCache.get(cacheKey);
|
|
1016
843
|
if (cached && cached.expires > Date.now())
|
|
1017
|
-
return cached
|
|
844
|
+
return cached;
|
|
1018
845
|
let value = 0;
|
|
846
|
+
let screen = null;
|
|
1019
847
|
try {
|
|
1020
848
|
const preflight = await ensureUiDriverReady(udid);
|
|
1021
849
|
if (preflight.ready) {
|
|
@@ -1033,17 +861,25 @@ export async function getIOSSafeAreaTop(udid) {
|
|
|
1033
861
|
const y = root?.frame?.y;
|
|
1034
862
|
if (typeof y === "number" && y > 0)
|
|
1035
863
|
value = Math.round(y);
|
|
864
|
+
const w = root?.frame?.width;
|
|
865
|
+
const h = root?.frame?.height;
|
|
866
|
+
if (typeof w === "number" && w > 0 && typeof h === "number" && h > 0) {
|
|
867
|
+
screen = { width: w, height: h };
|
|
868
|
+
}
|
|
1036
869
|
}
|
|
1037
870
|
}
|
|
1038
871
|
catch {
|
|
1039
|
-
// Fall through: value stays 0 (no correction)
|
|
1040
872
|
}
|
|
1041
|
-
|
|
1042
|
-
|
|
873
|
+
const entry = { value, screen, expires: Date.now() + SAFE_AREA_CACHE_TTL_MS };
|
|
874
|
+
safeAreaTopCache.set(cacheKey, entry);
|
|
875
|
+
return entry;
|
|
876
|
+
}
|
|
877
|
+
export async function getIOSSafeAreaTop(udid) {
|
|
878
|
+
return (await probeIOSRootFrame(udid)).value;
|
|
879
|
+
}
|
|
880
|
+
export async function getIOSScreenPointSize(udid) {
|
|
881
|
+
return (await probeIOSRootFrame(udid)).screen;
|
|
1043
882
|
}
|
|
1044
|
-
/**
|
|
1045
|
-
* Get accessibility info for the entire screen
|
|
1046
|
-
*/
|
|
1047
883
|
export async function iosDescribeAll(udid) {
|
|
1048
884
|
try {
|
|
1049
885
|
const preflight = await ensureUiDriverReady(udid);
|
|
@@ -1061,9 +897,7 @@ export async function iosDescribeAll(udid) {
|
|
|
1061
897
|
throw new Error(result.stderr);
|
|
1062
898
|
stdout = result.stdout;
|
|
1063
899
|
}
|
|
1064
|
-
// Parse JSON response
|
|
1065
900
|
const elements = JSON.parse(stdout);
|
|
1066
|
-
// Format for human-readable output
|
|
1067
901
|
const formatted = formatAccessibilityTree(elements);
|
|
1068
902
|
const header = 'Note: All coordinates are in iOS points. Use tap(text="...") for tapping — do not pass these coordinates to tap(x, y) directly.\n\n';
|
|
1069
903
|
return {
|
|
@@ -1079,27 +913,8 @@ export async function iosDescribeAll(udid) {
|
|
|
1079
913
|
};
|
|
1080
914
|
}
|
|
1081
915
|
}
|
|
1082
|
-
/**
|
|
1083
|
-
* I1 (2026-05-16): when a native iOS system sheet (auth prompt, permission request,
|
|
1084
|
-
* Safari sign-in sheet) is on screen, the RN app's fiber tree still describes the
|
|
1085
|
-
* underlying screen — but those pressables aren't interactable until the sheet is
|
|
1086
|
-
* dismissed. Detect the overlay so the screenshot output can warn the agent and
|
|
1087
|
-
* point it at tap(x, y, native=true) for the overlay's actual buttons.
|
|
1088
|
-
*
|
|
1089
|
-
* Heuristic: scan the top three levels of the AXe accessibility tree for an
|
|
1090
|
-
* element whose type matches an iOS sheet/alert role, OR whose AXLabel matches a
|
|
1091
|
-
* well-known system-overlay phrase. False positives are rare because the patterns
|
|
1092
|
-
* are specific to system-presented sheets — RN content rarely uses these labels.
|
|
1093
|
-
*/
|
|
1094
916
|
const IOS_SYSTEM_OVERLAY_TYPES = /^(Alert|ActionSheet|Sheet|Dialog|SystemDialog)$/i;
|
|
1095
917
|
const IOS_SYSTEM_OVERLAY_LABEL_PATTERNS = /Wants to Use|Sign in to|Sign In to|Allow .* to (use|access)|Open in Safari|Continue with|Use Face ID|Use Touch ID|Use Passcode/i;
|
|
1096
|
-
/**
|
|
1097
|
-
* Telltale of an RN accessibilityLabel rollup vs a system-presented sheet:
|
|
1098
|
-
* a rollup concatenates multiple child labels with commas / newlines (3+ separators),
|
|
1099
|
-
* while a system sheet's label is a single sentence. Without this guard the label
|
|
1100
|
-
* patterns above false-positive on screens like gifted.co's login wrapper whose
|
|
1101
|
-
* rollup happens to contain "Continue with" amongst many other phrases.
|
|
1102
|
-
*/
|
|
1103
918
|
function looksLikeRNLabelRollup(label) {
|
|
1104
919
|
const commas = (label.match(/,/g) ?? []).length;
|
|
1105
920
|
const newlines = (label.match(/\n/g) ?? []).length;
|
|
@@ -1146,9 +961,6 @@ export function formatIOSSystemOverlayWarning(detection) {
|
|
|
1146
961
|
`React Native screen UNDERNEATH and may be stale. For overlay buttons, take the coordinates ` +
|
|
1147
962
|
`from the screenshot and call tap(x, y, native=true, platform="ios").`);
|
|
1148
963
|
}
|
|
1149
|
-
/**
|
|
1150
|
-
* Get accessibility info at a specific point
|
|
1151
|
-
*/
|
|
1152
964
|
export async function iosDescribePoint(x, y, udid) {
|
|
1153
965
|
try {
|
|
1154
966
|
const preflight = await ensureUiDriverReady(udid);
|
|
@@ -1168,7 +980,6 @@ export async function iosDescribePoint(x, y, udid) {
|
|
|
1168
980
|
throw new Error(result.stderr);
|
|
1169
981
|
stdout = result.stdout;
|
|
1170
982
|
}
|
|
1171
|
-
// Parse JSON response - may be single element or array
|
|
1172
983
|
let element;
|
|
1173
984
|
try {
|
|
1174
985
|
const parsed = JSON.parse(stdout);
|
|
@@ -1181,7 +992,6 @@ export async function iosDescribePoint(x, y, udid) {
|
|
|
1181
992
|
elements: [],
|
|
1182
993
|
};
|
|
1183
994
|
}
|
|
1184
|
-
// Format for human-readable output
|
|
1185
995
|
const parts = [];
|
|
1186
996
|
if (element.type)
|
|
1187
997
|
parts.push(`Type: ${element.type}`);
|
|
@@ -1211,9 +1021,6 @@ export async function iosDescribePoint(x, y, udid) {
|
|
|
1211
1021
|
};
|
|
1212
1022
|
}
|
|
1213
1023
|
}
|
|
1214
|
-
/**
|
|
1215
|
-
* Helper to flatten nested accessibility elements
|
|
1216
|
-
*/
|
|
1217
1024
|
function flattenElements(elements) {
|
|
1218
1025
|
const result = [];
|
|
1219
1026
|
for (const el of elements) {
|
|
@@ -1224,10 +1031,6 @@ function flattenElements(elements) {
|
|
|
1224
1031
|
}
|
|
1225
1032
|
return result;
|
|
1226
1033
|
}
|
|
1227
|
-
/**
|
|
1228
|
-
* Tap an element by its accessibility label using IDB
|
|
1229
|
-
* This simplifies the workflow: no need to manually find coordinates
|
|
1230
|
-
*/
|
|
1231
1034
|
export async function iosTapElement(options) {
|
|
1232
1035
|
try {
|
|
1233
1036
|
const { label, labelContains, index = 0, duration, udid } = options;
|
|
@@ -1237,7 +1040,6 @@ export async function iosTapElement(options) {
|
|
|
1237
1040
|
error: "Either 'label' (exact match) or 'labelContains' (partial match) is required",
|
|
1238
1041
|
};
|
|
1239
1042
|
}
|
|
1240
|
-
// Get all accessibility elements
|
|
1241
1043
|
const describeResult = await iosDescribeAll(udid);
|
|
1242
1044
|
if (!describeResult.success || !describeResult.elements) {
|
|
1243
1045
|
return {
|
|
@@ -1245,7 +1047,6 @@ export async function iosTapElement(options) {
|
|
|
1245
1047
|
error: describeResult.error || "Failed to get accessibility elements",
|
|
1246
1048
|
};
|
|
1247
1049
|
}
|
|
1248
|
-
// Flatten the tree and find matching elements
|
|
1249
1050
|
const allElements = flattenElements(describeResult.elements);
|
|
1250
1051
|
const matches = allElements.filter((el) => {
|
|
1251
1052
|
if (!el.AXLabel)
|
|
@@ -1265,7 +1066,6 @@ export async function iosTapElement(options) {
|
|
|
1265
1066
|
error: `No element found with ${searchTerm}`,
|
|
1266
1067
|
};
|
|
1267
1068
|
}
|
|
1268
|
-
// Select element by index (default 0 = first match)
|
|
1269
1069
|
if (index >= matches.length) {
|
|
1270
1070
|
return {
|
|
1271
1071
|
success: false,
|
|
@@ -1273,17 +1073,14 @@ export async function iosTapElement(options) {
|
|
|
1273
1073
|
};
|
|
1274
1074
|
}
|
|
1275
1075
|
const element = matches[index];
|
|
1276
|
-
// Check if element has frame coordinates
|
|
1277
1076
|
if (!element.frame) {
|
|
1278
1077
|
return {
|
|
1279
1078
|
success: false,
|
|
1280
1079
|
error: `Element "${element.AXLabel}" has no frame coordinates`,
|
|
1281
1080
|
};
|
|
1282
1081
|
}
|
|
1283
|
-
// Calculate center
|
|
1284
1082
|
const centerX = Math.round(element.frame.x + element.frame.width / 2);
|
|
1285
1083
|
const centerY = Math.round(element.frame.y + element.frame.height / 2);
|
|
1286
|
-
// Tap at center
|
|
1287
1084
|
const tapResult = await iosTap(centerX, centerY, { duration, udid });
|
|
1288
1085
|
if (tapResult.success) {
|
|
1289
1086
|
return {
|
|
@@ -1300,9 +1097,6 @@ export async function iosTapElement(options) {
|
|
|
1300
1097
|
};
|
|
1301
1098
|
}
|
|
1302
1099
|
}
|
|
1303
|
-
/**
|
|
1304
|
-
* Parse IDB accessibility output into simplified element array
|
|
1305
|
-
*/
|
|
1306
1100
|
function parseIdbAccessibilityForFindElement(output) {
|
|
1307
1101
|
const elements = [];
|
|
1308
1102
|
try {
|
|
@@ -1355,14 +1149,9 @@ function parseIdbAccessibilityForFindElement(output) {
|
|
|
1355
1149
|
}
|
|
1356
1150
|
}
|
|
1357
1151
|
catch {
|
|
1358
|
-
// If JSON parsing fails, return empty array
|
|
1359
1152
|
}
|
|
1360
1153
|
return elements;
|
|
1361
1154
|
}
|
|
1362
|
-
/**
|
|
1363
|
-
* Check if an element's center is within the screen viewport.
|
|
1364
|
-
* Filters out off-screen elements in scroll views, horizontal pagers, etc.
|
|
1365
|
-
*/
|
|
1366
1155
|
function isIOSElementInViewport(element, screenWidth, screenHeight) {
|
|
1367
1156
|
const { center } = element;
|
|
1368
1157
|
return (center.x >= 0 &&
|
|
@@ -1370,10 +1159,6 @@ function isIOSElementInViewport(element, screenWidth, screenHeight) {
|
|
|
1370
1159
|
center.y >= 0 &&
|
|
1371
1160
|
center.y <= screenHeight);
|
|
1372
1161
|
}
|
|
1373
|
-
/**
|
|
1374
|
-
* Extract screen dimensions from the parsed UI tree.
|
|
1375
|
-
* The root Application element's frame represents the screen size.
|
|
1376
|
-
*/
|
|
1377
1162
|
function extractIOSScreenSize(rawOutput) {
|
|
1378
1163
|
try {
|
|
1379
1164
|
const data = JSON.parse(rawOutput);
|
|
@@ -1384,13 +1169,9 @@ function extractIOSScreenSize(rawOutput) {
|
|
|
1384
1169
|
}
|
|
1385
1170
|
}
|
|
1386
1171
|
catch {
|
|
1387
|
-
// Fall through
|
|
1388
1172
|
}
|
|
1389
1173
|
return null;
|
|
1390
1174
|
}
|
|
1391
|
-
/**
|
|
1392
|
-
* Match iOS element against find options
|
|
1393
|
-
*/
|
|
1394
1175
|
function matchesIOSFindElement(element, options) {
|
|
1395
1176
|
if (options.label !== undefined) {
|
|
1396
1177
|
if (element.label !== options.label)
|
|
@@ -1424,9 +1205,6 @@ function matchesIOSFindElement(element, options) {
|
|
|
1424
1205
|
}
|
|
1425
1206
|
return true;
|
|
1426
1207
|
}
|
|
1427
|
-
/**
|
|
1428
|
-
* Get UI accessibility tree from iOS simulator using IDB (for find_element)
|
|
1429
|
-
*/
|
|
1430
1208
|
export async function iosGetUITree(udid) {
|
|
1431
1209
|
try {
|
|
1432
1210
|
const preflight = await ensureUiDriverReady(udid);
|
|
@@ -1456,20 +1234,6 @@ export async function iosGetUITree(udid) {
|
|
|
1456
1234
|
};
|
|
1457
1235
|
}
|
|
1458
1236
|
}
|
|
1459
|
-
/**
|
|
1460
|
-
* Bug 3 (2026-05-16): RN rolls up child Text labels into a parent View's
|
|
1461
|
-
* accessibilityLabel when the parent is marked `accessible`. Result: a screen-root
|
|
1462
|
-
* View whose label is a 200+ char concatenation of every visible text. `labelContains`
|
|
1463
|
-
* matches it, frame is the whole screen, centroid happens to land on whatever button
|
|
1464
|
-
* sits near the middle — and the tap silently lands on the wrong target.
|
|
1465
|
-
*
|
|
1466
|
-
* Heuristic filter: an element is an aggregator wrapper when EITHER
|
|
1467
|
-
* (a) its label is much longer than the query AND contains concatenation hints
|
|
1468
|
-
* (newlines, commas, semicolons — RN rollup typically joins with `, ` or `\n`)
|
|
1469
|
-
* (b) its frame covers >50% of the viewport (a real button never does this)
|
|
1470
|
-
*
|
|
1471
|
-
* Both heuristics together catch the gifted.co LoginScreen wrapper case the doc cites.
|
|
1472
|
-
*/
|
|
1473
1237
|
function isAggregatorWrapper(element, query, screenSize) {
|
|
1474
1238
|
const labelLen = element.label?.length ?? 0;
|
|
1475
1239
|
const queryLen = query.length;
|
|
@@ -1487,15 +1251,6 @@ function isAggregatorWrapper(element, query, screenSize) {
|
|
|
1487
1251
|
}
|
|
1488
1252
|
return isLongConcatenatedLabel || isFullScreenFrame;
|
|
1489
1253
|
}
|
|
1490
|
-
/**
|
|
1491
|
-
* Find element(s) in the iOS UI tree matching the given criteria.
|
|
1492
|
-
*
|
|
1493
|
-
* `tree` lets a caller reuse an accessibility dump it already has. Each fetch
|
|
1494
|
-
* shells out to `axe describe-ui` (~210ms measured 2026-08-01) and the tap
|
|
1495
|
-
* strategy runs two or three passes with different predicates, so re-fetching
|
|
1496
|
-
* per pass was costing 400-600ms per accessibility tap for a tree that cannot
|
|
1497
|
-
* change between passes.
|
|
1498
|
-
*/
|
|
1499
1254
|
export async function iosFindElement(options, udid, tree) {
|
|
1500
1255
|
try {
|
|
1501
1256
|
if (!options.label &&
|
|
@@ -1519,8 +1274,6 @@ export async function iosFindElement(options, udid, tree) {
|
|
|
1519
1274
|
error: treeResult.error,
|
|
1520
1275
|
};
|
|
1521
1276
|
}
|
|
1522
|
-
// Filter to viewport-visible elements first, then match criteria.
|
|
1523
|
-
// This prevents selecting off-screen duplicates in scroll views / pagers.
|
|
1524
1277
|
const screenSize = treeResult.rawOutput
|
|
1525
1278
|
? extractIOSScreenSize(treeResult.rawOutput)
|
|
1526
1279
|
: null;
|
|
@@ -1528,21 +1281,9 @@ export async function iosFindElement(options, udid, tree) {
|
|
|
1528
1281
|
? treeResult.elements.filter((el) => isIOSElementInViewport(el, screenSize.width, screenSize.height))
|
|
1529
1282
|
: treeResult.elements;
|
|
1530
1283
|
let matches = visibleElements.filter((el) => matchesIOSFindElement(el, options));
|
|
1531
|
-
// If no visible matches, fall back to all elements (element may be partially visible
|
|
1532
|
-
// or the screen size extraction failed)
|
|
1533
1284
|
if (matches.length === 0) {
|
|
1534
1285
|
matches = treeResult.elements.filter((el) => matchesIOSFindElement(el, options));
|
|
1535
1286
|
}
|
|
1536
|
-
// Bug 3 (2026-05-16): when matching by partial string (labelContains / identifierContains),
|
|
1537
|
-
// drop aggregator-wrapper elements that match only because their label rolls up every
|
|
1538
|
-
// visible child's text. Without this filter, tapping such a wrapper's centroid
|
|
1539
|
-
// silently lands on the wrong on-screen element.
|
|
1540
|
-
//
|
|
1541
|
-
// When ALL matches are aggregator wrappers (the common case — RN often rolls labels
|
|
1542
|
-
// up to a single accessible wrapper, leaving no individual element with a matching
|
|
1543
|
-
// label), refuse with a clear error pointing the agent at component= or coordinates.
|
|
1544
|
-
// This is safer than silently tapping the wrapper centroid (which lands wherever the
|
|
1545
|
-
// wrapper's geometric center happens to be — usually NOT on the requested button).
|
|
1546
1287
|
if (options.labelContains !== undefined && matches.length > 0) {
|
|
1547
1288
|
const filtered = matches.filter((el) => !isAggregatorWrapper(el, options.labelContains, screenSize));
|
|
1548
1289
|
if (filtered.length > 0) {
|
|
@@ -1608,20 +1349,11 @@ export async function iosFindElement(options, udid, tree) {
|
|
|
1608
1349
|
};
|
|
1609
1350
|
}
|
|
1610
1351
|
}
|
|
1611
|
-
// DPR cache: UDID → device pixel ratio (never changes during session)
|
|
1612
1352
|
const dprCache = new Map();
|
|
1613
|
-
|
|
1614
|
-
* Pure calculation: DPR = screenshotPixelWidth / screenPointWidth.
|
|
1615
|
-
* Exported for unit testing.
|
|
1616
|
-
*/
|
|
1353
|
+
const pixelSizeCache = new Map();
|
|
1617
1354
|
export function calculateDPR(screenshotPixelWidth, screenPointWidth) {
|
|
1618
1355
|
return Math.round(screenshotPixelWidth / screenPointWidth);
|
|
1619
1356
|
}
|
|
1620
|
-
/**
|
|
1621
|
-
* Query the actual device pixel ratio for an iOS simulator.
|
|
1622
|
-
* Uses screenshot pixel width vs accessibility root frame width.
|
|
1623
|
-
* Result is cached per UDID for the session.
|
|
1624
|
-
*/
|
|
1625
1357
|
export async function getDevicePixelRatio(udid, pixelDims) {
|
|
1626
1358
|
const cacheKey = udid || "__default__";
|
|
1627
1359
|
const cached = dprCache.get(cacheKey);
|
|
@@ -1634,14 +1366,10 @@ export async function getDevicePixelRatio(udid, pixelDims) {
|
|
|
1634
1366
|
let pixelWidth;
|
|
1635
1367
|
let pixelHeight;
|
|
1636
1368
|
if (pixelDims && pixelDims.width > 0 && pixelDims.height > 0) {
|
|
1637
|
-
// The caller already captured a frame this turn (tap always does) — reuse
|
|
1638
|
-
// its dimensions instead of shelling out for a screenshot of our own, which
|
|
1639
|
-
// measured ~230ms of the ~470ms first-call cost.
|
|
1640
1369
|
pixelWidth = pixelDims.width;
|
|
1641
1370
|
pixelHeight = pixelDims.height;
|
|
1642
1371
|
}
|
|
1643
1372
|
else {
|
|
1644
|
-
// Take screenshot to get pixel dimensions
|
|
1645
1373
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
1646
1374
|
const screenshotPath = path.join(os.tmpdir(), `ios-dpr-screenshot-${timestamp}.png`);
|
|
1647
1375
|
await execAsync(`xcrun simctl io ${resolvedUdid} screenshot "${screenshotPath}"`, {
|
|
@@ -1651,7 +1379,9 @@ export async function getDevicePixelRatio(udid, pixelDims) {
|
|
|
1651
1379
|
pixelWidth = metadata.width;
|
|
1652
1380
|
pixelHeight = metadata.height;
|
|
1653
1381
|
}
|
|
1654
|
-
|
|
1382
|
+
if (pixelWidth > 0 && pixelHeight > 0) {
|
|
1383
|
+
pixelSizeCache.set(cacheKey, { width: pixelWidth, height: pixelHeight });
|
|
1384
|
+
}
|
|
1655
1385
|
const driver = getIosDriver();
|
|
1656
1386
|
const driverOk = driver === "axe" ? await isAxeAvailable() : await isIdbAvailable();
|
|
1657
1387
|
if (driverOk) {
|
|
@@ -1674,22 +1404,25 @@ export async function getDevicePixelRatio(udid, pixelDims) {
|
|
|
1674
1404
|
}
|
|
1675
1405
|
}
|
|
1676
1406
|
catch {
|
|
1677
|
-
// driver failed — fall through to inference
|
|
1678
1407
|
}
|
|
1679
1408
|
}
|
|
1680
|
-
// Fallback: infer DPR from screenshot pixel dimensions
|
|
1681
1409
|
const { inferIOSDevicePixelRatio } = await import("./ocr.js");
|
|
1682
1410
|
const dpr = inferIOSDevicePixelRatio(pixelWidth, pixelHeight);
|
|
1683
1411
|
dprCache.set(cacheKey, dpr);
|
|
1684
1412
|
return dpr;
|
|
1685
1413
|
}
|
|
1686
|
-
|
|
1414
|
+
export async function getIOSScreenPixelSize(udid, pixelDims) {
|
|
1415
|
+
const cacheKey = udid || "__default__";
|
|
1416
|
+
const cached = pixelSizeCache.get(cacheKey);
|
|
1417
|
+
if (cached)
|
|
1418
|
+
return cached;
|
|
1419
|
+
await getDevicePixelRatio(udid, pixelDims).catch(() => undefined);
|
|
1420
|
+
return pixelSizeCache.get(cacheKey) ?? null;
|
|
1421
|
+
}
|
|
1687
1422
|
export function clearDPRCache() {
|
|
1688
1423
|
dprCache.clear();
|
|
1424
|
+
pixelSizeCache.clear();
|
|
1689
1425
|
}
|
|
1690
|
-
/**
|
|
1691
|
-
* Wait for element to appear on iOS screen with polling
|
|
1692
|
-
*/
|
|
1693
1426
|
export async function iosWaitForElement(options, udid) {
|
|
1694
1427
|
const timeoutMs = options.timeoutMs ?? 10000;
|
|
1695
1428
|
const pollIntervalMs = options.pollIntervalMs ?? 500;
|