blob: 757615e84c3e4ee741c189cfcd3af4e741413523 [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/settings/sync/sync_settings_table_view_controller.h"
#include <memory>
#include "base/bind.h"
#import "base/mac/foundation_util.h"
#include "base/strings/sys_string_conversions.h"
#include "components/autofill/core/common/autofill_prefs.h"
#include "components/browser_sync/profile_sync_service_mock.h"
#include "components/google/core/common/google_util.h"
#include "components/strings/grit/components_strings.h"
#include "components/sync_preferences/pref_service_mock_factory.h"
#include "components/sync_preferences/pref_service_syncable.h"
#include "components/unified_consent/feature.h"
#include "ios/chrome/browser/application_context.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
#include "ios/chrome/browser/chrome_url_constants.h"
#include "ios/chrome/browser/experimental_flags.h"
#include "ios/chrome/browser/prefs/browser_prefs.h"
#include "ios/chrome/browser/sync/ios_chrome_profile_sync_test_util.h"
#include "ios/chrome/browser/sync/profile_sync_service_factory.h"
#include "ios/chrome/browser/sync/sync_setup_service.h"
#include "ios/chrome/browser/sync/sync_setup_service_factory.h"
#include "ios/chrome/browser/sync/sync_setup_service_mock.h"
#import "ios/chrome/browser/ui/settings/cells/sync_switch_item.h"
#import "ios/chrome/browser/ui/settings/sync/utils/sync_util.h"
#import "ios/chrome/browser/ui/table_view/cells/table_view_image_item.h"
#import "ios/chrome/browser/ui/table_view/chrome_table_view_controller_test.h"
#import "ios/chrome/browser/ui/table_view/table_view_model.h"
#include "ios/chrome/grit/ios_strings.h"
#include "ios/web/public/test/test_web_thread_bundle.h"
#import "testing/gtest_mac.h"
#include "ui/base/l10n/l10n_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface SyncSettingsTableViewController (ExposedForTesting)
- (int)titleIdForSyncableDataType:(SyncSetupService::SyncableDatatype)datatype;
- (void)onSyncStateChanged;
@end
namespace {
using testing::DefaultValue;
using testing::NiceMock;
using testing::Return;
class SyncSetupServiceMockThatFails : public SyncSetupServiceMock {
public:
SyncSetupServiceMockThatFails(syncer::SyncService* sync_service)
: SyncSetupServiceMock(sync_service) {}
bool IsSyncEnabled() const override { return sync_enabled_; }
void SetSyncEnabled(bool sync_enabled) override {}
bool IsSyncingAllDataTypes() const override { return sync_all_; }
void SetSyncingAllDataTypes(bool sync_all) override {}
static void SetSyncEnabledTestValue(bool sync_enabled) {
sync_enabled_ = sync_enabled;
}
static void SetSyncEverythingTestValue(bool sync_all) {
sync_all_ = sync_all;
}
protected:
static bool sync_enabled_;
static bool sync_all_;
};
bool SyncSetupServiceMockThatFails::sync_enabled_ = true;
bool SyncSetupServiceMockThatFails::sync_all_ = true;
class SyncSetupServiceMockThatSucceeds : public SyncSetupServiceMockThatFails {
public:
SyncSetupServiceMockThatSucceeds(syncer::SyncService* sync_service)
: SyncSetupServiceMockThatFails(sync_service) {}
void SetSyncEnabled(bool sync_enabled) override {
sync_enabled_ = sync_enabled;
}
void SetSyncingAllDataTypes(bool sync_all) override { sync_all_ = sync_all; }
};
class SyncSettingsTableViewControllerTest
: public ChromeTableViewControllerTest {
public:
SyncSettingsTableViewControllerTest()
: default_auth_error_(GoogleServiceAuthError::NONE) {}
static std::unique_ptr<KeyedService> CreateSyncSetupService(
web::BrowserState* context) {
ios::ChromeBrowserState* chrome_browser_state =
ios::ChromeBrowserState::FromBrowserState(context);
syncer::SyncService* sync_service =
ProfileSyncServiceFactory::GetForBrowserState(chrome_browser_state);
return std::make_unique<NiceMock<SyncSetupServiceMock>>(sync_service);
}
static std::unique_ptr<KeyedService> CreateSucceedingSyncSetupService(
web::BrowserState* context) {
ios::ChromeBrowserState* chrome_browser_state =
ios::ChromeBrowserState::FromBrowserState(context);
syncer::SyncService* sync_service =
ProfileSyncServiceFactory::GetForBrowserState(chrome_browser_state);
return std::make_unique<NiceMock<SyncSetupServiceMockThatSucceeds>>(
sync_service);
}
static std::unique_ptr<KeyedService> CreateFailingSyncSetupService(
web::BrowserState* context) {
ios::ChromeBrowserState* chrome_browser_state =
ios::ChromeBrowserState::FromBrowserState(context);
syncer::SyncService* sync_service =
ProfileSyncServiceFactory::GetForBrowserState(chrome_browser_state);
return std::make_unique<NiceMock<SyncSetupServiceMockThatFails>>(
sync_service);
}
static std::unique_ptr<KeyedService> CreateProfileSyncService(
web::BrowserState* context) {
browser_sync::ProfileSyncService::InitParams init_params =
CreateProfileSyncServiceParamsForTest(
nullptr, ios::ChromeBrowserState::FromBrowserState(context));
return std::make_unique<NiceMock<browser_sync::ProfileSyncServiceMock>>(
std::move(init_params));
}
std::unique_ptr<sync_preferences::PrefServiceSyncable> CreatePrefService() {
scoped_refptr<user_prefs::PrefRegistrySyncable> registry(
new user_prefs::PrefRegistrySyncable);
RegisterBrowserStatePrefs(registry.get());
sync_preferences::PrefServiceMockFactory factory;
return factory.CreateSyncable(registry.get());
}
void TurnSyncOn() {
ON_CALL(*mock_sync_setup_service_, IsSyncEnabled())
.WillByDefault(Return(true));
}
void TurnSyncEverythingOn() {
ON_CALL(*mock_sync_setup_service_, IsSyncingAllDataTypes())
.WillByDefault(Return(true));
}
void SetSyncServiceState(SyncSetupService::SyncServiceState state) {
ON_CALL(*mock_sync_setup_service_, GetSyncServiceState())
.WillByDefault(Return(state));
}
void TurnSyncPassphraseErrorOn() {
SetSyncServiceState(SyncSetupService::kSyncServiceNeedsPassphrase);
}
void TurnSyncErrorOff() {
SetSyncServiceState(SyncSetupService::kNoSyncServiceError);
}
protected:
void SetUp() override {
TestChromeBrowserState::Builder test_cbs_builder;
test_cbs_builder.AddTestingFactory(
SyncSetupServiceFactory::GetInstance(),
base::BindRepeating(&CreateSyncSetupService));
test_cbs_builder.AddTestingFactory(
ProfileSyncServiceFactory::GetInstance(),
base::BindRepeating(&CreateProfileSyncService));
test_cbs_builder.SetPrefService(CreatePrefService());
chrome_browser_state_ = test_cbs_builder.Build();
ChromeTableViewControllerTest::SetUp();
DefaultValue<const GoogleServiceAuthError&>::Set(default_auth_error_);
DefaultValue<syncer::SyncCycleSnapshot>::Set(default_sync_cycle_snapshot_);
mock_sync_setup_service_ = static_cast<NiceMock<SyncSetupServiceMock>*>(
SyncSetupServiceFactory::GetForBrowserState(
chrome_browser_state_.get()));
// The other mocked functions of SyncSetupServiceMock return bools, so they
// will by default return false. |syncServiceState|, however, returns an
// enum, and thus always needs its default value set.
TurnSyncErrorOff();
mock_profile_sync_service_ =
static_cast<browser_sync::ProfileSyncServiceMock*>(
ProfileSyncServiceFactory::GetForBrowserState(
chrome_browser_state_.get()));
ON_CALL(*mock_profile_sync_service_, GetTransportState())
.WillByDefault(Return(syncer::SyncService::TransportState::ACTIVE));
ON_CALL(*mock_profile_sync_service_, GetRegisteredDataTypes())
.WillByDefault(Return(syncer::ModelTypeSet()));
mock_profile_sync_service_->Initialize();
EXPECT_CALL(*mock_profile_sync_service_->GetUserSettingsMock(),
GetChosenDataTypes())
.WillRepeatedly(Return(syncer::UserSelectableTypes()));
}
ChromeTableViewController* InstantiateController() override {
return [[SyncSettingsTableViewController alloc]
initWithBrowserState:chrome_browser_state_.get()
allowSwitchSyncAccount:YES];
}
SyncSettingsTableViewController* CreateSyncController() {
CreateController();
CheckTitleWithId(IDS_IOS_SYNC_SETTING_TITLE);
return static_cast<SyncSettingsTableViewController*>(controller());
}
void CheckErrorIcon(BOOL expectedShouldDisplayError,
NSInteger section,
NSInteger item) {
TableViewModel* model = [controller() tableViewModel];
NSIndexPath* path = [NSIndexPath indexPathForItem:item inSection:section];
TableViewImageItem* errorObject =
base::mac::ObjCCastStrict<TableViewImageItem>(
[model itemAtIndexPath:path]);
EXPECT_TRUE(errorObject);
BOOL hasImage = !!errorObject.image;
EXPECT_EQ(expectedShouldDisplayError, hasImage);
}
web::TestWebThreadBundle thread_bundle_;
std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
// Weak, owned by |profile_|.
NiceMock<SyncSetupServiceMock>* mock_sync_setup_service_;
// Weak, owned by |profile_|.
browser_sync::ProfileSyncServiceMock* mock_profile_sync_service_;
// Default return values for ProfileSyncServiceMock.
GoogleServiceAuthError default_auth_error_;
syncer::SyncCycleSnapshot default_sync_cycle_snapshot_;
};
TEST_F(SyncSettingsTableViewControllerTest, TestModel) {
TurnSyncEverythingOn();
TurnSyncOn();
SyncSettingsTableViewController* sync_controller = CreateSyncController();
EXPECT_EQ(3, NumberOfSections());
EXPECT_EQ(1, NumberOfItemsInSection(0));
// There are two extra items, one for "Sync Everything" and another for
// Autofill wallet import.
constexpr int expected_number_of_items =
SyncSetupService::kNumberOfSyncableDatatypes + 2;
EXPECT_EQ(expected_number_of_items, NumberOfItemsInSection(1));
EXPECT_EQ(2, NumberOfItemsInSection(2));
SyncSwitchItem* syncItem = GetTableViewItem(0, 0);
EXPECT_EQ(syncItem.isOn, YES);
EXPECT_NSEQ(syncItem.text,
l10n_util::GetNSString(IDS_IOS_SYNC_SETTING_TITLE));
EXPECT_NSEQ(
syncItem.detailText,
l10n_util::GetNSString(IDS_IOS_SIGN_IN_TO_CHROME_SETTING_SUBTITLE));
SyncSwitchItem* syncEverythingItem = GetTableViewItem(1, 0);
EXPECT_EQ(syncEverythingItem.isOn, YES);
EXPECT_NSEQ(syncEverythingItem.text,
l10n_util::GetNSString(IDS_IOS_SYNC_EVERYTHING_TITLE));
int item = 1;
for (int i = 0; i < SyncSetupService::kNumberOfSyncableDatatypes; i++) {
SyncSetupService::SyncableDatatype dataType =
static_cast<SyncSetupService::SyncableDatatype>(i);
SyncSwitchItem* syncDataTypeItem = GetTableViewItem(1, item++);
EXPECT_NSEQ(syncDataTypeItem.text,
l10n_util::GetNSString(
[sync_controller titleIdForSyncableDataType:dataType]));
}
SyncSwitchItem* autofillWalletImportItem = GetTableViewItem(1, item);
NSString* title = l10n_util::GetNSString(
IDS_AUTOFILL_ENABLE_PAYMENTS_INTEGRATION_CHECKBOX_LABEL);
EXPECT_NSEQ(autofillWalletImportItem.text, title);
TableViewImageItem* encryptionItem = GetTableViewItem(2, 0);
EXPECT_NSEQ(encryptionItem.title,
l10n_util::GetNSString(IDS_IOS_SYNC_ENCRYPTION_TITLE));
CheckErrorIcon(NO, 2, 0);
}
TEST_F(SyncSettingsTableViewControllerTest, TestEnabledCellsSyncOff) {
CreateSyncController();
for (int item = 0; item < NumberOfItemsInSection(1); ++item) {
SyncSwitchItem* object = GetTableViewItem(1, item);
EXPECT_FALSE(object.enabled);
}
TableViewImageItem* encryptionItem = GetTableViewItem(2, 0);
EXPECT_FALSE(encryptionItem.enabled);
}
TEST_F(SyncSettingsTableViewControllerTest, TestEnabledCellsSyncEverythingOn) {
TurnSyncOn();
TurnSyncEverythingOn();
CreateSyncController();
SyncSwitchItem* syncEverythingItem = GetTableViewItem(1, 0);
EXPECT_TRUE(syncEverythingItem.enabled);
for (int item = 1; item <= SyncSetupService::kNumberOfSyncableDatatypes;
++item) {
SyncSwitchItem* object = GetTableViewItem(1, item);
EXPECT_FALSE(object.enabled);
}
SyncSwitchItem* autofillWalletImportItem =
GetTableViewItem(1, NumberOfItemsInSection(1) - 1);
EXPECT_FALSE(autofillWalletImportItem.enabled);
TableViewImageItem* encryptionItem = GetTableViewItem(2, 0);
EXPECT_TRUE(encryptionItem.enabled);
}
TEST_F(SyncSettingsTableViewControllerTest, TestAutofillWalletImportOff) {
autofill::prefs::SetPaymentsIntegrationEnabled(
chrome_browser_state_->GetPrefs(), false);
TurnSyncOn();
TurnSyncEverythingOn();
CreateSyncController();
SyncSwitchItem* autofillWalletImportItem =
GetTableViewItem(1, NumberOfItemsInSection(1) - 1);
EXPECT_FALSE(autofillWalletImportItem.isOn);
}
TEST_F(SyncSettingsTableViewControllerTest, TestAutofillWalletImportOn) {
autofill::prefs::SetPaymentsIntegrationEnabled(
chrome_browser_state_->GetPrefs(), true);
TurnSyncOn();
TurnSyncEverythingOn();
CreateSyncController();
SyncSwitchItem* autofillWalletImportItem =
GetTableViewItem(1, NumberOfItemsInSection(1) - 1);
EXPECT_TRUE(autofillWalletImportItem.isOn);
}
TEST_F(SyncSettingsTableViewControllerTest, TestShouldDisplayError) {
SyncSettingsTableViewController* sync_controller = CreateSyncController();
EXPECT_FALSE([sync_controller shouldDisplaySyncError]);
EXPECT_FALSE([sync_controller shouldDisableSettingsOnSyncError]);
EXPECT_FALSE([sync_controller shouldDisplayEncryptionError]);
SetSyncServiceState(SyncSetupService::kSyncServiceSignInNeedsUpdate);
EXPECT_TRUE([sync_controller shouldDisplaySyncError]);
EXPECT_TRUE([sync_controller shouldDisableSettingsOnSyncError]);
EXPECT_FALSE([sync_controller shouldDisplayEncryptionError]);
SetSyncServiceState(SyncSetupService::kSyncServiceCouldNotConnect);
EXPECT_TRUE([sync_controller shouldDisplaySyncError]);
EXPECT_TRUE([sync_controller shouldDisableSettingsOnSyncError]);
EXPECT_FALSE([sync_controller shouldDisplayEncryptionError]);
SetSyncServiceState(SyncSetupService::kSyncServiceServiceUnavailable);
EXPECT_TRUE([sync_controller shouldDisplaySyncError]);
EXPECT_TRUE([sync_controller shouldDisableSettingsOnSyncError]);
EXPECT_FALSE([sync_controller shouldDisplayEncryptionError]);
SetSyncServiceState(SyncSetupService::kSyncServiceNeedsPassphrase);
EXPECT_TRUE([sync_controller shouldDisplaySyncError]);
EXPECT_FALSE([sync_controller shouldDisableSettingsOnSyncError]);
EXPECT_TRUE([sync_controller shouldDisplayEncryptionError]);
SetSyncServiceState(SyncSetupService::kSyncServiceUnrecoverableError);
EXPECT_TRUE([sync_controller shouldDisplaySyncError]);
EXPECT_TRUE([sync_controller shouldDisableSettingsOnSyncError]);
EXPECT_FALSE([sync_controller shouldDisplayEncryptionError]);
SetSyncServiceState(SyncSetupService::kNoSyncServiceError);
EXPECT_FALSE([sync_controller shouldDisplaySyncError]);
EXPECT_FALSE([sync_controller shouldDisableSettingsOnSyncError]);
EXPECT_FALSE([sync_controller shouldDisplayEncryptionError]);
}
TEST_F(SyncSettingsTableViewControllerTest,
TestSyncStateChangedWithEncryptionError) {
TurnSyncOn();
TurnSyncEverythingOn();
SyncSettingsTableViewController* sync_controller = CreateSyncController();
TableViewImageItem* encryptionItem = GetTableViewItem(2, 0);
EXPECT_NSEQ(encryptionItem.title,
l10n_util::GetNSString(IDS_IOS_SYNC_ENCRYPTION_TITLE));
CheckErrorIcon(NO, 2, 0);
TurnSyncPassphraseErrorOn();
[sync_controller onSyncStateChanged];
encryptionItem = GetTableViewItem(3, 0);
EXPECT_NSEQ(encryptionItem.title,
l10n_util::GetNSString(IDS_IOS_SYNC_ENCRYPTION_TITLE));
CheckErrorIcon(YES, 3, 0);
TurnSyncErrorOff();
[sync_controller onSyncStateChanged];
encryptionItem = GetTableViewItem(2, 0);
EXPECT_NSEQ(encryptionItem.title,
l10n_util::GetNSString(IDS_IOS_SYNC_ENCRYPTION_TITLE));
CheckErrorIcon(NO, 2, 0);
}
} // namespace