blob: 3d9569c6312b8d4abe3be0b6c2f1423ff26778e5 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/authentication/ui_bundled/signin_promo_view_mediator.h"
#import "base/functional/callback_helpers.h"
#import "base/run_loop.h"
#import "base/strings/sys_string_conversions.h"
#import "base/test/ios/wait_util.h"
#import "build/branding_buildflags.h"
#import "components/pref_registry/pref_registry_syncable.h"
#import "components/prefs/pref_service.h"
#import "components/signin/public/base/signin_metrics.h"
#import "components/sync/test/mock_sync_service.h"
#import "components/sync_preferences/pref_service_mock_factory.h"
#import "components/sync_preferences/pref_service_syncable.h"
#import "ios/chrome/browser/authentication/ui_bundled/account_settings_presenter.h"
#import "ios/chrome/browser/authentication/ui_bundled/cells/signin_promo_view.h"
#import "ios/chrome/browser/authentication/ui_bundled/cells/signin_promo_view_configurator.h"
#import "ios/chrome/browser/authentication/ui_bundled/cells/signin_promo_view_constants.h"
#import "ios/chrome/browser/authentication/ui_bundled/cells/signin_promo_view_consumer.h"
#import "ios/chrome/browser/authentication/ui_bundled/signin/signin_constants.h"
#import "ios/chrome/browser/authentication/ui_bundled/signin_presenter.h"
#import "ios/chrome/browser/policy/model/policy_util.h"
#import "ios/chrome/browser/shared/model/application_context/application_context.h"
#import "ios/chrome/browser/shared/model/browser/test/test_browser.h"
#import "ios/chrome/browser/shared/model/prefs/browser_prefs.h"
#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
#import "ios/chrome/browser/signin/model/authentication_service.h"
#import "ios/chrome/browser/signin/model/authentication_service_factory.h"
#import "ios/chrome/browser/signin/model/chrome_account_manager_service_factory.h"
#import "ios/chrome/browser/signin/model/chrome_account_manager_service_observer_bridge.h"
#import "ios/chrome/browser/signin/model/fake_authentication_service_delegate.h"
#import "ios/chrome/browser/signin/model/fake_system_identity.h"
#import "ios/chrome/browser/signin/model/fake_system_identity_manager.h"
#import "ios/chrome/browser/signin/model/identity_manager_factory.h"
#import "ios/chrome/browser/sync/model/mock_sync_service_utils.h"
#import "ios/chrome/browser/sync/model/sync_service_factory.h"
#import "ios/chrome/grit/ios_branded_strings.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ios/chrome/test/ios_chrome_scoped_testing_local_state.h"
#import "ios/web/public/test/web_task_environment.h"
#import "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#import "third_party/ocmock/gtest_support.h"
#import "third_party/ocmock/ocmock_extensions.h"
#import "ui/base/l10n/l10n_util.h"
using base::SysNSStringToUTF16;
using base::test::ios::kWaitForUIElementTimeout;
using base::test::ios::WaitUntilConditionOrTimeout;
using l10n_util::GetNSString;
using l10n_util::GetNSStringF;
using sync_preferences::PrefServiceMockFactory;
using sync_preferences::PrefServiceSyncable;
using user_prefs::PrefRegistrySyncable;
using web::WebTaskEnvironment;
namespace {
class SigninPromoViewMediatorTest : public PlatformTest {
protected:
void SetUp() override {
identity_ = [FakeSystemIdentity fakeIdentity1];
TestProfileIOS::Builder builder;
builder.AddTestingFactory(SyncServiceFactory::GetInstance(),
base::BindRepeating(&CreateMockSyncService));
builder.AddTestingFactory(
AuthenticationServiceFactory::GetInstance(),
AuthenticationServiceFactory::GetFactoryWithDelegate(
std::make_unique<FakeAuthenticationServiceDelegate>()));
profile_ = std::move(builder).Build();
}
void TearDown() override {
// All callbacks should be triggered to make sure tests are working
// correctly.
fake_system_identity_manager()->WaitForServiceCallbacksToComplete();
if (mediator_) {
[mediator_ disconnect];
EXPECT_EQ(SigninPromoViewState::kInvalid, mediator_.signinPromoViewState);
EXPECT_EQ(nil, mediator_.consumer);
mediator_ = nil;
}
EXPECT_OCMOCK_VERIFY((id)consumer_);
EXPECT_OCMOCK_VERIFY((id)signin_promo_view_);
EXPECT_OCMOCK_VERIFY((id)primary_button_);
EXPECT_OCMOCK_VERIFY((id)secondary_button_);
EXPECT_OCMOCK_VERIFY((id)close_button_);
}
void CreateMediator(signin_metrics::AccessPoint access_point) {
consumer_ = OCMStrictProtocolMock(@protocol(SigninPromoViewConsumer));
signin_presenter_ = OCMStrictProtocolMock(@protocol(SigninPresenter));
account_settings_presenter_ =
OCMStrictProtocolMock(@protocol(AccountSettingsPresenter));
mediator_ = [[SigninPromoViewMediator alloc]
initWithIdentityManager:IdentityManagerFactory::GetForProfile(
profile_.get())
accountManagerService:ChromeAccountManagerServiceFactory::
GetForProfile(profile_.get())
authService:GetAuthenticationService()
prefService:profile_.get()->GetPrefs()
syncService:GetSyncService()
accessPoint:access_point
signinPresenter:signin_presenter_
accountSettingsPresenter:account_settings_presenter_];
mediator_.consumer = consumer_;
signin_promo_view_ = OCMStrictClassMock([SigninPromoView class]);
primary_button_ = OCMStrictClassMock([UIButton class]);
OCMStub([signin_promo_view_ primaryButton]).andReturn(primary_button_);
secondary_button_ = OCMStrictClassMock([UIButton class]);
OCMStub([signin_promo_view_ secondaryButton]).andReturn(secondary_button_);
close_button_ = OCMStrictClassMock([UIButton class]);
OCMStub([signin_promo_view_ closeButton]).andReturn(close_button_);
}
std::unique_ptr<PrefServiceSyncable> CreatePrefService() {
PrefServiceMockFactory factory;
scoped_refptr<PrefRegistrySyncable> registry(new PrefRegistrySyncable);
std::unique_ptr<PrefServiceSyncable> prefs =
factory.CreateSyncable(registry.get());
RegisterProfilePrefs(registry.get());
return prefs;
}
AuthenticationService* GetAuthenticationService() {
return AuthenticationServiceFactory::GetForProfile(profile_.get());
}
syncer::SyncService* GetSyncService() {
return SyncServiceFactory::GetForProfile(profile_.get());
}
// Creates the default identity and adds it into the ChromeIdentityService.
void AddDefaultIdentity() {
fake_system_identity_manager()->AddIdentity(identity_);
}
PrefService* GetLocalState() {
return GetApplicationContext()->GetLocalState();
}
// Tests the mediator with a new created configurator when no accounts are on
// the device.
void TestSigninPromoWithNoAccounts(SigninPromoViewStyle style) {
EXPECT_EQ(nil, mediator_.displayedIdentity);
CheckNoAccountsConfigurator([mediator_ createConfigurator], style);
}
// Adds an identity and tests the mediator.
void TestSigninPromoWithAccount(SigninPromoViewStyle style) {
// Expect to receive an update to the consumer with a configurator.
ExpectConfiguratorNotification(YES /* identity changed */);
AddDefaultIdentity();
// Check the configurator received by the consumer.
CheckSigninWithAccountConfigurator(configurator_, style);
// Check a new created configurator.
CheckSigninWithAccountConfigurator([mediator_ createConfigurator], style);
// The consumer should receive a notification related to the image.
CheckForImageNotification(style);
}
// Expects a notification on the consumer for an identity update, and stores
// the received configurator into configurator_.
void ExpectConfiguratorNotification(BOOL identity_changed) {
configurator_ = nil;
SigninPromoViewConfigurator* configurator_arg =
[OCMArg checkWithBlock:^BOOL(id value) {
configurator_ = value;
return YES;
}];
OCMExpect([consumer_
configureSigninPromoWithConfigurator:configurator_arg
identityChanged:identity_changed]);
}
// Expects the signin promo view to be configured with no accounts on the
// device.
void ExpectNoAccountsConfiguration(SigninPromoViewStyle style) {
OCMExpect([signin_promo_view_ setMode:SigninPromoViewModeNoAccounts]);
NSString* title = nil;
switch (style) {
case SigninPromoViewStyleStandard:
title = GetNSString(IDS_IOS_CONSISTENCY_PROMO_SIGN_IN);
break;
case SigninPromoViewStyleCompact:
title = GetNSString(IDS_IOS_NTP_FEED_SIGNIN_PROMO_CONTINUE);
break;
case SigninPromoViewStyleOnlyButton:
title = GetNSString(IDS_IOS_SIGNIN_PROMO_TURN_ON);
break;
}
OCMExpect([signin_promo_view_ configurePrimaryButtonWithTitle:title]);
image_view_profile_image_ = nil;
}
// Checks a configurator with no accounts on the device.
void CheckNoAccountsConfigurator(SigninPromoViewConfigurator* configurator,
SigninPromoViewStyle style) {
EXPECT_NE(nil, configurator);
ExpectNoAccountsConfiguration(style);
OCMExpect([close_button_ setHidden:YES]);
OCMExpect([signin_promo_view_ setPromoViewStyle:style]);
OCMExpect([signin_promo_view_ stopSignInSpinner]);
if (style == SigninPromoViewStyleCompact) {
#if BUILDFLAG(IOS_USE_BRANDED_SYMBOLS)
UIImage* logo = [UIImage imageNamed:kChromeSigninPromoLogoImage];
#else
UIImage* logo = [UIImage imageNamed:kChromiumSigninPromoLogoImage];
#endif // BUILDFLAG(IOS_USE_BRANDED_SYMBOLS)
OCMExpect([signin_promo_view_ setNonProfileImage:logo]);
}
[configurator configureSigninPromoView:signin_promo_view_ withStyle:style];
EXPECT_EQ(nil, image_view_profile_image_);
}
// Expects the signin promo view to be configured when accounts are on the
// device.
void ExpectSigninWithAccountConfiguration(SigninPromoViewStyle style) {
EXPECT_EQ(identity_, mediator_.displayedIdentity);
OCMExpect(
[signin_promo_view_ setMode:SigninPromoViewModeSigninWithAccount]);
switch (style) {
case SigninPromoViewStyleStandard: {
NSString* name = identity_.userGivenName.length
? identity_.userGivenName
: identity_.userEmail;
std::u16string name16 = SysNSStringToUTF16(name);
OCMExpect([signin_promo_view_
configurePrimaryButtonWithTitle:
GetNSStringF(IDS_IOS_SIGNIN_PROMO_CONTINUE_AS, name16)]);
OCMExpect([secondary_button_
setTitle:GetNSString(IDS_IOS_SIGNIN_PROMO_CHANGE_ACCOUNT)
forState:UIControlStateNormal]);
OCMExpect([signin_promo_view_
setProfileImage:[OCMArg checkWithBlock:^BOOL(id value) {
image_view_profile_image_ = value;
return YES;
}]]);
break;
}
case SigninPromoViewStyleCompact: {
OCMExpect([signin_promo_view_
configurePrimaryButtonWithTitle:
GetNSString(IDS_IOS_NTP_FEED_SIGNIN_PROMO_CONTINUE)]);
OCMExpect([signin_promo_view_
setProfileImage:[OCMArg checkWithBlock:^BOOL(id value) {
image_view_profile_image_ = value;
return YES;
}]]);
break;
}
case SigninPromoViewStyleOnlyButton:
OCMExpect([signin_promo_view_
configurePrimaryButtonWithTitle:GetNSString(
IDS_IOS_SIGNIN_PROMO_TURN_ON)]);
break;
}
}
// Checks a configurator with accounts on the device.
void CheckSigninWithAccountConfigurator(
SigninPromoViewConfigurator* configurator,
SigninPromoViewStyle style) {
EXPECT_NE(nil, configurator);
ExpectSigninWithAccountConfiguration(style);
OCMExpect([close_button_ setHidden:YES]);
OCMExpect([signin_promo_view_ setPromoViewStyle:style]);
OCMExpect([signin_promo_view_ stopSignInSpinner]);
[configurator configureSigninPromoView:signin_promo_view_ withStyle:style];
switch (style) {
case SigninPromoViewStyleStandard:
case SigninPromoViewStyleCompact:
EXPECT_NE(nil, image_view_profile_image_);
break;
case SigninPromoViewStyleOnlyButton:
EXPECT_EQ(nil, image_view_profile_image_);
break;
}
}
// Expects the sync promo view to be configured
void ExpectSyncPromoConfiguration() {
OCMExpect([signin_promo_view_
setMode:SigninPromoViewModeSignedInWithPrimaryAccount]);
OCMExpect([signin_promo_view_
setProfileImage:[OCMArg checkWithBlock:^BOOL(id value) {
image_view_profile_image_ = value;
return YES;
}]]);
NSString* name = identity_.userGivenName.length ? identity_.userGivenName
: identity_.userEmail;
std::u16string name16 = SysNSStringToUTF16(name);
OCMExpect([signin_promo_view_
configurePrimaryButtonWithTitle:GetNSString(
IDS_IOS_SYNC_PROMO_TURN_ON_SYNC)]);
image_view_profile_image_ = nil;
}
// Expects the review account settings promo view to be configured.
void ExpectReviewAccountSettingsPromoConfiguration() {
OCMExpect([signin_promo_view_
setMode:SigninPromoViewModeSignedInWithPrimaryAccount]);
OCMExpect([signin_promo_view_
setProfileImage:[OCMArg checkWithBlock:^BOOL(id value) {
image_view_profile_image_ = value;
return YES;
}]]);
OCMExpect([signin_promo_view_
configurePrimaryButtonWithTitle:
GetNSString(IDS_IOS_SIGNIN_PROMO_REVIEW_SETTINGS_BUTTON)]);
image_view_profile_image_ = nil;
}
// Checks a configurator with accounts on the device.
void CheckSyncPromoWithAccountConfigurator(
SigninPromoViewConfigurator* configurator,
SigninPromoViewStyle style) {
EXPECT_NE(nil, configurator);
ExpectSyncPromoConfiguration();
OCMExpect([close_button_ setHidden:YES]);
OCMExpect([signin_promo_view_ setPromoViewStyle:style]);
OCMExpect([signin_promo_view_ stopSignInSpinner]);
[configurator configureSigninPromoView:signin_promo_view_ withStyle:style];
EXPECT_NE(nil, image_view_profile_image_);
}
// Checks a configurator with a signed-in account and review account settings
// action.
void CheckPromoWithReviewAccountSettingsAction(
SigninPromoViewConfigurator* configurator,
SigninPromoViewStyle style) {
EXPECT_NE(nil, configurator);
ExpectReviewAccountSettingsPromoConfiguration();
// The close button should exist on the promo when shown on the bookmarks
// manager UI.
OCMExpect([close_button_ setHidden:NO]);
OCMExpect([signin_promo_view_ setPromoViewStyle:style]);
OCMExpect([signin_promo_view_ stopSignInSpinner]);
[configurator configureSigninPromoView:signin_promo_view_ withStyle:style];
EXPECT_NE(nil, image_view_profile_image_);
}
// Checks a configurator for recent tabs.
void CheckPromoForRecentTabs(SigninPromoViewConfigurator* configurator,
SigninPromoViewStyle style) {
EXPECT_NE(nil, configurator);
// ExpectReviewAccountSettingsPromoConfiguration();
// The close button should exist on the promo when shown on the bookmarks
// manager UI.
OCMExpect([close_button_ setHidden:YES]);
OCMExpect([signin_promo_view_
setProfileImage:[OCMArg checkWithBlock:^BOOL(id value) {
image_view_profile_image_ = value;
return YES;
}]]);
OCMExpect(
[signin_promo_view_ setMode:SigninPromoViewModeSigninWithAccount]);
OCMExpect([signin_promo_view_
configurePrimaryButtonWithTitle:GetNSStringF(
IDS_IOS_SIGNIN_PROMO_CONTINUE_AS,
SysNSStringToUTF16(
identity_.userGivenName))]);
OCMExpect([secondary_button_
setTitle:GetNSString(IDS_IOS_SIGNIN_PROMO_CHANGE_ACCOUNT)
forState:UIControlStateNormal]);
OCMExpect([signin_promo_view_ setPromoViewStyle:style]);
OCMExpect([signin_promo_view_ stopSignInSpinner]);
[configurator configureSigninPromoView:signin_promo_view_ withStyle:style];
EXPECT_NE(nil, image_view_profile_image_);
}
// Checks to receive a notification for the image upate of the current
// identity.
void CheckForImageNotification(SigninPromoViewStyle style) {
configurator_ = nil;
ExpectConfiguratorNotification(NO /* identity changed */);
fake_system_identity_manager()->WaitForServiceCallbacksToComplete();
// Check the configurator received by the consumer.
CheckSigninWithAccountConfigurator(configurator_, style);
}
FakeSystemIdentityManager* fake_system_identity_manager() {
return FakeSystemIdentityManager::FromSystemIdentityManager(
GetApplicationContext()->GetSystemIdentityManager());
}
// Task environment.
WebTaskEnvironment task_environment_;
IOSChromeScopedTestingLocalState scoped_testing_local_state_;
std::unique_ptr<TestProfileIOS> profile_;
// Mediator used for the tests.
SigninPromoViewMediator* mediator_;
// Identity used for sign-in.
id<SystemIdentity> identity_;
// Configurator received from the consumer.
SigninPromoViewConfigurator* configurator_;
// Mocks.
id<SigninPresenter> signin_presenter_;
id<AccountSettingsPresenter> account_settings_presenter_;
id<SigninPromoViewConsumer> consumer_;
SigninPromoView* signin_promo_view_;
UIButton* primary_button_;
UIButton* secondary_button_;
UIButton* close_button_;
// Value set by -[SigninPromoView setProfileImage:].
UIImage* image_view_profile_image_;
};
// Tests signin promo view and its configurator with no accounts on the device.
TEST_F(SigninPromoViewMediatorTest, NoAccountsConfigureSigninPromoView) {
CreateMediator(signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS);
TestSigninPromoWithNoAccounts(SigninPromoViewStyleStandard);
}
// Tests signin promo view and its configurator with accounts on the device.
TEST_F(SigninPromoViewMediatorTest, SigninWithAccountConfigureSigninPromoView) {
CreateMediator(signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS);
TestSigninPromoWithAccount(SigninPromoViewStyleStandard);
}
// Tests signin promo view and its configurator with an identity
// without full name.
TEST_F(SigninPromoViewMediatorTest,
SigninWithAccountConfigureSigninPromoViewWithoutName) {
CreateMediator(signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS);
TestSigninPromoWithAccount(SigninPromoViewStyleStandard);
}
// Tests the scenario with the sign-in promo when no accounts on the device, and
// then add an identity to update the view.
TEST_F(SigninPromoViewMediatorTest, ConfigureSigninPromoViewWithColdAndWarm) {
CreateMediator(signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS);
TestSigninPromoWithNoAccounts(SigninPromoViewStyleStandard);
TestSigninPromoWithAccount(SigninPromoViewStyleStandard);
}
// Tests the sign-in promo with and without account when the promo style is
// SigninPromoViewStyleOnlyButton.
TEST_F(SigninPromoViewMediatorTest,
ConfigureOnlyButtonSigninPromoViewWithColdAndWarm) {
CreateMediator(signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS);
TestSigninPromoWithNoAccounts(SigninPromoViewStyleOnlyButton);
TestSigninPromoWithAccount(SigninPromoViewStyleOnlyButton);
}
// Tests the sign-in promo with and without account when the promo style is
// compact vertical.
TEST_F(SigninPromoViewMediatorTest,
ConfigureCompactSigninPromoViewWithColdAndWarm) {
CreateMediator(signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS);
TestSigninPromoWithNoAccounts(SigninPromoViewStyleCompact);
TestSigninPromoWithAccount(SigninPromoViewStyleCompact);
}
// Tests the scenario with the sign-in promo with accounts on the device, and
// then removing the identity to update the view.
TEST_F(SigninPromoViewMediatorTest, ConfigureSigninPromoViewWithWarmAndCold) {
CreateMediator(signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS);
TestSigninPromoWithAccount(SigninPromoViewStyleStandard);
// Expect to receive a new configuration from -[Consumer
// configureSigninPromoWithConfigurator:identityChanged:].
ExpectConfiguratorNotification(YES /* identity changed */);
// Forgetting an identity is an asynchronous operation, so we need to wait
// before the notification is sent.
{
base::RunLoop run_loop;
fake_system_identity_manager()->ForgetIdentity(
identity_, base::IgnoreArgs<NSError*>(run_loop.QuitClosure()));
run_loop.Run();
}
identity_ = nil;
// Check the received configurator.
CheckNoAccountsConfigurator(configurator_, SigninPromoViewStyleStandard);
}
// Tests the view state before and after calling -[SigninPromoViewMediator
// signinPromoViewIsVisible].
TEST_F(SigninPromoViewMediatorTest, SigninPromoViewStateVisible) {
CreateMediator(signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS);
// Test initial state.
EXPECT_EQ(SigninPromoViewState::kNeverVisible,
mediator_.signinPromoViewState);
[mediator_ signinPromoViewIsVisible];
// Test state once the sign-in promo view is visible.
EXPECT_EQ(SigninPromoViewState::kUnused, mediator_.signinPromoViewState);
}
// Tests the view state while signing in.
TEST_F(SigninPromoViewMediatorTest, SigninPromoViewStateSignedin) {
CreateMediator(signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS);
[mediator_ signinPromoViewIsVisible];
__block ShowSigninCommand* command;
ShowSigninCommand* command_arg =
[OCMArg checkWithBlock:^BOOL(ShowSigninCommand* value) {
command = value;
return YES;
}];
// Start sign-in.
OCMExpect([signin_presenter_ showSignin:command_arg]);
OCMExpect([consumer_ promoProgressStateDidChange]);
ExpectConfiguratorNotification(NO /* identity changed */);
[mediator_ signinPromoViewDidTapSigninWithNewAccount:signin_promo_view_];
EXPECT_TRUE(mediator_.showSpinner);
EXPECT_EQ(SigninPromoViewState::kUsedAtLeastOnce,
mediator_.signinPromoViewState);
EXPECT_NE(nil, command.completion);
// Stop sign-in.
OCMExpect([consumer_ promoProgressStateDidChange]);
OCMExpect([consumer_ signinDidFinish]);
ExpectConfiguratorNotification(NO /* identity changed */);
command.completion(SigninCoordinatorResultSuccess, nil);
EXPECT_FALSE(mediator_.showSpinner);
EXPECT_EQ(SigninPromoViewState::kUsedAtLeastOnce,
mediator_.signinPromoViewState);
}
// Tests that no update notification is sent by the mediator to its consumer,
// while the sign-in is in progress, when an identity is added.
TEST_F(SigninPromoViewMediatorTest,
SigninPromoViewNoUpdateNotificationWhileSignin) {
CreateMediator(signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS);
[mediator_ signinPromoViewIsVisible];
__block ShowSigninCommand* command;
ShowSigninCommand* command_arg =
[OCMArg checkWithBlock:^BOOL(ShowSigninCommand* value) {
command = value;
return YES;
}];
OCMExpect([signin_presenter_ showSignin:command_arg]);
OCMExpect([consumer_ promoProgressStateDidChange]);
ExpectConfiguratorNotification(NO /* identity changed */);
// Starts sign-in without identity.
[mediator_ signinPromoViewDidTapSigninWithNewAccount:signin_promo_view_];
// Adds an identity while doing sign-in.
AddDefaultIdentity();
// No consumer notification should be expected.
fake_system_identity_manager()->WaitForServiceCallbacksToComplete();
// Finishs the sign-in.
OCMExpect([consumer_ promoProgressStateDidChange]);
OCMExpect([consumer_ signinDidFinish]);
ExpectConfiguratorNotification(NO /* identity changed */);
command.completion(SigninCoordinatorResultSuccess, nil);
}
// Tests that no update notification is sent by the mediator to its consumer,
// while the sign-in is in progress.
TEST_F(SigninPromoViewMediatorTest,
SigninPromoViewNoUpdateNotificationWhileSignin2) {
AddDefaultIdentity();
CreateMediator(signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS);
[mediator_ signinPromoViewIsVisible];
__block ShowSigninCommand* command;
ShowSigninCommand* command_arg =
[OCMArg checkWithBlock:^BOOL(ShowSigninCommand* value) {
command = value;
return YES;
}];
OCMExpect([signin_presenter_ showSignin:command_arg]);
OCMExpect([consumer_ promoProgressStateDidChange]);
ExpectConfiguratorNotification(NO /* identity changed */);
// Starts sign-in with an identity.
[mediator_
signinPromoViewDidTapPrimaryButtonWithDefaultAccount:signin_promo_view_];
EXPECT_TRUE([mediator_
conformsToProtocol:@protocol(ChromeAccountManagerServiceObserver)]);
id<ChromeAccountManagerServiceObserver> accountManagerServiceObserver =
(id<ChromeAccountManagerServiceObserver>)mediator_;
// Simulates an identity update.
[accountManagerServiceObserver identityUpdated:identity_];
// Spins the run loop to wait for the profile image update.
fake_system_identity_manager()->WaitForServiceCallbacksToComplete();
// Finishs the sign-in.
OCMExpect([consumer_ promoProgressStateDidChange]);
OCMExpect([consumer_ signinDidFinish]);
ExpectConfiguratorNotification(NO /* identity changed */);
command.completion(SigninCoordinatorResultSuccess, nil);
}
// Tests that promos aren't shown if browser sign-in is disabled by policy
TEST_F(SigninPromoViewMediatorTest,
ShouldNotDisplaySigninPromoViewIfDisabledByPolicy) {
CreateMediator(signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS);
TestProfileIOS::Builder builder;
builder.SetPrefService(CreatePrefService());
std::unique_ptr<TestProfileIOS> profile = std::move(builder).Build();
GetLocalState()->SetInteger(prefs::kBrowserSigninPolicy,
static_cast<int>(BrowserSigninMode::kDisabled));
EXPECT_FALSE([SigninPromoViewMediator
shouldDisplaySigninPromoViewWithAccessPoint:signin_metrics::AccessPoint::
ACCESS_POINT_RECENT_TABS
signinPromoAction:SigninPromoAction::
kInstantSignin
authenticationService:GetAuthenticationService()
prefService:profile->GetPrefs()]);
}
// Tests that the default identity is the primary account, when the user is
// signed in.
TEST_F(SigninPromoViewMediatorTest, SigninPromoWhileSignedIn) {
AddDefaultIdentity();
identity_ = [FakeSystemIdentity fakeIdentity2];
fake_system_identity_manager()->AddIdentity(identity_);
GetAuthenticationService()->SignIn(
identity_, signin_metrics::AccessPoint::ACCESS_POINT_SIGNIN_PROMO);
CreateMediator(signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS);
ExpectConfiguratorNotification(NO /* identity changed */);
[mediator_ signinPromoViewIsVisible];
EXPECT_EQ(identity_, mediator_.displayedIdentity);
fake_system_identity_manager()->WaitForServiceCallbacksToComplete();
CheckSyncPromoWithAccountConfigurator(configurator_,
SigninPromoViewStyleStandard);
}
// Tests that the sign-in promo view being removed and the mediator being
// deallocated while the sign-in is in progress, and tests the consumer is still
// called at the end of the sign-in.
TEST_F(SigninPromoViewMediatorTest,
RemoveSigninPromoAndDeallocMediatorWhileSignedIn) {
// Setup.
AddDefaultIdentity();
CreateMediator(signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS);
__weak __typeof(mediator_) weak_mediator = mediator_;
__block ShowSigninCommand* command;
// This test wants to verify behavior when `mediator_` gets deallocated.
// OCMock uses autorelease in places, which could result in `mediator_`
// staying allocated longer than we want without this @autoreleasepool.
@autoreleasepool {
[mediator_ signinPromoViewIsVisible];
ShowSigninCommand* command_arg =
[OCMArg checkWithBlock:^BOOL(ShowSigninCommand* value) {
command = value;
return YES;
}];
OCMExpect([signin_presenter_ showSignin:command_arg]);
OCMExpect([consumer_ promoProgressStateDidChange]);
ExpectConfiguratorNotification(NO /* identity changed */);
// Start sign-in with an identity.
[mediator_ signinPromoViewDidTapPrimaryButtonWithDefaultAccount:
signin_promo_view_];
// Remove the sign-in promo.
[mediator_ disconnect];
EXPECT_EQ(SigninPromoViewState::kInvalid, mediator_.signinPromoViewState);
// Dealloc the mediator.
mediator_ = nil;
// Also clear all invocations from `consumer_` after verifying them, as the
// invocations also contain a reference to `mediator_`. Generally this is
// what `stopMocking` is meant for, but we still want to verify that
// `signinDidfinish` is called below, so we can't just stop mocking yet.
EXPECT_OCMOCK_VERIFY(consumer_);
[(OCMockObject*)consumer_ clearInvocations];
}
EXPECT_EQ(weak_mediator, nil);
// Finish the sign-in.
OCMExpect([consumer_ signinDidFinish]);
command.completion(SigninCoordinatorResultSuccess, nil);
}
// Tests that the sign-in promo view being removed, and tests the consumer is
// still called at the end of the sign-in.
TEST_F(SigninPromoViewMediatorTest, RemoveSigninPromoWhileSignedIn) {
// Setup.
AddDefaultIdentity();
CreateMediator(signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS);
[mediator_ signinPromoViewIsVisible];
__block ShowSigninCommand* command;
ShowSigninCommand* command_arg =
[OCMArg checkWithBlock:^BOOL(ShowSigninCommand* value) {
command = value;
return YES;
}];
OCMExpect([signin_presenter_ showSignin:command_arg]);
OCMExpect([consumer_ promoProgressStateDidChange]);
ExpectConfiguratorNotification(NO /* identity changed */);
// Start sign-in with an identity.
[mediator_
signinPromoViewDidTapPrimaryButtonWithDefaultAccount:signin_promo_view_];
// Remove the sign-in promo.
[mediator_ disconnect];
EXPECT_EQ(SigninPromoViewState::kInvalid, mediator_.signinPromoViewState);
// Finish the sign-in.
OCMExpect([consumer_ signinDidFinish]);
command.completion(SigninCoordinatorResultSuccess, nil);
// Set mediator_ to nil to avoid the TearDown doesn't call
// -[mediator_ disconnect] again.
mediator_ = nil;
}
// Tests that promo setup with kSigninWithNoDefaultIdentity creates the expected
// configurator and promo.
TEST_F(SigninPromoViewMediatorTest,
SigninPromoWithSigninWithNoDefaultIdentity) {
AddDefaultIdentity();
CreateMediator(signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS);
ExpectConfiguratorNotification(NO /* identity changed */);
[mediator_ signinPromoViewIsVisible];
ExpectConfiguratorNotification(NO /* identity changed */);
[mediator_
setSigninPromoAction:SigninPromoAction::kSigninWithNoDefaultIdentity];
EXPECT_EQ(identity_, mediator_.displayedIdentity);
fake_system_identity_manager()->WaitForServiceCallbacksToComplete();
CheckPromoForRecentTabs(configurator_, SigninPromoViewStyleStandard);
}
// Tests that promo setup with review account settings promo action.
TEST_F(SigninPromoViewMediatorTest,
SigninPromoWithReviewAccountSettingsAction) {
AddDefaultIdentity();
identity_ = [FakeSystemIdentity fakeIdentity2];
fake_system_identity_manager()->AddIdentity(identity_);
GetAuthenticationService()->SignIn(
identity_, signin_metrics::AccessPoint::ACCESS_POINT_SIGNIN_PROMO);
CreateMediator(signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER);
ExpectConfiguratorNotification(NO /* identity changed */);
[mediator_ signinPromoViewIsVisible];
ExpectConfiguratorNotification(NO /* identity changed */);
[mediator_ setSigninPromoAction:SigninPromoAction::kReviewAccountSettings];
EXPECT_EQ(identity_, mediator_.displayedIdentity);
fake_system_identity_manager()->WaitForServiceCallbacksToComplete();
CheckPromoWithReviewAccountSettingsAction(configurator_,
SigninPromoViewStyleStandard);
OCMExpect([account_settings_presenter_ showAccountSettings]);
[mediator_
signinPromoViewDidTapPrimaryButtonWithDefaultAccount:signin_promo_view_];
EXPECT_EQ(SigninPromoViewState::kUsedAtLeastOnce,
mediator_.signinPromoViewState);
}
// Tests that review settings promo is not shown if the user has already
// dismissed it, but the signin promo should not be affected.
TEST_F(SigninPromoViewMediatorTest,
ShouldNotDisplaySigninPromoViewIfAlreadySeen) {
CreateMediator(signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER);
TestProfileIOS::Builder builder;
builder.SetPrefService(CreatePrefService());
std::unique_ptr<TestProfileIOS> profile = std::move(builder).Build();
profile->GetPrefs()->SetBoolean(prefs::kIosBookmarkSettingsPromoAlreadySeen,
true);
EXPECT_FALSE([SigninPromoViewMediator
shouldDisplaySigninPromoViewWithAccessPoint:
signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER
signinPromoAction:SigninPromoAction::
kReviewAccountSettings
authenticationService:GetAuthenticationService()
prefService:profile->GetPrefs()]);
EXPECT_TRUE([SigninPromoViewMediator
shouldDisplaySigninPromoViewWithAccessPoint:
signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_MANAGER
signinPromoAction:SigninPromoAction::
kInstantSignin
authenticationService:GetAuthenticationService()
prefService:profile->GetPrefs()]);
}
} // namespace