// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "base/ios/ios_util.h"
#import "base/strings/sys_string_conversions.h"
#import "base/test/ios/wait_util.h"
#import "base/time/time.h"
#import "components/browser_sync/browser_sync_switches.h"
#import "components/sync/base/command_line_switches.h"
#import "components/sync/base/data_type.h"
#import "components/sync/base/features.h"
#import "ios/chrome/browser/bookmarks/model/bookmark_storage_type.h"
#import "ios/chrome/browser/bookmarks/ui_bundled/bookmark_earl_grey.h"
#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/shared/ui/table_view/table_view_navigation_controller_constants.h"
#import "ios/chrome/browser/signin/model/fake_system_identity.h"
#import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
#import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui_test_util.h"
#import "ios/chrome/browser/ui/reading_list/reading_list_app_interface.h"
#import "ios/chrome/browser/ui/reading_list/reading_list_egtest_utils.h"
#import "ios/chrome/browser/ui/settings/google_services/manage_sync_settings_constants.h"
#import "ios/chrome/browser/ui/settings/password/password_manager_egtest_utils.h"
#import "ios/chrome/browser/ui/settings/password/password_settings_app_interface.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ios/chrome/test/earl_grey/chrome_actions.h"
#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
#import "ios/chrome/test/earl_grey/chrome_matchers.h"
#import "ios/chrome/test/earl_grey/test_switches.h"
#import "ios/chrome/test/earl_grey/web_http_server_chrome_test_case.h"
#import "ios/testing/earl_grey/app_launch_manager.h"
#import "ios/testing/earl_grey/earl_grey_test.h"
#import "ios/web/public/test/http_server/http_server.h"
#import "ios/web/public/test/http_server/http_server_util.h"
#import "net/base/apple/url_conversions.h"
#import "net/test/embedded_test_server/embedded_test_server.h"
#import "ui/base/l10n/l10n_util.h"
namespace {
// Constant for timeout while waiting for asynchronous sync operations.
constexpr base::TimeDelta kSyncOperationTimeout = base::Seconds(10);
constexpr NSString* kBookmarkUrl = @"https://www.goo.com/";
constexpr NSString* kBookmarkTitle = @"Goo";
constexpr NSString* kReadingListUrl = @"https://www.rl.com/";
constexpr NSString* kReadingListTitle = @"RL";
constexpr NSString* kPassphrase = @"passphrase";
// Waits for `entity_count` entities of type `entity_type` on the fake server,
// and fails with a GREYAssert if the condition is not met, within a short
// period of time.
void WaitForEntitiesOnFakeServer(int entity_count,
syncer::DataType entity_type) {
ConditionBlock condition = ^{
return [ChromeEarlGrey numberOfSyncEntitiesWithType:entity_type] ==
entity_count;
};
GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(kSyncOperationTimeout,
condition),
@"Expected %d %s entities but found %d", entity_count,
syncer::DataTypeToDebugString(entity_type),
[ChromeEarlGrey numberOfSyncEntitiesWithType:entity_type]);
}
void ClearRelevantData() {
[BookmarkEarlGrey clearBookmarks];
GREYAssertNil([ReadingListAppInterface clearEntries],
@"Unable to clear Reading List entries");
[PasswordSettingsAppInterface clearPasswordStores];
[ChromeEarlGrey clearFakeSyncServerData];
WaitForEntitiesOnFakeServer(0, syncer::BOOKMARKS);
WaitForEntitiesOnFakeServer(0, syncer::HISTORY);
WaitForEntitiesOnFakeServer(0, syncer::PASSWORDS);
WaitForEntitiesOnFakeServer(0, syncer::READING_LIST);
// Ensure that all of the changes made are flushed to disk before the app is
// terminated.
[ChromeEarlGrey flushFakeSyncServerToDisk];
[ChromeEarlGrey commitPendingUserPrefsWrite];
[BookmarkEarlGrey commitPendingWrite];
// Note that the ReadingListModel immediately writes pending changes to disk,
// so no need for an explicit "flush" there.
}
} // namespace
// Hermetic sync tests, which use the fake sync server.
@interface SyncFakeServerTestCase : WebHttpServerChromeTestCase
@end
@implementation SyncFakeServerTestCase
+ (void)setUpForTestCase {
[super setUpForTestCase];
[BookmarkEarlGrey waitForBookmarkModelLoaded];
// Normally there shouldn't be any data (locally or on the fake server) at
// this point, but just in case some other test case didn't clean up after
// itself, clear everything here.
ClearRelevantData();
}
- (void)setUp {
[super setUp];
GREYAssertTrue(self.testServer->Start(), @"Server did not start.");
}
- (void)tearDown {
ClearRelevantData();
[super tearDown];
}
- (AppLaunchConfiguration)appConfigurationForTestCase {
AppLaunchConfiguration config = [super appConfigurationForTestCase];
config.additional_args.push_back(std::string("--") +
syncer::kSyncShortNudgeDelayForTest);
if ([self isRunningTest:@selector(testMigrateSyncToSignin)] ||
[self
isRunningTest:@selector(testMigrateSyncToSignin_PasswordsDisabled)] ||
[self
isRunningTest:@selector(testMigrateSyncToSignin_BookmarksDisabled)] ||
[self isRunningTest:@selector
(testMigrateSyncToSignin_ReadingListDisabled)] ||
[self isRunningTest:@selector(testMigrateSyncToSignin_SyncNotActive)] ||
[self
isRunningTest:@selector(testMigrateSyncToSignin_CustomPassphrase)] ||
[self isRunningTest:@selector
(testMigrateSyncToSignin_CustomPassphraseMissing)] ||
[self isRunningTest:@selector(testMigrateSyncToSignin_ManagedAccount)] ||
[self isRunningTest:@selector(testMigrateSyncToSignin_Undo)]) {
// The testMigrateSyncToSignin* tests start with
// kMigrateSyncingUserToSignedIn disabled, but later turn on the flag and
// restart Chrome.
config.features_disabled.push_back(switches::kMigrateSyncingUserToSignedIn);
}
if ([self isRunningTest:@selector
(testManagedAccountClearsDataForSignedInPeriod)]) {
config.features_enabled.push_back(kClearDeviceDataOnSignOutForManagedUsers);
config.features_disabled.push_back(kIdentityDiscAccountMenu);
}
if ([self isRunningTest:@selector
(testManagedAccountClearsDataAndTabsForSignedInPeriod)]) {
config.features_enabled.push_back(kClearDeviceDataOnSignOutForManagedUsers);
config.features_enabled.push_back(kIdentityDiscAccountMenu);
}
return config;
}
- (void)relaunchWithIdentity:(FakeSystemIdentity*)identity
enabledFeatures:(const std::vector<base::test::FeatureRef>&)enabled
disabledFeatures:
(const std::vector<base::test::FeatureRef>&)disabled {
// Before restarting, ensure that the FakeServer has written all its pending
// state to disk.
[ChromeEarlGrey flushFakeSyncServerToDisk];
// Also make sure any pending prefs and bookmarks changes are written to disk.
[ChromeEarlGrey commitPendingUserPrefsWrite];
[BookmarkEarlGrey commitPendingWrite];
AppLaunchConfiguration config = [self appConfigurationForTestCase];
config.relaunch_policy = ForceRelaunchByCleanShutdown;
config.features_enabled = enabled;
config.features_disabled = disabled;
config.additional_args.push_back(
base::StrCat({"--", test_switches::kSignInAtStartup}));
config.additional_args.push_back(base::StrCat({
"-", test_switches::kAddFakeIdentitiesAtStartup, "=",
[FakeSystemIdentity encodeIdentitiesToBase64:@[ identity ]]
}));
[[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];
// After the relaunch, wait for Sync-the-transport to become active again
// (which should always happen if there's a signed-in account).
[ChromeEarlGrey
waitForSyncTransportStateActiveWithTimeout:kSyncOperationTimeout];
}
// Opens the legacy Sync settings, and disables the data type whose toggle is
// identified by `typeIdentifier` (e.g. `kSyncPasswordsIdentifier`).
- (void)disableTypeForSyncTheFeature:(NSString*)typeIdentifier {
[ChromeEarlGreyUI openSettingsMenu];
[ChromeEarlGreyUI
tapSettingsMenuButton:chrome_test_util::ManageSyncSettingsButton()];
[[EarlGrey
selectElementWithMatcher:grey_accessibilityID(
kSyncEverythingItemAccessibilityIdentifier)]
performAction:chrome_test_util::TurnTableViewSwitchOn(/*on=*/NO)];
[[EarlGrey selectElementWithMatcher:chrome_test_util::TableViewSwitchCell(
typeIdentifier,
/*is_toggled_on=*/YES,
/*is_enabled=*/YES)]
performAction:chrome_test_util::TurnTableViewSwitchOn(/*on=*/NO)];
[[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsDoneButton()]
performAction:grey_tap()];
}
- (void)enableTypeForSyncTheFeature:(NSString*)typeIdentifier {
[ChromeEarlGreyUI openSettingsMenu];
[ChromeEarlGreyUI
tapSettingsMenuButton:chrome_test_util::ManageSyncSettingsButton()];
[[EarlGrey selectElementWithMatcher:chrome_test_util::TableViewSwitchCell(
typeIdentifier,
/*is_toggled_on=*/NO,
/*is_enabled=*/YES)]
performAction:chrome_test_util::TurnTableViewSwitchOn(/*on=*/YES)];
[[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsDoneButton()]
performAction:grey_tap()];
}
// Tests that a bookmark added on the client is uploaded to the Sync server.
- (void)testSyncUploadBookmark {
FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
[SigninEarlGrey addFakeIdentity:fakeIdentity];
[SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
// Add a bookmark after sync is initialized.
[ChromeEarlGrey waitForSyncEngineInitialized:YES
syncTimeout:kSyncOperationTimeout];
[BookmarkEarlGrey addBookmarkWithTitle:@"goo"
URL:@"https://www.goo.com"
inStorage:BookmarkStorageType::kAccount];
WaitForEntitiesOnFakeServer(1, syncer::BOOKMARKS);
}
// Tests that a bookmark injected in the FakeServer is synced down to the
// client.
- (void)testSyncDownloadBookmark {
[BookmarkEarlGrey verifyBookmarksWithTitle:@"hoo"
expectedCount:0
inStorage:BookmarkStorageType::kAccount];
const GURL URL = web::test::HttpServer::MakeUrl("http://www.hoo.com");
[ChromeEarlGrey addFakeSyncServerBookmarkWithURL:URL title:"hoo"];
// Sign in to sync, after a bookmark has been injected in the sync server.
FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
[SigninEarlGrey addFakeIdentity:fakeIdentity];
[SigninEarlGrey signinWithFakeIdentity:fakeIdentity];
[ChromeEarlGrey
waitForSyncTransportStateActiveWithTimeout:kSyncOperationTimeout];
[BookmarkEarlGrey verifyBookmarksWithTitle:@"hoo"
expectedCount:1
inStorage:BookmarkStorageType::kAccount];
}
// Tests that the local cache guid is reused when the user signs out and then
// signs back in with the same account.
- (void)testSyncCheckSameCacheGuid_SignOutAndSignIn {
// Sign in a fake identity, and store the initial sync guid.
FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
[SigninEarlGrey signinWithFakeIdentity:fakeIdentity];
[ChromeEarlGrey
waitForSyncTransportStateActiveWithTimeout:kSyncOperationTimeout];
std::string original_guid = [ChromeEarlGrey syncCacheGUID];
[SigninEarlGrey verifySignedInWithFakeIdentity:fakeIdentity];
[SigninEarlGrey signOut];
[ChromeEarlGrey waitForSyncEngineInitialized:NO
syncTimeout:kSyncOperationTimeout];
// Sign the user back in, and verify the guid has *not* changed.
[SigninEarlGrey signinWithFakeIdentity:fakeIdentity];
[ChromeEarlGrey
waitForSyncTransportStateActiveWithTimeout:kSyncOperationTimeout];
GREYAssertTrue([ChromeEarlGrey syncCacheGUID] == original_guid,
@"guid changed after user signed out and signed back in");
}
// Tests that the local cache guid changes when the user signs out and then
// signs back in with a different account.
- (void)testSyncCheckDifferentCacheGuid_SignOutAndSignInWithDifferentAccount {
// Sign in a fake identity, and store the initial sync guid.
FakeSystemIdentity* fakeIdentity1 = [FakeSystemIdentity fakeIdentity1];
[SigninEarlGrey signinWithFakeIdentity:fakeIdentity1];
[ChromeEarlGrey
waitForSyncTransportStateActiveWithTimeout:kSyncOperationTimeout];
std::string original_guid = [ChromeEarlGrey syncCacheGUID];
[SigninEarlGrey verifySignedInWithFakeIdentity:fakeIdentity1];
[SigninEarlGrey signOut];
[ChromeEarlGrey waitForSyncEngineInitialized:NO
syncTimeout:kSyncOperationTimeout];
// Sign a different user in, and verify the guid has changed.
FakeSystemIdentity* fakeIdentity2 = [FakeSystemIdentity fakeIdentity2];
[SigninEarlGrey signinWithFakeIdentity:fakeIdentity2];
[ChromeEarlGrey
waitForSyncTransportStateActiveWithTimeout:kSyncOperationTimeout];
GREYAssertTrue(
[ChromeEarlGrey syncCacheGUID] != original_guid,
@"guid didn't change after user signed out and different user signed in");
}
// Tests that tabs opened on this client are committed to the Sync server and
// that the created sessions entities are correct.
- (void)testSyncUploadOpenTabs {
// Create map of canned responses and set up the test HTML server.
const GURL URL1 = web::test::HttpServer::MakeUrl("http://page1");
const GURL URL2 = web::test::HttpServer::MakeUrl("http://page2");
std::map<GURL, std::string> responses = {
{URL1, std::string("page 1")},
{URL2, std::string("page 2")},
};
web::test::SetUpSimpleHttpServer(responses);
// Load both URLs in separate tabs.
[ChromeEarlGrey loadURL:URL1];
[ChromeEarlGrey openNewTab];
[ChromeEarlGrey loadURL:URL2];
// Sign in to sync, after opening two tabs.
FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
[SigninEarlGrey addFakeIdentity:fakeIdentity];
[SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity enableHistorySync:YES];
// Verify the sessions on the sync server.
[ChromeEarlGrey waitForSyncEngineInitialized:YES
syncTimeout:kSyncOperationTimeout];
WaitForEntitiesOnFakeServer(3, syncer::SESSIONS);
NSArray<NSString*>* specs = @[
base::SysUTF8ToNSString(URL1.spec()),
base::SysUTF8ToNSString(URL2.spec()),
];
[ChromeEarlGrey verifySyncServerSessionURLs:specs];
}
// Tests that browsing history is uploaded to the Sync server.
- (void)testSyncHistoryUpload {
const GURL preSyncURL = self.testServer->GetURL("/console.html");
const GURL whileSyncURL = self.testServer->GetURL("/pony.html");
const GURL postSyncURL = self.testServer->GetURL("/destination.html");
[ChromeEarlGrey clearBrowsingHistory];
[self setTearDownHandler:^{
[ChromeEarlGrey clearBrowsingHistory];
}];
// Visit a URL before turning on Sync.
[ChromeEarlGrey loadURL:preSyncURL];
// Navigate away from that URL.
[ChromeEarlGrey loadURL:whileSyncURL];
// Sign in and wait for sync to become active.
FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
[SigninEarlGrey addFakeIdentity:fakeIdentity];
[SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity enableHistorySync:YES];
[ChromeEarlGrey
waitForSyncTransportStateActiveWithTimeout:kSyncOperationTimeout];
// Navigate again. This URL, plus the URL that was currently open when Sync
// was turned on, should arrive on the Sync server.
[ChromeEarlGrey loadURL:postSyncURL];
// This URL, plus the URL that was currently open when Sync was turned on,
// should arrive on the Sync server.
NSArray<NSURL*>* URLs = @[
net::NSURLWithGURL(whileSyncURL),
net::NSURLWithGURL(postSyncURL),
];
[ChromeEarlGrey waitForSyncServerHistoryURLs:URLs
timeout:kSyncOperationTimeout];
}
// Tests that history is downloaded from the sync server.
- (void)testSyncHistoryDownload {
const GURL mockURL("http://not-a-real-site/");
[ChromeEarlGrey clearBrowsingHistory];
[self setTearDownHandler:^{
[ChromeEarlGrey clearBrowsingHistory];
}];
// Inject a history visit on the server.
[ChromeEarlGrey addFakeSyncServerHistoryVisit:mockURL];
// Sign in and wait for sync to become active.
FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
[SigninEarlGrey addFakeIdentity:fakeIdentity];
[SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity enableHistorySync:YES];
[ChromeEarlGrey
waitForSyncTransportStateActiveWithTimeout:kSyncOperationTimeout];
// Wait for the visit to appear on the client.
[ChromeEarlGrey waitForHistoryURL:mockURL
expectPresent:YES
timeout:kSyncOperationTimeout];
}
// Tests download of two legacy bookmarks with the same item id.
- (void)testDownloadTwoPre2015BookmarksWithSameItemId {
const GURL URL1 = web::test::HttpServer::MakeUrl("http://page1.com");
const GURL URL2 = web::test::HttpServer::MakeUrl("http://page2.com");
NSString* title1 = @"title1";
NSString* title2 = @"title2";
[BookmarkEarlGrey verifyBookmarksWithTitle:title1
expectedCount:0
inStorage:BookmarkStorageType::kAccount];
[BookmarkEarlGrey verifyBookmarksWithTitle:title2
expectedCount:0
inStorage:BookmarkStorageType::kAccount];
// Mimic the creation of two bookmarks from two different devices, with the
// same client item ID.
[ChromeEarlGrey
addFakeSyncServerLegacyBookmarkWithURL:URL1
title:base::SysNSStringToUTF8(title1)
originator_client_item_id:"1"];
[ChromeEarlGrey
addFakeSyncServerLegacyBookmarkWithURL:URL2
title:base::SysNSStringToUTF8(title2)
originator_client_item_id:"1"];
// Sign in to sync.
FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
[SigninEarlGrey addFakeIdentity:fakeIdentity];
[SigninEarlGrey signinWithFakeIdentity:fakeIdentity];
[ChromeEarlGrey
waitForSyncTransportStateActiveWithTimeout:kSyncOperationTimeout];
[BookmarkEarlGrey verifyBookmarksWithTitle:title1
expectedCount:1
inStorage:BookmarkStorageType::kAccount];
[BookmarkEarlGrey verifyBookmarksWithTitle:title2
expectedCount:1
inStorage:BookmarkStorageType::kAccount];
}
- (void)testSyncInvalidationsEnabled {
// Sign in to sync.
FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
[SigninEarlGrey addFakeIdentity:fakeIdentity];
[SigninEarlGrey signinWithFakeIdentity:fakeIdentity];
[ChromeEarlGrey waitForSyncEngineInitialized:YES
syncTimeout:kSyncOperationTimeout];
WaitForEntitiesOnFakeServer(1, syncer::DEVICE_INFO);
[ChromeEarlGrey waitForSyncInvalidationFields];
}
- (void)testMigrateSyncToSignin {
FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
[SigninEarlGrey addFakeIdentity:fakeIdentity];
// Sign in and turn on Sync-the-feature.
[SigninEarlGrey signinAndEnableLegacySyncFeature:fakeIdentity];
[ChromeEarlGrey waitForSyncFeatureEnabled:YES
syncTimeout:kSyncOperationTimeout];
[ChromeEarlGrey
waitForSyncTransportStateActiveWithTimeout:kSyncOperationTimeout];
// Create some data and wait for it to arrive on the server.
[BookmarkEarlGrey addBookmarkWithTitle:kBookmarkTitle
URL:kBookmarkUrl
inStorage:BookmarkStorageType::kLocalOrSyncable];
GREYAssertNil([ReadingListAppInterface
addEntryWithURL:[NSURL URLWithString:kReadingListUrl]
title:kReadingListTitle
read:YES],
@"Unable to add Reading List item");
password_manager_test_utils::SavePasswordFormToProfileStore();
WaitForEntitiesOnFakeServer(1, syncer::BOOKMARKS);
WaitForEntitiesOnFakeServer(1, syncer::READING_LIST);
WaitForEntitiesOnFakeServer(1, syncer::PASSWORDS);
// Restart Chrome with UNO phase 3 (i.e. the migration) enabled.
[self relaunchWithIdentity:fakeIdentity
enabledFeatures:{switches::kMigrateSyncingUserToSignedIn}
disabledFeatures:{}];
// Sync-the-feature should *not* be enabled anymore.
[ChromeEarlGrey waitForSyncFeatureEnabled:NO
syncTimeout:kSyncOperationTimeout];
// The bookmark should still exist, but now be in the account store.
[BookmarkEarlGrey
verifyAbsenceOfBookmarkWithURL:kBookmarkUrl
inStorage:BookmarkStorageType::kLocalOrSyncable];
[BookmarkEarlGrey
verifyExistenceOfBookmarkWithURL:kBookmarkUrl
name:kBookmarkTitle
inStorage:BookmarkStorageType::kAccount];
// Similarly the password.
GREYAssertEqual(
0, [PasswordSettingsAppInterface passwordProfileStoreResultsCount],
@"Password should NOT be in the profile store");
GREYAssertEqual(
1, [PasswordSettingsAppInterface passwordAccountStoreResultsCount],
@"Password should be in the account store");
// The reading list item should still exist, and *not* have a crossed-cloud
// icon (no crossed-cloud icon means that it's in the account store).
reading_list_test_utils::OpenReadingList();
[[EarlGrey
selectElementWithMatcher:reading_list_test_utils::VisibleReadingListItem(
kReadingListTitle)]
assertWithMatcher:grey_notNil()];
[[EarlGrey
selectElementWithMatcher:reading_list_test_utils::VisibleLocalItemIcon(
kReadingListTitle)]
assertWithMatcher:grey_nil()];
// Close the Reading List.
[[EarlGrey selectElementWithMatcher:grey_accessibilityID(
kTableViewNavigationDismissButtonId)]
performAction:grey_tap()];
// The sync machinery should still be functional: Add an account bookmark
// and ensure it arrives on the server.
[BookmarkEarlGrey addBookmarkWithTitle:@"Second bookmark"
URL:@"https://second.com/"
inStorage:BookmarkStorageType::kAccount];
WaitForEntitiesOnFakeServer(2, syncer::BOOKMARKS);
}
- (void)testMigrateSyncToSignin_PasswordsDisabled {
FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
[SigninEarlGrey addFakeIdentity:fakeIdentity];
// Sign in and turn on Sync-the-feature.
[SigninEarlGrey signinAndEnableLegacySyncFeature:fakeIdentity];
[ChromeEarlGrey waitForSyncFeatureEnabled:YES
syncTimeout:kSyncOperationTimeout];
[ChromeEarlGrey
waitForSyncTransportStateActiveWithTimeout:kSyncOperationTimeout];
// Save a password and wait for it to be uploaded to the server.
password_manager_test_utils::SavePasswordFormToProfileStore();
WaitForEntitiesOnFakeServer(1, syncer::PASSWORDS);
// Also create a bookmark.
[BookmarkEarlGrey addBookmarkWithTitle:kBookmarkTitle
URL:kBookmarkUrl
inStorage:BookmarkStorageType::kLocalOrSyncable];
WaitForEntitiesOnFakeServer(1, syncer::BOOKMARKS);
// Disable the Passwords data type.
[self disableTypeForSyncTheFeature:kSyncPasswordsIdentifier];
// Restart Chrome with UNO phase 3 (i.e. the migration) enabled.
[self relaunchWithIdentity:fakeIdentity
enabledFeatures:{switches::kMigrateSyncingUserToSignedIn}
disabledFeatures:{}];
// Sync-the-feature should *not* be enabled anymore.
[ChromeEarlGrey waitForSyncFeatureEnabled:NO
syncTimeout:kSyncOperationTimeout];
// The password should still be in the profile store, since the Passwords data
// type was disabled at the time of migration.
GREYAssertEqual(
1, [PasswordSettingsAppInterface passwordProfileStoreResultsCount],
@"Password should (still) be in the profile store");
GREYAssertEqual(
0, [PasswordSettingsAppInterface passwordAccountStoreResultsCount],
@"Password should NOT be in the account store");
// The bookmark should have been moved to the account store.
[BookmarkEarlGrey
verifyAbsenceOfBookmarkWithURL:kBookmarkUrl
inStorage:BookmarkStorageType::kLocalOrSyncable];
[BookmarkEarlGrey
verifyExistenceOfBookmarkWithURL:kBookmarkUrl
name:kBookmarkTitle
inStorage:BookmarkStorageType::kAccount];
}
- (void)testMigrateSyncToSignin_BookmarksDisabled {
FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
[SigninEarlGrey addFakeIdentity:fakeIdentity];
// Sign in and turn on Sync-the-feature.
[SigninEarlGrey signinAndEnableLegacySyncFeature:fakeIdentity];
[ChromeEarlGrey waitForSyncFeatureEnabled:YES
syncTimeout:kSyncOperationTimeout];
[ChromeEarlGrey
waitForSyncTransportStateActiveWithTimeout:kSyncOperationTimeout];
// Create a bookmark and wait for it to be uploaded to the server.
[BookmarkEarlGrey addBookmarkWithTitle:kBookmarkTitle
URL:kBookmarkUrl
inStorage:BookmarkStorageType::kLocalOrSyncable];
WaitForEntitiesOnFakeServer(1, syncer::BOOKMARKS);
// Also save a password.
password_manager_test_utils::SavePasswordFormToProfileStore();
WaitForEntitiesOnFakeServer(1, syncer::PASSWORDS);
// Disable the Bookmarks data type.
[self disableTypeForSyncTheFeature:kSyncBookmarksIdentifier];
// Restart Chrome with UNO phase 3 (i.e. the migration) enabled.
[self relaunchWithIdentity:fakeIdentity
enabledFeatures:{switches::kMigrateSyncingUserToSignedIn}
disabledFeatures:{}];
// Sync-the-feature should *not* be enabled anymore.
[ChromeEarlGrey waitForSyncFeatureEnabled:NO
syncTimeout:kSyncOperationTimeout];
// The bookmark should still be in the local-or-syncable store, since the
// Bookmarks data type was disabled at the time of migration.
[BookmarkEarlGrey
verifyExistenceOfBookmarkWithURL:kBookmarkUrl
name:kBookmarkTitle
inStorage:BookmarkStorageType::kLocalOrSyncable];
[BookmarkEarlGrey
verifyAbsenceOfBookmarkWithURL:kBookmarkUrl
inStorage:BookmarkStorageType::kAccount];
// The password should have been moved to the account store.
GREYAssertEqual(
0, [PasswordSettingsAppInterface passwordProfileStoreResultsCount],
@"Password should NOT be in the profile store");
GREYAssertEqual(
1, [PasswordSettingsAppInterface passwordAccountStoreResultsCount],
@"Password should be in the account store");
}
- (void)testMigrateSyncToSignin_ReadingListDisabled {
FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
[SigninEarlGrey addFakeIdentity:fakeIdentity];
// Sign in and turn on Sync-the-feature.
[SigninEarlGrey signinAndEnableLegacySyncFeature:fakeIdentity];
[ChromeEarlGrey waitForSyncFeatureEnabled:YES
syncTimeout:kSyncOperationTimeout];
[ChromeEarlGrey
waitForSyncTransportStateActiveWithTimeout:kSyncOperationTimeout];
// Disable the ReadingList data type.
[self disableTypeForSyncTheFeature:kSyncReadingListIdentifier];
// Create a reading list entry.
GREYAssertNil([ReadingListAppInterface
addEntryWithURL:[NSURL URLWithString:kReadingListUrl]
title:kReadingListTitle
read:YES],
@"Unable to add Reading List item");
// Also save a password.
password_manager_test_utils::SavePasswordFormToProfileStore();
WaitForEntitiesOnFakeServer(1, syncer::PASSWORDS);
// Restart Chrome with UNO phase 3 (i.e. the migration) enabled.
[self relaunchWithIdentity:fakeIdentity
enabledFeatures:{switches::kMigrateSyncingUserToSignedIn}
disabledFeatures:{}];
// Sync-the-feature should *not* be enabled anymore.
[ChromeEarlGrey waitForSyncFeatureEnabled:NO
syncTimeout:kSyncOperationTimeout];
// Enable the ReadingList data type again. This isn't really required, but
// without doing this there's no easy way to verify that the entry is still in
// the local store (the crossed-out cloud icon only shows up if the type is
// enabled).
[ChromeEarlGreyUI openSettingsMenu];
[ChromeEarlGreyUI
tapSettingsMenuButton:chrome_test_util::SettingsAccountButton()];
[[EarlGrey selectElementWithMatcher:chrome_test_util::TableViewSwitchCell(
kSyncReadingListIdentifier,
/*is_toggled_on=*/NO,
/*is_enabled=*/YES)]
performAction:chrome_test_util::TurnTableViewSwitchOn(/*on=*/YES)];
[[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsDoneButton()]
performAction:grey_tap()];
// The reading list entry should still be in the local-or-syncable store
// (indicated by the crossed-out cloud icon), since the ReadingList data type
// was disabled at the time of migration.
reading_list_test_utils::OpenReadingList();
[[EarlGrey
selectElementWithMatcher:reading_list_test_utils::VisibleReadingListItem(
kReadingListTitle)]
assertWithMatcher:grey_notNil()];
[[EarlGrey
selectElementWithMatcher:reading_list_test_utils::VisibleLocalItemIcon(
kReadingListTitle)]
assertWithMatcher:grey_notNil()];
// The password should have been moved to the account store.
GREYAssertEqual(
0, [PasswordSettingsAppInterface passwordProfileStoreResultsCount],
@"Password should NOT be in the profile store");
GREYAssertEqual(
1, [PasswordSettingsAppInterface passwordAccountStoreResultsCount],
@"Password should be in the account store");
}
- (void)testMigrateSyncToSignin_SyncNotActive {
FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
[SigninEarlGrey addFakeIdentity:fakeIdentity];
// Sign in and turn on Sync-the-feature.
[SigninEarlGrey signinAndEnableLegacySyncFeature:fakeIdentity];
[ChromeEarlGrey waitForSyncFeatureEnabled:YES
syncTimeout:kSyncOperationTimeout];
[ChromeEarlGrey
waitForSyncTransportStateActiveWithTimeout:kSyncOperationTimeout];
// Disable a data type (so that we can later re-enable it to trigger a
// reconfiguration).
[self disableTypeForSyncTheFeature:kSyncReadingListIdentifier];
// Disconnect the fake server, simulating a network/connection issue.
[ChromeEarlGrey disconnectFakeSyncServerNetwork];
[self setTearDownHandler:^{
[ChromeEarlGrey connectFakeSyncServerNetwork];
}];
// Re-enable the data type that was previously disabled. This causes a
// reconfiguration, which will not complete due to the network issue.
[self enableTypeForSyncTheFeature:kSyncReadingListIdentifier];
// Now, while Sync is not active (it's reconfiguring), restart Chrome with UNO
// phase 3 (i.e. the migration) enabled.
[self relaunchWithIdentity:fakeIdentity
enabledFeatures:{switches::kMigrateSyncingUserToSignedIn}
disabledFeatures:{}];
// Because Sync wasn't active at the time of the migration attempt, the
// migration should NOT have happened, and Sync-the-feature should still be
// enabled.
[ChromeEarlGrey waitForSyncFeatureEnabled:YES
syncTimeout:kSyncOperationTimeout];
// Resolve the network error and wait for Sync to become active.
[ChromeEarlGrey connectFakeSyncServerNetwork];
[ChromeEarlGrey
waitForSyncTransportStateActiveWithTimeout:kSyncOperationTimeout];
// Relaunch again - this time the migration should trigger.
[self relaunchWithIdentity:fakeIdentity
enabledFeatures:{switches::kMigrateSyncingUserToSignedIn}
disabledFeatures:{}];
// ...and Sync-the-feature should NOT be enabled anymore.
[ChromeEarlGrey waitForSyncFeatureEnabled:NO
syncTimeout:kSyncOperationTimeout];
}
- (void)testMigrateSyncToSignin_CustomPassphrase {
FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
[SigninEarlGrey addFakeIdentity:fakeIdentity];
// Set up a custom passphrase.
[ChromeEarlGrey addBookmarkWithSyncPassphrase:kPassphrase];
// Sign in and turn on Sync-the-feature.
[SigninEarlGrey signinAndEnableLegacySyncFeature:fakeIdentity];
[ChromeEarlGrey waitForSyncFeatureEnabled:YES
syncTimeout:kSyncOperationTimeout];
[ChromeEarlGrey
waitForSyncTransportStateActiveWithTimeout:kSyncOperationTimeout];
// Now Sync is in the "passphrase required" state. Resolve the passphrase
// error from Sync settings.
[ChromeEarlGreyUI openSettingsMenu];
[ChromeEarlGreyUI
tapSettingsMenuButton:chrome_test_util::ManageSyncSettingsButton()];
// Tap "Enter Passphrase" button.
[[EarlGrey
selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabelId(
IDS_IOS_SYNC_ERROR_TITLE)]
performAction:grey_tap()];
// Enter the passphrase.
[SigninEarlGreyUI submitSyncPassphrase:kPassphrase];
// Close settings.
[[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsDoneButton()]
performAction:grey_tap()];
// Save a bookmark and a password and wait for them to be uploaded.
[BookmarkEarlGrey addBookmarkWithTitle:kBookmarkTitle
URL:kBookmarkUrl
inStorage:BookmarkStorageType::kLocalOrSyncable];
password_manager_test_utils::SavePasswordFormToProfileStore();
WaitForEntitiesOnFakeServer(2, syncer::BOOKMARKS);
WaitForEntitiesOnFakeServer(1, syncer::PASSWORDS);
// Restart Chrome with UNO phase 3 (i.e. the migration) enabled.
[self relaunchWithIdentity:fakeIdentity
enabledFeatures:{switches::kMigrateSyncingUserToSignedIn}
disabledFeatures:{}];
// Sync-the-feature should *not* be enabled anymore.
[ChromeEarlGrey waitForSyncFeatureEnabled:NO
syncTimeout:kSyncOperationTimeout];
// The password should have been migrated to the account store.
GREYAssertEqual(
0, [PasswordSettingsAppInterface passwordProfileStoreResultsCount],
@"Password should NOT be in the profile store anymore");
GREYAssertEqual(
1, [PasswordSettingsAppInterface passwordAccountStoreResultsCount],
@"Password should be in the account store");
// The bookmark should have been migrated to the account store.
[BookmarkEarlGrey
verifyAbsenceOfBookmarkWithURL:kBookmarkUrl
inStorage:BookmarkStorageType::kLocalOrSyncable];
[BookmarkEarlGrey
verifyExistenceOfBookmarkWithURL:kBookmarkUrl
name:kBookmarkTitle
inStorage:BookmarkStorageType::kAccount];
}
- (void)testMigrateSyncToSignin_CustomPassphraseMissing {
FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
[SigninEarlGrey addFakeIdentity:fakeIdentity];
// Set up a custom passphrase.
[ChromeEarlGrey addSyncPassphrase:kPassphrase];
// Sign in and turn on Sync-the-feature.
[SigninEarlGrey signinAndEnableLegacySyncFeature:fakeIdentity];
[ChromeEarlGrey waitForSyncFeatureEnabled:YES
syncTimeout:kSyncOperationTimeout];
[ChromeEarlGrey
waitForSyncTransportStateActiveWithTimeout:kSyncOperationTimeout];
// Now Sync is in the "passphrase required" state. Verify this in settings.
[ChromeEarlGreyUI openSettingsMenu];
[ChromeEarlGreyUI
tapSettingsMenuButton:chrome_test_util::ManageSyncSettingsButton()];
// Check that the "Enter Passphrase" button is there.
[[EarlGrey
selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabelId(
IDS_IOS_SYNC_ERROR_TITLE)]
assertWithMatcher:grey_sufficientlyVisible()];
// Close settings.
[[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsDoneButton()]
performAction:grey_tap()];
// Save a bookmark and a password. Note that they will not be uploaded to the
// server, due to the missing passphrase.
[BookmarkEarlGrey addBookmarkWithTitle:kBookmarkTitle
URL:kBookmarkUrl
inStorage:BookmarkStorageType::kLocalOrSyncable];
password_manager_test_utils::SavePasswordFormToProfileStore();
// Restart Chrome with UNO phase 3 (i.e. the migration) enabled.
[self relaunchWithIdentity:fakeIdentity
enabledFeatures:{switches::kMigrateSyncingUserToSignedIn}
disabledFeatures:{}];
// Sync-the-feature should *not* be enabled anymore.
[ChromeEarlGrey waitForSyncFeatureEnabled:NO
syncTimeout:kSyncOperationTimeout];
// The password should NOT have been migrated to the account store.
GREYAssertEqual(
1, [PasswordSettingsAppInterface passwordProfileStoreResultsCount],
@"Password should still be in the profile store");
GREYAssertEqual(
0, [PasswordSettingsAppInterface passwordAccountStoreResultsCount],
@"Password should NOT be in the account store");
// The bookmark should NOT have been migrated to the account store.
[BookmarkEarlGrey
verifyExistenceOfBookmarkWithURL:kBookmarkUrl
name:kBookmarkTitle
inStorage:BookmarkStorageType::kLocalOrSyncable];
[BookmarkEarlGrey
verifyAbsenceOfBookmarkWithURL:kBookmarkUrl
inStorage:BookmarkStorageType::kAccount];
}
- (void)testMigrateSyncToSignin_ManagedAccount {
// Use a managed (aka enterprise) account.
FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeManagedIdentity];
[SigninEarlGrey addFakeIdentity:fakeIdentity];
// Sign in and turn on Sync-the-feature.
[SigninEarlGrey signinAndEnableLegacySyncFeature:fakeIdentity];
[ChromeEarlGrey waitForSyncFeatureEnabled:YES
syncTimeout:kSyncOperationTimeout];
[ChromeEarlGrey
waitForSyncTransportStateActiveWithTimeout:kSyncOperationTimeout];
// Disable the Passwords data type.
[self disableTypeForSyncTheFeature:kSyncPasswordsIdentifier];
// Save a password. Since the type is disabled, this is local and won't be
// migrated to the account store.
password_manager_test_utils::SavePasswordFormToProfileStore();
// Also create a bookmark and wait for it to arrive on the server.
[BookmarkEarlGrey addBookmarkWithTitle:kBookmarkTitle
URL:kBookmarkUrl
inStorage:BookmarkStorageType::kLocalOrSyncable];
WaitForEntitiesOnFakeServer(1, syncer::BOOKMARKS);
// Restart Chrome with UNO phase 3 (i.e. the migration) enabled.
[self relaunchWithIdentity:fakeIdentity
enabledFeatures:{switches::kMigrateSyncingUserToSignedIn}
disabledFeatures:{}];
// Sync-the-feature should *not* be enabled anymore.
[ChromeEarlGrey waitForSyncFeatureEnabled:NO
syncTimeout:kSyncOperationTimeout];
// The password should NOT have been migrated to the account store.
GREYAssertEqual(
1, [PasswordSettingsAppInterface passwordProfileStoreResultsCount],
@"Password should still be in the profile store");
GREYAssertEqual(
0, [PasswordSettingsAppInterface passwordAccountStoreResultsCount],
@"Password should NOT be in the account store");
// The bookmark should have been migrated to the account store.
[BookmarkEarlGrey
verifyAbsenceOfBookmarkWithURL:kBookmarkUrl
inStorage:BookmarkStorageType::kLocalOrSyncable];
[BookmarkEarlGrey
verifyExistenceOfBookmarkWithURL:kBookmarkUrl
name:kBookmarkTitle
inStorage:BookmarkStorageType::kAccount];
// Open settings and tap "Sign Out".
[ChromeEarlGreyUI openSettingsMenu];
[ChromeEarlGreyUI
tapSettingsMenuButton:chrome_test_util::SettingsAccountButton()];
[[[EarlGrey selectElementWithMatcher:
grey_accessibilityLabel(l10n_util::GetNSString(
IDS_IOS_GOOGLE_ACCOUNT_SETTINGS_SIGN_OUT_ITEM))]
usingSearchAction:grey_swipeSlowInDirection(kGREYDirectionUp)
onElementWithMatcher:grey_accessibilityID(
kManageSyncTableViewAccessibilityIdentifier)]
performAction:grey_tap()];
// Ensure the confirmation dialog is shown (this happens only for migrated
// managed users!), and does *not* have the "Keep Data" option.
[[EarlGrey
selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabelId(
IDS_IOS_SIGNOUT_DIALOG_KEEP_DATA_BUTTON)]
assertWithMatcher:grey_nil()];
// Confirm "Clear Data".
[[EarlGrey
selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabelId(
IDS_IOS_SIGNOUT_DIALOG_CLEAR_DATA_BUTTON)]
performAction:grey_tap()];
// Wait until the user is signed out. Use a longer timeout for cases where
// sign out also triggers a clear browsing data.
[ChromeEarlGrey
waitForUIElementToAppearWithMatcher:chrome_test_util::SettingsDoneButton()
timeout:base::test::ios::
kWaitForClearBrowsingDataTimeout];
[[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsDoneButton()]
performAction:grey_tap()];
[SigninEarlGrey verifySignedOut];
[ChromeEarlGrey waitForSyncEngineInitialized:NO
syncTimeout:kSyncOperationTimeout];
// Both the bookmark and the password should be gone.
GREYAssertEqual(
0, [PasswordSettingsAppInterface passwordProfileStoreResultsCount],
@"Password should NOT be in the profile store anymore");
GREYAssertEqual(
0, [PasswordSettingsAppInterface passwordAccountStoreResultsCount],
@"Password should NOT be in the account store");
[BookmarkEarlGrey
verifyAbsenceOfBookmarkWithURL:kBookmarkUrl
inStorage:BookmarkStorageType::kLocalOrSyncable];
[BookmarkEarlGrey
verifyAbsenceOfBookmarkWithURL:kBookmarkUrl
inStorage:BookmarkStorageType::kAccount];
}
- (void)testManagedAccountClearsDataForSignedInPeriod {
const GURL preSigninURL = self.testServer->GetURL("/console.html");
const GURL firstSigninURL = self.testServer->GetURL("/pony.html");
const GURL secondSigninURL = self.testServer->GetURL("/destination.html");
const GURL thirdSigninURL = self.testServer->GetURL("/links.html");
// Clear browsing history before and after the test to avoid conflicting with
// other tests.
[ChromeEarlGrey clearBrowsingHistory];
[self setTearDownHandler:^{
[ChromeEarlGrey clearBrowsingHistory];
}];
GREYAssertEqual([ChromeEarlGrey browsingHistoryEntryCount], 0,
@"History was unexpectedly not empty");
// Save a password to the local store and visit a URL before sign-in.
password_manager_test_utils::SavePasswordFormToProfileStore(
@"password1", @"user1", @"https://example.com");
[ChromeEarlGrey loadURL:preSigninURL];
GREYAssertEqual([ChromeEarlGrey browsingHistoryEntryCount], 1,
@"History was unexpectedly empty");
// Still before signing in, open a second tab.
[ChromeEarlGrey openNewTab];
GREYAssertEqual([ChromeEarlGrey mainTabCount], 2,
@"Tabs left behind from previous test?!");
// Sign in a managed (aka enterprise) account.
FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeManagedIdentity];
[SigninEarlGrey addFakeIdentity:fakeIdentity];
[SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
// Save another password to the local store after sign-in.
password_manager_test_utils::SavePasswordFormToProfileStore(
@"password2", @"user2", @"https://example.com");
// Navigate to a few URLs.
[ChromeEarlGrey loadURL:firstSigninURL];
[ChromeEarlGrey loadURL:secondSigninURL];
[ChromeEarlGrey loadURL:thirdSigninURL];
GREYAssertEqual([ChromeEarlGrey browsingHistoryEntryCount], 4,
@"History did not contain the expected entries");
// Open settings and tap "Sign Out".
[ChromeEarlGreyUI openSettingsMenu];
[ChromeEarlGreyUI
tapSettingsMenuButton:chrome_test_util::SettingsAccountButton()];
[[[EarlGrey selectElementWithMatcher:
grey_accessibilityLabel(l10n_util::GetNSString(
IDS_IOS_GOOGLE_ACCOUNT_SETTINGS_SIGN_OUT_ITEM))]
usingSearchAction:grey_swipeSlowInDirection(kGREYDirectionUp)
onElementWithMatcher:grey_accessibilityID(
kManageSyncTableViewAccessibilityIdentifier)]
performAction:grey_tap()];
// Confirm "Sign Out" when alert dialog that data will be cleared is shown.
[[EarlGrey
selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabelId(
IDS_IOS_SIGNOUT_DIALOG_SIGN_OUT_BUTTON)]
performAction:grey_tap()];
// Wait until the user is signed out. Use a longer timeout to give time for
// data to be cleared.
[ChromeEarlGrey
waitForUIElementToAppearWithMatcher:chrome_test_util::SettingsDoneButton()
timeout:base::test::ios::
kWaitForClearBrowsingDataTimeout];
[[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsDoneButton()]
performAction:grey_tap()];
[SigninEarlGrey verifySignedOut];
// Only the password saved before sign-in should be remaining.
GREYAssertEqual(
1, [PasswordSettingsAppInterface passwordProfileStoreResultsCount],
@"Only the password saved BEFORE sign-in should be in the profile store");
GREYAssertEqual(
0, [PasswordSettingsAppInterface passwordAccountStoreResultsCount],
@"Password should NOT be in the account store");
// Only two history entries remain after browsing history is cleared: the one
// from before sign-in and the active URL.
// Do one more navigation to ensure everything's in a settled state, bringing
// the total count to 3.
[ChromeEarlGrey loadURL:secondSigninURL];
GREYAssertEqual([ChromeEarlGrey browsingHistoryEntryCount], 3,
@"History did not contain the expected entries");
// Both tabs should still be there.
GREYAssertEqual([ChromeEarlGrey mainTabCount], 2,
@"Tab was unexpectedly closed");
}
- (void)testManagedAccountClearsDataAndTabsForSignedInPeriod {
const GURL preSigninURL = self.testServer->GetURL("/console.html");
const GURL firstSigninURL = self.testServer->GetURL("/pony.html");
const GURL secondSigninURL = self.testServer->GetURL("/destination.html");
const GURL thirdSigninURL = self.testServer->GetURL("/links.html");
// Clear browsing history before and after the test to avoid conflicting with
// other tests.
[ChromeEarlGrey clearBrowsingHistory];
[self setTearDownHandler:^{
[ChromeEarlGrey clearBrowsingHistory];
}];
GREYAssertEqual([ChromeEarlGrey browsingHistoryEntryCount], 0,
@"History was unexpectedly not empty");
// Save a password to the local store and visit a URL before sign-in.
password_manager_test_utils::SavePasswordFormToProfileStore(
@"password1", @"user1", @"https://example.com");
[ChromeEarlGrey loadURL:preSigninURL];
GREYAssertEqual([ChromeEarlGrey browsingHistoryEntryCount], 1,
@"History was unexpectedly empty");
// Still before signing in, open a second tab.
[ChromeEarlGrey openNewTab];
GREYAssertEqual([ChromeEarlGrey mainTabCount], 2,
@"Tabs left behind from previous test?!");
// Sign in a managed (aka enterprise) account.
FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeManagedIdentity];
[SigninEarlGrey addFakeIdentity:fakeIdentity];
[SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
// Save another password to the local store after sign-in.
password_manager_test_utils::SavePasswordFormToProfileStore(
@"password2", @"user2", @"https://example.com");
// Navigate to a few URLs (in the second tab). This also marks the tab as
// "used since signin".
[ChromeEarlGrey loadURL:firstSigninURL];
[ChromeEarlGrey loadURL:secondSigninURL];
[ChromeEarlGrey loadURL:thirdSigninURL];
GREYAssertEqual([ChromeEarlGrey browsingHistoryEntryCount], 4,
@"History did not contain the expected entries");
// Open settings and tap "Sign Out".
[ChromeEarlGreyUI openSettingsMenu];
[ChromeEarlGreyUI
tapSettingsMenuButton:chrome_test_util::SettingsAccountButton()];
[[[EarlGrey selectElementWithMatcher:
grey_accessibilityLabel(l10n_util::GetNSString(
IDS_IOS_GOOGLE_ACCOUNT_SETTINGS_SIGN_OUT_ITEM))]
usingSearchAction:grey_swipeSlowInDirection(kGREYDirectionUp)
onElementWithMatcher:grey_accessibilityID(
kManageSyncTableViewAccessibilityIdentifier)]
performAction:grey_tap()];
// Confirm "Sign Out" when alert dialog that data will be cleared is shown.
[[EarlGrey
selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabelId(
IDS_IOS_SIGNOUT_DIALOG_SIGN_OUT_BUTTON)]
performAction:grey_tap()];
// Wait until the user is signed out. Use a longer timeout to give time for
// data to be cleared.
[ChromeEarlGrey
waitForUIElementToAppearWithMatcher:chrome_test_util::SettingsDoneButton()
timeout:base::test::ios::
kWaitForClearBrowsingDataTimeout];
[[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsDoneButton()]
performAction:grey_tap()];
[SigninEarlGrey verifySignedOut];
// Only the password saved before sign-in should be remaining.
GREYAssertEqual(
1, [PasswordSettingsAppInterface passwordProfileStoreResultsCount],
@"Only the password saved BEFORE sign-in should be in the profile store");
GREYAssertEqual(
0, [PasswordSettingsAppInterface passwordAccountStoreResultsCount],
@"Password should NOT be in the account store");
// Only one history entry should remain after browsing history is cleared: the
// one from before sign-in.
GREYAssertEqual([ChromeEarlGrey browsingHistoryEntryCount], 1,
@"History did not contain the expected entries");
// The original tab (not used since signing in) should still be there. The
// second tab, where we navigated while signed in, should have been closed.
GREYAssertEqual([ChromeEarlGrey mainTabCount], 1,
@"Tab wasn't closed as expected");
}
- (void)testMigrateSyncToSignin_Undo {
FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
[SigninEarlGrey addFakeIdentity:fakeIdentity];
// Sign in and turn on Sync-the-feature.
[SigninEarlGrey signinAndEnableLegacySyncFeature:fakeIdentity];
[ChromeEarlGrey waitForSyncFeatureEnabled:YES
syncTimeout:kSyncOperationTimeout];
[ChromeEarlGrey
waitForSyncTransportStateActiveWithTimeout:kSyncOperationTimeout];
// Create some data and wait for it to arrive on the server.
[BookmarkEarlGrey addBookmarkWithTitle:kBookmarkTitle
URL:kBookmarkUrl
inStorage:BookmarkStorageType::kLocalOrSyncable];
password_manager_test_utils::SavePasswordFormToProfileStore();
WaitForEntitiesOnFakeServer(1, syncer::BOOKMARKS);
WaitForEntitiesOnFakeServer(1, syncer::PASSWORDS);
// Restart Chrome with UNO phase 3 (i.e. the migration) enabled.
[self relaunchWithIdentity:fakeIdentity
enabledFeatures:{switches::kMigrateSyncingUserToSignedIn}
disabledFeatures:{}];
// Sync-the-feature should *not* be enabled anymore.
[ChromeEarlGrey waitForSyncFeatureEnabled:NO
syncTimeout:kSyncOperationTimeout];
// The bookmark should still exist, but now be in the account store.
[BookmarkEarlGrey
verifyAbsenceOfBookmarkWithURL:kBookmarkUrl
inStorage:BookmarkStorageType::kLocalOrSyncable];
[BookmarkEarlGrey
verifyExistenceOfBookmarkWithURL:kBookmarkUrl
name:kBookmarkTitle
inStorage:BookmarkStorageType::kAccount];
// Similarly the password.
GREYAssertEqual(
0, [PasswordSettingsAppInterface passwordProfileStoreResultsCount],
@"Password should NOT be in the profile store");
GREYAssertEqual(
1, [PasswordSettingsAppInterface passwordAccountStoreResultsCount],
@"Password should be in the account store");
// Restart Chrome with the reverse migration (undo) enabled.
[self relaunchWithIdentity:fakeIdentity
enabledFeatures:{switches::kUndoMigrationOfSyncingUserToSignedIn}
disabledFeatures:{}];
// Sync-the-feature should be enabled again.
[ChromeEarlGrey waitForSyncFeatureEnabled:YES
syncTimeout:kSyncOperationTimeout];
// The bookmark should be back in the local store.
[BookmarkEarlGrey
verifyExistenceOfBookmarkWithURL:kBookmarkUrl
name:kBookmarkTitle
inStorage:BookmarkStorageType::kLocalOrSyncable];
[BookmarkEarlGrey
verifyAbsenceOfBookmarkWithURL:kBookmarkUrl
inStorage:BookmarkStorageType::kAccount];
// Similarly the password.
GREYAssertEqual(
1, [PasswordSettingsAppInterface passwordProfileStoreResultsCount],
@"Password should be back in the profile store");
GREYAssertEqual(
0, [PasswordSettingsAppInterface passwordAccountStoreResultsCount],
@"Password should NOT be in the account store anymore");
// Verify that the local-or-syncable store is the one being synced again: Add
// another bookmark (to the local store) and ensure it arrives on the server.
[BookmarkEarlGrey addBookmarkWithTitle:@"Other title"
URL:@"https://other.url.com"
inStorage:BookmarkStorageType::kLocalOrSyncable];
WaitForEntitiesOnFakeServer(2, syncer::BOOKMARKS);
}
@end