@@ -40,9 +40,6 @@ @interface GIDEMMErrorHandlerTest : XCTestCase
4040@end
4141
4242@implementation GIDEMMErrorHandlerTest {
43- // Whether or not the current device runs on iOS 10.
44- BOOL _isIOS10;
45-
4643 // Whether key window has been set.
4744 BOOL _keyWindowSet;
4845
@@ -52,7 +49,6 @@ @implementation GIDEMMErrorHandlerTest {
5249
5350- (void )setUp {
5451 [super setUp ];
55- _isIOS10 = [UIDevice currentDevice ].systemVersion .integerValue == 10 ;
5652 _keyWindowSet = NO ;
5753 _presentedViewController = nil ;
5854 UIWindow *fakeKeyWindow = [[UIWindow alloc ] initWithFrame: [UIScreen mainScreen ].bounds];
@@ -91,6 +87,44 @@ - (void)tearDown {
9187 [super tearDown ];
9288}
9389
90+ // Waits until work already dispatched to the main queue (such as the handler presenting
91+ // its dialog) has run.
92+ - (void )waitForMainQueue {
93+ XCTestExpectation *expectation = [self expectationWithDescription: @" wait for main queue" ];
94+ dispatch_async (dispatch_get_main_queue (), ^() {
95+ [expectation fulfill ];
96+ });
97+ [self waitForExpectationsWithTimeout: 1 handler: nil ];
98+ }
99+
100+ // Returns the presented UIAlertController, or records a test failure and returns nil.
101+ - (nullable UIAlertController *)presentedAlert {
102+ if (![_presentedViewController isKindOfClass: [UIAlertController class ]]) {
103+ XCTFail (@" Expected a presented UIAlertController, got %@ ." , _presentedViewController);
104+ return nil ;
105+ }
106+ return (UIAlertController *)_presentedViewController;
107+ }
108+
109+ // Asserts that the alert has a title and message and that its actions have the expected titles.
110+ - (void )assertAlert : (UIAlertController *)alert hasActionTitles : (NSArray <NSString *> *)titles {
111+ XCTAssertNotNil (alert.title );
112+ XCTAssertNotNil (alert.message );
113+ XCTAssertEqualObjects ([alert.actions valueForKey: @" title" ], titles);
114+ }
115+
116+ // Invokes the handler of the alert action with the given title, or records a test failure.
117+ - (void )tapActionTitled : (NSString *)title inAlert : (UIAlertController *)alert {
118+ for (UIAlertAction *action in alert.actions ) {
119+ if ([action.title isEqualToString: title]) {
120+ action.actionHandler (action);
121+ return ;
122+ }
123+ }
124+ XCTFail (@" No action titled '%@ ' in alert (found: %@ )." ,
125+ title, [alert.actions valueForKey: @" title" ]);
126+ }
127+
94128// Expects opening a particular URL string in performing an action.
95129- (void )expectOpenURLString : (NSString *)urlString inAction : (void (^)(void ))action {
96130 // Swizzle and mock [UIApplication sharedApplication] since it is unavailable in unit tests.
@@ -160,13 +194,6 @@ - (void)testGeneralEMMErrorOK {
160194 completion: ^() {
161195 completionCalled = YES ;
162196 }];
163- if (![UIAlertController class ]) {
164- XCTAssertFalse (result);
165- XCTAssertTrue (completionCalled);
166- XCTAssertFalse (_keyWindowSet);
167- XCTAssertNil (_presentedViewController);
168- return ;
169- }
170197 XCTAssertTrue (result);
171198 XCTAssertFalse (completionCalled);
172199 XCTAssertFalse (_keyWindowSet);
@@ -183,163 +210,67 @@ - (void)testGeneralEMMErrorOK {
183210 XCTAssertFalse (_keyWindowSet);
184211 XCTAssertNil (_presentedViewController);
185212
186- // Wait for the code under test to be executed on the main thread.
187- XCTestExpectation *expectation = [self expectationWithDescription: @" wait for main thread" ];
188- dispatch_async (dispatch_get_main_queue (), ^() {
189- [expectation fulfill ];
190- });
191- [self waitForExpectationsWithTimeout: 1 handler: nil ];
192- XCTAssertFalse (completionCalled);
213+ [self waitForMainQueue ];
193214 XCTAssertTrue (_keyWindowSet);
194- XCTAssertTrue ([_presentedViewController isKindOfClass: [UIAlertController class ]]);
195- UIAlertController *alert = (UIAlertController *)_presentedViewController;
196- XCTAssertNotNil (alert.title );
197- XCTAssertNotNil (alert.message );
198- XCTAssertEqual (alert.actions .count , 1 );
215+ UIAlertController *alert = [self presentedAlert ];
216+ if (!alert) return ;
217+ [self assertAlert: alert hasActionTitles: @[ @" OK" ]];
218+ XCTAssertFalse (completionCalled);
199219
200- // Pretend to touch the "OK" button.
201- UIAlertAction *action = alert.actions [0 ];
202- XCTAssertEqualObjects (action.title , @" OK" );
203- action.actionHandler (action);
220+ [self tapActionTitled: @" OK" inAlert: alert];
204221 XCTAssertTrue (completionCalled);
205222}
206223
207224// Verifies that the handler handles EMM screenlock required error with user tapping 'Cancel'.
208225- (void )testScreenlockRequiredCancel {
209- if (_isIOS10) {
210- // The dialog is different on iOS 10.
211- return ;
212- }
213226 __block BOOL completionCalled = NO ;
214227 NSDictionary <NSString *, NSString *> *response = @{ @" error" : @" emm_passcode_required" };
215228 BOOL result = [[GIDEMMErrorHandler sharedInstance ] handleErrorFromResponse: response
216229 completion: ^() {
217230 completionCalled = YES ;
218231 }];
219- if (![UIAlertController class ]) {
220- XCTAssertFalse (result);
221- XCTAssertTrue (completionCalled);
222- XCTAssertFalse (_keyWindowSet);
223- XCTAssertNil (_presentedViewController);
224- return ;
225- }
226232 XCTAssertTrue (result);
227233 XCTAssertFalse (completionCalled);
228234 XCTAssertFalse (_keyWindowSet);
229235 XCTAssertNil (_presentedViewController);
230236
231- // Wait for the code under test to be executed on the main thread.
232- XCTestExpectation *expectation = [self expectationWithDescription: @" wait for main thread" ];
233- dispatch_async (dispatch_get_main_queue (), ^() {
234- [expectation fulfill ];
235- });
236- [self waitForExpectationsWithTimeout: 1 handler: nil ];
237- XCTAssertFalse (completionCalled);
237+ [self waitForMainQueue ];
238238 XCTAssertTrue (_keyWindowSet);
239- XCTAssertTrue ([_presentedViewController isKindOfClass: [UIAlertController class ]]);
240- UIAlertController *alert = (UIAlertController *)_presentedViewController;
241- XCTAssertNotNil (alert.title );
242- XCTAssertNotNil (alert.message );
243- XCTAssertEqual (alert.actions .count , 2 );
239+ UIAlertController *alert = [self presentedAlert ];
240+ if (!alert) return ;
241+ [self assertAlert: alert hasActionTitles: @[ @" Cancel" , @" Settings" ]];
242+ XCTAssertFalse (completionCalled);
244243
245- // Pretend to touch the "Cancel" button.
246- UIAlertAction *action = alert.actions [0 ];
247- XCTAssertEqualObjects (action.title , @" Cancel" );
248- action.actionHandler (action);
244+ [self tapActionTitled: @" Cancel" inAlert: alert];
249245 XCTAssertTrue (completionCalled);
250246}
251247
252248// Verifies that the handler handles EMM screenlock required error with user tapping 'Settings'.
253249- (void )testScreenlockRequiredSettings {
254- if (_isIOS10) {
255- // The dialog is different on iOS 10.
256- return ;
257- }
258250 __block BOOL completionCalled = NO ;
259251 NSDictionary <NSString *, NSString *> *response = @{ @" error" : @" emm_passcode_required" };
260252 BOOL result = [[GIDEMMErrorHandler sharedInstance ] handleErrorFromResponse: response
261253 completion: ^() {
262254 completionCalled = YES ;
263255 }];
264- if (![UIAlertController class ]) {
265- XCTAssertFalse (result);
266- XCTAssertTrue (completionCalled);
267- XCTAssertFalse (_keyWindowSet);
268- XCTAssertNil (_presentedViewController);
269- return ;
270- }
271256 XCTAssertTrue (result);
272257 XCTAssertFalse (completionCalled);
273258 XCTAssertFalse (_keyWindowSet);
274259 XCTAssertNil (_presentedViewController);
275260
276- // Wait for the code under test to be executed on the main thread.
277- XCTestExpectation *expectation = [self expectationWithDescription: @" wait for main thread" ];
278- dispatch_async (dispatch_get_main_queue (), ^() {
279- [expectation fulfill ];
280- });
281- [self waitForExpectationsWithTimeout: 1 handler: nil ];
282- XCTAssertFalse (completionCalled);
261+ [self waitForMainQueue ];
283262 XCTAssertTrue (_keyWindowSet);
284- XCTAssertTrue ([_presentedViewController isKindOfClass: [UIAlertController class ]]);
285- UIAlertController *alert = (UIAlertController *)_presentedViewController;
286- XCTAssertNotNil (alert.title );
287- XCTAssertNotNil (alert.message );
288- XCTAssertEqual (alert.actions .count , 2 );
263+ UIAlertController *alert = [self presentedAlert ];
264+ if (!alert) return ;
265+ [self assertAlert: alert hasActionTitles: @[ @" Cancel" , @" Settings" ]];
266+ XCTAssertFalse (completionCalled);
289267
290- // Pretend to touch the "Settings" button.
291- UIAlertAction *action = alert.actions [1 ];
292- XCTAssertEqualObjects (action.title , @" Settings" );
293268 [self expectOpenURLString: UIApplicationOpenSettingsURLString inAction: ^() {
294- action. actionHandler (action) ;
269+ [ self tapActionTitled: @" Settings " inAlert: alert] ;
295270 }];
296271 XCTAssertTrue (completionCalled);
297272}
298273
299- - (void )testScreenlockRequiredOkOnIOS10 {
300- if (!_isIOS10) {
301- // A more useful dialog is used for other iOS versions.
302- return ;
303- }
304- __block BOOL completionCalled = NO ;
305- NSDictionary <NSString *, NSString *> *response = @{ @" error" : @" emm_passcode_required" };
306- BOOL result = [[GIDEMMErrorHandler sharedInstance ] handleErrorFromResponse: response
307- completion: ^() {
308- completionCalled = YES ;
309- }];
310- if (![UIAlertController class ]) {
311- XCTAssertFalse (result);
312- XCTAssertTrue (completionCalled);
313- XCTAssertFalse (_keyWindowSet);
314- XCTAssertNil (_presentedViewController);
315- return ;
316- }
317- XCTAssertTrue (result);
318- XCTAssertFalse (completionCalled);
319- XCTAssertFalse (_keyWindowSet);
320- XCTAssertNil (_presentedViewController);
321-
322- // Wait for the code under test to be executed on the main thread.
323- XCTestExpectation *expectation = [self expectationWithDescription: @" wait for main thread" ];
324- dispatch_async (dispatch_get_main_queue (), ^() {
325- [expectation fulfill ];
326- });
327- [self waitForExpectationsWithTimeout: 1 handler: nil ];
328- XCTAssertFalse (completionCalled);
329- XCTAssertTrue (_keyWindowSet);
330- XCTAssertTrue ([_presentedViewController isKindOfClass: [UIAlertController class ]]);
331- UIAlertController *alert = (UIAlertController *)_presentedViewController;
332- XCTAssertNotNil (alert.title );
333- XCTAssertNotNil (alert.message );
334- XCTAssertEqual (alert.actions .count , 1 );
335-
336- // Pretend to touch the "OK" button.
337- UIAlertAction *action = alert.actions [0 ];
338- XCTAssertEqualObjects (action.title , @" OK" );
339- action.actionHandler (action);
340- XCTAssertTrue (completionCalled);
341- }
342-
343274// Verifies that the handler handles EMM app verification required error without a URL.
344275- (void )testAppVerificationNoURL {
345276 __block BOOL completionCalled = NO ;
@@ -348,36 +279,19 @@ - (void)testAppVerificationNoURL {
348279 completion: ^() {
349280 completionCalled = YES ;
350281 }];
351- if (![UIAlertController class ]) {
352- XCTAssertFalse (result);
353- XCTAssertTrue (completionCalled);
354- XCTAssertFalse (_keyWindowSet);
355- XCTAssertNil (_presentedViewController);
356- return ;
357- }
358282 XCTAssertTrue (result);
359283 XCTAssertFalse (completionCalled);
360284 XCTAssertFalse (_keyWindowSet);
361285 XCTAssertNil (_presentedViewController);
362286
363- // Wait for the code under test to be executed on the main thread.
364- XCTestExpectation *expectation = [self expectationWithDescription: @" wait for main thread" ];
365- dispatch_async (dispatch_get_main_queue (), ^() {
366- [expectation fulfill ];
367- });
368- [self waitForExpectationsWithTimeout: 1 handler: nil ];
369- XCTAssertFalse (completionCalled);
287+ [self waitForMainQueue ];
370288 XCTAssertTrue (_keyWindowSet);
371- XCTAssertTrue ([_presentedViewController isKindOfClass: [UIAlertController class ]]);
372- UIAlertController *alert = (UIAlertController *)_presentedViewController;
373- XCTAssertNotNil (alert.title );
374- XCTAssertNotNil (alert.message );
375- XCTAssertEqual (alert.actions .count , 1 );
289+ UIAlertController *alert = [self presentedAlert ];
290+ if (!alert) return ;
291+ [self assertAlert: alert hasActionTitles: @[ @" OK" ]];
292+ XCTAssertFalse (completionCalled);
376293
377- // Pretend to touch the "OK" button.
378- UIAlertAction *action = alert.actions [0 ];
379- XCTAssertEqualObjects (action.title , @" OK" );
380- action.actionHandler (action);
294+ [self tapActionTitled: @" OK" inAlert: alert];
381295 XCTAssertTrue (completionCalled);
382296}
383297
@@ -391,36 +305,19 @@ - (void)testAppVerificationCancel {
391305 completion: ^() {
392306 completionCalled = YES ;
393307 }];
394- if (![UIAlertController class ]) {
395- XCTAssertFalse (result);
396- XCTAssertTrue (completionCalled);
397- XCTAssertFalse (_keyWindowSet);
398- XCTAssertNil (_presentedViewController);
399- return ;
400- }
401308 XCTAssertTrue (result);
402309 XCTAssertFalse (completionCalled);
403310 XCTAssertFalse (_keyWindowSet);
404311 XCTAssertNil (_presentedViewController);
405312
406- // Wait for the code under test to be executed on the main thread.
407- XCTestExpectation *expectation = [self expectationWithDescription: @" wait for main thread" ];
408- dispatch_async (dispatch_get_main_queue (), ^() {
409- [expectation fulfill ];
410- });
411- [self waitForExpectationsWithTimeout: 1 handler: nil ];
412- XCTAssertFalse (completionCalled);
313+ [self waitForMainQueue ];
413314 XCTAssertTrue (_keyWindowSet);
414- XCTAssertTrue ([_presentedViewController isKindOfClass: [UIAlertController class ]]);
415- UIAlertController *alert = (UIAlertController *)_presentedViewController;
416- XCTAssertNotNil (alert.title );
417- XCTAssertNotNil (alert.message );
418- XCTAssertEqual (alert.actions .count , 2 );
315+ UIAlertController *alert = [self presentedAlert ];
316+ if (!alert) return ;
317+ [self assertAlert: alert hasActionTitles: @[ @" Cancel" , @" Connect" ]];
318+ XCTAssertFalse (completionCalled);
419319
420- // Pretend to touch the "Cancel" button.
421- UIAlertAction *action = alert.actions [0 ];
422- XCTAssertEqualObjects (action.title , @" Cancel" );
423- action.actionHandler (action);
320+ [self tapActionTitled: @" Cancel" inAlert: alert];
424321 XCTAssertTrue (completionCalled);
425322}
426323
@@ -433,37 +330,20 @@ - (void)testAppVerificationConnect {
433330 completion: ^() {
434331 completionCalled = YES ;
435332 }];
436- if (![UIAlertController class ]) {
437- XCTAssertFalse (result);
438- XCTAssertTrue (completionCalled);
439- XCTAssertFalse (_keyWindowSet);
440- XCTAssertNil (_presentedViewController);
441- return ;
442- }
443333 XCTAssertTrue (result);
444334 XCTAssertFalse (completionCalled);
445335 XCTAssertFalse (_keyWindowSet);
446336 XCTAssertNil (_presentedViewController);
447337
448- // Wait for the code under test to be executed on the main thread.
449- XCTestExpectation *expectation = [self expectationWithDescription: @" wait for main thread" ];
450- dispatch_async (dispatch_get_main_queue (), ^() {
451- [expectation fulfill ];
452- });
453- [self waitForExpectationsWithTimeout: 1 handler: nil ];
454- XCTAssertFalse (completionCalled);
338+ [self waitForMainQueue ];
455339 XCTAssertTrue (_keyWindowSet);
456- XCTAssertTrue ([_presentedViewController isKindOfClass: [UIAlertController class ]]);
457- UIAlertController *alert = (UIAlertController *)_presentedViewController;
458- XCTAssertNotNil (alert.title );
459- XCTAssertNotNil (alert.message );
460- XCTAssertEqual (alert.actions .count , 2 );
340+ UIAlertController *alert = [self presentedAlert ];
341+ if (!alert) return ;
342+ [self assertAlert: alert hasActionTitles: @[ @" Cancel" , @" Connect" ]];
343+ XCTAssertFalse (completionCalled);
461344
462- // Pretend to touch the "Connect" button.
463- UIAlertAction *action = alert.actions [1 ];
464- XCTAssertEqualObjects (action.title , @" Connect" );
465345 [self expectOpenURLString: @" https://host.domain/path" inAction: ^() {
466- action. actionHandler (action) ;
346+ [ self tapActionTitled: @" Connect " inAlert: alert] ;
467347 }];
468348 XCTAssertTrue (completionCalled);
469349}
0 commit comments