blob: a95560e41f66b45fcab5a0ad6360b7f7f774fe81 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include "ash/public/cpp/ash_features.h"
#include "ash/public/cpp/ash_pref_names.h"
#include "ash/public/cpp/ash_switches.h"
#include "ash/public/cpp/login_screen_test_api.h"
#include "ash/public/cpp/test/shell_test_api.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/optional.h"
#include "base/test/scoped_feature_list.h"
#include "base/values.h"
#include "build/branding_buildflags.h"
#include "build/buildflag.h"
#include "chrome/browser/chrome_browser_main.h"
#include "chrome/browser/chrome_browser_main_extra_parts.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/chromeos/arc/session/arc_service_launcher.h"
#include "chrome/browser/chromeos/arc/session/arc_session_manager.h"
#include "chrome/browser/chromeos/arc/test/test_arc_session_manager.h"
#include "chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.h"
#include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h"
#include "chrome/browser/chromeos/login/screens/recommend_apps/recommend_apps_fetcher.h"
#include "chrome/browser/chromeos/login/screens/recommend_apps/recommend_apps_fetcher_delegate.h"
#include "chrome/browser/chromeos/login/screens/recommend_apps/scoped_test_recommend_apps_fetcher_factory.h"
#include "chrome/browser/chromeos/login/test/device_state_mixin.h"
#include "chrome/browser/chromeos/login/test/embedded_test_server_mixin.h"
#include "chrome/browser/chromeos/login/test/enrollment_ui_mixin.h"
#include "chrome/browser/chromeos/login/test/fake_eula_mixin.h"
#include "chrome/browser/chromeos/login/test/fake_gaia_mixin.h"
#include "chrome/browser/chromeos/login/test/js_checker.h"
#include "chrome/browser/chromeos/login/test/local_policy_test_server_mixin.h"
#include "chrome/browser/chromeos/login/test/login_manager_mixin.h"
#include "chrome/browser/chromeos/login/test/oobe_base_test.h"
#include "chrome/browser/chromeos/login/test/oobe_screen_exit_waiter.h"
#include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
#include "chrome/browser/chromeos/login/test/oobe_screens_utils.h"
#include "chrome/browser/chromeos/login/test/test_predicate_waiter.h"
#include "chrome/browser/chromeos/login/ui/login_display_host.h"
#include "chrome/browser/chromeos/login/wizard_controller.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/webui/chromeos/login/app_downloading_screen_handler.h"
#include "chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.h"
#include "chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h"
#include "chrome/browser/ui/webui/chromeos/login/gesture_navigation_screen_handler.h"
#include "chrome/browser/ui/webui/chromeos/login/marketing_opt_in_screen_handler.h"
#include "chrome/browser/ui/webui/chromeos/login/recommend_apps_screen_handler.h"
#include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
#include "chrome/browser/ui/webui/chromeos/login/terms_of_service_screen_handler.h"
#include "chromeos/assistant/buildflags.h"
#include "chromeos/attestation/attestation_flow_utils.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/constants/chromeos_switches.h"
#include "chromeos/dbus/update_engine_client.h"
#include "chromeos/system/fake_statistics_provider.h"
#include "components/arc/arc_service_manager.h"
#include "components/arc/arc_util.h"
#include "components/arc/session/arc_session_runner.h"
#include "components/arc/test/fake_arc_session.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "ui/aura/window.h"
#include "ui/aura/window_observer.h"
#include "ui/display/display_switches.h"
using net::test_server::BasicHttpResponse;
using net::test_server::HttpRequest;
using net::test_server::HttpResponse;
namespace chromeos {
namespace {
enum class ArcState { kNotAvailable, kAcceptTerms };
std::string ArcStateToString(ArcState arc_state) {
switch (arc_state) {
case ArcState::kNotAvailable:
return "not-available";
case ArcState::kAcceptTerms:
return "accept-terms";
}
NOTREACHED();
return "unknown";
}
void RunWelcomeScreenChecks() {
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
constexpr int kNumberOfVideosPlaying = 1;
#else
constexpr int kNumberOfVideosPlaying = 0;
#endif
test::OobeJS().ExpectVisiblePath({"connect", "welcomeScreen"});
test::OobeJS().ExpectHiddenPath({"connect", "accessibilityScreen"});
test::OobeJS().ExpectHiddenPath({"connect", "languageScreen"});
test::OobeJS().ExpectHiddenPath({"connect", "timezoneScreen"});
test::OobeJS().ExpectFocused(
{"connect", "welcomeScreen", "welcomeNextButton"});
test::OobeJS().ExpectEQ(
"(() => {let cnt = 0; for (let v of "
"$('connect').$.welcomeScreen.root.querySelectorAll('video')) "
"{ cnt += v.paused ? 0 : 1; }; return cnt; })()",
kNumberOfVideosPlaying);
EXPECT_TRUE(ash::LoginScreenTestApi::IsShutdownButtonShown());
EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
EXPECT_FALSE(ash::LoginScreenTestApi::IsAddUserButtonShown());
}
void RunNetworkSelectionScreenChecks() {
test::OobeJS().ExpectEnabledPath({"network-selection", "nextButton"});
EXPECT_TRUE(ash::LoginScreenTestApi::IsShutdownButtonShown());
EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
EXPECT_FALSE(ash::LoginScreenTestApi::IsAddUserButtonShown());
test::OobeJS().CreateFocusWaiter({"network-selection", "nextButton"})->Wait();
}
void RunEulaScreenChecks() {
// Wait for actual EULA to appear.
test::OobeJS()
.CreateVisibilityWaiter(true, {"oobe-eula-md", "eulaDialog"})
->Wait();
test::OobeJS().ExpectEnabledPath({"oobe-eula-md", "acceptButton"});
test::OobeJS().CreateFocusWaiter({"oobe-eula-md", "crosEulaFrame"})->Wait();
EXPECT_TRUE(ash::LoginScreenTestApi::IsShutdownButtonShown);
EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
EXPECT_FALSE(ash::LoginScreenTestApi::IsAddUserButtonShown());
}
void WaitForGaiaSignInScreen(bool arc_available) {
OobeScreenWaiter(GaiaView::kScreenId).Wait();
// Arc terms of service content gets preloaded when GAIA screen is shown,
// wait for the preload to finish before proceeding - requesting reload
// (which may happen when ARC terms of service screen is show) before the
// preload is done may cause flaky load failures.
// TODO(https://crbug/com/959902): Fix ARC terms of service screen to better
// handle this case.
if (arc_available) {
test::OobeJS()
.CreateHasClassWaiter(true, "arc-tos-loaded",
{"arc-tos", "arcTosDialog"})
->Wait();
}
LOG(INFO) << "OobeInteractiveUITest: Switched to 'gaia-signin' screen.";
}
void LogInAsRegularUser() {
LoginDisplayHost::default_host()
->GetOobeUI()
->GetView<GaiaScreenHandler>()
->ShowSigninScreenForTest(FakeGaiaMixin::kFakeUserEmail,
FakeGaiaMixin::kFakeUserPassword,
FakeGaiaMixin::kEmptyUserServices);
LOG(INFO) << "OobeInteractiveUITest: Logged in.";
}
void RunSyncConsentScreenChecks() {
SyncConsentScreen* screen = static_cast<SyncConsentScreen*>(
WizardController::default_controller()->GetScreen(
SyncConsentScreenView::kScreenId));
screen->SetProfileSyncEngineInitializedForTesting(true);
screen->OnStateChanged(nullptr);
const std::string button_name =
chromeos::features::IsSplitSettingsSyncEnabled()
? "acceptButton"
: "settingsSaveAndContinueButton";
test::OobeJS().ExpectEnabledPath({"sync-consent", button_name});
test::OobeJS().CreateFocusWaiter({"sync-consent", button_name})->Wait();
EXPECT_FALSE(ash::LoginScreenTestApi::IsShutdownButtonShown());
EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
EXPECT_FALSE(ash::LoginScreenTestApi::IsAddUserButtonShown());
}
void RunFingerprintScreenChecks() {
test::OobeJS().ExpectVisible("fingerprint-setup");
test::OobeJS().ExpectVisiblePath({"fingerprint-setup", "setupFingerprint"});
EXPECT_FALSE(ash::LoginScreenTestApi::IsShutdownButtonShown());
EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
EXPECT_FALSE(ash::LoginScreenTestApi::IsAddUserButtonShown());
}
void RunPinSetupScreenChecks() {
test::OobeJS().ExpectVisible("discover");
test::OobeJS().ExpectVisible("discover-impl");
test::OobeJS().ExpectVisiblePath({"discover-impl", "pin-setup-impl"});
test::OobeJS().ExpectVisiblePath(
{"discover-impl", "pin-setup-impl", "setup"});
test::OobeJS()
.CreateFocusWaiter({"discover-impl", "pin-setup-impl", "pinKeyboard"})
->Wait();
EXPECT_FALSE(ash::LoginScreenTestApi::IsShutdownButtonShown());
EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
EXPECT_FALSE(ash::LoginScreenTestApi::IsAddUserButtonShown());
}
// Waits for the ARC terms of service screen to be shown, it accepts
// the terms, and waits for the flow to leave the ARC terms of service screen.
void HandleArcTermsOfServiceScreen() {
OobeScreenWaiter(ArcTermsOfServiceScreenView::kScreenId).Wait();
LOG(INFO) << "OobeInteractiveUITest: Switched to 'arc-tos' screen.";
EXPECT_FALSE(ash::LoginScreenTestApi::IsShutdownButtonShown());
EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
EXPECT_FALSE(ash::LoginScreenTestApi::IsAddUserButtonShown());
test::OobeJS()
.CreateEnabledWaiter(true, {"arc-tos", "arcTosNextButton"})
->Wait();
test::OobeJS().TapOnPath({"arc-tos", "arcTosNextButton"});
test::OobeJS()
.CreateVisibilityWaiter(true, {"arc-tos", "arcLocationService"})
->Wait();
test::OobeJS()
.CreateVisibilityWaiter(true, {"arc-tos", "arcTosAcceptButton"})
->Wait();
test::OobeJS().TapOnPath({"arc-tos", "arcTosAcceptButton"});
OobeScreenExitWaiter(ArcTermsOfServiceScreenView::kScreenId).Wait();
LOG(INFO) << "OobeInteractiveUITest: 'arc-tos' screen done.";
}
// Waits for the recommend apps screen to be shown, selects the single app
// reported by FakeRecommendAppsFetcher, and requests the apps install. It
// will wait for the flow to progress away from the RecommendAppsScreen before
// returning.
// This assumes that ARC terms of service have bee accepted in
// HandleArcTermsOfServiceScreen.
void HandleRecommendAppsScreen() {
OobeScreenWaiter(RecommendAppsScreenView::kScreenId).Wait();
LOG(INFO) << "OobeInteractiveUITest: Switched to 'recommend-apps' screen.";
EXPECT_FALSE(ash::LoginScreenTestApi::IsShutdownButtonShown());
EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
EXPECT_FALSE(ash::LoginScreenTestApi::IsAddUserButtonShown());
test::OobeJS()
.CreateVisibilityWaiter(true, {"recommend-apps", "appsDialog"})
->Wait();
test::OobeJS().ExpectPathDisplayed(true, {"recommend-apps", "appView"});
std::string toggle_apps_script = base::StringPrintf(
"(function() {"
" if (!document.getElementById('recommend-apps-container'))"
" return false;"
" var items ="
" Array.from(document.getElementById('recommend-apps-container')"
" .querySelectorAll('.item') || [])"
" .filter(i => '%s' == i.getAttribute('data-packagename'));"
" if (items.length == 0)"
" return false;"
" items.forEach(i => i.querySelector('.image-picker').click());"
" return true;"
"})();",
"test.package");
const std::string webview_path =
test::GetOobeElementPath({"recommend-apps", "appView"});
const std::string script = base::StringPrintf(
"(function() {"
" var toggleApp = function() {"
" %s.executeScript({code: \"%s\"}, r => {"
" if (!r || !r[0]) {"
" setTimeout(toggleApp, 50);"
" return;"
" }"
" window.domAutomationController.send(true);"
" });"
" };"
" toggleApp();"
"})();",
webview_path.c_str(), toggle_apps_script.c_str());
bool result;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
LoginDisplayHost::default_host()->GetOobeWebContents(), script, &result));
EXPECT_TRUE(result);
const std::initializer_list<base::StringPiece> install_button = {
"recommend-apps", "installButton"};
test::OobeJS().CreateEnabledWaiter(true, install_button)->Wait();
test::OobeJS().TapOnPath(install_button);
OobeScreenExitWaiter(RecommendAppsScreenView::kScreenId).Wait();
LOG(INFO) << "OobeInteractiveUITest: 'recommend-apps' screen done.";
}
// Waits for AppDownloadingScreen to be shown, clicks 'Continue' button, and
// waits until the flow moves to the next screen. This assumes that an app was
// selected in HandleRecommendAppsScreen.
void HandleAppDownloadingScreen() {
OobeScreenWaiter(AppDownloadingScreenView::kScreenId).Wait();
LOG(INFO) << "OobeInteractiveUITest: Switched to 'app-downloading' screen.";
EXPECT_FALSE(ash::LoginScreenTestApi::IsShutdownButtonShown());
EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
EXPECT_FALSE(ash::LoginScreenTestApi::IsAddUserButtonShown());
const std::initializer_list<base::StringPiece> continue_button = {
"app-downloading", "continue-setup-button"};
test::OobeJS().TapOnPath(continue_button);
OobeScreenExitWaiter(AppDownloadingScreenView::kScreenId).Wait();
LOG(INFO) << "OobeInteractiveUITest: 'app-downloading' screen done.";
}
// Waits for AssistantOptInFlowScreen to be shown, skips the opt-in, and waits
// for the flow to move away from the screen.
// Note that due to test setup, the screen will fail to load assistant value
// proposal error (as the URL is not faked in this test), and display an
// error, This is good enough for this tests, whose goal is to verify the
// screen is shown, and how the setup progresses after the screen. The actual
// assistant opt-in flow is tested separately.
void HandleAssistantOptInScreen() {
#if BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
OobeScreenWaiter(AssistantOptInFlowScreenView::kScreenId).Wait();
LOG(INFO) << "OobeInteractiveUITest: Switched to 'assistant-optin' screen.";
EXPECT_FALSE(ash::LoginScreenTestApi::IsShutdownButtonShown());
EXPECT_FALSE(ash::LoginScreenTestApi::IsGuestButtonShown());
EXPECT_FALSE(ash::LoginScreenTestApi::IsAddUserButtonShown());
test::OobeJS()
.CreateVisibilityWaiter(true, {"assistant-optin-flow", "card", "loading"})
->Wait();
std::initializer_list<base::StringPiece> skip_button_path = {
"assistant-optin-flow", "card", "loading", "skip-button"};
test::OobeJS().CreateEnabledWaiter(true, skip_button_path)->Wait();
test::OobeJS().TapOnPath(skip_button_path);
OobeScreenExitWaiter(AssistantOptInFlowScreenView::kScreenId).Wait();
LOG(INFO) << "OobeInteractiveUITest: 'assistant-optin' screen done.";
#endif
}
// Waits for gesture navigation to get shown, runs through all pages in the
// screen, and waits for the screen to exit.
void HandleGestureNavigationScreen() {
OobeScreenWaiter(GestureNavigationScreenView::kScreenId).Wait();
struct {
std::string page_id;
std::string button_id;
} kPages[] = {{"gestureIntro", "gesture-intro-next-button"},
{"gestureHome", "gesture-home-next-button"},
{"gestureOverview", "gesture-overview-next-button"},
{"gestureBack", "gesture-back-next-button"}};
for (const auto& page : kPages) {
SCOPED_TRACE(page.page_id);
test::OobeJS()
.CreateVisibilityWaiter(true, {"gesture-navigation", page.page_id})
->Wait();
test::OobeJS().TapOnPath({"gesture-navigation", page.button_id});
}
OobeScreenExitWaiter(GestureNavigationScreenView::kScreenId).Wait();
}
// Waits for marketing opt in screen to get shown, then taps through the screen
// and waits for the screen to exit.
void HandleMarketingOptInScreen() {
OobeScreenWaiter(MarketingOptInScreenView::kScreenId).Wait();
// Enable the tablet mode shelf navigation buttons so that the next button
// is shown on the marketing opt in screen in tablet mode.
ProfileManager::GetActiveUserProfile()->GetPrefs()->SetBoolean(
ash::prefs::kAccessibilityTabletModeShelfNavigationButtonsEnabled, true);
test::OobeJS().CreateVisibilityWaiter(true, {"marketing-opt-in"})->Wait();
test::OobeJS()
.CreateVisibilityWaiter(
true, {"marketing-opt-in", "marketing-opt-in-next-button"})
->Wait();
test::OobeJS()
.CreateFocusWaiter({"marketing-opt-in", "marketing-opt-in-next-button"})
->Wait();
test::OobeJS().TapOnPath(
{"marketing-opt-in", "marketing-opt-in-next-button"});
OobeScreenExitWaiter(MarketingOptInScreenView::kScreenId).Wait();
}
class FakeRecommendAppsFetcher : public RecommendAppsFetcher {
public:
explicit FakeRecommendAppsFetcher(RecommendAppsFetcherDelegate* delegate)
: delegate_(delegate) {}
~FakeRecommendAppsFetcher() override = default;
// RecommendAppsFetcher:
void Start() override {
base::Value app(base::Value::Type::DICTIONARY);
app.SetKey("package_name", base::Value("test.package"));
base::Value app_list(base::Value::Type::LIST);
app_list.Append(std::move(app));
delegate_->OnLoadSuccess(std::move(app_list));
}
void Retry() override { NOTREACHED(); }
private:
RecommendAppsFetcherDelegate* const delegate_;
};
std::unique_ptr<RecommendAppsFetcher> CreateRecommendAppsFetcher(
RecommendAppsFetcherDelegate* delegate) {
return std::make_unique<FakeRecommendAppsFetcher>(delegate);
}
class ScopedQuickUnlockPrivateGetAuthTokenFunctionObserver
: public extensions::QuickUnlockPrivateGetAuthTokenFunction::TestObserver {
public:
ScopedQuickUnlockPrivateGetAuthTokenFunctionObserver() {
extensions::QuickUnlockPrivateGetAuthTokenFunction::SetTestObserver(this);
}
~ScopedQuickUnlockPrivateGetAuthTokenFunctionObserver() {
extensions::QuickUnlockPrivateGetAuthTokenFunction::SetTestObserver(
nullptr);
}
// extensions::QuickUnlockPrivateGetAuthTokenFunction::TestObserver:
void OnGetAuthTokenCalled(const std::string& password) override {
get_auth_token_password_ = password;
}
const base::Optional<std::string>& get_auth_token_password() const {
return get_auth_token_password_;
}
private:
base::Optional<std::string> get_auth_token_password_;
DISALLOW_COPY_AND_ASSIGN(
ScopedQuickUnlockPrivateGetAuthTokenFunctionObserver);
};
// Observes an `aura::Window` to see if the window was visible at some point in
// time.
class NativeWindowVisibilityObserver : public aura::WindowObserver {
public:
NativeWindowVisibilityObserver() = default;
// aura::Window will remove observers on destruction.
~NativeWindowVisibilityObserver() override = default;
void Observe(aura::Window* window) {
window_ = window;
window_->AddObserver(this);
}
void OnWindowVisibilityChanged(aura::Window* window, bool visible) override {
if (visible)
was_visible_ = visible;
}
bool was_visible() { return was_visible_; }
private:
// The window was visible at some point in time.
bool was_visible_ = false;
aura::Window* window_;
DISALLOW_COPY_AND_ASSIGN(NativeWindowVisibilityObserver);
};
// Sets the `NativeWindowVisibilityObserver` to observe the
// `LoginDisplayHost`'s `NativeWindow`. This needs to be done in
// `PostProfileInit()` as the `default_host` will not be initialized before
// this.
class NativeWindowVisibilityBrowserMainExtraParts
: public ChromeBrowserMainExtraParts {
public:
NativeWindowVisibilityBrowserMainExtraParts(
NativeWindowVisibilityObserver* observer)
: observer_(observer) {}
~NativeWindowVisibilityBrowserMainExtraParts() override = default;
// ChromeBrowserMainExtraParts:
void PostProfileInit() override {
observer_->Observe(LoginDisplayHost::default_host()->GetNativeWindow());
}
private:
NativeWindowVisibilityObserver* observer_;
DISALLOW_COPY_AND_ASSIGN(NativeWindowVisibilityBrowserMainExtraParts);
};
class OobeEndToEndTestSetupMixin : public InProcessBrowserTestMixin {
public:
struct Parameters {
bool is_tablet;
bool is_quick_unlock_enabled;
bool hide_shelf_controls_in_tablet_mode;
ArcState arc_state;
std::string ToString() const {
return std::string("{is_tablet: ") + (is_tablet ? "true" : "false") +
", is_quick_unlock_enabled: " +
(is_quick_unlock_enabled ? "true" : "false") +
", hide_shelf_controls_in_tablet_mode: " +
(hide_shelf_controls_in_tablet_mode ? "true" : "false") +
", arc_state: " + ArcStateToString(arc_state) + "}";
}
};
explicit OobeEndToEndTestSetupMixin(
InProcessBrowserTestMixinHost* mixin_host,
net::EmbeddedTestServer* arc_tos_server,
const std::tuple<bool, bool, bool, ArcState>& parameters)
: InProcessBrowserTestMixin(mixin_host), arc_tos_server_(arc_tos_server) {
std::tie(params_.is_tablet, params_.is_quick_unlock_enabled,
params_.hide_shelf_controls_in_tablet_mode, params_.arc_state) =
parameters;
// TODO(crbug.com/1101318): disable ChildSpecificSign in feature for now due
// to test flakiness
if (params_.hide_shelf_controls_in_tablet_mode) {
feature_list_.InitWithFeatures(
{ash::features::kHideShelfControlsInTabletMode},
{features::kChildSpecificSignin});
} else {
feature_list_.InitWithFeatures(
{}, {ash::features::kHideShelfControlsInTabletMode,
features::kChildSpecificSignin});
}
}
~OobeEndToEndTestSetupMixin() override = default;
// InProcessBrowserTestMixin:
void SetUp() override {
LOG(INFO) << "OOBE end-to-end test started with params "
<< params_.ToString();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
if (params_.is_tablet) {
// Makes the device capable to entering tablet mode.
command_line->AppendSwitch(ash::switches::kAshEnableTabletMode);
// Having an active internal display so that tablet mode does not end
// on display config change.
command_line->AppendSwitch(::switches::kUseFirstDisplayAsInternal);
}
if (params_.arc_state != ArcState::kNotAvailable) {
arc::SetArcAvailableCommandLineForTesting(command_line);
// Prevent encryption migration screen from showing up after user login
// with ARC available.
command_line->AppendSwitch(switches::kDisableEncryptionMigration);
command_line->AppendSwitchASCII(
switches::kArcTosHostForTests,
arc_tos_server_->GetURL("/arc-tos").spec());
}
}
void SetUpInProcessBrowserTestFixture() override {
if (params_.is_quick_unlock_enabled)
quick_unlock::EnabledForTesting(true);
if (params_.arc_state != ArcState::kNotAvailable) {
recommend_apps_fetcher_factory_ =
std::make_unique<ScopedTestRecommendAppsFetcherFactory>(
base::BindRepeating(&CreateRecommendAppsFetcher));
if (arc_tos_server_) {
arc_tos_server_->RegisterRequestHandler(
base::BindRepeating(&OobeEndToEndTestSetupMixin::HandleRequest,
base::Unretained(this)));
}
}
}
void SetUpOnMainThread() override {
ASSERT_TRUE(arc_temp_dir_.CreateUniqueTempDir());
if (params_.is_tablet)
ash::ShellTestApi().SetTabletModeEnabledForTest(true);
if (params_.arc_state != ArcState::kNotAvailable) {
// Init ArcSessionManager for testing.
arc::ArcServiceLauncher::Get()->ResetForTesting();
arc::ArcSessionManager::Get()->SetArcSessionRunnerForTesting(
std::make_unique<arc::ArcSessionRunner>(
base::BindRepeating(arc::FakeArcSession::Create)));
EXPECT_TRUE(arc::ExpandPropertyFilesForTesting(
arc::ArcSessionManager::Get(), arc_temp_dir_.GetPath()));
}
}
void TearDownInProcessBrowserTestFixture() override {
recommend_apps_fetcher_factory_.reset();
}
void TearDown() override { quick_unlock::EnabledForTesting(false); }
std::unique_ptr<HttpResponse> HandleRequest(const HttpRequest& request) {
auto response = std::make_unique<BasicHttpResponse>();
if (request.relative_url != "/arc-tos/about/play-terms.html") {
response->set_code(net::HTTP_NOT_FOUND);
} else {
response->set_code(net::HTTP_OK);
response->set_content("<html><body>Test Terms of Service</body></html>");
response->set_content_type("text/html");
}
return response;
}
bool is_tablet() const { return params_.is_tablet; }
bool is_quick_unlock_enabled() const {
return params_.is_quick_unlock_enabled;
}
bool hide_shelf_controls_in_tablet_mode() const {
return params_.hide_shelf_controls_in_tablet_mode;
}
ArcState arc_state() const { return params_.arc_state; }
private:
Parameters params_;
base::test::ScopedFeatureList feature_list_;
std::unique_ptr<ScopedTestRecommendAppsFetcherFactory>
recommend_apps_fetcher_factory_;
net::EmbeddedTestServer* arc_tos_server_;
base::ScopedTempDir arc_temp_dir_;
DISALLOW_COPY_AND_ASSIGN(OobeEndToEndTestSetupMixin);
};
} // namespace
class OobeInteractiveUITest : public OobeBaseTest,
public ::testing::WithParamInterface<
std::tuple<bool, bool, bool, ArcState>> {
public:
OobeInteractiveUITest() {
// TODO(https://crbug.com/1130502): Merge these functions into one.
branded_build_override_ = WizardController::ForceBrandedBuildForTesting();
sync_branded_build_override_ =
SyncConsentScreen::ForceBrandedBuildForTesting(true);
}
~OobeInteractiveUITest() override = default;
// OobeBaseTest:
void TearDownOnMainThread() override {
// If the login display is still showing, exit gracefully.
if (LoginDisplayHost::default_host()) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&chrome::AttemptExit));
RunUntilBrowserProcessQuits();
}
OobeBaseTest::TearDownOnMainThread();
}
void WaitForLoginDisplayHostShutdown() {
if (!LoginDisplayHost::default_host())
return;
LOG(INFO) << "OobeInteractiveUITest: Waiting for LoginDisplayHost to "
"shut down.";
test::TestPredicateWaiter(base::BindRepeating([]() {
return !LoginDisplayHost::default_host();
})).Wait();
LOG(INFO) << "OobeInteractiveUITest: LoginDisplayHost is down.";
}
void PerformStepsBeforeEnrollmentCheck();
void PerformSessionSignInSteps(
const ScopedQuickUnlockPrivateGetAuthTokenFunctionObserver&
get_auth_token_observer);
void SimpleEndToEnd();
const OobeEndToEndTestSetupMixin* test_setup() const { return &setup_; }
private:
std::unique_ptr<base::AutoReset<bool>> branded_build_override_;
std::unique_ptr<base::AutoReset<bool>> sync_branded_build_override_;
FakeGaiaMixin fake_gaia_{&mixin_host_, embedded_test_server()};
FakeEulaMixin fake_eula_{&mixin_host_, embedded_test_server()};
net::EmbeddedTestServer arc_tos_server_{net::EmbeddedTestServer::TYPE_HTTPS};
EmbeddedTestServerSetupMixin arc_tos_server_setup_{&mixin_host_,
&arc_tos_server_};
OobeEndToEndTestSetupMixin setup_{&mixin_host_, &arc_tos_server_, GetParam()};
DISALLOW_COPY_AND_ASSIGN(OobeInteractiveUITest);
};
void OobeInteractiveUITest::PerformStepsBeforeEnrollmentCheck() {
test::WaitForWelcomeScreen();
RunWelcomeScreenChecks();
test::TapWelcomeNext();
test::WaitForNetworkSelectionScreen();
RunNetworkSelectionScreenChecks();
test::TapNetworkSelectionNext();
test::WaitForEulaScreen();
RunEulaScreenChecks();
test::TapEulaAccept();
test::WaitForUpdateScreen();
test::ExitUpdateScreenNoUpdate();
}
void OobeInteractiveUITest::PerformSessionSignInSteps(
const ScopedQuickUnlockPrivateGetAuthTokenFunctionObserver&
get_auth_token_observer) {
if (features::IsChildSpecificSigninEnabled()) {
test::WaitForUserCreationScreen();
test::TapUserCreationNext();
}
WaitForGaiaSignInScreen(test_setup()->arc_state() != ArcState::kNotAvailable);
LogInAsRegularUser();
test::WaitForSyncConsentScreen();
RunSyncConsentScreenChecks();
test::ExitScreenSyncConsent();
if (test_setup()->is_quick_unlock_enabled()) {
test::WaitForFingerprintScreen();
RunFingerprintScreenChecks();
test::ExitFingerprintPinSetupScreen();
}
if (test_setup()->is_tablet()) {
test::WaitForPinSetupScreen();
RunPinSetupScreenChecks();
EXPECT_TRUE(get_auth_token_observer.get_auth_token_password().has_value());
EXPECT_EQ(get_auth_token_observer.get_auth_token_password().value(),
FakeGaiaMixin::kFakeUserPassword);
test::ExitDiscoverPinSetupScreen();
}
if (test_setup()->arc_state() != ArcState::kNotAvailable) {
HandleArcTermsOfServiceScreen();
}
if (test_setup()->arc_state() == ArcState::kAcceptTerms) {
HandleRecommendAppsScreen();
HandleAppDownloadingScreen();
}
HandleAssistantOptInScreen();
if (test_setup()->is_tablet() &&
test_setup()->hide_shelf_controls_in_tablet_mode()) {
HandleGestureNavigationScreen();
}
HandleMarketingOptInScreen();
}
void OobeInteractiveUITest::SimpleEndToEnd() {
ScopedQuickUnlockPrivateGetAuthTokenFunctionObserver get_auth_token_observer;
PerformStepsBeforeEnrollmentCheck();
PerformSessionSignInSteps(get_auth_token_observer);
WaitForLoginDisplayHostShutdown();
}
// Disabled on *San bots since they time out.
#if defined(MEMORY_SANITIZER) || defined(ADDRESS_SANITIZER) || \
defined(LEAK_SANITIZER)
#define MAYBE_SimpleEndToEnd DISABLED_SimpleEndToEnd
#else
#define MAYBE_SimpleEndToEnd SimpleEndToEnd
#endif
// Note that this probably the largest test that is run on ChromeOS, and it
// might be running close to time limits especially on instrumented builds.
// As such it might sometimes cause flakiness.
// Please do not disable it for whole ChromeOS, only for specific instrumented
// bots. Another alternative is to increase respective multiplier in
// base/test/test_timeouts.h.
IN_PROC_BROWSER_TEST_P(OobeInteractiveUITest, MAYBE_SimpleEndToEnd) {
SimpleEndToEnd();
}
INSTANTIATE_TEST_SUITE_P(
All,
OobeInteractiveUITest,
testing::Combine(testing::Bool(),
testing::Bool(),
testing::Bool(),
testing::Values(ArcState::kNotAvailable,
ArcState::kAcceptTerms)));
class OobeZeroTouchInteractiveUITest : public OobeInteractiveUITest {
public:
OobeZeroTouchInteractiveUITest() = default;
~OobeZeroTouchInteractiveUITest() override = default;
void SetUpOnMainThread() override {
chromeos::AttestationClient::Get()
->GetTestInterface()
->AllowlistSignSimpleChallengeKey(
/*username=*/"", chromeos::attestation::GetKeyNameForProfile(
chromeos::attestation::
PROFILE_ENTERPRISE_ENROLLMENT_CERTIFICATE,
""));
OobeInteractiveUITest::SetUpOnMainThread();
policy_server_.ConfigureFakeStatisticsForZeroTouch(
&fake_statistics_provider_);
}
void SetUpCommandLine(base::CommandLine* command_line) override {
OobeInteractiveUITest::SetUpCommandLine(command_line);
command_line->AppendSwitchASCII(
switches::kEnterpriseEnableInitialEnrollment,
AutoEnrollmentController::kInitialEnrollmentAlways);
}
void ZeroTouchEndToEnd();
private:
LocalPolicyTestServerMixin policy_server_{&mixin_host_};
test::EnrollmentUIMixin enrollment_ui_{&mixin_host_};
system::ScopedFakeStatisticsProvider fake_statistics_provider_;
DISALLOW_COPY_AND_ASSIGN(OobeZeroTouchInteractiveUITest);
};
void OobeZeroTouchInteractiveUITest::ZeroTouchEndToEnd() {
policy_server_.SetupZeroTouchForcedEnrollment();
ScopedQuickUnlockPrivateGetAuthTokenFunctionObserver get_auth_token_observer;
PerformStepsBeforeEnrollmentCheck();
test::WaitForEnrollmentScreen();
enrollment_ui_.WaitForStep(test::ui::kEnrollmentStepSuccess);
auto login_screen_waiter =
std::make_unique<content::WindowedNotificationObserver>(
chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
content::NotificationService::AllSources());
enrollment_ui_.LeaveSuccessScreen();
login_screen_waiter->Wait();
PerformSessionSignInSteps(get_auth_token_observer);
WaitForLoginDisplayHostShutdown();
}
// crbug.com/997987. Disabled on MSAN since they time out.
// crbug.com/1055853: EndToEnd is flaky on Linux Chromium OS ASan LSan
#if defined(MEMORY_SANITIZER) || defined(ADDRESS_SANITIZER) || \
defined(LEAK_SANITIZER)
#define MAYBE_EndToEnd DISABLED_EndToEnd
#else
#define MAYBE_EndToEnd EndToEnd
#endif
// Note that this probably the largest test that is run on ChromeOS, and it
// might be running close to time limits especially on instrumented builds.
// As such it might sometimes cause flakiness.
// Please do not disable it for whole ChromeOS, only for specific instrumented
// bots. Another alternative is to increase respective multiplier in
// base/test/test_timeouts.h.
IN_PROC_BROWSER_TEST_P(OobeZeroTouchInteractiveUITest, MAYBE_EndToEnd) {
ZeroTouchEndToEnd();
}
INSTANTIATE_TEST_SUITE_P(
All,
OobeZeroTouchInteractiveUITest,
testing::Combine(testing::Bool(),
testing::Bool(),
testing::Bool(),
testing::Values(ArcState::kNotAvailable,
ArcState::kAcceptTerms)));
class PublicSessionOobeTest : public MixinBasedInProcessBrowserTest,
public ::testing::WithParamInterface<
std::tuple<bool, bool, bool, ArcState>> {
public:
PublicSessionOobeTest()
: PublicSessionOobeTest(false /*requires_terms_of_service*/) {}
explicit PublicSessionOobeTest(bool requires_terms_of_service)
: requires_terms_of_service_(requires_terms_of_service),
observer_(std::make_unique<NativeWindowVisibilityObserver>()) {
// Prevents Chrome from starting to quit right after login display is
// finalized.
login_manager_.set_should_launch_browser(true);
}
~PublicSessionOobeTest() override = default;
void SetUpInProcessBrowserTestFixture() override {
std::unique_ptr<ScopedDevicePolicyUpdate> device_policy_update =
device_state_.RequestDevicePolicyUpdate();
const std::string kAccountId = "public-session@test";
enterprise_management::DeviceLocalAccountsProto* const
device_local_accounts = device_policy_update->policy_payload()
->mutable_device_local_accounts();
// Add public session account.
enterprise_management::DeviceLocalAccountInfoProto* const account =
device_local_accounts->add_account();
account->set_account_id(kAccountId);
account->set_type(enterprise_management::DeviceLocalAccountInfoProto::
ACCOUNT_TYPE_PUBLIC_SESSION);
// Set the public session to auto-launch.
device_local_accounts->set_auto_login_id(kAccountId);
device_policy_update.reset();
std::unique_ptr<ScopedUserPolicyUpdate> device_local_account_policy_update =
device_state_.RequestDeviceLocalAccountPolicyUpdate(kAccountId);
// Specify terms of service if needed.
if (requires_terms_of_service_) {
device_local_account_policy_update->policy_payload()
->mutable_termsofserviceurl()
->set_value(embedded_test_server()
->GetURL("/chromeos/enterprise/tos.txt")
.spec());
}
device_local_account_policy_update.reset();
MixinBasedInProcessBrowserTest::SetUpInProcessBrowserTestFixture();
}
void CreatedBrowserMainParts(content::BrowserMainParts* parts) override {
MixinBasedInProcessBrowserTest::CreatedBrowserMainParts(parts);
static_cast<ChromeBrowserMainParts*>(parts)->AddParts(
std::make_unique<NativeWindowVisibilityBrowserMainExtraParts>(
observer_.get()));
}
void TearDownOnMainThread() override {
observer_.reset();
MixinBasedInProcessBrowserTest::TearDownOnMainThread();
}
bool DialogWasVisible() { return observer_->was_visible(); }
LoginManagerMixin login_manager_{&mixin_host_, {}};
private:
const bool requires_terms_of_service_;
std::unique_ptr<NativeWindowVisibilityObserver> observer_;
OobeEndToEndTestSetupMixin setup_{&mixin_host_, nullptr, GetParam()};
DeviceStateMixin device_state_{
&mixin_host_, DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED};
};
IN_PROC_BROWSER_TEST_P(PublicSessionOobeTest, NoTermsOfService) {
login_manager_.WaitForActiveSession();
// Check that the dialog was never shown.
EXPECT_FALSE(DialogWasVisible());
}
INSTANTIATE_TEST_SUITE_P(
All,
PublicSessionOobeTest,
testing::Combine(testing::Bool(),
testing::Bool(),
testing::Bool(),
testing::Values(ArcState::kNotAvailable)));
class PublicSessionWithTermsOfServiceOobeTest : public PublicSessionOobeTest {
public:
PublicSessionWithTermsOfServiceOobeTest()
: PublicSessionOobeTest(true /*requires_terms_od_service*/) {}
~PublicSessionWithTermsOfServiceOobeTest() override = default;
// Use embedded test server to serve Public session Terms of Service.
EmbeddedTestServerSetupMixin embedded_test_server_setup_{
&mixin_host_, embedded_test_server()};
};
IN_PROC_BROWSER_TEST_P(PublicSessionWithTermsOfServiceOobeTest,
AcceptTermsOfService) {
OobeScreenWaiter(TermsOfServiceScreenView::kScreenId).Wait();
test::OobeJS()
.CreateWaiter(
test::GetOobeElementPath({"terms-of-service", "acceptButton"}))
->Wait();
test::OobeJS().ClickOnPath({"terms-of-service", "acceptButton"});
OobeScreenExitWaiter(TermsOfServiceScreenView::kScreenId).Wait();
login_manager_.WaitForActiveSession();
}
INSTANTIATE_TEST_SUITE_P(
All,
PublicSessionWithTermsOfServiceOobeTest,
testing::Combine(testing::Bool(),
testing::Bool(),
testing::Bool(),
testing::Values(ArcState::kNotAvailable)));
class EphemeralUserOobeTest : public MixinBasedInProcessBrowserTest,
public ::testing::WithParamInterface<
std::tuple<bool, bool, bool, ArcState>> {
public:
EphemeralUserOobeTest() { login_manager_.set_should_launch_browser(true); }
~EphemeralUserOobeTest() override = default;
// MixinBaseInProcessBrowserTest:
void SetUpInProcessBrowserTestFixture() override {
std::unique_ptr<ScopedDevicePolicyUpdate> device_policy_update =
device_state_.RequestDevicePolicyUpdate();
device_policy_update->policy_payload()
->mutable_ephemeral_users_enabled()
->set_ephemeral_users_enabled(true);
device_policy_update.reset();
MixinBasedInProcessBrowserTest::SetUpInProcessBrowserTestFixture();
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
base::RunLoop run_loop;
if (!LoginDisplayHost::default_host()->GetOobeUI()->IsJSReady(
run_loop.QuitClosure())) {
run_loop.Run();
}
MixinBasedInProcessBrowserTest::SetUpOnMainThread();
}
void WaitForActiveSession() { login_manager_.WaitForActiveSession(); }
const OobeEndToEndTestSetupMixin* test_setup() const { return &setup_; }
private:
EmbeddedTestServerSetupMixin gaia_server_setup_{&mixin_host_,
embedded_test_server()};
FakeGaiaMixin fake_gaia_{&mixin_host_, embedded_test_server()};
net::EmbeddedTestServer arc_tos_server_{net::EmbeddedTestServer::TYPE_HTTPS};
EmbeddedTestServerSetupMixin arc_tos_server_setup_{&mixin_host_,
&arc_tos_server_};
OobeEndToEndTestSetupMixin setup_{&mixin_host_, &arc_tos_server_, GetParam()};
LoginManagerMixin login_manager_{&mixin_host_, {}};
DeviceStateMixin device_state_{
&mixin_host_, DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED};
};
// TODO(crbug.com/1004561) Disabled due to flake.
IN_PROC_BROWSER_TEST_P(EphemeralUserOobeTest, DISABLED_RegularEphemeralUser) {
ScopedQuickUnlockPrivateGetAuthTokenFunctionObserver get_auth_token_observer;
WaitForGaiaSignInScreen(test_setup()->arc_state() != ArcState::kNotAvailable);
LogInAsRegularUser();
test::WaitForSyncConsentScreen();
RunSyncConsentScreenChecks();
test::ExitScreenSyncConsent();
if (test_setup()->is_quick_unlock_enabled()) {
test::WaitForFingerprintScreen();
RunFingerprintScreenChecks();
test::ExitFingerprintPinSetupScreen();
}
if (test_setup()->is_tablet()) {
test::WaitForPinSetupScreen();
RunPinSetupScreenChecks();
EXPECT_TRUE(get_auth_token_observer.get_auth_token_password().has_value());
EXPECT_EQ("", get_auth_token_observer.get_auth_token_password().value());
test::ExitDiscoverPinSetupScreen();
}
if (test_setup()->arc_state() != ArcState::kNotAvailable) {
HandleArcTermsOfServiceScreen();
}
if (test_setup()->arc_state() == ArcState::kAcceptTerms) {
HandleRecommendAppsScreen();
HandleAppDownloadingScreen();
}
HandleAssistantOptInScreen();
if (test_setup()->is_tablet() &&
test_setup()->hide_shelf_controls_in_tablet_mode()) {
HandleGestureNavigationScreen();
HandleMarketingOptInScreen();
}
WaitForActiveSession();
}
INSTANTIATE_TEST_SUITE_P(
All,
EphemeralUserOobeTest,
testing::Combine(testing::Bool(),
testing::Bool(),
testing::Bool(),
testing::Values(ArcState::kNotAvailable,
ArcState::kAcceptTerms)));
} // namespace chromeos