mirror of
https://github.com/callstack/agent-device.git
synced 2026-09-14 20:06:34 +08:00
e58cbcdb5f
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).
95 lines
3.2 KiB
Swift
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
|