agent-device 0.21.24 → 0.21.25
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/android/ime-helper/dist/agent-device-android-ime-helper-0.21.25.apk +0 -0
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.25.apk.sha256 +1 -0
- package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.24.manifest.json → agent-device-android-ime-helper-0.21.25.manifest.json} +4 -4
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.25.apk +0 -0
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.25.apk.sha256 +1 -0
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.25.manifest.json +16 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +159 -4
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerHttpRequestFraming.swift +139 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerListenerPeer.swift +41 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerListenerToken.swift +25 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Environment.swift +18 -11
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift +25 -1
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedTextEntry.swift +0 -3
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +1 -3
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryConfirmation.swift +59 -26
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextTyping.swift +47 -103
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +142 -54
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TypeExecution.swift +4 -1
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +21 -19
- package/dist/src/absence-observation-errors.js +1 -0
- package/dist/src/adapter.js +1 -1
- package/dist/src/adb-executor.js +2 -2
- package/dist/src/adb-failure.js +2 -2
- package/dist/src/agent-browser-lifecycle.js +1 -1
- package/dist/src/agent-browser-process-record.js +1 -1
- package/dist/src/agent-browser-provider.js +1 -1
- package/dist/src/agent-browser-tool.js +1 -1
- package/dist/src/agent-device-client.js +2 -2
- package/dist/src/android-adb.js +1 -1
- package/dist/src/android-input-ownership.js +1 -1
- package/dist/src/android-system-surface-disclosure.js +1 -0
- package/dist/src/android-text-entry.js +1 -0
- package/dist/src/android.js +1 -1
- package/dist/src/app-backend.js +1 -1
- package/dist/src/app-helpers.js +1 -1
- package/dist/src/app-launch.js +1 -1
- package/dist/src/app-lifecycle.js +4 -3
- package/dist/src/app-lifecycle2.js +2 -2
- package/dist/src/app-log-files.js +1 -1
- package/dist/src/app-log-reconnect.js +1 -1
- package/dist/src/app-log-runtime2.js +3 -3
- package/dist/src/app-resolution-facade.js +1 -1
- package/dist/src/app-resolution.js +2 -2
- package/dist/src/apple-runner-platform.js +1 -1
- package/dist/src/artifact-download.js +1 -1
- package/dist/src/auth.js +1 -1
- package/dist/src/aws-device-farm-connection.js +1 -1
- package/dist/src/back-mode.js +1 -1
- package/dist/src/boot-diagnostics.js +1 -1
- package/dist/src/browserstack-connection.js +1 -1
- package/dist/src/capture-disclosure.js +1 -0
- package/dist/src/cli-config.js +1 -1
- package/dist/src/cli-help.js +17 -14
- package/dist/src/cli-output.js +1 -1
- package/dist/src/cli-runner.js +3 -1
- package/dist/src/cli.js +3 -3
- package/dist/src/client-companion-tunnel.js +1 -1
- package/dist/src/client-metro.js +1 -1
- package/dist/src/client-types.d.ts +8 -2
- package/dist/src/code-signature-cache.js +1 -1
- package/dist/src/code-signature.js +1 -1
- package/dist/src/command-metadata.js +73 -1
- package/dist/src/command-policy.js +1 -0
- package/dist/src/command-schema.js +3 -3
- package/dist/src/command-surface.js +1 -1
- package/dist/src/command-tools.js +2 -2
- package/dist/src/connection-runtime.js +1 -1
- package/dist/src/connection-verification.js +1 -1
- package/dist/src/connection.js +3 -3
- package/dist/src/contracts.js +1 -1
- package/dist/src/daemon-client-lease-beat.js +1 -1
- package/dist/src/daemon-client-lifecycle.js +1 -1
- package/dist/src/daemon-client-liveness-probe.js +1 -1
- package/dist/src/daemon-policy-file.js +1 -1
- package/dist/src/daemon-registration-owner.js +1 -1
- package/dist/src/daemon-registration.js +1 -1
- package/dist/src/daemon-resolution.js +3 -1
- package/dist/src/debug-symbols-facade.js +1 -1
- package/dist/src/device-claim-inspection.js +1 -1
- package/dist/src/device-input-state.d.ts +5 -3
- package/dist/src/device-inventory-context.js +1 -1
- package/dist/src/device-readiness-runtime.js +1 -1
- package/dist/src/device-release.js +1 -1
- package/dist/src/device-shell.js +1 -1
- package/dist/src/device.js +3 -1
- package/dist/src/devicectl.js +1 -1
- package/dist/src/diagnostics.js +1 -1
- package/dist/src/doctor-facade.js +1 -1
- package/dist/src/durable-capture-resource.js +1 -1
- package/dist/src/durable-resource.js +1 -1
- package/dist/src/engine-eval-script.js +1 -1
- package/dist/src/error.js +1 -1
- package/dist/src/exec.js +1 -1
- package/dist/src/expo-dev-client-launch.js +1 -0
- package/dist/src/file.js +3 -1
- package/dist/src/find.js +1 -1
- package/dist/src/finders.js +1 -1
- package/dist/src/generic-settle.js +1 -1
- package/dist/src/generic.js +1 -1
- package/dist/src/gesture-plan.js +1 -1
- package/dist/src/gesture-runtime.js +1 -1
- package/dist/src/harmonyos.js +1 -1
- package/dist/src/hdc.js +1 -1
- package/dist/src/helper.js +2 -2
- package/dist/src/human-control.js +1 -1
- package/dist/src/ime-rebind.js +1 -1
- package/dist/src/ime-state.js +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/input-actions.js +1 -1
- package/dist/src/input-actions2.js +2 -2
- package/dist/src/install-source-network-transport.js +1 -1
- package/dist/src/install-source2.js +1 -1
- package/dist/src/interaction-error.js +1 -0
- package/dist/src/interaction-positionals.js +1 -1
- package/dist/src/interaction.js +1 -1
- package/dist/src/interaction2.js +1 -1
- package/dist/src/interactor-operation-catalog.js +1 -1
- package/dist/src/interactor.js +5 -5
- package/dist/src/interactor2.js +1 -1
- package/dist/src/internal/bin.js +1 -1
- package/dist/src/internal/daemon.js +5 -1
- package/dist/src/internal/png-worker.js +1 -1
- package/dist/src/inventory.js +2 -2
- package/dist/src/inventory6.js +1 -1
- package/dist/src/io.d.ts +2 -2
- package/dist/src/ios-client.js +1 -1
- package/dist/src/ios-settings.js +1 -1
- package/dist/src/ios-snapshot-acquisition.js +1 -1
- package/dist/src/ios-snapshot-adapter.js +1 -1
- package/dist/src/ios-snapshot-engine.js +1 -1
- package/dist/src/ios-snapshot-runtime.js +1 -1
- package/dist/src/keyed-lock.js +1 -1
- package/dist/src/launch-confirmation.js +1 -1
- package/dist/src/lease.js +1 -1
- package/dist/src/limrun-runtime-dependencies.js +1 -1
- package/dist/src/limrun.d.ts +2 -10
- package/dist/src/limrun.js +1 -1
- package/dist/src/linux.js +1 -1
- package/dist/src/load.js +1 -1
- package/dist/src/local-interactor-operation-set.js +1 -1
- package/dist/src/log-stream.js +9 -9
- package/dist/src/mechanics.js +16 -16
- package/dist/src/metro.js +1 -1
- package/dist/src/metro2.js +1 -1
- package/dist/src/mobile-snapshot-semantics.js +1 -1
- package/dist/src/observability.js +1 -1
- package/dist/src/open-target-resolution.js +1 -1
- package/dist/src/output-path.js +1 -1
- package/dist/src/overlay.js +1 -3
- package/dist/src/perf-facade.js +1 -1
- package/dist/src/perf-runtime-operation-builder.js +1 -1
- package/dist/src/perf-runtime.js +1 -1
- package/dist/src/physical-device-control.js +1 -1
- package/dist/src/platform-request-scope.js +1 -1
- package/dist/src/platform-runtime-android-adb-host.js +1 -1
- package/dist/src/platform-runtime-app-log-android-transport.js +1 -1
- package/dist/src/platform-runtime-apple-resources.js +1 -0
- package/dist/src/platform-runtime-claim-recovery.js +1 -0
- package/dist/src/platform-runtime-daemon-services.js +1 -0
- package/dist/src/platform-runtime-host.js +1 -1
- package/dist/src/platform-runtime-network-web-transport.js +1 -1
- package/dist/src/platform-runtime-open-target.js +1 -1
- package/dist/src/platform-runtime-operation-host.js +4 -4
- package/dist/src/platform-runtime-runtime-hints.js +2 -2
- package/dist/src/platform-runtime-screen-recording-android-host.js +1 -1
- package/dist/src/platform-runtime-screen-recording-apple-runner-transport.js +1 -1
- package/dist/src/platform-runtime-screen-recording-apple-simulator-host.js +1 -1
- package/dist/src/platform-runtime-screen-recording-harmony-host.js +1 -1
- package/dist/src/platform-runtime-screen-recording-web-host.js +1 -1
- package/dist/src/platform-runtime-unavailable.js +1 -1
- package/dist/src/platform-runtime.js +1 -1
- package/dist/src/platform-runtime2.js +1 -1
- package/dist/src/plugin.js +1 -1
- package/dist/src/png-crop-bytes.js +1 -1
- package/dist/src/png-resize.js +1 -0
- package/dist/src/png-worker-client.js +1 -1
- package/dist/src/png-worker-contract.js +1 -1
- package/dist/src/process-entry.js +1 -1
- package/dist/src/process.js +1 -1
- package/dist/src/provider-credential-fingerprint.js +1 -1
- package/dist/src/provider-device-admission.js +1 -1
- package/dist/src/provider-policy.js +1 -1
- package/dist/src/provider-webdriver.js +1 -1
- package/dist/src/proxy.js +2 -2
- package/dist/src/quality-warnings.js +1 -1
- package/dist/src/react-native-overlay2.js +3 -0
- package/dist/src/react-native.js +1 -1
- package/dist/src/record-runtime.js +1 -1
- package/dist/src/record-trace.js +1 -1
- package/dist/src/recording.js +2 -2
- package/dist/src/register-builtins.js +1 -1
- package/dist/src/remote-config-core.js +1 -1
- package/dist/src/replay-backend-registry.js +1 -0
- package/dist/src/replay-divergence.js +1 -1
- package/dist/src/replay-maestro-backend.js +117 -0
- package/dist/src/replay-script-selection.js +1 -0
- package/dist/src/replay.js +1 -3
- package/dist/src/replay2.js +1 -0
- package/dist/src/reporting.js +3 -3
- package/dist/src/request-cancel.js +1 -1
- package/dist/src/request-providers.js +1 -1
- package/dist/src/request-runtime-binding.js +1 -1
- package/dist/src/result-serialization.js +1 -1
- package/dist/src/retry.js +1 -1
- package/dist/src/runner-cache-retention.js +1 -1
- package/dist/src/runner-cache-trim.js +1 -1
- package/dist/src/runner-client.js +1 -1
- package/dist/src/runner-dev-tools-security.js +1 -1
- package/dist/src/runner-device-readiness.js +1 -1
- package/dist/src/runner-device-set.js +1 -1
- package/dist/src/runner-error-classification.js +3 -3
- package/dist/src/runner-exchange.js +1 -1
- package/dist/src/runner-lease.js +1 -1
- package/dist/src/runner-operations-facade.js +1 -1
- package/dist/src/runner-retention.js +1 -0
- package/dist/src/runner-start-budget.js +1 -1
- package/dist/src/runner-startup-transport.js +1 -0
- package/dist/src/runner-toolchain-probe.js +1 -1
- package/dist/src/runner.js +2 -1
- package/dist/src/runtime-command-surface.js +1 -0
- package/dist/src/runtime-deployment.js +1 -1
- package/dist/src/runtime-factory.js +1 -0
- package/dist/src/runtime-session.js +1 -0
- package/dist/src/runtime-transport-hints.js +1 -1
- package/dist/src/runtime.js +2 -1
- package/dist/src/runtime10.js +2 -1
- package/dist/src/runtime2.js +5 -2
- package/dist/src/runtime3.js +1 -5
- package/dist/src/runtime4.js +5 -1
- package/dist/src/runtime5.js +1 -5
- package/dist/src/runtime6.js +1 -1
- package/dist/src/runtime7.js +1 -1
- package/dist/src/runtime8.js +1 -1
- package/dist/src/runtime9.js +1 -1
- package/dist/src/screenshot-image.js +1 -1
- package/dist/src/screenshot-runtime.js +1 -0
- package/dist/src/screenshot3.js +5 -5
- package/dist/src/sdk-batch-runner.js +2 -2
- package/dist/src/sdk-batch.js +1 -1
- package/dist/src/sdk-contracts.js +2 -2
- package/dist/src/sdk-finders.js +1 -1
- package/dist/src/sdk-io.d.ts +4 -2
- package/dist/src/sdk-selectors.d.ts +4 -5
- package/dist/src/sdk-selectors.js +1 -1
- package/dist/src/selection.js +0 -0
- package/dist/src/selector-pipeline-policy.js +1 -1
- package/dist/src/selector-runtime.js +1 -1
- package/dist/src/selectors.js +1 -1
- package/dist/src/server.js +2 -2
- package/dist/src/session-allocation.js +1 -1
- package/dist/src/session-replay-runtime-failure-response.js +1 -0
- package/dist/src/session-runtime-admission.js +1 -0
- package/dist/src/session.js +1 -1
- package/dist/src/session2.js +5 -5
- package/dist/src/settings.js +1 -1
- package/dist/src/settings2.js +1 -1
- package/dist/src/simctl-settings.js +1 -1
- package/dist/src/simulator.js +1 -1
- package/dist/src/snapshot-chrome.js +1 -0
- package/dist/src/snapshot-desktop-projection.js +1 -1
- package/dist/src/snapshot-evidence.js +1 -0
- package/dist/src/snapshot-node-lookup.js +1 -0
- package/dist/src/snapshot-occlusion.js +1 -0
- package/dist/src/snapshot-quality-verdict.js +1 -0
- package/dist/src/snapshot-runtime-binding.js +1 -1
- package/dist/src/snapshot-runtime.js +1 -1
- package/dist/src/snapshot-state.js +1 -1
- package/dist/src/snapshot.js +1 -1
- package/dist/src/snapshot2.js +1 -1
- package/dist/src/snapshot3.js +1 -1
- package/dist/src/src.js +5 -1
- package/dist/src/src2.js +1 -1
- package/dist/src/src3.js +2 -1
- package/dist/src/store.js +1 -1
- package/dist/src/surface-snapshot2.js +1 -1
- package/dist/src/target-evidence-tree.js +1 -0
- package/dist/src/target-evidence.js +1 -1
- package/dist/src/text-entry-focus.js +1 -1
- package/dist/src/tool-provider2.js +1 -1
- package/dist/src/touch-runtime.js +1 -1
- package/dist/src/touch-runtime2.js +1 -1
- package/dist/src/tree.js +1 -1
- package/dist/src/tv-remote.js +1 -1
- package/dist/src/type-text-runtime.js +1 -1
- package/dist/src/undici.js +8 -8
- package/dist/src/update-check.js +1 -1
- package/dist/src/vars.js +2 -0
- package/dist/src/version.js +1 -1
- package/dist/src/video.js +2 -2
- package/dist/src/wait.js +1 -0
- package/dist/src/web.js +1 -1
- package/dist/src/web2.js +1 -1
- package/package.json +9 -11
- package/server.json +2 -2
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.24.apk +0 -0
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.24.apk.sha256 +0 -1
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.24.apk +0 -0
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.24.apk.sha256 +0 -1
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.24.manifest.json +0 -16
- package/dist/src/adb-transport.js +0 -1
- package/dist/src/alert-runtime.js +0 -1
- package/dist/src/app-deployment-resolution.js +0 -2
- package/dist/src/app-inventory-contract.js +0 -1
- package/dist/src/app-log-session-resource.js +0 -1
- package/dist/src/application-lifecycle-runtime.js +0 -1
- package/dist/src/auth-session.js +0 -1
- package/dist/src/browserstack-device-features.js +0 -1
- package/dist/src/browserstack.js +0 -1
- package/dist/src/cache.js +0 -1
- package/dist/src/capture.js +0 -1
- package/dist/src/catalog.js +0 -1
- package/dist/src/click-button.js +0 -1
- package/dist/src/client-metro-companion.js +0 -1
- package/dist/src/client-options.js +0 -3
- package/dist/src/daemon-client-transport.js +0 -1
- package/dist/src/daemon-http.js +0 -1
- package/dist/src/device-rotation.js +0 -1
- package/dist/src/device-session.js +0 -3
- package/dist/src/device2.js +0 -1
- package/dist/src/device3.js +0 -3
- package/dist/src/dispatch-resolve.js +0 -4
- package/dist/src/format.js +0 -2
- package/dist/src/gesture-plan-types.js +0 -1
- package/dist/src/host.js +0 -1
- package/dist/src/ime-settings-record.js +0 -1
- package/dist/src/interaction-ref-policy.js +0 -1
- package/dist/src/interactor-operation-binding.js +0 -1
- package/dist/src/keyboard-actions.js +0 -1
- package/dist/src/lease-context.js +0 -1
- package/dist/src/lease-registry-scope.js +0 -1
- package/dist/src/lease-scope.js +0 -1
- package/dist/src/managed-device-scope.js +0 -1
- package/dist/src/manifest.js +0 -1
- package/dist/src/observe-until.js +0 -1
- package/dist/src/output-common.js +0 -5
- package/dist/src/owner-identity.js +0 -3
- package/dist/src/path-resolution.js +0 -1
- package/dist/src/perf-runtime-plan.js +0 -1
- package/dist/src/platform-runtime-app-log-process.js +0 -1
- package/dist/src/platform-runtime-managed-owner.js +0 -1
- package/dist/src/platform-runtime-screen-recording-process-host.js +0 -1
- package/dist/src/png.js +0 -1
- package/dist/src/provider-session-ownership.js +0 -2
- package/dist/src/providers.js +0 -1
- package/dist/src/registry.js +0 -68
- package/dist/src/remote-connection-state.js +0 -1
- package/dist/src/request-admission.js +0 -1
- package/dist/src/request-cancellation.js +0 -1
- package/dist/src/request-progress-protocol.js +0 -1
- package/dist/src/runner-cache-metadata.js +0 -2
- package/dist/src/runner-disposal.js +0 -1
- package/dist/src/runner-session-types.js +0 -1
- package/dist/src/runtime-admission.js +0 -1
- package/dist/src/runtime11.js +0 -2
- package/dist/src/screen-recording-resource-recovery.js +0 -1
- package/dist/src/screenshot-result.js +0 -1
- package/dist/src/script.js +0 -1
- package/dist/src/scroll-command.js +0 -1
- package/dist/src/scroll-gesture.js +0 -1
- package/dist/src/scroll-indicator.js +0 -1
- package/dist/src/session-artifact-paths.js +0 -1
- package/dist/src/session-paths.js +0 -1
- package/dist/src/session-repair-tombstone.js +0 -1
- package/dist/src/session-snapshot.js +0 -1
- package/dist/src/snapshot-private-evidence.js +0 -1
- package/dist/src/snapshot4.js +0 -1
- package/dist/src/src4.js +0 -1
- package/dist/src/src5.js +0 -1
- package/dist/src/src6.js +0 -1
- package/dist/src/src7.js +0 -2
- package/dist/src/src8.js +0 -115
- package/dist/src/string-enum.js +0 -1
- package/dist/src/success-text.js +0 -1
- package/dist/src/transport.js +0 -3
- package/dist/src/ttl-memo.js +0 -1
- package/dist/src/verified-file.js +0 -1
- package/dist/src/window-state.js +0 -1
- /package/dist/src/{doctor-output.js → doctor-lines.js} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2b1c6a028b28e426c4bed80449b6dd1eff69985f1741a58d033e30f8b9fa5bf1 agent-device-android-ime-helper-0.21.25.apk
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "android-ime-helper",
|
|
3
|
-
"version": "0.21.
|
|
4
|
-
"assetName": "agent-device-android-ime-helper-0.21.
|
|
5
|
-
"sha256": "
|
|
3
|
+
"version": "0.21.25",
|
|
4
|
+
"assetName": "agent-device-android-ime-helper-0.21.25.apk",
|
|
5
|
+
"sha256": "2b1c6a028b28e426c4bed80449b6dd1eff69985f1741a58d033e30f8b9fa5bf1",
|
|
6
6
|
"packageName": "com.callstack.agentdevice.imehelper",
|
|
7
|
-
"versionCode":
|
|
7
|
+
"versionCode": 21025,
|
|
8
8
|
"serviceComponent": "com.callstack.agentdevice.imehelper/.TestInputMethodService",
|
|
9
9
|
"broadcastProtocol": "android-ime-helper-v1"
|
|
10
10
|
}
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
79175080eb71f444224c844a599e75e839d6456790eb96d175fc887f85237a12 agent-device-android-snapshot-helper-0.21.25.apk
|
package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.25.manifest.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "android-snapshot-helper",
|
|
3
|
+
"version": "0.21.25",
|
|
4
|
+
"releaseTag": "v0.21.25",
|
|
5
|
+
"assetName": "agent-device-android-snapshot-helper-0.21.25.apk",
|
|
6
|
+
"apkUrl": "https://github.com/callstack/agent-device/releases/download/v0.21.25/agent-device-android-snapshot-helper-0.21.25.apk",
|
|
7
|
+
"sha256": "79175080eb71f444224c844a599e75e839d6456790eb96d175fc887f85237a12",
|
|
8
|
+
"checksumName": "agent-device-android-snapshot-helper-0.21.25.apk.sha256",
|
|
9
|
+
"packageName": "com.callstack.agentdevice.snapshothelper",
|
|
10
|
+
"versionCode": 21025,
|
|
11
|
+
"instrumentationRunner": "com.callstack.agentdevice.snapshothelper/.SnapshotInstrumentation",
|
|
12
|
+
"minSdk": 23,
|
|
13
|
+
"targetSdk": 36,
|
|
14
|
+
"outputFormat": "uiautomator-xml",
|
|
15
|
+
"statusProtocol": "android-snapshot-helper-v1"
|
|
16
|
+
}
|
|
@@ -71,6 +71,7 @@ int main(int argc, const char *argv[]) {
|
|
|
71
71
|
// took focus from an input that has stopped answering the accessibility query that found it.
|
|
72
72
|
@property(nonatomic, strong) UILabel *textEntryFocusWitness;
|
|
73
73
|
@property(nonatomic, assign) NSUInteger textEntryWriteBacks;
|
|
74
|
+
@property(nonatomic, assign) NSUInteger textEntryTotalEdits;
|
|
74
75
|
@property(nonatomic, copy, nullable) NSString *textEntryRenderedValue;
|
|
75
76
|
@property(nonatomic, assign) NSTimeInterval textEntryLastEditTime;
|
|
76
77
|
@property(nonatomic, assign) NSTimeInterval textEntryBurstStartTime;
|
|
@@ -97,6 +98,122 @@ int main(int argc, const char *argv[]) {
|
|
|
97
98
|
return [NSString stringWithFormat:@"%lu of 6 digits", (unsigned long)self.text.length];
|
|
98
99
|
}
|
|
99
100
|
@end
|
|
101
|
+
|
|
102
|
+
// A field that reformats its own text on every edit into `## ### ## ##` digit groups, the way a
|
|
103
|
+
// digit-grouping formatter in a Flutter or React Native input does. The accessibility value is
|
|
104
|
+
// what the user sees — the formatted string — so it never equals the digits a `fill` sent (#2634).
|
|
105
|
+
// The field owns the delegate it installs, keeping the formatter out of the view controller's
|
|
106
|
+
// own delegate callbacks.
|
|
107
|
+
@interface AgentDeviceDigitGroupingTextField : UITextField <UITextFieldDelegate>
|
|
108
|
+
@end
|
|
109
|
+
|
|
110
|
+
@interface AgentDeviceCurrencyTextField : UITextField <UITextFieldDelegate>
|
|
111
|
+
@property(nonatomic, strong) UILabel *entryStatus;
|
|
112
|
+
@property(nonatomic, assign) NSUInteger insertedDigits;
|
|
113
|
+
@end
|
|
114
|
+
|
|
115
|
+
@interface AgentDeviceSubmitClearingTextField : UITextField <UITextFieldDelegate>
|
|
116
|
+
@property(nonatomic, strong) UILabel *entryStatus;
|
|
117
|
+
@property(nonatomic, assign) NSUInteger submits;
|
|
118
|
+
@end
|
|
119
|
+
|
|
120
|
+
static NSString *AgentDeviceFormatGroupedDigits(NSString *raw) {
|
|
121
|
+
NSMutableString *digits = [NSMutableString string];
|
|
122
|
+
NSCharacterSet *nonDigits = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
|
|
123
|
+
for (NSString *chunk in [raw componentsSeparatedByCharactersInSet:nonDigits]) {
|
|
124
|
+
[digits appendString:chunk];
|
|
125
|
+
}
|
|
126
|
+
NSMutableArray<NSNumber *> *groupWidths = [NSMutableArray arrayWithArray:@[@2, @3, @2, @2]];
|
|
127
|
+
NSMutableString *formatted = [NSMutableString string];
|
|
128
|
+
NSUInteger cursor = 0;
|
|
129
|
+
for (NSNumber *width in groupWidths) {
|
|
130
|
+
if (cursor >= digits.length) {
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
if (formatted.length > 0) {
|
|
134
|
+
[formatted appendString:@" "];
|
|
135
|
+
}
|
|
136
|
+
NSUInteger end = MIN(cursor + width.unsignedIntegerValue, digits.length);
|
|
137
|
+
[formatted appendString:[digits substringWithRange:NSMakeRange(cursor, end - cursor)]];
|
|
138
|
+
cursor = end;
|
|
139
|
+
}
|
|
140
|
+
// Digits past the last group keep grouping by threes rather than vanishing from the field.
|
|
141
|
+
while (cursor < digits.length) {
|
|
142
|
+
NSUInteger end = MIN(cursor + 3, digits.length);
|
|
143
|
+
[formatted appendString:[NSString stringWithFormat:@" %@", [digits substringWithRange:NSMakeRange(cursor, end - cursor)]]];
|
|
144
|
+
cursor = end;
|
|
145
|
+
}
|
|
146
|
+
return formatted;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
static NSUInteger AgentDeviceDigitCount(NSString *text, NSUInteger limit) {
|
|
150
|
+
NSUInteger count = 0;
|
|
151
|
+
for (NSUInteger index = 0; index < limit && index < text.length; index++) {
|
|
152
|
+
if ([NSCharacterSet.decimalDigitCharacterSet characterIsMember:[text characterAtIndex:index]]) {
|
|
153
|
+
count += 1;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return count;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
@implementation AgentDeviceDigitGroupingTextField
|
|
160
|
+
- (BOOL)textField:(UITextField *)textField
|
|
161
|
+
shouldChangeCharactersInRange:(NSRange)range
|
|
162
|
+
replacementString:(NSString *)string {
|
|
163
|
+
NSString *combined = [textField.text stringByReplacingCharactersInRange:range withString:string];
|
|
164
|
+
NSString *formatted = AgentDeviceFormatGroupedDigits(combined);
|
|
165
|
+
textField.text = formatted;
|
|
166
|
+
// Assigning `text` drops the caret at the end, which a real grouping formatter does not do:
|
|
167
|
+
// a mid-string edit must keep editing where the user put the caret. The digits before the
|
|
168
|
+
// caret survive the reformat unchanged, so the caret belongs before the digit that follows
|
|
169
|
+
// them — past the separators the grouping inserted behind them — and at the end when it
|
|
170
|
+
// followed all of them. Typed digits count as before the caret; a delete does not move it.
|
|
171
|
+
NSUInteger digitsBeforeCaret =
|
|
172
|
+
AgentDeviceDigitCount(textField.text, range.location) + AgentDeviceDigitCount(string, string.length);
|
|
173
|
+
NSUInteger caretOffset = formatted.length;
|
|
174
|
+
NSUInteger seen = 0;
|
|
175
|
+
for (NSUInteger index = 0; index < formatted.length; index++) {
|
|
176
|
+
if (![NSCharacterSet.decimalDigitCharacterSet characterIsMember:[formatted characterAtIndex:index]]) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
seen += 1;
|
|
180
|
+
if (seen == digitsBeforeCaret + 1) {
|
|
181
|
+
caretOffset = index;
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
textField.selectedTextRange = [textField textRangeFromPosition:[textField positionFromPosition:textField.beginningOfDocument offset:caretOffset]
|
|
186
|
+
toPosition:[textField positionFromPosition:textField.beginningOfDocument offset:caretOffset]];
|
|
187
|
+
return NO;
|
|
188
|
+
}
|
|
189
|
+
@end
|
|
190
|
+
|
|
191
|
+
@implementation AgentDeviceCurrencyTextField
|
|
192
|
+
- (BOOL)textField:(UITextField *)textField
|
|
193
|
+
shouldChangeCharactersInRange:(NSRange)range
|
|
194
|
+
replacementString:(NSString *)string {
|
|
195
|
+
NSString *combined = [textField.text stringByReplacingCharactersInRange:range withString:string];
|
|
196
|
+
NSMutableString *digits = [NSMutableString string];
|
|
197
|
+
for (NSString *part in [combined componentsSeparatedByCharactersInSet:NSCharacterSet.decimalDigitCharacterSet.invertedSet]) {
|
|
198
|
+
[digits appendString:part];
|
|
199
|
+
}
|
|
200
|
+
unsigned long long cents = digits.longLongValue;
|
|
201
|
+
textField.text = [NSString stringWithFormat:@"$%llu.%02llu", cents / 100, cents % 100];
|
|
202
|
+
self.insertedDigits += AgentDeviceDigitCount(string, string.length);
|
|
203
|
+
self.entryStatus.text = [NSString stringWithFormat:@"%lu", (unsigned long)self.insertedDigits];
|
|
204
|
+
textField.selectedTextRange = [textField textRangeFromPosition:textField.endOfDocument toPosition:textField.endOfDocument];
|
|
205
|
+
return NO;
|
|
206
|
+
}
|
|
207
|
+
@end
|
|
208
|
+
|
|
209
|
+
@implementation AgentDeviceSubmitClearingTextField
|
|
210
|
+
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
|
|
211
|
+
self.submits += 1;
|
|
212
|
+
self.entryStatus.text = [NSString stringWithFormat:@"%lu", (unsigned long)self.submits];
|
|
213
|
+
textField.text = @"";
|
|
214
|
+
return YES;
|
|
215
|
+
}
|
|
216
|
+
@end
|
|
100
217
|
#endif
|
|
101
218
|
|
|
102
219
|
@implementation AgentDeviceRunnerViewController
|
|
@@ -202,11 +319,12 @@ static NSTimeInterval AgentDeviceAlertActivationBusyWindow(void) {
|
|
|
202
319
|
- (void)updateTextEntryWriteBackStatus {
|
|
203
320
|
NSTimeInterval burstSpan = self.textEntryLastEditTime - self.textEntryBurstStartTime;
|
|
204
321
|
self.textEntryWriteBackStatus.text = [NSString
|
|
205
|
-
stringWithFormat:@"write-backs=%lu burst-edits=%lu burst-ms=%lu min-gap-ms=%lu",
|
|
322
|
+
stringWithFormat:@"write-backs=%lu burst-edits=%lu burst-ms=%lu min-gap-ms=%lu total-edits=%lu",
|
|
206
323
|
(unsigned long)self.textEntryWriteBacks,
|
|
207
324
|
(unsigned long)self.textEntryBurstEdits,
|
|
208
325
|
(unsigned long)llround(burstSpan * 1000),
|
|
209
|
-
(unsigned long)llround(self.textEntryBurstMinGap * 1000)
|
|
326
|
+
(unsigned long)llround(self.textEntryBurstMinGap * 1000),
|
|
327
|
+
(unsigned long)self.textEntryTotalEdits];
|
|
210
328
|
}
|
|
211
329
|
|
|
212
330
|
- (void)presentAlertFixtureReplacement:(BOOL)replacement {
|
|
@@ -292,6 +410,7 @@ static const CGFloat AgentDeviceTextEntryNeighbourGap = 16;
|
|
|
292
410
|
// typed faster than the app renders loses the characters that arrived while a render was in
|
|
293
411
|
// flight, and the field settles stable short of the request.
|
|
294
412
|
if ([NSProcessInfo.processInfo.arguments containsObject:@"--agent-device-text-entry-app-owned-value"]) {
|
|
413
|
+
self.textEntryTotalEdits += 1;
|
|
295
414
|
NSTimeInterval now = NSProcessInfo.processInfo.systemUptime;
|
|
296
415
|
NSTimeInterval gap = now - self.textEntryLastEditTime;
|
|
297
416
|
BOOL overtookARender = self.textEntryRenderedValue != nil && gap < self.textEntryAcknowledgeWindowSeconds;
|
|
@@ -408,8 +527,28 @@ static const CGFloat AgentDeviceTextEntryNeighbourGap = 16;
|
|
|
408
527
|
if ([NSProcessInfo.processInfo.arguments containsObject:@"--agent-device-text-entry-regression"]) {
|
|
409
528
|
BOOL digitCountValue =
|
|
410
529
|
[NSProcessInfo.processInfo.arguments containsObject:@"--agent-device-text-entry-digit-count-value"];
|
|
411
|
-
|
|
412
|
-
|
|
530
|
+
BOOL digitGroupingValue =
|
|
531
|
+
[NSProcessInfo.processInfo.arguments containsObject:@"--agent-device-text-entry-digit-grouping-value"];
|
|
532
|
+
BOOL currencyValue =
|
|
533
|
+
[NSProcessInfo.processInfo.arguments containsObject:@"--agent-device-text-entry-currency-value"];
|
|
534
|
+
BOOL submitClears =
|
|
535
|
+
[NSProcessInfo.processInfo.arguments containsObject:@"--agent-device-text-entry-submit-clears"];
|
|
536
|
+
UITextField *textField;
|
|
537
|
+
if (digitCountValue) {
|
|
538
|
+
textField = [[AgentDeviceDigitCountTextField alloc] init];
|
|
539
|
+
} else if (digitGroupingValue) {
|
|
540
|
+
textField = [[AgentDeviceDigitGroupingTextField alloc] init];
|
|
541
|
+
} else if (currencyValue) {
|
|
542
|
+
textField = [[AgentDeviceCurrencyTextField alloc] init];
|
|
543
|
+
textField.text = @"$0.00";
|
|
544
|
+
} else if (submitClears) {
|
|
545
|
+
textField = [[AgentDeviceSubmitClearingTextField alloc] init];
|
|
546
|
+
} else {
|
|
547
|
+
textField = [[UITextField alloc] init];
|
|
548
|
+
}
|
|
549
|
+
if (digitGroupingValue || currencyValue || submitClears) {
|
|
550
|
+
textField.delegate = (id<UITextFieldDelegate>)textField;
|
|
551
|
+
}
|
|
413
552
|
if (![NSProcessInfo.processInfo.arguments containsObject:@"--agent-device-text-entry-unnamed-input"]) {
|
|
414
553
|
textField.accessibilityIdentifier = @"agent-device-hardware-keyboard-input";
|
|
415
554
|
}
|
|
@@ -434,6 +573,22 @@ static const CGFloat AgentDeviceTextEntryNeighbourGap = 16;
|
|
|
434
573
|
[textField.widthAnchor constraintEqualToConstant:240],
|
|
435
574
|
[textField.heightAnchor constraintEqualToConstant:AgentDeviceTextEntryFieldHeight],
|
|
436
575
|
]];
|
|
576
|
+
if (currencyValue || submitClears) {
|
|
577
|
+
UILabel *entryStatus = [[UILabel alloc] init];
|
|
578
|
+
entryStatus.accessibilityIdentifier = @"agent-device-text-entry-events";
|
|
579
|
+
entryStatus.text = @"0";
|
|
580
|
+
entryStatus.translatesAutoresizingMaskIntoConstraints = NO;
|
|
581
|
+
[self.view addSubview:entryStatus];
|
|
582
|
+
[NSLayoutConstraint activateConstraints:@[
|
|
583
|
+
[entryStatus.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
|
|
584
|
+
[entryStatus.topAnchor constraintEqualToAnchor:textField.bottomAnchor constant:12],
|
|
585
|
+
]];
|
|
586
|
+
if (currencyValue) {
|
|
587
|
+
((AgentDeviceCurrencyTextField *)textField).entryStatus = entryStatus;
|
|
588
|
+
} else {
|
|
589
|
+
((AgentDeviceSubmitClearingTextField *)textField).entryStatus = entryStatus;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
437
592
|
if ([NSProcessInfo.processInfo.arguments containsObject:@"--agent-device-text-entry-unqueryable-on-focus"]) {
|
|
438
593
|
self.textEntryFocusWitness = [[UILabel alloc] init];
|
|
439
594
|
self.textEntryFocusWitness.accessibilityIdentifier = @"agent-device-text-entry-focus";
|
package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerHttpRequestFraming.swift
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
enum RunnerHttpRequestRejection: Equatable {
|
|
6
|
+
case malformed(String)
|
|
7
|
+
case headTooLarge
|
|
8
|
+
case unauthorized
|
|
9
|
+
case transferEncodingUnsupported
|
|
10
|
+
case lengthRequired
|
|
11
|
+
case bodyTooLarge
|
|
12
|
+
case methodNotAllowed
|
|
13
|
+
case notFound
|
|
14
|
+
case timedOut
|
|
15
|
+
|
|
16
|
+
var status: Int {
|
|
17
|
+
switch self {
|
|
18
|
+
case .malformed, .transferEncodingUnsupported: return 400
|
|
19
|
+
case .unauthorized: return 401
|
|
20
|
+
case .notFound: return 404
|
|
21
|
+
case .methodNotAllowed: return 405
|
|
22
|
+
case .timedOut: return 408
|
|
23
|
+
case .lengthRequired: return 411
|
|
24
|
+
case .bodyTooLarge: return 413
|
|
25
|
+
case .headTooLarge: return 431
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
var code: String {
|
|
30
|
+
self == .unauthorized ? "UNAUTHORIZED" : "INVALID_ARGS"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
var message: String {
|
|
34
|
+
switch self {
|
|
35
|
+
case .malformed(let part): return "runner request is malformed: \(part)"
|
|
36
|
+
case .headTooLarge:
|
|
37
|
+
return "runner request head exceeds \(RunnerHttpRequestFraming.maxHeadBytes) bytes"
|
|
38
|
+
case .unauthorized: return "runner request is missing a valid listener token"
|
|
39
|
+
case .transferEncodingUnsupported: return "runner requests must not use Transfer-Encoding"
|
|
40
|
+
case .lengthRequired: return "runner requests must declare one Content-Length"
|
|
41
|
+
case .bodyTooLarge:
|
|
42
|
+
return "runner request body exceeds \(RunnerHttpRequestFraming.maxBodyBytes) bytes"
|
|
43
|
+
case .methodNotAllowed: return "runner accepts only POST"
|
|
44
|
+
case .notFound: return "runner serves only \(RunnerHttpRequestFraming.commandPath)"
|
|
45
|
+
case .timedOut: return "runner request did not arrive before its deadline"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
struct RunnerHttpAdmittedHead: Equatable {
|
|
52
|
+
let bodyOffset: Int
|
|
53
|
+
let contentLength: Int
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
enum RunnerHttpHeadVerdict: Equatable {
|
|
57
|
+
case incomplete
|
|
58
|
+
case admitted(RunnerHttpAdmittedHead)
|
|
59
|
+
case rejected(RunnerHttpRequestRejection)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
enum RunnerHttpRequestFraming {
|
|
65
|
+
static let commandPath = "/command"
|
|
66
|
+
static let maxHeadBytes = 16 * 1024
|
|
67
|
+
static let maxBodyBytes = 2 * 1024 * 1024
|
|
68
|
+
private static let headTerminator = Data("\r\n\r\n".utf8)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
static func admitHead(
|
|
73
|
+
_ buffer: Data,
|
|
74
|
+
token: RunnerListenerToken,
|
|
75
|
+
alreadyScanned: Int = 0
|
|
76
|
+
) -> RunnerHttpHeadVerdict {
|
|
77
|
+
let searchStart = buffer.startIndex + max(0, alreadyScanned - (headTerminator.count - 1))
|
|
78
|
+
guard let terminator = buffer.range(of: headTerminator, in: searchStart..<buffer.endIndex) else {
|
|
79
|
+
return buffer.count > maxHeadBytes ? .rejected(.headTooLarge) : .incomplete
|
|
80
|
+
}
|
|
81
|
+
if terminator.lowerBound - buffer.startIndex > maxHeadBytes { return .rejected(.headTooLarge) }
|
|
82
|
+
guard
|
|
83
|
+
let head = String(data: buffer[buffer.startIndex..<terminator.lowerBound], encoding: .utf8)
|
|
84
|
+
else {
|
|
85
|
+
return .rejected(.malformed("head is not UTF-8"))
|
|
86
|
+
}
|
|
87
|
+
var lines = head.components(separatedBy: "\r\n")
|
|
88
|
+
let requestLine = lines.removeFirst().split(separator: " ", omittingEmptySubsequences: false)
|
|
89
|
+
guard requestLine.count == 3, requestLine[2] == "HTTP/1.1" || requestLine[2] == "HTTP/1.0" else {
|
|
90
|
+
return .rejected(.malformed("request line"))
|
|
91
|
+
}
|
|
92
|
+
var headers: [String: [String]] = [:]
|
|
93
|
+
for line in lines {
|
|
94
|
+
guard let colon = line.firstIndex(of: ":") else {
|
|
95
|
+
return .rejected(.malformed("header line"))
|
|
96
|
+
}
|
|
97
|
+
let name = line[line.startIndex..<colon]
|
|
98
|
+
guard !name.isEmpty, name.allSatisfy(isHeaderNameCharacter) else {
|
|
99
|
+
return .rejected(.malformed("header name"))
|
|
100
|
+
}
|
|
101
|
+
let value = line[line.index(after: colon)...].trimmingCharacters(in: CharacterSet(charactersIn: " \t"))
|
|
102
|
+
headers[name.lowercased(), default: []].append(value)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
guard let presented = headers[RunnerListenerToken.requestHeaderName], presented.count == 1,
|
|
106
|
+
token.matches(presented[0])
|
|
107
|
+
else {
|
|
108
|
+
return .rejected(.unauthorized)
|
|
109
|
+
}
|
|
110
|
+
guard requestLine[0] == "POST" else { return .rejected(.methodNotAllowed) }
|
|
111
|
+
guard requestLine[1] == Substring(commandPath) else { return .rejected(.notFound) }
|
|
112
|
+
if headers["transfer-encoding"] != nil { return .rejected(.transferEncodingUnsupported) }
|
|
113
|
+
guard let lengths = headers["content-length"], lengths.count == 1 else {
|
|
114
|
+
return .rejected(.lengthRequired)
|
|
115
|
+
}
|
|
116
|
+
let declared = lengths[0]
|
|
117
|
+
guard !declared.isEmpty, declared.utf8.allSatisfy({ $0 >= 0x30 && $0 <= 0x39 }) else {
|
|
118
|
+
return .rejected(.malformed("Content-Length"))
|
|
119
|
+
}
|
|
120
|
+
guard declared.utf8.count <= 10, let contentLength = Int(declared), contentLength <= maxBodyBytes
|
|
121
|
+
else {
|
|
122
|
+
return .rejected(.bodyTooLarge)
|
|
123
|
+
}
|
|
124
|
+
return .admitted(
|
|
125
|
+
RunnerHttpAdmittedHead(
|
|
126
|
+
bodyOffset: terminator.upperBound - buffer.startIndex,
|
|
127
|
+
contentLength: contentLength
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private static func isHeaderNameCharacter(_ character: Character) -> Bool {
|
|
133
|
+
guard let ascii = character.asciiValue else { return false }
|
|
134
|
+
switch ascii {
|
|
135
|
+
case 0x30...0x39, 0x41...0x5A, 0x61...0x7A: return true
|
|
136
|
+
default: return "!#$%&'*+-.^_`|~".utf8.contains(ascii)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import Network
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
enum RunnerListenerPeer: Equatable {
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
case loopback
|
|
12
|
+
case tunnel
|
|
13
|
+
case directNetwork
|
|
14
|
+
|
|
15
|
+
static func of(_ connection: NWConnection) -> RunnerListenerPeer {
|
|
16
|
+
classify(
|
|
17
|
+
isLoopbackAddress: isLoopback(connection.endpoint),
|
|
18
|
+
interfaceTypes: connection.currentPath?.availableInterfaces.map(\.type) ?? []
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static func classify(
|
|
23
|
+
isLoopbackAddress: Bool,
|
|
24
|
+
interfaceTypes: [NWInterface.InterfaceType]
|
|
25
|
+
) -> RunnerListenerPeer {
|
|
26
|
+
if isLoopbackAddress { return .loopback }
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
if !interfaceTypes.isEmpty && interfaceTypes.allSatisfy({ $0 == .other }) { return .tunnel }
|
|
30
|
+
return .directNetwork
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static func isLoopback(_ endpoint: NWEndpoint) -> Bool {
|
|
34
|
+
guard case .hostPort(let host, _) = endpoint else { return false }
|
|
35
|
+
switch host {
|
|
36
|
+
case .ipv4(let address): return address.isLoopback
|
|
37
|
+
case .ipv6(let address): return address.isLoopback || (address.asIPv4?.isLoopback ?? false)
|
|
38
|
+
default: return false
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerListenerToken.swift
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
struct RunnerListenerToken {
|
|
5
|
+
static let requestHeaderName = "x-agent-device-runner-token"
|
|
6
|
+
static let minimumByteCount = 32
|
|
7
|
+
|
|
8
|
+
private let expected: [UInt8]
|
|
9
|
+
|
|
10
|
+
init?(_ token: String?) {
|
|
11
|
+
guard let token, token.utf8.count >= Self.minimumByteCount else { return nil }
|
|
12
|
+
expected = Array(token.utf8)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
func matches(_ presented: String) -> Bool {
|
|
17
|
+
let candidate = Array(presented.utf8)
|
|
18
|
+
var difference = candidate.count ^ expected.count
|
|
19
|
+
for index in 0..<expected.count {
|
|
20
|
+
let byte = index < candidate.count ? candidate[index] : 0
|
|
21
|
+
difference |= Int(byte ^ expected[index])
|
|
22
|
+
}
|
|
23
|
+
return difference == 0
|
|
24
|
+
}
|
|
25
|
+
}
|
package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Environment.swift
CHANGED
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
import Foundation
|
|
2
|
+
import Network
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
struct RunnerListenerConfiguration {
|
|
10
|
+
let port: NWEndpoint.Port
|
|
11
|
+
let token: RunnerListenerToken
|
|
12
|
+
|
|
13
|
+
static func fromEnvironment(
|
|
14
|
+
_ environment: [String: String] = ProcessInfo.processInfo.environment
|
|
15
|
+
) -> RunnerListenerConfiguration? {
|
|
16
|
+
guard
|
|
17
|
+
let rawPort = environment["AGENT_DEVICE_RUNNER_PORT"].flatMap({ UInt16($0) }), rawPort > 0,
|
|
18
|
+
let port = NWEndpoint.Port(rawValue: rawPort),
|
|
19
|
+
let token = RunnerListenerToken(environment["AGENT_DEVICE_RUNNER_TOKEN"])
|
|
20
|
+
else {
|
|
21
|
+
return nil
|
|
15
22
|
}
|
|
16
|
-
return
|
|
23
|
+
return RunnerListenerConfiguration(port: port, token: token)
|
|
17
24
|
}
|
|
18
25
|
}
|
package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift
CHANGED
|
@@ -3,6 +3,8 @@ import XCTest
|
|
|
3
3
|
|
|
4
4
|
extension RunnerTests {
|
|
5
5
|
static let navigationBackKeywords = ["back", "close", "cancel"]
|
|
6
|
+
static let systemBackButtonIdentifier = "BackButton"
|
|
7
|
+
static let floatingBarBackControlMaxYFraction: CGFloat = 0.4
|
|
6
8
|
static let navigationFallbackVerificationDelay: TimeInterval = 0.25
|
|
7
9
|
|
|
8
10
|
|
|
@@ -88,7 +90,11 @@ extension RunnerTests {
|
|
|
88
90
|
let candidates = app.buttons.matching(Self.navigationBackPredicate()).allElementsBoundByIndex.compactMap {
|
|
89
91
|
element -> (XCUIElement, Int)? in
|
|
90
92
|
guard element.exists, element.isHittable else { return nil }
|
|
91
|
-
guard Self.
|
|
93
|
+
guard Self.isTopNavigationBackCandidateFrame(
|
|
94
|
+
element.frame,
|
|
95
|
+
in: frame,
|
|
96
|
+
identifier: element.identifier
|
|
97
|
+
) else { return nil }
|
|
92
98
|
guard let rank = Self.navigationBackControlRank(
|
|
93
99
|
label: element.label,
|
|
94
100
|
identifier: element.identifier
|
|
@@ -132,6 +138,24 @@ extension RunnerTests {
|
|
|
132
138
|
return candidate.midY >= window.minY && candidate.midY <= maxY
|
|
133
139
|
}
|
|
134
140
|
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
static func isTopNavigationBackCandidateFrame(
|
|
145
|
+
_ candidate: CGRect,
|
|
146
|
+
in window: CGRect,
|
|
147
|
+
identifier: String
|
|
148
|
+
) -> Bool {
|
|
149
|
+
if isTopNavigationControlFrame(candidate, in: window) { return true }
|
|
150
|
+
guard identifier == systemBackButtonIdentifier,
|
|
151
|
+
SnapshotGeometry.isPositiveFinite(candidate), SnapshotGeometry.isPositiveFinite(window)
|
|
152
|
+
else {
|
|
153
|
+
return false
|
|
154
|
+
}
|
|
155
|
+
let maxY = window.minY + window.height * floatingBarBackControlMaxYFraction
|
|
156
|
+
return candidate.midY >= window.minY && candidate.midY <= maxY
|
|
157
|
+
}
|
|
158
|
+
|
|
135
159
|
static func topLeadingNavigationFallbackPoint(in frame: CGRect) -> CGPoint? {
|
|
136
160
|
guard SnapshotGeometry.isPositiveFinite(frame) else {
|
|
137
161
|
return nil
|
package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift
CHANGED
|
@@ -64,9 +64,7 @@ extension RunnerTests {
|
|
|
64
64
|
static let warmupValueTimeout: TimeInterval = 0.4
|
|
65
65
|
static let verificationStabilityWindow: TimeInterval = 0.2
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
static let unconfirmedSettleCeiling: TimeInterval = 1.0
|
|
67
|
+
static let replacementSettleCeiling: TimeInterval = 1.0
|
|
70
68
|
|
|
71
69
|
|
|
72
70
|
|
|
@@ -39,41 +39,74 @@ extension RunnerTests {
|
|
|
39
39
|
let target: TextEntryElementIdentity
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
struct ReplacementTextEntryConfirmation {
|
|
43
|
+
private let requested: String
|
|
44
|
+
private let baseline: TextEntryObservation?
|
|
45
|
+
private var deadline: Date
|
|
46
|
+
private var latest: TextEntryObservation?
|
|
47
|
+
private var stableSince: Date
|
|
48
|
+
|
|
49
|
+
init(requested: String, baseline: TextEntryObservation?, startedAt: Date) {
|
|
50
|
+
self.requested = requested
|
|
51
|
+
self.baseline = baseline
|
|
52
|
+
deadline = startedAt.addingTimeInterval(TextEntryTiming.replacementSettleCeiling)
|
|
53
|
+
stableSince = startedAt
|
|
54
|
+
}
|
|
42
55
|
|
|
56
|
+
mutating func observe(_ observed: TextEntryObservation, at sampledAt: Date) -> TextEntryResult? {
|
|
43
57
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
58
|
+
if latest == nil {
|
|
59
|
+
deadline = max(deadline, sampledAt.addingTimeInterval(TextEntryTiming.verificationStabilityWindow))
|
|
60
|
+
}
|
|
61
|
+
if latest.map({ observed.isSettled(with: $0) }) != true { stableSince = sampledAt }
|
|
62
|
+
latest = observed
|
|
63
|
+
let result = RunnerTests.replacementTextEntryResult(requested: requested, baseline: baseline, observed: observed)
|
|
64
|
+
let settled = sampledAt.timeIntervalSince(stableSince) >= TextEntryTiming.verificationStabilityWindow
|
|
65
|
+
let moved = baseline.map { !observed.isSettled(with: $0) } ?? true
|
|
66
|
+
if settled && (moved || result.verified != false || sampledAt >= deadline) {
|
|
67
|
+
return result
|
|
68
|
+
}
|
|
69
|
+
if sampledAt >= deadline {
|
|
70
|
+
return TextEntryResult(
|
|
71
|
+
verified: nil, repaired: false, expectedText: requested, observedText: observed.value,
|
|
72
|
+
failure: .commitNotObserved
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
return nil
|
|
76
|
+
}
|
|
53
77
|
}
|
|
54
78
|
|
|
55
79
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
static func unconfirmedTextEntryEvidence(
|
|
80
|
+
static func replacementTextEntryResult(
|
|
59
81
|
requested: String,
|
|
60
82
|
baseline: TextEntryObservation?,
|
|
61
83
|
observed: TextEntryObservation?
|
|
62
|
-
) ->
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
else {
|
|
70
|
-
return nil
|
|
84
|
+
) -> TextEntryResult {
|
|
85
|
+
func result(_ verified: Bool?) -> TextEntryResult {
|
|
86
|
+
TextEntryResult(verified: verified, repaired: false, expectedText: requested, observedText: observed?.value)
|
|
87
|
+
}
|
|
88
|
+
guard let observed else { return result(nil) }
|
|
89
|
+
if let baseline, !baseline.identity.isSameElement(as: observed.identity) {
|
|
90
|
+
return result(false)
|
|
71
91
|
}
|
|
72
|
-
|
|
73
|
-
requested: requested
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
92
|
+
let request = observed.identity.elementType == elementTypeNamesByRawValue[XCUIElement.ElementType.textView.rawValue]
|
|
93
|
+
? requested : textEntryRequestWithoutSubmitKeys(requested)
|
|
94
|
+
if observed.value == requested || observed.value == request { return result(true) }
|
|
95
|
+
|
|
96
|
+
if request != requested { return result(nil) }
|
|
97
|
+
guard !request.isEmpty, let baseline else { return result(false) }
|
|
98
|
+
if isOrderedSubsequence(observed.value, of: baseline.value + request) { return result(false) }
|
|
99
|
+
return TextEntryResult(
|
|
100
|
+
verified: nil,
|
|
101
|
+
repaired: false,
|
|
102
|
+
expectedText: requested,
|
|
103
|
+
observedText: observed.value,
|
|
104
|
+
unconfirmed: TextEntryUnconfirmedEvidence(
|
|
105
|
+
requested: requested,
|
|
106
|
+
before: baseline.value,
|
|
107
|
+
after: observed.value,
|
|
108
|
+
target: observed.identity
|
|
109
|
+
)
|
|
77
110
|
)
|
|
78
111
|
}
|
|
79
112
|
|