agent-device 0.21.20 → 0.21.22
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.20.apk → agent-device-android-ime-helper-0.21.22.apk} +0 -0
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.22.apk.sha256 +1 -0
- package/android/ime-helper/dist/{agent-device-android-ime-helper-0.21.20.manifest.json → agent-device-android-ime-helper-0.21.22.manifest.json} +4 -4
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.22.apk +0 -0
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.22.apk.sha256 +1 -0
- package/android/snapshot-helper/dist/{agent-device-android-snapshot-helper-0.21.20.manifest.json → agent-device-android-snapshot-helper-0.21.22.manifest.json} +6 -6
- package/apple/macos-helper/Sources/AgentDeviceMacOSHelper/AppWindowScreenshot.swift +73 -0
- package/apple/macos-helper/Sources/AgentDeviceMacOSHelper/BackgroundInteraction.swift +557 -0
- package/apple/macos-helper/Sources/AgentDeviceMacOSHelper/GhostCursor.swift +141 -0
- package/apple/macos-helper/Sources/AgentDeviceMacOSHelper/SnapshotTraversal.swift +107 -3
- package/apple/macos-helper/Sources/AgentDeviceMacOSHelper/main.swift +136 -22
- package/apple/macos-helper/Tests/AgentDeviceMacOSHelperTests/BackgroundInteractionTests.swift +88 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +121 -4
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.h +10 -2
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m +36 -9
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +28 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +27 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedCommitDeadline.swift +6 -1
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedGesturePolicy.swift +7 -2
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedInteraction.swift +21 -7
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedTextEntry.swift +121 -17
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +179 -6
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryConfirmation.swift +119 -0
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryFocus.swift +8 -1
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntryReadiness.swift +23 -11
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextInputProbe.swift +59 -6
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextTyping.swift +112 -23
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TypeExecution.swift +25 -12
- package/dist/apple/runner/AgentDeviceRunner/RecordingScripts/RecordingExportSupport.swift +6 -1
- package/dist/apple/runner/AgentDeviceRunner/RecordingScripts/recording-overlay.swift +43 -13
- package/dist/src/adapter.js +1 -1
- package/dist/src/adb-executor.js +2 -2
- package/dist/src/adb-failure.js +1 -1
- package/dist/src/adb-transport.js +1 -1
- 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/ai-sdk.js +1 -1
- package/dist/src/alert-runtime.js +1 -1
- package/dist/src/alert.js +1 -1
- package/dist/src/android-adb.d.ts +12 -0
- package/dist/src/android-input-ownership.js +1 -1
- package/dist/src/app-backend.js +1 -0
- 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 +2 -2
- package/dist/src/app-lifecycle2.js +1 -1
- 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-log-session-resource.js +1 -0
- 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 +2 -2
- 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/back-mode.js +1 -1
- package/dist/src/backend-snapshot-options.js +1 -1
- package/dist/src/boot-diagnostics.js +1 -1
- package/dist/src/cache.js +1 -1
- package/dist/src/capabilities.js +1 -1
- package/dist/src/capture.js +1 -1
- package/dist/src/cli-config.js +1 -1
- package/dist/src/cli-help.js +13 -5
- package/dist/src/cli.js +4 -4
- package/dist/src/click-button.js +1 -1
- package/dist/src/client-metro-companion.js +1 -1
- package/dist/src/client-metro.js +1 -1
- package/dist/src/client-options.js +3 -0
- package/dist/src/client-types.d.ts +88 -5
- package/dist/src/clipboard-shell-response.js +1 -1
- package/dist/src/cloud-artifacts.d.ts +7 -2
- package/dist/src/code-signature-cache.js +1 -1
- package/dist/src/code-signature.js +1 -1
- package/dist/src/command-schema.js +1 -1
- package/dist/src/command-tools.js +2 -2
- package/dist/src/connection-runtime.js +1 -1
- package/dist/src/connection.js +5 -5
- package/dist/src/contracts.d.ts +1 -1
- package/dist/src/contracts.js +1 -1
- package/dist/src/daemon-artifacts.js +1 -0
- 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 -0
- package/dist/src/daemon-client-transport.js +1 -1
- package/dist/src/daemon-http.js +1 -0
- package/dist/src/daemon-policy-file.js +1 -1
- package/dist/src/daemon-registration-owner.js +1 -0
- package/dist/src/daemon-registration.js +1 -1
- package/dist/src/daemon-resolution.js +1 -1
- package/dist/src/daemon-shutdown-report.js +1 -1
- package/dist/src/daemon.js +2 -2
- package/dist/src/debug-symbols-facade.js +1 -1
- package/dist/src/device-claim-inspection.js +1 -1
- package/dist/src/device-claim-rule.js +1 -1
- 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-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/device.js +1 -1
- package/dist/src/device2.js +1 -3
- package/dist/src/device3.js +3 -0
- package/dist/src/devicectl.js +1 -1
- package/dist/src/diagnostics.js +3 -3
- package/dist/src/dispatch-resolve.js +4 -1
- package/dist/src/display-inventory.js +1 -1
- package/dist/src/doctor-facade.js +1 -1
- package/dist/src/doctor2.js +1 -1
- package/dist/src/durable-capture-resource.js +1 -0
- 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/generic-settle.js +1 -1
- package/dist/src/generic.js +1 -1
- package/dist/src/gesture-admission.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/host-file.js +1 -1
- package/dist/src/host.js +1 -1
- package/dist/src/human-control-contract.js +1 -1
- package/dist/src/human-control.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-artifact-facade.js +1 -1
- package/dist/src/install-source-network-transport.js +1 -1
- package/dist/src/install-source.d.ts +5 -0
- package/dist/src/install-source2.js +1 -1
- package/dist/src/instance-client.js +1 -0
- package/dist/src/interaction-positionals.js +1 -1
- package/dist/src/interaction-ref-policy.js +1 -1
- package/dist/src/interaction.js +1 -1
- package/dist/src/interaction2.js +1 -1
- package/dist/src/interactor-operation-binding.js +1 -1
- package/dist/src/interactor.js +5 -5
- 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 +1 -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 +4 -4
- 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-client.js +4 -30
- package/dist/src/ios-settings.js +1 -0
- package/dist/src/ios-snapshot-engine.js +1 -1
- package/dist/src/ios-snapshot-runtime.js +1 -1
- package/dist/src/launch-confirmation.js +1 -1
- package/dist/src/lease-context.js +1 -1
- package/dist/src/lease-registry-scope.js +1 -0
- package/dist/src/lease-scope.js +1 -1
- package/dist/src/lease.js +1 -1
- package/dist/src/legacy-xctest-device-set.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 +28 -2
- package/dist/src/limrun.js +1 -1
- package/dist/src/linux.js +1 -1
- package/dist/src/load.js +1 -0
- package/dist/src/location-coordinates.js +1 -1
- package/dist/src/macos-facade.js +1 -1
- package/dist/src/managed-device-scope.js +1 -1
- package/dist/src/mechanics.js +16 -16
- package/dist/src/observation.js +1 -1
- package/dist/src/observe-until.js +1 -1
- package/dist/src/overlay.js +2 -2
- package/dist/src/owner-identity.js +3 -3
- package/dist/src/path-resolution.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-request-scope.js +1 -1
- package/dist/src/platform-runtime-android-adb-host.js +1 -1
- package/dist/src/platform-runtime-app-log-process.js +1 -1
- package/dist/src/platform-runtime-host.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 +2 -2
- 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-finalizer-host.js +1 -1
- package/dist/src/platform-runtime-screen-recording-output-host.js +1 -1
- package/dist/src/platform-runtime-screen-recording-process-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/plugins.d.ts +9 -0
- package/dist/src/plugins.js +1 -0
- package/dist/src/plugins2.js +2 -0
- 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/process.js +1 -1
- package/dist/src/project-runtime.js +1 -1
- package/dist/src/provider-credential-fingerprint.js +1 -0
- package/dist/src/provider-device-admission.js +1 -1
- package/dist/src/provider-device-runtimes.js +1 -0
- package/dist/src/provider-limrun-credentials.js +1 -0
- package/dist/src/provider-profile-fields.js +1 -0
- package/dist/src/provider-session-ownership.js +2 -2
- package/dist/src/provider-webdriver.js +2 -1
- package/dist/src/providers.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/record-trace.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 +39 -39
- package/dist/src/remote-config2.js +1 -1
- package/dist/src/replay-divergence.js +1 -1
- package/dist/src/replay.js +2 -2
- package/dist/src/reporting.js +5 -5
- package/dist/src/request-admission.js +1 -1
- package/dist/src/request-cancel.js +1 -1
- package/dist/src/request-cancellation.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-artifact-manifest.js +1 -1
- package/dist/src/runner-cache-metadata.js +3 -3
- 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-exchange.js +1 -1
- package/dist/src/runner-operations-facade.js +1 -1
- package/dist/src/runner-session-types.js +1 -1
- package/dist/src/runner-start-budget.js +1 -1
- package/dist/src/runner.js +1 -1
- package/dist/src/runtime-admission.js +1 -1
- package/dist/src/runtime-transport-hints.js +1 -1
- package/dist/src/runtime.js +1 -1
- package/dist/src/runtime10.js +2 -2
- package/dist/src/runtime2.js +3 -3
- 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/screen-recording-transport.js +1 -1
- package/dist/src/screenshot-density.js +1 -1
- package/dist/src/screenshot-image.js +1 -1
- package/dist/src/screenshot-result.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.js +1 -1
- package/dist/src/scroll-gesture.js +1 -1
- package/dist/src/sdk-batch-runner.js +2 -2
- package/dist/src/sdk-batch.js +2 -2
- package/dist/src/sdk-contracts.d.ts +45 -2
- package/dist/src/sdk-contracts.js +2 -2
- package/dist/src/sdk-device.js +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 +33 -4
- package/dist/src/selection.js +0 -0
- package/dist/src/selector-runtime.js +1 -1
- package/dist/src/selectors.d.ts +2 -2
- package/dist/src/selectors.js +1 -1
- package/dist/src/server.js +1 -1
- package/dist/src/session-allocation.js +1 -1
- package/dist/src/session-artifact-paths.js +1 -1
- package/dist/src/session-repair-tombstone.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-stream.js +27 -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-facade.js +1 -0
- package/dist/src/simctl-settings.js +1 -0
- package/dist/src/simctl.js +1 -1
- package/dist/src/simulator.js +1 -1
- package/dist/src/snapshot-diagnostics.js +1 -1
- 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/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/src7.js +1 -1
- package/dist/src/src8.js +2 -2
- package/dist/src/store.js +1 -0
- 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/target-evidence.js +1 -1
- package/dist/src/telemetry.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/toolchain-probe.js +2 -2
- package/dist/src/touch-runtime.js +1 -1
- package/dist/src/touch-runtime2.js +1 -1
- package/dist/src/transport.js +2 -2
- package/dist/src/tv-remote.js +1 -1
- package/dist/src/type-text-runtime.js +1 -1
- package/dist/src/update-check.js +1 -1
- package/dist/src/vars.js +1 -1
- package/dist/src/verified-file.js +1 -1
- package/dist/src/version.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 +5 -1
- package/server.json +2 -2
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.21.20.apk.sha256 +0 -1
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.20.apk +0 -0
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.21.20.apk.sha256 +0 -1
- package/dist/src/api.js +0 -3
- package/dist/src/daemon-process.js +0 -1
- package/dist/src/multitouch-support.js +0 -1
- package/dist/src/parameterized-recorded-fill.js +0 -1
- package/dist/src/runner-startup-transport.js +0 -1
- package/dist/src/runner-xctestrun.js +0 -1
- package/dist/src/session-store.js +0 -4
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 clearNotObserved = "TEXT_INPUT_CLEAR_NOT_OBSERVED"
|
|
11
12
|
case synthesisBudgetExceeded = "TEXT_INPUT_SYNTHESIS_BUDGET_EXCEEDED"
|
|
12
13
|
|
|
13
14
|
var message: String {
|
|
@@ -18,6 +19,8 @@ extension RunnerTests {
|
|
|
18
19
|
return "Reliable text synthesis is unavailable while the software keyboard is hidden."
|
|
19
20
|
case .commitNotObserved:
|
|
20
21
|
return "The runner could not confirm the typed text reached the field."
|
|
22
|
+
case .clearNotObserved:
|
|
23
|
+
return "The runner could not clear the field before typing, so it typed nothing."
|
|
21
24
|
case .synthesisBudgetExceeded:
|
|
22
25
|
return "The text is longer than one runner command can type at this pace."
|
|
23
26
|
}
|
|
@@ -31,6 +34,8 @@ extension RunnerTests {
|
|
|
31
34
|
return "Show the software keyboard, then retry type."
|
|
32
35
|
case .commitNotObserved:
|
|
33
36
|
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."
|
|
37
|
+
case .clearNotObserved:
|
|
38
|
+
return "The field may still hold some or all of its old text. Run snapshot -i and inspect the field, then retry fill. Do not use type, which appends to whatever the field holds."
|
|
34
39
|
case .synthesisBudgetExceeded:
|
|
35
40
|
let recoveryDelay = TextEntryTiming.recoveryDelayMilliseconds
|
|
36
41
|
let recoveryBudget = SynthesizedDeliveryBudget.maxTextLength(
|
|
@@ -61,13 +66,24 @@ extension RunnerTests {
|
|
|
61
66
|
|
|
62
67
|
|
|
63
68
|
|
|
69
|
+
static let unconfirmedSettleCeiling: TimeInterval = 1.0
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
64
73
|
static let synthesizedCommitStallTimeout: TimeInterval = 3.0
|
|
65
74
|
|
|
66
75
|
|
|
67
76
|
static let synthesizedCommitCeiling: TimeInterval = 10.0
|
|
68
77
|
|
|
69
78
|
|
|
70
|
-
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
static let synthesizedReplacementFocusAllowance: TimeInterval = 4.0
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
static let synthesizedClearReadAllowance: TimeInterval = 0.9
|
|
71
87
|
|
|
72
88
|
|
|
73
89
|
|
|
@@ -108,6 +124,27 @@ extension RunnerTests {
|
|
|
108
124
|
let observedText: String?
|
|
109
125
|
var textEntryRoute: String? = nil
|
|
110
126
|
var failure: TextEntryFailure? = nil
|
|
127
|
+
var unconfirmed: TextEntryUnconfirmedEvidence? = nil
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
struct TextEntryInputIdentity: Equatable {
|
|
136
|
+
let elementType: XCUIElement.ElementType
|
|
137
|
+
let identifier: String
|
|
138
|
+
|
|
139
|
+
var isDistinguishable: Bool { !identifier.isEmpty }
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
struct TextInputAtPoint {
|
|
146
|
+
let element: XCUIElement
|
|
147
|
+
let identity: TextEntryInputIdentity
|
|
111
148
|
}
|
|
112
149
|
|
|
113
150
|
struct TextEntryTarget {
|
|
@@ -116,33 +153,81 @@ extension RunnerTests {
|
|
|
116
153
|
let prefersFocusedElement: Bool
|
|
117
154
|
let fromTapWitness: Bool
|
|
118
155
|
|
|
156
|
+
let boundIdentity: TextEntryInputIdentity?
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
let inputAtRefreshPoint: TextInputAtPoint?
|
|
163
|
+
|
|
119
164
|
init(
|
|
120
165
|
element: XCUIElement?,
|
|
121
166
|
refreshPoint: CGPoint?,
|
|
122
167
|
prefersFocusedElement: Bool,
|
|
123
|
-
fromTapWitness: Bool = false
|
|
168
|
+
fromTapWitness: Bool = false,
|
|
169
|
+
boundIdentity: TextEntryInputIdentity? = nil,
|
|
170
|
+
inputAtRefreshPoint: TextInputAtPoint? = nil
|
|
124
171
|
) {
|
|
125
172
|
self.element = element
|
|
126
173
|
self.refreshPoint = refreshPoint
|
|
127
174
|
self.prefersFocusedElement = prefersFocusedElement
|
|
128
175
|
self.fromTapWitness = fromTapWitness
|
|
176
|
+
self.boundIdentity = boundIdentity ?? inputAtRefreshPoint?.identity
|
|
177
|
+
self.inputAtRefreshPoint = inputAtRefreshPoint
|
|
129
178
|
}
|
|
130
179
|
|
|
131
180
|
func withElement(_ nextElement: XCUIElement?) -> TextEntryTarget {
|
|
132
181
|
guard let nextElement else {
|
|
133
182
|
return self
|
|
134
183
|
}
|
|
135
|
-
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
let snapshot = nextElement.exists ? try? nextElement.snapshot() : nil
|
|
188
|
+
let frame = snapshot?.frame ?? .zero
|
|
136
189
|
let point = frame.isEmpty ? refreshPoint : CGPoint(x: frame.midX, y: frame.midY)
|
|
137
190
|
return TextEntryTarget(
|
|
138
191
|
element: nextElement,
|
|
139
192
|
refreshPoint: point,
|
|
140
193
|
prefersFocusedElement: prefersFocusedElement,
|
|
141
|
-
fromTapWitness: fromTapWitness
|
|
194
|
+
fromTapWitness: fromTapWitness,
|
|
195
|
+
boundIdentity: boundIdentity ?? snapshot.map {
|
|
196
|
+
TextEntryInputIdentity(elementType: $0.elementType, identifier: $0.identifier)
|
|
197
|
+
},
|
|
198
|
+
inputAtRefreshPoint: inputAtRefreshPoint
|
|
142
199
|
)
|
|
143
200
|
}
|
|
144
201
|
}
|
|
145
202
|
|
|
203
|
+
|
|
204
|
+
enum TextEntryInputProbe: Equatable {
|
|
205
|
+
case input(TextEntryInputIdentity)
|
|
206
|
+
|
|
207
|
+
case noMatch
|
|
208
|
+
|
|
209
|
+
case unavailable
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
static let xCTestUITestingErrorDomain = "com.apple.dt.xctest.ui-testing.error"
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
static let xCTestNoMatchesErrorCode = 10008
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
static func textEntryInputProbe(snapshotError error: Error) -> TextEntryInputProbe {
|
|
219
|
+
let error = error as NSError
|
|
220
|
+
return error.domain == xCTestUITestingErrorDomain && error.code == xCTestNoMatchesErrorCode
|
|
221
|
+
? .noMatch
|
|
222
|
+
: .unavailable
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
static func textEntryRepairCanTarget(boundIdentity: TextEntryInputIdentity?) -> Bool {
|
|
228
|
+
boundIdentity?.isDistinguishable ?? true
|
|
229
|
+
}
|
|
230
|
+
|
|
146
231
|
struct TextEntryStabilization {
|
|
147
232
|
let element: XCUIElement?
|
|
148
233
|
let focusConfirmed: Bool
|
|
@@ -199,7 +284,95 @@ extension RunnerTests {
|
|
|
199
284
|
}
|
|
200
285
|
}
|
|
201
286
|
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
|
|
202
290
|
func resolveTextEntryElement(app: XCUIApplication, target: TextEntryTarget) -> XCUIElement? {
|
|
291
|
+
guard let boundIdentity = target.boundIdentity else {
|
|
292
|
+
return resolveUnboundTextEntryElement(app: app, target: target)
|
|
293
|
+
}
|
|
294
|
+
func isBoundInput(_ candidate: XCUIElement) -> Bool {
|
|
295
|
+
candidate.exists && probeTextEntryInput(candidate) == .input(boundIdentity)
|
|
296
|
+
}
|
|
297
|
+
if target.prefersFocusedElement, let focused = focusedTextInput(app: app), isBoundInput(focused) {
|
|
298
|
+
return focused
|
|
299
|
+
}
|
|
300
|
+
if let element = target.element, isBoundInput(element) {
|
|
301
|
+
return element
|
|
302
|
+
}
|
|
303
|
+
if let input = target.inputAtRefreshPoint, textInputStillResolves(input) {
|
|
304
|
+
return input.element
|
|
305
|
+
}
|
|
306
|
+
if target.inputAtRefreshPoint == nil,
|
|
307
|
+
let refreshPoint = target.refreshPoint,
|
|
308
|
+
case .matches(let candidates) = probeTextInputs(app: app, point: refreshPoint),
|
|
309
|
+
let match = candidates.first(where: isBoundInput) {
|
|
310
|
+
return match
|
|
311
|
+
}
|
|
312
|
+
if let focused = focusedTextInput(app: app), isBoundInput(focused) {
|
|
313
|
+
return focused
|
|
314
|
+
}
|
|
315
|
+
if let byIdentifier = boundTextEntryInputQuery(app: app, identity: boundIdentity), isBoundInput(byIdentifier) {
|
|
316
|
+
return byIdentifier
|
|
317
|
+
}
|
|
318
|
+
return nil
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
func boundTextEntryInputIsGone(app: XCUIApplication, target: TextEntryTarget) -> Bool {
|
|
326
|
+
guard let boundIdentity = target.boundIdentity,
|
|
327
|
+
let query = boundTextEntryInputQuery(app: app, identity: boundIdentity) ?? target.element
|
|
328
|
+
else {
|
|
329
|
+
return false
|
|
330
|
+
}
|
|
331
|
+
switch probeTextEntryInput(query) {
|
|
332
|
+
case .input(let identity) where identity != boundIdentity:
|
|
333
|
+
break
|
|
334
|
+
case .noMatch:
|
|
335
|
+
break
|
|
336
|
+
case .input, .unavailable:
|
|
337
|
+
return false
|
|
338
|
+
}
|
|
339
|
+
guard let refreshPoint = target.refreshPoint else {
|
|
340
|
+
return true
|
|
341
|
+
}
|
|
342
|
+
switch probeTextInputs(app: app, point: refreshPoint) {
|
|
343
|
+
case .absent:
|
|
344
|
+
return true
|
|
345
|
+
case .matches(let candidates):
|
|
346
|
+
return candidates.allSatisfy { probeTextEntryInput($0) != .input(boundIdentity) }
|
|
347
|
+
case .unavailable:
|
|
348
|
+
return false
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
private func boundTextEntryInputQuery(app: XCUIApplication, identity: TextEntryInputIdentity) -> XCUIElement? {
|
|
354
|
+
guard identity.isDistinguishable else {
|
|
355
|
+
return nil
|
|
356
|
+
}
|
|
357
|
+
return app.descendants(matching: identity.elementType).matching(identifier: identity.identifier).element
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
func probeTextEntryInput(_ element: XCUIElement) -> TextEntryInputProbe {
|
|
362
|
+
var probe = TextEntryInputProbe.unavailable
|
|
363
|
+
let (_, exception) = catchingObjCException(fallback: ()) {
|
|
364
|
+
do {
|
|
365
|
+
let snapshot = try element.snapshot()
|
|
366
|
+
probe = .input(TextEntryInputIdentity(elementType: snapshot.elementType, identifier: snapshot.identifier))
|
|
367
|
+
} catch {
|
|
368
|
+
probe = Self.textEntryInputProbe(snapshotError: error)
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return exception == nil ? probe : .unavailable
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
private func resolveUnboundTextEntryElement(app: XCUIApplication, target: TextEntryTarget) -> XCUIElement? {
|
|
203
376
|
if target.prefersFocusedElement {
|
|
204
377
|
if let focused = focusedTextInput(app: app) {
|
|
205
378
|
return focused
|
|
@@ -251,7 +424,7 @@ extension RunnerTests {
|
|
|
251
424
|
}
|
|
252
425
|
|
|
253
426
|
func editableTextValue(
|
|
254
|
-
for element:
|
|
427
|
+
for element: (any XCUIElementAttributes)?,
|
|
255
428
|
treatingPlaceholderAsEmpty: Bool = false
|
|
256
429
|
) -> String? {
|
|
257
430
|
guard let element else {
|
|
@@ -271,7 +444,7 @@ extension RunnerTests {
|
|
|
271
444
|
}
|
|
272
445
|
}
|
|
273
446
|
|
|
274
|
-
private func isPlaceholderValue(_ value: String, for element:
|
|
447
|
+
private func isPlaceholderValue(_ value: String, for element: any XCUIElementAttributes) -> Bool {
|
|
275
448
|
if Self.textMatchesPlaceholder(value, placeholder: element.placeholderValue) {
|
|
276
449
|
return true
|
|
277
450
|
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import XCTest
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
extension RunnerTests {
|
|
7
|
+
|
|
8
|
+
struct TextEntryElementIdentity: Equatable {
|
|
9
|
+
let identifier: String?
|
|
10
|
+
let elementType: String
|
|
11
|
+
let frame: CGRect
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
func isSameElement(as other: TextEntryElementIdentity) -> Bool {
|
|
16
|
+
if identifier != nil || other.identifier != nil {
|
|
17
|
+
return identifier == other.identifier
|
|
18
|
+
}
|
|
19
|
+
return elementType == other.elementType && frame == other.frame
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
struct TextEntryObservation: Equatable {
|
|
25
|
+
let value: String
|
|
26
|
+
let identity: TextEntryElementIdentity
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
func isSettled(with other: TextEntryObservation) -> Bool {
|
|
30
|
+
value == other.value && identity.isSameElement(as: other.identity)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
struct TextEntryUnconfirmedEvidence: Equatable {
|
|
36
|
+
let requested: String
|
|
37
|
+
let before: String
|
|
38
|
+
let after: String
|
|
39
|
+
let target: TextEntryElementIdentity
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
static func textEntryValueEchoes(observed: String, expected: String, baseline: String) -> Bool {
|
|
48
|
+
let request = textEntryRequestWithoutSubmitKeys(expected)
|
|
49
|
+
let residualAndRequest = baseline + request
|
|
50
|
+
return isOrderedSubsequence(observed, of: residualAndRequest)
|
|
51
|
+
|| (isOrderedSubsequence(request, of: observed) && !isOrderedSubsequence(request, of: baseline))
|
|
52
|
+
|| isOrderedSubsequence(residualAndRequest, of: observed)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
static func unconfirmedTextEntryEvidence(
|
|
59
|
+
requested: String,
|
|
60
|
+
baseline: TextEntryObservation?,
|
|
61
|
+
observed: TextEntryObservation?
|
|
62
|
+
) -> TextEntryUnconfirmedEvidence? {
|
|
63
|
+
guard !textEntryRequestWithoutSubmitKeys(requested).isEmpty,
|
|
64
|
+
let baseline,
|
|
65
|
+
let observed,
|
|
66
|
+
baseline.identity.isSameElement(as: observed.identity),
|
|
67
|
+
observed.value != baseline.value,
|
|
68
|
+
!textEntryValueEchoes(observed: observed.value, expected: requested, baseline: baseline.value)
|
|
69
|
+
else {
|
|
70
|
+
return nil
|
|
71
|
+
}
|
|
72
|
+
return TextEntryUnconfirmedEvidence(
|
|
73
|
+
requested: requested,
|
|
74
|
+
before: baseline.value,
|
|
75
|
+
after: observed.value,
|
|
76
|
+
target: observed.identity
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
func textEntryObservation(for element: XCUIElement?) -> TextEntryObservation? {
|
|
83
|
+
guard let element,
|
|
84
|
+
let snapshot = try? element.snapshot(),
|
|
85
|
+
let value = editableTextValue(for: snapshot, treatingPlaceholderAsEmpty: true)
|
|
86
|
+
else {
|
|
87
|
+
return nil
|
|
88
|
+
}
|
|
89
|
+
return TextEntryObservation(
|
|
90
|
+
value: value,
|
|
91
|
+
identity: TextEntryElementIdentity(
|
|
92
|
+
identifier: snapshot.identifier.isEmpty ? nil : snapshot.identifier,
|
|
93
|
+
elementType: elementTypeName(snapshot.elementType),
|
|
94
|
+
frame: snapshot.frame
|
|
95
|
+
)
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
static func textEntryRequestWithoutSubmitKeys(_ text: String) -> String {
|
|
101
|
+
var request = text
|
|
102
|
+
while request.hasSuffix("\n") || request.hasSuffix("\r") {
|
|
103
|
+
request.removeLast()
|
|
104
|
+
}
|
|
105
|
+
return request
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
static func isOrderedSubsequence(_ candidate: String, of text: String) -> Bool {
|
|
110
|
+
var remaining = text[...]
|
|
111
|
+
for character in candidate {
|
|
112
|
+
guard let match = remaining.firstIndex(of: character) else {
|
|
113
|
+
return false
|
|
114
|
+
}
|
|
115
|
+
remaining = remaining[remaining.index(after: match)...]
|
|
116
|
+
}
|
|
117
|
+
return true
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -6,9 +6,16 @@ import XCTest
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
extension RunnerTests {
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
9
16
|
@MainActor
|
|
10
17
|
func rememberTextEntryTap(_ element: XCUIElement?) {
|
|
11
|
-
guard let element
|
|
18
|
+
guard let element else {
|
|
12
19
|
clearRememberedTextEntryTap()
|
|
13
20
|
return
|
|
14
21
|
}
|
|
@@ -80,21 +80,33 @@ extension RunnerTests {
|
|
|
80
80
|
#endif
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
83
97
|
func waitForTextEntryReadinessAfterTap(app: XCUIApplication, element: XCUIElement) {
|
|
84
98
|
#if os(iOS)
|
|
85
|
-
|
|
86
|
-
case .textField, .secureTextField, .searchField, .textView:
|
|
87
|
-
if waitForFocusedTextInput(app: app, timeout: TextEntryTiming.readinessTimeout) != nil {
|
|
88
|
-
return
|
|
89
|
-
}
|
|
90
|
-
let frame = element.frame
|
|
91
|
-
if !frame.isEmpty {
|
|
92
|
-
_ = tapAt(app: app, x: frame.midX, y: frame.midY)
|
|
93
|
-
_ = waitForFocusedTextInput(app: app, timeout: TextEntryTiming.readinessTimeout)
|
|
94
|
-
}
|
|
95
|
-
default:
|
|
99
|
+
if waitForFocusedTextInput(app: app, timeout: TextEntryTiming.readinessTimeout) != nil {
|
|
96
100
|
return
|
|
97
101
|
}
|
|
102
|
+
guard let settled = safely("TEXT_ENTRY_READINESS_FRAME", { try? element.snapshot() }) else {
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
let frame = settled.frame
|
|
106
|
+
if !frame.isEmpty {
|
|
107
|
+
_ = tapAt(app: app, x: frame.midX, y: frame.midY)
|
|
108
|
+
_ = waitForFocusedTextInput(app: app, timeout: TextEntryTiming.readinessTimeout)
|
|
109
|
+
}
|
|
98
110
|
#endif
|
|
99
111
|
}
|
|
100
112
|
|
|
@@ -5,7 +5,7 @@ final class TextInputProbeIssues {
|
|
|
5
5
|
var count = 0
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
enum TextInputProbeFailure: String {
|
|
8
|
+
enum TextInputProbeFailure: String, Error {
|
|
9
9
|
case recordedIssue = "text_input_probe_recorded_issue"
|
|
10
10
|
case exception = "text_input_probe_exception"
|
|
11
11
|
}
|
|
@@ -38,7 +38,60 @@ extension RunnerTests {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
func coordinateTapTextInputIdentityAt(app: XCUIApplication, x: Double, y: Double) -> TextInputAtPoint? {
|
|
44
|
+
let probed = containingTextInputProbeIssues(fallback: nil) { shouldStop -> TextInputAtPoint? in
|
|
45
|
+
guard let element = queryTextInputs(app: app, point: CGPoint(x: x, y: y), shouldStop: shouldStop).first,
|
|
46
|
+
case .input(let identity) = probeTextEntryInput(element)
|
|
47
|
+
else {
|
|
48
|
+
return nil
|
|
49
|
+
}
|
|
50
|
+
return TextInputAtPoint(element: element, identity: identity)
|
|
51
|
+
}
|
|
52
|
+
guard case .success(let input) = probed else { return nil }
|
|
53
|
+
return input
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
func textInputStillResolves(_ input: TextInputAtPoint) -> Bool {
|
|
59
|
+
let probed = containingTextInputProbeIssues(fallback: false) { _ in
|
|
60
|
+
input.element.exists && probeTextEntryInput(input.element) == .input(input.identity)
|
|
61
|
+
}
|
|
62
|
+
guard case .success(let resolves) = probed else { return false }
|
|
63
|
+
return resolves
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
func clearedFieldRead(app: XCUIApplication, target: TextEntryTarget) -> ClearedFieldRead {
|
|
69
|
+
let probed = containingTextInputProbeIssues(fallback: ClearedFieldRead.unavailable) { _ in
|
|
70
|
+
guard let input = resolveTextEntryElement(app: app, target: target) else { return .unavailable }
|
|
71
|
+
if input.elementType == .secureTextField { return .unreadable }
|
|
72
|
+
return editableTextValue(for: input, treatingPlaceholderAsEmpty: true).map { .text($0) } ?? .unavailable
|
|
73
|
+
}
|
|
74
|
+
guard case .success(let read) = probed else { return .unavailable }
|
|
75
|
+
return read
|
|
76
|
+
}
|
|
77
|
+
|
|
41
78
|
func probeTextInputs(app: XCUIApplication, point: CGPoint) -> TextInputProbeOutcome {
|
|
79
|
+
switch containingTextInputProbeIssues(fallback: [], { shouldStop in
|
|
80
|
+
queryTextInputs(app: app, point: point, shouldStop: shouldStop)
|
|
81
|
+
}) {
|
|
82
|
+
case .success(let elements):
|
|
83
|
+
return elements.isEmpty ? .absent : .matches(elements)
|
|
84
|
+
case .failure(let failure):
|
|
85
|
+
return .unavailable(failure)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
private func containingTextInputProbeIssues<T>(
|
|
92
|
+
fallback: T,
|
|
93
|
+
_ probe: (_ shouldStop: () -> Bool) -> T
|
|
94
|
+
) -> Result<T, TextInputProbeFailure> {
|
|
42
95
|
precondition(Thread.isMainThread)
|
|
43
96
|
let issues = TextInputProbeIssues()
|
|
44
97
|
suppressedIssueLock.lock()
|
|
@@ -50,12 +103,12 @@ extension RunnerTests {
|
|
|
50
103
|
textInputProbeIssues = previous
|
|
51
104
|
suppressedIssueLock.unlock()
|
|
52
105
|
}
|
|
53
|
-
let (
|
|
54
|
-
|
|
106
|
+
let (value, exception) = catchingObjCException(fallback: fallback) {
|
|
107
|
+
probe({ self.hasTextInputProbeIssues(issues) })
|
|
55
108
|
}
|
|
56
|
-
if hasTextInputProbeIssues(issues) { return .
|
|
57
|
-
if exception != nil { return .
|
|
58
|
-
return
|
|
109
|
+
if hasTextInputProbeIssues(issues) { return .failure(.recordedIssue) }
|
|
110
|
+
if exception != nil { return .failure(.exception) }
|
|
111
|
+
return .success(value)
|
|
59
112
|
}
|
|
60
113
|
|
|
61
114
|
private func hasTextInputProbeIssues(_ scope: TextInputProbeIssues) -> Bool {
|