| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <memory> |
| #include <optional> |
| #include <vector> |
| |
| #include "base/functional/callback_forward.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/notreached.h" |
| #include "base/run_loop.h" |
| #include "build/buildflag.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/signin/account_reconcilor_factory.h" |
| #include "chrome/browser/signin/signin_browser_test_base.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/webui/signin/turn_sync_on_helper.h" |
| #include "components/signin/core/browser/account_reconcilor.h" |
| #include "components/signin/public/base/consent_level.h" |
| #include "components/signin/public/base/signin_switches.h" |
| #include "components/signin/public/identity_manager/account_info.h" |
| #include "components/signin/public/identity_manager/accounts_mutator.h" |
| #include "components/signin/public/identity_manager/identity_manager.h" |
| #include "components/signin/public/identity_manager/identity_test_utils.h" |
| #include "content/public/test/browser_test.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using testing::UnorderedElementsAre; |
| |
| namespace { |
| |
| class Delegate : public TurnSyncOnHelper::Delegate { |
| public: |
| enum class BlockingStep { |
| kNone, |
| kMergeData, |
| kEnterpriseManagement, |
| kSyncConfirmation, |
| kSyncDisabled, |
| }; |
| |
| struct Choices { |
| std::optional<signin::SigninChoice> merge_data_choice = |
| signin::SIGNIN_CHOICE_CONTINUE; |
| std::optional<signin::SigninChoice> enterprise_management_choice = |
| signin::SIGNIN_CHOICE_CONTINUE; |
| std::optional<LoginUIService::SyncConfirmationUIClosedResult> |
| sync_optin_choice = LoginUIService::SYNC_WITH_DEFAULT_SETTINGS; |
| std::optional<LoginUIService::SyncConfirmationUIClosedResult> |
| sync_disabled_choice = std::nullopt; |
| }; |
| |
| using SyncConfirmationCallback = |
| base::OnceCallback<void(LoginUIService::SyncConfirmationUIClosedResult)>; |
| using CallbackVariant = |
| absl::variant<signin::SigninChoiceCallback, SyncConfirmationCallback>; |
| |
| explicit Delegate(Choices choices) |
| : choices_(choices), run_loop_(std::make_unique<base::RunLoop>()) {} |
| ~Delegate() override = default; |
| |
| // TurnSyncOnHelper::Delegate: |
| void ShowLoginError(const SigninUIError& error) override { NOTREACHED(); } |
| void ShowMergeSyncDataConfirmation( |
| const std::string& previous_email, |
| const std::string& new_email, |
| signin::SigninChoiceCallback callback) override { |
| AdvanceFlowOrCapture(BlockingStep::kMergeData, std::move(callback)); |
| } |
| void ShowEnterpriseAccountConfirmation( |
| const AccountInfo& account_info, |
| signin::SigninChoiceCallback callback) override { |
| AdvanceFlowOrCapture(BlockingStep::kEnterpriseManagement, |
| std::move(callback)); |
| } |
| void ShowSyncConfirmation(SyncConfirmationCallback callback) override { |
| AdvanceFlowOrCapture(BlockingStep::kSyncConfirmation, std::move(callback)); |
| } |
| void ShowSyncDisabledConfirmation( |
| bool is_managed_account, |
| SyncConfirmationCallback callback) override { |
| AdvanceFlowOrCapture(BlockingStep::kSyncDisabled, std::move(callback)); |
| } |
| void ShowSyncSettings() override { NOTREACHED(); } |
| void SwitchToProfile(Profile* new_profile) override { NOTREACHED(); } |
| |
| BlockingStep blocking_step() const { return blocking_step_; } |
| |
| base::WeakPtr<Delegate> GetWeakPtr() { |
| return weak_ptr_factory_.GetWeakPtr(); |
| } |
| |
| void WaitUntilBlock() { |
| DCHECK(run_loop_); |
| run_loop_->Run(); |
| |
| // After the wait ends, reset the `run_loop_` to wait for the next choice. |
| run_loop_.reset(); |
| run_loop_ = std::make_unique<base::RunLoop>(); |
| } |
| |
| // Call this function when the delegate is blocked, with new `choices` that |
| // should now unblock it. |
| void UpdateChoicesAndAdvanceFlow(Choices choices) { |
| auto blocking_step = blocking_step_; |
| ASSERT_NE(BlockingStep::kNone, blocking_step) |
| << "UpdateChoicesAndAdvanceFlow() expects to be called while the " |
| "delegate is blocked."; |
| choices_ = choices; |
| blocking_step_ = BlockingStep::kNone; |
| |
| switch (blocking_step) { |
| case BlockingStep::kNone: |
| NOTREACHED(); |
| break; |
| case BlockingStep::kMergeData: |
| ASSERT_TRUE(choices_.merge_data_choice.has_value()); |
| std::move(absl::get<signin::SigninChoiceCallback>(blocking_callback_)) |
| .Run(*choices_.merge_data_choice); |
| break; |
| case BlockingStep::kEnterpriseManagement: |
| ASSERT_TRUE(choices_.enterprise_management_choice.has_value()); |
| std::move(absl::get<signin::SigninChoiceCallback>(blocking_callback_)) |
| .Run(*choices_.enterprise_management_choice); |
| break; |
| case BlockingStep::kSyncConfirmation: |
| ASSERT_TRUE(choices_.sync_optin_choice.has_value()); |
| std::move(absl::get<SyncConfirmationCallback>(blocking_callback_)) |
| .Run(*choices_.sync_optin_choice); |
| break; |
| case BlockingStep::kSyncDisabled: |
| ASSERT_TRUE(choices_.sync_disabled_choice.has_value()); |
| std::move(absl::get<SyncConfirmationCallback>(blocking_callback_)) |
| .Run(*choices_.sync_disabled_choice); |
| break; |
| } |
| } |
| |
| private: |
| void AdvanceFlowOrCapture(BlockingStep step, CallbackVariant callback) { |
| switch (step) { |
| case BlockingStep::kNone: |
| NOTREACHED(); |
| break; |
| case BlockingStep::kMergeData: |
| if (!choices_.merge_data_choice.has_value()) |
| break; |
| std::move(absl::get<signin::SigninChoiceCallback>(callback)) |
| .Run(*choices_.merge_data_choice); |
| return; |
| case BlockingStep::kEnterpriseManagement: |
| if (!choices_.enterprise_management_choice.has_value()) |
| break; |
| std::move(absl::get<signin::SigninChoiceCallback>(callback)) |
| .Run(*choices_.enterprise_management_choice); |
| return; |
| case BlockingStep::kSyncConfirmation: |
| if (!choices_.sync_optin_choice.has_value()) |
| break; |
| std::move(absl::get<SyncConfirmationCallback>(callback)) |
| .Run(*choices_.sync_optin_choice); |
| return; |
| case BlockingStep::kSyncDisabled: |
| if (!choices_.sync_disabled_choice.has_value()) |
| break; |
| std::move(absl::get<SyncConfirmationCallback>(callback)) |
| .Run(*choices_.sync_disabled_choice); |
| return; |
| } |
| |
| blocking_step_ = step; |
| blocking_callback_ = std::move(callback); |
| DCHECK(run_loop_); |
| run_loop_->Quit(); |
| } |
| |
| Choices choices_; |
| |
| BlockingStep blocking_step_ = BlockingStep::kNone; |
| CallbackVariant blocking_callback_; |
| std::unique_ptr<base::RunLoop> run_loop_; |
| |
| base::WeakPtrFactory<Delegate> weak_ptr_factory_{this}; |
| }; |
| |
| } // namespace |
| |
| class TurnSyncOnHelperBrowserTestWithParam |
| : public SigninBrowserTestBase, |
| public testing::WithParamInterface< |
| std::tuple<TurnSyncOnHelper::SigninAbortedMode, bool>> { |
| public: |
| TurnSyncOnHelperBrowserTestWithParam() |
| : SigninBrowserTestBase(/*use_main_profile=*/false) {} |
| |
| protected: |
| bool should_remove_initial_account() const { |
| return std::get<bool>(GetParam()); |
| } |
| |
| TurnSyncOnHelper::SigninAbortedMode aborted_mode() const { |
| return std::get<TurnSyncOnHelper::SigninAbortedMode>(GetParam()); |
| } |
| }; |
| |
| // Tests that aborting a Sync opt-in flow started with a secondary account |
| // reverts the primary account to the initial one. |
| IN_PROC_BROWSER_TEST_P(TurnSyncOnHelperBrowserTestWithParam, |
| PrimaryAccountResetAfterSyncOptInFlowAborted) { |
| Profile* profile = GetProfile(); |
| CoreAccountInfo primary_account_info = signin::MakeAccountAvailable( |
| identity_manager(), identity_test_env() |
| ->CreateAccountAvailabilityOptionsBuilder() |
| .AsPrimary(signin::ConsentLevel::kSignin) |
| .WithCookie() |
| .Build("first@gmail.com")); |
| auto secondary_accounts_info = |
| SetAccountsCookiesAndTokens({"second@gmail.com", "third@gmail.com"}); |
| AccountInfo second_account_info = secondary_accounts_info[0]; |
| AccountInfo third_account_info = secondary_accounts_info[1]; |
| CoreAccountId first_account_id = primary_account_info.account_id; |
| CoreAccountId second_account_id = second_account_info.account_id; |
| |
| ASSERT_EQ(signin::ConsentLevel::kSignin, |
| signin::GetPrimaryAccountConsentLevel(identity_manager())); |
| ASSERT_EQ(first_account_id, identity_manager()->GetPrimaryAccountId( |
| signin::ConsentLevel::kSignin)); |
| |
| base::RunLoop run_loop; |
| Delegate::Choices choices = {.sync_optin_choice = std::nullopt}; |
| auto owned_delegate = std::make_unique<Delegate>(choices); |
| base::WeakPtr<Delegate> delegate = owned_delegate->GetWeakPtr(); |
| new TurnSyncOnHelper( |
| profile, signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN, |
| signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO, |
| second_account_id, aborted_mode(), std::move(owned_delegate), |
| run_loop.QuitClosure()); |
| |
| delegate->WaitUntilBlock(); |
| EXPECT_EQ(Delegate::BlockingStep::kSyncConfirmation, |
| delegate->blocking_step()); |
| EXPECT_EQ(signin::ConsentLevel::kSignin, |
| signin::GetPrimaryAccountConsentLevel(identity_manager())); |
| EXPECT_EQ(second_account_id, identity_manager()->GetPrimaryAccountId( |
| signin::ConsentLevel::kSignin)); |
| |
| if (should_remove_initial_account()) { |
| identity_manager()->GetAccountsMutator()->RemoveAccount( |
| first_account_id, |
| signin_metrics::SourceForRefreshTokenOperation::kUnknown); |
| } |
| |
| choices.sync_optin_choice = LoginUIService::ABORT_SYNC; |
| delegate->UpdateChoicesAndAdvanceFlow(choices); |
| |
| // The flow should complete and destroy the delegate and TurnSyncOnHelper. |
| run_loop.Run(); |
| EXPECT_FALSE(delegate); |
| |
| // Check expectations. |
| switch (aborted_mode()) { |
| case TurnSyncOnHelper::SigninAbortedMode::REMOVE_ACCOUNT: |
| if (should_remove_initial_account()) { |
| // All accounts are removed. |
| EXPECT_TRUE(identity_manager()->GetAccountsWithRefreshTokens().empty()); |
| EXPECT_FALSE(identity_manager()->HasPrimaryAccount( |
| signin::ConsentLevel::kSignin)); |
| } else { |
| // Second account removed, first account is still primary. |
| EXPECT_THAT( |
| identity_manager()->GetAccountsWithRefreshTokens(), |
| UnorderedElementsAre(primary_account_info, third_account_info)); |
| EXPECT_EQ(signin::ConsentLevel::kSignin, |
| signin::GetPrimaryAccountConsentLevel(identity_manager())); |
| EXPECT_EQ(first_account_id, identity_manager()->GetPrimaryAccountId( |
| signin::ConsentLevel::kSignin)); |
| } |
| break; |
| case TurnSyncOnHelper::SigninAbortedMode::KEEP_ACCOUNT: |
| if (should_remove_initial_account()) { |
| #if BUILDFLAG(IS_CHROMEOS_LACROS) |
| // First account was removed, second account became the primary. |
| EXPECT_THAT( |
| identity_manager()->GetAccountsWithRefreshTokens(), |
| UnorderedElementsAre(second_account_info, third_account_info)); |
| EXPECT_EQ(signin::ConsentLevel::kSignin, |
| signin::GetPrimaryAccountConsentLevel(identity_manager())); |
| EXPECT_EQ(second_account_id, identity_manager()->GetPrimaryAccountId( |
| signin::ConsentLevel::kSignin)); |
| #else |
| // With Dice, all accounts are removed, because the first account in |
| // cookies doesn't match. |
| EXPECT_TRUE(identity_manager()->GetAccountsWithRefreshTokens().empty()); |
| EXPECT_FALSE(identity_manager()->HasPrimaryAccount( |
| signin::ConsentLevel::kSignin)); |
| #endif |
| } else { |
| // First account is still primary, second account was not removed. |
| EXPECT_THAT( |
| identity_manager()->GetAccountsWithRefreshTokens(), |
| UnorderedElementsAre(primary_account_info, second_account_info, |
| third_account_info)); |
| EXPECT_EQ(signin::ConsentLevel::kSignin, |
| signin::GetPrimaryAccountConsentLevel(identity_manager())); |
| EXPECT_EQ(first_account_id, identity_manager()->GetPrimaryAccountId( |
| signin::ConsentLevel::kSignin)); |
| } |
| break; |
| case TurnSyncOnHelper::SigninAbortedMode::KEEP_ACCOUNT_ON_WEB_ONLY: |
| // This case is handled in the TurnSyncOnHelperBrowserTestWithUnoDesktop |
| // test suite, since this mode is used only when Uno Desktop is enabled. |
| NOTREACHED(); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| , |
| TurnSyncOnHelperBrowserTestWithParam, |
| testing::Combine( |
| testing::Values(TurnSyncOnHelper::SigninAbortedMode::REMOVE_ACCOUNT, |
| TurnSyncOnHelper::SigninAbortedMode::KEEP_ACCOUNT), |
| // Whether the initial account should be removed during the flow. |
| testing::Values(true, false))); |
| |
| class TurnSyncOnHelperBrowserTest : public SigninBrowserTestBase { |
| public: |
| TurnSyncOnHelperBrowserTest() |
| : SigninBrowserTestBase(/*use_main_profile=*/false) {} |
| }; |
| |
| // Regression test for https://crbug.com/1404961 |
| IN_PROC_BROWSER_TEST_F(TurnSyncOnHelperBrowserTest, UndoSyncRemoveAccount) { |
| Profile* profile = GetProfile(); |
| |
| CoreAccountInfo account_info = signin::MakeAccountAvailable( |
| identity_manager(), identity_test_env() |
| ->CreateAccountAvailabilityOptionsBuilder() |
| .AsPrimary(signin::ConsentLevel::kSignin) |
| .WithCookie() |
| .Build("account@gmail.com")); |
| CoreAccountId account_id = account_info.account_id; |
| |
| base::RunLoop run_loop; |
| Delegate::Choices choices = {.sync_optin_choice = std::nullopt}; |
| auto owned_delegate = std::make_unique<Delegate>(choices); |
| base::WeakPtr<Delegate> delegate = owned_delegate->GetWeakPtr(); |
| new TurnSyncOnHelper( |
| profile, signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN, |
| signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO, account_id, |
| TurnSyncOnHelper::SigninAbortedMode::REMOVE_ACCOUNT, |
| std::move(owned_delegate), run_loop.QuitClosure()); |
| |
| delegate->WaitUntilBlock(); |
| EXPECT_EQ(Delegate::BlockingStep::kSyncConfirmation, |
| delegate->blocking_step()); |
| EXPECT_EQ(signin::ConsentLevel::kSignin, |
| signin::GetPrimaryAccountConsentLevel(identity_manager())); |
| EXPECT_EQ(account_id, identity_manager()->GetPrimaryAccountId( |
| signin::ConsentLevel::kSignin)); |
| |
| AccountReconcilor* reconcilor = |
| AccountReconcilorFactory::GetForProfile(profile); |
| // For the scenario in https://crbug.com/1404961, the reconcilor has to be |
| // triggered by the account removal. |
| ASSERT_EQ(reconcilor->GetState(), |
| signin_metrics::AccountReconcilorState::kOk); |
| |
| choices.sync_optin_choice = LoginUIService::ABORT_SYNC; |
| delegate->UpdateChoicesAndAdvanceFlow(choices); |
| |
| // The flow should complete and destroy the delegate and TurnSyncOnHelper. |
| run_loop.Run(); |
| EXPECT_FALSE(delegate); |
| EXPECT_TRUE(identity_manager()->GetAccountsWithRefreshTokens().empty()); |
| EXPECT_FALSE( |
| identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSignin)); |
| |
| // For the scenario in https://crbug.com/1404961, the reconcilor has to be |
| // triggered by the account removal. |
| #if BUILDFLAG(IS_CHROMEOS_LACROS) |
| ASSERT_EQ(reconcilor->GetState(), |
| signin_metrics::AccountReconcilorState::kRunning); |
| #endif // BUILDFLAG(IS_CHROMEOS_LACROS) |
| // On Dice platforms with `switches::kUnoDesktop` enabled and empty primary |
| // account, updating cookies is disabled. Therefore running the reconcilor |
| // doesn't require any network requests and might have been completed by now. |
| // The reconcilor will not remove the account from cookies but revoking |
| // refresh tokens should be sufficient to invalidate cookies. |
| } |
| |
| #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) |
| class TurnSyncOnHelperBrowserTestWithUnoDesktop |
| : public TurnSyncOnHelperBrowserTest { |
| private: |
| base::test::ScopedFeatureList feature_list_{switches::kUnoDesktop}; |
| }; |
| |
| // Tests that aborting a Sync opt-in flow started with a web only signed in |
| // account reverts the account to the initial web only signed in state. |
| IN_PROC_BROWSER_TEST_F(TurnSyncOnHelperBrowserTestWithUnoDesktop, |
| WebOnlyAccountResetAfterSyncOptInFlowAborted) { |
| Profile* profile = GetProfile(); |
| // Set up first account. |
| AccountInfo first_account_info = |
| identity_test_env()->MakeAccountAvailable("first@gmail.com"); |
| identity_test_env()->UpdateAccountInfoForAccount(first_account_info); |
| CoreAccountId first_account_id = first_account_info.account_id; |
| |
| ASSERT_NE(signin::ConsentLevel::kSignin, |
| signin::GetPrimaryAccountConsentLevel(identity_manager())); |
| ASSERT_FALSE( |
| identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSignin)); |
| |
| base::RunLoop run_loop; |
| Delegate::Choices choices = {.sync_optin_choice = std::nullopt}; |
| auto owned_delegate = std::make_unique<Delegate>(choices); |
| base::WeakPtr<Delegate> delegate = owned_delegate->GetWeakPtr(); |
| new TurnSyncOnHelper( |
| profile, signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN, |
| signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO, |
| first_account_id, |
| TurnSyncOnHelper::SigninAbortedMode::KEEP_ACCOUNT_ON_WEB_ONLY, |
| std::move(owned_delegate), run_loop.QuitClosure()); |
| |
| delegate->WaitUntilBlock(); |
| EXPECT_EQ(Delegate::BlockingStep::kSyncConfirmation, |
| delegate->blocking_step()); |
| EXPECT_EQ(signin::ConsentLevel::kSignin, |
| signin::GetPrimaryAccountConsentLevel(identity_manager())); |
| EXPECT_EQ(first_account_id, identity_manager()->GetPrimaryAccountId( |
| signin::ConsentLevel::kSignin)); |
| |
| choices.sync_optin_choice = LoginUIService::ABORT_SYNC; |
| delegate->UpdateChoicesAndAdvanceFlow(choices); |
| |
| // The flow should complete and destroy the delegate and TurnSyncOnHelper. |
| run_loop.Run(); |
| EXPECT_FALSE(delegate); |
| |
| // Check expectations. |
| EXPECT_THAT(identity_manager()->GetAccountsWithRefreshTokens(), |
| UnorderedElementsAre(first_account_info)); |
| EXPECT_FALSE( |
| identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSignin)); |
| } |
| |
| // Tests that aborting a Sync opt-in flow started with a secondary account |
| // reverts the primary account to the initial one. |
| IN_PROC_BROWSER_TEST_F( |
| TurnSyncOnHelperBrowserTestWithUnoDesktop, |
| PrimaryAccountResetAfterSyncOptInFlowAbortedForSecondaryAccount) { |
| Profile* profile = GetProfile(); |
| // Set up the primary account. |
| CoreAccountInfo primary_account_info = signin::MakeAccountAvailable( |
| identity_manager(), identity_test_env() |
| ->CreateAccountAvailabilityOptionsBuilder() |
| .AsPrimary(signin::ConsentLevel::kSignin) |
| .WithCookie() |
| .Build("first@gmail.com")); |
| auto secondary_accounts_info = |
| SetAccountsCookiesAndTokens({"second@gmail.com", "third@gmail.com"}); |
| AccountInfo second_account_info = secondary_accounts_info[0]; |
| AccountInfo third_account_info = secondary_accounts_info[1]; |
| CoreAccountId first_account_id = primary_account_info.account_id; |
| CoreAccountId second_account_id = second_account_info.account_id; |
| |
| ASSERT_EQ(signin::ConsentLevel::kSignin, |
| signin::GetPrimaryAccountConsentLevel(identity_manager())); |
| ASSERT_EQ(first_account_id, identity_manager()->GetPrimaryAccountId( |
| signin::ConsentLevel::kSignin)); |
| |
| base::RunLoop run_loop; |
| Delegate::Choices choices = {.sync_optin_choice = std::nullopt}; |
| auto owned_delegate = std::make_unique<Delegate>(choices); |
| base::WeakPtr<Delegate> delegate = owned_delegate->GetWeakPtr(); |
| new TurnSyncOnHelper( |
| profile, signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN, |
| signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO, |
| second_account_id, |
| TurnSyncOnHelper::SigninAbortedMode::KEEP_ACCOUNT_ON_WEB_ONLY, |
| std::move(owned_delegate), run_loop.QuitClosure()); |
| |
| delegate->WaitUntilBlock(); |
| EXPECT_EQ(Delegate::BlockingStep::kSyncConfirmation, |
| delegate->blocking_step()); |
| EXPECT_EQ(signin::ConsentLevel::kSignin, |
| signin::GetPrimaryAccountConsentLevel(identity_manager())); |
| EXPECT_EQ(second_account_id, identity_manager()->GetPrimaryAccountId( |
| signin::ConsentLevel::kSignin)); |
| |
| choices.sync_optin_choice = LoginUIService::ABORT_SYNC; |
| delegate->UpdateChoicesAndAdvanceFlow(choices); |
| |
| // The flow should complete and destroy the delegate and TurnSyncOnHelper. |
| run_loop.Run(); |
| EXPECT_FALSE(delegate); |
| |
| // First account is still primary, second account was not removed. |
| EXPECT_THAT(identity_manager()->GetAccountsWithRefreshTokens(), |
| UnorderedElementsAre(primary_account_info, second_account_info, |
| third_account_info)); |
| EXPECT_EQ(signin::ConsentLevel::kSignin, |
| signin::GetPrimaryAccountConsentLevel(identity_manager())); |
| EXPECT_EQ(first_account_id, identity_manager()->GetPrimaryAccountId( |
| signin::ConsentLevel::kSignin)); |
| } |
| |
| // Tests that aborting a Sync opt-in flow started with a new secondary account |
| // reverts the primary account to the initial one and removes the new account. |
| IN_PROC_BROWSER_TEST_F( |
| TurnSyncOnHelperBrowserTestWithUnoDesktop, |
| PrimaryAccountResetAfterSyncOptInFlowAbortedForNewAccount) { |
| Profile* profile = GetProfile(); |
| |
| // Set up the primary account. |
| CoreAccountInfo primary_account_info = signin::MakeAccountAvailable( |
| identity_manager(), identity_test_env() |
| ->CreateAccountAvailabilityOptionsBuilder() |
| .AsPrimary(signin::ConsentLevel::kSignin) |
| .WithCookie() |
| .Build("first@gmail.com")); |
| CoreAccountId first_account_id = primary_account_info.account_id; |
| auto secondary_accounts_info = |
| SetAccountsCookiesAndTokens({"second@gmail.com"}); |
| AccountInfo second_account_info = secondary_accounts_info[0]; |
| CoreAccountId second_account_id = second_account_info.account_id; |
| |
| ASSERT_EQ(signin::ConsentLevel::kSignin, |
| signin::GetPrimaryAccountConsentLevel(identity_manager())); |
| ASSERT_EQ(first_account_id, identity_manager()->GetPrimaryAccountId( |
| signin::ConsentLevel::kSignin)); |
| |
| base::RunLoop run_loop; |
| Delegate::Choices choices = {.sync_optin_choice = std::nullopt}; |
| auto owned_delegate = std::make_unique<Delegate>(choices); |
| base::WeakPtr<Delegate> delegate = owned_delegate->GetWeakPtr(); |
| new TurnSyncOnHelper( |
| profile, signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN, |
| signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO, |
| second_account_id, TurnSyncOnHelper::SigninAbortedMode::REMOVE_ACCOUNT, |
| std::move(owned_delegate), run_loop.QuitClosure()); |
| |
| delegate->WaitUntilBlock(); |
| EXPECT_EQ(Delegate::BlockingStep::kSyncConfirmation, |
| delegate->blocking_step()); |
| EXPECT_EQ(signin::ConsentLevel::kSignin, |
| signin::GetPrimaryAccountConsentLevel(identity_manager())); |
| EXPECT_EQ(second_account_id, identity_manager()->GetPrimaryAccountId( |
| signin::ConsentLevel::kSignin)); |
| |
| choices.sync_optin_choice = LoginUIService::ABORT_SYNC; |
| delegate->UpdateChoicesAndAdvanceFlow(choices); |
| |
| // The flow should complete and destroy the delegate and TurnSyncOnHelper. |
| run_loop.Run(); |
| EXPECT_FALSE(delegate); |
| |
| // First account is still primary, second account was removed. |
| EXPECT_THAT(identity_manager()->GetAccountsWithRefreshTokens(), |
| UnorderedElementsAre(primary_account_info)); |
| EXPECT_EQ(signin::ConsentLevel::kSignin, |
| signin::GetPrimaryAccountConsentLevel(identity_manager())); |
| EXPECT_EQ(first_account_id, identity_manager()->GetPrimaryAccountId( |
| signin::ConsentLevel::kSignin)); |
| } |
| |
| #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) |