// Copyright 2012 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/ash/login/wizard_controller.h"

#include <memory>
#include <tuple>

#include "ash/constants/ash_features.h"
#include "ash/constants/ash_policy_pref_names.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/constants/ash_switches.h"
#include "ash/login/resources/grit/ash_login_strings.h"
#include "ash/public/cpp/login_screen_test_api.h"
#include "ash/root_window_controller.h"
#include "ash/shell.h"
#include "base/check_deref.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/functional/bind.h"
#include "base/i18n/time_formatting.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/run_until.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/time/time.h"
#include "base/types/expected.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/ash/accessibility/accessibility_manager.h"
#include "chrome/browser/ash/base/locale_util.h"
#include "chrome/browser/ash/login/demo_mode/demo_mode_test_utils.h"
#include "chrome/browser/ash/login/demo_mode/demo_setup_controller.h"
#include "chrome/browser/ash/login/enrollment/enrollment_screen.h"
#include "chrome/browser/ash/login/enrollment/mock_auto_enrollment_check_screen.h"
#include "chrome/browser/ash/login/enrollment/mock_enrollment_screen.h"
#include "chrome/browser/ash/login/existing_user_controller.h"
#include "chrome/browser/ash/login/fjord_oobe/fjord_oobe_state_manager.h"
#include "chrome/browser/ash/login/fjord_oobe/proto/fjord_oobe_state.pb.h"
#include "chrome/browser/ash/login/login_manager_test.h"
#include "chrome/browser/ash/login/login_wizard.h"
#include "chrome/browser/ash/login/oobe_configuration.h"
#include "chrome/browser/ash/login/oobe_screen.h"
#include "chrome/browser/ash/login/screens/consolidated_consent_screen.h"
#include "chrome/browser/ash/login/screens/device_disabled_screen.h"
#include "chrome/browser/ash/login/screens/error_screen.h"
#include "chrome/browser/ash/login/screens/hid_detection_screen.h"
#include "chrome/browser/ash/login/screens/mock_consolidated_consent_screen.h"
#include "chrome/browser/ash/login/screens/mock_demo_preferences_screen.h"
#include "chrome/browser/ash/login/screens/mock_demo_setup_screen.h"
#include "chrome/browser/ash/login/screens/mock_device_disabled_screen_view.h"
#include "chrome/browser/ash/login/screens/mock_enable_adb_sideloading_screen.h"
#include "chrome/browser/ash/login/screens/mock_enable_debugging_screen.h"
#include "chrome/browser/ash/login/screens/mock_network_screen.h"
#include "chrome/browser/ash/login/screens/mock_update_screen.h"
#include "chrome/browser/ash/login/screens/mock_welcome_screen.h"
#include "chrome/browser/ash/login/screens/mock_wrong_hwid_screen.h"
#include "chrome/browser/ash/login/screens/remote_activity_notification_screen.h"
#include "chrome/browser/ash/login/screens/reset_screen.h"
#include "chrome/browser/ash/login/screens/welcome_screen.h"
#include "chrome/browser/ash/login/screens/wrong_hwid_screen.h"
#include "chrome/browser/ash/login/startup_utils.h"
#include "chrome/browser/ash/login/test/cryptohome_mixin.h"
#include "chrome/browser/ash/login/test/device_state_mixin.h"
#include "chrome/browser/ash/login/test/js_checker.h"
#include "chrome/browser/ash/login/test/login_manager_mixin.h"
#include "chrome/browser/ash/login/test/oobe_base_test.h"
#include "chrome/browser/ash/login/test/oobe_configuration_waiter.h"
#include "chrome/browser/ash/login/test/oobe_screen_exit_waiter.h"
#include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
#include "chrome/browser/ash/login/test/oobe_screens_utils.h"
#include "chrome/browser/ash/login/test/test_predicate_waiter.h"
#include "chrome/browser/ash/login/test/user_auth_config.h"
#include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
#include "chrome/browser/ash/policy/enrollment/auto_enrollment_controller.h"
#include "chrome/browser/ash/policy/enrollment/auto_enrollment_state.h"
#include "chrome/browser/ash/policy/enrollment/auto_enrollment_type_checker.h"
#include "chrome/browser/ash/policy/enrollment/enrollment_config.h"
#include "chrome/browser/ash/policy/enrollment/enrollment_state_fetcher.h"
#include "chrome/browser/ash/policy/server_backed_state/server_backed_device_state.h"
#include "chrome/browser/ash/policy/server_backed_state/server_backed_state_keys_broker.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/global_features.h"
#include "chrome/browser/lifetime/termination_notification.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/ash/login/login_display_host.h"
#include "chrome/browser/ui/ash/login/webui_login_view.h"
#include "chrome/browser/ui/webui/ash/login/app_launch_splash_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/consolidated_consent_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/display_size_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/fjord_fw_update_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/fjord_image_selection_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/fjord_station_setup_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/fjord_touch_controller_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/gaia_info_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/gaia_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/gesture_navigation_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/local_state_error_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/marketing_opt_in_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/oobe_ui.h"
#include "chrome/browser/ui/webui/ash/login/remote_activity_notification_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/reset_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/signin_fatal_error_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/theme_selection_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/touchpad_scroll_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/update_required_screen_handler.h"
#include "chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/ash/components/audio/cras_audio_handler.h"
#include "chromeos/ash/components/dbus/device_management/fake_install_attributes_client.h"
#include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
#include "chromeos/ash/components/dbus/shill/fake_shill_manager_client.h"
#include "chromeos/ash/components/dbus/system_clock/system_clock_client.h"
#include "chromeos/ash/components/geolocation/location_fetcher.h"
#include "chromeos/ash/components/geolocation/system_location_provider.h"
#include "chromeos/ash/components/http_auth_dialog/http_auth_dialog.h"
#include "chromeos/ash/components/install_attributes/stub_install_attributes.h"
#include "chromeos/ash/components/network/network_state.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "chromeos/ash/components/settings/timezone_settings.h"
#include "chromeos/ash/components/system/fake_statistics_provider.h"
#include "chromeos/ash/components/system/statistics_provider.h"
#include "chromeos/ash/components/timezone/timezone_request.h"
#include "chromeos/dbus/constants/dbus_switches.h"
#include "chromeos/test/chromeos_test_utils.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/pref_service_factory.h"
#include "components/prefs/testing_pref_store.h"
#include "components/session_manager/core/session_manager.h"
#include "components/session_manager/session_manager_types.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "google_apis/gaia/gaia_id.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/register_basic_auth_handler.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "remoting/host/chromeos/features.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/icu/source/common/unicode/locid.h"
#include "third_party/icu/source/i18n/unicode/timezone.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/l10n/l10n_util.h"

namespace ash {
namespace {

using ::testing::_;
using ::testing::ElementsAre;
using ::testing::Eq;
using ::testing::Field;
using ::testing::IsNull;
using ::testing::Mock;
using ::testing::NotNull;

const char kDMServerURLPrefix[] =
    "https://m.google.com/devicemanagement/data/api";

const char kGeolocationResponseBody[] =
    "{\n"
    "  \"location\": {\n"
    "    \"lat\": 51.0,\n"
    "    \"lng\": -0.1\n"
    "  },\n"
    "  \"accuracy\": 1200.4\n"
    "}";

// Timezone should not match kGeolocationResponseBody to check that exactly
// this value will be used.
const char kTimezoneResponseBody[] =
    "{\n"
    "    \"dstOffset\" : 0.0,\n"
    "    \"rawOffset\" : -32400.0,\n"
    "    \"status\" : \"OK\",\n"
    "    \"timeZoneId\" : \"America/Anchorage\",\n"
    "    \"timeZoneName\" : \"Pacific Standard Time\"\n"
    "}";

const char kDisabledMessage[] = "This device has been disabled.";

const test::UIPath kGuestSessionLink = {"error-message",
                                        "error-guest-signin-fix-network"};

constexpr policy::AutoEnrollmentState kAutoEnrollmentConnectionError =
    base::unexpected(policy::AutoEnrollmentDMServerError{
        .dm_error = policy::DM_STATUS_REQUEST_FAILED,
        .network_error = net::ERR_CONNECTION_REFUSED});

constexpr policy::AutoEnrollmentState kAutoEnrollmentServerError =
    base::unexpected(policy::AutoEnrollmentDMServerError{
        .dm_error = policy::DM_STATUS_TEMPORARY_UNAVAILABLE});

constexpr policy::AutoEnrollmentState kAutoEnrollmentStateKeysRetrievalError =
    base::unexpected(policy::AutoEnrollmentStateKeysRetrievalError{});

// Matches on the mode parameter of an EnrollmentConfig object.
MATCHER_P(EnrollmentModeMatches, mode, "") {
  return arg.mode == mode;
}

template <typename Error>
policy::AutoEnrollmentState ToAutoEnrollmentState(Error error) {
  return base::unexpected(error);
}

class PrefStoreStub : public TestingPrefStore {
 public:
  // TestingPrefStore overrides:
  PrefReadError GetReadError() const override {
    return PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE;
  }

  bool IsInitializationComplete() const override { return true; }

 private:
  ~PrefStoreStub() override = default;
};

// Used to set up a fake `EnrollmentStateFetcher::Factory` for the duration of a
// test, which invokes result callback with the specified enrollment state.
class ScopedEnrollmentStateFetcherFactory {
 public:
  explicit ScopedEnrollmentStateFetcherFactory(
      policy::AutoEnrollmentController* controller)
      : controller_(controller) {
    controller_->SetEnrollmentStateFetcherFactoryForTesting(base::BindRepeating(
        &ScopedEnrollmentStateFetcherFactory::Create, base::Unretained(this)));
  }

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

  ~ScopedEnrollmentStateFetcherFactory() {
    controller_->SetEnrollmentStateFetcherFactoryForTesting(
        base::NullCallback());
  }

  // Waits until the `policy::AutoEnrollmentController` has requested the
  // creation of an `EnrollmentStateFetcher`. If an `EnrollmentStateFetcher` has
  // already been created, returns immediately. Note: The returned instance is
  // owned by `policy::AutoEnrollmentController`.
  void WaitUntilEnrollmentStateFetcherCreated() {
    if (!fetcher_created_) {
      base::RunLoop run_loop;
      created_callback_ = run_loop.QuitClosure();
      run_loop.Run();
    }
  }

  void ReportEnrollmentState(policy::AutoEnrollmentState state) {
    ASSERT_TRUE(fetcher_created_);
    ASSERT_FALSE(report_result_.is_null());
    std::move(report_result_).Run(state);
    base::RunLoop().RunUntilIdle();
  }

  void Reset() {
    fetcher_created_ = false;
    created_callback_.Reset();
  }

 private:
  class MockEnrollmentStateFetcher
      : public testing::StrictMock<policy::EnrollmentStateFetcher> {
   public:
    MOCK_METHOD(void, Start, ());
  };

  std::unique_ptr<policy::EnrollmentStateFetcher> Create(
      base::OnceCallback<void(policy::AutoEnrollmentState)> report_result,
      PrefService* local_state,
      policy::EnrollmentStateFetcher::RlweClientFactory rlwe_client_factory,
      policy::DeviceManagementService* device_management_service,
      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
      policy::ServerBackedStateKeysBroker* state_key_broker,
      ash::DeviceSettingsService* device_settings_service,
      OobeConfiguration* oobe_configuration) {
    // Only allow one `EnrollmentStateFetcher` to be created. The test should
    // call `Reset` to expect a new `EnrollmentStateFetcher` to be created.
    EXPECT_FALSE(fetcher_created_);
    fetcher_created_ = true;
    report_result_ = std::move(report_result);

    auto fetcher = std::make_unique<MockEnrollmentStateFetcher>();
    EXPECT_CALL(*fetcher, Start()).WillOnce(testing::Return());

    if (!created_callback_.is_null()) {
      std::move(created_callback_).Run();
    }
    return std::move(fetcher);
  }

  // The `policy::AutoEnrollmentController` which is using
  // `MockEnrollmentStateFetcher`.
  raw_ptr<policy::AutoEnrollmentController> controller_;

  bool fetcher_created_ = false;
  base::OnceCallback<void(policy::AutoEnrollmentState)> report_result_;
  base::OnceClosure created_callback_;
};

struct SwitchLanguageTestData {
  SwitchLanguageTestData() : result("", "", false), done(false) {}

  locale_util::LanguageSwitchResult result;
  bool done;
};

void OnLocaleSwitched(SwitchLanguageTestData* self,
                      const locale_util::LanguageSwitchResult& result) {
  self->result = result;
  self->done = true;
}

void RunSwitchLanguageTest(const std::string& locale,
                           const std::string& expected_locale,
                           const bool expect_success) {
  SwitchLanguageTestData data;
  locale_util::SwitchLanguageCallback callback(
      base::BindOnce(&OnLocaleSwitched, base::Unretained(&data)));
  locale_util::SwitchLanguage(
      g_browser_process->GetFeatures()->application_locale_storage(), locale,
      true, false, std::move(callback), ProfileManager::GetActiveUserProfile());

  // Token writing moves control to BlockingPool and back.
  content::RunAllTasksUntilIdle();

  EXPECT_EQ(data.done, true);
  EXPECT_EQ(data.result.requested_locale, locale);
  EXPECT_EQ(data.result.loaded_locale, expected_locale);
  EXPECT_EQ(data.result.success, expect_success);
}

void SetUpCrasAndEnableChromeVox(int volume_percent, bool mute_on) {
  AccessibilityManager* a11y = AccessibilityManager::Get();
  CrasAudioHandler* cras = CrasAudioHandler::Get();

  // Audio output is at `volume_percent` and `mute_on`. Spoken feedback
  // is disabled.
  cras->SetOutputVolumePercent(volume_percent);
  cras->SetOutputMute(mute_on);
  a11y->EnableSpokenFeedback(false);

  // Spoken feedback is enabled.
  a11y->EnableSpokenFeedback(true);
  base::RunLoop().RunUntilIdle();
}

void QuitLoopOnAutoEnrollmentProgress(
    policy::AutoEnrollmentState expected_state,
    base::RunLoop* loop,
    policy::AutoEnrollmentState actual_state) {
  if (expected_state == actual_state) {
    loop->Quit();
  }
}

template <typename View>
void ExpectBind(View* view) {
  // TODO(jdufault): The view* api should follow the bind/unbind pattern instead
  // of bind(ptr), bind(nullptr).
  EXPECT_CALL(*view, MockBind(NotNull())).Times(1);
  EXPECT_CALL(*view, MockBind(IsNull())).Times(1);
}

template <typename View>
void ExpectBindUnbind(View* view) {
  EXPECT_CALL(*view, MockBind(NotNull())).Times(1);
  EXPECT_CALL(*view, MockUnbind()).Times(1);
}

template <typename View>
void ExpectSetDelegate(View* view) {
  EXPECT_CALL(*view, MockSetDelegate(NotNull())).Times(1);
  EXPECT_CALL(*view, MockSetDelegate(IsNull())).Times(1);
}

template <typename Mock>
Mock* MockScreen(std::unique_ptr<Mock> mock) {
  auto mock0 = mock.get();
  WizardController::default_controller()->screen_manager()->SetScreenForTesting(
      std::move(mock));
  return mock0;
}

template <typename Mock>
Mock* MockScreenExpectLifecycle(std::unique_ptr<Mock> mock) {
  auto mock0 = MockScreen(std::move(mock));
  EXPECT_CALL(*mock0, ShowImpl()).Times(0);
  return mock0;
}

}  // namespace

class WizardControllerTest : public OobeBaseTest {
 public:
  WizardControllerTest(const WizardControllerTest&) = delete;
  WizardControllerTest& operator=(const WizardControllerTest&) = delete;

 protected:
  WizardControllerTest() = default;
  ~WizardControllerTest() override = default;

  ErrorScreen* GetErrorScreen() { return GetOobeUI()->GetErrorScreen(); }

  OobeUI* GetOobeUI() { return LoginDisplayHost::default_host()->GetOobeUI(); }

  content::WebContents* GetWebContents() {
    LoginDisplayHost* host = LoginDisplayHost::default_host();
    if (!host) {
      return nullptr;
    }
    return host->GetOobeWebContents();
  }

  bool JSExecute(const std::string& script) {
    return content::ExecJs(GetWebContents(), script);
  }

  bool JSExecuteBooleanExpression(const std::string& expression) {
    return content::EvalJs(GetWebContents(), "!!(" + expression + ");")
        .ExtractBool();
  }

  std::string JSExecuteStringExpression(const std::string& expression) {
    return content::EvalJs(GetWebContents(), expression).ExtractString();
  }

  void CheckCurrentScreen(OobeScreenId screen) {
    BaseScreen* current_screen =
        WizardController::default_controller()->current_screen();
    const std::string actual_screen =
        current_screen ? current_screen->screen_id().name : "nullptr";
    const std::string expected_screen = screen.name;
    EXPECT_EQ(actual_screen, expected_screen);
  }

  WrongHWIDScreen* GetWrongHWIDScreen() {
    return static_cast<WrongHWIDScreen*>(
        WizardController::default_controller()->GetScreen(
            WrongHWIDScreenView::kScreenId));
  }
};

IN_PROC_BROWSER_TEST_F(WizardControllerTest, SwitchLanguage) {
  ASSERT_TRUE(WizardController::default_controller() != nullptr);
  WizardController::default_controller()->AdvanceToScreen(
      WelcomeView::kScreenId);
  test::WaitForWelcomeScreen();

  // Checking the default locale. Provided that the profile is cleared in SetUp.
  EXPECT_EQ("en-US", g_browser_process->GetApplicationLocale());
  EXPECT_STREQ("en", icu::Locale::getDefault().getLanguage());
  EXPECT_FALSE(base::i18n::IsRTL());
  const std::u16string en_str =
      l10n_util::GetStringUTF16(IDS_LOGIN_GET_STARTED);

  RunSwitchLanguageTest("fr", "fr", true);
  EXPECT_EQ("fr", g_browser_process->GetApplicationLocale());
  EXPECT_STREQ("fr", icu::Locale::getDefault().getLanguage());
  EXPECT_FALSE(base::i18n::IsRTL());
  const std::u16string fr_str =
      l10n_util::GetStringUTF16(IDS_LOGIN_GET_STARTED);

  EXPECT_NE(en_str, fr_str);

  RunSwitchLanguageTest("ar", "ar", true);
  EXPECT_EQ("ar", g_browser_process->GetApplicationLocale());
  EXPECT_STREQ("ar", icu::Locale::getDefault().getLanguage());
  EXPECT_TRUE(base::i18n::IsRTL());
  const std::u16string ar_str =
      l10n_util::GetStringUTF16(IDS_LOGIN_GET_STARTED);

  EXPECT_NE(fr_str, ar_str);
}

IN_PROC_BROWSER_TEST_F(WizardControllerTest, VolumeIsChangedForChromeVox) {
  SetUpCrasAndEnableChromeVox(75 /* volume_percent */, true /* mute_on */);

  // Check that output is unmuted now and at some level.
  CrasAudioHandler* cras = CrasAudioHandler::Get();
  ASSERT_FALSE(cras->IsOutputMuted());
  ASSERT_EQ(WizardController::kMinAudibleOutputVolumePercent,
            cras->GetOutputVolumePercent());
}

IN_PROC_BROWSER_TEST_F(WizardControllerTest, VolumeIsUnchangedForChromeVox) {
  SetUpCrasAndEnableChromeVox(75 /* volume_percent */, false /* mute_on */);

  // Check that output is unmuted now and at some level.
  CrasAudioHandler* cras = CrasAudioHandler::Get();
  ASSERT_FALSE(cras->IsOutputMuted());
  ASSERT_EQ(75, cras->GetOutputVolumePercent());
}

IN_PROC_BROWSER_TEST_F(WizardControllerTest, VolumeIsAdjustedForChromeVox) {
  SetUpCrasAndEnableChromeVox(5 /* volume_percent */, false /* mute_on */);

  // Check that output is unmuted now and at some level.
  CrasAudioHandler* cras = CrasAudioHandler::Get();
  ASSERT_FALSE(cras->IsOutputMuted());
  ASSERT_EQ(WizardController::kMinAudibleOutputVolumePercent,
            cras->GetOutputVolumePercent());
}

class WizardControllerFlowTest : public WizardControllerTest {
 public:
  WizardControllerFlowTest(const WizardControllerFlowTest&) = delete;
  WizardControllerFlowTest& operator=(const WizardControllerFlowTest&) = delete;

 protected:
  WizardControllerFlowTest() = default;
  // WizardControllerTest:
  void SetUpOnMainThread() override {
    WizardControllerTest::SetUpOnMainThread();

    // Make sure that OOBE is run as an "official" build.
    LoginDisplayHost::default_host()->GetWizardContext()->is_branded_build =
        true;

    WizardController* wizard_controller =
        WizardController::default_controller();
    wizard_controller->SetCurrentScreen(nullptr);
    WaitForOobeUI();
    wizard_controller->SetSharedURLLoaderFactoryForTesting(
        test_url_loader_factory_.GetSafeWeakWrapper());
    SystemLocationProvider::GetInstance()
        ->GetLocationProviderForTesting()
        ->GetLocationFetcherForTesting()
        ->SetSharedUrlLoaderFactoryForTesting(
            test_url_loader_factory_.GetSafeWeakWrapper());

    // Set up the mocks for all screens.
    mock_welcome_screen_ =
        MockScreenExpectLifecycle(std::make_unique<MockWelcomeScreen>(
            g_browser_process->local_state(),
            g_browser_process->GetFeatures()->application_locale_storage(),
            GetOobeUI()->GetView<WelcomeScreenHandler>()->AsWeakPtr(),
            base::BindRepeating(&WizardController::OnWelcomeScreenExit,
                                base::Unretained(wizard_controller))));

    mock_demo_preferences_screen_view_ =
        std::make_unique<MockDemoPreferencesScreenView>();
    mock_demo_preferences_screen_ =
        MockScreenExpectLifecycle(std::make_unique<MockDemoPreferencesScreen>(
            g_browser_process->local_state(),
            mock_demo_preferences_screen_view_->AsWeakPtr(),
            base::BindRepeating(&WizardController::OnDemoPreferencesScreenExit,
                                base::Unretained(wizard_controller))));

    device_disabled_screen_view_ =
        std::make_unique<MockDeviceDisabledScreenView>();
    MockScreen(std::make_unique<DeviceDisabledScreen>(
        device_disabled_screen_view_->AsWeakPtr()));
    EXPECT_CALL(*device_disabled_screen_view_, Show(_)).Times(0);

    mock_network_screen_view_ = std::make_unique<MockNetworkScreenView>();
    mock_network_screen_ =
        MockScreenExpectLifecycle(std::make_unique<MockNetworkScreen>(
            mock_network_screen_view_.get()->AsWeakPtr(),
            base::BindRepeating(&WizardController::OnNetworkScreenExit,
                                base::Unretained(wizard_controller))));

    mock_update_view_ = std::make_unique<MockUpdateView>();
    mock_update_screen_ =
        MockScreenExpectLifecycle(std::make_unique<MockUpdateScreen>(
            g_browser_process->local_state(),
            mock_update_view_.get()->AsWeakPtr(), GetErrorScreen(),
            base::BindRepeating(&WizardController::OnUpdateScreenExit,
                                base::Unretained(wizard_controller))));

    mock_enrollment_screen_view_ = std::make_unique<MockEnrollmentScreenView>();
    mock_enrollment_screen_ =
        MockScreenExpectLifecycle(std::make_unique<MockEnrollmentScreen>(
            g_browser_process->local_state(),
            g_browser_process->shared_url_loader_factory(),
            g_browser_process->platform_part()->browser_policy_connector_ash(),
            mock_enrollment_screen_view_->AsWeakPtr(), GetErrorScreen(),
            base::BindRepeating(&WizardController::OnEnrollmentScreenExit,
                                base::Unretained(wizard_controller))));

    mock_auto_enrollment_check_screen_view_ =
        std::make_unique<MockAutoEnrollmentCheckScreenView>();
    mock_auto_enrollment_check_screen_ = MockScreen(
        std::make_unique<testing::NiceMock<MockAutoEnrollmentCheckScreen>>(
            g_browser_process->local_state(),
            mock_auto_enrollment_check_screen_view_.get()->AsWeakPtr(),
            GetErrorScreen(),
            base::BindRepeating(
                &WizardController::OnAutoEnrollmentCheckScreenExit,
                base::Unretained(wizard_controller))));

    mock_wrong_hwid_screen_view_ = std::make_unique<MockWrongHWIDScreenView>();

    mock_wrong_hwid_screen_ =
        MockScreenExpectLifecycle(std::make_unique<MockWrongHWIDScreen>(
            mock_wrong_hwid_screen_view_.get()->AsWeakPtr(),
            base::BindRepeating(&WizardController::OnWrongHWIDScreenExit,
                                base::Unretained(wizard_controller))));

    mock_enable_adb_sideloading_screen_view_ =
        std::make_unique<MockEnableAdbSideloadingScreenView>();
    mock_enable_adb_sideloading_screen_ = MockScreenExpectLifecycle(
        std::make_unique<MockEnableAdbSideloadingScreen>(
            g_browser_process->local_state(),
            mock_enable_adb_sideloading_screen_view_->AsWeakPtr(),
            base::BindRepeating(
                &WizardController::OnEnableAdbSideloadingScreenExit,
                base::Unretained(wizard_controller))));

    mock_enable_debugging_screen_view_ =
        std::make_unique<MockEnableDebuggingScreenView>();
    mock_enable_debugging_screen_ =
        MockScreenExpectLifecycle(std::make_unique<MockEnableDebuggingScreen>(
            g_browser_process->local_state(),
            mock_enable_debugging_screen_view_.get()->AsWeakPtr(),
            base::BindRepeating(&WizardController::OnEnableDebuggingScreenExit,
                                base::Unretained(wizard_controller))));

    mock_demo_setup_screen_view_ = std::make_unique<MockDemoSetupScreenView>();
    mock_demo_setup_screen_ =
        MockScreenExpectLifecycle(std::make_unique<MockDemoSetupScreen>(
            mock_demo_setup_screen_view_->AsWeakPtr(),
            base::BindRepeating(&WizardController::OnDemoSetupScreenExit,
                                base::Unretained(wizard_controller))));

    mock_demo_preferences_screen_view_ =
        std::make_unique<MockDemoPreferencesScreenView>();
    mock_demo_preferences_screen_ =
        MockScreenExpectLifecycle(std::make_unique<MockDemoPreferencesScreen>(
            g_browser_process->local_state(),
            mock_demo_preferences_screen_view_->AsWeakPtr(),
            base::BindRepeating(&WizardController::OnDemoPreferencesScreenExit,
                                base::Unretained(wizard_controller))));

    mock_consolidated_consent_screen_view_ =
        std::make_unique<MockConsolidatedConsentScreenView>();
    mock_consolidated_consent_screen_ = MockScreenExpectLifecycle(
        std::make_unique<MockConsolidatedConsentScreen>(
            g_browser_process->local_state(),
            g_browser_process->GetFeatures()->application_locale_storage(),
            g_browser_process->metrics_service(),
            mock_consolidated_consent_screen_view_.get()->AsWeakPtr(),
            base::BindRepeating(
                &WizardController::OnConsolidatedConsentScreenExit,
                base::Unretained(wizard_controller))));

    // Switch to the initial screen.
    EXPECT_EQ(nullptr, wizard_controller->current_screen());
    EXPECT_CALL(*mock_welcome_screen_, ShowImpl()).Times(1);
    wizard_controller->AdvanceToScreen(WelcomeView::kScreenId);
  }

  void TearDownOnMainThread() override {
    mock_welcome_screen_ = nullptr;
    device_disabled_screen_view_.reset();
    test_url_loader_factory_.ClearResponses();
    WizardControllerTest::TearDownOnMainThread();
  }

  void SetUpCommandLine(base::CommandLine* command_line) override {
    WizardControllerTest::SetUpCommandLine(command_line);

    // Default to now showing auto enrollment check screen. If you want to show
    // this screen, you can override the flags.
    command_line->AppendSwitchASCII(
        switches::kEnterpriseEnableUnifiedStateDetermination,
        policy::AutoEnrollmentTypeChecker::kUnifiedStateDeterminationNever);
  }

  void WaitUntilTimezoneResolved() {
    base::RunLoop loop;
    if (!WizardController::default_controller()
             ->SetOnTimeZoneResolvedForTesting(loop.QuitClosure())) {
      return;
    }

    loop.Run();
  }

  void ResetAutoEnrollmentCheckScreen() {
    WizardController::default_controller()
        ->screen_manager()
        ->DeleteScreenForTesting(AutoEnrollmentCheckScreenView::kScreenId);
  }

  void TestControlFlowMain() {
    CheckCurrentScreen(WelcomeView::kScreenId);

    test_url_loader_factory_.SetInterceptor(base::BindLambdaForTesting(
        [&](const network::ResourceRequest& request) {
          if (base::StartsWith(request.url.spec(),
                               LocationFetcher::kDefaultGeolocationProviderUrl,
                               base::CompareCase::SENSITIVE)) {
            test_url_loader_factory_.AddResponse(request.url.spec(),
                                                 kGeolocationResponseBody);
          } else if (base::StartsWith(request.url.spec(),
                                      DefaultTimezoneProviderURL().spec(),
                                      base::CompareCase::SENSITIVE)) {
            test_url_loader_factory_.AddResponse(request.url.spec(),
                                                 kTimezoneResponseBody);
          }
        }));

    ASSERT_TRUE(LoginScreenTestApi::IsLoginShelfShown());

    EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
    EXPECT_CALL(*mock_network_screen_, ShowImpl()).Times(1);
    mock_welcome_screen_->ExitScreen(WelcomeScreen::Result::kNext);

    CheckCurrentScreen(NetworkScreenView::kScreenId);
    EXPECT_CALL(*mock_network_screen_, HideImpl()).Times(1);

    EXPECT_CALL(*mock_update_screen_, ShowImpl()).Times(1);
    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);

    EXPECT_NE(SystemLocationProvider::GetInstance(), nullptr);

    // Let update screen smooth time process (time = 0ms).
    content::RunAllPendingInMessageLoop();

    CheckCurrentScreen(UpdateView::kScreenId);
    EXPECT_CALL(*mock_update_screen_, HideImpl()).Times(1);
    EXPECT_CALL(*mock_auto_enrollment_check_screen_, ShowImpl()).Times(0);
    mock_update_screen_->RunExit(UpdateScreen::Result::UPDATE_NOT_REQUIRED);

    CheckCurrentScreen(UserCreationView::kScreenId);
    EXPECT_CALL(*mock_auto_enrollment_check_screen_, HideImpl()).Times(0);

    EXPECT_FALSE(ExistingUserController::current_controller() == nullptr);

    WaitUntilTimezoneResolved();
    EXPECT_EQ(
        "America/Anchorage",
        base::UTF16ToUTF8(
            system::TimezoneSettings::GetInstance()->GetCurrentTimezoneID()));
  }

  // All of the *Screen types are owned by WizardController. The views are owned
  // by this test class.
  raw_ptr<MockWelcomeScreen, DanglingUntriaged> mock_welcome_screen_ = nullptr;

  raw_ptr<MockNetworkScreen, DanglingUntriaged> mock_network_screen_ = nullptr;
  std::unique_ptr<MockNetworkScreenView> mock_network_screen_view_;

  raw_ptr<MockUpdateScreen, DanglingUntriaged> mock_update_screen_ = nullptr;
  std::unique_ptr<MockUpdateView> mock_update_view_;

  raw_ptr<MockEnrollmentScreen, DanglingUntriaged> mock_enrollment_screen_ =
      nullptr;
  std::unique_ptr<MockEnrollmentScreenView> mock_enrollment_screen_view_;

  // Auto enrollment check screen is a nice mock because it may or may not be
  // shown depending on when asynchronous auto enrollment check finishes. Only
  // add expectations for this if you are sure they are not affected by race
  // conditions.
  raw_ptr<testing::NiceMock<MockAutoEnrollmentCheckScreen>, DanglingUntriaged>
      mock_auto_enrollment_check_screen_ = nullptr;
  std::unique_ptr<MockAutoEnrollmentCheckScreenView>
      mock_auto_enrollment_check_screen_view_;

  raw_ptr<MockWrongHWIDScreen, DanglingUntriaged> mock_wrong_hwid_screen_ =
      nullptr;
  std::unique_ptr<MockWrongHWIDScreenView> mock_wrong_hwid_screen_view_;

  raw_ptr<MockEnableAdbSideloadingScreen, DanglingUntriaged>
      mock_enable_adb_sideloading_screen_ = nullptr;
  std::unique_ptr<MockEnableAdbSideloadingScreenView>
      mock_enable_adb_sideloading_screen_view_;

  raw_ptr<MockEnableDebuggingScreen, DanglingUntriaged>
      mock_enable_debugging_screen_ = nullptr;
  std::unique_ptr<MockEnableDebuggingScreenView>
      mock_enable_debugging_screen_view_;

  raw_ptr<MockDemoSetupScreen, DanglingUntriaged> mock_demo_setup_screen_ =
      nullptr;
  std::unique_ptr<MockDemoSetupScreenView> mock_demo_setup_screen_view_;

  raw_ptr<MockDemoPreferencesScreen, DanglingUntriaged>
      mock_demo_preferences_screen_ = nullptr;
  std::unique_ptr<MockDemoPreferencesScreenView>
      mock_demo_preferences_screen_view_;

  raw_ptr<MockConsolidatedConsentScreen, DanglingUntriaged>
      mock_consolidated_consent_screen_ = nullptr;
  std::unique_ptr<MockConsolidatedConsentScreenView>
      mock_consolidated_consent_screen_view_;

  std::unique_ptr<MockDeviceDisabledScreenView> device_disabled_screen_view_;

  network::TestURLLoaderFactory test_url_loader_factory_;

 private:
  std::unique_ptr<base::AutoReset<bool>> branded_build_override_;
};

IN_PROC_BROWSER_TEST_F(WizardControllerFlowTest, ControlFlowMain) {
  TestControlFlowMain();
}

// This test verifies that if WizardController fails to apply a non-critical
// update before the OOBE is marked complete, it allows the user to proceed to
// log in.
IN_PROC_BROWSER_TEST_F(WizardControllerFlowTest,
                       ControlFlowErrorUpdateNonCriticalUpdate) {
  CheckCurrentScreen(WelcomeView::kScreenId);
  EXPECT_CALL(*mock_update_screen_, ShowImpl()).Times(0);
  EXPECT_CALL(*mock_network_screen_, ShowImpl()).Times(1);
  EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
  mock_welcome_screen_->ExitScreen(WelcomeScreen::Result::kNext);

  CheckCurrentScreen(NetworkScreenView::kScreenId);
  EXPECT_CALL(*mock_network_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_update_screen_, ShowImpl()).Times(1);

  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);

  // Let update screen smooth time process (time = 0ms).
  content::RunAllPendingInMessageLoop();

  CheckCurrentScreen(UpdateView::kScreenId);
  EXPECT_CALL(*mock_update_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_auto_enrollment_check_screen_, ShowImpl()).Times(0);
  mock_update_screen_->RunExit(UpdateScreen::Result::UPDATE_NOT_REQUIRED);

  CheckCurrentScreen(UserCreationView::kScreenId);
  EXPECT_CALL(*mock_auto_enrollment_check_screen_, HideImpl()).Times(0);

  EXPECT_FALSE(ExistingUserController::current_controller() == nullptr);
}

// This test verifies that if WizardController fails to apply a critical update
// before the OOBE is marked complete, it goes back the network selection
// screen and thus prevents the user from proceeding to log in.
IN_PROC_BROWSER_TEST_F(WizardControllerFlowTest,
                       ControlFlowErrorUpdateCriticalUpdate) {
  CheckCurrentScreen(WelcomeView::kScreenId);
  EXPECT_CALL(*mock_update_screen_, ShowImpl()).Times(0);
  EXPECT_CALL(*mock_network_screen_, ShowImpl()).Times(1);
  EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
  mock_welcome_screen_->ExitScreen(WelcomeScreen::Result::kNext);

  CheckCurrentScreen(NetworkScreenView::kScreenId);
  EXPECT_CALL(*mock_network_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_update_screen_, ShowImpl()).Times(1);

  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);

  // Let update screen smooth time process (time = 0ms).
  content::RunAllPendingInMessageLoop();

  CheckCurrentScreen(UpdateView::kScreenId);
  EXPECT_CALL(*mock_update_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_auto_enrollment_check_screen_, ShowImpl()).Times(0);
  EXPECT_CALL(*mock_network_screen_, ShowImpl()).Times(1);
  EXPECT_CALL(*mock_network_screen_, HideImpl()).Times(0);  // last transition
  mock_update_screen_->RunExit(UpdateScreen::Result::UPDATE_ERROR);
  CheckCurrentScreen(NetworkScreenView::kScreenId);
}

IN_PROC_BROWSER_TEST_F(WizardControllerFlowTest, ControlFlowSkipUpdateEnroll) {
  CheckCurrentScreen(WelcomeView::kScreenId);
  EXPECT_CALL(*mock_update_screen_, ShowImpl()).Times(0);
  EXPECT_CALL(*mock_network_screen_, ShowImpl()).Times(1);
  EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
  mock_welcome_screen_->ExitScreen(WelcomeScreen::Result::kNext);

  EXPECT_CALL(*mock_network_screen_, HideImpl()).Times(1);

  CheckCurrentScreen(NetworkScreenView::kScreenId);
  EXPECT_CALL(*mock_update_screen_, ShowImpl()).Times(0);

  WizardController::default_controller()
      ->wizard_context_->enrollment_triggered_early = true;
  EXPECT_CALL(*mock_enrollment_screen_view_,
              SetEnrollmentConfig(
                  EnrollmentModeMatches(policy::EnrollmentConfig::MODE_MANUAL)))
      .Times(1);
  EXPECT_CALL(*mock_auto_enrollment_check_screen_, ShowImpl()).Times(0);
  EXPECT_CALL(*mock_enrollment_screen_, ShowImpl()).Times(1);
  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);

  content::RunAllPendingInMessageLoop();

  CheckCurrentScreen(EnrollmentScreenView::kScreenId);
  EXPECT_CALL(*mock_auto_enrollment_check_screen_, HideImpl()).Times(0);
  EXPECT_CALL(*mock_enrollment_screen_, HideImpl()).Times(0);
  content::RunAllPendingInMessageLoop();
}

IN_PROC_BROWSER_TEST_F(WizardControllerFlowTest,
                       ControlFlowEnrollmentCompleted) {
  CheckCurrentScreen(WelcomeView::kScreenId);
  EXPECT_CALL(*mock_update_screen_, ShowImpl()).Times(0);
  EXPECT_CALL(*mock_enrollment_screen_view_,
              SetEnrollmentConfig(
                  EnrollmentModeMatches(policy::EnrollmentConfig::MODE_MANUAL)))
      .Times(1);
  EXPECT_CALL(*mock_enrollment_screen_, ShowImpl()).Times(1);
  EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);

  WizardController::default_controller()->AdvanceToScreen(
      EnrollmentScreenView::kScreenId);
  CheckCurrentScreen(EnrollmentScreenView::kScreenId);
  mock_enrollment_screen_->ExitScreen(EnrollmentScreen::Result::COMPLETED);

  EXPECT_FALSE(ExistingUserController::current_controller() == nullptr);
}

IN_PROC_BROWSER_TEST_F(WizardControllerFlowTest,
                       ControlFlowWrongHWIDScreenFromLogin) {
  CheckCurrentScreen(WelcomeView::kScreenId);

  // Verify and clear all expectations on the mock welcome screen before setting
  // new ones.
  testing::Mock::VerifyAndClearExpectations(mock_welcome_screen_);

  EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
  LoginDisplayHost::default_host()->StartSignInScreen();
  EXPECT_FALSE(ExistingUserController::current_controller() == nullptr);

  EXPECT_CALL(*mock_wrong_hwid_screen_, ShowImpl()).Times(1);
  WizardController::default_controller()->AdvanceToScreen(
      WrongHWIDScreenView::kScreenId);

  CheckCurrentScreen(WrongHWIDScreenView::kScreenId);

  // Verify and clear all expectations on the mock wrong hwid screen before
  // setting new ones.
  testing::Mock::VerifyAndClearExpectations(mock_wrong_hwid_screen_);

  // After warning is skipped, user returns to sign-in screen.
  // And this destroys WizardController.
  EXPECT_CALL(*mock_wrong_hwid_screen_, HideImpl()).Times(1);
  GetWrongHWIDScreen()->OnExit();
  EXPECT_FALSE(ExistingUserController::current_controller() == nullptr);
}

// This parameterized test class extends WizardControllerFlowTest to verify how
// WizardController behaves if it does not find or fails to apply an update
// after the OOBE is marked complete.
class WizardControllerUpdateAfterCompletedOobeTest
    : public WizardControllerFlowTest,
      public testing::WithParamInterface<UpdateScreen::Result> {
 public:
  WizardControllerUpdateAfterCompletedOobeTest(
      const WizardControllerUpdateAfterCompletedOobeTest&) = delete;
  WizardControllerUpdateAfterCompletedOobeTest& operator=(
      const WizardControllerUpdateAfterCompletedOobeTest&) = delete;

 protected:
  WizardControllerUpdateAfterCompletedOobeTest() = default;

  void SetUpLocalStatePrefService(PrefService* local_state) override {
    WizardControllerFlowTest::SetUpLocalStatePrefService(local_state);
    StartupUtils::MarkOobeCompleted(
        CHECK_DEREF(local_state));  // Pretend OOBE was complete.
  }
};

// This test verifies that if WizardController reports any result after the
// OOBE is marked complete, it allows the user to proceed to log in.
IN_PROC_BROWSER_TEST_P(WizardControllerUpdateAfterCompletedOobeTest,
                       ControlFlowErrorUpdate) {
  CheckCurrentScreen(WelcomeView::kScreenId);

  // Verify and clear all expectations on the mock welcome screen before setting
  // new ones.
  testing::Mock::VerifyAndClearExpectations(mock_welcome_screen_);

  EXPECT_CALL(*mock_update_screen_, ShowImpl()).Times(0);
  EXPECT_CALL(*mock_network_screen_, ShowImpl()).Times(1);
  EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
  mock_welcome_screen_->ExitScreen(WelcomeScreen::Result::kNext);

  CheckCurrentScreen(NetworkScreenView::kScreenId);

  // Verify and clear all expectations on the mock network screen before setting
  // new ones.
  testing::Mock::VerifyAndClearExpectations(mock_network_screen_);

  EXPECT_CALL(*mock_network_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_update_screen_, ShowImpl()).Times(1);

  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);

  // Let update screen smooth time process (time = 0ms).
  content::RunAllPendingInMessageLoop();

  CheckCurrentScreen(UpdateView::kScreenId);

  testing::Mock::VerifyAndClearExpectations(mock_update_screen_);
  EXPECT_CALL(*mock_update_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_auto_enrollment_check_screen_, ShowImpl()).Times(0);
  mock_update_screen_->RunExit(GetParam());

  CheckCurrentScreen(UserCreationView::kScreenId);

  testing::Mock::VerifyAndClearExpectations(mock_auto_enrollment_check_screen_);
  EXPECT_CALL(*mock_auto_enrollment_check_screen_, HideImpl()).Times(0);

  EXPECT_NE(nullptr, ExistingUserController::current_controller());
}

INSTANTIATE_TEST_SUITE_P(
    All,
    WizardControllerUpdateAfterCompletedOobeTest,
    testing::Values(UpdateScreen::Result::UPDATE_NOT_REQUIRED,
                    UpdateScreen::Result::UPDATE_ERROR));

class WizardControllerDeviceStateTest : public WizardControllerFlowTest {
 public:
  WizardControllerDeviceStateTest(const WizardControllerDeviceStateTest&) =
      delete;
  WizardControllerDeviceStateTest& operator=(
      const WizardControllerDeviceStateTest&) = delete;

 protected:
  WizardControllerDeviceStateTest() {
    fake_statistics_provider_.SetMachineStatistic(system::kSerialNumberKey,
                                                  "test");
    fake_statistics_provider_.SetMachineStatistic(system::kActivateDateKey,
                                                  "2000-01");
    fake_statistics_provider_.SetVpdStatus(
        system::StatisticsProvider::VpdStatus::kValid);
    // Make all requests to DMServer fail with net::ERR_CONNECTION_REFUSED.
    test_url_loader_factory_.SetInterceptor(base::BindLambdaForTesting(
        [&](const network::ResourceRequest& request) {
          if (request.url.spec().starts_with(kDMServerURLPrefix)) {
            test_url_loader_factory_.AddResponse(
                request.url, network::mojom::URLResponseHead::New(),
                std::string(),
                network::URLLoaderCompletionStatus(
                    net::ERR_CONNECTION_REFUSED));
          }
        }));
  }

  static policy::AutoEnrollmentController* auto_enrollment_controller() {
    return WizardController::default_controller()
        ->GetAutoEnrollmentController();
  }

  static void WaitForAutoEnrollmentState(policy::AutoEnrollmentState state) {
    base::RunLoop loop;
    base::CallbackListSubscription progress_subscription =
        auto_enrollment_controller()->RegisterProgressCallback(
            base::BindRepeating(&QuitLoopOnAutoEnrollmentProgress, state,
                                &loop));
    loop.Run();
  }

  // WizardControllerFlowTest:
  void SetUpOnMainThread() override {
    WizardControllerFlowTest::SetUpOnMainThread();

    histogram_tester_ = std::make_unique<base::HistogramTester>();

    // Initialize the FakeShillManagerClient. This does not happen
    // automatically because of the `DBusThreadManager::Initialize`
    // call in `SetUpInProcessBrowserTestFixture`. See crbug.com/40578322.
    // TODO(pmarko): Find a way for FakeShillManagerClient to be initialized
    // automatically (https://crbug.com/40578322).
    ShillManagerClient::Get()->GetTestInterface()->SetupDefaultEnvironment();
  }

  void SetUpCommandLine(base::CommandLine* command_line) override {
    WizardControllerFlowTest::SetUpCommandLine(command_line);

    command_line->AppendSwitchASCII(
        switches::kEnterpriseEnableUnifiedStateDetermination,
        policy::AutoEnrollmentTypeChecker::kUnifiedStateDeterminationAlways);
  }

  system::ScopedFakeStatisticsProvider fake_statistics_provider_;

  base::HistogramTester* histogram_tester() { return histogram_tester_.get(); }

 private:
  DeviceStateMixin device_state_{&mixin_host_,
                                 DeviceStateMixin::State::BEFORE_OOBE};

  std::unique_ptr<base::HistogramTester> histogram_tester_;
};

IN_PROC_BROWSER_TEST_F(WizardControllerDeviceStateTest,
                       AutoEnrollmentControllerStartedAfterNetworkScreen) {
  CheckCurrentScreen(WelcomeView::kScreenId);
  EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_network_screen_, ShowImpl()).Times(1);
  mock_welcome_screen_->ExitScreen(WelcomeScreen::Result::kNext);

  CheckCurrentScreen(NetworkScreenView::kScreenId);
  EXPECT_CALL(*mock_network_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_update_screen_, ShowImpl()).Times(1);
  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);

  content::RunAllTasksUntilIdle();

  // Verify that the state fetch has started or already completed.
  EXPECT_TRUE(auto_enrollment_controller()->IsInProgress() ||
              auto_enrollment_controller()->state().has_value());
}

IN_PROC_BROWSER_TEST_F(WizardControllerDeviceStateTest,
                       ControlFlowNoForcedReEnrollmentOnFirstBoot) {
  fake_statistics_provider_.ClearMachineStatistic(system::kActivateDateKey);
  EXPECT_NE(policy::AutoEnrollmentResult::kNoEnrollment,
            auto_enrollment_controller()->state());

  CheckCurrentScreen(WelcomeView::kScreenId);
  EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_network_screen_, ShowImpl()).Times(1);
  mock_welcome_screen_->ExitScreen(WelcomeScreen::Result::kNext);

  CheckCurrentScreen(NetworkScreenView::kScreenId);
  EXPECT_CALL(*mock_network_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_update_screen_, ShowImpl()).Times(1);
  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);

  // Let update screen smooth time process (time = 0ms).
  content::RunAllPendingInMessageLoop();

  CheckCurrentScreen(UpdateView::kScreenId);
  EXPECT_CALL(*mock_update_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_auto_enrollment_check_screen_, ShowImpl()).Times(0);
  mock_update_screen_->RunExit(UpdateScreen::Result::UPDATE_NOT_REQUIRED);

  CheckCurrentScreen(UserCreationView::kScreenId);
  EXPECT_EQ(policy::AutoEnrollmentResult::kNoEnrollment,
            auto_enrollment_controller()->state());
  EXPECT_EQ(1,
            FakeInstallAttributesClient::Get()
                ->remove_firmware_management_parameters_from_tpm_call_count());
  EXPECT_EQ(
      1, FakeSessionManagerClient::Get()->clear_block_devmode_vpd_call_count());
}

class WizardControllerUnifiedEnrollmentTest
    : public WizardControllerDeviceStateTest {
 protected:
  void SetUpCommandLine(base::CommandLine* command_line) override {
    WizardControllerDeviceStateTest::SetUpCommandLine(command_line);
  }

  void ProgressUntilAutoEnrollmentCheckScreen() {
    CheckCurrentScreen(WelcomeView::kScreenId);
    EXPECT_CALL(*mock_welcome_screen_, HideImpl());
    EXPECT_CALL(*mock_network_screen_, ShowImpl());
    mock_welcome_screen_->ExitScreen(WelcomeScreen::Result::kNext);

    CheckCurrentScreen(NetworkScreenView::kScreenId);
    EXPECT_CALL(*mock_network_screen_, HideImpl());
    EXPECT_CALL(*mock_update_screen_, ShowImpl());

    mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);

    // Let update screen smooth time process (time = 0ms).
    base::RunLoop().RunUntilIdle();

    CheckCurrentScreen(UpdateView::kScreenId);
    EXPECT_CALL(*mock_update_screen_, HideImpl());
    EXPECT_CALL(*mock_auto_enrollment_check_screen_, ShowImpl());
    mock_update_screen_->RunExit(UpdateScreen::Result::UPDATE_NOT_REQUIRED);

    CheckCurrentScreen(AutoEnrollmentCheckScreenView::kScreenId);
    mock_auto_enrollment_check_screen_->RealShow();
  }
};

// Tests that when EnrollmentStateFetcher reports kNoEnrollment, we skip
// enrollment screen and set state correctly.
IN_PROC_BROWSER_TEST_F(WizardControllerUnifiedEnrollmentTest, NoEnrollment) {
  ScopedEnrollmentStateFetcherFactory fetcher_factory(
      auto_enrollment_controller());
  ProgressUntilAutoEnrollmentCheckScreen();
  fetcher_factory.WaitUntilEnrollmentStateFetcherCreated();

  fetcher_factory.ReportEnrollmentState(
      policy::AutoEnrollmentResult::kNoEnrollment);

  CheckCurrentScreen(UserCreationView::kScreenId);
  EXPECT_CALL(*mock_enrollment_screen_, ShowImpl()).Times(0);
  EXPECT_CALL(*mock_enrollment_screen_, HideImpl()).Times(0);
  EXPECT_EQ(policy::AutoEnrollmentResult::kNoEnrollment,
            auto_enrollment_controller()->state());
}

// Tests that when EnrollmentStateFetcher reports state keys retrieval error, we
// show an error on enrollment check screen and that it is not possible to enter
// guest mode.
IN_PROC_BROWSER_TEST_F(WizardControllerUnifiedEnrollmentTest,
                       BlockedByCommunicationErrorOnStateKeysRetrieval) {
  ScopedEnrollmentStateFetcherFactory fetcher_factory(
      auto_enrollment_controller());
  ProgressUntilAutoEnrollmentCheckScreen();
  fetcher_factory.WaitUntilEnrollmentStateFetcherCreated();

  fetcher_factory.ReportEnrollmentState(kAutoEnrollmentStateKeysRetrievalError);

  CheckCurrentScreen(AutoEnrollmentCheckScreenView::kScreenId);
  EXPECT_EQ(AutoEnrollmentCheckScreenView::kScreenId.AsId(),
            GetErrorScreen()->GetParentScreen());
  test::OobeJS().ExpectHiddenPath(kGuestSessionLink);
}

// Tests that when EnrollmentStateFetcher reports kServerError, we show an error
// on enrollment check screen and that it is not possible to enter guest mode.
IN_PROC_BROWSER_TEST_F(WizardControllerUnifiedEnrollmentTest, ServerError) {
  ScopedEnrollmentStateFetcherFactory fetcher_factory(
      auto_enrollment_controller());
  ProgressUntilAutoEnrollmentCheckScreen();
  fetcher_factory.WaitUntilEnrollmentStateFetcherCreated();

  fetcher_factory.ReportEnrollmentState(kAutoEnrollmentServerError);

  CheckCurrentScreen(AutoEnrollmentCheckScreenView::kScreenId);
  EXPECT_EQ(AutoEnrollmentCheckScreenView::kScreenId.AsId(),
            GetErrorScreen()->GetParentScreen());
  test::OobeJS().ExpectHiddenPath(kGuestSessionLink);
}

// Tests that when EnrollmentStateFetcher reports kConnectionError, we show an
// error on enrollment check screen and that it is not possible to enter guest
// mode (like in FRE).
IN_PROC_BROWSER_TEST_F(WizardControllerUnifiedEnrollmentTest, ConnectionError) {
  ScopedEnrollmentStateFetcherFactory fetcher_factory(
      auto_enrollment_controller());
  ProgressUntilAutoEnrollmentCheckScreen();
  fetcher_factory.WaitUntilEnrollmentStateFetcherCreated();

  fetcher_factory.ReportEnrollmentState(kAutoEnrollmentConnectionError);

  CheckCurrentScreen(AutoEnrollmentCheckScreenView::kScreenId);
  EXPECT_EQ(AutoEnrollmentCheckScreenView::kScreenId.AsId(),
            GetErrorScreen()->GetParentScreen());
  test::OobeJS().ExpectHiddenPath(kGuestSessionLink);
}

// Tests that when EnrollmentStateFetcher reports kEnrollment and configures
// device state mode as FRE, we shown enrollment screen.
IN_PROC_BROWSER_TEST_F(WizardControllerUnifiedEnrollmentTest, Enrollment) {
  ScopedEnrollmentStateFetcherFactory fetcher_factory(
      auto_enrollment_controller());
  ProgressUntilAutoEnrollmentCheckScreen();
  fetcher_factory.WaitUntilEnrollmentStateFetcherCreated();

  EXPECT_CALL(*mock_auto_enrollment_check_screen_, HideImpl());
  EXPECT_CALL(*mock_enrollment_screen_, ShowImpl());
  base::DictValue device_state;
  device_state.Set(
      policy::kDeviceStateMode,
      base::Value(policy::kDeviceStateRestoreModeReEnrollmentEnforced));
  g_browser_process->local_state()->SetDict(
      ash::prefs::kServerBackedDeviceState, std::move(device_state));
  fetcher_factory.ReportEnrollmentState(
      policy::AutoEnrollmentResult::kEnrollment);

  CheckCurrentScreen(EnrollmentScreenView::kScreenId);
}

// Tests that when EnrollmentStateFetcher reports kDisabled and configures
// corresponding device state mode, we show device disabled screen.
IN_PROC_BROWSER_TEST_F(WizardControllerUnifiedEnrollmentTest, Disabled) {
  ScopedEnrollmentStateFetcherFactory fetcher_factory(
      auto_enrollment_controller());
  ProgressUntilAutoEnrollmentCheckScreen();
  fetcher_factory.WaitUntilEnrollmentStateFetcherCreated();

  EXPECT_CALL(*mock_auto_enrollment_check_screen_, HideImpl());
  EXPECT_CALL(*device_disabled_screen_view_,
              Show(Field(&DeviceDisabledScreenView::Params::message,
                         Eq(kDisabledMessage))));
  base::DictValue device_state;
  device_state.Set(policy::kDeviceStateMode,
                   base::Value(policy::kDeviceStateModeDisabled));
  device_state.Set(policy::kDeviceStateDisabledMessage,
                   base::Value(kDisabledMessage));
  g_browser_process->local_state()->SetDict(
      ash::prefs::kServerBackedDeviceState, std::move(device_state));
  fetcher_factory.ReportEnrollmentState(
      policy::AutoEnrollmentResult::kDisabled);

  CheckCurrentScreen(DeviceDisabledScreenView::kScreenId);
}

// Tests that when EnrollmentStateFetcher reports kDisabled and configures
// corresponding device state mode, we show device disabled screen after
// attempting to enter demo mode.
IN_PROC_BROWSER_TEST_F(WizardControllerUnifiedEnrollmentTest,
                       DisabledDemoMode) {
  ScopedEnrollmentStateFetcherFactory fetcher_factory(
      auto_enrollment_controller());

  CheckCurrentScreen(WelcomeView::kScreenId);
  EXPECT_FALSE(DemoSetupController::IsOobeDemoSetupFlowInProgress());

  EXPECT_CALL(*mock_welcome_screen_, HideImpl());
  EXPECT_CALL(*mock_network_screen_, ShowImpl());

  WizardController::default_controller()->StartDemoModeSetup();

  CheckCurrentScreen(NetworkScreenView::kScreenId);
  EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());

  EXPECT_CALL(*mock_network_screen_, HideImpl());
  EXPECT_CALL(*mock_demo_preferences_screen_, ShowImpl());

  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);

  CheckCurrentScreen(DemoPreferencesScreenView::kScreenId);
  EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());

  EXPECT_CALL(*mock_demo_preferences_screen_, HideImpl());
  EXPECT_CALL(*mock_update_screen_, ShowImpl());

  mock_demo_preferences_screen_->ExitScreen(
      DemoPreferencesScreen::Result::COMPLETED);

  // Let update screen smooth time process (time = 0ms).
  base::RunLoop().RunUntilIdle();

  CheckCurrentScreen(UpdateView::kScreenId);
  EXPECT_CALL(*mock_update_screen_, HideImpl());
  EXPECT_CALL(*mock_consolidated_consent_screen_, ShowImpl());

  mock_update_screen_->RunExit(UpdateScreen::Result::UPDATE_NOT_REQUIRED);

  CheckCurrentScreen(ConsolidatedConsentScreenView::kScreenId);
  EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());

  EXPECT_CALL(*mock_consolidated_consent_screen_, HideImpl());
  EXPECT_CALL(*mock_auto_enrollment_check_screen_, ShowImpl());

  mock_consolidated_consent_screen_->ExitScreen(
      ConsolidatedConsentScreen::Result::ACCEPTED_DEMO_ONLINE);

  CheckCurrentScreen(AutoEnrollmentCheckScreenView::kScreenId);
  EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());

  mock_auto_enrollment_check_screen_->RealShow();

  fetcher_factory.WaitUntilEnrollmentStateFetcherCreated();

  base::DictValue device_state;
  device_state.Set(policy::kDeviceStateMode,
                   base::Value(policy::kDeviceStateModeDisabled));
  device_state.Set(policy::kDeviceStateDisabledMessage,
                   base::Value(kDisabledMessage));
  g_browser_process->local_state()->SetDict(
      ash::prefs::kServerBackedDeviceState, std::move(device_state));

  EXPECT_CALL(*mock_auto_enrollment_check_screen_, HideImpl());
  EXPECT_CALL(*device_disabled_screen_view_,
              Show(Field(&DeviceDisabledScreenView::Params::message,
                         Eq(kDisabledMessage))));

  fetcher_factory.ReportEnrollmentState(
      policy::AutoEnrollmentResult::kDisabled);

  CheckCurrentScreen(DeviceDisabledScreenView::kScreenId);
}

// Tests that when EnrollmentStateFetcher times out, we set state correctly,
// show an error on enrollment check screen and that it is not possible to enter
// guest mode (like in FRE).
IN_PROC_BROWSER_TEST_F(WizardControllerUnifiedEnrollmentTest, Timeout) {
  ScopedEnrollmentStateFetcherFactory fetcher_factory(
      auto_enrollment_controller());
  ProgressUntilAutoEnrollmentCheckScreen();
  fetcher_factory.WaitUntilEnrollmentStateFetcherCreated();

  auto_enrollment_controller()->SafeguardTimerForTesting().FireNow();

  // Ensure that we show an error on enrollment check screen and that it is not
  // possible to enter guest mode (like in FRE).
  EXPECT_EQ(
      auto_enrollment_controller()->state(),
      ToAutoEnrollmentState(policy::AutoEnrollmentSafeguardTimeoutError{}));
  CheckCurrentScreen(AutoEnrollmentCheckScreenView::kScreenId);
  EXPECT_EQ(AutoEnrollmentCheckScreenView::kScreenId.AsId(),
            GetErrorScreen()->GetParentScreen());
  test::OobeJS().ExpectHiddenPath(kGuestSessionLink);
  histogram_tester()->ExpectBucketCount(
      "Enterprise.AutoEnrollmentControllerTimeout", 3 /*kTimeoutUnified*/, 1);
}

// Tests that AutoEnrollmentController does not create another
// EnrollmentStateFetcher while previous one is still running, but also that it
// does create one when previous has completed and reported a state.
IN_PROC_BROWSER_TEST_F(WizardControllerUnifiedEnrollmentTest, OneFetchAtATime) {
  ScopedEnrollmentStateFetcherFactory fetcher_factory(
      auto_enrollment_controller());
  ProgressUntilAutoEnrollmentCheckScreen();
  fetcher_factory.WaitUntilEnrollmentStateFetcherCreated();

  // This should determine that the enrollment state is already being fetched
  // and skip creating a new EnrollmentStateFetcher. Otherwise, it would fail an
  // expectation in fetcher_factory allowing only one EnrollmentStateFetcher to
  // be created.
  auto_enrollment_controller()->Start();

  // Simulate connection error, reset factory and attempt a retry.
  fetcher_factory.ReportEnrollmentState(kAutoEnrollmentConnectionError);
  fetcher_factory.Reset();
  auto_enrollment_controller()->Start();

  fetcher_factory.WaitUntilEnrollmentStateFetcherCreated();
}

class WizardControllerFjordOOBETest
    : public WizardControllerUnifiedEnrollmentTest {
 public:
  void SetUp() override {
    scoped_feature_list_.InitWithFeatures(
        /*enabled_features=*/{features::kFjordOobeForceEnabled},
        /*disabled_features=*/{features::kFjordOobeImageSwitch});
    FjordOobeStateManager::Initialize();
    WizardControllerUnifiedEnrollmentTest::SetUp();
  }

  ~WizardControllerFjordOOBETest() override {
    FjordOobeStateManager::Shutdown();
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

IN_PROC_BROWSER_TEST_F(WizardControllerFjordOOBETest, FjordOobeScreenFlow) {
  WizardController* const wizard_controller =
      WizardController::default_controller();
  ScopedEnrollmentStateFetcherFactory fetcher_factory(
      auto_enrollment_controller());
  ProgressUntilAutoEnrollmentCheckScreen();
  fetcher_factory.WaitUntilEnrollmentStateFetcherCreated();

  EXPECT_CALL(*mock_auto_enrollment_check_screen_, HideImpl());
  EXPECT_CALL(*mock_enrollment_screen_, ShowImpl());
  base::DictValue device_state;
  device_state.Set(
      policy::kDeviceStateMode,
      base::Value(policy::kDeviceStateRestoreModeReEnrollmentEnforced));
  g_browser_process->local_state()->SetDict(
      ash::prefs::kServerBackedDeviceState, std::move(device_state));
  fetcher_factory.ReportEnrollmentState(
      policy::AutoEnrollmentResult::kEnrollment);

  EXPECT_EQ(
      FjordOobeStateManager::Get()->GetFjordOobeStateInfo().oobe_state(),
      fjord_oobe_state::proto::FjordOobeStateInfo::FJORD_OOBE_STATE_START);
  CheckCurrentScreen(EnrollmentScreenView::kScreenId);
  EXPECT_FALSE(StartupUtils::IsOobeCompleted(
      CHECK_DEREF(g_browser_process->local_state())));

  // Make sure enterprise enrollment page shows up right after update screen.
  mock_enrollment_screen_->ExitScreen(EnrollmentScreen::Result::COMPLETED);

  EXPECT_TRUE(StartupUtils::IsOobeCompleted(
      CHECK_DEREF(g_browser_process->local_state())));
  CheckCurrentScreen(FjordTouchControllerScreenView::kScreenId);

  EXPECT_TRUE(wizard_controller->ExitFjordTouchControllerScreen());
  EXPECT_EQ(FjordOobeStateManager::Get()->GetFjordOobeStateInfo().oobe_state(),
            fjord_oobe_state::proto::FjordOobeStateInfo::
                FJORD_OOBE_STATE_ENROLLMENT_DONE);
  CheckCurrentScreen(FjordFwUpdateScreenView::kScreenId);
  EXPECT_TRUE(wizard_controller->ExitFjordTouchControllerScreen());
}

IN_PROC_BROWSER_TEST_F(WizardControllerFjordOOBETest,
                       ExitTouchControllerScreenDoesNothing) {
  WizardController* const wizard_controller =
      WizardController::default_controller();
  ScopedEnrollmentStateFetcherFactory fetcher_factory(
      auto_enrollment_controller());
  ProgressUntilAutoEnrollmentCheckScreen();
  fetcher_factory.WaitUntilEnrollmentStateFetcherCreated();

  EXPECT_CALL(*mock_auto_enrollment_check_screen_, HideImpl());
  EXPECT_CALL(*mock_enrollment_screen_, ShowImpl());
  base::DictValue device_state;
  device_state.Set(
      policy::kDeviceStateMode,
      base::Value(policy::kDeviceStateRestoreModeReEnrollmentEnforced));
  g_browser_process->local_state()->SetDict(
      ash::prefs::kServerBackedDeviceState, std::move(device_state));
  fetcher_factory.ReportEnrollmentState(
      policy::AutoEnrollmentResult::kEnrollment);

  CheckCurrentScreen(EnrollmentScreenView::kScreenId);
  EXPECT_EQ(
      FjordOobeStateManager::Get()->GetFjordOobeStateInfo().oobe_state(),
      fjord_oobe_state::proto::FjordOobeStateInfo::FJORD_OOBE_STATE_START);

  // Expect that Exit has no affect since TC setup screen is not showing.
  EXPECT_FALSE(wizard_controller->ExitFjordTouchControllerScreen());
  CheckCurrentScreen(EnrollmentScreenView::kScreenId);
  EXPECT_EQ(
      FjordOobeStateManager::Get()->GetFjordOobeStateInfo().oobe_state(),
      fjord_oobe_state::proto::FjordOobeStateInfo::FJORD_OOBE_STATE_START);
}

IN_PROC_BROWSER_TEST_F(WizardControllerFjordOOBETest,
                       AppSplashLaunchScreenNotifiesStateManager) {
  EXPECT_EQ(
      FjordOobeStateManager::Get()->GetFjordOobeStateInfo().oobe_state(),
      fjord_oobe_state::proto::FjordOobeStateInfo::FJORD_OOBE_STATE_START);

  WizardController::default_controller()->AdvanceToScreen(
      AppLaunchSplashScreenView::kScreenId);
  OobeScreenWaiter(AppLaunchSplashScreenView::kScreenId).Wait();

  EXPECT_EQ(
      FjordOobeStateManager::Get()->GetFjordOobeStateInfo().oobe_state(),
      fjord_oobe_state::proto::FjordOobeStateInfo::FJORD_OOBE_STATE_COMPLETE);
}

class WizardControllerFjordImageSelectionTest
    : public WizardControllerFlowTest {
 public:
  void SetUp() override {
    scoped_feature_list_.InitWithFeatures(
        {features::kFjordOobeForceEnabled, features::kFjordOobeImageSwitch},
        {});
    WizardControllerFlowTest::SetUp();
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

IN_PROC_BROWSER_TEST_F(WizardControllerFjordImageSelectionTest,
                       ImageSwitchScreenShownAfterUpdate) {
  CheckCurrentScreen(WelcomeView::kScreenId);

  EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_network_screen_, ShowImpl()).Times(1);
  mock_welcome_screen_->ExitScreen(WelcomeScreen::Result::kNext);

  CheckCurrentScreen(NetworkScreenView::kScreenId);
  EXPECT_CALL(*mock_network_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_update_screen_, ShowImpl()).Times(1);
  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);

  CheckCurrentScreen(UpdateView::kScreenId);
  EXPECT_CALL(*mock_update_screen_, HideImpl()).Times(1);
  mock_update_screen_->RunExit(UpdateScreen::Result::UPDATE_NOT_REQUIRED);

  CheckCurrentScreen(FjordImageSelectionScreenView::kScreenId);
}

class WizardControllerScreenPriorityOOBETest : public OobeBaseTest {
 protected:
  WizardControllerScreenPriorityOOBETest() = default;
  ~WizardControllerScreenPriorityOOBETest() override = default;

  void CheckCurrentScreen(OobeScreenId screen) {
    EXPECT_EQ(WizardController::default_controller()->GetScreen(screen),
              WizardController::default_controller()->current_screen());
  }
};

IN_PROC_BROWSER_TEST_F(WizardControllerScreenPriorityOOBETest,
                       DefaultPriorityTest) {
  ASSERT_TRUE(WizardController::default_controller() != nullptr);
  LoginDisplayHost::default_host()->GetWizardContext()->is_branded_build = true;

  CheckCurrentScreen(WelcomeView::kScreenId);
  // Showing network screen should pass it has default priority which is same as
  // welcome screen.
  WizardController::default_controller()->AdvanceToScreen(
      NetworkScreenView::kScreenId);
  CheckCurrentScreen(NetworkScreenView::kScreenId);

  // Showing update screen should pass it has default priority which is same as
  // network screen.
  WizardController::default_controller()->AdvanceToScreen(
      UpdateView::kScreenId);
  CheckCurrentScreen(UpdateView::kScreenId);
}

class WizardControllerScreenPriorityTest : public LoginManagerTest {
 protected:
  WizardControllerScreenPriorityTest() {
    login_manager_mixin_.AppendRegularUsers(1);
  }
  ~WizardControllerScreenPriorityTest() override = default;

  void CheckCurrentScreen(OobeScreenId screen) {
    EXPECT_EQ(WizardController::default_controller()->GetScreen(screen),
              WizardController::default_controller()->current_screen());
  }

  void SetUpLocalStatePrefService(PrefService* local_state) override {
    LoginManagerTest::SetUpLocalStatePrefService(local_state);

    // Set pref to show reset screen on startup.
    local_state->SetBoolean(ash::prefs::kFactoryResetRequested, true);
  }

 private:
  LoginManagerMixin login_manager_mixin_{&mixin_host_};
};

IN_PROC_BROWSER_TEST_F(WizardControllerScreenPriorityTest, CanNavigateToTest) {
  WizardController* const wizard_controller =
      WizardController::default_controller();
  ASSERT_TRUE(wizard_controller != nullptr);
  EXPECT_EQ(1, LoginScreenTestApi::GetUsersCount());

  // Check reset screen is visible on startup.
  OobeScreenWaiter(ResetView::kScreenId).Wait();
  EXPECT_TRUE(LoginScreenTestApi::IsOobeDialogVisible());

  // Showing update required screen should fail due to lower priority than reset
  // screen.
  LoginDisplayHost::default_host()->StartWizard(UpdateRequiredView::kScreenId);
  CheckCurrentScreen(ResetView::kScreenId);
  // Wizard controller should not be recreated.
  EXPECT_EQ(wizard_controller, WizardController::default_controller());

  // Showing device disabled screen is allowed due to higher priority than reset
  // screen.
  LoginDisplayHost::default_host()->StartWizard(
      DeviceDisabledScreenView::kScreenId);
  CheckCurrentScreen(DeviceDisabledScreenView::kScreenId);
  // Wizard controller should not be recreated.
  EXPECT_EQ(wizard_controller, WizardController::default_controller());

  // Showing update required screen should fail due to lower priority than
  // device disabled screen.
  LoginDisplayHost::default_host()->StartWizard(UpdateRequiredView::kScreenId);
  CheckCurrentScreen(DeviceDisabledScreenView::kScreenId);
  EXPECT_EQ(wizard_controller, WizardController::default_controller());
}

class WizardControllerBrokenLocalStateTest : public WizardControllerTest {
 public:
  WizardControllerBrokenLocalStateTest(
      const WizardControllerBrokenLocalStateTest&) = delete;
  WizardControllerBrokenLocalStateTest& operator=(
      const WizardControllerBrokenLocalStateTest&) = delete;

 protected:
  WizardControllerBrokenLocalStateTest() = default;
  ~WizardControllerBrokenLocalStateTest() override = default;

  // WizardControllerTest:
  void SetUpInProcessBrowserTestFixture() override {
    WizardControllerTest::SetUpInProcessBrowserTestFixture();
    PrefServiceFactory factory;
    factory.set_user_prefs(base::MakeRefCounted<PrefStoreStub>());
    local_state_ = factory.Create(new PrefRegistrySimple());
    WizardController::set_local_state_for_testing(local_state_.get());
  }

 private:
  std::unique_ptr<PrefService> local_state_;
};

IN_PROC_BROWSER_TEST_F(WizardControllerBrokenLocalStateTest,
                       LocalStateCorrupted) {
  // Checks that after wizard controller initialization local error screen
  // is displayed.
  ASSERT_EQ(WizardController::default_controller()->GetScreen(
                LocalStateErrorScreenView::kScreenId),
            WizardController::default_controller()->current_screen());

  OobeScreenWaiter(LocalStateErrorScreenView::kScreenId).Wait();

  // Checks visibility of the powerwash button.
  test::OobeJS().ExpectVisiblePath({"local-state-error", "powerwashButton"});

  // Emulates user click on the "Restart and Powerwash" button.
  ASSERT_EQ(0, FakeSessionManagerClient::Get()->start_device_wipe_call_count());
  test::OobeJS().TapOnPath({"local-state-error", "powerwashButton"});
  ASSERT_EQ(1, FakeSessionManagerClient::Get()->start_device_wipe_call_count());
}

class WizardControllerProxyAuthOnSigninTest : public OobeBaseTest {
 public:
  WizardControllerProxyAuthOnSigninTest(
      const WizardControllerProxyAuthOnSigninTest&) = delete;
  WizardControllerProxyAuthOnSigninTest& operator=(
      const WizardControllerProxyAuthOnSigninTest&) = delete;

 protected:
  WizardControllerProxyAuthOnSigninTest() {}
  ~WizardControllerProxyAuthOnSigninTest() override = default;

  // WizardControllerTest:
  void SetUp() override {
    // No need to configure the "proxy" test server to proxy anything. This test
    // only requires the configured "proxy" return proxy auth challenges.
    net::test_server::RegisterProxyBasicAuthHandler(proxy_server_, "user",
                                                    "pass");
    ASSERT_TRUE(proxy_server_.Start());
    OobeBaseTest::SetUp();
  }

  void SetUpCommandLine(base::CommandLine* command_line) override {
    OobeBaseTest::SetUpCommandLine(command_line);
    command_line->AppendSwitchASCII(::switches::kProxyServer,
                                    proxy_server_.host_port_pair().ToString());
  }

 private:
  DeviceStateMixin device_state_{
      &mixin_host_, DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED};
  net::test_server::EmbeddedTestServer proxy_server_{
      net::test_server::EmbeddedTestServer::Type::TYPE_HTTP};
};

IN_PROC_BROWSER_TEST_F(WizardControllerProxyAuthOnSigninTest,
                       ProxyAuthDialogOnSigninScreen) {
  OobeScreenWaiter(GaiaView::kScreenId).Wait();
  ASSERT_TRUE(base::test::RunUntil(
      []() { return HttpAuthDialog::GetAllDialogsForTest().size() == 1; }));
}

class WizardControllerKioskFlowTest : public WizardControllerFlowTest {
 public:
  WizardControllerKioskFlowTest(const WizardControllerKioskFlowTest&) = delete;
  WizardControllerKioskFlowTest& operator=(
      const WizardControllerKioskFlowTest&) = delete;

 protected:
  WizardControllerKioskFlowTest() = default;

  // WizardControllerFlowTest:
  void SetUpCommandLine(base::CommandLine* command_line) override {
    WizardControllerFlowTest::SetUpCommandLine(command_line);
    base::FilePath test_data_dir;
    ASSERT_TRUE(chromeos::test_utils::GetTestDataPath(
        "app_mode", "kiosk_manifest", &test_data_dir));
    command_line->AppendSwitchPath(
        switches::kAppOemManifestFile,
        test_data_dir.AppendASCII("kiosk_manifest.json"));
  }
};

IN_PROC_BROWSER_TEST_F(WizardControllerKioskFlowTest,
                       ControlFlowKioskForcedEnrollment) {
  EXPECT_CALL(*mock_enrollment_screen_view_,
              SetEnrollmentConfig(EnrollmentModeMatches(
                  policy::EnrollmentConfig::MODE_LOCAL_FORCED)))
      .Times(1);
  CheckCurrentScreen(WelcomeView::kScreenId);
  EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_network_screen_, ShowImpl()).Times(1);
  mock_welcome_screen_->ExitScreen(WelcomeScreen::Result::kNext);

  CheckCurrentScreen(NetworkScreenView::kScreenId);
  EXPECT_CALL(*mock_network_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_update_screen_, ShowImpl()).Times(1);

  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);

  // Let update screen smooth time process (time = 0ms).
  content::RunAllPendingInMessageLoop();

  CheckCurrentScreen(UpdateView::kScreenId);
  EXPECT_CALL(*mock_update_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_auto_enrollment_check_screen_, ShowImpl()).Times(0);
  EXPECT_CALL(*mock_enrollment_screen_, ShowImpl()).Times(1);
  mock_update_screen_->RunExit(UpdateScreen::Result::UPDATE_NOT_REQUIRED);

  CheckCurrentScreen(EnrollmentScreenView::kScreenId);
  EXPECT_CALL(*mock_auto_enrollment_check_screen_, HideImpl()).Times(0);

  EXPECT_FALSE(StartupUtils::IsOobeCompleted(
      CHECK_DEREF(g_browser_process->local_state())));

  // Make sure enterprise enrollment page shows up right after update screen.
  mock_enrollment_screen_->ExitScreen(EnrollmentScreen::Result::COMPLETED);

  EXPECT_TRUE(StartupUtils::IsOobeCompleted(
      CHECK_DEREF(g_browser_process->local_state())));
}

IN_PROC_BROWSER_TEST_F(WizardControllerKioskFlowTest,
                       ControlFlowEnrollmentBackForced) {
  EXPECT_CALL(*mock_enrollment_screen_view_,
              SetEnrollmentConfig(EnrollmentModeMatches(
                  policy::EnrollmentConfig::MODE_LOCAL_FORCED)))
      .Times(2);

  CheckCurrentScreen(WelcomeView::kScreenId);
  EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_network_screen_, ShowImpl()).Times(1);
  mock_welcome_screen_->ExitScreen(WelcomeScreen::Result::kNext);

  CheckCurrentScreen(NetworkScreenView::kScreenId);
  EXPECT_CALL(*mock_network_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_update_screen_, ShowImpl()).Times(1);

  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);

  // Let update screen smooth time process (time = 0ms).
  content::RunAllPendingInMessageLoop();

  CheckCurrentScreen(UpdateView::kScreenId);
  EXPECT_CALL(*mock_update_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_auto_enrollment_check_screen_, ShowImpl()).Times(0);
  EXPECT_CALL(*mock_enrollment_screen_, ShowImpl()).Times(1);
  mock_update_screen_->RunExit(UpdateScreen::Result::UPDATE_NOT_REQUIRED);

  CheckCurrentScreen(EnrollmentScreenView::kScreenId);
  EXPECT_CALL(*mock_auto_enrollment_check_screen_, HideImpl()).Times(0);

  EXPECT_FALSE(StartupUtils::IsOobeCompleted(
      CHECK_DEREF(g_browser_process->local_state())));

  // Make sure enterprise enrollment page shows up right after update screen.
  EXPECT_CALL(*mock_enrollment_screen_, ShowImpl()).Times(0);
  EXPECT_CALL(*mock_enrollment_screen_, HideImpl()).Times(0);
  EXPECT_CALL(*mock_auto_enrollment_check_screen_, ShowImpl()).Times(0);
  mock_enrollment_screen_->ExitScreen(
      EnrollmentScreen::Result::BACK_TO_AUTO_ENROLLMENT_CHECK);

  CheckCurrentScreen(EnrollmentScreenView::kScreenId);
  EXPECT_FALSE(StartupUtils::IsOobeCompleted(
      CHECK_DEREF(g_browser_process->local_state())));
}

class WizardControllerEnableAdbSideloadingTest
    : public WizardControllerFlowTest {
 public:
  WizardControllerEnableAdbSideloadingTest(
      const WizardControllerEnableAdbSideloadingTest&) = delete;
  WizardControllerEnableAdbSideloadingTest& operator=(
      const WizardControllerEnableAdbSideloadingTest&) = delete;

 protected:
  WizardControllerEnableAdbSideloadingTest() = default;

  template <class T>
  void SkipToScreen(OobeScreenId screen, T* screen_mock) {
    EXPECT_CALL(*screen_mock, ShowImpl()).Times(1);
    auto* const wizard_controller = WizardController::default_controller();
    wizard_controller->AdvanceToScreen(screen);
  }
};

IN_PROC_BROWSER_TEST_F(WizardControllerEnableAdbSideloadingTest,
                       ShowAndEnableSideloading) {
  CheckCurrentScreen(WelcomeView::kScreenId);

  EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
  SkipToScreen(EnableAdbSideloadingScreenView::kScreenId,
               mock_enable_adb_sideloading_screen_.get());
  CheckCurrentScreen(EnableAdbSideloadingScreenView::kScreenId);

  EXPECT_CALL(*mock_enable_adb_sideloading_screen_,
              OnUserAction(ElementsAre("enable-pressed")))
      .Times(1);
  test::OobeJS().ClickOnPath(
      {"adb-sideloading", "enable-adb-sideloading-ok-button"});

  base::RunLoop().RunUntilIdle();

  CheckCurrentScreen(EnableAdbSideloadingScreenView::kScreenId);
  EXPECT_CALL(*mock_enable_adb_sideloading_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_welcome_screen_, ShowImpl()).Times(1);

  mock_enable_adb_sideloading_screen_->ExitScreen();

  // Let update screen smooth time process (time = 0ms).
  base::RunLoop().RunUntilIdle();

  CheckCurrentScreen(WelcomeView::kScreenId);
}

IN_PROC_BROWSER_TEST_F(WizardControllerEnableAdbSideloadingTest,
                       ShowAndDoNotEnableSideloading) {
  CheckCurrentScreen(WelcomeView::kScreenId);

  EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
  SkipToScreen(EnableAdbSideloadingScreenView::kScreenId,
               mock_enable_adb_sideloading_screen_.get());
  CheckCurrentScreen(EnableAdbSideloadingScreenView::kScreenId);

  EXPECT_CALL(*mock_enable_adb_sideloading_screen_,
              OnUserAction(ElementsAre("cancel-pressed")))
      .Times(1);
  test::OobeJS().ClickOnPath(
      {"adb-sideloading", "enable-adb-sideloading-cancel-button"});

  base::RunLoop().RunUntilIdle();

  EXPECT_CALL(*mock_enable_adb_sideloading_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_welcome_screen_, ShowImpl()).Times(1);

  mock_enable_adb_sideloading_screen_->ExitScreen();

  // Let update screen smooth time process (time = 0ms).
  base::RunLoop().RunUntilIdle();

  CheckCurrentScreen(WelcomeView::kScreenId);
}

class WizardControllerEnableDebuggingTest : public WizardControllerFlowTest {
 public:
  WizardControllerEnableDebuggingTest(
      const WizardControllerEnableDebuggingTest&) = delete;
  WizardControllerEnableDebuggingTest& operator=(
      const WizardControllerEnableDebuggingTest&) = delete;

 protected:
  WizardControllerEnableDebuggingTest() = default;

  // MixinBasedInProcessBrowserTest:
  void SetUpCommandLine(base::CommandLine* command_line) override {
    WizardControllerFlowTest::SetUpCommandLine(command_line);
    command_line->AppendSwitch(chromeos::switches::kSystemDevMode);
  }
};

IN_PROC_BROWSER_TEST_F(WizardControllerEnableDebuggingTest,
                       ShowAndCancelEnableDebugging) {
  CheckCurrentScreen(WelcomeView::kScreenId);

  EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_enable_debugging_screen_, ShowImpl()).Times(1);

  mock_welcome_screen_->ExitScreen(WelcomeScreen::Result::kEnableDebugging);

  content::RunAllPendingInMessageLoop();

  CheckCurrentScreen(EnableDebuggingScreenView::kScreenId);
  EXPECT_CALL(*mock_enable_debugging_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_welcome_screen_, ShowImpl()).Times(1);

  mock_enable_debugging_screen_->ExitScreen();

  // Let update screen smooth time process (time = 0ms).
  content::RunAllPendingInMessageLoop();

  CheckCurrentScreen(WelcomeView::kScreenId);
}

class WizardControllerDemoSetupTest : public WizardControllerFlowTest {
 public:
  WizardControllerDemoSetupTest(const WizardControllerDemoSetupTest&) = delete;
  WizardControllerDemoSetupTest& operator=(
      const WizardControllerDemoSetupTest&) = delete;

 protected:
  WizardControllerDemoSetupTest() = default;
  ~WizardControllerDemoSetupTest() override = default;

  // MixinBasedInProcessBrowserTest:
  void SetUpOnMainThread() override {
    WizardControllerFlowTest::SetUpOnMainThread();
    testing::Mock::VerifyAndClearExpectations(mock_welcome_screen_);
  }

  template <class T>
  void SkipToScreen(OobeScreenId screen, T* screen_mock) {
    EXPECT_CALL(*screen_mock, ShowImpl()).Times(1);
    auto* const wizard_controller = WizardController::default_controller();
    wizard_controller->SimulateDemoModeSetupForTesting();
    wizard_controller->AdvanceToScreen(screen);
  }
};

IN_PROC_BROWSER_TEST_F(WizardControllerDemoSetupTest,
                       OnlineDemoSetupFlowFinished) {
  CheckCurrentScreen(WelcomeView::kScreenId);
  EXPECT_FALSE(DemoSetupController::IsOobeDemoSetupFlowInProgress());

  EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_network_screen_, ShowImpl()).Times(1);

  WizardController::default_controller()->StartDemoModeSetup();

  CheckCurrentScreen(NetworkScreenView::kScreenId);
  EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());

  EXPECT_CALL(*mock_network_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_demo_preferences_screen_, ShowImpl()).Times(1);

  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);

  CheckCurrentScreen(DemoPreferencesScreenView::kScreenId);
  EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
  EXPECT_CALL(*mock_demo_preferences_screen_, HideImpl()).Times(1);

  EXPECT_CALL(*mock_update_screen_, ShowImpl()).Times(1);
  mock_demo_preferences_screen_->ExitScreen(
      DemoPreferencesScreen::Result::COMPLETED);

  base::RunLoop().RunUntilIdle();

  CheckCurrentScreen(UpdateView::kScreenId);
  EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());

  EXPECT_CALL(*mock_consolidated_consent_screen_, ShowImpl()).Times(1);
  EXPECT_CALL(*mock_update_screen_, HideImpl()).Times(1);
  mock_update_screen_->RunExit(UpdateScreen::Result::UPDATE_NOT_REQUIRED);

  EXPECT_CALL(*mock_consolidated_consent_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_auto_enrollment_check_screen_, ShowImpl()).Times(0);
  EXPECT_CALL(*mock_demo_setup_screen_, ShowImpl()).Times(1);
  mock_consolidated_consent_screen_->ExitScreen(
      ConsolidatedConsentScreen::Result::ACCEPTED_DEMO_ONLINE);

  EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());

  EXPECT_CALL(*mock_auto_enrollment_check_screen_, HideImpl()).Times(0);

  CheckCurrentScreen(DemoSetupScreenView::kScreenId);
  EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());

  test::LockDemoDeviceInstallAttributes();
  mock_demo_setup_screen_->ExitScreen(DemoSetupScreen::Result::kCompleted);

  EXPECT_TRUE(StartupUtils::IsOobeCompleted(
      CHECK_DEREF(g_browser_process->local_state())));
  EXPECT_TRUE(ExistingUserController::current_controller());
  EXPECT_FALSE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
}

IN_PROC_BROWSER_TEST_F(WizardControllerDemoSetupTest, DemoSetupCanceled) {
  CheckCurrentScreen(WelcomeView::kScreenId);
  EXPECT_FALSE(DemoSetupController::IsOobeDemoSetupFlowInProgress());

  EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_network_screen_, ShowImpl()).Times(1);

  WizardController::default_controller()->StartDemoModeSetup();

  CheckCurrentScreen(NetworkScreenView::kScreenId);
  EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());

  EXPECT_CALL(*mock_network_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_demo_preferences_screen_, ShowImpl()).Times(1);

  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);

  CheckCurrentScreen(DemoPreferencesScreenView::kScreenId);
  EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());

  EXPECT_CALL(*mock_demo_preferences_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_update_screen_, ShowImpl()).Times(1);

  mock_demo_preferences_screen_->ExitScreen(
      DemoPreferencesScreen::Result::COMPLETED);

  CheckCurrentScreen(UpdateView::kScreenId);
  EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());

  EXPECT_CALL(*mock_update_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_consolidated_consent_screen_, ShowImpl()).Times(1);
  mock_update_screen_->RunExit(UpdateScreen::Result::UPDATE_NOT_REQUIRED);

  CheckCurrentScreen(ConsolidatedConsentScreenView::kScreenId);
  EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());

  EXPECT_CALL(*mock_consolidated_consent_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_auto_enrollment_check_screen_, ShowImpl()).Times(0);
  EXPECT_CALL(*mock_demo_setup_screen_, ShowImpl()).Times(1);

  mock_consolidated_consent_screen_->ExitScreen(
      ConsolidatedConsentScreen::Result::ACCEPTED_DEMO_ONLINE);

  EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
  EXPECT_CALL(*mock_auto_enrollment_check_screen_, HideImpl()).Times(0);

  CheckCurrentScreen(DemoSetupScreenView::kScreenId);
  EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());

  EXPECT_CALL(*mock_demo_setup_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_welcome_screen_, ShowImpl()).Times(1);

  mock_demo_setup_screen_->ExitScreen(DemoSetupScreen::Result::kCanceled);

  CheckCurrentScreen(WelcomeView::kScreenId);
  EXPECT_FALSE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
  EXPECT_FALSE(StartupUtils::IsOobeCompleted(
      CHECK_DEREF(g_browser_process->local_state())));
}

IN_PROC_BROWSER_TEST_F(WizardControllerDemoSetupTest, DemoPreferencesCanceled) {
  CheckCurrentScreen(WelcomeView::kScreenId);
  EXPECT_FALSE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
  SkipToScreen(DemoPreferencesScreenView::kScreenId,
               mock_demo_preferences_screen_.get());

  CheckCurrentScreen(DemoPreferencesScreenView::kScreenId);
  EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());

  EXPECT_CALL(*mock_demo_preferences_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_network_screen_, ShowImpl()).Times(1);

  mock_demo_preferences_screen_->ExitScreen(
      DemoPreferencesScreen::Result::CANCELED);

  CheckCurrentScreen(NetworkScreenView::kScreenId);

  EXPECT_CALL(*mock_network_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_welcome_screen_, ShowImpl()).Times(1);

  mock_network_screen_->ExitScreen(NetworkScreen::Result::BACK);

  EXPECT_FALSE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
}

IN_PROC_BROWSER_TEST_F(WizardControllerDemoSetupTest, NetworkBackPressed) {
  CheckCurrentScreen(WelcomeView::kScreenId);
  EXPECT_FALSE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
  SkipToScreen(NetworkScreenView::kScreenId, mock_network_screen_.get());

  CheckCurrentScreen(NetworkScreenView::kScreenId);
  EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());

  EXPECT_CALL(*mock_network_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_welcome_screen_, ShowImpl()).Times(1);

  mock_network_screen_->ExitScreen(NetworkScreen::Result::BACK);

  CheckCurrentScreen(WelcomeView::kScreenId);
  EXPECT_FALSE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
}

class WizardControllerOobeResumeTest : public WizardControllerTest {
 public:
  WizardControllerOobeResumeTest(const WizardControllerOobeResumeTest&) =
      delete;
  WizardControllerOobeResumeTest& operator=(
      const WizardControllerOobeResumeTest&) = delete;

 protected:
  WizardControllerOobeResumeTest() {
    fake_statistics_provider_.SetMachineStatistic(system::kSerialNumberKey,
                                                  "test");
    fake_statistics_provider_.SetMachineStatistic(system::kActivateDateKey,
                                                  "2000-01");
    fake_statistics_provider_.SetVpdStatus(
        system::StatisticsProvider::VpdStatus::kValid);
    // Make all requests to DMServer fail with net::ERR_CONNECTION_REFUSED.
    // (This prevents unexpected successful enrollment checks unless mocked
    // otherwise)
    test_url_loader_factory_.SetInterceptor(base::BindLambdaForTesting(
        [&](const network::ResourceRequest& request) {
          if (request.url.spec().starts_with(kDMServerURLPrefix)) {
            test_url_loader_factory_.AddResponse(
                request.url, network::mojom::URLResponseHead::New(),
                std::string(),
                network::URLLoaderCompletionStatus(
                    net::ERR_CONNECTION_REFUSED));
          }
        }));
  }

  void PreRunTestOnMainThread() override {
    // Make sure that OOBE is run as an "official" build.
    LoginDisplayHost::default_host()->GetWizardContext()->is_branded_build =
        true;
  }

  // WizardControllerTest:
  void SetUpOnMainThread() override {
    WizardControllerTest::SetUpOnMainThread();

    ShillManagerClient::Get()->GetTestInterface()->SetupDefaultEnvironment();

    WizardController* wizard_controller =
        WizardController::default_controller();
    WaitForOobeUI();
    wizard_controller->SetSharedURLLoaderFactoryForTesting(
        test_url_loader_factory_.GetSafeWeakWrapper());
    SystemLocationProvider::GetInstance()
        ->GetLocationProviderForTesting()
        ->GetLocationFetcherForTesting()
        ->SetSharedUrlLoaderFactoryForTesting(
            test_url_loader_factory_.GetSafeWeakWrapper());

    mock_auto_enrollment_check_screen_view_ =
        std::make_unique<MockAutoEnrollmentCheckScreenView>();
    mock_auto_enrollment_check_screen_ = MockScreen(
        std::make_unique<testing::NiceMock<MockAutoEnrollmentCheckScreen>>(
            g_browser_process->local_state(),
            mock_auto_enrollment_check_screen_view_.get()->AsWeakPtr(),
            GetErrorScreen(),
            base::BindRepeating(
                &WizardController::OnAutoEnrollmentCheckScreenExit,
                base::Unretained(wizard_controller))));
    LoginDisplayHost::default_host()->GetWizardContext()->is_branded_build =
        true;
  }

  void TearDownOnMainThread() override {
    mock_auto_enrollment_check_screen_ = nullptr;
    mock_auto_enrollment_check_screen_view_.reset();

    test_url_loader_factory_.ClearResponses();
    WizardControllerTest::TearDownOnMainThread();
  }

  void SetUpCommandLine(base::CommandLine* command_line) override {
    WizardControllerTest::SetUpCommandLine(command_line);

    // Ensure unified state determination logic is used for enrollment checks
    command_line->AppendSwitchASCII(
        switches::kEnterpriseEnableUnifiedStateDetermination,
        policy::AutoEnrollmentTypeChecker::kUnifiedStateDeterminationAlways);
  }

  static policy::AutoEnrollmentController* auto_enrollment_controller() {
    return WizardController::default_controller()
        ->GetAutoEnrollmentController();
  }

  static void WaitForAutoEnrollmentState(policy::AutoEnrollmentState state) {
    base::RunLoop loop;
    base::CallbackListSubscription progress_subscription =
        auto_enrollment_controller()->RegisterProgressCallback(
            base::BindRepeating(&QuitLoopOnAutoEnrollmentProgress, state,
                                &loop));
    loop.Run();
  }

  void ProgressUntilAutoEnrollmentCheckScreen() {
    // Start OOBE at Welcome Screen
    WizardController::default_controller()->AdvanceToScreen(
        WelcomeView::kScreenId);
    test::WaitForWelcomeScreen();

    test::TapWelcomeNext();

    test::WaitForNetworkSelectionScreen();
    test::TapNetworkSelectionNext();

    test::WaitForUpdateScreen();
    test::ExitUpdateScreenNoUpdate();

    CheckCurrentScreen(AutoEnrollmentCheckScreenView::kScreenId);
    mock_auto_enrollment_check_screen_->RealShow();
  }

  std::unique_ptr<MockAutoEnrollmentCheckScreenView>
      mock_auto_enrollment_check_screen_view_;
  raw_ptr<testing::NiceMock<MockAutoEnrollmentCheckScreen>, DanglingUntriaged>
      mock_auto_enrollment_check_screen_ = nullptr;

  system::ScopedFakeStatisticsProvider fake_statistics_provider_;
  network::TestURLLoaderFactory test_url_loader_factory_;

  DeviceStateMixin device_state_{&mixin_host_,
                                 DeviceStateMixin::State::BEFORE_OOBE};
};

// PRE-Test: Run OOBE until after AutoEnrollmentCheck determines no enrollment,
// landing on UserCreation screen.
IN_PROC_BROWSER_TEST_F(WizardControllerOobeResumeTest,
                       PRE_ControlFlowResumeInterruptedOobe) {
  ScopedEnrollmentStateFetcherFactory fetcher_factory(
      auto_enrollment_controller());
  ProgressUntilAutoEnrollmentCheckScreen();
  fetcher_factory.WaitUntilEnrollmentStateFetcherCreated();

  fetcher_factory.ReportEnrollmentState(
      policy::AutoEnrollmentResult::kNoEnrollment);

  CheckCurrentScreen(UserCreationView::kScreenId);
  EXPECT_EQ(policy::AutoEnrollmentResult::kNoEnrollment,
            auto_enrollment_controller()->state());
}

// Main Test: Resume OOBE after the interruption simulated in the PRE_ test.
IN_PROC_BROWSER_TEST_F(WizardControllerOobeResumeTest,
                       ControlFlowResumeInterruptedOobe) {
  WaitForOobeUI();
  CheckCurrentScreen(UpdateView::kScreenId);
}

class WizardControllerOnboardingResumeTest : public WizardControllerTest {
 protected:
  void SetUpOnMainThread() override {
    cryptohome_mixin_.ApplyAuthConfigIfUserExists(
        user_, test::UserAuthConfig::Create(test::kDefaultAuthSetup));
    WizardControllerTest::SetUpOnMainThread();
  }

  DeviceStateMixin device_state_{
      &mixin_host_,
      DeviceStateMixin::State::OOBE_COMPLETED_PERMANENTLY_UNOWNED};
  FakeGaiaMixin gaia_mixin_{&mixin_host_};
  CryptohomeMixin cryptohome_mixin_{&mixin_host_};
  LoginManagerMixin login_mixin_{&mixin_host_, LoginManagerMixin::UserList(),
                                 &gaia_mixin_};
  AccountId user_{AccountId::FromUserEmailGaiaId(test::kTestEmail,
                                                 GaiaId(test::kTestGaiaId))};
};

IN_PROC_BROWSER_TEST_F(WizardControllerOnboardingResumeTest,
                       PRE_ControlFlowResumeInterruptedOnboarding) {
  LoginDisplayHost::default_host()->GetWizardContext()->is_branded_build = true;
  OobeScreenWaiter(UserCreationView::kScreenId).Wait();
  LoginManagerMixin::TestUserInfo test_user(user_);
  login_mixin_.LoginWithDefaultContext(test_user);
  OobeScreenExitWaiter(UserCreationView::kScreenId).Wait();
  WizardController::default_controller()->AdvanceToScreen(
      MarketingOptInScreenView::kScreenId);
  OobeScreenWaiter(MarketingOptInScreenView::kScreenId).Wait();
}

IN_PROC_BROWSER_TEST_F(WizardControllerOnboardingResumeTest,
                       ControlFlowResumeInterruptedOnboarding) {
  LoginDisplayHost::default_host()->GetWizardContext()->is_branded_build = true;
  login_mixin_.LoginAsNewRegularUser();
  OobeScreenWaiter(MarketingOptInScreenView::kScreenId).Wait();
}

class WizardControllerCellularFirstTest : public WizardControllerFlowTest {
 public:
  WizardControllerCellularFirstTest(const WizardControllerCellularFirstTest&) =
      delete;
  WizardControllerCellularFirstTest& operator=(
      const WizardControllerCellularFirstTest&) = delete;

 protected:
  WizardControllerCellularFirstTest() = default;

  // WizardControllerFlowTest:
  void SetUpCommandLine(base::CommandLine* command_line) override {
    WizardControllerFlowTest::SetUpCommandLine(command_line);
    command_line->AppendSwitch(switches::kCellularFirst);
  }
};

IN_PROC_BROWSER_TEST_F(WizardControllerCellularFirstTest, CellularFirstFlow) {
  TestControlFlowMain();
}

class WizardControllerOobeConfigurationTest : public WizardControllerTest {
 public:
  WizardControllerOobeConfigurationTest(
      const WizardControllerOobeConfigurationTest&) = delete;
  WizardControllerOobeConfigurationTest& operator=(
      const WizardControllerOobeConfigurationTest&) = delete;

 protected:
  WizardControllerOobeConfigurationTest() = default;

  // WizardControllerTest:
  void SetUpCommandLine(base::CommandLine* command_line) override {
    WizardControllerTest::SetUpCommandLine(command_line);

    base::FilePath configuration_file;
    ASSERT_TRUE(chromeos::test_utils::GetTestDataPath(
        "oobe_configuration", "non_empty_configuration.json",
        &configuration_file));
    command_line->AppendSwitchPath(chromeos::switches::kFakeOobeConfiguration,
                                   configuration_file);
  }
};

IN_PROC_BROWSER_TEST_F(WizardControllerOobeConfigurationTest,
                       ConfigurationIsLoaded) {
  OobeScreenWaiter(WelcomeView::kScreenId).Wait();
  WelcomeScreen* screen =
      WizardController::default_controller()->GetScreen<WelcomeScreen>();
  const base::DictValue& configuration = screen->GetConfigurationForTesting();
  EXPECT_FALSE(configuration.empty());
}

// Verifies that incomplete token-based enrollment flows (e.g. device is
// rebooted after enrollment fails) resume enrollment without issue. See
// b/336337134 for more details.
class WizardControllerEnrollmentTokenRebootTest : public WizardControllerTest {
 public:
  WizardControllerEnrollmentTokenRebootTest(
      const WizardControllerEnrollmentTokenRebootTest&) = delete;
  WizardControllerEnrollmentTokenRebootTest operator=(
      const WizardControllerEnrollmentTokenRebootTest&) = delete;

 protected:
  WizardControllerEnrollmentTokenRebootTest() = default;
  // WizardControllerTest:
  void SetUpCommandLine(base::CommandLine* command_line) override {
    WizardControllerTest::SetUpCommandLine(command_line);

    command_line->AppendSwitch(ash::switches::kRevenBranding);
    base::FilePath configuration_file;
    ASSERT_TRUE(chromeos::test_utils::GetTestDataPath(
        "oobe_configuration", "enrollment_token_configuration.json",
        &configuration_file));
    command_line->AppendSwitchPath(chromeos::switches::kFakeOobeConfiguration,
                                   configuration_file);
  }

  void SetUpLocalStatePrefService(PrefService* local_state) override {
    WizardControllerTest::SetUpLocalStatePrefService(local_state);

    // Simulate device having previously gone through state determination.
    base::DictValue device_state;
    device_state.Set(
        policy::kDeviceStateMode,
        base::Value(policy::kDeviceStateInitialModeTokenEnrollment));
    local_state->SetDict(ash::prefs::kServerBackedDeviceState,
                         std::move(device_state));
  }
};

IN_PROC_BROWSER_TEST_F(WizardControllerEnrollmentTokenRebootTest,
                       ConfigurationIsLoaded) {
  OobeScreenWaiter(WelcomeView::kScreenId).Wait();
  WelcomeScreen* screen =
      WizardController::default_controller()->GetScreen<WelcomeScreen>();
  const base::DictValue& configuration = screen->GetConfigurationForTesting();
  EXPECT_FALSE(configuration.empty());
}

class WizardControllerRemoteActivityNotificationTest
    : public WizardControllerTest {
 public:
  WizardControllerRemoteActivityNotificationTest(
      const WizardControllerRemoteActivityNotificationTest&) = delete;
  WizardControllerRemoteActivityNotificationTest& operator=(
      const WizardControllerRemoteActivityNotificationTest&) = delete;

 protected:
  WizardControllerRemoteActivityNotificationTest() = default;
  ~WizardControllerRemoteActivityNotificationTest() override = default;

  // WizardControllerTest:
  void SetUpInProcessBrowserTestFixture() override {
    WizardControllerTest::SetUpInProcessBrowserTestFixture();
    login_manager_mixin_.AppendRegularUsers(1);
  }

  void SetUpLocalStatePrefService(PrefService* local_state) override {
    WizardControllerTest::SetUpLocalStatePrefService(local_state);
    StartupUtils::MarkOobeCompleted(CHECK_DEREF(local_state));
  }

  void SetPref(const std::string& pref, bool value) {
    local_state()->SetBoolean(pref, value);
  }

  bool GetPref(const std::string& pref) {
    return local_state()->GetBoolean(pref);
  }

  PrefService* local_state() { return g_browser_process->local_state(); }

  bool IsCurrentScreen(OobeScreenId screen) {
    BaseScreen* current_screen =
        WizardController::default_controller()->current_screen();
    const std::string actual_screen =
        current_screen ? current_screen->screen_id().name : "nullptr";
    const std::string expected_screen = screen.name;
    return actual_screen == expected_screen;
  }

  bool IsRemoteActivityScreenVisible() {
    return LoginScreenTestApi::IsOobeDialogVisible() &&
           IsCurrentScreen(RemoteActivityNotificationView::kScreenId);
  }

 private:
  LoginManagerMixin login_manager_mixin_{&mixin_host_};
};

class RemoteActivityNotificationTestWhenPrefIsSet
    : public WizardControllerRemoteActivityNotificationTest {
  void SetUpLocalStatePrefService(PrefService* local_state) override {
    WizardControllerRemoteActivityNotificationTest::SetUpLocalStatePrefService(
        local_state);
    local_state->SetBoolean(ash::prefs::kRemoteAdminWasPresent, true);
  }
};

class RemoteActivityNotificationTestWhenPrefIsNotSet
    : public WizardControllerRemoteActivityNotificationTest {
  void SetUpLocalStatePrefService(PrefService* local_state) override {
    WizardControllerRemoteActivityNotificationTest::SetUpLocalStatePrefService(
        local_state);
    local_state->SetBoolean(ash::prefs::kRemoteAdminWasPresent, false);
  }
};

IN_PROC_BROWSER_TEST_F(RemoteActivityNotificationTestWhenPrefIsSet,
                       ShouldSetPrefOnRemoteActivityScreenExit) {
  OobeScreenWaiter(RemoteActivityNotificationView::kScreenId).Wait();
  CheckCurrentScreen(RemoteActivityNotificationView::kScreenId);
  ASSERT_TRUE(GetPref(ash::prefs::kRemoteAdminWasPresent));
  ASSERT_TRUE(LoginScreenTestApi::IsOobeDialogVisible());

  test::OobeJS().ClickOnPath({"remote-activity-notification", "cancelButton"});

  test::TestPredicateWaiter(base::BindRepeating([]() {
    return !LoginScreenTestApi::IsOobeDialogVisible();
  })).Wait();
  EXPECT_FALSE(IsRemoteActivityScreenVisible());
  EXPECT_FALSE(GetPref(ash::prefs::kRemoteAdminWasPresent));
}

IN_PROC_BROWSER_TEST_F(RemoteActivityNotificationTestWhenPrefIsNotSet,
                       NotificationShouldNotBeVisible) {
  ASSERT_FALSE(GetPref(ash::prefs::kRemoteAdminWasPresent));

  EXPECT_FALSE(IsRemoteActivityScreenVisible());
}

class RemoteActivityNotificationTestWhenNoLoginAccountPresentTest
    : public WizardControllerTest {
 public:
  // WizardControllerTest:
  void SetUpInProcessBrowserTestFixture() override {
    WizardControllerTest::SetUpInProcessBrowserTestFixture();
  }

  void SetUpOnMainThread() override {
    WizardControllerTest::SetUpOnMainThread();
  }

  PrefService* local_state() { return g_browser_process->local_state(); }

 protected:
  DeviceStateMixin device_state_{&mixin_host_,
                                 DeviceStateMixin::State::BEFORE_OOBE};
  FakeGaiaMixin gaia_mixin_{&mixin_host_};
  LoginManagerMixin login_mixin_{&mixin_host_, LoginManagerMixin::UserList(),
                                 &gaia_mixin_};
};

IN_PROC_BROWSER_TEST_F(
    RemoteActivityNotificationTestWhenNoLoginAccountPresentTest,
    PRE_ShouldGoToPreviousScreenWhenNotificationIsDismissed) {
  CheckCurrentScreen(UserCreationView::kScreenId);

  local_state()->SetBoolean(ash::prefs::kRemoteAdminWasPresent, true);
}

IN_PROC_BROWSER_TEST_F(
    RemoteActivityNotificationTestWhenNoLoginAccountPresentTest,
    ShouldGoToPreviousScreenWhenNotificationIsDismissed) {
  OobeScreenWaiter(RemoteActivityNotificationView::kScreenId).Wait();
  CheckCurrentScreen(RemoteActivityNotificationView::kScreenId);

  test::OobeJS().ClickOnPath({"remote-activity-notification", "cancelButton"});

  OobeScreenWaiter(UserCreationView::kScreenId).Wait();
  CheckCurrentScreen(UserCreationView::kScreenId);
}

class WizardControllerThemeSelectionTest : public WizardControllerTest {
 protected:
  FakeGaiaMixin gaia_mixin_{&mixin_host_};
  LoginManagerMixin login_mixin_{&mixin_host_, LoginManagerMixin::UserList(),
                                 &gaia_mixin_};
};

IN_PROC_BROWSER_TEST_F(WizardControllerThemeSelectionTest,
                       TransitionToMarketingOptIn) {
  LoginDisplayHost::default_host()
      ->GetWizardContextForTesting()
      ->skip_choobe_for_tests = true;

  LoginDisplayHost::default_host()->GetWizardContext()->is_branded_build = true;
  login_mixin_.LoginAsNewRegularUser();
  WizardController::default_controller()->AdvanceToScreen(
      ThemeSelectionScreenView::kScreenId);
  test::OobeJS().ClickOnPath({"theme-selection", "nextButton"});
  OobeScreenWaiter(MarketingOptInScreenView::kScreenId).Wait();
}

IN_PROC_BROWSER_TEST_F(WizardControllerThemeSelectionTest,
                       TransitionToThemeSelection) {
  LoginDisplayHost::default_host()
      ->GetWizardContextForTesting()
      ->skip_choobe_for_tests = true;

  login_mixin_.LoginAsNewRegularUser();
  if (features::IsOobeDisplaySizeEnabled()) {
    WizardController::default_controller()->AdvanceToScreen(
        DisplaySizeScreenView::kScreenId);
    test::OobeJS().ClickOnPath({"display-size", "nextButton"});
  } else if (features::IsOobeTouchpadScrollEnabled()) {
    WizardController::default_controller()->AdvanceToScreen(
        TouchpadScrollScreenView::kScreenId);
    test::OobeJS().ClickOnPath({"touchpad-scroll", "nextButton"});
  } else {
    WizardController::default_controller()->AdvanceToScreen(
        GestureNavigationScreenView::kScreenId);
  }
  OobeScreenWaiter(ThemeSelectionScreenView::kScreenId).Wait();
}

class GaiaInfoScreenForEnterpriseEnrollmentTest : public WizardControllerTest {
 private:
  DeviceStateMixin device_state_{
      &mixin_host_, DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED};
};

IN_PROC_BROWSER_TEST_F(GaiaInfoScreenForEnterpriseEnrollmentTest,
                       SkippingGaiaInfo) {
  WizardController::default_controller()->AdvanceToScreen(
      GaiaInfoScreenView::kScreenId);
  OobeScreenWaiter(GaiaView::kScreenId).Wait();
}

IN_PROC_BROWSER_TEST_F(WizardControllerTest, TransitionToGaiaInfo) {
  WaitForOobeUI();
  WizardController::default_controller()->AdvanceToScreen(
      UserCreationView::kScreenId);
  test::OobeJS().ClickOnPath({"user-creation", "selfButton"});
  test::OobeJS().ClickOnPath({"user-creation", "nextButton"});
  test::WaitForConsumerUpdateScreen();
  test::ExitConsumerUpdateScreenNoUpdate();
  OobeScreenWaiter(GaiaInfoScreenView::kScreenId).Wait();
}

IN_PROC_BROWSER_TEST_F(WizardControllerTest, SkipGaiaInfoForChildAccount) {
  WaitForOobeUI();
  WizardController::default_controller()->AdvanceToScreen(
      UserCreationView::kScreenId);
  OobeScreenWaiter(UserCreationView::kScreenId).Wait();

  test::OobeJS().ClickOnPath({"user-creation", "childButton"});
  test::OobeJS().ClickOnPath({"user-creation", "nextButton"});
  test::OobeJS().ClickOnPath({"user-creation", "childAccountButton"});
  test::OobeJS().ClickOnPath({"user-creation", "childSetupNextButton"});
  test::WaitForConsumerUpdateScreen();
  test::ExitConsumerUpdateScreenNoUpdate();
  OobeScreenWaiter(AddChildScreenView::kScreenId).Wait();
}

// Showing the system tray on the kiosk splash screen is a source of exploits.
// Ensure we don't.
IN_PROC_BROWSER_TEST_F(WizardControllerTest, SystemTrayShouldBeDisabledOnKioskSplashScreen) {
  WaitForOobeUI();
  WizardController::default_controller()->AdvanceToScreen(
      AppLaunchSplashScreenView::kScreenId);
  OobeScreenWaiter(AppLaunchSplashScreenView::kScreenId).Wait();

  ASSERT_FALSE(Shell::GetPrimaryRootWindowController()->IsSystemTrayVisible());
}

class WizardControllerGaiaTest : public WizardControllerTest {
 protected:
  FakeGaiaMixin fake_gaia_{&mixin_host_};
  DeviceStateMixin device_state_{
      &mixin_host_, DeviceStateMixin::State::OOBE_COMPLETED_UNOWNED};
};

IN_PROC_BROWSER_TEST_F(WizardControllerGaiaTest, GoBackToGaiaInfo) {
  LoginDisplayHost::default_host()
      ->GetWizardContext()
      ->is_user_creation_enabled = true;
  WaitForOobeUI();
  WizardController::default_controller()->AdvanceToScreen(
      GaiaInfoScreenView::kScreenId);
  test::OobeJS().ClickOnPath({"gaia-info", "nextButton"});
  OobeScreenWaiter(GaiaView::kScreenId).Wait();

  test::OobeJS().ClickOnPath(
      {"gaia-signin", "signin-frame-dialog", "signin-back-button"});

  if (features::IsOobeSoftwareUpdateEnabled()) {
    OobeScreenWaiter(UserCreationView::kScreenId).Wait();
  } else {
    OobeScreenWaiter(GaiaInfoScreenView::kScreenId).Wait();
  }
}

IN_PROC_BROWSER_TEST_F(WizardControllerGaiaTest,
                       GoBackSkippingGaiaInfoInAddPersonFlow) {
  LoginDisplayHost::default_host()
      ->GetWizardContext()
      ->is_user_creation_enabled = true;
  LoginDisplayHost::default_host()->GetWizardContext()->is_add_person_flow =
      true;
  WaitForOobeUI();

  WizardController::default_controller()->AdvanceToScreen(
      GaiaInfoScreenView::kScreenId);
  OobeScreenWaiter(GaiaView::kScreenId).Wait();

  test::OobeJS().ClickOnPath(
      {"gaia-signin", "signin-frame-dialog", "signin-back-button"});
  OobeScreenWaiter(UserCreationView::kScreenId).Wait();
}

IN_PROC_BROWSER_TEST_F(WizardControllerGaiaTest, TransitionFromGaiaInfo) {
  WaitForOobeUI();
  WizardController::default_controller()->AdvanceToScreen(
      GaiaInfoScreenView::kScreenId);
  test::OobeJS().ClickOnPath({"gaia-info", "nextButton"});
  OobeScreenWaiter(GaiaView::kScreenId).Wait();
}

class GoingBackFromGaiaScreenInChildFlowTest
    : public WizardControllerTest,
      public testing::WithParamInterface<std::tuple<bool, std::string>> {
  FakeGaiaMixin fake_gaia_{&mixin_host_};
  DeviceStateMixin device_state_{
      &mixin_host_, DeviceStateMixin::State::OOBE_COMPLETED_UNOWNED};
};

IN_PROC_BROWSER_TEST_P(GoingBackFromGaiaScreenInChildFlowTest,
                       SkippingGaiaInfoScreen) {
  LoginDisplayHost::default_host()->GetWizardContext()->is_add_person_flow =
      std::get<0>(GetParam());

  WizardController::default_controller()->AdvanceToScreen(
      UserCreationView::kScreenId);
  OobeScreenWaiter(UserCreationView::kScreenId).Wait();

  test::OobeJS().ClickOnPath({"user-creation", "childButton"});
  test::OobeJS().ClickOnPath({"user-creation", "nextButton"});
  test::OobeJS().ClickOnPath({"user-creation", "childAccountButton"});
  test::OobeJS().ClickOnPath({"user-creation", "childSetupNextButton"});

  if (!LoginDisplayHost::default_host()
           ->GetWizardContext()
           ->is_add_person_flow) {
    test::WaitForConsumerUpdateScreen();
    test::ExitConsumerUpdateScreenNoUpdate();
  }

  OobeScreenWaiter(AddChildScreenView::kScreenId).Wait();
  test::OobeJS().ClickOnPath({"add-child", std::get<1>(GetParam())});
  test::OobeJS().ClickOnPath({"add-child", "childNextButton"});

  OobeScreenWaiter(GaiaView::kScreenId).Wait();

  test::OobeJS().ClickOnPath(
      {"gaia-signin", "signin-frame-dialog", "signin-back-button"});

  OobeScreenWaiter(AddChildScreenView::kScreenId).Wait();
}

INSTANTIATE_TEST_SUITE_P(
    All,
    GoingBackFromGaiaScreenInChildFlowTest,
    testing::Values(std::make_tuple(true, "childCreateButton"),
                    std::make_tuple(true, "childSignInButton"),
                    std::make_tuple(false, "childCreateButton"),
                    std::make_tuple(false, "childSignInButton")));

class WizardControllerFlowWithAutoEnrollmentCheckForcedTest
    : public WizardControllerFlowTest {
 public:
  WizardControllerFlowWithAutoEnrollmentCheckForcedTest() {
    scoped_feature_list_.InitAndEnableFeature(
        features::kOobeAutoEnrollmentCheckForced);
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

IN_PROC_BROWSER_TEST_F(WizardControllerFlowWithAutoEnrollmentCheckForcedTest,
                       OobeMarkedCompleted) {
  WaitForOobeUI();
  CheckCurrentScreen(WelcomeView::kScreenId);
  EXPECT_CALL(*mock_update_screen_, ShowImpl()).Times(0);
  EXPECT_CALL(*mock_network_screen_, ShowImpl()).Times(1);
  EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
  mock_welcome_screen_->ExitScreen(WelcomeScreen::Result::kNext);

  CheckCurrentScreen(NetworkScreenView::kScreenId);
  EXPECT_CALL(*mock_network_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_update_screen_, ShowImpl()).Times(1);
  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);

  // Let update screen smooth time process (time = 0ms).
  content::RunAllPendingInMessageLoop();
  CheckCurrentScreen(UpdateView::kScreenId);

  EXPECT_CALL(*mock_update_screen_, HideImpl()).Times(1);
  mock_update_screen_->RunExit(UpdateScreen::Result::UPDATE_NOT_REQUIRED);
  base::RunLoop().RunUntilIdle();

  OobeScreenWaiter(UserCreationView::kScreenId).Wait();
  EXPECT_TRUE(StartupUtils::IsOobeCompleted(
      CHECK_DEREF(g_browser_process->local_state())));
}

IN_PROC_BROWSER_TEST_F(WizardControllerFlowWithAutoEnrollmentCheckForcedTest,
                       OobeNotMarkedCompleted) {
  WaitForOobeUI();
  LoginDisplayHost::default_host()
      ->GetWizardContext()
      ->skip_auto_enrollment_check_for_tests = true;
  CheckCurrentScreen(WelcomeView::kScreenId);
  EXPECT_CALL(*mock_update_screen_, ShowImpl()).Times(0);
  EXPECT_CALL(*mock_network_screen_, ShowImpl()).Times(1);
  EXPECT_CALL(*mock_welcome_screen_, HideImpl()).Times(1);
  mock_welcome_screen_->ExitScreen(WelcomeScreen::Result::kNext);

  CheckCurrentScreen(NetworkScreenView::kScreenId);
  EXPECT_CALL(*mock_network_screen_, HideImpl()).Times(1);
  EXPECT_CALL(*mock_update_screen_, ShowImpl()).Times(1);
  mock_network_screen_->ExitScreen(NetworkScreen::Result::CONNECTED);

  // Let update screen smooth time process (time = 0ms).
  content::RunAllPendingInMessageLoop();
  CheckCurrentScreen(UpdateView::kScreenId);

  EXPECT_CALL(*mock_update_screen_, HideImpl()).Times(1);
  mock_update_screen_->RunExit(UpdateScreen::Result::UPDATE_NOT_REQUIRED);
  base::RunLoop().RunUntilIdle();

  OobeScreenWaiter(SignInFatalErrorView::kScreenId).Wait();
  EXPECT_FALSE(StartupUtils::IsOobeCompleted(
      CHECK_DEREF(g_browser_process->local_state())));
}

IN_PROC_BROWSER_TEST_F(WizardControllerFlowWithAutoEnrollmentCheckForcedTest,
                       ShowFatalErrorOnGaiaAdvanceWhenOobeIncomplete) {
  WaitForOobeUI();

  WizardController::default_controller()->AdvanceToScreen(GaiaView::kScreenId);

  OobeScreenWaiter(SignInFatalErrorView::kScreenId).Wait();
}

IN_PROC_BROWSER_TEST_F(WizardControllerFlowWithAutoEnrollmentCheckForcedTest,
                       NoFatalErrorOnGaiaAdvanceWhenOobeComplete) {
  StartupUtils::MarkOobeCompleted(
      CHECK_DEREF(g_browser_process->local_state()));
  WaitForOobeUI();

  WizardController::default_controller()->AdvanceToScreen(GaiaView::kScreenId);

  OobeScreenWaiter(GaiaView::kScreenId).Wait();
}

// TODO(nkostylev): Add test for WebUI accelerators http://crosbug.com/22571

// TODO(merkulova): Add tests for bluetooth HID detection screen variations when
// UI and logic is ready. http://crbug.com/40205279

// TODO(khmel): Add tests for ARC OptIn flow.
// http://crbug.com/41277995

// TODO(fukino): Add tests for encryption migration UI.
// http://crbug.com/41309925

// TODO(alemate): Add tests for Sync Consent UI.

// TODO(rsgingerrs): Add tests for Recommend Apps UI.

// TODO(alemate): Add tests for Marketing Opt-In.

// TODO(khorimoto): Add tests for MultiDevice Setup UI.

}  // namespace ash
