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/android.js
CHANGED
|
@@ -4,19 +4,10 @@ import os from "os";
|
|
|
4
4
|
import sharp from "sharp";
|
|
5
5
|
import { notifyDriverMissing } from "./logbox.js";
|
|
6
6
|
import { execAsync } from "./exec.js";
|
|
7
|
-
// XML parsing for uiautomator dump
|
|
8
7
|
import { XMLParser } from "fast-xml-parser";
|
|
9
|
-
// ADB command timeout in milliseconds
|
|
10
8
|
const ADB_TIMEOUT = 30000;
|
|
11
|
-
// Module-level cache for ADB availability — checked once per session.
|
|
12
|
-
// `null` = unchecked, boolean = decided. Reset only via `resetAdbAvailabilityCache()` (tests).
|
|
13
9
|
let adbAvailableCache = null;
|
|
14
10
|
let adbPathFixApplied = false;
|
|
15
|
-
/**
|
|
16
|
-
* Standard locations where the Android SDK platform-tools may live when adb is
|
|
17
|
-
* installed but not on the spawned process's PATH (very common on macOS where
|
|
18
|
-
* users add platform-tools only to ~/.zshrc, which GUI-spawned processes don't read).
|
|
19
|
-
*/
|
|
20
11
|
function candidateAdbDirs() {
|
|
21
12
|
const dirs = [];
|
|
22
13
|
const env = process.env;
|
|
@@ -40,19 +31,6 @@ function candidateAdbDirs() {
|
|
|
40
31
|
function adbBinaryName() {
|
|
41
32
|
return process.platform === "win32" ? "adb.exe" : "adb";
|
|
42
33
|
}
|
|
43
|
-
/**
|
|
44
|
-
* Check if ADB is reachable. Caches the result for the lifetime of the process.
|
|
45
|
-
*
|
|
46
|
-
* Resolution order:
|
|
47
|
-
* 1. `adb version` against the inherited PATH.
|
|
48
|
-
* 2. Standard SDK install paths (ANDROID_HOME, ~/Library/Android/sdk/..., etc).
|
|
49
|
-
* If found, prepend the directory to process.env.PATH so subsequent bare
|
|
50
|
-
* `adb` invocations work without any call-site changes.
|
|
51
|
-
*
|
|
52
|
-
* The recovery path covers the common case of macOS users with Android Studio
|
|
53
|
-
* installed but adb missing from PATH because ~/.zshrc isn't sourced by GUI-spawned
|
|
54
|
-
* processes (Claude Code, VS Code MCP, etc).
|
|
55
|
-
*/
|
|
56
34
|
export async function isAdbAvailable() {
|
|
57
35
|
if (adbAvailableCache !== null)
|
|
58
36
|
return adbAvailableCache;
|
|
@@ -62,7 +40,6 @@ export async function isAdbAvailable() {
|
|
|
62
40
|
return true;
|
|
63
41
|
}
|
|
64
42
|
catch {
|
|
65
|
-
// Fall through to path discovery below.
|
|
66
43
|
}
|
|
67
44
|
if (!adbPathFixApplied) {
|
|
68
45
|
for (const dir of candidateAdbDirs()) {
|
|
@@ -78,7 +55,6 @@ export async function isAdbAvailable() {
|
|
|
78
55
|
return true;
|
|
79
56
|
}
|
|
80
57
|
catch {
|
|
81
|
-
// Found the binary but it didn't run — try next candidate.
|
|
82
58
|
continue;
|
|
83
59
|
}
|
|
84
60
|
}
|
|
@@ -86,27 +62,17 @@ export async function isAdbAvailable() {
|
|
|
86
62
|
adbAvailableCache = false;
|
|
87
63
|
return false;
|
|
88
64
|
}
|
|
89
|
-
/**
|
|
90
|
-
* Test-only: clear the ADB availability cache so the next call re-probes.
|
|
91
|
-
*/
|
|
92
65
|
export function resetAdbAvailabilityCache() {
|
|
93
66
|
adbAvailableCache = null;
|
|
94
67
|
adbPathFixApplied = false;
|
|
95
68
|
}
|
|
96
69
|
const ADB_MISSING_ERROR = "ADB is not installed or not in PATH. To fix:\n1. Install Android SDK Platform Tools: https://developer.android.com/tools/releases/platform-tools\n2. Add to PATH: export PATH=$PATH:~/Library/Android/sdk/platform-tools\n3. Verify: run 'adb devices' in terminal";
|
|
97
|
-
/**
|
|
98
|
-
* Check ADB availability and push a LogBox notification if missing.
|
|
99
|
-
* Returns an AdbResult error if ADB is unavailable, or null if ready.
|
|
100
|
-
*/
|
|
101
70
|
async function requireAdb() {
|
|
102
71
|
if (await isAdbAvailable())
|
|
103
72
|
return null;
|
|
104
73
|
notifyDriverMissing("android");
|
|
105
74
|
return { success: false, error: ADB_MISSING_ERROR };
|
|
106
75
|
}
|
|
107
|
-
/**
|
|
108
|
-
* List connected Android devices
|
|
109
|
-
*/
|
|
110
76
|
export async function listAndroidDevices() {
|
|
111
77
|
try {
|
|
112
78
|
const adbMissing = await requireAdb();
|
|
@@ -114,7 +80,6 @@ export async function listAndroidDevices() {
|
|
|
114
80
|
return adbMissing;
|
|
115
81
|
const { stdout } = await execAsync("adb devices -l", { timeout: ADB_TIMEOUT });
|
|
116
82
|
const lines = stdout.trim().split("\n");
|
|
117
|
-
// Skip the "List of devices attached" header
|
|
118
83
|
const deviceLines = lines.slice(1).filter((line) => line.trim().length > 0);
|
|
119
84
|
if (deviceLines.length === 0) {
|
|
120
85
|
return {
|
|
@@ -127,7 +92,6 @@ export async function listAndroidDevices() {
|
|
|
127
92
|
const id = parts[0];
|
|
128
93
|
const status = parts[1];
|
|
129
94
|
const device = { id, status };
|
|
130
|
-
// Parse additional info like product:xxx model:xxx device:xxx transport_id:xxx
|
|
131
95
|
for (let i = 2; i < parts.length; i++) {
|
|
132
96
|
const [key, value] = parts[i].split(":");
|
|
133
97
|
if (key === "product")
|
|
@@ -164,9 +128,6 @@ export async function listAndroidDevices() {
|
|
|
164
128
|
};
|
|
165
129
|
}
|
|
166
130
|
}
|
|
167
|
-
/**
|
|
168
|
-
* Get the first connected Android device ID
|
|
169
|
-
*/
|
|
170
131
|
export async function getDefaultAndroidDevice() {
|
|
171
132
|
try {
|
|
172
133
|
const { stdout } = await execAsync("adb devices", { timeout: ADB_TIMEOUT });
|
|
@@ -184,19 +145,12 @@ export async function getDefaultAndroidDevice() {
|
|
|
184
145
|
return null;
|
|
185
146
|
}
|
|
186
147
|
}
|
|
187
|
-
/**
|
|
188
|
-
* Locate the `emulator` binary. Returns the command string to invoke it
|
|
189
|
-
* (either bare "emulator" if it's on PATH, or a quoted absolute path).
|
|
190
|
-
* Returns null when the binary cannot be located. Mirrors `isAdbAvailable`'s
|
|
191
|
-
* PATH → ANDROID_HOME → common-install-paths discovery order.
|
|
192
|
-
*/
|
|
193
148
|
async function findEmulatorBinary() {
|
|
194
149
|
try {
|
|
195
150
|
await execAsync("emulator -help", { timeout: 3000 });
|
|
196
151
|
return "emulator";
|
|
197
152
|
}
|
|
198
153
|
catch {
|
|
199
|
-
// not on PATH — try standard install locations
|
|
200
154
|
}
|
|
201
155
|
const env = process.env;
|
|
202
156
|
const candidates = [];
|
|
@@ -211,20 +165,10 @@ async function findEmulatorBinary() {
|
|
|
211
165
|
return `"${candidate}"`;
|
|
212
166
|
}
|
|
213
167
|
catch {
|
|
214
|
-
// try next
|
|
215
168
|
}
|
|
216
169
|
}
|
|
217
170
|
return null;
|
|
218
171
|
}
|
|
219
|
-
/**
|
|
220
|
-
* List all Android Virtual Devices defined on the host machine, regardless of
|
|
221
|
-
* whether they are currently running.
|
|
222
|
-
*
|
|
223
|
-
* Returns `[]` when adb is missing, the emulator binary cannot be located, or
|
|
224
|
-
* the listing fails. Callers should treat that as "no inventory available"
|
|
225
|
-
* rather than an error so the surrounding tool can still surface other
|
|
226
|
-
* platforms.
|
|
227
|
-
*/
|
|
228
172
|
export async function getAndroidEmulatorAvds() {
|
|
229
173
|
const adbMissing = await requireAdb();
|
|
230
174
|
if (adbMissing)
|
|
@@ -243,22 +187,13 @@ export async function getAndroidEmulatorAvds() {
|
|
|
243
187
|
return [];
|
|
244
188
|
}
|
|
245
189
|
}
|
|
246
|
-
/**
|
|
247
|
-
* Map a running emulator's AVD name to its adb serial (e.g. "emulator-5554").
|
|
248
|
-
*
|
|
249
|
-
* Strategy: list adb devices, filter to `emulator-NNNN` serials, then call
|
|
250
|
-
* `adb -s <serial> emu avd name` per serial until one matches the requested
|
|
251
|
-
* AVD name. Returns `null` if no running emulator matches or if adb is
|
|
252
|
-
* unavailable. This is the slow path for the resolver — callers should prefer
|
|
253
|
-
* the cached registry entry first.
|
|
254
|
-
*/
|
|
255
190
|
export async function getAdbIdForAvd(avdName) {
|
|
256
191
|
const adbMissing = await requireAdb();
|
|
257
192
|
if (adbMissing)
|
|
258
193
|
return null;
|
|
259
194
|
try {
|
|
260
195
|
const { stdout: devicesOut } = await execAsync("adb devices", { timeout: ADB_TIMEOUT });
|
|
261
|
-
const lines = devicesOut.trim().split("\n").slice(1);
|
|
196
|
+
const lines = devicesOut.trim().split("\n").slice(1);
|
|
262
197
|
const emulatorSerials = lines
|
|
263
198
|
.map((line) => line.trim().split(/\s+/)[0])
|
|
264
199
|
.filter((id) => /^emulator-\d+$/.test(id));
|
|
@@ -273,7 +208,6 @@ export async function getAdbIdForAvd(avdName) {
|
|
|
273
208
|
return serial;
|
|
274
209
|
}
|
|
275
210
|
catch {
|
|
276
|
-
// skip this serial, try next
|
|
277
211
|
}
|
|
278
212
|
}
|
|
279
213
|
return null;
|
|
@@ -282,15 +216,9 @@ export async function getAdbIdForAvd(avdName) {
|
|
|
282
216
|
return null;
|
|
283
217
|
}
|
|
284
218
|
}
|
|
285
|
-
/**
|
|
286
|
-
* Build device selector for ADB command
|
|
287
|
-
*/
|
|
288
219
|
function buildDeviceArg(deviceId) {
|
|
289
220
|
return deviceId ? `-s ${deviceId}` : "";
|
|
290
221
|
}
|
|
291
|
-
/**
|
|
292
|
-
* Take a screenshot from an Android device
|
|
293
|
-
*/
|
|
294
222
|
export async function androidScreenshot(outputPath, deviceId, signal) {
|
|
295
223
|
try {
|
|
296
224
|
const adbMissing = await requireAdb();
|
|
@@ -304,29 +232,22 @@ export async function androidScreenshot(outputPath, deviceId, signal) {
|
|
|
304
232
|
};
|
|
305
233
|
}
|
|
306
234
|
const deviceArg = buildDeviceArg(device);
|
|
307
|
-
// Generate output path if not provided
|
|
308
235
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
309
236
|
const finalOutputPath = outputPath || path.join(os.tmpdir(), `android-screenshot-${timestamp}.png`);
|
|
310
|
-
// Capture screenshot on device
|
|
311
237
|
const remotePath = "/sdcard/screenshot-temp.png";
|
|
312
238
|
await execAsync(`adb ${deviceArg} shell screencap -p ${remotePath}`, {
|
|
313
239
|
timeout: ADB_TIMEOUT,
|
|
314
240
|
signal
|
|
315
241
|
});
|
|
316
|
-
// Pull screenshot to local machine
|
|
317
242
|
await execAsync(`adb ${deviceArg} pull ${remotePath} "${finalOutputPath}"`, {
|
|
318
243
|
timeout: ADB_TIMEOUT,
|
|
319
244
|
signal
|
|
320
245
|
});
|
|
321
|
-
// Clean up remote file
|
|
322
246
|
await execAsync(`adb ${deviceArg} shell rm ${remotePath}`, {
|
|
323
247
|
timeout: ADB_TIMEOUT,
|
|
324
248
|
signal
|
|
325
249
|
}).catch(() => {
|
|
326
|
-
// Ignore cleanup errors
|
|
327
250
|
});
|
|
328
|
-
// Resize image if needed (API limit: 2000px max for multi-image requests)
|
|
329
|
-
// Return scale factor so AI can convert image coords to device coords
|
|
330
251
|
const MAX_DIMENSION = 2000;
|
|
331
252
|
const image = sharp(finalOutputPath);
|
|
332
253
|
const metadata = await image.metadata();
|
|
@@ -335,7 +256,6 @@ export async function androidScreenshot(outputPath, deviceId, signal) {
|
|
|
335
256
|
let imageData;
|
|
336
257
|
let scaleFactor = 1;
|
|
337
258
|
if (originalWidth > MAX_DIMENSION || originalHeight > MAX_DIMENSION) {
|
|
338
|
-
// Calculate scale to fit within MAX_DIMENSION
|
|
339
259
|
scaleFactor = Math.max(originalWidth, originalHeight) / MAX_DIMENSION;
|
|
340
260
|
imageData = await image
|
|
341
261
|
.resize(MAX_DIMENSION, MAX_DIMENSION, {
|
|
@@ -366,15 +286,11 @@ export async function androidScreenshot(outputPath, deviceId, signal) {
|
|
|
366
286
|
};
|
|
367
287
|
}
|
|
368
288
|
}
|
|
369
|
-
/**
|
|
370
|
-
* Install an APK on an Android device
|
|
371
|
-
*/
|
|
372
289
|
export async function androidInstallApp(apkPath, deviceId, options) {
|
|
373
290
|
try {
|
|
374
291
|
const adbMissing = await requireAdb();
|
|
375
292
|
if (adbMissing)
|
|
376
293
|
return adbMissing;
|
|
377
|
-
// Verify APK exists
|
|
378
294
|
if (!existsSync(apkPath)) {
|
|
379
295
|
return {
|
|
380
296
|
success: false,
|
|
@@ -389,15 +305,13 @@ export async function androidInstallApp(apkPath, deviceId, options) {
|
|
|
389
305
|
};
|
|
390
306
|
}
|
|
391
307
|
const deviceArg = buildDeviceArg(device);
|
|
392
|
-
// Build install flags
|
|
393
308
|
const flags = [];
|
|
394
309
|
if (options?.replace)
|
|
395
310
|
flags.push("-r");
|
|
396
311
|
if (options?.grantPermissions)
|
|
397
312
|
flags.push("-g");
|
|
398
313
|
const flagsStr = flags.length > 0 ? flags.join(" ") + " " : "";
|
|
399
|
-
const { stdout, stderr } = await execAsync(`adb ${deviceArg} install ${flagsStr}"${apkPath}"`, { timeout: 120000 }
|
|
400
|
-
);
|
|
314
|
+
const { stdout, stderr } = await execAsync(`adb ${deviceArg} install ${flagsStr}"${apkPath}"`, { timeout: 120000 });
|
|
401
315
|
const output = stdout + stderr;
|
|
402
316
|
if (output.includes("Success")) {
|
|
403
317
|
return {
|
|
@@ -419,9 +333,6 @@ export async function androidInstallApp(apkPath, deviceId, options) {
|
|
|
419
333
|
};
|
|
420
334
|
}
|
|
421
335
|
}
|
|
422
|
-
/**
|
|
423
|
-
* Launch an app on an Android device
|
|
424
|
-
*/
|
|
425
336
|
export async function androidLaunchApp(packageName, activityName, deviceId) {
|
|
426
337
|
try {
|
|
427
338
|
const adbMissing = await requireAdb();
|
|
@@ -437,16 +348,13 @@ export async function androidLaunchApp(packageName, activityName, deviceId) {
|
|
|
437
348
|
const deviceArg = buildDeviceArg(device);
|
|
438
349
|
let command;
|
|
439
350
|
if (activityName) {
|
|
440
|
-
// Launch specific activity
|
|
441
351
|
command = `adb ${deviceArg} shell am start -n ${packageName}/${activityName}`;
|
|
442
352
|
}
|
|
443
353
|
else {
|
|
444
|
-
// Launch main/launcher activity
|
|
445
354
|
command = `adb ${deviceArg} shell monkey -p ${packageName} -c android.intent.category.LAUNCHER 1`;
|
|
446
355
|
}
|
|
447
356
|
const { stdout, stderr } = await execAsync(command, { timeout: ADB_TIMEOUT });
|
|
448
357
|
const output = stdout + stderr;
|
|
449
|
-
// Check for errors
|
|
450
358
|
if (output.includes("Error") || output.includes("Exception")) {
|
|
451
359
|
return {
|
|
452
360
|
success: false,
|
|
@@ -465,9 +373,6 @@ export async function androidLaunchApp(packageName, activityName, deviceId) {
|
|
|
465
373
|
};
|
|
466
374
|
}
|
|
467
375
|
}
|
|
468
|
-
/**
|
|
469
|
-
* Get list of installed packages on the device
|
|
470
|
-
*/
|
|
471
376
|
export async function androidListPackages(deviceId, filter) {
|
|
472
377
|
try {
|
|
473
378
|
const adbMissing = await requireAdb();
|
|
@@ -511,12 +416,6 @@ export async function androidListPackages(deviceId, filter) {
|
|
|
511
416
|
};
|
|
512
417
|
}
|
|
513
418
|
}
|
|
514
|
-
// ============================================================================
|
|
515
|
-
// UI Input Functions (Phase 2)
|
|
516
|
-
// ============================================================================
|
|
517
|
-
/**
|
|
518
|
-
* Common key event codes for Android
|
|
519
|
-
*/
|
|
520
419
|
export const ANDROID_KEY_EVENTS = {
|
|
521
420
|
HOME: 3,
|
|
522
421
|
BACK: 4,
|
|
@@ -541,9 +440,6 @@ export const ANDROID_KEY_EVENTS = {
|
|
|
541
440
|
APP_SWITCH: 187,
|
|
542
441
|
ESCAPE: 111
|
|
543
442
|
};
|
|
544
|
-
/**
|
|
545
|
-
* Tap at coordinates on an Android device
|
|
546
|
-
*/
|
|
547
443
|
export async function androidTap(x, y, deviceId) {
|
|
548
444
|
try {
|
|
549
445
|
const adbMissing = await requireAdb();
|
|
@@ -572,9 +468,6 @@ export async function androidTap(x, y, deviceId) {
|
|
|
572
468
|
};
|
|
573
469
|
}
|
|
574
470
|
}
|
|
575
|
-
/**
|
|
576
|
-
* Long press at coordinates on an Android device
|
|
577
|
-
*/
|
|
578
471
|
export async function androidLongPress(x, y, durationMs = 1000, deviceId) {
|
|
579
472
|
try {
|
|
580
473
|
const adbMissing = await requireAdb();
|
|
@@ -588,7 +481,6 @@ export async function androidLongPress(x, y, durationMs = 1000, deviceId) {
|
|
|
588
481
|
};
|
|
589
482
|
}
|
|
590
483
|
const deviceArg = buildDeviceArg(device);
|
|
591
|
-
// Long press is implemented as a swipe from the same point to the same point
|
|
592
484
|
const xRounded = Math.round(x);
|
|
593
485
|
const yRounded = Math.round(y);
|
|
594
486
|
await execAsync(`adb ${deviceArg} shell input swipe ${xRounded} ${yRounded} ${xRounded} ${yRounded} ${durationMs}`, { timeout: ADB_TIMEOUT + durationMs });
|
|
@@ -604,9 +496,6 @@ export async function androidLongPress(x, y, durationMs = 1000, deviceId) {
|
|
|
604
496
|
};
|
|
605
497
|
}
|
|
606
498
|
}
|
|
607
|
-
/**
|
|
608
|
-
* Swipe on an Android device
|
|
609
|
-
*/
|
|
610
499
|
export async function androidSwipe(startX, startY, endX, endY, durationMs = 300, deviceId) {
|
|
611
500
|
try {
|
|
612
501
|
const adbMissing = await requireAdb();
|
|
@@ -637,12 +526,6 @@ export async function androidSwipe(startX, startY, endX, endY, durationMs = 300,
|
|
|
637
526
|
};
|
|
638
527
|
}
|
|
639
528
|
}
|
|
640
|
-
/**
|
|
641
|
-
* Input text on an Android device
|
|
642
|
-
*
|
|
643
|
-
* ADB input text has limitations with special characters.
|
|
644
|
-
* This function handles escaping properly for URLs, emails, and special strings.
|
|
645
|
-
*/
|
|
646
529
|
export async function androidInputText(text, deviceId) {
|
|
647
530
|
try {
|
|
648
531
|
const adbMissing = await requireAdb();
|
|
@@ -656,21 +539,15 @@ export async function androidInputText(text, deviceId) {
|
|
|
656
539
|
};
|
|
657
540
|
}
|
|
658
541
|
const deviceArg = buildDeviceArg(device);
|
|
659
|
-
// For complex strings with special characters, type character by character
|
|
660
|
-
// using key events for reliability
|
|
661
542
|
const hasComplexChars = /[/:?=&#@%+]/.test(text);
|
|
662
543
|
if (hasComplexChars) {
|
|
663
|
-
// Use character-by-character input for strings with special chars
|
|
664
|
-
// This is slower but more reliable for URLs, emails, etc.
|
|
665
544
|
for (const char of text) {
|
|
666
545
|
let keyCmd;
|
|
667
|
-
// Map special characters to their escaped form or use direct input
|
|
668
546
|
switch (char) {
|
|
669
547
|
case " ":
|
|
670
548
|
keyCmd = `adb ${deviceArg} shell input text "%s"`;
|
|
671
549
|
break;
|
|
672
550
|
case "'":
|
|
673
|
-
// Single quote needs special handling
|
|
674
551
|
keyCmd = `adb ${deviceArg} shell input text "\\'"`;
|
|
675
552
|
break;
|
|
676
553
|
case '"':
|
|
@@ -707,13 +584,9 @@ export async function androidInputText(text, deviceId) {
|
|
|
707
584
|
keyCmd = `adb ${deviceArg} shell input text "\\\`"`;
|
|
708
585
|
break;
|
|
709
586
|
case "#":
|
|
710
|
-
// # is a shell comment character — single quotes don't prevent interpretation
|
|
711
|
-
// on some Android shell versions, so use backslash escaping in double quotes
|
|
712
587
|
keyCmd = `adb ${deviceArg} shell input text "\\#"`;
|
|
713
588
|
break;
|
|
714
589
|
default:
|
|
715
|
-
// For most characters, wrap in single quotes to prevent shell interpretation
|
|
716
|
-
// Single quotes preserve literal meaning of all characters except single quote itself
|
|
717
590
|
keyCmd = `adb ${deviceArg} shell input text '${char}'`;
|
|
718
591
|
}
|
|
719
592
|
await execAsync(keyCmd, { timeout: 5000 });
|
|
@@ -723,8 +596,6 @@ export async function androidInputText(text, deviceId) {
|
|
|
723
596
|
result: `Typed: "${text}"`
|
|
724
597
|
};
|
|
725
598
|
}
|
|
726
|
-
// For simple alphanumeric strings, use the faster bulk input
|
|
727
|
-
// Escape basic special characters
|
|
728
599
|
const escapedText = text
|
|
729
600
|
.replace(/\\/g, "\\\\")
|
|
730
601
|
.replace(/"/g, '\\"')
|
|
@@ -747,9 +618,6 @@ export async function androidInputText(text, deviceId) {
|
|
|
747
618
|
};
|
|
748
619
|
}
|
|
749
620
|
}
|
|
750
|
-
/**
|
|
751
|
-
* Send a key event to an Android device
|
|
752
|
-
*/
|
|
753
621
|
export async function androidKeyEvent(keyCode, deviceId) {
|
|
754
622
|
try {
|
|
755
623
|
const adbMissing = await requireAdb();
|
|
@@ -763,7 +631,6 @@ export async function androidKeyEvent(keyCode, deviceId) {
|
|
|
763
631
|
};
|
|
764
632
|
}
|
|
765
633
|
const deviceArg = buildDeviceArg(device);
|
|
766
|
-
// Resolve key code from name if needed
|
|
767
634
|
const resolvedKeyCode = typeof keyCode === "string" ? ANDROID_KEY_EVENTS[keyCode] : keyCode;
|
|
768
635
|
if (resolvedKeyCode === undefined) {
|
|
769
636
|
return {
|
|
@@ -774,7 +641,6 @@ export async function androidKeyEvent(keyCode, deviceId) {
|
|
|
774
641
|
await execAsync(`adb ${deviceArg} shell input keyevent ${resolvedKeyCode}`, {
|
|
775
642
|
timeout: ADB_TIMEOUT
|
|
776
643
|
});
|
|
777
|
-
// Get key name for display
|
|
778
644
|
const keyName = typeof keyCode === "string"
|
|
779
645
|
? keyCode
|
|
780
646
|
: Object.entries(ANDROID_KEY_EVENTS).find(([_, v]) => v === keyCode)?.[0] ||
|
|
@@ -791,9 +657,6 @@ export async function androidKeyEvent(keyCode, deviceId) {
|
|
|
791
657
|
};
|
|
792
658
|
}
|
|
793
659
|
}
|
|
794
|
-
/**
|
|
795
|
-
* Parse bounds string like "[0,0][1080,1920]" to AndroidUIElement bounds
|
|
796
|
-
*/
|
|
797
660
|
function parseBoundsForUIElement(boundsStr) {
|
|
798
661
|
const match = boundsStr.match(/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/);
|
|
799
662
|
if (!match)
|
|
@@ -811,19 +674,14 @@ function parseBoundsForUIElement(boundsStr) {
|
|
|
811
674
|
height: bottom - top
|
|
812
675
|
};
|
|
813
676
|
}
|
|
814
|
-
/**
|
|
815
|
-
* Parse uiautomator XML dump into element array
|
|
816
|
-
*/
|
|
817
677
|
function parseUIAutomatorXML(xml) {
|
|
818
678
|
const elements = [];
|
|
819
|
-
// Match all node elements with their attributes
|
|
820
679
|
const nodeRegex = /<node\s+([^>]+)\/?>|<node\s+([^>]+)>/g;
|
|
821
680
|
let match;
|
|
822
681
|
while ((match = nodeRegex.exec(xml)) !== null) {
|
|
823
682
|
const attrStr = match[1] || match[2];
|
|
824
683
|
if (!attrStr)
|
|
825
684
|
continue;
|
|
826
|
-
// Extract attributes
|
|
827
685
|
const getAttr = (name) => {
|
|
828
686
|
const attrMatch = attrStr.match(new RegExp(`${name}="([^"]*)"`));
|
|
829
687
|
return attrMatch ? attrMatch[1] : "";
|
|
@@ -852,23 +710,11 @@ function parseUIAutomatorXML(xml) {
|
|
|
852
710
|
}
|
|
853
711
|
return elements;
|
|
854
712
|
}
|
|
855
|
-
/**
|
|
856
|
-
* Match element against find options
|
|
857
|
-
*/
|
|
858
|
-
/**
|
|
859
|
-
* Check if an element's center is within the screen viewport.
|
|
860
|
-
* Filters out off-screen elements in scroll views, pagers, etc.
|
|
861
|
-
*/
|
|
862
713
|
function isAndroidElementInViewport(element, screenWidth, screenHeight) {
|
|
863
714
|
const { center } = element;
|
|
864
715
|
return center.x >= 0 && center.x <= screenWidth && center.y >= 0 && center.y <= screenHeight;
|
|
865
716
|
}
|
|
866
|
-
/**
|
|
867
|
-
* Extract screen dimensions from the uiautomator XML.
|
|
868
|
-
* The root hierarchy node's bounds represent the screen size.
|
|
869
|
-
*/
|
|
870
717
|
function extractAndroidScreenSize(rawXml) {
|
|
871
|
-
// Match the first node's bounds (root element = screen)
|
|
872
718
|
const match = rawXml.match(/<(?:hierarchy|node)[^>]*bounds="\[0,0\]\[(\d+),(\d+)\]"/);
|
|
873
719
|
if (match) {
|
|
874
720
|
return { width: parseInt(match[1], 10), height: parseInt(match[2], 10) };
|
|
@@ -893,16 +739,12 @@ function matchesElement(element, options) {
|
|
|
893
739
|
return false;
|
|
894
740
|
}
|
|
895
741
|
if (options.resourceId !== undefined) {
|
|
896
|
-
// Support both full "com.app:id/button" and short "button" forms
|
|
897
742
|
const shortId = element.resourceId.split("/").pop() || "";
|
|
898
743
|
if (element.resourceId !== options.resourceId && shortId !== options.resourceId)
|
|
899
744
|
return false;
|
|
900
745
|
}
|
|
901
746
|
return true;
|
|
902
747
|
}
|
|
903
|
-
/**
|
|
904
|
-
* Get UI accessibility tree from Android device using uiautomator
|
|
905
|
-
*/
|
|
906
748
|
export async function androidGetUITree(deviceId, signal) {
|
|
907
749
|
try {
|
|
908
750
|
const adbMissing = await requireAdb();
|
|
@@ -916,21 +758,15 @@ export async function androidGetUITree(deviceId, signal) {
|
|
|
916
758
|
};
|
|
917
759
|
}
|
|
918
760
|
const deviceArg = buildDeviceArg(device);
|
|
919
|
-
// Dump UI hierarchy to device. The `signal` is passed so a strategy-level
|
|
920
|
-
// timeout in tap.ts can SIGKILL the host adb process — the device-side
|
|
921
|
-
// uiautomator dump is independent and finishes silently, but we stop
|
|
922
|
-
// waiting on the ADB channel so the next strategy can use it.
|
|
923
761
|
const remotePath = "/sdcard/ui_dump.xml";
|
|
924
762
|
await execAsync(`adb ${deviceArg} shell uiautomator dump ${remotePath}`, {
|
|
925
763
|
timeout: ADB_TIMEOUT,
|
|
926
764
|
signal
|
|
927
765
|
});
|
|
928
|
-
// Read the XML content
|
|
929
766
|
const { stdout } = await execAsync(`adb ${deviceArg} shell cat ${remotePath}`, {
|
|
930
767
|
timeout: ADB_TIMEOUT,
|
|
931
768
|
signal
|
|
932
769
|
});
|
|
933
|
-
// Clean up remote file
|
|
934
770
|
await execAsync(`adb ${deviceArg} shell rm ${remotePath}`, {
|
|
935
771
|
timeout: ADB_TIMEOUT,
|
|
936
772
|
signal
|
|
@@ -951,7 +787,6 @@ export async function androidGetUITree(deviceId, signal) {
|
|
|
951
787
|
}
|
|
952
788
|
export async function androidFindElement(options, deviceId, signal, tree) {
|
|
953
789
|
try {
|
|
954
|
-
// Validate that at least one search criteria is provided
|
|
955
790
|
if (!options.text && !options.textContains && !options.contentDesc &&
|
|
956
791
|
!options.contentDescContains && !options.resourceId) {
|
|
957
792
|
return {
|
|
@@ -960,9 +795,6 @@ export async function androidFindElement(options, deviceId, signal, tree) {
|
|
|
960
795
|
error: "At least one search criteria (text, textContains, contentDesc, contentDescContains, or resourceId) must be provided"
|
|
961
796
|
};
|
|
962
797
|
}
|
|
963
|
-
// A caller that already dumped the hierarchy can pass it in — uiautomator
|
|
964
|
-
// dumps are the single most expensive step of an Android accessibility
|
|
965
|
-
// tap, and the tap strategy runs two passes over the same screen.
|
|
966
798
|
const treeResult = tree ?? (await androidGetUITree(deviceId, signal));
|
|
967
799
|
if (!treeResult.success || !treeResult.elements) {
|
|
968
800
|
return {
|
|
@@ -971,15 +803,11 @@ export async function androidFindElement(options, deviceId, signal, tree) {
|
|
|
971
803
|
error: treeResult.error
|
|
972
804
|
};
|
|
973
805
|
}
|
|
974
|
-
// Filter to viewport-visible elements first, then match criteria.
|
|
975
|
-
// This prevents selecting off-screen duplicates in scroll views / pagers.
|
|
976
806
|
const screenSize = treeResult.rawXml ? extractAndroidScreenSize(treeResult.rawXml) : null;
|
|
977
807
|
const visibleElements = screenSize
|
|
978
808
|
? treeResult.elements.filter(el => isAndroidElementInViewport(el, screenSize.width, screenSize.height))
|
|
979
809
|
: treeResult.elements;
|
|
980
810
|
let matches = visibleElements.filter(el => matchesElement(el, options));
|
|
981
|
-
// If no visible matches, fall back to all elements (element may be partially visible
|
|
982
|
-
// or the screen size extraction failed)
|
|
983
811
|
if (matches.length === 0) {
|
|
984
812
|
matches = treeResult.elements.filter(el => matchesElement(el, options));
|
|
985
813
|
}
|
|
@@ -990,7 +818,6 @@ export async function androidFindElement(options, deviceId, signal, tree) {
|
|
|
990
818
|
matchCount: 0
|
|
991
819
|
};
|
|
992
820
|
}
|
|
993
|
-
// Select the element at the specified index (default 0)
|
|
994
821
|
const index = options.index ?? 0;
|
|
995
822
|
const selectedElement = matches[index];
|
|
996
823
|
if (!selectedElement) {
|
|
@@ -1017,14 +844,10 @@ export async function androidFindElement(options, deviceId, signal, tree) {
|
|
|
1017
844
|
};
|
|
1018
845
|
}
|
|
1019
846
|
}
|
|
1020
|
-
/**
|
|
1021
|
-
* Wait for element to appear on screen with polling
|
|
1022
|
-
*/
|
|
1023
847
|
export async function androidWaitForElement(options, deviceId) {
|
|
1024
848
|
const timeoutMs = options.timeoutMs ?? 10000;
|
|
1025
849
|
const pollIntervalMs = options.pollIntervalMs ?? 500;
|
|
1026
850
|
const startTime = Date.now();
|
|
1027
|
-
// Validate that at least one search criteria is provided
|
|
1028
851
|
if (!options.text && !options.textContains && !options.contentDesc &&
|
|
1029
852
|
!options.contentDescContains && !options.resourceId) {
|
|
1030
853
|
return {
|
|
@@ -1043,7 +866,6 @@ export async function androidWaitForElement(options, deviceId) {
|
|
|
1043
866
|
timedOut: false
|
|
1044
867
|
};
|
|
1045
868
|
}
|
|
1046
|
-
// If there was an error (not just "not found"), return it
|
|
1047
869
|
if (!result.success) {
|
|
1048
870
|
return {
|
|
1049
871
|
...result,
|
|
@@ -1051,7 +873,6 @@ export async function androidWaitForElement(options, deviceId) {
|
|
|
1051
873
|
timedOut: false
|
|
1052
874
|
};
|
|
1053
875
|
}
|
|
1054
|
-
// Wait before next poll
|
|
1055
876
|
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
|
|
1056
877
|
}
|
|
1057
878
|
return {
|
|
@@ -1062,9 +883,6 @@ export async function androidWaitForElement(options, deviceId) {
|
|
|
1062
883
|
error: `Timed out after ${timeoutMs}ms waiting for element`
|
|
1063
884
|
};
|
|
1064
885
|
}
|
|
1065
|
-
/**
|
|
1066
|
-
* Get device screen size
|
|
1067
|
-
*/
|
|
1068
886
|
export async function androidGetScreenSize(deviceId) {
|
|
1069
887
|
try {
|
|
1070
888
|
const adbMissing = await requireAdb();
|
|
@@ -1081,7 +899,6 @@ export async function androidGetScreenSize(deviceId) {
|
|
|
1081
899
|
const { stdout } = await execAsync(`adb ${deviceArg} shell wm size`, {
|
|
1082
900
|
timeout: ADB_TIMEOUT
|
|
1083
901
|
});
|
|
1084
|
-
// Parse output like "Physical size: 1080x1920"
|
|
1085
902
|
const match = stdout.match(/(\d+)x(\d+)/);
|
|
1086
903
|
if (match) {
|
|
1087
904
|
return {
|
|
@@ -1102,9 +919,6 @@ export async function androidGetScreenSize(deviceId) {
|
|
|
1102
919
|
};
|
|
1103
920
|
}
|
|
1104
921
|
}
|
|
1105
|
-
/**
|
|
1106
|
-
* Get device display density (dpi)
|
|
1107
|
-
*/
|
|
1108
922
|
export async function androidGetDensity(deviceId) {
|
|
1109
923
|
try {
|
|
1110
924
|
const adbMissing = await requireAdb();
|
|
@@ -1121,7 +935,6 @@ export async function androidGetDensity(deviceId) {
|
|
|
1121
935
|
const { stdout } = await execAsync(`adb ${deviceArg} shell wm density`, {
|
|
1122
936
|
timeout: ADB_TIMEOUT
|
|
1123
937
|
});
|
|
1124
|
-
// Parse output like "Physical density: 440" or "Override density: 440"
|
|
1125
938
|
const match = stdout.match(/density:\s*(\d+)/i);
|
|
1126
939
|
if (match) {
|
|
1127
940
|
return {
|
|
@@ -1141,30 +954,21 @@ export async function androidGetDensity(deviceId) {
|
|
|
1141
954
|
};
|
|
1142
955
|
}
|
|
1143
956
|
}
|
|
1144
|
-
/**
|
|
1145
|
-
* Get status bar height in pixels
|
|
1146
|
-
* Android status bar is typically 24dp, but can vary by device/OS version
|
|
1147
|
-
*/
|
|
1148
957
|
export async function androidGetStatusBarHeight(deviceId) {
|
|
1149
958
|
try {
|
|
1150
|
-
// Get density first
|
|
1151
959
|
const densityResult = await androidGetDensity(deviceId);
|
|
1152
960
|
if (!densityResult.success || !densityResult.density) {
|
|
1153
|
-
// Fallback to common estimate
|
|
1154
961
|
return {
|
|
1155
962
|
success: true,
|
|
1156
|
-
heightPixels: 63,
|
|
963
|
+
heightPixels: 63,
|
|
1157
964
|
heightDp: 24
|
|
1158
965
|
};
|
|
1159
966
|
}
|
|
1160
967
|
const density = densityResult.density;
|
|
1161
|
-
const densityScale = density / 160;
|
|
1162
|
-
// Try to get actual status bar height from resources
|
|
968
|
+
const densityScale = density / 160;
|
|
1163
969
|
const deviceArg = buildDeviceArg(deviceId);
|
|
1164
970
|
try {
|
|
1165
971
|
const { stdout } = await execAsync(`adb ${deviceArg} shell "dumpsys window | grep -E 'statusBars|mStatusBarLayer|InsetsSource.*statusBars'"`, { timeout: ADB_TIMEOUT });
|
|
1166
|
-
// Try to parse status bar height from dumpsys output
|
|
1167
|
-
// Look for patterns like "statusBars frame=[0,0][1080,63]"
|
|
1168
972
|
const frameMatch = stdout.match(/statusBars.*frame=\[[\d,]+\]\[(\d+),(\d+)\]/);
|
|
1169
973
|
if (frameMatch) {
|
|
1170
974
|
const heightPixels = parseInt(frameMatch[2], 10);
|
|
@@ -1176,9 +980,7 @@ export async function androidGetStatusBarHeight(deviceId) {
|
|
|
1176
980
|
}
|
|
1177
981
|
}
|
|
1178
982
|
catch {
|
|
1179
|
-
// Fallback to standard calculation
|
|
1180
983
|
}
|
|
1181
|
-
// Standard status bar height is 24dp on most devices
|
|
1182
984
|
const statusBarDp = 24;
|
|
1183
985
|
const statusBarPixels = Math.round(statusBarDp * densityScale);
|
|
1184
986
|
return {
|
|
@@ -1194,20 +996,12 @@ export async function androidGetStatusBarHeight(deviceId) {
|
|
|
1194
996
|
};
|
|
1195
997
|
}
|
|
1196
998
|
}
|
|
1197
|
-
/**
|
|
1198
|
-
* Simplify Android class name for display
|
|
1199
|
-
* android.widget.Button -> Button
|
|
1200
|
-
* android.widget.TextView -> TextView
|
|
1201
|
-
*/
|
|
1202
999
|
function simplifyClassName(className) {
|
|
1203
1000
|
if (!className)
|
|
1204
1001
|
return "Unknown";
|
|
1205
1002
|
const parts = className.split(".");
|
|
1206
1003
|
return parts[parts.length - 1];
|
|
1207
1004
|
}
|
|
1208
|
-
/**
|
|
1209
|
-
* Parse bounds string "[left,top][right,bottom]" to object
|
|
1210
|
-
*/
|
|
1211
1005
|
function parseBounds(boundsStr) {
|
|
1212
1006
|
const match = boundsStr?.match(/\[(\d+),(\d+)\]\[(\d+),(\d+)\]/);
|
|
1213
1007
|
if (!match)
|
|
@@ -1219,9 +1013,6 @@ function parseBounds(boundsStr) {
|
|
|
1219
1013
|
bottom: parseInt(match[4], 10)
|
|
1220
1014
|
};
|
|
1221
1015
|
}
|
|
1222
|
-
/**
|
|
1223
|
-
* Parse a single node from uiautomator XML
|
|
1224
|
-
*/
|
|
1225
1016
|
function parseUiNode(node) {
|
|
1226
1017
|
const attrs = node["@_bounds"]
|
|
1227
1018
|
? node
|
|
@@ -1253,7 +1044,6 @@ function parseUiNode(node) {
|
|
|
1253
1044
|
},
|
|
1254
1045
|
children: []
|
|
1255
1046
|
};
|
|
1256
|
-
// Add optional attributes
|
|
1257
1047
|
if (attrs["@_text"])
|
|
1258
1048
|
element.text = attrs["@_text"];
|
|
1259
1049
|
if (attrs["@_content-desc"])
|
|
@@ -1278,16 +1068,11 @@ function parseUiNode(node) {
|
|
|
1278
1068
|
element.selected = true;
|
|
1279
1069
|
return element;
|
|
1280
1070
|
}
|
|
1281
|
-
/**
|
|
1282
|
-
* Recursively parse UI hierarchy from XML node
|
|
1283
|
-
*/
|
|
1284
1071
|
function parseHierarchy(node) {
|
|
1285
1072
|
const results = [];
|
|
1286
|
-
// Handle the node itself
|
|
1287
1073
|
if (node["@_bounds"]) {
|
|
1288
1074
|
const element = parseUiNode(node);
|
|
1289
1075
|
if (element) {
|
|
1290
|
-
// Parse children
|
|
1291
1076
|
if (node.node) {
|
|
1292
1077
|
const children = Array.isArray(node.node) ? node.node : [node.node];
|
|
1293
1078
|
for (const child of children) {
|
|
@@ -1298,7 +1083,6 @@ function parseHierarchy(node) {
|
|
|
1298
1083
|
}
|
|
1299
1084
|
}
|
|
1300
1085
|
else if (node.node) {
|
|
1301
|
-
// This is a container without bounds (like hierarchy root)
|
|
1302
1086
|
const children = Array.isArray(node.node) ? node.node : [node.node];
|
|
1303
1087
|
for (const child of children) {
|
|
1304
1088
|
results.push(...parseHierarchy(child));
|
|
@@ -1306,35 +1090,25 @@ function parseHierarchy(node) {
|
|
|
1306
1090
|
}
|
|
1307
1091
|
return results;
|
|
1308
1092
|
}
|
|
1309
|
-
/**
|
|
1310
|
-
* Format accessibility tree for display (similar to iOS format)
|
|
1311
|
-
*/
|
|
1312
1093
|
function formatAndroidAccessibilityTree(elements, indent = 0) {
|
|
1313
1094
|
const lines = [];
|
|
1314
1095
|
const prefix = " ".repeat(indent);
|
|
1315
1096
|
for (const element of elements) {
|
|
1316
1097
|
const parts = [];
|
|
1317
|
-
// [ClassName] "text" or "content-desc"
|
|
1318
1098
|
parts.push(`[${element.class}]`);
|
|
1319
|
-
// Add label (text or content-desc)
|
|
1320
1099
|
const label = element.text || element.contentDesc;
|
|
1321
1100
|
if (label) {
|
|
1322
1101
|
parts.push(`"${label}"`);
|
|
1323
1102
|
}
|
|
1324
|
-
// Add frame and tap coordinates
|
|
1325
1103
|
const f = element.frame;
|
|
1326
1104
|
parts.push(`frame=(${f.x}, ${f.y}, ${f.width}x${f.height}) tap=(${element.tap.x}, ${element.tap.y})`);
|
|
1327
1105
|
lines.push(`${prefix}${parts.join(" ")}`);
|
|
1328
|
-
// Recurse into children
|
|
1329
1106
|
if (element.children.length > 0) {
|
|
1330
1107
|
lines.push(formatAndroidAccessibilityTree(element.children, indent + 1));
|
|
1331
1108
|
}
|
|
1332
1109
|
}
|
|
1333
1110
|
return lines.join("\n");
|
|
1334
1111
|
}
|
|
1335
|
-
/**
|
|
1336
|
-
* Flatten element tree to array for searching
|
|
1337
|
-
*/
|
|
1338
1112
|
function flattenElements(elements) {
|
|
1339
1113
|
const result = [];
|
|
1340
1114
|
for (const element of elements) {
|
|
@@ -1345,9 +1119,6 @@ function flattenElements(elements) {
|
|
|
1345
1119
|
}
|
|
1346
1120
|
return result;
|
|
1347
1121
|
}
|
|
1348
|
-
/**
|
|
1349
|
-
* Get the UI hierarchy from the connected Android device using uiautomator dump
|
|
1350
|
-
*/
|
|
1351
1122
|
export async function androidDescribeAll(deviceId) {
|
|
1352
1123
|
try {
|
|
1353
1124
|
const adbMissing = await requireAdb();
|
|
@@ -1361,8 +1132,6 @@ export async function androidDescribeAll(deviceId) {
|
|
|
1361
1132
|
};
|
|
1362
1133
|
}
|
|
1363
1134
|
const deviceArg = buildDeviceArg(device);
|
|
1364
|
-
// Use file-based approach (most reliable across devices)
|
|
1365
|
-
// /dev/tty doesn't work on most emulators/devices
|
|
1366
1135
|
const remotePath = "/sdcard/ui_dump.xml";
|
|
1367
1136
|
await execAsync(`adb ${deviceArg} shell uiautomator dump ${remotePath}`, {
|
|
1368
1137
|
timeout: ADB_TIMEOUT
|
|
@@ -1372,7 +1141,6 @@ export async function androidDescribeAll(deviceId) {
|
|
|
1372
1141
|
maxBuffer: 10 * 1024 * 1024
|
|
1373
1142
|
});
|
|
1374
1143
|
const xmlContent = stdout.trim();
|
|
1375
|
-
// Clean up
|
|
1376
1144
|
await execAsync(`adb ${deviceArg} shell rm ${remotePath}`, {
|
|
1377
1145
|
timeout: 5000
|
|
1378
1146
|
}).catch(() => { });
|
|
@@ -1382,7 +1150,6 @@ export async function androidDescribeAll(deviceId) {
|
|
|
1382
1150
|
error: "Failed to get UI hierarchy. Make sure the device screen is unlocked and the app is in foreground."
|
|
1383
1151
|
};
|
|
1384
1152
|
}
|
|
1385
|
-
// Parse XML
|
|
1386
1153
|
const parser = new XMLParser({
|
|
1387
1154
|
ignoreAttributes: false,
|
|
1388
1155
|
attributeNamePrefix: "@_"
|
|
@@ -1409,19 +1176,13 @@ export async function androidDescribeAll(deviceId) {
|
|
|
1409
1176
|
};
|
|
1410
1177
|
}
|
|
1411
1178
|
}
|
|
1412
|
-
/**
|
|
1413
|
-
* Get accessibility info for the UI element at specific coordinates
|
|
1414
|
-
*/
|
|
1415
1179
|
export async function androidDescribePoint(x, y, deviceId) {
|
|
1416
1180
|
try {
|
|
1417
|
-
// First get the full hierarchy
|
|
1418
1181
|
const result = await androidDescribeAll(deviceId);
|
|
1419
1182
|
if (!result.success || !result.elements) {
|
|
1420
1183
|
return result;
|
|
1421
1184
|
}
|
|
1422
|
-
// Flatten and find elements containing the point
|
|
1423
1185
|
const allElements = flattenElements(result.elements);
|
|
1424
|
-
// Find all elements whose bounds contain the point
|
|
1425
1186
|
const matchingElements = allElements.filter((el) => {
|
|
1426
1187
|
const b = el.bounds;
|
|
1427
1188
|
return x >= b.left && x <= b.right && y >= b.top && y <= b.bottom;
|
|
@@ -1432,15 +1193,12 @@ export async function androidDescribePoint(x, y, deviceId) {
|
|
|
1432
1193
|
formatted: `No element found at (${x}, ${y})`
|
|
1433
1194
|
};
|
|
1434
1195
|
}
|
|
1435
|
-
// Return the deepest (smallest) element that contains the point
|
|
1436
|
-
// Sort by area (smallest first) to get the most specific element
|
|
1437
1196
|
matchingElements.sort((a, b) => {
|
|
1438
1197
|
const areaA = a.frame.width * a.frame.height;
|
|
1439
1198
|
const areaB = b.frame.width * b.frame.height;
|
|
1440
1199
|
return areaA - areaB;
|
|
1441
1200
|
});
|
|
1442
1201
|
const element = matchingElements[0];
|
|
1443
|
-
// Format detailed output
|
|
1444
1202
|
const lines = [];
|
|
1445
1203
|
const label = element.text || element.contentDesc;
|
|
1446
1204
|
lines.push(`[${element.class}]${label ? ` "${label}"` : ""} frame=(${element.frame.x}, ${element.frame.y}, ${element.frame.width}x${element.frame.height}) tap=(${element.tap.x}, ${element.tap.y})`);
|
|
@@ -1448,14 +1206,11 @@ export async function androidDescribePoint(x, y, deviceId) {
|
|
|
1448
1206
|
lines.push(` resource-id: ${element.resourceId}`);
|
|
1449
1207
|
}
|
|
1450
1208
|
if (element.contentDesc && element.text) {
|
|
1451
|
-
// Show content-desc separately if we showed text as label
|
|
1452
1209
|
lines.push(` content-desc: ${element.contentDesc}`);
|
|
1453
1210
|
}
|
|
1454
1211
|
if (element.text && element.contentDesc) {
|
|
1455
|
-
// Show text separately if we showed content-desc as label
|
|
1456
1212
|
lines.push(` text: ${element.text}`);
|
|
1457
1213
|
}
|
|
1458
|
-
// Show state flags
|
|
1459
1214
|
const flags = [];
|
|
1460
1215
|
if (element.clickable)
|
|
1461
1216
|
flags.push("clickable");
|
|
@@ -1487,20 +1242,15 @@ export async function androidDescribePoint(x, y, deviceId) {
|
|
|
1487
1242
|
};
|
|
1488
1243
|
}
|
|
1489
1244
|
}
|
|
1490
|
-
/**
|
|
1491
|
-
* Tap an element by its text, content-description, or resource-id
|
|
1492
|
-
*/
|
|
1493
1245
|
export async function androidTapElement(options) {
|
|
1494
1246
|
try {
|
|
1495
1247
|
const { text, textContains, contentDesc, contentDescContains, resourceId, index = 0, deviceId } = options;
|
|
1496
|
-
// Validate that at least one search criterion is provided
|
|
1497
1248
|
if (!text && !textContains && !contentDesc && !contentDescContains && !resourceId) {
|
|
1498
1249
|
return {
|
|
1499
1250
|
success: false,
|
|
1500
1251
|
error: "At least one of text, textContains, contentDesc, contentDescContains, or resourceId must be provided"
|
|
1501
1252
|
};
|
|
1502
1253
|
}
|
|
1503
|
-
// Get the UI hierarchy
|
|
1504
1254
|
const result = await androidDescribeAll(deviceId);
|
|
1505
1255
|
if (!result.success || !result.elements) {
|
|
1506
1256
|
return {
|
|
@@ -1508,9 +1258,7 @@ export async function androidTapElement(options) {
|
|
|
1508
1258
|
error: result.error || "Failed to get UI hierarchy"
|
|
1509
1259
|
};
|
|
1510
1260
|
}
|
|
1511
|
-
// Flatten and search
|
|
1512
1261
|
const allElements = flattenElements(result.elements);
|
|
1513
|
-
// Filter elements based on search criteria
|
|
1514
1262
|
const matchingElements = allElements.filter((el) => {
|
|
1515
1263
|
if (text && el.text !== text)
|
|
1516
1264
|
return false;
|
|
@@ -1521,7 +1269,6 @@ export async function androidTapElement(options) {
|
|
|
1521
1269
|
if (contentDescContains && (!el.contentDesc || !el.contentDesc.toLowerCase().includes(contentDescContains.toLowerCase())))
|
|
1522
1270
|
return false;
|
|
1523
1271
|
if (resourceId) {
|
|
1524
|
-
// Support both full resource-id and short form
|
|
1525
1272
|
if (!el.resourceId)
|
|
1526
1273
|
return false;
|
|
1527
1274
|
if (el.resourceId !== resourceId && !el.resourceId.endsWith(`:id/${resourceId}`))
|
|
@@ -1554,7 +1301,6 @@ export async function androidTapElement(options) {
|
|
|
1554
1301
|
}
|
|
1555
1302
|
const element = matchingElements[index];
|
|
1556
1303
|
const label = element.text || element.contentDesc || element.resourceId || element.class;
|
|
1557
|
-
// Log if multiple matches
|
|
1558
1304
|
let resultMessage;
|
|
1559
1305
|
if (matchingElements.length > 1) {
|
|
1560
1306
|
resultMessage = `Found ${matchingElements.length} elements, tapping "${label}" (index ${index}) at (${element.tap.x}, ${element.tap.y})`;
|
|
@@ -1562,7 +1308,6 @@ export async function androidTapElement(options) {
|
|
|
1562
1308
|
else {
|
|
1563
1309
|
resultMessage = `Tapped "${label}" at (${element.tap.x}, ${element.tap.y})`;
|
|
1564
1310
|
}
|
|
1565
|
-
// Perform the tap
|
|
1566
1311
|
const tapResult = await androidTap(element.tap.x, element.tap.y, deviceId);
|
|
1567
1312
|
if (!tapResult.success) {
|
|
1568
1313
|
return tapResult;
|