Files
callstack__agent-device/apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests/RunnerTests+BlockingSystemModalResolution.swift
T
Michał Pierzchała e58cbcdb5f refactor: colocate native platform sources under android/, apple/, linux/ (#1273)
Move the scattered root-level native projects into per-platform folders and drop
the now-redundant platform prefix:

- android-ime-helper/        -> android/ime-helper/
- android-multitouch-helper/ -> android/multitouch-helper/
- android-snapshot-helper/   -> android/snapshot-helper/
- apple-runner/              -> apple/runner/
- macos-helper/              -> apple/macos-helper/
- src/platforms/linux/atspi-dump.py -> linux/atspi-dump.py

Only repo source paths move. Identity surfaces stay frozen so no user's runner
cache is invalidated on upgrade: the derived-cache key hashes source paths
relative to AgentDeviceRunner and excludes packageVersion, and the
~/.agent-device/{apple-runner,macos-helper} namespaces, the
agent-device-android-*-helper artifact/manifest/protocol names, the
AgentDeviceRunner Xcode project, and the `prepare ios-runner` CLI command are
unchanged. Updates build/package scripts, CI, package.json files+scripts,
ignore/attr/fallow configs, runtime path resolvers, and test fixtures.

Also: re-base repo-root-relative refs inside the moved apple/runner for the
added nesting level (gated XCUITest fixture walk + two doc links), and clean the
legacy dist/apple-runner packaged output so the relocated runner can't
double-ship into the wholesale-included dist (with a regression test).
2026-07-15 21:47:38 +02:00

95 lines
3.2 KiB
Swift

import XCTest
extension RunnerTests {
struct ResolvedBlockingSystemModal {
let root: XCUIElement
let ownerApp: XCUIApplication
let actions: [XCUIElement]
}
enum BlockingSystemModalResolution {
case absent
case unresolved
case resolved(ResolvedBlockingSystemModal)
}
func resolveBlockingSystemModal(
deadline: Date
) -> BlockingSystemModalResolution {
guard let springboardModal = firstBlockingSystemModal(
in: springboard,
deadline: deadline
) else {
return .absent
}
let springboardActions = actionableElements(in: springboardModal)
if !RemoteHostedSystemModalPolicy.shouldProbeRemoteHost(
springboardActionCount: springboardActions.count
) {
return .resolved(
ResolvedBlockingSystemModal(
root: springboardModal,
ownerApp: springboard,
actions: springboardActions
)
)
}
guard Date() < deadline, let remoteModal = remoteHostedSystemModal(deadline: deadline) else {
return .unresolved
}
return .resolved(remoteModal)
}
// AccessorySetupUI mirrors into SpringBoard, but its mirrored elements are not
// reliably hittable. Query the host directly; activating it breaks the picker.
private static let remoteSystemModalHostBundleIds = [
"com.apple.AccessorySetupUI"
]
private func remoteHostedSystemModal(deadline: Date) -> ResolvedBlockingSystemModal? {
for bundleId in Self.remoteSystemModalHostBundleIds {
guard Date() < deadline else { return nil }
let host = XCUIApplication(bundleIdentifier: bundleId)
let state = safely("REMOTE_MODAL_STATE", XCUIApplication.State.unknown) { host.state }
guard RemoteHostedSystemModalPolicy.isEligibleHostState(state) else { continue }
guard Date() < deadline else { return nil }
// A dismissed remote host can raise kAXErrorServerNotFound. The safe queries
// inside actionableElements turn that disappearance into an empty result.
let actions = actionableElements(in: host)
guard !actions.isEmpty else { continue }
return ResolvedBlockingSystemModal(root: host, ownerApp: host, actions: actions)
}
return nil
}
}
enum RemoteHostedSystemModalPolicy {
static func shouldProbeRemoteHost(springboardActionCount: Int) -> Bool {
springboardActionCount == 0
}
static func isEligibleHostState(_ state: XCUIApplication.State) -> Bool {
state == .runningForeground
}
}
#if AGENT_DEVICE_RUNNER_UNIT_TESTS
extension RunnerTests {
func testRemoteHostProbeRunsOnlyWhenSpringboardModalHasNoActions() {
XCTAssertTrue(RemoteHostedSystemModalPolicy.shouldProbeRemoteHost(springboardActionCount: 0))
XCTAssertFalse(RemoteHostedSystemModalPolicy.shouldProbeRemoteHost(springboardActionCount: 1))
XCTAssertFalse(RemoteHostedSystemModalPolicy.shouldProbeRemoteHost(springboardActionCount: 3))
}
func testRemoteHostStateGateFailsClosedToForeground() {
XCTAssertTrue(RemoteHostedSystemModalPolicy.isEligibleHostState(.runningForeground))
XCTAssertFalse(RemoteHostedSystemModalPolicy.isEligibleHostState(.runningBackground))
XCTAssertFalse(RemoteHostedSystemModalPolicy.isEligibleHostState(.notRunning))
XCTAssertFalse(RemoteHostedSystemModalPolicy.isEligibleHostState(.unknown))
}
}
#endif