@@ -5,6 +5,7 @@ import ObjectiveC.runtime
55
66private final class AlertTapDeadlineStub : NSObject {
77 static var status : RunnerTapSynthesisStatus ?
8+ static var interruptionHandlingDisabled : Bool ?
89
910 @objc ( synthesizeTapWithApplication: resolvedWindow: x: y: deadline: errorMessage: )
1011 class func synthesizeTap(
@@ -15,7 +16,8 @@ private final class AlertTapDeadlineStub: NSObject {
1516 deadline: NSDate ? ,
1617 errorMessage: AutoreleasingUnsafeMutablePointer < NSString ? > ?
1718 ) -> RunnerTapSynthesisStatus {
18- status ?? ( deadline == nil ? . succeeded : . deadlineExceeded)
19+ interruptionHandlingDisabled = ( application. value ( forKey: " doesNotHandleUIInterruptions " ) as? NSNumber ) ? . boolValue
20+ return status ?? ( deadline == nil ? . succeeded : . deadlineExceeded)
1921 }
2022}
2123#endif
@@ -49,7 +51,7 @@ extension RunnerTests {
4951 invalidateCachedTarget ( reason: " unit_test_cleanup " )
5052 app. terminate ( )
5153 }
52- XCTAssertTrue ( app. alerts. firstMatch. waitForExistence ( timeout: appExistenceTimeout) )
54+ XCTAssertTrue ( app. alerts. firstMatch. waitForFixtureExistence ( timeout: appExistenceTimeout) )
5355 let alert = try resolveAlertBeforeTheCommand ( )
5456 let response = handleAlert ( alert, action: " accept " , deadline: . distantPast)
5557 XCTAssertFalse ( response. ok)
@@ -67,7 +69,7 @@ extension RunnerTests {
6769 invalidateCachedTarget ( reason: " unit_test_cleanup " )
6870 app. terminate ( )
6971 }
70- XCTAssertTrue ( app. alerts. firstMatch. waitForExistence ( timeout: appExistenceTimeout) )
72+ XCTAssertTrue ( app. alerts. firstMatch. waitForFixtureExistence ( timeout: appExistenceTimeout) )
7173 let alert = try resolveAlertBeforeTheCommand ( )
7274 alertButtonHittabilityProbeOverrideForTesting = { probeDeadline in
7375 while Date ( ) < probeDeadline {
@@ -90,7 +92,7 @@ extension RunnerTests {
9092 invalidateCachedTarget ( reason: " unit_test_cleanup " )
9193 app. terminate ( )
9294 }
93- XCTAssertTrue ( app. alerts. firstMatch. waitForExistence ( timeout: appExistenceTimeout) )
95+ XCTAssertTrue ( app. alerts. firstMatch. waitForFixtureExistence ( timeout: appExistenceTimeout) )
9496 let alert = try resolveAlertBeforeTheCommand ( )
9597 let button = try XCTUnwrap ( alert. buttons. first { $0. label == " OK " } )
9698 let frame = button. frame
@@ -110,7 +112,7 @@ extension RunnerTests {
110112 invalidateCachedTarget ( reason: " unit_test_cleanup " )
111113 app. terminate ( )
112114 }
113- XCTAssertTrue ( app. alerts. firstMatch. waitForExistence ( timeout: appExistenceTimeout) )
115+ XCTAssertTrue ( app. alerts. firstMatch. waitForFixtureExistence ( timeout: appExistenceTimeout) )
114116 let alert = try resolveAlertBeforeTheCommand ( )
115117 let button = try XCTUnwrap ( alert. buttons. first { $0. label == " OK " } )
116118 let frame = button. frame
@@ -121,6 +123,7 @@ extension RunnerTests {
121123 method_setImplementation ( method, method_getImplementation ( stub) )
122124 defer {
123125 AlertTapDeadlineStub . status = nil
126+ AlertTapDeadlineStub . interruptionHandlingDisabled = nil
124127 method_setImplementation ( method, original)
125128 }
126129
@@ -150,7 +153,7 @@ extension RunnerTests {
150153 invalidateCachedTarget ( reason: " unit_test_cleanup " )
151154 app. terminate ( )
152155 }
153- XCTAssertTrue ( app. alerts. firstMatch. waitForExistence ( timeout: appExistenceTimeout) )
156+ XCTAssertTrue ( app. alerts. firstMatch. waitForFixtureExistence ( timeout: appExistenceTimeout) )
154157 let alert = try resolveAlertBeforeTheCommand ( )
155158
156159 let response = handleAlert ( alert, action: " accept " , deadline: Date ( ) . addingTimeInterval ( RunnerTests . alertActivationDeadline) )
@@ -165,7 +168,7 @@ extension RunnerTests {
165168 }
166169
167170 @MainActor
168- func testAlertActivationDoesNotConsultInterruptionHandlingWithANotificationBanner ( ) throws {
171+ func testAlertActivationConfirmsBeforeDeadlineWithoutHandlingTheNotificationBanner ( ) throws {
169172 try withNotificationBannerAlert { alert in
170173 var consultedInterruptions : [ String ] = [ ]
171174 let monitor = addUIInterruptionMonitor ( withDescription: " alert activation banner " ) { element in
@@ -174,24 +177,41 @@ extension RunnerTests {
174177 }
175178 defer { removeUIInterruptionMonitor ( monitor) }
176179
177- _ = handleAlert ( alert, action: " accept " , deadline: Date ( ) . addingTimeInterval ( RunnerTests . alertActivationDeadline) )
178-
179- XCTAssertEqual ( consultedInterruptions, [ ] , " alert activation consulted XCTest's interruption handling " )
180- }
181- }
182-
183- @MainActor
184- func testAlertActivationConfirmsBeforeDeadlineWithANotificationBanner( ) throws {
185- try withNotificationBannerAlert { alert in
180+ try assertAlertActivationDisablesInterruptionHandling ( alert)
186181 let deadline = Date ( ) . addingTimeInterval ( RunnerTests . alertActivationDeadline)
187182 let response = handleAlert ( alert, action: " accept " , deadline: deadline)
188183
184+ XCTAssertEqual ( consultedInterruptions, [ ] , " alert activation consulted XCTest's interruption handling " )
189185 XCTAssertTrue ( response. ok, String ( describing: response. error) )
190186 XCTAssertLessThan ( Date ( ) , deadline)
191187 XCTAssertEqual ( app. staticTexts [ " agent-device-alert-actions " ] . label, " First actions: 1; replacement actions: 0 " )
192188 }
193189 }
194190
191+ @MainActor
192+ private func assertAlertActivationDisablesInterruptionHandling( _ alert: RunnerAlert ) throws {
193+ let button = try XCTUnwrap ( alert. buttons. first { $0. label == " OK " } )
194+ let frame = button. frame
195+ let selector = NSSelectorFromString ( " synthesizeTapWithApplication:resolvedWindow:x:y:deadline:errorMessage: " )
196+ let method = try XCTUnwrap ( class_getClassMethod ( RunnerSynthesizedGesture . self, selector) )
197+ let stub = try XCTUnwrap ( class_getClassMethod ( AlertTapDeadlineStub . self, selector) )
198+ let original = method_getImplementation ( method)
199+ AlertTapDeadlineStub . status = . deadlineExceeded
200+ AlertTapDeadlineStub . interruptionHandlingDisabled = nil
201+ method_setImplementation ( method, method_getImplementation ( stub) )
202+ defer {
203+ AlertTapDeadlineStub . status = nil
204+ AlertTapDeadlineStub . interruptionHandlingDisabled = nil
205+ method_setImplementation ( method, original)
206+ }
207+
208+ let outcome = activateAlertButton (
209+ alert, button: button, action: " accept " , frame: frame, deadline: . distantFuture
210+ )
211+ XCTAssertNil ( outcome)
212+ XCTAssertEqual ( AlertTapDeadlineStub . interruptionHandlingDisabled, true )
213+ }
214+
195215 @MainActor
196216 private func withNotificationBannerAlert( _ assertions: ( RunnerAlert ) throws -> Void ) throws {
197217 app. launchArguments = [ " --agent-device-alert-replacement-regression " , " --agent-device-alert-banner " ]
@@ -204,7 +224,7 @@ extension RunnerTests {
204224 _ = banner. waitForNonExistence ( timeout: 15 )
205225 }
206226 try acceptNotificationAuthorizationUntilAlertAppears ( )
207- XCTAssertTrue ( banner. waitForExistence ( timeout: appExistenceTimeout) , " the fixture keeps a banner up " )
227+ XCTAssertTrue ( banner. waitForFixtureExistence ( timeout: appExistenceTimeout) , " the fixture keeps a banner up " )
208228 let alert = try resolveAlertBeforeTheCommand ( )
209229
210230 try assertions ( alert)
@@ -255,7 +275,7 @@ extension RunnerTests {
255275 invalidateCachedTarget ( reason: " unit_test_cleanup " )
256276 app. terminate ( )
257277 }
258- XCTAssertTrue ( app. alerts. firstMatch. waitForExistence ( timeout: appExistenceTimeout) )
278+ XCTAssertTrue ( app. alerts. firstMatch. waitForFixtureExistence ( timeout: appExistenceTimeout) )
259279 let original = try resolveAlertBeforeTheCommand ( )
260280 alertResolutionOverrideForTesting = { _ in original }
261281 let timeoutMs = Int ( RunnerTests . alertActivationDeadline * 1000 )
0 commit comments