agent-device 0.19.3 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -70
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.0.apk +0 -0
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.0.apk.sha256 +1 -0
- package/android/ime-helper/dist/agent-device-android-ime-helper-0.20.0.manifest.json +10 -0
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.0.apk +0 -0
- package/android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.0.apk.sha256 +1 -0
- package/{android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.19.3.manifest.json → android/snapshot-helper/dist/agent-device-android-snapshot-helper-0.20.0.manifest.json} +6 -6
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.h +2 -0
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerAXSnapshotBridge.m +5 -0
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.h +5 -10
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerSynthesizedGesture.m +74 -174
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Alert.swift +113 -19
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+BlockingSystemModalResolution.swift +82 -0
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandExecution.swift +272 -118
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+CommandJournal.swift +2 -2
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Interaction.swift +134 -101
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Lifecycle.swift +56 -2
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+LifecycleCacheTests.swift +73 -0
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Models.swift +44 -24
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScrollGesture.swift +2 -2
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SequenceExecution.swift +2 -107
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Snapshot.swift +126 -11
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotCapturePlan.swift +51 -8
- package/dist/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SnapshotTraversalIdentityTests.swift +24 -0
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SynthesizedGesturePolicy.swift +1 -1
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+SystemModal.swift +43 -35
- package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests.swift +14 -0
- package/dist/src/agent-browser-tool.js +1 -1
- package/dist/src/agent-device-client.js +3 -0
- package/dist/src/android-adb.d.ts +1 -1
- package/dist/src/android-adb.js +1 -1
- package/dist/src/android-input-method-overlays.js +1 -0
- package/dist/src/android-input-ownership.js +1 -6
- package/dist/src/android-system-chrome.js +1 -0
- package/dist/src/android.js +1 -1
- package/dist/src/app-lifecycle.js +5 -5
- package/dist/src/app-log-request-scope.js +1 -0
- package/dist/src/app-resolution.js +1 -0
- package/dist/src/apple-multitouch-support.js +1 -0
- package/dist/src/apps.js +5 -5
- package/dist/src/boot-diagnostics.js +2 -0
- package/dist/src/cli-config.js +1 -0
- package/dist/src/cli-help.js +61 -27
- package/dist/src/cli-runner.js +1 -1
- package/dist/src/cli.js +15 -11
- package/dist/src/command-schema.js +3 -3
- package/dist/src/config.js +1 -0
- package/dist/src/context.js +1 -0
- package/dist/src/contracts.d.ts +1 -1
- package/dist/src/daemon-shutdown-report.js +1 -0
- package/dist/src/debug-symbols.js +1 -1
- package/dist/src/device-input-state.js +1 -1
- package/dist/src/devices2.js +1 -1
- package/dist/src/find.js +1 -1
- package/dist/src/finders.js +1 -1
- package/dist/src/generic.js +1 -1
- package/dist/src/helper.js +1 -1
- package/dist/src/index.d.ts +412 -293
- package/dist/src/index.js +1 -1
- package/dist/src/input-actions.js +4 -3
- package/dist/src/install-artifact.js +1 -0
- package/dist/src/install-source.d.ts +5 -1
- package/dist/src/install-source.js +1 -1
- package/dist/src/install-source2.js +1 -1
- package/dist/src/interaction-snapshot.js +1 -1
- package/dist/src/interaction.d.ts +230 -0
- package/dist/src/interaction.js +1 -1
- package/dist/src/interactor.js +2 -2
- package/dist/src/internal/bin.js +1 -1
- package/dist/src/internal/daemon.js +42 -20
- package/dist/src/internal/png-worker.d.ts +22 -0
- package/dist/src/internal/png-worker.js +1 -1
- package/dist/src/launch-console.js +1 -0
- package/dist/src/lease-context.js +1 -0
- package/dist/src/lease.js +1 -1
- package/dist/src/linux.js +1 -1
- package/dist/src/logcat.js +5 -0
- package/dist/src/metro.d.ts +2 -2
- package/dist/src/notifications.js +1 -1
- package/dist/src/png-worker-contract.js +1 -1
- package/dist/src/process-lock.js +1 -0
- package/dist/src/react-native.js +1 -1
- package/dist/src/record-trace.js +1 -1
- package/dist/src/register-builtins.js +1 -0
- package/dist/src/registry.js +24 -24
- package/dist/src/reporting.js +8 -7
- package/dist/src/runner-client.js +1 -1
- package/dist/src/runner-disposal.js +2 -0
- package/dist/src/runner-lease.js +1 -0
- package/dist/src/runner-sequence.js +1 -1
- package/dist/src/runtime.js +3 -3
- package/dist/src/runtime2.js +1 -0
- package/dist/src/sdk-android-adb.d.ts +119 -33
- package/dist/src/sdk-android-adb.js +2 -2
- package/dist/src/sdk-batch-runner.d.ts +2 -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 +105 -98
- package/dist/src/sdk-contracts.js +1 -1
- package/dist/src/sdk-device.js +1 -1
- package/dist/src/sdk-finders.d.ts +3 -3
- package/dist/src/sdk-finders.js +1 -1
- package/dist/src/sdk-io.d.ts +1 -1
- package/dist/src/sdk-remote-config.d.ts +9 -1
- package/dist/src/sdk-remote-config.js +1 -1
- package/dist/src/sdk-selectors.js +1 -1
- package/dist/src/selector-runtime.js +1 -1
- package/dist/src/selector-vocabulary.js +1 -0
- package/dist/src/selectors.d.ts +12 -3
- package/dist/src/selectors.js +1 -1
- package/dist/src/server.js +3 -3
- package/dist/src/session-target-evidence.js +1 -0
- package/dist/src/session.js +8 -10
- package/dist/src/simulator.js +1 -1
- package/dist/src/snapshot-diagnostics.js +5 -2
- package/dist/src/snapshot.js +1 -1
- package/dist/src/snapshot2.js +1 -2
- package/dist/src/tv-remote.js +13 -12
- package/dist/src/url.js +1 -1
- package/dist/src/web.js +1 -1
- package/package.json +28 -23
- package/server.json +2 -2
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.19.3.apk +0 -0
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.19.3.apk.sha256 +0 -1
- package/android-multitouch-helper/dist/agent-device-android-multitouch-helper-0.19.3.manifest.json +0 -10
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.19.3.apk +0 -0
- package/android-snapshot-helper/dist/agent-device-android-snapshot-helper-0.19.3.apk.sha256 +0 -1
- package/dist/src/apple-runner-options.js +0 -1
- package/dist/src/cli-output.js +0 -1
- package/dist/src/client.js +0 -3
- package/dist/src/handler-utils.js +0 -1
- package/dist/src/output-path.js +0 -1
- package/dist/src/points.js +0 -4
- package/dist/src/provider.js +0 -1
- package/dist/src/providers.js +0 -1
- package/dist/src/record-trace-recording.js +0 -23
- package/dist/src/recording-provider.js +0 -1
- package/dist/src/selectors2.js +0 -1
- package/dist/src/simctl.js +0 -1
- package/dist/src/ttl-memo.js +0 -1
- package/dist/src/xml.js +0 -2
- /package/{macos-helper → apple/macos-helper}/Package.swift +0 -0
- /package/{macos-helper → apple/macos-helper}/Sources/AgentDeviceMacOSHelper/AudioProbe.swift +0 -0
- /package/{macos-helper → apple/macos-helper}/Sources/AgentDeviceMacOSHelper/SnapshotTraversal.swift +0 -0
- /package/{macos-helper → apple/macos-helper}/Sources/AgentDeviceMacOSHelper/main.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunner/AgentDeviceRunnerApp.m +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.pbxproj +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunner.xcodeproj/xcshareddata/xcschemes/AgentDeviceRunner.xcscheme +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/AgentDeviceRunnerUITests-Bridging-Header.h +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/AgentDeviceRunnerUITests.entitlements +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerObjCExceptionCatcher.h +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerObjCExceptionCatcher.m +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTapPointPolicy.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+AXSnapshotFallback.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Environment.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Exceptions.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+FlatSnapshotFiltering.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Navigation.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+ScreenRecorder.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TextEntry.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+Transport.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+TvRemote.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/AgentDeviceRunnerUITests.xctestplan +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/RecordingScripts/recording-overlay.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/RecordingScripts/recording-resize.swift +0 -0
- /package/dist/{apple-runner → apple/runner}/AgentDeviceRunner/RecordingScripts/recording-trim.swift +0 -0
- /package/{src/platforms/linux → linux}/atspi-dump.py +0 -0
|
@@ -1,48 +1,89 @@
|
|
|
1
1
|
import XCTest
|
|
2
2
|
|
|
3
3
|
extension RunnerTests {
|
|
4
|
+
enum RunnerAlertSource {
|
|
5
|
+
case blockingSystemModal
|
|
6
|
+
case appAlert
|
|
7
|
+
case dismissPopup
|
|
8
|
+
}
|
|
9
|
+
|
|
4
10
|
struct RunnerAlert {
|
|
5
11
|
let root: XCUIElement
|
|
6
12
|
let ownerApp: XCUIApplication
|
|
7
13
|
let buttons: [XCUIElement]
|
|
14
|
+
let source: RunnerAlertSource
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static let defaultAlertCommandTimeout: TimeInterval = 10
|
|
18
|
+
|
|
19
|
+
static func alertCommandTimeout(timeoutMs: Double?) -> TimeInterval {
|
|
20
|
+
guard let timeoutMs, timeoutMs.isFinite else { return defaultAlertCommandTimeout }
|
|
21
|
+
return max(0.001, timeoutMs / 1000)
|
|
8
22
|
}
|
|
9
23
|
|
|
10
|
-
func resolveAlert(app activeApp: XCUIApplication) -> RunnerAlert? {
|
|
24
|
+
func resolveAlert(app activeApp: XCUIApplication, deadline: Date) -> RunnerAlert? {
|
|
11
25
|
#if !os(macOS)
|
|
12
|
-
|
|
13
|
-
|
|
26
|
+
switch resolveBlockingSystemModal(deadline: deadline) {
|
|
27
|
+
case .resolved(let modal):
|
|
28
|
+
return runnerAlert(modal)
|
|
29
|
+
case .unresolved:
|
|
30
|
+
return nil
|
|
31
|
+
case .absent:
|
|
32
|
+
break
|
|
14
33
|
}
|
|
15
34
|
#endif
|
|
16
|
-
|
|
17
|
-
|
|
35
|
+
// Guard the query: when a remote-hosted modal (e.g. the AccessorySetupKit
|
|
36
|
+
// picker) was just dismissed, the dismissal re-check re-enters here with the
|
|
37
|
+
// now-gone host as `activeApp`, and its `alerts` query raises
|
|
38
|
+
// kAXErrorServerNotFound. safeElementsQuery absorbs it and reports no alert.
|
|
39
|
+
if let alert = firstExistingElement(in: safeElementsQuery { activeApp.alerts.allElementsBoundByIndex }) {
|
|
40
|
+
return runnerAlert(root: alert, ownerApp: activeApp, source: .appAlert)
|
|
18
41
|
}
|
|
19
42
|
if let popup = firstDismissPopupWindow(in: activeApp) {
|
|
20
|
-
return runnerAlert(root: popup, ownerApp: activeApp)
|
|
43
|
+
return runnerAlert(root: popup, ownerApp: activeApp, source: .dismissPopup)
|
|
21
44
|
}
|
|
22
45
|
return nil
|
|
23
46
|
}
|
|
24
47
|
|
|
25
|
-
func handleAlert(_ alert: RunnerAlert, action: String) -> Response {
|
|
48
|
+
func handleAlert(_ alert: RunnerAlert, action: String, deadline: Date) -> Response {
|
|
26
49
|
if action == "accept" || action == "dismiss" {
|
|
27
50
|
guard let button = chooseAlertButton(alert.buttons, action: action) else {
|
|
28
51
|
return Response(ok: false, error: ErrorPayload(message: "alert \(action) button not found"))
|
|
29
52
|
}
|
|
53
|
+
let previousTitle = preferredAlertTitle(alert.root, buttons: alert.buttons)
|
|
54
|
+
let actionButtonLabel = button.label.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
55
|
+
let actionButtonFrame = button.frame
|
|
30
56
|
let outcome = activateElement(app: alert.ownerApp, element: button, action: "alert \(action)")
|
|
31
57
|
if let response = unsupportedResponse(for: outcome) {
|
|
32
58
|
return response
|
|
33
59
|
}
|
|
34
60
|
sleepFor(0.2)
|
|
35
|
-
if alertStillVisible(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
61
|
+
if alertStillVisible(
|
|
62
|
+
in: alert.ownerApp,
|
|
63
|
+
source: alert.source,
|
|
64
|
+
previousTitle: previousTitle,
|
|
65
|
+
actionButtonLabel: actionButtonLabel,
|
|
66
|
+
deadline: deadline
|
|
67
|
+
) {
|
|
68
|
+
if !actionButtonFrame.isNull && !actionButtonFrame.isEmpty {
|
|
69
|
+
let coordinateOutcome = tapAt(
|
|
70
|
+
app: alert.ownerApp,
|
|
71
|
+
x: actionButtonFrame.midX,
|
|
72
|
+
y: actionButtonFrame.midY
|
|
73
|
+
)
|
|
39
74
|
if let response = unsupportedResponse(for: coordinateOutcome) {
|
|
40
75
|
return response
|
|
41
76
|
}
|
|
42
77
|
sleepFor(0.2)
|
|
43
78
|
}
|
|
44
79
|
}
|
|
45
|
-
if alertStillVisible(
|
|
80
|
+
if alertStillVisible(
|
|
81
|
+
in: alert.ownerApp,
|
|
82
|
+
source: alert.source,
|
|
83
|
+
previousTitle: previousTitle,
|
|
84
|
+
actionButtonLabel: actionButtonLabel,
|
|
85
|
+
deadline: deadline
|
|
86
|
+
) {
|
|
46
87
|
return Response(
|
|
47
88
|
ok: false,
|
|
48
89
|
error: ErrorPayload(
|
|
@@ -64,26 +105,79 @@ extension RunnerTests {
|
|
|
64
105
|
)
|
|
65
106
|
}
|
|
66
107
|
|
|
67
|
-
private func runnerAlert(
|
|
108
|
+
private func runnerAlert(_ modal: ResolvedBlockingSystemModal) -> RunnerAlert? {
|
|
109
|
+
let buttons = modal.actions.filter { isEnabledElement($0) }
|
|
110
|
+
guard !buttons.isEmpty else {
|
|
111
|
+
return nil
|
|
112
|
+
}
|
|
113
|
+
return RunnerAlert(
|
|
114
|
+
root: modal.root,
|
|
115
|
+
ownerApp: modal.ownerApp,
|
|
116
|
+
buttons: buttons,
|
|
117
|
+
source: .blockingSystemModal
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private func runnerAlert(
|
|
122
|
+
root: XCUIElement,
|
|
123
|
+
ownerApp: XCUIApplication,
|
|
124
|
+
source: RunnerAlertSource
|
|
125
|
+
) -> RunnerAlert? {
|
|
68
126
|
let buttons = actionableElements(in: root).filter { isEnabledElement($0) }
|
|
69
127
|
guard !buttons.isEmpty else {
|
|
70
128
|
return nil
|
|
71
129
|
}
|
|
72
|
-
return RunnerAlert(root: root, ownerApp: ownerApp, buttons: buttons)
|
|
130
|
+
return RunnerAlert(root: root, ownerApp: ownerApp, buttons: buttons, source: source)
|
|
73
131
|
}
|
|
74
132
|
|
|
75
|
-
private func alertStillVisible(
|
|
76
|
-
|
|
133
|
+
private func alertStillVisible(
|
|
134
|
+
in ownerApp: XCUIApplication,
|
|
135
|
+
source: RunnerAlertSource,
|
|
136
|
+
previousTitle: String,
|
|
137
|
+
actionButtonLabel: String,
|
|
138
|
+
deadline: Date
|
|
139
|
+
) -> Bool {
|
|
140
|
+
guard Date() < deadline,
|
|
141
|
+
let current = resolveAlert(source: source, app: ownerApp, deadline: deadline)
|
|
142
|
+
else {
|
|
77
143
|
return false
|
|
78
144
|
}
|
|
79
|
-
let previousTitle = preferredAlertTitle(alert.root, buttons: alert.buttons)
|
|
80
145
|
let currentTitle = preferredAlertTitle(current.root, buttons: current.buttons)
|
|
81
146
|
if previousTitle == currentTitle {
|
|
82
147
|
return true
|
|
83
148
|
}
|
|
84
|
-
let normalizedActionLabel = actionButtonLabel.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
85
149
|
return current.buttons.contains { button in
|
|
86
|
-
button.label.trimmingCharacters(in: .whitespacesAndNewlines) ==
|
|
150
|
+
button.label.trimmingCharacters(in: .whitespacesAndNewlines) == actionButtonLabel
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private func resolveAlert(
|
|
155
|
+
source: RunnerAlertSource,
|
|
156
|
+
app: XCUIApplication,
|
|
157
|
+
deadline: Date
|
|
158
|
+
) -> RunnerAlert? {
|
|
159
|
+
switch source {
|
|
160
|
+
case .blockingSystemModal:
|
|
161
|
+
#if os(macOS)
|
|
162
|
+
return nil
|
|
163
|
+
#else
|
|
164
|
+
guard case .resolved(let modal) = resolveBlockingSystemModal(deadline: deadline) else {
|
|
165
|
+
return nil
|
|
166
|
+
}
|
|
167
|
+
return runnerAlert(modal)
|
|
168
|
+
#endif
|
|
169
|
+
case .appAlert:
|
|
170
|
+
guard let alert = firstExistingElement(
|
|
171
|
+
in: safeElementsQuery { app.alerts.allElementsBoundByIndex }
|
|
172
|
+
) else {
|
|
173
|
+
return nil
|
|
174
|
+
}
|
|
175
|
+
return runnerAlert(root: alert, ownerApp: app, source: .appAlert)
|
|
176
|
+
case .dismissPopup:
|
|
177
|
+
guard let popup = firstDismissPopupWindow(in: app) else {
|
|
178
|
+
return nil
|
|
179
|
+
}
|
|
180
|
+
return runnerAlert(root: popup, ownerApp: app, source: .dismissPopup)
|
|
87
181
|
}
|
|
88
182
|
}
|
|
89
183
|
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import XCTest
|
|
2
|
+
|
|
3
|
+
extension RunnerTests {
|
|
4
|
+
struct ResolvedBlockingSystemModal {
|
|
5
|
+
let root: XCUIElement
|
|
6
|
+
let ownerApp: XCUIApplication
|
|
7
|
+
let actions: [XCUIElement]
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
enum BlockingSystemModalResolution {
|
|
11
|
+
case absent
|
|
12
|
+
case unresolved
|
|
13
|
+
case resolved(ResolvedBlockingSystemModal)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
func resolveBlockingSystemModal(
|
|
17
|
+
deadline: Date
|
|
18
|
+
) -> BlockingSystemModalResolution {
|
|
19
|
+
guard Self.hasSpringBoardSystemModalHost else {
|
|
20
|
+
return .absent
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
guard let springboardModal = firstBlockingSystemModal(
|
|
24
|
+
in: springboard,
|
|
25
|
+
deadline: deadline
|
|
26
|
+
) else {
|
|
27
|
+
return .absent
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let springboardActions = actionableElements(in: springboardModal)
|
|
31
|
+
if !RemoteHostedSystemModalPolicy.shouldProbeRemoteHost(
|
|
32
|
+
springboardActionCount: springboardActions.count
|
|
33
|
+
) {
|
|
34
|
+
return .resolved(
|
|
35
|
+
ResolvedBlockingSystemModal(
|
|
36
|
+
root: springboardModal,
|
|
37
|
+
ownerApp: springboard,
|
|
38
|
+
actions: springboardActions
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
guard Date() < deadline, let remoteModal = remoteHostedSystemModal(deadline: deadline) else {
|
|
44
|
+
return .unresolved
|
|
45
|
+
}
|
|
46
|
+
return .resolved(remoteModal)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// AccessorySetupUI mirrors into SpringBoard, but its mirrored elements are not
|
|
50
|
+
// reliably hittable. Query the host directly; activating it breaks the picker.
|
|
51
|
+
private static let remoteSystemModalHostBundleIds = [
|
|
52
|
+
"com.apple.AccessorySetupUI"
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
private func remoteHostedSystemModal(deadline: Date) -> ResolvedBlockingSystemModal? {
|
|
56
|
+
for bundleId in Self.remoteSystemModalHostBundleIds {
|
|
57
|
+
guard Date() < deadline else { return nil }
|
|
58
|
+
let host = XCUIApplication(bundleIdentifier: bundleId)
|
|
59
|
+
let state = safely("REMOTE_MODAL_STATE", XCUIApplication.State.unknown) { host.state }
|
|
60
|
+
guard RemoteHostedSystemModalPolicy.isEligibleHostState(state) else { continue }
|
|
61
|
+
guard Date() < deadline else { return nil }
|
|
62
|
+
|
|
63
|
+
// A dismissed remote host can raise kAXErrorServerNotFound. The safe queries
|
|
64
|
+
// inside actionableElements turn that disappearance into an empty result.
|
|
65
|
+
let actions = actionableElements(in: host)
|
|
66
|
+
guard !actions.isEmpty else { continue }
|
|
67
|
+
return ResolvedBlockingSystemModal(root: host, ownerApp: host, actions: actions)
|
|
68
|
+
}
|
|
69
|
+
return nil
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
enum RemoteHostedSystemModalPolicy {
|
|
74
|
+
static func shouldProbeRemoteHost(springboardActionCount: Int) -> Bool {
|
|
75
|
+
springboardActionCount == 0
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
static func isEligibleHostState(_ state: XCUIApplication.State) -> Bool {
|
|
79
|
+
state == .runningForeground
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|