agent-device 0.21.14 → 0.21.15
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.14.apk → agent-device-android-ime-helper-0.21.15.apk} +0 -0
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.15.apk.sha256 +1 -0
- package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.14.manifest.json → agent-device-android-ime-helper-0.21.15.manifest.json} +4 -4
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.15.apk +0 -0
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.15.apk.sha256 +1 -0
- package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.21.14.manifest.json → agent-device-android-snapshot-helper-0.21.15.manifest.json} +6 -6
- package/apple/snapshot-bridge/SnapshotBridgeRuntime.m +11 -1
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +87 -2
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.m +4 -2
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerMainOwnedState.swift +27 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedTextEntry.h +8 -2
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedTextEntry.m +26 -1
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +40 -5
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandDispatch.swift +179 -91
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +5 -4
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +1 -1
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+GestureExecution.swift +1 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +36 -49
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+MainThreadWork.swift +11 -15
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +136 -29
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift +4 -2
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+PrivateAXPresentation.swift +3 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScreenRecorder.swift +6 -5
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollDragExecution.swift +4 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SequenceExecution.swift +2 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +8 -4
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotAcquisition.swift +56 -14
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +18 -8
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCaptureTarget.swift +9 -7
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotExecution.swift +5 -4
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotTiming.swift +2 -2
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedGesturePolicy.swift +4 -1
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedInteraction.swift +5 -1
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedTextEntry.swift +78 -3
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +32 -2
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryFocus.swift +7 -4
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextTyping.swift +23 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +17 -10
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TvRemote.swift +8 -9
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TypeExecution.swift +2 -1
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +12 -11
- package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotGeometry.swift +7 -4
- package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotModels.swift +15 -8
- package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotPresentationProjection.swift +1 -0
- package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotVisibilityFold.swift +26 -9
- package/dist/apple/snapshot-presentation/Sources/AgentDeviceSnapshotPresentation/SnapshotVisibilityFoldProjection.swift +1 -0
- package/dist/src/adapter.js +1 -1
- package/dist/src/adb-executor.js +1 -1
- package/dist/src/adb-failure.js +1 -1
- package/dist/src/adb-transport.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/ai-sdk.js +1 -1
- package/dist/src/alert-runtime.js +1 -1
- package/dist/src/android-adb.d.ts +1 -8
- package/dist/src/android-input-ownership.js +1 -1
- package/dist/src/android-snapshot-quality.js +1 -0
- package/dist/src/android-system-chrome.js +1 -1
- package/dist/src/android.js +1 -1
- package/dist/src/app-catalog.js +1 -1
- package/dist/src/app-deployment-resolution.js +1 -1
- package/dist/src/app-inventory.js +1 -1
- package/dist/src/app-launch.js +1 -1
- package/dist/src/app-lifecycle.js +1 -1
- package/dist/src/app-lifecycle2.js +1 -1
- package/dist/src/app-log-files.js +1 -1
- package/dist/src/app-log-runtime2.js +1 -1
- package/dist/src/app-resolution.js +2 -2
- package/dist/src/apple-runner-platform.js +1 -1
- package/dist/src/application-lifecycle-interaction.js +1 -1
- package/dist/src/archive.js +1 -1
- package/dist/src/artifact-download.js +1 -1
- package/dist/src/auth-session.js +1 -1
- package/dist/src/auth.js +1 -1
- package/dist/src/backend-snapshot-options.js +1 -1
- package/dist/src/boot-diagnostics.js +1 -1
- package/dist/src/capture-disclosure.js +1 -1
- package/dist/src/capture.js +1 -1
- package/dist/src/cli-config.js +1 -1
- package/dist/src/cli.js +1 -1
- package/dist/src/click-button.js +1 -1
- package/dist/src/client-metro.js +1 -1
- package/dist/src/client-types.d.ts +102 -95
- package/dist/src/clipboard-shell-response.js +1 -1
- package/dist/src/command-schema.js +1 -1
- package/dist/src/command-tools.js +3 -3
- package/dist/src/connection-runtime.js +1 -1
- package/dist/src/connection.js +1 -1
- package/dist/src/contracts.d.ts +1 -1
- package/dist/src/contracts.js +1 -1
- package/dist/src/daemon-client-lifecycle.js +1 -1
- package/dist/src/daemon.js +1 -1
- package/dist/src/debug-symbols-facade.js +1 -1
- package/dist/src/device-claim-rule.js +2 -2
- package/dist/src/device-input-state.d.ts +15 -2
- package/dist/src/device-inventory-context.js +1 -1
- package/dist/src/device-rotation.js +1 -1
- package/dist/src/device-selection-resolver.js +1 -1
- package/dist/src/device-session.js +1 -1
- package/dist/src/device-shell.js +1 -1
- package/dist/src/device2.js +1 -1
- package/dist/src/devicectl.js +1 -1
- package/dist/src/diagnostics.js +1 -1
- package/dist/src/dispatch-resolve.js +1 -1
- package/dist/src/doctor2.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/file.js +1 -1
- package/dist/src/find.js +1 -1
- package/dist/src/format.js +1 -1
- package/dist/src/gesture-plan-types.d.ts +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/helper.js +2 -2
- package/dist/src/human-control-contract.js +1 -1
- package/dist/src/human-control.js +1 -1
- package/dist/src/index.d.ts +7 -3
- package/dist/src/index.js +1 -1
- package/dist/src/input-actions.js +1 -1
- package/dist/src/input-actions2.js +1 -1
- package/dist/src/install-artifact-facade.js +1 -1
- package/dist/src/install-source-network-transport.js +1 -1
- package/dist/src/install-source2.js +1 -1
- package/dist/src/interaction-positionals.js +1 -1
- package/dist/src/interaction-ref-policy.js +1 -1
- package/dist/src/interaction2.js +1 -1
- package/dist/src/interactor-operation-binding.js +1 -1
- package/dist/src/interactor.js +4 -4
- package/dist/src/interactor2.js +1 -1
- package/dist/src/interactors.js +1 -1
- package/dist/src/internal/bin.js +1 -1
- package/dist/src/internal/daemon.js +2 -2
- package/dist/src/internal/png-worker.js +1 -1
- package/dist/src/internal/run-script-http-child.js +1 -1
- package/dist/src/inventory.js +1 -1
- package/dist/src/inventory2.js +1 -1
- package/dist/src/inventory3.js +1 -1
- package/dist/src/inventory5.js +1 -1
- package/dist/src/ios-snapshot-acquisition.js +1 -1
- package/dist/src/ios-snapshot-engine.js +1 -0
- package/dist/src/ios-snapshot-runtime.js +1 -1
- package/dist/src/lease.js +1 -1
- package/dist/src/limrun-download-file.js +1 -1
- package/dist/src/limrun-runtime-dependencies.js +1 -1
- package/dist/src/limrun.d.ts +16 -3
- package/dist/src/linux.js +1 -1
- package/dist/src/location-coordinates.js +1 -1
- package/dist/src/managed-device-scope.js +1 -1
- package/dist/src/mechanics.js +17 -17
- package/dist/src/mobile-snapshot-semantics.js +1 -1
- package/dist/src/multitouch-support.js +1 -1
- package/dist/src/native-build-cache.js +1 -1
- package/dist/src/observation.js +1 -1
- package/dist/src/overlay.js +3 -0
- package/dist/src/parameterized-recorded-fill.js +1 -1
- package/dist/src/perf-facade.js +1 -1
- package/dist/src/perf-runtime-operation-builder.js +1 -1
- package/dist/src/perf-runtime-plan.js +1 -1
- package/dist/src/perf-target.js +1 -1
- package/dist/src/perf.js +1 -1
- package/dist/src/physical-device-control.js +1 -1
- package/dist/src/platform-runtime-managed-owner.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 +1 -1
- package/dist/src/platform-runtime-screen-recording-apple-simulator-host.js +1 -1
- package/dist/src/platform-runtime-screen-recording-finalizer-host.js +1 -1
- package/dist/src/platform-runtime-screen-recording-process-host.js +1 -1
- package/dist/src/platform-runtime.js +1 -1
- package/dist/src/platform-runtime2.js +1 -1
- package/dist/src/png-crop-bytes.js +1 -1
- package/dist/src/png-size.js +1 -1
- package/dist/src/png-worker-client.js +1 -1
- package/dist/src/png.js +1 -1
- package/dist/src/prepare-kind.js +1 -1
- package/dist/src/provider-session-ownership.js +1 -1
- package/dist/src/provider-webdriver.js +1 -1
- package/dist/src/proxy.js +1 -1
- package/dist/src/quality-warnings.js +1 -1
- package/dist/src/react-native.js +1 -1
- package/dist/src/record-runtime.js +1 -1
- package/dist/src/recording.js +1 -1
- package/dist/src/rect-center.js +1 -1
- package/dist/src/register-builtins.js +1 -1
- package/dist/src/registry.js +36 -36
- package/dist/src/remote-config2.js +1 -1
- package/dist/src/replay-divergence.js +1 -1
- package/dist/src/replay.js +1 -1
- package/dist/src/reporting.js +1 -1
- package/dist/src/request-admission.js +1 -1
- package/dist/src/request-cancel.js +1 -1
- package/dist/src/request-runtime-binding.js +1 -1
- package/dist/src/retry.js +1 -1
- package/dist/src/runner-cache-metadata.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-disposal.js +1 -1
- package/dist/src/runner-error-classification.js +2 -2
- package/dist/src/runner.js +1 -1
- package/dist/src/runtime-transport-hints.js +1 -1
- package/dist/src/runtime.js +1 -3
- package/dist/src/runtime10.js +2 -2
- package/dist/src/runtime2.js +4 -4
- package/dist/src/runtime3.js +1 -1
- package/dist/src/runtime4.js +5 -5
- package/dist/src/runtime5.js +1 -1
- 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/screen-recording-admission-ledger.js +1 -1
- package/dist/src/screen-recording-completion.js +1 -1
- package/dist/src/screen-recording-options.js +1 -1
- package/dist/src/screen-recording-resource-recovery.js +1 -1
- package/dist/src/screenshot-density.js +1 -1
- package/dist/src/screenshot-image.js +1 -1
- package/dist/src/screenshot-overlay-draw.js +1 -1
- package/dist/src/screenshot-runtime.js +1 -1
- package/dist/src/screenshot.js +1 -1
- package/dist/src/screenshot3.js +1 -1
- package/dist/src/script.js +1 -1
- package/dist/src/scroll-command.d.ts +13 -6
- package/dist/src/scroll-command.js +1 -1
- package/dist/src/scroll-gesture.js +1 -1
- package/dist/src/sdk-batch-runner.js +1 -1
- package/dist/src/sdk-batch.js +2 -2
- package/dist/src/sdk-contracts.d.ts +38 -10
- package/dist/src/sdk-contracts.js +2 -2
- package/dist/src/sdk-device.js +1 -1
- package/dist/src/sdk-finders.d.ts +1 -1
- package/dist/src/sdk-io.js +1 -1
- package/dist/src/sdk-remote-config.js +1 -1
- package/dist/src/sdk-selectors.d.ts +1 -1
- package/dist/src/selection.js +0 -0
- package/dist/src/selector-runtime.js +1 -1
- package/dist/src/server.js +1 -1
- package/dist/src/session-allocation.js +1 -1
- package/dist/src/session-routing.js +1 -1
- package/dist/src/session-runtime-admission.js +1 -1
- package/dist/src/session-snapshot.js +1 -1
- package/dist/src/session-store.js +2 -2
- package/dist/src/session.js +1 -1
- package/dist/src/session2.js +4 -4
- package/dist/src/settings.js +1 -1
- package/dist/src/settings2.js +1 -0
- package/dist/src/simulator.js +1 -1
- package/dist/src/snapshot-capture-annotations.js +1 -1
- package/dist/src/snapshot-desktop-projection.js +1 -1
- package/dist/src/snapshot-runtime.js +1 -1
- package/dist/src/snapshot-state.js +1 -1
- package/dist/src/snapshot2.js +1 -1
- package/dist/src/snapshot3.js +1 -1
- package/dist/src/snapshot4.js +1 -1
- package/dist/src/src.js +1 -1
- package/dist/src/src4.js +1 -1
- package/dist/src/src7.js +1 -1
- package/dist/src/src8.js +2 -2
- package/dist/src/string-enum.js +1 -1
- package/dist/src/surface-snapshot.js +1 -1
- package/dist/src/takeover.js +1 -1
- package/dist/src/text-entry-focus.js +1 -1
- package/dist/src/tool-provider.js +1 -1
- package/dist/src/tool-provider2.js +1 -1
- package/dist/src/touch-runtime.js +1 -1
- package/dist/src/transport.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/verified-file.js +1 -1
- package/dist/src/video.js +1 -1
- package/dist/src/viewport.js +1 -1
- package/dist/src/web.js +1 -1
- package/dist/src/web2.js +1 -1
- package/dist/src/webdriver-source.js +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.14.apk.sha256 +0 -1
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.14.apk +0 -0
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.14.apk.sha256 +0 -1
- package/dist/src/absence-observation-errors.js +0 -1
- package/dist/src/runner-presentation.js +0 -1
- package/dist/src/semantic-index.js +0 -1
- package/dist/src/snapshot-scope.js +0 -1
package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift
CHANGED
|
@@ -35,6 +35,7 @@ extension RunnerTests {
|
|
|
35
35
|
let label: String
|
|
36
36
|
let identifier: String
|
|
37
37
|
let valueText: String?
|
|
38
|
+
let placeholder: String?
|
|
38
39
|
let focused: Bool
|
|
39
40
|
let selected: Bool
|
|
40
41
|
}
|
|
@@ -175,6 +176,7 @@ extension RunnerTests {
|
|
|
175
176
|
)
|
|
176
177
|
}
|
|
177
178
|
|
|
179
|
+
@MainActor
|
|
178
180
|
func recursiveTreeSnapshotAcquisition(
|
|
179
181
|
context: SnapshotTraversalContext,
|
|
180
182
|
hint: CaptureHint
|
|
@@ -327,7 +329,7 @@ extension RunnerTests {
|
|
|
327
329
|
private func boundedBlockingSystemAlertSnapshotBody(
|
|
328
330
|
deadline: Date,
|
|
329
331
|
penaltyTarget: SnapshotProbePenaltyTarget,
|
|
330
|
-
probe: @escaping (Date) -> DataPayload?
|
|
332
|
+
probe: @escaping @MainActor (Date) -> DataPayload?
|
|
331
333
|
) -> DataPayload? {
|
|
332
334
|
#if os(macOS)
|
|
333
335
|
return nil
|
|
@@ -361,7 +363,7 @@ extension RunnerTests {
|
|
|
361
363
|
)
|
|
362
364
|
}
|
|
363
365
|
) {
|
|
364
|
-
penaltyIdentity.captureFromMain(bundleId: self.
|
|
366
|
+
penaltyIdentity.captureFromMain(bundleId: self.mainOwned.bundleId)
|
|
365
367
|
return probe(probeDeadline)
|
|
366
368
|
}
|
|
367
369
|
} catch {
|
|
@@ -437,6 +439,7 @@ extension RunnerTests {
|
|
|
437
439
|
)
|
|
438
440
|
}
|
|
439
441
|
|
|
442
|
+
@MainActor
|
|
440
443
|
func querySweepSnapshotAcquisition(
|
|
441
444
|
app: XCUIApplication,
|
|
442
445
|
hint: CaptureHint,
|
|
@@ -500,6 +503,7 @@ extension RunnerTests {
|
|
|
500
503
|
label: candidate.label,
|
|
501
504
|
identifier: candidate.identifier,
|
|
502
505
|
value: candidate.value,
|
|
506
|
+
placeholder: candidate.placeholder,
|
|
503
507
|
rect: candidate.rect,
|
|
504
508
|
enabled: candidate.enabled,
|
|
505
509
|
focused: candidate.focused,
|
|
@@ -527,7 +531,7 @@ extension RunnerTests {
|
|
|
527
531
|
func snapshotAccessibilityUnavailable(failure: SnapshotCaptureFailure) -> DataPayload {
|
|
528
532
|
NSLog("AGENT_DEVICE_RUNNER_SNAPSHOT_AX_UNAVAILABLE=%@", failure.message)
|
|
529
533
|
applyMainOwnedSnapshotState("ax_unavailable_invalidation") {
|
|
530
|
-
self.
|
|
534
|
+
self.mainOwned.accessibilityHealth = .unavailable
|
|
531
535
|
self.invalidateCachedTarget(reason: Self.axSnapshotUnavailableReason)
|
|
532
536
|
}
|
|
533
537
|
|
|
@@ -535,7 +539,7 @@ extension RunnerTests {
|
|
|
535
539
|
return sparseTruncatedSnapshotPayload(
|
|
536
540
|
message: recoveredSnapshotMessage(failure),
|
|
537
541
|
snapshotQuality: SnapshotQuality(
|
|
538
|
-
state:
|
|
542
|
+
state: .sparse,
|
|
539
543
|
backend: SnapshotBackendKind.recursiveTree.rawValue,
|
|
540
544
|
reason: failure.message,
|
|
541
545
|
reasonCode: "ax-rejected",
|
|
@@ -38,22 +38,22 @@ extension RunnerTests {
|
|
|
38
38
|
captureDeadline: Date = .distantFuture,
|
|
39
39
|
treeCaptureSliceBudgetOverride: TimeInterval? = nil
|
|
40
40
|
) throws -> SnapshotTraversalContext? {
|
|
41
|
+
let treeSliceBudget = treeCaptureSliceBudgetOverride ?? treeCaptureSliceBudget
|
|
42
|
+
let slice = min(treeSliceBudget, max(0.5, captureDeadline.timeIntervalSinceNow))
|
|
43
|
+
guard let rootSnapshot = try captureSnapshotRootBounded(app, sliceSeconds: slice) else {
|
|
44
|
+
return nil
|
|
45
|
+
}
|
|
46
|
+
|
|
41
47
|
|
|
42
48
|
|
|
43
49
|
|
|
44
50
|
let viewport = try runMainThreadWork(
|
|
45
51
|
"snapshot_viewport",
|
|
46
52
|
timeout: min(1.0, max(0.1, captureDeadline.timeIntervalSinceNow)),
|
|
47
|
-
timeoutError: snapshotMainThreadTimeoutError("
|
|
53
|
+
timeoutError: snapshotMainThreadTimeoutError("reading snapshot viewport")
|
|
48
54
|
) {
|
|
49
55
|
self.safeSnapshotViewport(app: app, readingOrientation: true)
|
|
50
56
|
}
|
|
51
|
-
let treeSliceBudget = treeCaptureSliceBudgetOverride ?? treeCaptureSliceBudget
|
|
52
|
-
let slice = min(treeSliceBudget, max(0.5, captureDeadline.timeIntervalSinceNow))
|
|
53
|
-
guard let rootSnapshot = try captureSnapshotRootBounded(app, sliceSeconds: slice) else {
|
|
54
|
-
return nil
|
|
55
|
-
}
|
|
56
|
-
|
|
57
57
|
|
|
58
58
|
|
|
59
59
|
|
|
@@ -93,7 +93,7 @@ extension RunnerTests {
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
private func treeCaptureTimeoutError(sliceSeconds: TimeInterval) -> () -> Error {
|
|
96
|
+
private func treeCaptureTimeoutError(sliceSeconds: TimeInterval) -> @Sendable () -> Error {
|
|
97
97
|
{
|
|
98
98
|
SnapshotCaptureFailure(
|
|
99
99
|
code: Self.xCTestSnapshotTimeoutCode,
|
|
@@ -103,7 +103,7 @@ extension RunnerTests {
|
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
func snapshotMainThreadTimeoutError(_ operation: String) -> () -> Error {
|
|
106
|
+
func snapshotMainThreadTimeoutError(_ operation: String) -> @Sendable () -> Error {
|
|
107
107
|
{
|
|
108
108
|
SnapshotCaptureFailure(
|
|
109
109
|
code: Self.xCTestSnapshotTimeoutCode,
|
|
@@ -189,6 +189,7 @@ extension RunnerTests {
|
|
|
189
189
|
label: label,
|
|
190
190
|
identifier: identifier,
|
|
191
191
|
valueText: valueText,
|
|
192
|
+
placeholder: placeholderText(snapshot.placeholderValue),
|
|
192
193
|
focused: snapshotHasFocus(snapshot),
|
|
193
194
|
selected: snapshotIsSelected(snapshot)
|
|
194
195
|
)
|
|
@@ -209,6 +210,7 @@ extension RunnerTests {
|
|
|
209
210
|
label: evaluation.label.isEmpty ? nil : evaluation.label,
|
|
210
211
|
identifier: evaluation.identifier.isEmpty ? nil : evaluation.identifier,
|
|
211
212
|
value: evaluation.valueText,
|
|
213
|
+
placeholder: evaluation.placeholder,
|
|
212
214
|
rect: SnapshotRect(snapshot.frame),
|
|
213
215
|
enabled: snapshot.isEnabled,
|
|
214
216
|
focused: evaluation.focused ? true : nil,
|
|
@@ -235,6 +237,26 @@ extension RunnerTests {
|
|
|
235
237
|
return text.isEmpty ? nil : text
|
|
236
238
|
}
|
|
237
239
|
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
func placeholderText(_ placeholderValue: String?) -> String? {
|
|
244
|
+
let text = placeholderValue?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
|
245
|
+
return text.isEmpty ? nil : text
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
static let placeholderElementTypes: Set<XCUIElement.ElementType> = [
|
|
252
|
+
.textField, .secureTextField, .searchField, .textView,
|
|
253
|
+
]
|
|
254
|
+
|
|
255
|
+
func elementPlaceholderText(_ element: XCUIElement, type: XCUIElement.ElementType) -> String? {
|
|
256
|
+
guard Self.placeholderElementTypes.contains(type) else { return nil }
|
|
257
|
+
return placeholderText(element.placeholderValue)
|
|
258
|
+
}
|
|
259
|
+
|
|
238
260
|
private func snapshotAppFrame(app: XCUIApplication) -> CGRect {
|
|
239
261
|
#if os(iOS)
|
|
240
262
|
return onScreenWindowFrame(app: app)
|
|
@@ -341,6 +363,7 @@ extension RunnerTests {
|
|
|
341
363
|
label: node.label,
|
|
342
364
|
identifier: node.identifier,
|
|
343
365
|
value: node.value,
|
|
366
|
+
placeholder: node.placeholder,
|
|
344
367
|
rect: node.rect,
|
|
345
368
|
enabled: node.enabled,
|
|
346
369
|
focused: node.focused,
|
|
@@ -376,7 +399,8 @@ extension RunnerTests {
|
|
|
376
399
|
let label = element.label.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
377
400
|
let identifier = element.identifier.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
378
401
|
let valueText = snapshotValueText(element)
|
|
379
|
-
let
|
|
402
|
+
let placeholder = elementPlaceholderText(element, type: elementType)
|
|
403
|
+
let hasContent = !label.isEmpty || !identifier.isEmpty || valueText != nil || placeholder != nil
|
|
380
404
|
if !hasContent { return }
|
|
381
405
|
if sameSemanticElement(
|
|
382
406
|
containerSnapshot: containerSnapshot,
|
|
@@ -395,6 +419,7 @@ extension RunnerTests {
|
|
|
395
419
|
label: label.isEmpty ? nil : label,
|
|
396
420
|
identifier: identifier.isEmpty ? nil : identifier,
|
|
397
421
|
value: valueText,
|
|
422
|
+
placeholder: placeholder,
|
|
398
423
|
rect: SnapshotRect(frame),
|
|
399
424
|
enabled: element.isEnabled,
|
|
400
425
|
focused: elementHasFocus(element) ? true : nil,
|
|
@@ -417,13 +442,27 @@ extension RunnerTests {
|
|
|
417
442
|
}
|
|
418
443
|
|
|
419
444
|
private func snapshotHasFocus(_ snapshot: XCUIElementSnapshot) -> Bool {
|
|
420
|
-
|
|
445
|
+
return focusBool(snapshot as! NSObject)
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
func focusBool(_ object: NSObject) -> Bool {
|
|
455
|
+
return kvcBool(object, forKey: "hasKeyboardFocus") || kvcBool(object, forKey: "hasFocus")
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
private func kvcBool(_ object: NSObject, forKey key: String) -> Bool {
|
|
459
|
+
var result = false
|
|
421
460
|
_ = RunnerObjCExceptionCatcher.catchException({
|
|
422
|
-
if let value =
|
|
423
|
-
|
|
461
|
+
if let value = object.value(forKey: key) as? Bool {
|
|
462
|
+
result = value
|
|
424
463
|
}
|
|
425
464
|
})
|
|
426
|
-
return
|
|
465
|
+
return result
|
|
427
466
|
}
|
|
428
467
|
|
|
429
468
|
private func snapshotIsSelected(_ snapshot: XCUIElementSnapshot) -> Bool {
|
|
@@ -461,6 +500,7 @@ extension RunnerTests {
|
|
|
461
500
|
return containerLabel == label && containerIdentifier == identifier
|
|
462
501
|
}
|
|
463
502
|
|
|
503
|
+
@MainActor
|
|
464
504
|
func flatInteractiveElements(
|
|
465
505
|
app: XCUIApplication,
|
|
466
506
|
deadline: Date
|
|
@@ -519,6 +559,7 @@ extension RunnerTests {
|
|
|
519
559
|
return (elements, .completed)
|
|
520
560
|
}
|
|
521
561
|
|
|
562
|
+
@MainActor
|
|
522
563
|
func snapshotElementsQuery(
|
|
523
564
|
_ fetch: () -> [XCUIElement]
|
|
524
565
|
) -> (elements: [XCUIElement], axUnavailable: Bool) {
|
|
@@ -558,6 +599,7 @@ extension RunnerTests {
|
|
|
558
599
|
label: label.isEmpty ? nil : label,
|
|
559
600
|
identifier: identifier.isEmpty ? nil : identifier,
|
|
560
601
|
value: valueText,
|
|
602
|
+
placeholder: elementPlaceholderText(element, type: elementType),
|
|
561
603
|
rect: SnapshotRect(frame),
|
|
562
604
|
enabled: enabled,
|
|
563
605
|
focused: elementHasFocus(element) ? true : nil,
|
|
@@ -9,10 +9,20 @@ import AgentDeviceSnapshotPresentation
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
struct SnapshotQuality: Codable {
|
|
13
12
|
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
enum SnapshotQualityState: String, Codable, CaseIterable {
|
|
15
|
+
|
|
16
|
+
case healthy
|
|
17
|
+
|
|
18
|
+
case recovered
|
|
19
|
+
|
|
20
|
+
case sparse
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
struct SnapshotQuality: Codable {
|
|
25
|
+
let state: SnapshotQualityState
|
|
16
26
|
|
|
17
27
|
let backend: String
|
|
18
28
|
|
|
@@ -391,7 +401,7 @@ extension RunnerTests {
|
|
|
391
401
|
return stampedSnapshotPayload(
|
|
392
402
|
capture,
|
|
393
403
|
backend: kind,
|
|
394
|
-
state: recovered ?
|
|
404
|
+
state: recovered ? .recovered : .healthy,
|
|
395
405
|
reason: recovered || firstFailure?.code == "requested-backend" ? firstFailure : nil
|
|
396
406
|
)
|
|
397
407
|
}
|
|
@@ -416,11 +426,11 @@ extension RunnerTests {
|
|
|
416
426
|
}
|
|
417
427
|
|
|
418
428
|
let fallbackPayload =
|
|
419
|
-
best.map { stampedSnapshotPayload($0.capture, backend: $0.kind, state:
|
|
429
|
+
best.map { stampedSnapshotPayload($0.capture, backend: $0.kind, state: .sparse, reason: firstFailure) }
|
|
420
430
|
?? stampedSnapshotPayload(
|
|
421
431
|
SnapshotBackendCapture(payload: sparseTruncatedSnapshotPayload(), effectiveDepth: nil),
|
|
422
432
|
backend: effectivePlan.last ?? plan.last ?? .recursiveTree,
|
|
423
|
-
state:
|
|
433
|
+
state: .sparse,
|
|
424
434
|
reason: firstFailure
|
|
425
435
|
)
|
|
426
436
|
return fallbackPayload
|
|
@@ -679,12 +689,12 @@ extension RunnerTests {
|
|
|
679
689
|
func stampedSnapshotPayload(
|
|
680
690
|
_ capture: SnapshotBackendCapture,
|
|
681
691
|
backend: SnapshotBackendKind,
|
|
682
|
-
state:
|
|
692
|
+
state: SnapshotQualityState,
|
|
683
693
|
reason: (reason: String, code: String)?
|
|
684
694
|
) -> DataPayload {
|
|
685
695
|
let health: RunnerAccessibilityHealth = reason?.code == "ax-rejected" ? .unavailable : .healthy
|
|
686
696
|
applyMainOwnedSnapshotState("accessibility_health") {
|
|
687
|
-
self.
|
|
697
|
+
self.mainOwned.accessibilityHealth = health
|
|
688
698
|
}
|
|
689
699
|
let payload = capture.payload
|
|
690
700
|
let quality = SnapshotQuality(
|
|
@@ -704,7 +714,7 @@ extension RunnerTests {
|
|
|
704
714
|
|
|
705
715
|
|
|
706
716
|
|
|
707
|
-
truncated: payload.truncated == true || state ==
|
|
717
|
+
truncated: payload.truncated == true || state == .sparse || capture.effectiveDepth != nil,
|
|
708
718
|
qualityPayload: capture.qualityPayload.flatMap { quality in
|
|
709
719
|
guard let nodes = quality.nodes else { return nil }
|
|
710
720
|
return SnapshotQualityPayload(nodes: nodes, truncated: quality.truncated == true)
|
|
@@ -8,7 +8,6 @@ import XCTest
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
|
|
12
11
|
struct SnapshotCaptureTarget {
|
|
13
12
|
let app: XCUIApplication
|
|
14
13
|
let bundleId: String?
|
|
@@ -67,32 +66,35 @@ final class SnapshotProbePenaltyIdentity {
|
|
|
67
66
|
|
|
68
67
|
extension RunnerTests {
|
|
69
68
|
|
|
69
|
+
@MainActor
|
|
70
70
|
func takeSnapshotCaptureTarget(app: XCUIApplication) -> SnapshotCaptureTarget {
|
|
71
71
|
SnapshotCaptureTarget(
|
|
72
72
|
app: app,
|
|
73
|
-
bundleId:
|
|
74
|
-
processIdentifier:
|
|
73
|
+
bundleId: mainOwned.bundleId,
|
|
74
|
+
processIdentifier: mainOwned.processIdentifier
|
|
75
75
|
)
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
|
|
79
79
|
|
|
80
80
|
|
|
81
|
-
func applyMainOwnedSnapshotState(_ operation: String, _ write: @escaping () -> Void) {
|
|
81
|
+
func applyMainOwnedSnapshotState(_ operation: String, _ write: @escaping @MainActor () -> Void) {
|
|
82
82
|
if Thread.isMainThread {
|
|
83
|
-
write
|
|
83
|
+
_ = runOnMainActor(write)
|
|
84
84
|
return
|
|
85
85
|
}
|
|
86
86
|
guard !hasAbandonedMainThreadWork() else {
|
|
87
87
|
NSLog("AGENT_DEVICE_RUNNER_SNAPSHOT_STATE_DEFERRED_XCTEST_OCCUPIED operation=%@", operation)
|
|
88
|
-
DispatchQueue.main.async
|
|
88
|
+
DispatchQueue.main.async {
|
|
89
|
+
_ = runOnMainActor(write)
|
|
90
|
+
}
|
|
89
91
|
return
|
|
90
92
|
}
|
|
91
93
|
do {
|
|
92
94
|
try runMainThreadWork(
|
|
93
95
|
operation,
|
|
94
96
|
timeout: 1,
|
|
95
|
-
timeoutError: mainThreadExecutionTimeoutError,
|
|
97
|
+
timeoutError: Self.mainThreadExecutionTimeoutError,
|
|
96
98
|
write
|
|
97
99
|
)
|
|
98
100
|
} catch {
|
|
@@ -11,8 +11,8 @@ extension RunnerTests {
|
|
|
11
11
|
private func executeSnapshotDispatchedOnce(command: Command) throws -> Response {
|
|
12
12
|
let preparation: SnapshotCommandPreparation = try runMainThreadWork(
|
|
13
13
|
"command_preparation",
|
|
14
|
-
timeout: mainThreadExecutionTimeout,
|
|
15
|
-
timeoutError: mainThreadExecutionTimeoutError
|
|
14
|
+
timeout: Self.mainThreadExecutionTimeout,
|
|
15
|
+
timeoutError: Self.mainThreadExecutionTimeoutError
|
|
16
16
|
) { () -> SnapshotCommandPreparation in
|
|
17
17
|
switch try self.prepareActiveCommandContextSafely(command: command, routeToSpringboard: false) {
|
|
18
18
|
case .response(let response):
|
|
@@ -36,6 +36,7 @@ extension RunnerTests {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
@MainActor
|
|
39
40
|
private func prepareActiveCommandContextSafely(
|
|
40
41
|
command: Command,
|
|
41
42
|
routeToSpringboard: Bool
|
|
@@ -125,9 +126,9 @@ extension RunnerTests {
|
|
|
125
126
|
try runMainThreadWork(
|
|
126
127
|
"post_snapshot_delay_mark",
|
|
127
128
|
timeout: 1,
|
|
128
|
-
timeoutError: mainThreadExecutionTimeoutError
|
|
129
|
+
timeoutError: Self.mainThreadExecutionTimeoutError
|
|
129
130
|
) {
|
|
130
|
-
self.needsPostSnapshotInteractionDelay = true
|
|
131
|
+
self.mainOwned.needsPostSnapshotInteractionDelay = true
|
|
131
132
|
}
|
|
132
133
|
} catch {
|
|
133
134
|
NSLog("AGENT_DEVICE_RUNNER_POST_SNAPSHOT_DELAY_MARK_FAILED=%@", String(describing: error))
|
|
@@ -24,11 +24,11 @@ enum SnapshotCapturePhase: Equatable {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
struct SnapshotPhaseTimer {
|
|
27
|
-
private let now: () -> Date
|
|
27
|
+
private let now: @Sendable () -> Date
|
|
28
28
|
private var acquisitionSeconds: TimeInterval = 0
|
|
29
29
|
private var presentationSeconds: TimeInterval = 0
|
|
30
30
|
|
|
31
|
-
init(now: @escaping () -> Date = { Date() }) {
|
|
31
|
+
init(now: @escaping @Sendable () -> Date = { Date() }) {
|
|
32
32
|
self.now = now
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -125,6 +125,7 @@ enum SynthesizedGestureAttempt {
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
extension RunnerTests {
|
|
128
|
+
@MainActor
|
|
128
129
|
func synthesizedSequenceCoordinateContext(
|
|
129
130
|
steps: [SequenceStep],
|
|
130
131
|
app: XCUIApplication
|
|
@@ -137,6 +138,7 @@ extension RunnerTests {
|
|
|
137
138
|
|
|
138
139
|
|
|
139
140
|
|
|
141
|
+
@MainActor
|
|
140
142
|
func performSynthesizedGesture(
|
|
141
143
|
_ app: XCUIApplication,
|
|
142
144
|
kind: SynthesizedGesturePolicyKind,
|
|
@@ -149,7 +151,7 @@ extension RunnerTests {
|
|
|
149
151
|
return .performed(timing: timing)
|
|
150
152
|
}
|
|
151
153
|
let fallbackAllowed = synthesizedGesturePolicy(kind).fallbackPolicy.allowsXCTestCoordinateFallback(
|
|
152
|
-
accessibilityHealth: context?.accessibilityHealth ??
|
|
154
|
+
accessibilityHealth: context?.accessibilityHealth ?? mainOwned.accessibilityHealth
|
|
153
155
|
)
|
|
154
156
|
logSynthesizedGesturePolicyDecision(
|
|
155
157
|
kind: kind,
|
|
@@ -161,6 +163,7 @@ extension RunnerTests {
|
|
|
161
163
|
: .refused(timing: timing, message: message, hint: hint)
|
|
162
164
|
}
|
|
163
165
|
|
|
166
|
+
@MainActor
|
|
164
167
|
func logSynthesizedGesturePolicyDecision(
|
|
165
168
|
kind: SynthesizedGesturePolicyKind,
|
|
166
169
|
context: SynthesizedCoordinateContext?,
|
|
@@ -2,6 +2,7 @@ import XCTest
|
|
|
2
2
|
import AgentDeviceSnapshotPresentation
|
|
3
3
|
|
|
4
4
|
extension RunnerTests {
|
|
5
|
+
@MainActor
|
|
5
6
|
func synthesizedDragAt(
|
|
6
7
|
app: XCUIApplication,
|
|
7
8
|
x: Double,
|
|
@@ -89,6 +90,7 @@ extension RunnerTests {
|
|
|
89
90
|
#endif
|
|
90
91
|
}
|
|
91
92
|
|
|
93
|
+
@MainActor
|
|
92
94
|
func synthesizedTapAt(
|
|
93
95
|
app: XCUIApplication,
|
|
94
96
|
x: Double,
|
|
@@ -276,6 +278,7 @@ extension RunnerTests {
|
|
|
276
278
|
#endif
|
|
277
279
|
}
|
|
278
280
|
|
|
281
|
+
@MainActor
|
|
279
282
|
func axFreeSynthesizedDragPlan(
|
|
280
283
|
app: XCUIApplication,
|
|
281
284
|
x: Double,
|
|
@@ -364,12 +367,13 @@ extension RunnerTests {
|
|
|
364
367
|
)
|
|
365
368
|
}
|
|
366
369
|
|
|
370
|
+
@MainActor
|
|
367
371
|
func synthesizedCoordinateContext(
|
|
368
372
|
app: XCUIApplication,
|
|
369
373
|
policy: SynthesizedGesturePolicy
|
|
370
374
|
) -> SynthesizedCoordinateContext? {
|
|
371
375
|
#if os(iOS)
|
|
372
|
-
let health =
|
|
376
|
+
let health = mainOwned.accessibilityHealth
|
|
373
377
|
let resolved = resolveRunnerWindow(app: app)
|
|
374
378
|
guard let window = resolved.window else {
|
|
375
379
|
return nil
|
|
@@ -82,11 +82,11 @@ extension RunnerTests {
|
|
|
82
82
|
text: String,
|
|
83
83
|
delaySeconds: Double
|
|
84
84
|
) -> [SynthesizedReplacementStep] {
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
guard synthesizedReplacementIsSpaced(characterCount: text.count, delaySeconds: delaySeconds)
|
|
86
|
+
else {
|
|
87
87
|
return [SynthesizedReplacementStep(text: text, replacesExistingText: true)]
|
|
88
88
|
}
|
|
89
|
-
return
|
|
89
|
+
return Array(text).enumerated().map { index, character in
|
|
90
90
|
SynthesizedReplacementStep(
|
|
91
91
|
text: String(character),
|
|
92
92
|
replacesExistingText: index == 0
|
|
@@ -94,11 +94,86 @@ extension RunnerTests {
|
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
static func synthesizedReplacementIsSpaced(characterCount: Int, delaySeconds: Double) -> Bool {
|
|
100
|
+
delaySeconds > 0 && characterCount > 1
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
enum SynthesizedDeliveryBudget {
|
|
107
|
+
|
|
108
|
+
static var characterInterval: TimeInterval {
|
|
109
|
+
1.0 / Double(RunnerSynthesizedTextEntry.typingSpeedCharactersPerSecond())
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
static func projectedSeconds(
|
|
118
|
+
textLength: Int,
|
|
119
|
+
delaySeconds: TimeInterval,
|
|
120
|
+
typeWarmup: Bool = false
|
|
121
|
+
) -> TimeInterval {
|
|
122
|
+
let spaced = synthesizedReplacementIsSpaced(characterCount: textLength, delaySeconds: delaySeconds)
|
|
123
|
+
let warmupSplit = typeWarmup && textLength > 1 && !spaced
|
|
124
|
+
let calls = spaced ? textLength : (warmupSplit ? 2 : 1)
|
|
125
|
+
return Double(textLength) * characterInterval
|
|
126
|
+
+ Double(calls) * TextEntryTiming.synthesizeCallOverhead
|
|
127
|
+
+ Double(calls - 1) * delaySeconds
|
|
128
|
+
+ (warmupSplit ? TextEntryTiming.pollInterval : 0)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
static func exceeds(
|
|
132
|
+
textLength: Int,
|
|
133
|
+
delaySeconds: TimeInterval,
|
|
134
|
+
typeWarmup: Bool = false
|
|
135
|
+
) -> Bool {
|
|
136
|
+
projectedSeconds(textLength: textLength, delaySeconds: delaySeconds, typeWarmup: typeWarmup)
|
|
137
|
+
> TextEntryTiming.synthesizedDeliveryCeiling
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
static func maxTextLength(delaySeconds: TimeInterval) -> Int {
|
|
142
|
+
var length = 1
|
|
143
|
+
while !exceeds(textLength: length + 1, delaySeconds: delaySeconds) {
|
|
144
|
+
length += 1
|
|
145
|
+
}
|
|
146
|
+
return length
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@MainActor
|
|
97
151
|
func runSynthesizedReplacementRoute(
|
|
98
152
|
_ request: SynthesizedReplacementRequest
|
|
99
153
|
) -> SynthesizedReplacementRouteOutcome {
|
|
100
154
|
#if os(iOS)
|
|
101
155
|
NSLog("AGENT_DEVICE_RUNNER_TEXT_ENTRY_ROUTE route=synthesized-first-responder-replacement")
|
|
156
|
+
if SynthesizedDeliveryBudget.exceeds(
|
|
157
|
+
textLength: request.text.count,
|
|
158
|
+
delaySeconds: request.delaySeconds
|
|
159
|
+
) {
|
|
160
|
+
NSLog(
|
|
161
|
+
"AGENT_DEVICE_RUNNER_TEXT_ENTRY_ROUTE route=synthesized-first-responder-replacement "
|
|
162
|
+
+ "reason=delivery-budget-refused chars=%d budgetChars=%d",
|
|
163
|
+
request.text.count,
|
|
164
|
+
SynthesizedDeliveryBudget.maxTextLength(delaySeconds: request.delaySeconds)
|
|
165
|
+
)
|
|
166
|
+
return .completed(
|
|
167
|
+
TextEntryResult(
|
|
168
|
+
verified: nil,
|
|
169
|
+
repaired: false,
|
|
170
|
+
expectedText: request.text,
|
|
171
|
+
observedText: nil,
|
|
172
|
+
textEntryRoute: "synthesized-first-responder-replacement",
|
|
173
|
+
failure: .synthesisBudgetExceeded
|
|
174
|
+
)
|
|
175
|
+
)
|
|
176
|
+
}
|
|
102
177
|
let steps = Self.synthesizedReplacementSteps(
|
|
103
178
|
text: request.text,
|
|
104
179
|
delaySeconds: request.delaySeconds
|
package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift
CHANGED
|
@@ -8,6 +8,7 @@ extension RunnerTests {
|
|
|
8
8
|
case notFocused = "TEXT_INPUT_NOT_FOCUSED"
|
|
9
9
|
case synthesisUnavailable = "TEXT_INPUT_SYNTHESIS_UNAVAILABLE"
|
|
10
10
|
case commitNotObserved = "TEXT_INPUT_COMMIT_NOT_OBSERVED"
|
|
11
|
+
case synthesisBudgetExceeded = "TEXT_INPUT_SYNTHESIS_BUDGET_EXCEEDED"
|
|
11
12
|
|
|
12
13
|
var message: String {
|
|
13
14
|
switch self {
|
|
@@ -17,6 +18,8 @@ extension RunnerTests {
|
|
|
17
18
|
return "Reliable text synthesis is unavailable while the software keyboard is hidden."
|
|
18
19
|
case .commitNotObserved:
|
|
19
20
|
return "The runner could not confirm the typed text reached the field."
|
|
21
|
+
case .synthesisBudgetExceeded:
|
|
22
|
+
return "The text is longer than one runner command can type at this pace."
|
|
20
23
|
}
|
|
21
24
|
}
|
|
22
25
|
|
|
@@ -27,7 +30,13 @@ extension RunnerTests {
|
|
|
27
30
|
case .synthesisUnavailable:
|
|
28
31
|
return "Show the software keyboard, then retry type."
|
|
29
32
|
case .commitNotObserved:
|
|
30
|
-
return "The field may hold none, part, or all of the text. Run snapshot -i and inspect the field: if it already matches, continue; otherwise retry fill with the full text quoted and --delay-ms
|
|
33
|
+
return "The field may hold none, part, or all of the text. Run snapshot -i and inspect the field: if it already matches, continue; otherwise retry fill with the full text quoted and --delay-ms \(TextEntryTiming.recoveryDelayMilliseconds). Do not use type, which appends to whatever committed."
|
|
34
|
+
case .synthesisBudgetExceeded:
|
|
35
|
+
let recoveryDelay = TextEntryTiming.recoveryDelayMilliseconds
|
|
36
|
+
let recoveryBudget = SynthesizedDeliveryBudget.maxTextLength(
|
|
37
|
+
delaySeconds: Double(recoveryDelay) / 1000
|
|
38
|
+
)
|
|
39
|
+
return "Fill at most \(SynthesizedDeliveryBudget.maxTextLength(delaySeconds: 0)) characters at a time without --delay-ms and append the rest with separate type commands, keeping each command inside that budget. --delay-ms lowers the budget, because each character then gets its own synthesize call and each gap between characters pays the delay: \(recoveryBudget) characters at --delay-ms \(recoveryDelay). A longer timeout does not help: this route is chosen when the accessibility channel is already degraded, and the pace is what makes the text long."
|
|
31
40
|
}
|
|
32
41
|
}
|
|
33
42
|
}
|
|
@@ -51,8 +60,29 @@ extension RunnerTests {
|
|
|
51
60
|
static let synthesizedCommitStallTimeout: TimeInterval = 3.0
|
|
52
61
|
|
|
53
62
|
|
|
54
|
-
|
|
55
63
|
static let synthesizedCommitCeiling: TimeInterval = 10.0
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
static let synthesizedReplacementFocusAllowance: TimeInterval = 2.0
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
static let synthesizedDeliveryCeiling: TimeInterval = RunnerTests.mainThreadExecutionTimeout
|
|
72
|
+
- synthesizedReplacementFocusAllowance
|
|
73
|
+
- synthesizedCommitCeiling
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
static let synthesizedAcknowledgeWindowSeconds: TimeInterval = 0.04
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
static let synthesizeCallOverhead: TimeInterval = 0.15
|
|
84
|
+
|
|
85
|
+
static let recoveryDelayMilliseconds = 80
|
|
56
86
|
static let synthesizedCommitPollInterval: TimeInterval = 0.2
|
|
57
87
|
}
|
|
58
88
|
|
|
@@ -6,6 +6,7 @@ import XCTest
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
extension RunnerTests {
|
|
9
|
+
@MainActor
|
|
9
10
|
func rememberTextEntryTap(_ element: XCUIElement?) {
|
|
10
11
|
guard let element, isTextEntryElement(element) else {
|
|
11
12
|
clearRememberedTextEntryTap()
|
|
@@ -13,8 +14,8 @@ extension RunnerTests {
|
|
|
13
14
|
}
|
|
14
15
|
textEntryTapWitness = TextEntryTapWitness(
|
|
15
16
|
element: element,
|
|
16
|
-
bundleId:
|
|
17
|
-
processIdentifier:
|
|
17
|
+
bundleId: mainOwned.bundleId,
|
|
18
|
+
processIdentifier: mainOwned.processIdentifier
|
|
18
19
|
)
|
|
19
20
|
}
|
|
20
21
|
|
|
@@ -22,6 +23,7 @@ extension RunnerTests {
|
|
|
22
23
|
textEntryTapWitness = nil
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
@MainActor
|
|
25
27
|
private func rememberedTextEntryTarget() -> TextEntryTarget? {
|
|
26
28
|
guard let witness = textEntryTapWitness else {
|
|
27
29
|
return nil
|
|
@@ -30,8 +32,8 @@ extension RunnerTests {
|
|
|
30
32
|
|
|
31
33
|
clearRememberedTextEntryTap()
|
|
32
34
|
guard witness.matches(
|
|
33
|
-
bundleId:
|
|
34
|
-
processIdentifier:
|
|
35
|
+
bundleId: mainOwned.bundleId,
|
|
36
|
+
processIdentifier: mainOwned.processIdentifier
|
|
35
37
|
) else {
|
|
36
38
|
return nil
|
|
37
39
|
}
|
|
@@ -80,6 +82,7 @@ extension RunnerTests {
|
|
|
80
82
|
#endif
|
|
81
83
|
}
|
|
82
84
|
|
|
85
|
+
@MainActor
|
|
83
86
|
func focusTextInputForTextEntry(app: XCUIApplication, x: Double?, y: Double?) -> TextEntryTarget {
|
|
84
87
|
guard let x, let y else {
|
|
85
88
|
let softwareKeyboardVisible = isKeyboardVisible(app: app)
|