Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
268 changes: 74 additions & 194 deletions GoogleSignIn/Tests/Unit/GIDEMMErrorHandlerTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ @interface GIDEMMErrorHandlerTest : XCTestCase
@end

@implementation GIDEMMErrorHandlerTest {
// Whether or not the current device runs on iOS 10.
BOOL _isIOS10;

// Whether key window has been set.
BOOL _keyWindowSet;

Expand All @@ -52,7 +49,6 @@ @implementation GIDEMMErrorHandlerTest {

- (void)setUp {
[super setUp];
_isIOS10 = [UIDevice currentDevice].systemVersion.integerValue == 10;
_keyWindowSet = NO;
_presentedViewController = nil;
UIWindow *fakeKeyWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
Expand Down Expand Up @@ -91,6 +87,44 @@ - (void)tearDown {
[super tearDown];
}

// Waits until work already dispatched to the main queue (such as the handler presenting
// its dialog) has run.
- (void)waitForMainQueue {
XCTestExpectation *expectation = [self expectationWithDescription:@"wait for main queue"];
dispatch_async(dispatch_get_main_queue(), ^() {
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:1 handler:nil];
}

// Returns the presented UIAlertController, or records a test failure and returns nil.
- (nullable UIAlertController *)presentedAlert {
if (![_presentedViewController isKindOfClass:[UIAlertController class]]) {
XCTFail(@"Expected a presented UIAlertController, got %@.", _presentedViewController);
return nil;
}
return (UIAlertController *)_presentedViewController;
}

// Asserts that the alert has a title and message and that its actions have the expected titles.
- (void)assertAlert:(UIAlertController *)alert hasActionTitles:(NSArray<NSString *> *)titles {
XCTAssertNotNil(alert.title);
XCTAssertNotNil(alert.message);
XCTAssertEqualObjects([alert.actions valueForKey:@"title"], titles);
}

// Invokes the handler of the alert action with the given title, or records a test failure.
- (void)tapActionTitled:(NSString *)title inAlert:(UIAlertController *)alert {
for (UIAlertAction *action in alert.actions) {
if ([action.title isEqualToString:title]) {
action.actionHandler(action);
return;
}
}
XCTFail(@"No action titled '%@' in alert (found: %@).",
title, [alert.actions valueForKey:@"title"]);
}

// Expects opening a particular URL string in performing an action.
- (void)expectOpenURLString:(NSString *)urlString inAction:(void (^)(void))action {
// Swizzle and mock [UIApplication sharedApplication] since it is unavailable in unit tests.
Expand Down Expand Up @@ -160,13 +194,6 @@ - (void)testGeneralEMMErrorOK {
completion:^() {
completionCalled = YES;
}];
if (![UIAlertController class]) {
XCTAssertFalse(result);
XCTAssertTrue(completionCalled);
XCTAssertFalse(_keyWindowSet);
XCTAssertNil(_presentedViewController);
return;
}
XCTAssertTrue(result);
XCTAssertFalse(completionCalled);
XCTAssertFalse(_keyWindowSet);
Expand All @@ -183,163 +210,67 @@ - (void)testGeneralEMMErrorOK {
XCTAssertFalse(_keyWindowSet);
XCTAssertNil(_presentedViewController);

// Wait for the code under test to be executed on the main thread.
XCTestExpectation *expectation = [self expectationWithDescription:@"wait for main thread"];
dispatch_async(dispatch_get_main_queue(), ^() {
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:1 handler:nil];
XCTAssertFalse(completionCalled);
[self waitForMainQueue];
XCTAssertTrue(_keyWindowSet);
XCTAssertTrue([_presentedViewController isKindOfClass:[UIAlertController class]]);
UIAlertController *alert = (UIAlertController *)_presentedViewController;
XCTAssertNotNil(alert.title);
XCTAssertNotNil(alert.message);
XCTAssertEqual(alert.actions.count, 1);
UIAlertController *alert = [self presentedAlert];
if (!alert) return;
[self assertAlert:alert hasActionTitles:@[ @"OK" ]];
XCTAssertFalse(completionCalled);

// Pretend to touch the "OK" button.
UIAlertAction *action = alert.actions[0];
XCTAssertEqualObjects(action.title, @"OK");
action.actionHandler(action);
[self tapActionTitled:@"OK" inAlert:alert];
XCTAssertTrue(completionCalled);
}

// Verifies that the handler handles EMM screenlock required error with user tapping 'Cancel'.
- (void)testScreenlockRequiredCancel {
if (_isIOS10) {
// The dialog is different on iOS 10.
return;
}
__block BOOL completionCalled = NO;
NSDictionary<NSString *, NSString *> *response = @{ @"error" : @"emm_passcode_required" };
BOOL result = [[GIDEMMErrorHandler sharedInstance] handleErrorFromResponse:response
completion:^() {
completionCalled = YES;
}];
if (![UIAlertController class]) {
XCTAssertFalse(result);
XCTAssertTrue(completionCalled);
XCTAssertFalse(_keyWindowSet);
XCTAssertNil(_presentedViewController);
return;
}
XCTAssertTrue(result);
XCTAssertFalse(completionCalled);
XCTAssertFalse(_keyWindowSet);
XCTAssertNil(_presentedViewController);

// Wait for the code under test to be executed on the main thread.
XCTestExpectation *expectation = [self expectationWithDescription:@"wait for main thread"];
dispatch_async(dispatch_get_main_queue(), ^() {
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:1 handler:nil];
XCTAssertFalse(completionCalled);
[self waitForMainQueue];
XCTAssertTrue(_keyWindowSet);
XCTAssertTrue([_presentedViewController isKindOfClass:[UIAlertController class]]);
UIAlertController *alert = (UIAlertController *)_presentedViewController;
XCTAssertNotNil(alert.title);
XCTAssertNotNil(alert.message);
XCTAssertEqual(alert.actions.count, 2);
UIAlertController *alert = [self presentedAlert];
if (!alert) return;
[self assertAlert:alert hasActionTitles:@[ @"Cancel", @"Settings" ]];
XCTAssertFalse(completionCalled);

// Pretend to touch the "Cancel" button.
UIAlertAction *action = alert.actions[0];
XCTAssertEqualObjects(action.title, @"Cancel");
action.actionHandler(action);
[self tapActionTitled:@"Cancel" inAlert:alert];
XCTAssertTrue(completionCalled);
}

// Verifies that the handler handles EMM screenlock required error with user tapping 'Settings'.
- (void)testScreenlockRequiredSettings {
if (_isIOS10) {
// The dialog is different on iOS 10.
return;
}
__block BOOL completionCalled = NO;
NSDictionary<NSString *, NSString *> *response = @{ @"error" : @"emm_passcode_required" };
BOOL result = [[GIDEMMErrorHandler sharedInstance] handleErrorFromResponse:response
completion:^() {
completionCalled = YES;
}];
if (![UIAlertController class]) {
XCTAssertFalse(result);
XCTAssertTrue(completionCalled);
XCTAssertFalse(_keyWindowSet);
XCTAssertNil(_presentedViewController);
return;
}
XCTAssertTrue(result);
XCTAssertFalse(completionCalled);
XCTAssertFalse(_keyWindowSet);
XCTAssertNil(_presentedViewController);

// Wait for the code under test to be executed on the main thread.
XCTestExpectation *expectation = [self expectationWithDescription:@"wait for main thread"];
dispatch_async(dispatch_get_main_queue(), ^() {
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:1 handler:nil];
XCTAssertFalse(completionCalled);
[self waitForMainQueue];
XCTAssertTrue(_keyWindowSet);
XCTAssertTrue([_presentedViewController isKindOfClass:[UIAlertController class]]);
UIAlertController *alert = (UIAlertController *)_presentedViewController;
XCTAssertNotNil(alert.title);
XCTAssertNotNil(alert.message);
XCTAssertEqual(alert.actions.count, 2);
UIAlertController *alert = [self presentedAlert];
if (!alert) return;
[self assertAlert:alert hasActionTitles:@[ @"Cancel", @"Settings" ]];
XCTAssertFalse(completionCalled);

// Pretend to touch the "Settings" button.
UIAlertAction *action = alert.actions[1];
XCTAssertEqualObjects(action.title, @"Settings");
[self expectOpenURLString:UIApplicationOpenSettingsURLString inAction:^() {
action.actionHandler(action);
[self tapActionTitled:@"Settings" inAlert:alert];
}];
XCTAssertTrue(completionCalled);
}

- (void)testScreenlockRequiredOkOnIOS10 {
if (!_isIOS10) {
// A more useful dialog is used for other iOS versions.
return;
}
__block BOOL completionCalled = NO;
NSDictionary<NSString *, NSString *> *response = @{ @"error" : @"emm_passcode_required" };
BOOL result = [[GIDEMMErrorHandler sharedInstance] handleErrorFromResponse:response
completion:^() {
completionCalled = YES;
}];
if (![UIAlertController class]) {
XCTAssertFalse(result);
XCTAssertTrue(completionCalled);
XCTAssertFalse(_keyWindowSet);
XCTAssertNil(_presentedViewController);
return;
}
XCTAssertTrue(result);
XCTAssertFalse(completionCalled);
XCTAssertFalse(_keyWindowSet);
XCTAssertNil(_presentedViewController);

// Wait for the code under test to be executed on the main thread.
XCTestExpectation *expectation = [self expectationWithDescription:@"wait for main thread"];
dispatch_async(dispatch_get_main_queue(), ^() {
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:1 handler:nil];
XCTAssertFalse(completionCalled);
XCTAssertTrue(_keyWindowSet);
XCTAssertTrue([_presentedViewController isKindOfClass:[UIAlertController class]]);
UIAlertController *alert = (UIAlertController *)_presentedViewController;
XCTAssertNotNil(alert.title);
XCTAssertNotNil(alert.message);
XCTAssertEqual(alert.actions.count, 1);

// Pretend to touch the "OK" button.
UIAlertAction *action = alert.actions[0];
XCTAssertEqualObjects(action.title, @"OK");
action.actionHandler(action);
XCTAssertTrue(completionCalled);
}

// Verifies that the handler handles EMM app verification required error without a URL.
- (void)testAppVerificationNoURL {
__block BOOL completionCalled = NO;
Expand All @@ -348,36 +279,19 @@ - (void)testAppVerificationNoURL {
completion:^() {
completionCalled = YES;
}];
if (![UIAlertController class]) {
XCTAssertFalse(result);
XCTAssertTrue(completionCalled);
XCTAssertFalse(_keyWindowSet);
XCTAssertNil(_presentedViewController);
return;
}
XCTAssertTrue(result);
XCTAssertFalse(completionCalled);
XCTAssertFalse(_keyWindowSet);
XCTAssertNil(_presentedViewController);

// Wait for the code under test to be executed on the main thread.
XCTestExpectation *expectation = [self expectationWithDescription:@"wait for main thread"];
dispatch_async(dispatch_get_main_queue(), ^() {
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:1 handler:nil];
XCTAssertFalse(completionCalled);
[self waitForMainQueue];
XCTAssertTrue(_keyWindowSet);
XCTAssertTrue([_presentedViewController isKindOfClass:[UIAlertController class]]);
UIAlertController *alert = (UIAlertController *)_presentedViewController;
XCTAssertNotNil(alert.title);
XCTAssertNotNil(alert.message);
XCTAssertEqual(alert.actions.count, 1);
UIAlertController *alert = [self presentedAlert];
if (!alert) return;
[self assertAlert:alert hasActionTitles:@[ @"OK" ]];
XCTAssertFalse(completionCalled);

// Pretend to touch the "OK" button.
UIAlertAction *action = alert.actions[0];
XCTAssertEqualObjects(action.title, @"OK");
action.actionHandler(action);
[self tapActionTitled:@"OK" inAlert:alert];
XCTAssertTrue(completionCalled);
}

Expand All @@ -391,36 +305,19 @@ - (void)testAppVerificationCancel {
completion:^() {
completionCalled = YES;
}];
if (![UIAlertController class]) {
XCTAssertFalse(result);
XCTAssertTrue(completionCalled);
XCTAssertFalse(_keyWindowSet);
XCTAssertNil(_presentedViewController);
return;
}
XCTAssertTrue(result);
XCTAssertFalse(completionCalled);
XCTAssertFalse(_keyWindowSet);
XCTAssertNil(_presentedViewController);

// Wait for the code under test to be executed on the main thread.
XCTestExpectation *expectation = [self expectationWithDescription:@"wait for main thread"];
dispatch_async(dispatch_get_main_queue(), ^() {
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:1 handler:nil];
XCTAssertFalse(completionCalled);
[self waitForMainQueue];
XCTAssertTrue(_keyWindowSet);
XCTAssertTrue([_presentedViewController isKindOfClass:[UIAlertController class]]);
UIAlertController *alert = (UIAlertController *)_presentedViewController;
XCTAssertNotNil(alert.title);
XCTAssertNotNil(alert.message);
XCTAssertEqual(alert.actions.count, 2);
UIAlertController *alert = [self presentedAlert];
if (!alert) return;
[self assertAlert:alert hasActionTitles:@[ @"Cancel", @"Connect" ]];
XCTAssertFalse(completionCalled);

// Pretend to touch the "Cancel" button.
UIAlertAction *action = alert.actions[0];
XCTAssertEqualObjects(action.title, @"Cancel");
action.actionHandler(action);
[self tapActionTitled:@"Cancel" inAlert:alert];
XCTAssertTrue(completionCalled);
}

Expand All @@ -433,37 +330,20 @@ - (void)testAppVerificationConnect {
completion:^() {
completionCalled = YES;
}];
if (![UIAlertController class]) {
XCTAssertFalse(result);
XCTAssertTrue(completionCalled);
XCTAssertFalse(_keyWindowSet);
XCTAssertNil(_presentedViewController);
return;
}
XCTAssertTrue(result);
XCTAssertFalse(completionCalled);
XCTAssertFalse(_keyWindowSet);
XCTAssertNil(_presentedViewController);

// Wait for the code under test to be executed on the main thread.
XCTestExpectation *expectation = [self expectationWithDescription:@"wait for main thread"];
dispatch_async(dispatch_get_main_queue(), ^() {
[expectation fulfill];
});
[self waitForExpectationsWithTimeout:1 handler:nil];
XCTAssertFalse(completionCalled);
[self waitForMainQueue];
XCTAssertTrue(_keyWindowSet);
XCTAssertTrue([_presentedViewController isKindOfClass:[UIAlertController class]]);
UIAlertController *alert = (UIAlertController *)_presentedViewController;
XCTAssertNotNil(alert.title);
XCTAssertNotNil(alert.message);
XCTAssertEqual(alert.actions.count, 2);
UIAlertController *alert = [self presentedAlert];
if (!alert) return;
[self assertAlert:alert hasActionTitles:@[ @"Cancel", @"Connect" ]];
XCTAssertFalse(completionCalled);

// Pretend to touch the "Connect" button.
UIAlertAction *action = alert.actions[1];
XCTAssertEqualObjects(action.title, @"Connect");
[self expectOpenURLString:@"https://host.domain/path" inAction:^() {
action.actionHandler(action);
[self tapActionTitled:@"Connect" inAlert:alert];
}];
XCTAssertTrue(completionCalled);
}
Expand Down
Loading