blob: ece727ed2ccdbbea25cb312b86dba2a757ea28a9 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/ui/authentication/authentication_flow.h"
#include <memory>
#import "base/mac/scoped_block.h"
#include "base/memory/ptr_util.h"
#import "base/test/ios/wait_util.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/sync_preferences/pref_service_mock_factory.h"
#include "components/sync_preferences/pref_service_syncable.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
#include "ios/chrome/browser/prefs/browser_prefs.h"
#include "ios/chrome/browser/signin/authentication_service_factory.h"
#import "ios/chrome/browser/signin/authentication_service_fake.h"
#import "ios/chrome/browser/ui/authentication/authentication_flow_performer.h"
#include "ios/public/provider/chrome/browser/signin/fake_chrome_identity_service.h"
#include "ios/web/public/test/test_web_thread_bundle.h"
#import "testing/gtest_mac.h"
#import "testing/platform_test.h"
#import "third_party/ocmock/gtest_support.h"
#import "third_party/ocmock/ocmock_extensions.h"
#include "ui/base/l10n/l10n_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
class AuthenticationFlowTest : public PlatformTest {
protected:
void SetUp() override {
PlatformTest::SetUp();
TestChromeBrowserState::Builder builder;
builder.AddTestingFactory(
AuthenticationServiceFactory::GetInstance(),
base::BindRepeating(
&AuthenticationServiceFake::CreateAuthenticationService));
builder.SetPrefService(CreatePrefService());
browser_state_ = builder.Build();
ios::FakeChromeIdentityService* identityService =
ios::FakeChromeIdentityService::GetInstanceFromChromeProvider();
identityService->AddIdentities(@[ @"identity1", @"identity2" ]);
identity1_ =
[identityService->GetAllIdentitiesSortedForDisplay() objectAtIndex:0];
identity2_ =
[identityService->GetAllIdentitiesSortedForDisplay() objectAtIndex:1];
sign_in_completion_ = ^(BOOL success) {
finished_ = true;
signed_in_success_ = success;
};
finished_ = false;
signed_in_success_ = false;
}
std::unique_ptr<sync_preferences::PrefServiceSyncable> CreatePrefService() {
sync_preferences::PrefServiceMockFactory factory;
scoped_refptr<user_prefs::PrefRegistrySyncable> registry(
new user_prefs::PrefRegistrySyncable);
std::unique_ptr<sync_preferences::PrefServiceSyncable> prefs =
factory.CreateSyncable(registry.get());
RegisterBrowserStatePrefs(registry.get());
return prefs;
}
AuthenticationFlowPerformer* GetAuthenticationFlowPerformer() {
return static_cast<AuthenticationFlowPerformer*>(performer_);
}
// Creates a new AuthenticationFlow with default values for fields that are
// not directly useful.
void CreateAuthenticationFlow(ShouldClearData shouldClearData,
PostSignInAction postSignInAction) {
ChromeIdentity* identity = identity1_;
view_controller_ = [OCMockObject niceMockForClass:[UIViewController class]];
authentication_flow_ =
[[AuthenticationFlow alloc] initWithBrowserState:browser_state_.get()
identity:identity
shouldClearData:shouldClearData
postSignInAction:postSignInAction
presentingViewController:view_controller_];
performer_ =
[OCMockObject mockForClass:[AuthenticationFlowPerformer class]];
[authentication_flow_
setPerformerForTesting:GetAuthenticationFlowPerformer()];
}
// Checks if the AuthenticationFlow operation has completed, and whether it
// was successful.
void CheckSignInCompletion(bool expectedSignedIn) {
base::test::ios::WaitUntilCondition(^bool {
return finished_;
});
EXPECT_EQ(true, finished_);
EXPECT_EQ(expectedSignedIn, signed_in_success_);
[performer_ verify];
}
web::TestWebThreadBundle thread_bundle_;
AuthenticationFlow* authentication_flow_;
std::unique_ptr<TestChromeBrowserState> browser_state_;
ChromeIdentity* identity1_;
ChromeIdentity* identity2_;
OCMockObject* performer_;
signin_ui::CompletionCallback sign_in_completion_;
UIViewController* view_controller_;
// State of the flow
bool finished_;
bool signed_in_success_;
};
// Tests a Sign In of a normal account on the same profile, merging user data
// and showing the sync settings.
TEST_F(AuthenticationFlowTest, TestSignInSimple) {
CreateAuthenticationFlow(SHOULD_CLEAR_DATA_MERGE_DATA,
POST_SIGNIN_ACTION_START_SYNC);
[[[performer_ expect] andDo:^(NSInvocation*) {
[authentication_flow_ didFetchManagedStatus:nil];
}] fetchManagedStatus:browser_state_.get()
forIdentity:identity1_];
[[[performer_ expect] andReturnBool:NO]
shouldHandleMergeCaseForIdentity:identity1_
browserState:browser_state_.get()];
[[performer_ expect] signInIdentity:identity1_
withHostedDomain:nil
toBrowserState:browser_state_.get()];
[[performer_ expect] commitSyncForBrowserState:browser_state_.get()];
[authentication_flow_ startSignInWithCompletion:sign_in_completion_];
CheckSignInCompletion(true);
}
// Tests that signing in an already signed in account correctly signs it out
// and back in.
TEST_F(AuthenticationFlowTest, TestAlreadySignedIn) {
CreateAuthenticationFlow(SHOULD_CLEAR_DATA_MERGE_DATA,
POST_SIGNIN_ACTION_NONE);
[[[performer_ expect] andDo:^(NSInvocation*) {
[authentication_flow_ didFetchManagedStatus:nil];
}] fetchManagedStatus:browser_state_.get()
forIdentity:identity1_];
[[[performer_ expect] andReturnBool:NO]
shouldHandleMergeCaseForIdentity:identity1_
browserState:browser_state_.get()];
[[[performer_ expect] andDo:^(NSInvocation*) {
[authentication_flow_ didSignOut];
}] signOutBrowserState:browser_state_.get()];
[[performer_ expect] signInIdentity:identity1_
withHostedDomain:nil
toBrowserState:browser_state_.get()];
AuthenticationServiceFactory::GetForBrowserState(browser_state_.get())
->SignIn(identity1_, std::string());
[authentication_flow_ startSignInWithCompletion:sign_in_completion_];
CheckSignInCompletion(true);
}
// Tests a Sign In of a different account, requiring a sign out of the already
// signed in account, and asking the user whether data should be cleared or
// merged.
TEST_F(AuthenticationFlowTest, TestSignOutUserChoice) {
CreateAuthenticationFlow(SHOULD_CLEAR_DATA_USER_CHOICE,
POST_SIGNIN_ACTION_START_SYNC);
[[[performer_ expect] andDo:^(NSInvocation*) {
[authentication_flow_ didFetchManagedStatus:nil];
}] fetchManagedStatus:browser_state_.get()
forIdentity:identity1_];
[[[performer_ expect] andReturnBool:YES]
shouldHandleMergeCaseForIdentity:identity1_
browserState:browser_state_.get()];
[[[performer_ expect] andDo:^(NSInvocation*) {
[authentication_flow_
didChooseClearDataPolicy:SHOULD_CLEAR_DATA_CLEAR_DATA];
}] promptMergeCaseForIdentity:identity1_
browserState:browser_state_.get()
viewController:view_controller_];
[[[performer_ expect] andDo:^(NSInvocation*) {
[authentication_flow_ didSignOut];
}] signOutBrowserState:browser_state_.get()];
[[[performer_ expect] andDo:^(NSInvocation*) {
[authentication_flow_ didClearData];
}] clearData:browser_state_.get()
dispatcher:nil];
[[performer_ expect] signInIdentity:identity1_
withHostedDomain:nil
toBrowserState:browser_state_.get()];
[[performer_ expect] commitSyncForBrowserState:browser_state_.get()];
AuthenticationServiceFactory::GetForBrowserState(browser_state_.get())
->SignIn(identity2_, std::string());
[authentication_flow_ startSignInWithCompletion:sign_in_completion_];
CheckSignInCompletion(true);
}
// Tests the cancelling of a Sign In.
TEST_F(AuthenticationFlowTest, TestCancel) {
CreateAuthenticationFlow(SHOULD_CLEAR_DATA_USER_CHOICE,
POST_SIGNIN_ACTION_START_SYNC);
[[[performer_ expect] andDo:^(NSInvocation*) {
[authentication_flow_ didFetchManagedStatus:nil];
}] fetchManagedStatus:browser_state_.get()
forIdentity:identity1_];
[[[performer_ expect] andReturnBool:YES]
shouldHandleMergeCaseForIdentity:identity1_
browserState:browser_state_.get()];
[[[performer_ expect] andDo:^(NSInvocation*) {
[authentication_flow_ cancelAndDismiss];
}] promptMergeCaseForIdentity:identity1_
browserState:browser_state_.get()
viewController:view_controller_];
[[performer_ expect] cancelAndDismiss];
[authentication_flow_ startSignInWithCompletion:sign_in_completion_];
CheckSignInCompletion(false);
}
// Tests the fetch managed status failure case.
TEST_F(AuthenticationFlowTest, TestFailFetchManagedStatus) {
CreateAuthenticationFlow(SHOULD_CLEAR_DATA_MERGE_DATA,
POST_SIGNIN_ACTION_START_SYNC);
NSError* error = [NSError errorWithDomain:@"foo" code:0 userInfo:nil];
[[[performer_ expect] andDo:^(NSInvocation*) {
[authentication_flow_ didFailFetchManagedStatus:error];
}] fetchManagedStatus:browser_state_.get()
forIdentity:identity1_];
[[[performer_ expect] andDo:^(NSInvocation* invocation) {
__unsafe_unretained ProceduralBlock completionBlock;
[invocation getArgument:&completionBlock atIndex:3];
completionBlock();
}] showAuthenticationError:[OCMArg any]
withCompletion:[OCMArg any]
viewController:view_controller_];
[authentication_flow_ startSignInWithCompletion:sign_in_completion_];
CheckSignInCompletion(false);
}
// Tests the managed sign in confirmation dialog is shown when signing in to
// a managed identity.
TEST_F(AuthenticationFlowTest, TestShowManagedConfirmation) {
CreateAuthenticationFlow(SHOULD_CLEAR_DATA_CLEAR_DATA,
POST_SIGNIN_ACTION_START_SYNC);
[[[performer_ expect] andDo:^(NSInvocation*) {
[authentication_flow_ didFetchManagedStatus:@"foo.com"];
}] fetchManagedStatus:browser_state_.get()
forIdentity:identity1_];
[[[performer_ expect] andReturnBool:NO]
shouldHandleMergeCaseForIdentity:identity1_
browserState:browser_state_.get()];
[[[performer_ expect] andDo:^(NSInvocation*) {
[authentication_flow_ didAcceptManagedConfirmation];
}] showManagedConfirmationForHostedDomain:@"foo.com"
viewController:view_controller_];
[[[performer_ expect] andDo:^(NSInvocation*) {
[authentication_flow_ didClearData];
}] clearData:browser_state_.get()
dispatcher:nil];
[[performer_ expect] signInIdentity:identity1_
withHostedDomain:@"foo.com"
toBrowserState:browser_state_.get()];
[[performer_ expect] commitSyncForBrowserState:browser_state_.get()];
[authentication_flow_ startSignInWithCompletion:sign_in_completion_];
CheckSignInCompletion(true);
}
} // namespace