Repository navigation
iOS alert activation can tap after the command deadline #2956
Description
Remaining fix at main e4716e6f514273f060b4f1fb8a523d3c51a9c17d
#2937 merged at 2026-09-25 05:49:20Z (b4c69936f2); this issue was filed later, at 08:23Z. It describes residual behavior, not a duplicate of that PR.
The checked button frame now avoids re-reading the button after the deadline, but activateAlertButton still enters activateElement → tapAt → performCoordinateTap → XCUICoordinate.tap(). A guard around that call cannot stop XCTest from resolving coordinates and synthesizing its event after the deadline inside the call. The banner test still combines interruption-monitor and confirmed-action assertions under a 90-second allowance.
Finish this as one bounded alert-activation fix:
- On iOS, use the existing private event-synthesis bridge for the resolved alert button. Keep the selected alert/button identity, owning display, and orientation/coordinate rules. Do not fall back to an unbounded XCTest coordinate tap on this deadline-sensitive path.
- Carry the command deadline through event preparation and check it immediately before
synthesizeWithError:. An early Swift guard alone is insufficient: window/display/orientation reads may consume the remaining time. Return a typed not-synthesized/deadline outcome, rather than classify an error message. - Keep one tap attempt and the existing post-activation uncertainty response. A synthesis acknowledgement does not prove the alert was answered; verification remains necessary. Other Apple platform activation policy stays in its current owner.
- Separate the notification-banner proofs: one test asserts no interruption-handler consultation; another asserts a confirmed answer within the command budget. The first must not fail merely because verification exhausted that budget.
Proof: a test must expire the deadline during event preparation and show zero native synthesis and zero alert action, with a future-deadline positive control. Removing the final native guard must fail that test. Run the existing replacement-alert, empty-frame, busy-app, app-owned and SpringBoard/modal controls on the changed route, plus selected runner gates. Document any device lane that is unavailable; do not close this as completed from a test split alone.
Problem
An alert accept/dismiss tap can land after the command's deadline. The last deadline guard runs before the synchronous coordinate tap (RunnerTests+Alert.swift ~131-143 → RunnerTests+Interaction.swift ~527-535). On a slow host the tap is synthesized late: CI job 107674012511 logged the activation point at about 56 s and XCTest's coordinate tap at 82.99 s, then ALERT_DEADLINE_EXCEEDED. The caller is told the command failed while the tap still lands.
The #2843 banner test (testAlertActivationDoesNotWaitOutANotificationBanner) proves XCTest's interruption handling was not consulted (RunnerTests+AlertObservationTests.swift ~124-128). It does not prove the tap landed before the deadline, which is why it failed there.
Required behavior
- Check the deadline immediately before the event is synthesized, and never tap after it.
- Keep the post-activation uncertainty response. Never retry an alert tap.
- Split the test so that "no interruption handling" and "confirmed before the deadline" are asserted separately.
Activity
okwasniewski commentedon Sep 25, 2026
Checked the activation path at
34f2eb1dcwhile triaging this downstream.The last
Date() < deadlineguard is already inside thewithBoundedInteractionIdleTimeoutIfSupportedclosure inactivateAlertButton(RunnerTests+Alert.swift), and between it and the event there is onlyactivateElement→tapAt→performCoordinateTap→interactionCoordinate(...).tap()(tvOS branches are compiled out on iOS). No wait or query sits between the guard andXCUICoordinate.tap(), so moving the check any closer changes nothing measurable.That means the 56 s → 82.99 s gap in job 107674012511 was spent inside XCTest's own
tap()(coordinate resolution against the SpringBoard-owned alert window on a starved host), which a pre-synthesis check cannot prevent. The fix that satisfies "never tap after the deadline" is to synthesize the alert tap through the HID bridge (synthesizedTapAt/RunnerSynthesizedGesture.synthesizeTap), which does not resolve elements before posting, with the deadline checked at the synthesize call. The test split (no interruption handling vs confirmed before the deadline) stands on its own.