diff options
Diffstat (limited to 'chromium/chrome/browser/signin/e2e_tests')
6 files changed, 1091 insertions, 0 deletions
diff --git a/chromium/chrome/browser/signin/e2e_tests/live_sign_in_test.cc b/chromium/chrome/browser/signin/e2e_tests/live_sign_in_test.cc new file mode 100644 index 00000000000..3e042e76b87 --- /dev/null +++ b/chromium/chrome/browser/signin/e2e_tests/live_sign_in_test.cc @@ -0,0 +1,776 @@ +// Copyright 2019 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. + +#include "base/memory/raw_ptr.h" +#include "base/run_loop.h" +#include "base/scoped_observation.h" +#include "base/strings/stringprintf.h" +#include "base/time/time.h" +#include "build/build_config.h" +#include "build/chromeos_buildflags.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/signin/account_reconcilor_factory.h" +#include "chrome/browser/signin/e2e_tests/live_test.h" +#include "chrome/browser/signin/e2e_tests/test_accounts_util.h" +#include "chrome/browser/signin/identity_manager_factory.h" +#include "chrome/browser/sync/sync_service_factory.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_finder.h" +#include "chrome/browser/ui/tabs/tab_strip_model_observer.h" +#include "chrome/browser/ui/webui/signin/login_ui_test_utils.h" +#include "chrome/browser/ui/webui/signin/signin_email_confirmation_dialog.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/signin/core/browser/account_reconcilor.h" +#include "components/signin/public/base/consent_level.h" +#include "components/signin/public/identity_manager/account_capabilities.h" +#include "components/signin/public/identity_manager/account_info.h" +#include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h" +#include "components/signin/public/identity_manager/identity_manager.h" +#include "components/signin/public/identity_manager/identity_test_utils.h" +#include "components/signin/public/identity_manager/tribool.h" +#include "components/sync/driver/sync_service.h" +#include "content/public/test/browser_test.h" +#include "google_apis/gaia/core_account_id.h" +#include "google_apis/gaia/gaia_auth_util.h" +#include "google_apis/gaia/gaia_urls.h" +#include "ui/compositor/scoped_animation_duration_scale_mode.h" + +#if !BUILDFLAG(IS_CHROMEOS_ASH) +#include "chrome/browser/sync/sync_ui_util.h" +#endif // !BUILDFLAG(IS_CHROMEOS_ASH) + +namespace signin { +namespace test { + +const base::TimeDelta kDialogTimeout = base::Seconds(10); + +// A wrapper importing the settings module when the chrome://settings serve the +// Polymer 3 version. +const char kSettingsScriptWrapperFormat[] = + "import('./settings.js').then(settings => {%s});"; + +enum class PrimarySyncAccountWait { kWaitForAdded, kWaitForCleared, kNotWait }; + +// Observes various sign-in events and allows to wait for a specific state of +// signed-in accounts. +class SignInTestObserver : public IdentityManager::Observer, + public AccountReconcilor::Observer { + public: + explicit SignInTestObserver(IdentityManager* identity_manager, + AccountReconcilor* reconcilor) + : identity_manager_(identity_manager), reconcilor_(reconcilor) { + identity_manager_observation_.Observe(identity_manager_.get()); + account_reconcilor_observation_.Observe(reconcilor_.get()); + } + ~SignInTestObserver() override = default; + + // IdentityManager::Observer: + void OnPrimaryAccountChanged( + const PrimaryAccountChangeEvent& event) override { + if (event.GetEventTypeFor(ConsentLevel::kSync) == + PrimaryAccountChangeEvent::Type::kNone) { + return; + } + QuitIfConditionIsSatisfied(); + } + void OnRefreshTokenUpdatedForAccount(const CoreAccountInfo&) override { + QuitIfConditionIsSatisfied(); + } + void OnRefreshTokenRemovedForAccount(const CoreAccountId&) override { + QuitIfConditionIsSatisfied(); + } + void OnErrorStateOfRefreshTokenUpdatedForAccount( + const CoreAccountInfo&, + const GoogleServiceAuthError&) override { + QuitIfConditionIsSatisfied(); + } + void OnAccountsInCookieUpdated(const AccountsInCookieJarInfo&, + const GoogleServiceAuthError&) override { + QuitIfConditionIsSatisfied(); + } + + // AccountReconcilor::Observer: + // TODO(https://crbug.com/1051864): Remove this obsever method once the bug is + // fixed. + void OnStateChanged(signin_metrics::AccountReconcilorState state) override { + if (state == signin_metrics::ACCOUNT_RECONCILOR_OK) { + // This will trigger cookie update if accounts are stale. + identity_manager_->GetAccountsInCookieJar(); + } + } + + void WaitForAccountChanges(int signed_in_accounts, + PrimarySyncAccountWait primary_sync_account_wait) { + expected_signed_in_accounts_ = signed_in_accounts; + primary_sync_account_wait_ = primary_sync_account_wait; + are_expectations_set = true; + QuitIfConditionIsSatisfied(); + run_loop_.Run(); + } + + private: + void QuitIfConditionIsSatisfied() { + if (!are_expectations_set) + return; + + int accounts_with_valid_refresh_token = + CountAccountsWithValidRefreshToken(); + int accounts_in_cookie = CountSignedInAccountsInCookie(); + + if (accounts_with_valid_refresh_token != accounts_in_cookie || + accounts_with_valid_refresh_token != expected_signed_in_accounts_) { + return; + } + + switch (primary_sync_account_wait_) { + case PrimarySyncAccountWait::kWaitForAdded: + if (!HasValidPrimarySyncAccount()) + return; + break; + case PrimarySyncAccountWait::kWaitForCleared: + if (identity_manager_->HasPrimaryAccount(signin::ConsentLevel::kSync)) + return; + break; + case PrimarySyncAccountWait::kNotWait: + break; + } + + run_loop_.Quit(); + } + + int CountAccountsWithValidRefreshToken() const { + std::vector<CoreAccountInfo> accounts_with_refresh_tokens = + identity_manager_->GetAccountsWithRefreshTokens(); + int valid_accounts = 0; + for (const auto& account_info : accounts_with_refresh_tokens) { + if (!identity_manager_->HasAccountWithRefreshTokenInPersistentErrorState( + account_info.account_id)) { + ++valid_accounts; + } + } + return valid_accounts; + } + + int CountSignedInAccountsInCookie() const { + signin::AccountsInCookieJarInfo accounts_in_cookie_jar = + identity_manager_->GetAccountsInCookieJar(); + if (!accounts_in_cookie_jar.accounts_are_fresh) + return -1; + + return accounts_in_cookie_jar.signed_in_accounts.size(); + } + + bool HasValidPrimarySyncAccount() const { + CoreAccountId primary_account_id = + identity_manager_->GetPrimaryAccountId(signin::ConsentLevel::kSync); + if (primary_account_id.empty()) + return false; + + return !identity_manager_->HasAccountWithRefreshTokenInPersistentErrorState( + primary_account_id); + } + + const raw_ptr<signin::IdentityManager> identity_manager_; + const raw_ptr<AccountReconcilor> reconcilor_; + base::ScopedObservation<IdentityManager, IdentityManager::Observer> + identity_manager_observation_{this}; + base::ScopedObservation<AccountReconcilor, AccountReconcilor::Observer> + account_reconcilor_observation_{this}; + base::RunLoop run_loop_; + + bool are_expectations_set = false; + int expected_signed_in_accounts_ = 0; + PrimarySyncAccountWait primary_sync_account_wait_ = + PrimarySyncAccountWait::kNotWait; +}; + +// Observer class allowing to wait for account capabilities to be known. +class AccountCapabilitiesObserver : public IdentityManager::Observer { + public: + explicit AccountCapabilitiesObserver(IdentityManager* identity_manager) + : identity_manager_(identity_manager) { + identity_manager_observation_.Observe(identity_manager); + } + + // IdentityManager::Observer: + void OnExtendedAccountInfoUpdated(const AccountInfo& info) override { + if (info.account_id != account_id_) + return; + + if (info.capabilities.AreAllCapabilitiesKnown()) + run_loop_.Quit(); + } + + // This should be called only once per AccountCapabilitiesObserver instance. + void WaitForAllCapabilitiesToBeKnown(CoreAccountId account_id) { + DCHECK(identity_manager_observation_.IsObservingSource( + identity_manager_.get())); + AccountInfo info = + identity_manager_->FindExtendedAccountInfoByAccountId(account_id); + if (info.capabilities.AreAllCapabilitiesKnown()) + return; + + account_id_ = account_id; + run_loop_.Run(); + identity_manager_observation_.Reset(); + } + + private: + raw_ptr<IdentityManager> identity_manager_ = nullptr; + CoreAccountId account_id_; + base::RunLoop run_loop_; + base::ScopedObservation<IdentityManager, IdentityManager::Observer> + identity_manager_observation_{this}; +}; + +// Live tests for SignIn. +// These tests can be run with: +// browser_tests --gtest_filter=LiveSignInTest.* --run-live-tests --run-manual +class LiveSignInTest : public signin::test::LiveTest { + public: + LiveSignInTest() = default; + ~LiveSignInTest() override = default; + + void SetUp() override { + LiveTest::SetUp(); + // Always disable animation for stability. + ui::ScopedAnimationDurationScaleMode disable_animation( + ui::ScopedAnimationDurationScaleMode::ZERO_DURATION); + } + + void SignInFromWeb(const TestAccount& test_account, + int previously_signed_in_accounts) { + AddTabAtIndex(0, GaiaUrls::GetInstance()->add_account_url(), + ui::PageTransition::PAGE_TRANSITION_TYPED); + SignInFromCurrentPage(test_account, previously_signed_in_accounts); + } + + void SignInFromSettings(const TestAccount& test_account, + int previously_signed_in_accounts) { + GURL settings_url("chrome://settings"); + AddTabAtIndex(0, settings_url, ui::PageTransition::PAGE_TRANSITION_TYPED); + auto* settings_tab = browser()->tab_strip_model()->GetActiveWebContents(); + EXPECT_TRUE(content::ExecuteScript( + settings_tab, + base::StringPrintf( + kSettingsScriptWrapperFormat, + "settings.SyncBrowserProxyImpl.getInstance().startSignIn();"))); + SignInFromCurrentPage(test_account, previously_signed_in_accounts); + } + + void SignInFromCurrentPage(const TestAccount& test_account, + int previously_signed_in_accounts) { + SignInTestObserver observer(identity_manager(), account_reconcilor()); + login_ui_test_utils::ExecuteJsToSigninInSigninFrame( + browser(), test_account.user, test_account.password); + observer.WaitForAccountChanges(previously_signed_in_accounts + 1, + PrimarySyncAccountWait::kNotWait); + } + + void TurnOnSync(const TestAccount& test_account, + int previously_signed_in_accounts) { + SignInFromSettings(test_account, previously_signed_in_accounts); + + SignInTestObserver observer(identity_manager(), account_reconcilor()); + EXPECT_TRUE(login_ui_test_utils::ConfirmSyncConfirmationDialog( + browser(), kDialogTimeout)); + observer.WaitForAccountChanges(previously_signed_in_accounts + 1, + PrimarySyncAccountWait::kWaitForAdded); + } + + void SignOutFromWeb() { + SignInTestObserver observer(identity_manager(), account_reconcilor()); + AddTabAtIndex(0, GaiaUrls::GetInstance()->service_logout_url(), + ui::PageTransition::PAGE_TRANSITION_TYPED); + observer.WaitForAccountChanges(0, PrimarySyncAccountWait::kNotWait); + } + + void TurnOffSync() { + GURL settings_url("chrome://settings"); + AddTabAtIndex(0, settings_url, ui::PageTransition::PAGE_TRANSITION_TYPED); + SignInTestObserver observer(identity_manager(), account_reconcilor()); + auto* settings_tab = browser()->tab_strip_model()->GetActiveWebContents(); + EXPECT_TRUE(content::ExecuteScript( + settings_tab, + base::StringPrintf( + kSettingsScriptWrapperFormat, + "settings.SyncBrowserProxyImpl.getInstance().signOut(false)"))); + observer.WaitForAccountChanges(0, PrimarySyncAccountWait::kWaitForCleared); + } + + signin::IdentityManager* identity_manager() { + return identity_manager(browser()); + } + + signin::IdentityManager* identity_manager(Browser* browser) { + return IdentityManagerFactory::GetForProfile(browser->profile()); + } + + syncer::SyncService* sync_service() { return sync_service(browser()); } + + syncer::SyncService* sync_service(Browser* browser) { + return SyncServiceFactory::GetForProfile(browser->profile()); + } + + AccountReconcilor* account_reconcilor() { + return account_reconcilor(browser()); + } + + AccountReconcilor* account_reconcilor(Browser* browser) { + return AccountReconcilorFactory::GetForProfile(browser->profile()); + } +}; + +// This test can pass. Marked as manual because it TIMED_OUT on Win7. +// See crbug.com/1025335. +// Sings in an account through the settings page and checks that the account is +// added to Chrome. Sync should be disabled because the test doesn't pass +// through the Sync confirmation dialog. +IN_PROC_BROWSER_TEST_F(LiveSignInTest, MANUAL_SimpleSignInFlow) { + TestAccount ta; + CHECK(GetTestAccountsUtil()->GetAccount("TEST_ACCOUNT_1", ta)); + SignInFromSettings(ta, 0); + + const AccountsInCookieJarInfo& accounts_in_cookie_jar = + identity_manager()->GetAccountsInCookieJar(); + EXPECT_TRUE(accounts_in_cookie_jar.accounts_are_fresh); + ASSERT_EQ(1u, accounts_in_cookie_jar.signed_in_accounts.size()); + EXPECT_TRUE(accounts_in_cookie_jar.signed_out_accounts.empty()); + const gaia::ListedAccount& account = + accounts_in_cookie_jar.signed_in_accounts[0]; + EXPECT_TRUE(gaia::AreEmailsSame(ta.user, account.email)); + EXPECT_TRUE(identity_manager()->HasAccountWithRefreshToken(account.id)); + EXPECT_FALSE(sync_service()->IsSyncFeatureEnabled()); +} + +// This test can pass. Marked as manual because it TIMED_OUT on Win7. +// See crbug.com/1025335. +// Signs in an account through the settings page and enables Sync. Checks that +// Sync is enabled. +// Then, signs out on the web and checks that the account is removed from +// cookies and Sync paused error is displayed. +IN_PROC_BROWSER_TEST_F(LiveSignInTest, MANUAL_WebSignOut) { + TestAccount test_account; + CHECK(GetTestAccountsUtil()->GetAccount("TEST_ACCOUNT_1", test_account)); + TurnOnSync(test_account, 0); + + const CoreAccountInfo& primary_account = + identity_manager()->GetPrimaryAccountInfo(signin::ConsentLevel::kSync); + EXPECT_FALSE(primary_account.IsEmpty()); + EXPECT_TRUE(gaia::AreEmailsSame(test_account.user, primary_account.email)); + EXPECT_TRUE(sync_service()->IsSyncFeatureEnabled()); + + SignOutFromWeb(); + + const AccountsInCookieJarInfo& accounts_in_cookie_jar = + identity_manager()->GetAccountsInCookieJar(); + EXPECT_TRUE(accounts_in_cookie_jar.accounts_are_fresh); + ASSERT_TRUE(accounts_in_cookie_jar.signed_in_accounts.empty()); + ASSERT_EQ(1u, accounts_in_cookie_jar.signed_out_accounts.size()); + EXPECT_TRUE(gaia::AreEmailsSame( + test_account.user, accounts_in_cookie_jar.signed_out_accounts[0].email)); + EXPECT_TRUE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + primary_account.account_id)); +#if !BUILDFLAG(IS_CHROMEOS_ASH) + EXPECT_EQ(GetAvatarSyncErrorType(browser()->profile()), + AvatarSyncErrorType::kAuthError); +#endif // !BUILDFLAG(IS_CHROMEOS_ASH) +} + +// This test can pass. Marked as manual because it TIMED_OUT on Win7. +// See crbug.com/1025335. +// Sings in two accounts on the web and checks that cookies and refresh tokens +// are added to Chrome. Sync should be disabled. +// Then, signs out on the web and checks that accounts are removed from Chrome. +IN_PROC_BROWSER_TEST_F(LiveSignInTest, MANUAL_WebSignInAndSignOut) { + TestAccount test_account_1; + CHECK(GetTestAccountsUtil()->GetAccount("TEST_ACCOUNT_1", test_account_1)); + SignInFromWeb(test_account_1, 0); + + const AccountsInCookieJarInfo& accounts_in_cookie_jar_1 = + identity_manager()->GetAccountsInCookieJar(); + EXPECT_TRUE(accounts_in_cookie_jar_1.accounts_are_fresh); + ASSERT_EQ(1u, accounts_in_cookie_jar_1.signed_in_accounts.size()); + EXPECT_TRUE(accounts_in_cookie_jar_1.signed_out_accounts.empty()); + const gaia::ListedAccount& account_1 = + accounts_in_cookie_jar_1.signed_in_accounts[0]; + EXPECT_TRUE(gaia::AreEmailsSame(test_account_1.user, account_1.email)); + EXPECT_TRUE(identity_manager()->HasAccountWithRefreshToken(account_1.id)); + EXPECT_FALSE( + identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSync)); + + TestAccount test_account_2; + CHECK(GetTestAccountsUtil()->GetAccount("TEST_ACCOUNT_2", test_account_2)); + SignInFromWeb(test_account_2, 1); + + const AccountsInCookieJarInfo& accounts_in_cookie_jar_2 = + identity_manager()->GetAccountsInCookieJar(); + EXPECT_TRUE(accounts_in_cookie_jar_2.accounts_are_fresh); + ASSERT_EQ(2u, accounts_in_cookie_jar_2.signed_in_accounts.size()); + EXPECT_TRUE(accounts_in_cookie_jar_2.signed_out_accounts.empty()); + EXPECT_EQ(accounts_in_cookie_jar_2.signed_in_accounts[0].id, account_1.id); + const gaia::ListedAccount& account_2 = + accounts_in_cookie_jar_2.signed_in_accounts[1]; + EXPECT_TRUE(gaia::AreEmailsSame(test_account_2.user, account_2.email)); + EXPECT_TRUE(identity_manager()->HasAccountWithRefreshToken(account_2.id)); + EXPECT_FALSE( + identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSync)); + + SignOutFromWeb(); + + const AccountsInCookieJarInfo& accounts_in_cookie_jar_3 = + identity_manager()->GetAccountsInCookieJar(); + EXPECT_TRUE(accounts_in_cookie_jar_3.accounts_are_fresh); + ASSERT_TRUE(accounts_in_cookie_jar_3.signed_in_accounts.empty()); + EXPECT_EQ(2u, accounts_in_cookie_jar_3.signed_out_accounts.size()); + EXPECT_TRUE(identity_manager()->GetAccountsWithRefreshTokens().empty()); +} + +// This test can pass. Marked as manual because it TIMED_OUT on Win7. +// See crbug.com/1025335. +// Signs in an account through the settings page and enables Sync. Checks that +// Sync is enabled. Signs in a second account on the web. +// Then, turns Sync off from the settings page and checks that both accounts are +// removed from Chrome and from cookies. +IN_PROC_BROWSER_TEST_F(LiveSignInTest, MANUAL_TurnOffSync) { + TestAccount test_account_1; + CHECK(GetTestAccountsUtil()->GetAccount("TEST_ACCOUNT_1", test_account_1)); + TurnOnSync(test_account_1, 0); + + TestAccount test_account_2; + CHECK(GetTestAccountsUtil()->GetAccount("TEST_ACCOUNT_2", test_account_2)); + SignInFromWeb(test_account_2, 1); + + const CoreAccountInfo& primary_account = + identity_manager()->GetPrimaryAccountInfo(signin::ConsentLevel::kSync); + EXPECT_FALSE(primary_account.IsEmpty()); + EXPECT_TRUE(gaia::AreEmailsSame(test_account_1.user, primary_account.email)); + EXPECT_TRUE(sync_service()->IsSyncFeatureEnabled()); + + TurnOffSync(); + + const AccountsInCookieJarInfo& accounts_in_cookie_jar_2 = + identity_manager()->GetAccountsInCookieJar(); + EXPECT_TRUE(accounts_in_cookie_jar_2.accounts_are_fresh); + ASSERT_TRUE(accounts_in_cookie_jar_2.signed_in_accounts.empty()); + EXPECT_TRUE(identity_manager()->GetAccountsWithRefreshTokens().empty()); + EXPECT_FALSE( + identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSync)); +} + +// In "Sync paused" state, when the primary account is invalid, turns off sync +// from settings. Checks that the account is removed from Chrome. +// Regression test for https://crbug.com/1114646 +IN_PROC_BROWSER_TEST_F(LiveSignInTest, MANUAL_TurnOffSyncWhenPaused) { + TestAccount test_account_1; + CHECK(GetTestAccountsUtil()->GetAccount("TEST_ACCOUNT_1", test_account_1)); + TurnOnSync(test_account_1, 0); + + // Get in sync paused state. + SignOutFromWeb(); + + const CoreAccountInfo& primary_account = + identity_manager()->GetPrimaryAccountInfo(signin::ConsentLevel::kSync); + EXPECT_FALSE(primary_account.IsEmpty()); + EXPECT_TRUE(gaia::AreEmailsSame(test_account_1.user, primary_account.email)); + EXPECT_TRUE(sync_service()->IsSyncFeatureEnabled()); + EXPECT_TRUE( + identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState( + primary_account.account_id)); + + TurnOffSync(); + EXPECT_TRUE(identity_manager()->GetAccountsWithRefreshTokens().empty()); + EXPECT_FALSE( + identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSync)); +} + +// This test can pass. Marked as manual because it TIMED_OUT on Win7. +// See crbug.com/1025335. +// Signs in an account on the web. Goes to the Chrome settings to enable Sync +// but cancels the sync confirmation dialog. Checks that the account is still +// signed in on the web but Sync is disabled. +IN_PROC_BROWSER_TEST_F(LiveSignInTest, MANUAL_CancelSyncWithWebAccount) { + TestAccount test_account; + CHECK(GetTestAccountsUtil()->GetAccount("TEST_ACCOUNT_1", test_account)); + SignInFromWeb(test_account, 0); + + SignInTestObserver observer(identity_manager(), account_reconcilor()); + GURL settings_url("chrome://settings"); + AddTabAtIndex(0, settings_url, ui::PageTransition::PAGE_TRANSITION_TYPED); + auto* settings_tab = browser()->tab_strip_model()->GetActiveWebContents(); + std::string start_syncing_script = base::StringPrintf( + "settings.SyncBrowserProxyImpl.getInstance()." + "startSyncingWithEmail(\"%s\", true);", + test_account.user.c_str()); + EXPECT_TRUE(content::ExecuteScript( + settings_tab, base::StringPrintf(kSettingsScriptWrapperFormat, + start_syncing_script.c_str()))); + EXPECT_TRUE(login_ui_test_utils::CancelSyncConfirmationDialog( + browser(), kDialogTimeout)); + observer.WaitForAccountChanges(1, PrimarySyncAccountWait::kWaitForCleared); + + const AccountsInCookieJarInfo& accounts_in_cookie_jar = + identity_manager()->GetAccountsInCookieJar(); + EXPECT_TRUE(accounts_in_cookie_jar.accounts_are_fresh); + ASSERT_EQ(1u, accounts_in_cookie_jar.signed_in_accounts.size()); + const gaia::ListedAccount& account = + accounts_in_cookie_jar.signed_in_accounts[0]; + EXPECT_TRUE(gaia::AreEmailsSame(test_account.user, account.email)); + EXPECT_TRUE(identity_manager()->HasAccountWithRefreshToken(account.id)); + EXPECT_FALSE( + identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSync)); +} + +// This test can pass. Marked as manual because it TIMED_OUT on Win7. +// See crbug.com/1025335. +// Starts the sign in flow from the settings page, enters credentials on the +// login page but cancels the Sync confirmation dialog. Checks that Sync is +// disabled and no account was added to Chrome. +IN_PROC_BROWSER_TEST_F(LiveSignInTest, MANUAL_CancelSync) { + TestAccount test_account; + CHECK(GetTestAccountsUtil()->GetAccount("TEST_ACCOUNT_1", test_account)); + SignInFromSettings(test_account, 0); + + SignInTestObserver observer(identity_manager(), account_reconcilor()); + EXPECT_TRUE(login_ui_test_utils::CancelSyncConfirmationDialog( + browser(), kDialogTimeout)); + observer.WaitForAccountChanges(0, PrimarySyncAccountWait::kWaitForCleared); + + const AccountsInCookieJarInfo& accounts_in_cookie_jar = + identity_manager()->GetAccountsInCookieJar(); + EXPECT_TRUE(accounts_in_cookie_jar.accounts_are_fresh); + EXPECT_TRUE(accounts_in_cookie_jar.signed_in_accounts.empty()); + EXPECT_TRUE(identity_manager()->GetAccountsWithRefreshTokens().empty()); + EXPECT_FALSE( + identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSync)); +} + +// This test can pass. Marked as manual because it TIMED_OUT on Win7. +// See crbug.com/1025335. +// Enables and disables sync to account 1. Enables sync to account 2 and clicks +// on "This wasn't me" in the email confirmation dialog. Checks that the new +// profile is created. Checks that Sync to account 2 is enabled in the new +// profile. Checks that account 2 was removed from the original profile. +IN_PROC_BROWSER_TEST_F(LiveSignInTest, + MANUAL_SyncSecondAccount_CreateNewProfile) { + // Enable and disable sync for the first account. + TestAccount test_account_1; + CHECK(GetTestAccountsUtil()->GetAccount("TEST_ACCOUNT_1", test_account_1)); + TurnOnSync(test_account_1, 0); + TurnOffSync(); + + // Start enable sync for the second account. + TestAccount test_account_2; + CHECK(GetTestAccountsUtil()->GetAccount("TEST_ACCOUNT_2", test_account_2)); + SignInFromSettings(test_account_2, 0); + + // Set up an observer for removing the second account from the original + // profile. + SignInTestObserver original_browser_observer(identity_manager(), + account_reconcilor()); + + // Check there is only one profile. + ProfileManager* profile_manager = g_browser_process->profile_manager(); + EXPECT_EQ(profile_manager->GetNumberOfProfiles(), 1U); + EXPECT_EQ(chrome::GetTotalBrowserCount(), 1U); + + // Click "This wasn't me" on the email confirmation dialog and wait for a new + // browser and profile created. + EXPECT_TRUE(login_ui_test_utils::CompleteSigninEmailConfirmationDialog( + browser(), kDialogTimeout, + SigninEmailConfirmationDialog::CREATE_NEW_USER)); + Browser* new_browser = ui_test_utils::WaitForBrowserToOpen(); + EXPECT_EQ(profile_manager->GetNumberOfProfiles(), 2U); + EXPECT_EQ(chrome::GetTotalBrowserCount(), 2U); + EXPECT_NE(browser()->profile(), new_browser->profile()); + + // Confirm sync in the new browser window. + SignInTestObserver new_browser_observer(identity_manager(new_browser), + account_reconcilor(new_browser)); + EXPECT_TRUE(login_ui_test_utils::ConfirmSyncConfirmationDialog( + new_browser, kDialogTimeout)); + new_browser_observer.WaitForAccountChanges( + 1, PrimarySyncAccountWait::kWaitForAdded); + + // Check accounts in cookies in the new profile. + const AccountsInCookieJarInfo& accounts_in_cookie_jar = + identity_manager(new_browser)->GetAccountsInCookieJar(); + EXPECT_TRUE(accounts_in_cookie_jar.accounts_are_fresh); + ASSERT_EQ(1u, accounts_in_cookie_jar.signed_in_accounts.size()); + const gaia::ListedAccount& account = + accounts_in_cookie_jar.signed_in_accounts[0]; + EXPECT_TRUE(gaia::AreEmailsSame(test_account_2.user, account.email)); + + // Check the primary account in the new profile is set and syncing. + const CoreAccountInfo& primary_account = + identity_manager(new_browser) + ->GetPrimaryAccountInfo(signin::ConsentLevel::kSync); + EXPECT_FALSE(primary_account.IsEmpty()); + EXPECT_TRUE(gaia::AreEmailsSame(test_account_2.user, primary_account.email)); + EXPECT_TRUE(identity_manager(new_browser) + ->HasAccountWithRefreshToken(primary_account.account_id)); + EXPECT_TRUE(sync_service(new_browser)->IsSyncFeatureEnabled()); + + // Check that the second account was removed from the original profile. + original_browser_observer.WaitForAccountChanges( + 0, PrimarySyncAccountWait::kWaitForCleared); + const AccountsInCookieJarInfo& accounts_in_cookie_jar_2 = + identity_manager()->GetAccountsInCookieJar(); + EXPECT_TRUE(accounts_in_cookie_jar_2.accounts_are_fresh); + ASSERT_TRUE(accounts_in_cookie_jar_2.signed_in_accounts.empty()); + EXPECT_TRUE(identity_manager()->GetAccountsWithRefreshTokens().empty()); + EXPECT_FALSE( + identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSync)); +} + +// This test can pass. Marked as manual because it TIMED_OUT on Win7. +// See crbug.com/1025335. +// Enables and disables sync to account 1. Enables sync to account 2 and clicks +// on "This was me" in the email confirmation dialog. Checks that Sync to +// account 2 is enabled in the current profile. +IN_PROC_BROWSER_TEST_F(LiveSignInTest, + MANUAL_SyncSecondAccount_InExistingProfile) { + // Enable and disable sync for the first account. + TestAccount test_account_1; + CHECK(GetTestAccountsUtil()->GetAccount("TEST_ACCOUNT_1", test_account_1)); + TurnOnSync(test_account_1, 0); + TurnOffSync(); + + // Start enable sync for the second account. + TestAccount test_account_2; + CHECK(GetTestAccountsUtil()->GetAccount("TEST_ACCOUNT_2", test_account_2)); + SignInFromSettings(test_account_2, 0); + + // Check there is only one profile. + ProfileManager* profile_manager = g_browser_process->profile_manager(); + EXPECT_EQ(profile_manager->GetNumberOfProfiles(), 1U); + EXPECT_EQ(chrome::GetTotalBrowserCount(), 1U); + + // Click "This was me" on the email confirmation dialog, confirm sync and wait + // for a primary account to be set. + SignInTestObserver observer(identity_manager(), account_reconcilor()); + EXPECT_TRUE(login_ui_test_utils::CompleteSigninEmailConfirmationDialog( + browser(), kDialogTimeout, SigninEmailConfirmationDialog::START_SYNC)); + EXPECT_TRUE(login_ui_test_utils::ConfirmSyncConfirmationDialog( + browser(), kDialogTimeout)); + observer.WaitForAccountChanges(1, PrimarySyncAccountWait::kWaitForAdded); + + // Check no profile was created. + EXPECT_EQ(profile_manager->GetNumberOfProfiles(), 1U); + EXPECT_EQ(chrome::GetTotalBrowserCount(), 1U); + + // Check accounts in cookies. + const AccountsInCookieJarInfo& accounts_in_cookie_jar = + identity_manager()->GetAccountsInCookieJar(); + EXPECT_TRUE(accounts_in_cookie_jar.accounts_are_fresh); + ASSERT_EQ(1u, accounts_in_cookie_jar.signed_in_accounts.size()); + const gaia::ListedAccount& account = + accounts_in_cookie_jar.signed_in_accounts[0]; + EXPECT_TRUE(gaia::AreEmailsSame(test_account_2.user, account.email)); + + // Check the primary account is set and syncing. + const CoreAccountInfo& primary_account = + identity_manager()->GetPrimaryAccountInfo(signin::ConsentLevel::kSync); + EXPECT_FALSE(primary_account.IsEmpty()); + EXPECT_TRUE(gaia::AreEmailsSame(test_account_2.user, primary_account.email)); + EXPECT_TRUE(identity_manager()->HasAccountWithRefreshToken( + primary_account.account_id)); + EXPECT_TRUE(sync_service()->IsSyncFeatureEnabled()); +} + +// This test can pass. Marked as manual because it TIMED_OUT on Win7. +// See crbug.com/1025335. +// Enables and disables sync to account 1. Enables sync to account 2 and clicks +// on "Cancel" in the email confirmation dialog. Checks that the signin flow is +// canceled and no accounts are added to Chrome. +IN_PROC_BROWSER_TEST_F(LiveSignInTest, + MANUAL_SyncSecondAccount_CancelOnEmailConfirmation) { + // Enable and disable sync for the first account. + TestAccount test_account_1; + CHECK(GetTestAccountsUtil()->GetAccount("TEST_ACCOUNT_1", test_account_1)); + TurnOnSync(test_account_1, 0); + TurnOffSync(); + + // Start enable sync for the second account. + TestAccount test_account_2; + CHECK(GetTestAccountsUtil()->GetAccount("TEST_ACCOUNT_2", test_account_2)); + SignInFromSettings(test_account_2, 0); + + // Check there is only one profile. + ProfileManager* profile_manager = g_browser_process->profile_manager(); + EXPECT_EQ(profile_manager->GetNumberOfProfiles(), 1U); + EXPECT_EQ(chrome::GetTotalBrowserCount(), 1U); + + // Click "Cancel" on the email confirmation dialog and wait for an account to + // removed from Chrome. + SignInTestObserver observer(identity_manager(), account_reconcilor()); + EXPECT_TRUE(login_ui_test_utils::CompleteSigninEmailConfirmationDialog( + browser(), kDialogTimeout, SigninEmailConfirmationDialog::CLOSE)); + observer.WaitForAccountChanges(0, PrimarySyncAccountWait::kWaitForCleared); + + // Check no profile was created. + EXPECT_EQ(profile_manager->GetNumberOfProfiles(), 1U); + EXPECT_EQ(chrome::GetTotalBrowserCount(), 1U); + + // Check Chrome has no accounts. + const AccountsInCookieJarInfo& accounts_in_cookie_jar = + identity_manager()->GetAccountsInCookieJar(); + EXPECT_TRUE(accounts_in_cookie_jar.accounts_are_fresh); + EXPECT_TRUE(accounts_in_cookie_jar.signed_in_accounts.empty()); + EXPECT_TRUE(identity_manager()->GetAccountsWithRefreshTokens().empty()); + EXPECT_FALSE( + identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSync)); +} + +IN_PROC_BROWSER_TEST_F(LiveSignInTest, + MANUAL_AccountCapabilities_FetchedOnSignIn) { + EnableAccountCapabilitiesFetches(identity_manager()); + + // Test primary adult account. + { + AccountCapabilitiesObserver capabilities_observer(identity_manager()); + + TestAccount ta; + ASSERT_TRUE(GetTestAccountsUtil()->GetAccount("TEST_ACCOUNT_1", ta)); + SignInFromSettings(ta, 0); + + CoreAccountInfo core_account_info = + identity_manager()->GetPrimaryAccountInfo(ConsentLevel::kSignin); + ASSERT_TRUE(gaia::AreEmailsSame(core_account_info.email, ta.user)); + + capabilities_observer.WaitForAllCapabilitiesToBeKnown( + core_account_info.account_id); + AccountInfo account_info = + identity_manager()->FindExtendedAccountInfoByAccountId( + core_account_info.account_id); + EXPECT_EQ(account_info.capabilities.can_offer_extended_chrome_sync_promos(), + Tribool::kTrue); + } + + // Test secondary minor account. + { + AccountCapabilitiesObserver capabilities_observer(identity_manager()); + + TestAccount ta; + ASSERT_TRUE(GetTestAccountsUtil()->GetAccount("TEST_ACCOUNT_MINOR", ta)); + SignInFromWeb(ta, /*previously_signed_in_accounts=*/1); + + CoreAccountInfo core_account_info = + identity_manager()->FindExtendedAccountInfoByEmailAddress(ta.user); + ASSERT_FALSE(core_account_info.IsEmpty()); + + capabilities_observer.WaitForAllCapabilitiesToBeKnown( + core_account_info.account_id); + AccountInfo account_info = + identity_manager()->FindExtendedAccountInfoByAccountId( + core_account_info.account_id); + EXPECT_EQ(account_info.capabilities.can_offer_extended_chrome_sync_promos(), + Tribool::kFalse); + } +} + +} // namespace test +} // namespace signin diff --git a/chromium/chrome/browser/signin/e2e_tests/live_test.cc b/chromium/chrome/browser/signin/e2e_tests/live_test.cc new file mode 100644 index 00000000000..e8d22189e73 --- /dev/null +++ b/chromium/chrome/browser/signin/e2e_tests/live_test.cc @@ -0,0 +1,61 @@ +// Copyright 2019 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. +#include "chrome/browser/signin/e2e_tests/live_test.h" + +#include "base/files/file_util.h" +#include "base/path_service.h" +#include "net/dns/mock_host_resolver.h" + +base::FilePath::StringPieceType kTestAccountFilePath = FILE_PATH_LITERAL( + "chrome/browser/internal/resources/signin/test_accounts.json"); + +const char* kRunLiveTestFlag = "run-live-tests"; + +namespace signin { +namespace test { + +void LiveTest::SetUpInProcessBrowserTestFixture() { + // Whitelists a bunch of hosts. + host_resolver()->AllowDirectLookup("*.google.com"); + host_resolver()->AllowDirectLookup("*.geotrust.com"); + host_resolver()->AllowDirectLookup("*.gstatic.com"); + host_resolver()->AllowDirectLookup("*.googleapis.com"); + // Allows country-specific TLDs. + host_resolver()->AllowDirectLookup("accounts.google.*"); + + InProcessBrowserTest::SetUpInProcessBrowserTestFixture(); +} + +void LiveTest::SetUp() { + // Only run live tests when specified. + auto* cmd_line = base::CommandLine::ForCurrentProcess(); + if (!cmd_line->HasSwitch(kRunLiveTestFlag)) { + LOG(INFO) << "This test should get skipped."; + skip_test_ = true; + GTEST_SKIP(); + } + base::FilePath root_path; + base::PathService::Get(base::BasePathKey::DIR_SOURCE_ROOT, &root_path); + base::FilePath config_path = + base::MakeAbsoluteFilePath(root_path.Append(kTestAccountFilePath)); + test_accounts_.Init(config_path); + InProcessBrowserTest::SetUp(); +} + +void LiveTest::TearDown() { + // This test was skipped, no need to tear down. + if (skip_test_) + return; + InProcessBrowserTest::TearDown(); +} + +void LiveTest::PostRunTestOnMainThread() { + // This test was skipped. Running PostRunTestOnMainThread can cause + // TIMED_OUT on Win7. + if (skip_test_) + return; + InProcessBrowserTest::PostRunTestOnMainThread(); +} +} // namespace test +} // namespace signin diff --git a/chromium/chrome/browser/signin/e2e_tests/live_test.h b/chromium/chrome/browser/signin/e2e_tests/live_test.h new file mode 100644 index 00000000000..6de9f8f4ded --- /dev/null +++ b/chromium/chrome/browser/signin/e2e_tests/live_test.h @@ -0,0 +1,33 @@ +// Copyright 2019 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. + +#ifndef CHROME_BROWSER_SIGNIN_E2E_TESTS_LIVE_TEST_H_ +#define CHROME_BROWSER_SIGNIN_E2E_TESTS_LIVE_TEST_H_ + +#include "chrome/browser/signin/e2e_tests/test_accounts_util.h" +#include "chrome/test/base/in_process_browser_test.h" + +namespace signin { +namespace test { + +class LiveTest : public InProcessBrowserTest { + protected: + void SetUpInProcessBrowserTestFixture() override; + void SetUp() override; + void TearDown() override; + void PostRunTestOnMainThread() override; + + const TestAccountsUtil* GetTestAccountsUtil() const { + return &test_accounts_; + } + + private: + TestAccountsUtil test_accounts_; + bool skip_test_ = false; +}; + +} // namespace test +} // namespace signin + +#endif // CHROME_BROWSER_SIGNIN_E2E_TESTS_LIVE_TEST_H_ diff --git a/chromium/chrome/browser/signin/e2e_tests/test_accounts_util.cc b/chromium/chrome/browser/signin/e2e_tests/test_accounts_util.cc new file mode 100644 index 00000000000..c533ea0fb3c --- /dev/null +++ b/chromium/chrome/browser/signin/e2e_tests/test_accounts_util.cc @@ -0,0 +1,75 @@ +// Copyright 2019 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. + +#include "chrome/browser/signin/e2e_tests/test_accounts_util.h" + +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/json/json_file_value_serializer.h" +#include "base/json/json_reader.h" +#include "build/build_config.h" +#include "build/chromeos_buildflags.h" + +using base::Value; + +namespace signin { +namespace test { + +#if defined(OS_WIN) +std::string kPlatform = "win"; +#elif defined(OS_MAC) +std::string kPlatform = "mac"; +#elif BUILDFLAG(IS_CHROMEOS_ASH) +std::string kPlatform = "chromeos"; +#elif defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) +std::string kPlatform = "linux"; +#elif defined(OS_ANDROID) +std::string kPlatform = "android"; +#else +std::string kPlatform = "all_platform"; +#endif + +TestAccountsUtil::TestAccountsUtil() = default; +TestAccountsUtil::~TestAccountsUtil() = default; + +bool TestAccountsUtil::Init(const base::FilePath& config_path) { + int error_code = 0; + std::string error_str; + JSONFileValueDeserializer deserializer(config_path); + std::unique_ptr<Value> content_json = + deserializer.Deserialize(&error_code, &error_str); + CHECK(error_code == 0) << "Error reading json file. Error code: " + << error_code << " " << error_str; + CHECK(content_json); + + // Only store platform specific users. If an account does not have + // platform specific user, try to use all_platform user. + for (auto account : content_json->DictItems()) { + const Value* platform_account = account.second.FindDictKey(kPlatform); + if (platform_account == nullptr) { + platform_account = account.second.FindDictKey("all_platform"); + if (platform_account == nullptr) { + continue; + } + } + TestAccount ta(*(platform_account->FindStringKey("user")), + *(platform_account->FindStringKey("password"))); + all_accounts_.insert( + std::pair<std::string, TestAccount>(account.first, ta)); + } + return true; +} + +bool TestAccountsUtil::GetAccount(const std::string& name, + TestAccount& out_account) const { + auto it = all_accounts_.find(name); + if (it == all_accounts_.end()) { + return false; + } + out_account = it->second; + return true; +} + +} // namespace test +} // namespace signin diff --git a/chromium/chrome/browser/signin/e2e_tests/test_accounts_util.h b/chromium/chrome/browser/signin/e2e_tests/test_accounts_util.h new file mode 100644 index 00000000000..4f73dd76d32 --- /dev/null +++ b/chromium/chrome/browser/signin/e2e_tests/test_accounts_util.h @@ -0,0 +1,46 @@ +// Copyright 2018 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. + +#ifndef CHROME_BROWSER_SIGNIN_E2E_TESTS_TEST_ACCOUNTS_UTIL_H_ +#define CHROME_BROWSER_SIGNIN_E2E_TESTS_TEST_ACCOUNTS_UTIL_H_ + +#include <map> +#include <string> + +namespace base { +class FilePath; +} + +namespace signin { +namespace test { + +struct TestAccount { + std::string user; + std::string password; + TestAccount() = default; + TestAccount(const std::string& user, const std::string& password) { + this->user = user; + this->password = password; + } +}; + +class TestAccountsUtil { + public: + TestAccountsUtil(); + + TestAccountsUtil(const TestAccountsUtil&) = delete; + TestAccountsUtil& operator=(const TestAccountsUtil&) = delete; + + virtual ~TestAccountsUtil(); + bool Init(const base::FilePath& config_path); + bool GetAccount(const std::string& name, TestAccount& out_account) const; + + private: + std::map<std::string, TestAccount> all_accounts_; +}; + +} // namespace test +} // namespace signin + +#endif // CHROME_BROWSER_SIGNIN_E2E_TESTS_TEST_ACCOUNTS_UTIL_H_ diff --git a/chromium/chrome/browser/signin/e2e_tests/test_accounts_util_unittest.cc b/chromium/chrome/browser/signin/e2e_tests/test_accounts_util_unittest.cc new file mode 100644 index 00000000000..7b63dd6128a --- /dev/null +++ b/chromium/chrome/browser/signin/e2e_tests/test_accounts_util_unittest.cc @@ -0,0 +1,100 @@ +// Copyright 2019 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. + +#include "chrome/browser/signin/e2e_tests/test_accounts_util.h" +#include "base/files/file_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::FilePath; + +namespace signin { +namespace test { + +class TestAccountsUtilTest : public testing::Test {}; + +FilePath WriteContentToTemporaryFile(const char* contents, + unsigned int length) { + FilePath tmp_file; + CHECK(base::CreateTemporaryFile(&tmp_file)); + unsigned int bytes_written = base::WriteFile(tmp_file, contents, length); + CHECK_EQ(bytes_written, length); + return tmp_file; +} + +TEST(TestAccountsUtilTest, ParsingJson) { + const char contents[] = + "{ \n" + " \"TEST_ACCOUNT_1\": {\n" + " \"win\": {\n" + " \"user\": \"user1\",\n" + " \"password\": \"pwd1\"\n" + " }\n" + " }\n" + "}"; + FilePath tmp_file = + WriteContentToTemporaryFile(contents, sizeof(contents) - 1); + TestAccountsUtil util; + util.Init(tmp_file); +} + +TEST(TestAccountsUtilTest, GetAccountForPlatformSpecific) { + const char contents[] = + "{ \n" + " \"TEST_ACCOUNT_1\": {\n" + " \"win\": {\n" + " \"user\": \"user1\",\n" + " \"password\": \"pwd1\"\n" + " },\n" + " \"mac\": {\n" + " \"user\": \"user1\",\n" + " \"password\": \"pwd1\"\n" + " },\n" + " \"linux\": {\n" + " \"user\": \"user1\",\n" + " \"password\": \"pwd1\"\n" + " },\n" + " \"chromeos\": {\n" + " \"user\": \"user1\",\n" + " \"password\": \"pwd1\"\n" + " },\n" + " \"android\": {\n" + " \"user\": \"user1\",\n" + " \"password\": \"pwd1\"\n" + " }\n" + " }\n" + "}"; + FilePath tmp_file = + WriteContentToTemporaryFile(contents, sizeof(contents) - 1); + TestAccountsUtil util; + util.Init(tmp_file); + TestAccount ta; + bool ret = util.GetAccount("TEST_ACCOUNT_1", ta); + ASSERT_TRUE(ret); + ASSERT_EQ(ta.user, "user1"); + ASSERT_EQ(ta.password, "pwd1"); +} + +TEST(TestAccountsUtilTest, GetAccountForAllPlatform) { + const char contents[] = + "{ \n" + " \"TEST_ACCOUNT_1\": {\n" + " \"all_platform\": {\n" + " \"user\": \"user_allplatform\",\n" + " \"password\": \"pwd_allplatform\"\n" + " }\n" + " }\n" + "}"; + FilePath tmp_file = + WriteContentToTemporaryFile(contents, sizeof(contents) - 1); + TestAccountsUtil util; + util.Init(tmp_file); + TestAccount ta; + bool ret = util.GetAccount("TEST_ACCOUNT_1", ta); + ASSERT_TRUE(ret); + ASSERT_EQ(ta.user, "user_allplatform"); + ASSERT_EQ(ta.password, "pwd_allplatform"); +} + +} // namespace test +} // namespace signin |