// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/webui/settings/people_handler.h"

#include <memory>
#include <string>
#include <vector>

#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/i18n/time_formatting.h"
#include "base/json/json_writer.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/defaults.h"
#include "chrome/browser/first_run/first_run.h"
#include "chrome/browser/signin/account_consistency_mode_manager.h"
#include "chrome/browser/signin/chrome_signin_client_factory.h"
#include "chrome/browser/signin/chrome_signin_client_test_util.h"
#include "chrome/browser/signin/dice_web_signin_interceptor.h"
#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
#include "chrome/browser/signin/signin_error_controller_factory.h"
#include "chrome/browser/signin/signin_ui_delegate.h"
#include "chrome/browser/signin/signin_ui_util.h"
#include "chrome/browser/signin/signin_util.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
#include "chrome/browser/ui/browser_window/public/global_browser_collection.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/signin/signin_view_controller.h"
#include "chrome/browser/ui/webui/signin/login_ui_service.h"
#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/test_chrome_web_ui_controller_factory.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "components/prefs/pref_service.h"
#include "components/signin/public/base/account_consistency_method.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/base/signin_pref_names.h"
#include "components/signin/public/base/signin_prefs.h"
#include "components/signin/public/base/signin_switches.h"
#include "components/signin/public/identity_manager/account_capabilities_test_mutator.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_environment.h"
#include "components/signin/public/identity_manager/identity_test_utils.h"
#include "components/signin/public/identity_manager/primary_account_mutator.h"
#include "components/sync/base/features.h"
#include "components/sync/base/passphrase_enums.h"
#include "components/sync/base/user_selectable_type.h"
#include "components/sync/service/sync_user_settings_impl.h"
#include "components/sync/test/test_sync_service.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_controller.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/scoped_web_ui_controller_factory_registration.h"
#include "content/public/test/test_web_contents_factory.h"
#include "content/public/test/test_web_ui.h"
#include "content/public/test/web_contents_tester.h"
#include "google_apis/gaia/gaia_urls.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"

#if BUILDFLAG(IS_CHROMEOS)
#include "ash/constants/ash_features.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chromeos/constants/pref_names.h"
#endif  // BUILDFLAG(IS_CHROMEOS)

namespace {

using signin::ConsentLevel;
using signin_util::SignedInState;
using ::testing::_;
using ::testing::ByMove;
using ::testing::Const;
using ::testing::IsEmpty;
using ::testing::Mock;
using ::testing::Return;
using ::testing::Values;

constexpr char kTestUser[] = "chrome_p13n_test@gmail.com";
constexpr char kTestCallbackId[] = "test-callback-id";

#if BUILDFLAG(ENABLE_DICE_SUPPORT)
// Event fired when calling
// `PeopleHandler::UpdateChromeSigninUserChoiceInfo()`.
constexpr char kChromeSigninUserChoiceInfoChangeEventName[] =
    "chrome-signin-user-choice-info-change";

// Event fired when calling `PeopleHandler::UpdateSyncStatus()`.
constexpr char kSyncStatusChangeEventName[] = "sync-status-changed";

class MockSigninUiDelegate : public signin_ui_util::SigninUiDelegate {
 public:
  MOCK_METHOD(void,
              ShowTurnSyncOnUI,
              (Profile*,
               signin_metrics::AccessPoint,
               signin_metrics::PromoAction,
               const CoreAccountId&,
               TurnSyncOnHelper::SigninAbortedMode,
               bool,
               bool),
              (override));

  MOCK_METHOD(void,
              ShowSigninUI,
              (Profile*,
               bool,
               signin_metrics::AccessPoint,
               signin_metrics::PromoAction,
               const std::string&),
              (override));
  MOCK_METHOD(void,
              ShowReauthUI,
              (Profile*,
               const std::string&,
               bool,
               signin_metrics::AccessPoint,
               signin_metrics::PromoAction),
              (override));
  MOCK_METHOD(void,
              ShowCrossDeviceSigninQrBubble,
              (BrowserWindowInterface*, base::OnceClosure),
              (override));
};
#endif

// Returns a UserSelectableTypeSet with all types set.
syncer::UserSelectableTypeSet GetAllTypes() {
  return syncer::UserSelectableTypeSet::All();
}

enum SyncAllDataConfig { SYNC_ALL_DATA, CHOOSE_WHAT_TO_SYNC };

// Create a json-format string with the key/value pairs appropriate for a call
// to HandleSetDatatypes().
std::string GetConfiguration(SyncAllDataConfig sync_all,
                             syncer::UserSelectableTypeSet types) {
  base::DictValue result;
  result.Set("syncAllDataTypes", sync_all == SYNC_ALL_DATA);
  // Add all of our data types.
  result.Set("appsSynced", types.Has(syncer::UserSelectableType::kApps));
  result.Set("autofillSynced",
             types.Has(syncer::UserSelectableType::kAutofill));
  result.Set("bookmarksSynced",
             types.Has(syncer::UserSelectableType::kBookmarks));
  result.Set("cookiesSynced", types.Has(syncer::UserSelectableType::kCookies));
  result.Set("extensionsSynced",
             types.Has(syncer::UserSelectableType::kExtensions));
  result.Set("passwordsSynced",
             types.Has(syncer::UserSelectableType::kPasswords));
  result.Set("paymentsSynced",
             types.Has(syncer::UserSelectableType::kPayments));
  result.Set("preferencesSynced",
             types.Has(syncer::UserSelectableType::kPreferences));
  result.Set("productComparisonSynced",
             types.Has(syncer::UserSelectableType::kProductComparison));
  result.Set("readingListSynced",
             types.Has(syncer::UserSelectableType::kReadingList));
  result.Set("savedTabGroupsSynced",
             types.Has(syncer::UserSelectableType::kSavedTabGroups));
  result.Set("tabsSynced", types.Has(syncer::UserSelectableType::kTabs));
  result.Set("themesSynced", types.Has(syncer::UserSelectableType::kThemes));
  result.Set("typedUrlsSynced",
             types.Has(syncer::UserSelectableType::kHistory));

  return base::WriteJson(result).value_or("");
}

// Checks whether the passed |dictionary| contains a |key| with the given
// |expected_value|. This will fail if the key isn't present, even if
// |expected_value| is false.
void ExpectHasBoolKey(const base::DictValue& dictionary,
                      const std::string& key,
                      bool expected_value) {
  ASSERT_TRUE(dictionary.contains(key)) << "No value found for " << key;
  ASSERT_TRUE(dictionary.FindBool(key).has_value()) << key << " has wrong type";
  EXPECT_EQ(expected_value, *dictionary.FindBool(key))
      << "Mismatch found for " << key;
}

// Checks to make sure that the values stored in |dictionary| match the values
// expected by the showSyncSetupPage() JS function for a given set of data
// types.
void CheckConfigDataTypeArguments(const base::DictValue& dictionary,
                                  SyncAllDataConfig config,
                                  syncer::UserSelectableTypeSet types) {
  ExpectHasBoolKey(dictionary, "syncAllDataTypes", config == SYNC_ALL_DATA);
  ExpectHasBoolKey(dictionary, "appsSynced",
                   types.Has(syncer::UserSelectableType::kApps));
  ExpectHasBoolKey(dictionary, "autofillSynced",
                   types.Has(syncer::UserSelectableType::kAutofill));
  ExpectHasBoolKey(dictionary, "bookmarksSynced",
                   types.Has(syncer::UserSelectableType::kBookmarks));
  ExpectHasBoolKey(dictionary, "cookiesSynced",
                   types.Has(syncer::UserSelectableType::kCookies));
  ExpectHasBoolKey(dictionary, "extensionsSynced",
                   types.Has(syncer::UserSelectableType::kExtensions));
  ExpectHasBoolKey(dictionary, "passwordsSynced",
                   types.Has(syncer::UserSelectableType::kPasswords));
  ExpectHasBoolKey(dictionary, "preferencesSynced",
                   types.Has(syncer::UserSelectableType::kPreferences));
  ExpectHasBoolKey(dictionary, "readingListSynced",
                   types.Has(syncer::UserSelectableType::kReadingList));
  ExpectHasBoolKey(dictionary, "savedTabGroupsSynced",
                   types.Has(syncer::UserSelectableType::kSavedTabGroups));
  ExpectHasBoolKey(dictionary, "tabsSynced",
                   types.Has(syncer::UserSelectableType::kTabs));
  ExpectHasBoolKey(dictionary, "themesSynced",
                   types.Has(syncer::UserSelectableType::kThemes));
  ExpectHasBoolKey(dictionary, "typedUrlsSynced",
                   types.Has(syncer::UserSelectableType::kHistory));
}

std::unique_ptr<KeyedService> BuildTestSyncService(
    content::BrowserContext* context) {
  return std::make_unique<syncer::TestSyncService>();
}

}  // namespace

namespace settings {

class TestingPeopleHandler : public PeopleHandler {
 public:
  TestingPeopleHandler(content::WebUI* web_ui, Profile* profile)
      : PeopleHandler(profile) {
    set_web_ui(web_ui);
  }

  TestingPeopleHandler(const TestingPeopleHandler&) = delete;
  TestingPeopleHandler& operator=(const TestingPeopleHandler&) = delete;

  using PeopleHandler::is_configuring_sync;
};

class PeopleHandlerTest
#if BUILDFLAG(IS_CHROMEOS)
    // ChromeRenderViewHostTestHarness is flaky, but is required on ChromeOS
    // because `TestWebContentsFactory` does not work out of the box.
    : public ChromeRenderViewHostTestHarness {
#else
    : public testing::Test {
#endif
 public:
  PeopleHandlerTest() = default;

  PeopleHandlerTest(const PeopleHandlerTest&) = delete;
  PeopleHandlerTest& operator=(const PeopleHandlerTest&) = delete;

  ~PeopleHandlerTest() override = default;

  void SetUp() override {
#if BUILDFLAG(IS_CHROMEOS)
    ChromeRenderViewHostTestHarness::SetUp();
#else
    profile_ = IdentityTestEnvironmentProfileAdaptor::
        CreateProfileForIdentityTestEnvironment();
#endif
    identity_test_env_adaptor_ =
        std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile());

    sync_service_ = static_cast<syncer::TestSyncService*>(
        SyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
            profile(), base::BindRepeating(&BuildTestSyncService)));

    sync_service_->SetSignedOut();
  }

  void TearDown() override {
    sync_service_ = nullptr;
    DestroyPeopleHandler();
    identity_test_env_adaptor_.reset();
#if BUILDFLAG(IS_CHROMEOS)
    ChromeRenderViewHostTestHarness::TearDown();
#endif
  }

#if BUILDFLAG(IS_CHROMEOS)
  TestingProfile::TestingFactories GetTestingFactories() const override {
    return IdentityTestEnvironmentProfileAdaptor::
        GetIdentityTestEnvironmentFactories();
  }
#endif

  void SigninUserWithoutSyncFeature() {
    const CoreAccountInfo account_info = identity_test_env()->SetPrimaryAccount(
        kTestUser, signin::ConsentLevel::kSignin);
    sync_service_->SetSignedIn(signin::ConsentLevel::kSignin, account_info);
  }

  void SigninUserAndTurnSyncFeatureOn() {
    const CoreAccountInfo account_info = identity_test_env()->SetPrimaryAccount(
        kTestUser, signin::ConsentLevel::kSync);
    sync_service_->SetSignedIn(signin::ConsentLevel::kSync, account_info);
  }

  void CreatePeopleHandler() {
    handler_ = std::make_unique<TestingPeopleHandler>(&web_ui_, profile());
    handler_->AllowJavascript();
#if !BUILDFLAG(IS_CHROMEOS)
    web_contents_ = web_contents_factory_.CreateWebContents(profile());
#endif
    web_ui_.set_web_contents(web_contents());
    handler_->RegisterMessages();
  }

  void DestroyPeopleHandler() {
    if (handler_) {
      handler_->set_web_ui(nullptr);
      handler_->DisallowJavascript();
      handler_ = nullptr;
#if !BUILDFLAG(IS_CHROMEOS)
      web_contents_ = nullptr;
#endif
    }
  }

  void ExpectPageStatusResponse(const std::string& expected_status) {
    auto& data = *web_ui_.call_data().back();
    EXPECT_EQ("cr.webUIResponse", data.function_name());

    ASSERT_TRUE(data.arg1()->is_string());
    EXPECT_EQ(kTestCallbackId, data.arg1()->GetString());
    ASSERT_TRUE(data.arg2()->is_bool());
    EXPECT_TRUE(data.arg2()->GetBool());
    ASSERT_TRUE(data.arg3()->is_string());
    EXPECT_EQ(expected_status, data.arg3()->GetString());
  }

  // Expects a call to ResolveJavascriptCallback() with |should_succeed| as its
  // argument.
  void ExpectSetPassphraseSuccess(bool should_succeed) {
    EXPECT_EQ(1u, web_ui_.call_data().size());
    const auto& data = *web_ui_.call_data()[0];
    EXPECT_EQ("cr.webUIResponse", data.function_name());
    EXPECT_TRUE(data.arg2()->is_bool());
    EXPECT_TRUE(data.arg2()->GetBool())
        << "Callback should be resolved with a boolean indicating the success, "
           "never rejected.";

    EXPECT_TRUE(data.arg3()->is_bool());
    EXPECT_EQ(should_succeed, data.arg3()->GetBool());
  }

  std::vector<const base::Value*> GetAllFiredValuesForEventName(
      const std::string& event_name) {
    std::vector<const base::Value*> arguments;
    for (const std::unique_ptr<content::TestWebUI::CallData>& data :
         web_ui_.call_data()) {
      if (data->function_name() == "cr.webUIListenerCallback" &&
          data->arg1()->is_string() &&
          data->arg1()->GetString() == event_name) {
        arguments.push_back(data->arg2());
      }
    }
    return arguments;
  }

  // Returns all fired sync-prefs-changed events, without any validation.
  std::vector<const base::Value*> GetFiredSyncPrefsChanged() {
    return GetAllFiredValuesForEventName("sync-prefs-changed");
  }

  // Must be called at most once per test to check if a sync-prefs-changed
  // event happened. Returns the single fired value.
  base::DictValue ExpectSyncPrefsChanged() {
    std::vector<const base::Value*> args = GetFiredSyncPrefsChanged();
    EXPECT_EQ(1U, args.size());
    EXPECT_NE(args[0], nullptr);
    EXPECT_TRUE(args[0]->is_dict());
    return args[0]->GetDict().Clone();
  }

  // Must be called at most once per test to check if a sync-status-changed
  // event happened. Returns the single fired value.
  base::DictValue ExpectSyncStatusChanged() {
    std::vector<const base::Value*> args =
        GetAllFiredValuesForEventName("sync-status-changed");
    EXPECT_EQ(1U, args.size());
    EXPECT_NE(args[0], nullptr);
    EXPECT_TRUE(args[0]->is_dict());
    return args[0]->GetDict().Clone();
  }

  signin::IdentityTestEnvironment* identity_test_env() {
    return identity_test_env_adaptor_->identity_test_env();
  }

  signin::IdentityManager* identity_manager() {
    return identity_test_env()->identity_manager();
  }

  syncer::TestSyncUserSettings* sync_user_settings() {
    return sync_service_->GetUserSettings();
  }

#if BUILDFLAG(ENABLE_DICE_SUPPORT)
  // Checks values returned as a response of WebUI called.
  void ExpectChromeSigninUserChoiceInfoFromWebUiResponse(
      bool should_show_settings,
      ChromeSigninUserChoice expected_choice,
      const std::string& expected_signed_in_email) {
    auto& data = *web_ui_.call_data().back();
    EXPECT_EQ("cr.webUIResponse", data.function_name());
    ASSERT_TRUE(data.arg1()->is_string());
    EXPECT_EQ(kTestCallbackId, data.arg1()->GetString());
    ASSERT_TRUE(data.arg2()->is_bool());
    EXPECT_TRUE(data.arg2()->GetBool())
        << "Callback should be resolved with a boolean indicating the success, "
           "never rejected.";
    ASSERT_TRUE(data.arg3()->is_dict());

    const base::DictValue& dict = data.arg3()->GetDict();
    ExpectChromeSigninUserChoiceInfoDict(
        dict, should_show_settings, expected_choice, expected_signed_in_email);
  }

  // Checks values returned from firing the change event.
  // Reads the last event. Expecting at least one event.
  void ExpectChromeSigninUserChoiceInfoFromLastChangeEvent(
      bool should_show_settings,
      ChromeSigninUserChoice expected_choice,
      const std::string& expected_signed_in_email) {
    auto values_list = GetAllFiredValuesForEventName(
        kChromeSigninUserChoiceInfoChangeEventName);
    ASSERT_GT(values_list.size(), 0U);
    size_t last_index = values_list.size() - 1;
    ASSERT_TRUE(values_list[last_index]);
    ASSERT_TRUE(values_list[last_index]->is_dict());

    const base::DictValue& values_dict = values_list[last_index]->GetDict();
    ExpectChromeSigninUserChoiceInfoDict(values_dict, should_show_settings,
                                         expected_choice,
                                         expected_signed_in_email);
  }

  // Tests the Dict content returned for the WebUI for
  // ChromeSigninUserChoiceInfo.
  static void ExpectChromeSigninUserChoiceInfoDict(
      const base::DictValue& values_dict,
      bool expected_should_show_settings,
      ChromeSigninUserChoice expected_choice,
      const std::string& expected_signed_in_email) {
    std::optional<bool> should_show_settings =
        values_dict.FindBool("shouldShowSettings");
    ASSERT_TRUE(should_show_settings.has_value());
    EXPECT_EQ(should_show_settings.value(), expected_should_show_settings);

    std::optional<int> choice_int = values_dict.FindInt("choice");
    ASSERT_TRUE(choice_int.has_value());
    EXPECT_EQ(static_cast<ChromeSigninUserChoice>(choice_int.value()),
              expected_choice);

    const std::string* signed_in_email =
        values_dict.FindString("signedInEmail");
    ASSERT_TRUE(signed_in_email);
    EXPECT_EQ(*signed_in_email, expected_signed_in_email);
  }

  bool HasChromeSigninUserChoiceInfoChangeEvent() {
    return !GetAllFiredValuesForEventName(
                kChromeSigninUserChoiceInfoChangeEventName)
                .empty();
  }

  void SetExplicitSignin(bool value) {
    profile()->GetPrefs()->SetBoolean(prefs::kExplicitBrowserSignin, value);
  }

  void TriggerPrimaryAccountInPersistentError() {
    ASSERT_TRUE(identity_manager()->HasPrimaryAccount(ConsentLevel::kSignin));

    // Inject the error.
    identity_test_env()->SetInvalidRefreshTokenForPrimaryAccount();
  }

  bool HasSyncStatusUpdateChangedEvent() {
    return !GetAllFiredValuesForEventName(kSyncStatusChangeEventName).empty();
  }

  void SimluateReauth() {
    ASSERT_TRUE(
        identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSignin));

    // Clear the error.
    identity_test_env()->SetRefreshTokenForPrimaryAccount();
  }

  void SimulateSignout() {
    ASSERT_TRUE(
        identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSignin));

    identity_test_env()->ClearPrimaryAccount();
  }

  void SimulateHandleGetChromeSigninUserChoiceInfo() const {
    base::ListValue args_get;
    args_get.Append(kTestCallbackId);
    handler_->HandleGetChromeSigninUserChoiceInfo(args_get);
  }

  void SimulateHandleSetChromeSigninUserChoiceInfo(
      std::string_view email,
      ChromeSigninUserChoice user_choice) {
    base::ListValue args_set;
    args_set.Append(static_cast<int>(user_choice));
    args_set.Append(email);
    handler_->HandleSetChromeSigninUserChoice(args_set);
  }

  void SimulateHandleSetDatatype(syncer::UserSelectableType type,
                                 bool value) const {
    base::ListValue args_set;
    args_set.Append(kTestCallbackId);
    args_set.Append(static_cast<int>(type));
    args_set.Append(value);
    handler_->HandleSetDatatype(args_set);
  }
#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)

#if !BUILDFLAG(IS_CHROMEOS)
  Profile* profile() { return profile_.get(); }
  content::WebContents* web_contents() { return web_contents_; }

  content::BrowserTaskEnvironment task_environment_;
#endif
  std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
      identity_test_env_adaptor_;
#if !BUILDFLAG(IS_CHROMEOS)
  std::unique_ptr<TestingProfile> profile_;
  content::TestWebContentsFactory web_contents_factory_;
  raw_ptr<content::WebContents> web_contents_ = nullptr;
#endif
  raw_ptr<syncer::TestSyncService> sync_service_;
  content::TestWebUI web_ui_;
  std::unique_ptr<TestingPeopleHandler> handler_;
  base::test::ScopedFeatureList feature_list_;
};

#if !BUILDFLAG(IS_CHROMEOS)
TEST_F(PeopleHandlerTest, DisplayBasicLogin) {
  testing::StrictMock<MockSigninUiDelegate> mock_signin_ui_delegate;
  base::AutoReset<signin_ui_util::SigninUiDelegate*> delegate_auto_reset =
      signin_ui_util::SetSigninUiDelegateForTesting(&mock_signin_ui_delegate);

  ASSERT_FALSE(identity_test_env()->identity_manager()->HasPrimaryAccount(
      ConsentLevel::kSignin));
  CreatePeopleHandler();
  // Test that the HandleStartSignin call enables JavaScript.
  handler_->DisallowJavascript();

  EXPECT_CALL(mock_signin_ui_delegate,
              ShowSigninUI(profile(), /*enable_sync=*/true,
                           signin_metrics::AccessPoint::kSettings,
                           signin_metrics::PromoAction::
                               PROMO_ACTION_NEW_ACCOUNT_NO_EXISTING_ACCOUNT,
                           /*extension_name=*/""));
  base::ListValue args;
  args.Append(0);
  handler_->HandleStartSignin(args);

  // The sign-in flow setup hands off control to the gaia login tab.
  EXPECT_EQ(
      nullptr,
      LoginUIServiceFactory::GetForProfile(profile())->current_login_ui());

  ASSERT_FALSE(handler_->is_configuring_sync());

  handler_->CloseSyncSetup();
  EXPECT_EQ(
      nullptr,
      LoginUIServiceFactory::GetForProfile(profile())->current_login_ui());
}

#endif  // !BUILDFLAG(IS_CHROMEOS)

TEST_F(PeopleHandlerTest, DisplayConfigureWithEngineDisabledAndCancel) {
  SigninUserAndTurnSyncFeatureOn();
  sync_user_settings()->ClearInitialSyncFeatureSetupComplete();

  CreatePeopleHandler();

  ASSERT_THAT(sync_service_->GetDisableReasons(), IsEmpty());

  sync_service_->SetMaxTransportState(
      syncer::SyncService::TransportState::INITIALIZING);

  // We're simulating a user setting up sync, which would cause the engine to
  // kick off initialization, but not download user data types. The sync
  // engine will try to download control data types (e.g encryption info), but
  // that won't finish for this test as we're simulating cancelling while the
  // spinner is showing.
  handler_->HandleShowSyncSetupUI(base::ListValue());

  EXPECT_EQ(
      handler_.get(),
      LoginUIServiceFactory::GetForProfile(profile())->current_login_ui());

  EXPECT_EQ(0U, web_ui_.call_data().size());

  handler_->CloseSyncSetup();
  EXPECT_EQ(
      nullptr,
      LoginUIServiceFactory::GetForProfile(profile())->current_login_ui());
}

// Verifies that the handler only sends the sync pref updates once the engine is
// initialized.
TEST_F(PeopleHandlerTest,
       DisplayConfigureWithEngineDisabledAndSyncStartupCompleted) {
  SigninUserAndTurnSyncFeatureOn();
  sync_user_settings()->ClearInitialSyncFeatureSetupComplete();

  CreatePeopleHandler();

  ASSERT_THAT(sync_service_->GetDisableReasons(), IsEmpty());

  // Sync engine is stopped initially, and will start up.
  sync_service_->SetMaxTransportState(
      syncer::SyncService::TransportState::START_DEFERRED);

  handler_->HandleShowSyncSetupUI(base::ListValue());

  // Mimic engine initialization.
  sync_service_->SetMaxTransportState(
      syncer::SyncService::TransportState::INITIALIZING);
  sync_service_->FireStateChanged();

  // No pref updates sent yet, because the engine is not initialized.
  EXPECT_EQ(0U, GetFiredSyncPrefsChanged().size());
  web_ui_.ClearTrackedCalls();

  // Now, act as if the SyncService has started up.
  sync_service_->SetMaxTransportState(
      syncer::SyncService::TransportState::ACTIVE);
  sync_service_->FireStateChanged();

  // Updates for the sync status, sync prefs, trusted vault opt-in and stored
  // accounts are sent.
  EXPECT_EQ(4U, web_ui_.call_data().size());

  base::DictValue dictionary = ExpectSyncPrefsChanged();
  ExpectHasBoolKey(dictionary, "syncAllDataTypes", true);
  ExpectHasBoolKey(dictionary, "customPassphraseAllowed", true);
  ExpectHasBoolKey(dictionary, "encryptAllData", false);
  ExpectHasBoolKey(dictionary, "passphraseRequired", false);
  ExpectHasBoolKey(dictionary, "trustedVaultKeysRequired", false);
}

// Verifies the case where the user cancels after the sync engine has
// initialized. This isn't reachable on Ash because
// IsInitialSyncFeatureSetupComplete() always returns true.
#if !BUILDFLAG(IS_CHROMEOS)
TEST_F(PeopleHandlerTest,
       DisplayConfigureWithEngineDisabledAndCancelAfterSigninSuccess) {
  SigninUserAndTurnSyncFeatureOn();
  sync_user_settings()->ClearInitialSyncFeatureSetupComplete();

  CreatePeopleHandler();

  handler_->HandleShowSyncSetupUI(base::ListValue());

  EXPECT_TRUE(sync_service_->IsSetupInProgress());

  // Sync engine becomes active, so |handler_| is notified.
  ASSERT_EQ(sync_service_->GetTransportState(),
            syncer::SyncService::TransportState::ACTIVE);

  handler_->CloseSyncSetup();
  EXPECT_EQ(
      nullptr,
      LoginUIServiceFactory::GetForProfile(profile())->current_login_ui());

  EXPECT_FALSE(sync_service_->IsSetupInProgress());
}
#endif  // !BUILDFLAG(IS_CHROMEOS)

TEST_F(PeopleHandlerTest, RestartSyncAfterDashboardClear) {
  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();

  // Mimic a dashboard clear, which should reset the sync engine and restart it
  // in transport mode. Defer the second initialization of the engine, to test
  // that prefs are not sent yet.
  sync_service_->SetMaxTransportState(
      syncer::SyncService::TransportState::INITIALIZING);
  sync_service_->MimicDashboardClear();

  ASSERT_EQ(sync_service_->GetTransportState(),
            syncer::SyncService::TransportState::INITIALIZING);
#if BUILDFLAG(IS_CHROMEOS)
  ASSERT_TRUE(sync_user_settings()->IsSyncFeatureDisabledViaDashboard());
#else   // BUILDFLAG(IS_CHROMEOS)
  ASSERT_FALSE(sync_user_settings()->IsInitialSyncFeatureSetupComplete());
#endif  // BUILDFLAG(IS_CHROMEOS)

  handler_->HandleShowSyncSetupUI(base::ListValue());

#if BUILDFLAG(IS_CHROMEOS)
  EXPECT_FALSE(sync_user_settings()->IsSyncFeatureDisabledViaDashboard());
#endif  // BUILDFLAG(IS_CHROMEOS)

  // Since the engine is not initialized yet, no prefs should be sent.
  EXPECT_EQ(0U, GetFiredSyncPrefsChanged().size());

  // Now, allow the sync engine to fully start.
  sync_service_->SetMaxTransportState(
      syncer::SyncService::TransportState::ACTIVE);
  sync_service_->FireStateChanged();

  // Upon initialization of the engine, the new prefs should be sent.
  ExpectSyncPrefsChanged();
}

// Tests that signals not related to user intention to configure sync don't
// trigger sync engine start.
TEST_F(PeopleHandlerTest, OnlyStartEngineWhenConfiguringSync) {
  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();
  sync_service_->SetMaxTransportState(
      syncer::SyncService::TransportState::START_DEFERRED);
  sync_service_->FireStateChanged();
  EXPECT_EQ(sync_service_->GetTransportState(),
            syncer::SyncService::TransportState::START_DEFERRED);
}

TEST_F(PeopleHandlerTest, AcquireSyncBlockerWhenLoadingSyncSettingsSubpage) {
  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();
  // Remove the WebUIConfig for chrome::kSyncSetupSubPage to prevent a new web
  // ui from being created when we navigate to a page that would normally create
  // one.
  content::ScopedWebUIConfigRegistration registration(
      chrome::GetSettingsUrl(chrome::kSyncSetupSubPage));

  EXPECT_FALSE(handler_->sync_blocker_);

  auto navigation = content::NavigationSimulator::CreateBrowserInitiated(
      chrome::GetSettingsUrl(chrome::kSyncSetupSubPage), web_contents());
  navigation->Start();
  handler_->InitializeSyncBlocker();

  EXPECT_TRUE(handler_->sync_blocker_);
}

TEST_F(PeopleHandlerTest, UnrecoverableErrorInitializingSync) {
  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();
  sync_service_->SetHasUnrecoverableError(true);
  sync_user_settings()->ClearInitialSyncFeatureSetupComplete();

  // Open the web UI.
  handler_->HandleShowSyncSetupUI(base::ListValue());

  ASSERT_FALSE(handler_->is_configuring_sync());
}

TEST_F(PeopleHandlerTest, GaiaErrorInitializingSync) {
  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();
  sync_service_->SetSignedOut();

  // Open the web UI.
  handler_->HandleShowSyncSetupUI(base::ListValue());

  ASSERT_FALSE(handler_->is_configuring_sync());
}

TEST_F(PeopleHandlerTest, TestSyncEverything) {
  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();

  sync_user_settings()->SetSelectedTypes(/*sync_everything=*/false,
                                         /*types=*/GetAllTypes());

  std::string args = GetConfiguration(SYNC_ALL_DATA, GetAllTypes());
  base::ListValue list_args;
  list_args.Append(kTestCallbackId);
  list_args.Append(args);
  handler_->HandleSetDatatypes(list_args);
  EXPECT_TRUE(sync_user_settings()->IsSyncEverythingEnabled());

  ExpectPageStatusResponse(PeopleHandler::kConfigurePageStatus);
}

TEST_F(PeopleHandlerTest, EnterCorrectExistingPassphrase) {
  const std::string kCorrectPassphrase = "correct_passphrase";

  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();

  sync_user_settings()->SetPassphraseRequired(kCorrectPassphrase);

  ASSERT_TRUE(sync_user_settings()->IsPassphraseRequired());

  base::ListValue list_args;
  list_args.Append(kTestCallbackId);
  list_args.Append(kCorrectPassphrase);
  handler_->HandleSetDecryptionPassphrase(list_args);

  ExpectSetPassphraseSuccess(true);

  EXPECT_FALSE(sync_user_settings()->IsPassphraseRequired());
}

TEST_F(PeopleHandlerTest, SuccessfullyCreateCustomPassphrase) {
  const std::string kPassphrase = "custom_passphrase";

  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();

  ASSERT_FALSE(sync_user_settings()->IsUsingExplicitPassphrase());

  base::ListValue list_args;
  list_args.Append(kTestCallbackId);
  list_args.Append(kPassphrase);
  handler_->HandleSetEncryptionPassphrase(list_args);

  ExpectSetPassphraseSuccess(true);

  EXPECT_EQ(sync_user_settings()->GetEncryptionPassphrase(), kPassphrase);
}

TEST_F(PeopleHandlerTest, EnterWrongExistingPassphrase) {
  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();

  sync_user_settings()->SetPassphraseRequired("correct_passphrase");

  ASSERT_TRUE(sync_user_settings()->IsPassphraseRequired());

  base::ListValue list_args;
  list_args.Append(kTestCallbackId);
  list_args.Append("invalid_passphrase");
  handler_->HandleSetDecryptionPassphrase(list_args);

  ExpectSetPassphraseSuccess(false);

  ASSERT_TRUE(sync_user_settings()->IsPassphraseRequired());
}

TEST_F(PeopleHandlerTest, CannotCreateBlankPassphrase) {
  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();

  ASSERT_FALSE(sync_user_settings()->IsUsingExplicitPassphrase());

  base::ListValue list_args;
  list_args.Append(kTestCallbackId);
  list_args.Append("");
  handler_->HandleSetEncryptionPassphrase(list_args);

  ExpectSetPassphraseSuccess(false);

  ASSERT_FALSE(sync_user_settings()->IsUsingExplicitPassphrase());
}

// Walks through each user selectable type, and tries to sync just that single
// data type.
TEST_F(PeopleHandlerTest, TestSyncIndividualTypes) {
  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();
  for (syncer::UserSelectableType type : GetAllTypes()) {
    syncer::UserSelectableTypeSet type_to_set;
    type_to_set.Put(type);

    std::string args = GetConfiguration(CHOOSE_WHAT_TO_SYNC, type_to_set);
    base::ListValue list_args;
    list_args.Append(kTestCallbackId);
    list_args.Append(args);

    handler_->HandleSetDatatypes(list_args);
    ExpectPageStatusResponse(PeopleHandler::kConfigurePageStatus);

    EXPECT_FALSE(sync_user_settings()->IsSyncEverythingEnabled());
    EXPECT_EQ(sync_user_settings()->GetSelectedTypes(), type_to_set);
  }
}

TEST_F(PeopleHandlerTest, TestSyncAllManually) {
  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();
  std::string args = GetConfiguration(CHOOSE_WHAT_TO_SYNC, GetAllTypes());
  base::ListValue list_args;
  list_args.Append(kTestCallbackId);
  list_args.Append(args);
  handler_->HandleSetDatatypes(list_args);

  ExpectPageStatusResponse(PeopleHandler::kConfigurePageStatus);

  EXPECT_FALSE(sync_user_settings()->IsSyncEverythingEnabled());
  EXPECT_EQ(sync_user_settings()->GetSelectedTypes(), GetAllTypes());
}

TEST_F(PeopleHandlerTest, NonRegisteredType) {
  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();

  // Simulate apps not being registered.
  syncer::UserSelectableTypeSet registered_types = GetAllTypes();
  registered_types.Remove(syncer::UserSelectableType::kApps);
  sync_user_settings()->SetRegisteredSelectableTypes(registered_types);

  // Simulate "Sync everything" being turned off, but all individual
  // toggles left on.
  std::string config = GetConfiguration(CHOOSE_WHAT_TO_SYNC, GetAllTypes());
  base::ListValue list_args;
  list_args.Append(kTestCallbackId);
  list_args.Append(config);

  // Only the registered types are selected.
  handler_->HandleSetDatatypes(list_args);
  EXPECT_FALSE(sync_user_settings()->IsSyncEverythingEnabled());
  EXPECT_EQ(sync_user_settings()->GetSelectedTypes(), registered_types);
}

TEST_F(PeopleHandlerTest, ShowSyncSetup) {
  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();
  // This should display the sync setup dialog (not login).
  handler_->HandleShowSyncSetupUI(base::ListValue());

  ExpectSyncPrefsChanged();
}

TEST_F(PeopleHandlerTest, ShowSetupSyncEverything) {
  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();

  // This should display the sync setup dialog (not login).
  handler_->HandleShowSyncSetupUI(base::ListValue());

  base::DictValue dictionary = ExpectSyncPrefsChanged();
  ExpectHasBoolKey(dictionary, "syncAllDataTypes", true);
  ExpectHasBoolKey(dictionary, "appsRegistered", true);
  ExpectHasBoolKey(dictionary, "autofillRegistered", true);
  ExpectHasBoolKey(dictionary, "bookmarksRegistered", true);
  ExpectHasBoolKey(dictionary, "cookiesRegistered", true);
  ExpectHasBoolKey(dictionary, "extensionsRegistered", true);
  ExpectHasBoolKey(dictionary, "passwordsRegistered", true);
  ExpectHasBoolKey(dictionary, "paymentsRegistered", true);
  ExpectHasBoolKey(dictionary, "preferencesRegistered", true);
  ExpectHasBoolKey(dictionary, "readingListRegistered", true);
  ExpectHasBoolKey(dictionary, "tabsRegistered", true);
  ExpectHasBoolKey(dictionary, "themesRegistered", true);
  ExpectHasBoolKey(dictionary, "typedUrlsRegistered", true);
  ExpectHasBoolKey(dictionary, "passphraseRequired", false);
  ExpectHasBoolKey(dictionary, "encryptAllData", false);
  CheckConfigDataTypeArguments(dictionary, SYNC_ALL_DATA, GetAllTypes());
}

TEST_F(PeopleHandlerTest, ShowSetupManuallySyncAll) {
  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();
  sync_user_settings()->SetSelectedTypes(/*sync_everything=*/false,
                                         /*types=*/GetAllTypes());
  ASSERT_FALSE(sync_user_settings()->IsSyncEverythingEnabled());

  // This should display the sync setup dialog (not login).
  handler_->HandleShowSyncSetupUI(base::ListValue());

  base::DictValue dictionary = ExpectSyncPrefsChanged();
  CheckConfigDataTypeArguments(dictionary, CHOOSE_WHAT_TO_SYNC, GetAllTypes());
}

TEST_F(PeopleHandlerTest, ShowSetupSyncForAllTypesIndividually) {
  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();

  for (syncer::UserSelectableType type : GetAllTypes()) {
    const syncer::UserSelectableTypeSet types = {type};

    sync_user_settings()->SetSelectedTypes(/*sync_everything=*/false, types);

    // This should display the sync setup dialog (not login).
    handler_->HandleShowSyncSetupUI(base::ListValue());

    // Close the config overlay.
    LoginUIServiceFactory::GetForProfile(profile())->LoginUIClosed(
        handler_.get());

    base::DictValue dictionary = ExpectSyncPrefsChanged();
    CheckConfigDataTypeArguments(dictionary, CHOOSE_WHAT_TO_SYNC, types);

    // Clean up so we can loop back to display the dialog again.
    web_ui_.ClearTrackedCalls();
  }
}

TEST_F(PeopleHandlerTest, ShowSetupOldGaiaPassphraseRequired) {
  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();

  const auto passphrase_time = base::Time::Now();

  sync_user_settings()->SetPassphraseRequired();
  sync_user_settings()->SetPassphraseType(
      syncer::PassphraseType::kFrozenImplicitPassphrase);
  sync_user_settings()->SetExplicitPassphraseTime(passphrase_time);

  handler_->HandleShowSyncSetupUI(base::ListValue());

  base::DictValue dictionary = ExpectSyncPrefsChanged();
  ExpectHasBoolKey(dictionary, "passphraseRequired", true);
  ASSERT_TRUE(dictionary.contains("explicitPassphraseTime"));
  ASSERT_TRUE(dictionary.FindString("explicitPassphraseTime"));
  EXPECT_EQ(base::UTF16ToUTF8(base::TimeFormatShortDate(passphrase_time)),
            *dictionary.FindString("explicitPassphraseTime"));
}

TEST_F(PeopleHandlerTest, ShowSetupCustomPassphraseRequired) {
  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();

  const auto passphrase_time = base::Time::Now();

  sync_user_settings()->SetPassphraseRequired();
  sync_user_settings()->SetPassphraseType(
      syncer::PassphraseType::kCustomPassphrase);
  sync_user_settings()->SetExplicitPassphraseTime(passphrase_time);

  handler_->HandleShowSyncSetupUI(base::ListValue());

  base::DictValue dictionary = ExpectSyncPrefsChanged();
  ExpectHasBoolKey(dictionary, "passphraseRequired", true);
  ASSERT_TRUE(dictionary.contains("explicitPassphraseTime"));
  ASSERT_TRUE(dictionary.FindString("explicitPassphraseTime"));
  EXPECT_EQ(base::UTF16ToUTF8(base::TimeFormatShortDate(passphrase_time)),
            *dictionary.FindString("explicitPassphraseTime"));
}

// Verifies that the user is not prompted to enter the custom passphrase while
// sync setup is ongoing. This isn't reachable on Ash because
// IsInitialSyncFeatureSetupComplete() always returns true.
#if !BUILDFLAG(IS_CHROMEOS)
TEST_F(PeopleHandlerTest, OngoingSetupCustomPassphraseRequired) {
  SigninUserWithoutSyncFeature();
  CreatePeopleHandler();

  const auto passphrase_time = base::Time::Now();

  sync_user_settings()->SetPassphraseRequired();
  sync_user_settings()->SetPassphraseType(
      syncer::PassphraseType::kCustomPassphrase);
  sync_user_settings()->SetExplicitPassphraseTime(passphrase_time);

  handler_->HandleShowSyncSetupUI(base::ListValue());

  base::DictValue dictionary = ExpectSyncPrefsChanged();
  ExpectHasBoolKey(dictionary, "passphraseRequired", false);
}
#endif  // !BUILDFLAG(IS_CHROMEOS)

TEST_F(PeopleHandlerTest, ShowSetupTrustedVaultKeysRequired) {
  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();

  sync_user_settings()->SetPassphraseType(
      syncer::PassphraseType::kTrustedVaultPassphrase);
  sync_service_->SetTrustedVaultKeyRequired(true);

  // This should display the sync setup dialog (not login).
  handler_->HandleShowSyncSetupUI(base::ListValue());

  base::DictValue dictionary = ExpectSyncPrefsChanged();
  ExpectHasBoolKey(dictionary, "passphraseRequired", false);
  ExpectHasBoolKey(dictionary, "trustedVaultKeysRequired", true);
  EXPECT_FALSE(dictionary.contains("explicitPassphraseTime"));
}

TEST_F(PeopleHandlerTest, ShowSetupEncryptAll) {
  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();
  sync_user_settings()->SetIsUsingExplicitPassphrase(true);
  ASSERT_TRUE(sync_user_settings()->IsEncryptEverythingEnabled());

  // This should display the sync setup dialog (not login).
  handler_->HandleShowSyncSetupUI(base::ListValue());

  base::DictValue dictionary = ExpectSyncPrefsChanged();
  ExpectHasBoolKey(dictionary, "encryptAllData", true);
}

TEST_F(PeopleHandlerTest, ShowSetupEncryptAllDisallowed) {
  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();
  sync_user_settings()->SetCustomPassphraseAllowed(false);

  // This should display the sync setup dialog (not login).
  handler_->HandleShowSyncSetupUI(base::ListValue());

  base::DictValue dictionary = ExpectSyncPrefsChanged();
  ExpectHasBoolKey(dictionary, "encryptAllData", false);
  ExpectHasBoolKey(dictionary, "customPassphraseAllowed", false);
}

TEST_F(PeopleHandlerTest, CannotCreatePassphraseIfCustomPassphraseDisallowed) {
  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();

  sync_user_settings()->SetCustomPassphraseAllowed(false);

  ASSERT_FALSE(sync_user_settings()->IsUsingExplicitPassphrase());

  base::ListValue list_args;
  list_args.Append(kTestCallbackId);
  list_args.Append("passphrase123");
  handler_->HandleSetEncryptionPassphrase(list_args);

  ExpectSetPassphraseSuccess(false);

  EXPECT_FALSE(sync_user_settings()->IsUsingExplicitPassphrase());
}

TEST_F(PeopleHandlerTest, CannotOverwritePassphraseWithNewOne) {
  const std::string kInitialPassphrase = "initial_passphrase";

  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();

  sync_user_settings()->SetEncryptionPassphrase(kInitialPassphrase);
  ASSERT_TRUE(sync_user_settings()->IsUsingExplicitPassphrase());

  base::ListValue list_args;
  list_args.Append(kTestCallbackId);
  list_args.Append("passphrase123");
  handler_->HandleSetEncryptionPassphrase(list_args);

  ExpectSetPassphraseSuccess(false);

  EXPECT_EQ(sync_user_settings()->GetEncryptionPassphrase(),
            kInitialPassphrase);
}

TEST_F(PeopleHandlerTest, DashboardClearWhileSettingsOpen_ConfirmSoon) {
  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();

  ASSERT_TRUE(sync_user_settings()->IsInitialSyncFeatureSetupComplete());

  handler_->HandleShowSyncSetupUI(base::ListValue());

  sync_service_->MimicDashboardClear();
  sync_service_->FireStateChanged();

#if BUILDFLAG(IS_CHROMEOS)
  ASSERT_TRUE(sync_user_settings()->IsSyncFeatureDisabledViaDashboard());
#else   // BUILDFLAG(IS_CHROMEOS)
  ASSERT_FALSE(sync_user_settings()->IsInitialSyncFeatureSetupComplete());
#endif  // BUILDFLAG(IS_CHROMEOS)

  // Now the user confirms sync again. This should set both the sync-requested
  // and the first-setup-complete bits.
  base::ListValue did_abort;
  did_abort.Append(false);
  handler_->OnDidClosePage(did_abort);

  EXPECT_TRUE(sync_user_settings()->IsInitialSyncFeatureSetupComplete());
}

TEST_F(PeopleHandlerTest, DashboardClearWhileSettingsOpen_ConfirmLater) {
  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();

  ASSERT_TRUE(sync_user_settings()->IsInitialSyncFeatureSetupComplete());

  handler_->HandleShowSyncSetupUI(base::ListValue());

  sync_service_->MimicDashboardClear();
  sync_service_->FireStateChanged();

#if BUILDFLAG(IS_CHROMEOS)
  ASSERT_TRUE(sync_user_settings()->IsSyncFeatureDisabledViaDashboard());
#else   // BUILDFLAG(IS_CHROMEOS)
  ASSERT_FALSE(sync_user_settings()->IsInitialSyncFeatureSetupComplete());
#endif  // BUILDFLAG(IS_CHROMEOS)

  // Sync starts up in transport mode.
  ASSERT_EQ(sync_service_->GetTransportState(),
            syncer::SyncService::TransportState::ACTIVE);

  // Now the user confirms sync again. This should set the sync-requested bit
  // and also the first-setup-complete bit (except on ChromeOS Ash where it is
  // always true).
  base::ListValue did_abort;
  did_abort.Append(false);
  handler_->OnDidClosePage(did_abort);

  EXPECT_EQ(sync_service_->GetTransportState(),
            syncer::SyncService::TransportState::ACTIVE);
  EXPECT_TRUE(sync_user_settings()->IsInitialSyncFeatureSetupComplete());
}

#if BUILDFLAG(ENABLE_DICE_SUPPORT)
TEST(PeopleHandlerDiceTest, StoredAccountsList) {
  content::BrowserTaskEnvironment task_environment;

  network::TestURLLoaderFactory url_loader_factory =
      network::TestURLLoaderFactory();

  TestingProfile::Builder builder;
  builder.AddTestingFactories(
      IdentityTestEnvironmentProfileAdaptor::
          GetIdentityTestEnvironmentFactoriesWithAppendedFactories(
              {TestingProfile::TestingFactory{
                  ChromeSigninClientFactory::GetInstance(),
                  base::BindRepeating(&BuildChromeSigninClientWithURLLoader,
                                      &url_loader_factory)}}));

  std::unique_ptr<TestingProfile> profile = builder.Build();
  ASSERT_EQ(true, AccountConsistencyModeManager::IsDiceEnabledForProfile(
                      profile.get()));

  auto identity_test_env_adaptor =
      std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile.get());
  auto* identity_test_env = identity_test_env_adaptor->identity_test_env();
  identity_test_env->SetTestURLLoaderFactory(&url_loader_factory);

  auto account_1 = identity_test_env->MakeAccountAvailable(
      "a@gmail.com", {.set_cookie = true});
  auto account_2 = identity_test_env->MakeAccountAvailable(
      "b@gmail.com", {.set_cookie = true});
  identity_test_env->SetPrimaryAccount(std::string(account_1.GetEmail()),
                                       signin::ConsentLevel::kSignin);

  PeopleHandler handler(profile.get());
  base::ListValue accounts = handler.GetStoredAccountsList();

  ASSERT_EQ(2u, accounts.size());
  ASSERT_TRUE(accounts[0].GetDict().FindString("email"));
  ASSERT_TRUE(accounts[1].GetDict().FindString("email"));
  EXPECT_EQ("a@gmail.com", *accounts[0].GetDict().FindString("email"));
  EXPECT_EQ("b@gmail.com", *accounts[1].GetDict().FindString("email"));
}
#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)

#if BUILDFLAG(IS_CHROMEOS)
// Regression test for crash in guest mode. https://crbug.com/40114033
TEST(PeopleHandlerGuestModeTest, GetStoredAccountsList) {
  content::BrowserTaskEnvironment task_environment;
  TestingProfile::Builder builder;
  builder.SetGuestSession();
  std::unique_ptr<Profile> profile = builder.Build();

  PeopleHandler handler(profile.get());
  base::ListValue accounts = handler.GetStoredAccountsList();
  EXPECT_TRUE(accounts.empty());
}

TEST_F(PeopleHandlerTest, GetStoredAccountsList) {
  // Chrome OS sets an unconsented primary account on login.
  identity_test_env()->MakePrimaryAccountAvailable("user@gmail.com",
                                                   ConsentLevel::kSignin);
  ASSERT_FALSE(identity_manager()->HasPrimaryAccount(ConsentLevel::kSync));

  CreatePeopleHandler();
  base::ListValue accounts = handler_->GetStoredAccountsList();
  ASSERT_EQ(1u, accounts.size());
  EXPECT_EQ("user@gmail.com", *accounts[0].GetDict().FindString("email"));
}

TEST_F(PeopleHandlerTest, SyncCookiesDisabled) {
  base::test::ScopedFeatureList features;
  // Disable Floating SSO feature flag.
  features.InitWithFeatures(
      /*enabled_features=*/{},
      /*disabled_features=*/{ash::features::kFloatingSso});

  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();

  const base::DictValue& sync_status_values =
      handler_->GetSyncStatusDictionary();
  std::optional<bool> sync_cookies_supported =
      sync_status_values.FindBool("syncCookiesSupported");
  EXPECT_FALSE(sync_cookies_supported.has_value());
}
#endif  // BUILDFLAG(IS_CHROMEOS)

#if BUILDFLAG(ENABLE_DICE_SUPPORT)
TEST_F(PeopleHandlerTest, ChromeSigninUserChoice) {
  base::HistogramTester histogram_tester;

  CreatePeopleHandler();

  SimulateHandleGetChromeSigninUserChoiceInfo();
  ExpectChromeSigninUserChoiceInfoFromWebUiResponse(
      false, ChromeSigninUserChoice::kNoChoice, "");

  std::string email("user@gmail.com");
  identity_test_env()->MakePrimaryAccountAvailable(email,
                                                   ConsentLevel::kSignin);

  SimulateHandleGetChromeSigninUserChoiceInfo();
  ExpectChromeSigninUserChoiceInfoFromWebUiResponse(
      true, ChromeSigninUserChoice::kNoChoice, email);

  ChromeSigninUserChoice user_choice = ChromeSigninUserChoice::kSignin;
  SimulateHandleSetChromeSigninUserChoiceInfo(email, user_choice);

  SimulateHandleGetChromeSigninUserChoiceInfo();
  ExpectChromeSigninUserChoiceInfoFromWebUiResponse(true, user_choice, email);

  DestroyPeopleHandler();

  histogram_tester.ExpectTotalCount(
      "Signin.Settings.ChromeSigninSettingModification", 1);
  histogram_tester.ExpectBucketCount(
      "Signin.Settings.ChromeSigninSettingModification",
      /*`ChromeSigninSettingModification::kToSignin`*/ 2, 1);
}

TEST_F(PeopleHandlerTest,
       ChromeSigninUserAvailableOnExplicitChromeSigninSignout) {
  const std::string email("user@gmail.com");
  identity_test_env()->MakePrimaryAccountAvailable(email,
                                                   ConsentLevel::kSignin);
  SetExplicitSignin(true);

  CreatePeopleHandler();

  SimulateHandleGetChromeSigninUserChoiceInfo();
  ExpectChromeSigninUserChoiceInfoFromWebUiResponse(
      true, ChromeSigninUserChoice::kNoChoice, email);

  SimulateSignout();

  ASSERT_FALSE(
      identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSignin));
  ASSERT_TRUE(identity_manager()->GetAccountsWithRefreshTokens().empty());

  SimulateHandleGetChromeSigninUserChoiceInfo();
  ExpectChromeSigninUserChoiceInfoFromWebUiResponse(
      false, ChromeSigninUserChoice::kNoChoice, "");
}

TEST_F(PeopleHandlerTest, ChromeSigninUserInfoUpdateOnPrefValueChange) {
  const std::string email("user@gmail.com");
  AccountInfo account_info = identity_test_env()->MakePrimaryAccountAvailable(
      email, ConsentLevel::kSignin);
  SetExplicitSignin(true);

  CreatePeopleHandler();

  ASSERT_FALSE(HasChromeSigninUserChoiceInfoChangeEvent());

  SigninPrefs signin_prefs(*profile()->GetPrefs());
  auto new_choice_value = ChromeSigninUserChoice::kSignin;
  ASSERT_NE(signin_prefs.GetChromeSigninInterceptionUserChoice(
                account_info.GetGaiaId()),
            new_choice_value);
  signin_prefs.SetChromeSigninInterceptionUserChoice(account_info.GetGaiaId(),
                                                     new_choice_value);
  ExpectChromeSigninUserChoiceInfoFromLastChangeEvent(true, new_choice_value,
                                                      email);
}

TEST_F(PeopleHandlerTest, ChromeSigninUserInfoUpdateOnSignin) {
  CreatePeopleHandler();

  ASSERT_FALSE(HasChromeSigninUserChoiceInfoChangeEvent());

  const std::string email("user@gmail.com");
  // Explicit browser signin.
  identity_test_env()->MakePrimaryAccountAvailable(email,
                                                   ConsentLevel::kSignin);

  // By default no choice yet.
  ExpectChromeSigninUserChoiceInfoFromLastChangeEvent(
      true, ChromeSigninUserChoice::kNoChoice, email);
}

TEST_F(PeopleHandlerTest, ChromeSigninUserInfoUpdateOnSync) {
  CreatePeopleHandler();

  ASSERT_FALSE(HasChromeSigninUserChoiceInfoChangeEvent());

  const std::string email("user@gmail.com");
  // Sync is a form of explicit browser signin.
  identity_test_env()->MakePrimaryAccountAvailable(email, ConsentLevel::kSync);

  // By default no choice yet.
  ExpectChromeSigninUserChoiceInfoFromLastChangeEvent(
      true, ChromeSigninUserChoice::kNoChoice, email);
}

// This test does not use `PeopleHandlerTest` test suite and needs it's own test
// setup because in order to get the proper web signin, we need to set a cookie
// while signing in, which requires setting up a `TestURLLoaderFactory` with the
// `ChromeSigninClient`.
TEST(PeopleHandlerWebOnlySigninTest, ChromeSigninUserAvailableOnWebSignin) {
  // -- Test Setup start

  // Needed to enable setting a proper account signed in on the web.
  content::BrowserTaskEnvironment task_environment;

  network::TestURLLoaderFactory url_loader_factory =
      network::TestURLLoaderFactory();

  TestingProfile::Builder builder;
  builder.AddTestingFactories(
      IdentityTestEnvironmentProfileAdaptor::
          GetIdentityTestEnvironmentFactoriesWithAppendedFactories(
              {TestingProfile::TestingFactory{
                   ChromeSigninClientFactory::GetInstance(),
                   base::BindRepeating(&BuildChromeSigninClientWithURLLoader,
                                       &url_loader_factory)},
               TestingProfile::TestingFactory{
                   SyncServiceFactory::GetInstance(),
                   base::BindRepeating(&BuildTestSyncService)}}));

  std::unique_ptr<TestingProfile> profile = builder.Build();
  auto identity_test_env_adaptor =
      std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile.get());
  // This test env should be used throughout the test.
  auto* identity_test_env = identity_test_env_adaptor->identity_test_env();
  identity_test_env->SetTestURLLoaderFactory(&url_loader_factory);

  PeopleHandler handler(profile.get());

  // -- Test Setup end.

  // Test before web signin -- only need to check the `shouldShowSettings` param
  {
    base::DictValue chrome_signin_user_choice_info_dict =
        handler.GetChromeSigninUserChoiceInfo();
    std::optional<bool> should_show_settings =
        chrome_signin_user_choice_info_dict.FindBool("shouldShowSettings");
    ASSERT_TRUE(should_show_settings.has_value());
    EXPECT_FALSE(should_show_settings.value());
  }

  const std::string email("user@gmail.com");
  // Signs in to the web only.
  identity_test_env->MakeAccountAvailable(email, {.set_cookie = true});
  ASSERT_FALSE(identity_test_env->identity_manager()->HasPrimaryAccount(
      signin::ConsentLevel::kSignin));

  // Test after web signin and check all the fields.
  {
    base::DictValue chrome_signin_user_choice_info_dict =
        handler.GetChromeSigninUserChoiceInfo();
    PeopleHandlerTest::ExpectChromeSigninUserChoiceInfoDict(
        chrome_signin_user_choice_info_dict,
        /*expected_should_show_settings=*/true,
        ChromeSigninUserChoice::kNoChoice, email);
  }

  // Check that `SignedInState` is properly computed
  {
    const base::DictValue& sync_status_values =
        handler.GetSyncStatusDictionary();
    std::optional<int> signedInState =
        sync_status_values.FindInt("signedInState");
    ASSERT_TRUE(signedInState.has_value());
    EXPECT_EQ(static_cast<SignedInState>(signedInState.value()),
              SignedInState::kWebOnlySignedIn);
  }
}

TEST_F(PeopleHandlerTest, SigninPendingThenSignout) {
  identity_test_env()->MakePrimaryAccountAvailable(kTestUser,
                                                   ConsentLevel::kSignin);
  SetExplicitSignin(true);
  CreatePeopleHandler();

  ASSERT_FALSE(HasSyncStatusUpdateChangedEvent());

  TriggerPrimaryAccountInPersistentError();

  {
    auto values_list =
        GetAllFiredValuesForEventName(kSyncStatusChangeEventName);
    ASSERT_GT(values_list.size(), 0U);
    size_t last_index = values_list.size() - 1;
    ASSERT_TRUE(values_list[last_index]->is_dict());
    const base::DictValue& sync_status_values =
        values_list[last_index]->GetDict();
    std::optional<int> signedInState =
        sync_status_values.FindInt("signedInState");
    ASSERT_TRUE(signedInState.has_value());
    EXPECT_EQ(static_cast<SignedInState>(signedInState.value()),
              SignedInState::kSignInPending);
  }

  // Simulates pressing on the "Sign out" Button in the Sign in Paused state,
  // that redirects to `PeopleHandler::HandleSignout()`. Since the test has no
  // browser, clearing the primary account should be equivalent.
  SimulateSignout();

  {
    auto values_list =
        GetAllFiredValuesForEventName(kSyncStatusChangeEventName);
    ASSERT_GT(values_list.size(), 0U);
    size_t last_index = values_list.size() - 1;
    ASSERT_TRUE(values_list[last_index]->is_dict());
    const base::DictValue& sync_status_values =
        values_list[last_index]->GetDict();
    std::optional<int> signedInState =
        sync_status_values.FindInt("signedInState");
    ASSERT_TRUE(signedInState.has_value());
    EXPECT_EQ(static_cast<SignedInState>(signedInState.value()),
              SignedInState::kSignedOut);
  }
}

TEST_F(PeopleHandlerTest, SigninPendingThenReauth) {
  identity_test_env()->MakePrimaryAccountAvailable(kTestUser,
                                                   ConsentLevel::kSignin);
  SetExplicitSignin(true);
  CreatePeopleHandler();

  ASSERT_FALSE(HasSyncStatusUpdateChangedEvent());

  TriggerPrimaryAccountInPersistentError();

  {
    auto values_list =
        GetAllFiredValuesForEventName(kSyncStatusChangeEventName);
    ASSERT_GT(values_list.size(), 0U);
    size_t last_index = values_list.size() - 1;
    ASSERT_TRUE(values_list[last_index]->is_dict());
    const base::DictValue& sync_status_values =
        values_list[last_index]->GetDict();
    std::optional<int> signedInState =
        sync_status_values.FindInt("signedInState");
    ASSERT_TRUE(signedInState.has_value());
    EXPECT_EQ(static_cast<SignedInState>(signedInState.value()),
              SignedInState::kSignInPending);
    ;
  }

  // Simulates pressing on the "Verify it's you" button in the Sign in Paused
  // state, and reauth.
  SimluateReauth();

  {
    auto values_list =
        GetAllFiredValuesForEventName(kSyncStatusChangeEventName);
    ASSERT_GT(values_list.size(), 0U);
    size_t last_index = values_list.size() - 1;
    ASSERT_TRUE(values_list[last_index]->is_dict());
    const base::DictValue& sync_status_values =
        values_list[last_index]->GetDict();
    std::optional<int> signedInState =
        sync_status_values.FindInt("signedInState");
    ASSERT_TRUE(signedInState.has_value());
    EXPECT_EQ(static_cast<SignedInState>(signedInState.value()),
              SignedInState::kSignedIn);
  }
}

// Regression test for https://crbug.com/389031469
TEST_F(PeopleHandlerTest, HandleStartSigninManaged) {
  testing::StrictMock<MockSigninUiDelegate> mock_signin_ui_delegate;
  base::AutoReset<signin_ui_util::SigninUiDelegate*> delegate_auto_reset =
      signin_ui_util::SetSigninUiDelegateForTesting(&mock_signin_ui_delegate);

  const char kManagedEmail[] = "user@managedchrome.com";
  AccountInfo account = identity_test_env()->MakePrimaryAccountAvailable(
      kManagedEmail, ConsentLevel::kSignin);
  SetExplicitSignin(true);
  // Make the account managed and disallow signout.
  account = AccountInfo::Builder(account)
                .SetHostedDomain("managedchrome.com")
                .Build();
  AccountCapabilitiesTestMutator(&account)
      .set_is_subject_to_enterprise_features(true);
  identity_test_env()->UpdateAccountInfoForAccount(account);
  SigninClient* client = ChromeSigninClientFactory::GetForProfile(profile());
  client->set_is_clear_primary_account_allowed_for_testing(
      SigninClient::SignoutDecision::CLEAR_PRIMARY_ACCOUNT_DISALLOWED);
  ASSERT_FALSE(client->IsClearPrimaryAccountAllowed());
  TriggerPrimaryAccountInPersistentError();
  CreatePeopleHandler();
  // This should not crash.
  EXPECT_CALL(
      mock_signin_ui_delegate,
      ShowReauthUI(profile(), kManagedEmail, /*enable_sync=*/false,
                   signin_metrics::AccessPoint::kSettings,
                   signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO));
  base::ListValue args;
  args.Append(0);
  web_ui_.HandleReceivedMessage("SyncSetupStartSignIn", args);
}

TEST_F(PeopleHandlerTest, SigninPendingValueWithSync) {
  CreatePeopleHandler();

  ASSERT_FALSE(HasSyncStatusUpdateChangedEvent());

  // User is syncing.
  identity_test_env()->MakePrimaryAccountAvailable(kTestUser,
                                                   ConsentLevel::kSync);

  {
    auto values_list =
        GetAllFiredValuesForEventName(kSyncStatusChangeEventName);
    ASSERT_GT(values_list.size(), 0U);
    size_t last_index = values_list.size() - 1;
    ASSERT_TRUE(values_list[last_index]->is_dict());
    const base::DictValue& sync_status_values =
        values_list[last_index]->GetDict();
    std::optional<int> signedInState =
        sync_status_values.FindInt("signedInState");
    ASSERT_TRUE(signedInState.has_value());
    EXPECT_EQ(static_cast<SignedInState>(signedInState.value()),
              SignedInState::kSyncing);
  }

  // Invalidate the account while it is syncing.
  TriggerPrimaryAccountInPersistentError();

  // `SigninPending` is still false even when the account is in error.
  {
    auto values_list =
        GetAllFiredValuesForEventName(kSyncStatusChangeEventName);
    ASSERT_GT(values_list.size(), 0U);
    size_t last_index = values_list.size() - 1;
    ASSERT_TRUE(values_list[last_index]->is_dict());
    const base::DictValue& sync_status_values =
        values_list[last_index]->GetDict();
    std::optional<int> signedInState =
        sync_status_values.FindInt("signedInState");
    ASSERT_TRUE(signedInState.has_value());
    EXPECT_EQ(static_cast<SignedInState>(signedInState.value()),
              SignedInState::kSyncing);
  }
}

TEST_F(PeopleHandlerTest, ChromeSigninUserChoiceHistogramsWhenSignedOut) {
  base::HistogramTester histogram_tester;
  CreatePeopleHandler();

  // Simluates settings page loading.
  SimulateHandleGetChromeSigninUserChoiceInfo();

  // Simulates closing the settings page.
  DestroyPeopleHandler();

  // No account are signed in, the setting is not expected to be shown, so no
  // values related to it should be recorded.
  histogram_tester.ExpectTotalCount(
      "Signin.Settings.ChromeSigninSettingModification", 0);
}

TEST_F(PeopleHandlerTest,
       ChromeSigninUserChoiceHistogramsWhenSignedInWithoutChangingSetting) {
  base::HistogramTester histogram_tester;
  // Signed in user can see the setting.
  identity_test_env()->MakePrimaryAccountAvailable("email@gmail.com",
                                                   ConsentLevel::kSignin);
  CreatePeopleHandler();

  // Simluates settings page loading.
  SimulateHandleGetChromeSigninUserChoiceInfo();

  // Simulates closing the settings page.
  DestroyPeopleHandler();

  // Setting is seen but not modiffied.
  histogram_tester.ExpectTotalCount(
      "Signin.Settings.ChromeSigninSettingModification", 1);
  histogram_tester.ExpectBucketCount(
      "Signin.Settings.ChromeSigninSettingModification",
      /*`ChromeSigninSettingModification::kNoModification`*/ 0, 1);
}

TEST_F(PeopleHandlerTest,
       ChromeSigninUserChoiceHistogramsWhenSignedInWithChangingSetting) {
  base::HistogramTester histogram_tester;
  // Signed in user can see the setting.
  AccountInfo account = identity_test_env()->MakePrimaryAccountAvailable(
      /*email=*/"email@gmail.com", ConsentLevel::kSignin);

  CreatePeopleHandler();

  // Simluates settings page loading.
  SimulateHandleGetChromeSigninUserChoiceInfo();

  SigninPrefs signin_prefs(*profile()->GetPrefs());
  ChromeSigninUserChoice current_choice =
      signin_prefs.GetChromeSigninInterceptionUserChoice(account.GetGaiaId());

  // Simulates setting a new value through the UI.
  ChromeSigninUserChoice user_choice = ChromeSigninUserChoice::kSignin;
  ASSERT_NE(current_choice, user_choice);
  SimulateHandleSetChromeSigninUserChoiceInfo(account.GetEmail(), user_choice);

  // Simulate a last bubble decline time as well.
  signin_prefs.SetChromeSigninInterceptionLastBubbleDeclineTime(
      account.GetGaiaId(), base::Time::Now());
  signin_prefs.IncrementChromeSigninBubbleRepromptCount(account.GetGaiaId());

  // Simulates a second selection within the same settings session.
  ChromeSigninUserChoice user_choice2 = ChromeSigninUserChoice::kDoNotSignin;
  ASSERT_NE(current_choice, user_choice2);
  SimulateHandleSetChromeSigninUserChoiceInfo(account.GetEmail(), user_choice2);
  // Explicitly setting the do not sign in option should clear bubble declined
  // time.
  EXPECT_FALSE(
      signin_prefs
          .GetChromeSigninInterceptionLastBubbleDeclineTime(account.GetGaiaId())
          .has_value());
  EXPECT_EQ(
      signin_prefs.GetChromeSigninBubbleRepromptCount(account.GetGaiaId()), 0);

  // Enforcing changing the value to the same previous one should not record a
  // new modification.
  SimulateHandleSetChromeSigninUserChoiceInfo(account.GetEmail(), user_choice2);

  // Simulates closing the settings page.
  DestroyPeopleHandler();

  // Setting is seen and modified twice.
  histogram_tester.ExpectTotalCount(
      "Signin.Settings.ChromeSigninSettingModification", 2);
  histogram_tester.ExpectBucketCount(
      "Signin.Settings.ChromeSigninSettingModification",
      /*`ChromeSigninSettingModification::kToSignin`*/ 2, 1);
  histogram_tester.ExpectBucketCount(
      "Signin.Settings.ChromeSigninSettingModification",
      /*`ChromeSigninSettingModification::kToDoNotSignin`*/ 3, 1);
}

TEST_F(
    PeopleHandlerTest,
    ChromeSigninUserChoiceHistogramsWhenSignedInWithChangingSettingThenSignout) {
  base::HistogramTester histogram_tester;
  // Signed in user can see the setting.
  AccountInfo account = identity_test_env()->MakePrimaryAccountAvailable(
      /*email=*/"email@gmail.com", ConsentLevel::kSignin);

  CreatePeopleHandler();

  // Simluates settings page loading.
  SimulateHandleGetChromeSigninUserChoiceInfo();

  SigninPrefs signin_prefs(*profile()->GetPrefs());
  ChromeSigninUserChoice current_choice =
      signin_prefs.GetChromeSigninInterceptionUserChoice(account.GetGaiaId());

  // Simulates setting a new value through the settings UI.
  ChromeSigninUserChoice new_value = ChromeSigninUserChoice::kSignin;
  ASSERT_NE(current_choice, new_value);
  SimulateHandleSetChromeSigninUserChoiceInfo(account.GetEmail(), new_value);

  SimulateSignout();

  SimulateHandleGetChromeSigninUserChoiceInfo();
  // The setting should not be seen anymore.
  ExpectChromeSigninUserChoiceInfoFromWebUiResponse(
      false, ChromeSigninUserChoice::kNoChoice, "");

  // Simulates closing the settings page.
  DestroyPeopleHandler();

  // A modification value is still recorded, even after signing out, since a
  // modification occurred during the session.
  histogram_tester.ExpectTotalCount(
      "Signin.Settings.ChromeSigninSettingModification", 1);
  histogram_tester.ExpectBucketCount(
      "Signin.Settings.ChromeSigninSettingModification",
      /*`ChromeSigninSettingModification::kToSignin`*/ 2, 1);
}

#endif

#if BUILDFLAG(IS_CHROMEOS)
class PeopleHandlerWithCookiesSyncTest : public PeopleHandlerTest {
 private:
  // Enable Floating SSO feature flag.
  base::test::ScopedFeatureList features_{ash::features::kFloatingSso};
};

TEST_F(PeopleHandlerWithCookiesSyncTest, SyncCookiesSupported) {
  SigninUserAndTurnSyncFeatureOn();
  CreatePeopleHandler();

  // Feature flag enabled, policy unset.
  {
    const base::DictValue& sync_status_values =
        handler_->GetSyncStatusDictionary();
    std::optional<bool> sync_cookies_supported =
        sync_status_values.FindBool("syncCookiesSupported");
    ASSERT_TRUE(sync_cookies_supported.has_value());
    EXPECT_FALSE(sync_cookies_supported.value());
  }

  // Feature flag enabled, policy set to false.
  {
    profile()->GetPrefs()->SetBoolean(chromeos::prefs::kFloatingSsoEnabled,
                                      false);

    const base::DictValue& sync_status_values =
        handler_->GetSyncStatusDictionary();
    std::optional<bool> sync_cookies_supported =
        sync_status_values.FindBool("syncCookiesSupported");
    ASSERT_TRUE(sync_cookies_supported.has_value());
    EXPECT_FALSE(sync_cookies_supported.value());
  }

  // Feature flag enabled, policy set to true.
  {
    profile()->GetPrefs()->SetBoolean(chromeos::prefs::kFloatingSsoEnabled,
                                      true);

    const base::DictValue& sync_status_values =
        handler_->GetSyncStatusDictionary();
    std::optional<bool> sync_cookies_supported =
        sync_status_values.FindBool("syncCookiesSupported");
    ASSERT_TRUE(sync_cookies_supported.has_value());
    EXPECT_TRUE(sync_cookies_supported.value());
  }
}
#endif  // BUILDFLAG(IS_CHROMEOS)

#if !BUILDFLAG(IS_CHROMEOS)
class PeopleHandlerWithReplaceSyncWithSigninUI : public PeopleHandlerTest {
  void SetUp() override {
    PeopleHandlerTest::SetUp();
    SigninUserWithoutSyncFeature();
    CreatePeopleHandler();
  }

 private:
  base::test::ScopedFeatureList features_{
      syncer::kReplaceSyncPromosWithSignInPromos};
};

// Walks through each user selectable type, and tries to activate account
// storage for that data type.
TEST_F(PeopleHandlerWithReplaceSyncWithSigninUI, SetIndividualDatatypes) {
  sync_user_settings()->SetSelectedTypes(/*sync_everything=*/false, {});

  for (syncer::UserSelectableType type : GetAllTypes()) {
    ASSERT_FALSE(sync_user_settings()->GetSelectedTypes().Has(type));

    SimulateHandleSetDatatype(type, true);
    ExpectPageStatusResponse(PeopleHandler::kConfigurePageStatus);

    EXPECT_TRUE(sync_user_settings()->GetSelectedTypes().Has(type));
  }
}

TEST_F(PeopleHandlerWithReplaceSyncWithSigninUI, SetNonRegisteredDatatype) {
  // Simulate apps not being registered.
  syncer::UserSelectableTypeSet registered_types = GetAllTypes();
  registered_types.Remove(syncer::UserSelectableType::kApps);
  sync_user_settings()->SetRegisteredSelectableTypes(registered_types);

  SimulateHandleSetDatatype(syncer::UserSelectableType::kApps, true);

  // Only the registered types are selected.
  EXPECT_EQ(sync_user_settings()->GetSelectedTypes(), registered_types);
  EXPECT_FALSE(sync_user_settings()->GetSelectedTypes().Has(
      syncer::UserSelectableType::kApps));
}

TEST_F(PeopleHandlerWithReplaceSyncWithSigninUI, HandleShowAccountSettingsUI) {
  handler_->HandleShowAccountSettingsUI(base::ListValue());

  EXPECT_FALSE(handler_->sync_blocker_);
  EXPECT_EQ(
      handler_.get(),
      LoginUIServiceFactory::GetForProfile(profile())->current_login_ui());
  ExpectSyncPrefsChanged();
}

#if BUILDFLAG(ENABLE_DICE_SUPPORT)
TEST(PeopleHandlerDiceTest, RecordSigninOffered) {
  content::BrowserTaskEnvironment task_environment;
  network::TestURLLoaderFactory url_loader_factory;

  TestingProfile::Builder builder;
  builder.AddTestingFactories(
      IdentityTestEnvironmentProfileAdaptor::
          GetIdentityTestEnvironmentFactoriesWithAppendedFactories(
              {TestingProfile::TestingFactory{
                  ChromeSigninClientFactory::GetInstance(),
                  base::BindRepeating(&BuildChromeSigninClientWithURLLoader,
                                      &url_loader_factory)}}));

  std::unique_ptr<TestingProfile> profile = builder.Build();

  auto identity_test_env_adaptor =
      std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile.get());
  auto* identity_test_env = identity_test_env_adaptor->identity_test_env();
  identity_test_env->SetTestURLLoaderFactory(&url_loader_factory);

  syncer::TestSyncService* sync_service = static_cast<syncer::TestSyncService*>(
      SyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
          profile.get(), base::BindRepeating(&BuildTestSyncService)));
  sync_service->SetSignedOut();

  content::TestWebUI web_ui;
  TestingPeopleHandler handler(&web_ui, profile.get());
  handler.AllowJavascript();

  content::TestWebContentsFactory web_contents_factory;
  content::WebContents* web_contents =
      web_contents_factory.CreateWebContents(profile.get());
  web_ui.set_web_contents(web_contents);
  handler.RegisterMessages();

  base::HistogramTester histogram_tester;

  // Test with no accounts (NewAccountNoExistingAccount)
  {
    base::ListValue args;
    args.Append(0);  // ChromeSigninAccessPoint::kSettings
    web_ui.HandleReceivedMessage("RecordSigninOffered", args);

    histogram_tester.ExpectUniqueSample(
        "Signin.SignIn.Offered", signin_metrics::AccessPoint::kSettings, 1);
    histogram_tester.ExpectUniqueSample(
        "Signin.SignIn.Offered.NewAccountNoExistingAccount",
        signin_metrics::AccessPoint::kSettings, 1);
  }

  // Test with an account (WithDefault)
  AccountInfo account_info = identity_test_env->MakeAccountAvailable(
      "user2@example.com", {.set_cookie = true});

  {
    base::ListValue args;
    args.Append(1);  // ChromeSigninAccessPoint::kSettingsYourSavedInfo
    web_ui.HandleReceivedMessage("RecordSigninOffered", args);

    histogram_tester.ExpectBucketCount(
        "Signin.SignIn.Offered",
        signin_metrics::AccessPoint::kSettingsYourSavedInfo, 1);
    histogram_tester.ExpectBucketCount(
        "Signin.SignIn.Offered.WithDefault",
        signin_metrics::AccessPoint::kSettingsYourSavedInfo, 1);
  }
}
#endif  // BUILDFLAG(ENABLE_DICE_SUPPORT)

#endif  // !BUILDFLAG(IS_CHROMEOS)
}  // namespace settings
