blob: 62946f4bea4bae59ace1c9d68c685030292e755d [file] [log] [blame]
// Copyright 2013 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 "ash/public/cpp/ash_features.h"
#include "ash/public/cpp/system_tray_test_api.h"
#include "ash/public/cpp/test/shell_test_api.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/chromeos/first_run/first_run.h"
#include "chrome/browser/chromeos/first_run/first_run_controller.h"
#include "chrome/browser/chromeos/first_run/step_names.h"
#include "chrome/browser/chromeos/login/test/js_checker.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chromeos/constants/chromeos_features.h"
#include "content/public/test/test_utils.h"
#include "ui/aura/window.h"
#include "ui/events/event_handler.h"
#include "ui/events/test/event_generator.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_utils.h"
#include "ui/views/window/dialog_delegate.h"
namespace chromeos {
namespace {
class TestModalDialogDelegate : public views::DialogDelegateView {
public:
TestModalDialogDelegate() = default;
~TestModalDialogDelegate() override = default;
// views::WidgetDelegate:
ui::ModalType GetModalType() const override { return ui::MODAL_TYPE_SYSTEM; }
private:
DISALLOW_COPY_AND_ASSIGN(TestModalDialogDelegate);
};
class CountingEventHandler : public ui::EventHandler {
public:
explicit CountingEventHandler(int* mouse_events_registered)
: mouse_events_registered_(mouse_events_registered) {}
~CountingEventHandler() override = default;
private:
// ui::EventHandler:
void OnMouseEvent(ui::MouseEvent* event) override {
++*mouse_events_registered_;
}
int* mouse_events_registered_;
DISALLOW_COPY_AND_ASSIGN(CountingEventHandler);
};
} // namespace
// The param respectively indicate whether tablet mode and the
// kHideShelfControlsInTabletMode feature are enabled.
class FirstRunUIBrowserTest
: public InProcessBrowserTest,
public FirstRunActor::Delegate,
public ::testing::WithParamInterface<std::tuple<bool, bool>> {
public:
FirstRunUIBrowserTest()
: initialized_(false),
finalized_(false) {
if (IsHomeButtonHiddenInTabletMode()) {
// kHideShelfControlsInTabletMode is predicated on hotseat being enabled.
scoped_feature_list_.InitWithFeatures(
{ash::features::kHideShelfControlsInTabletMode,
chromeos::features::kShelfHotseat},
{});
} else {
scoped_feature_list_.InitWithFeatures(
{}, {ash::features::kHideShelfControlsInTabletMode});
}
}
void SetUpOnMainThread() override {
InProcessBrowserTest::SetUpOnMainThread();
tray_test_api_ = ash::SystemTrayTestApi::Create();
if (InTabletMode())
ash::ShellTestApi().SetTabletModeEnabledForTest(true);
}
// FirstRunActor::Delegate overrides.
void OnActorInitialized() override {
initialized_ = true;
if (on_initialized_callback_)
std::move(on_initialized_callback_).Run();
controller()->OnActorInitialized();
}
void OnNextButtonClicked(const std::string& step_name) override {
controller()->OnNextButtonClicked(step_name);
}
void OnStepShown(const std::string& step_name) override {
current_step_name_ = step_name;
if (on_step_shown_callback_)
std::move(on_step_shown_callback_).Run();
controller()->OnStepShown(step_name);
}
void OnStepHidden(const std::string& step_name) override {
controller()->OnStepHidden(step_name);
}
void OnHelpButtonClicked() override { controller()->OnHelpButtonClicked(); }
void OnActorFinalized() override {
finalized_ = true;
if (on_finalized_callback_)
std::move(on_finalized_callback_).Run();
controller()->OnActorFinalized();
}
void OnActorDestroyed() override { controller()->OnActorDestroyed(); }
bool InTabletMode() const { return std::get<0>(GetParam()); }
bool IsHomeButtonHiddenInTabletMode() const {
return std::get<1>(GetParam());
}
bool IsHomeButtonShown() const {
return !InTabletMode() || !IsHomeButtonHiddenInTabletMode();
}
void LaunchTutorial() {
chromeos::first_run::LaunchTutorial();
EXPECT_TRUE(controller() != NULL);
// Replacing delegate to observe all messages coming from WebUI to
// controller.
controller()->actor_->set_delegate(this);
initialized_ = controller()->actor_->IsInitialized();
}
void WaitForInitialization() {
if (initialized_)
return;
WaitUntilCalled(&on_initialized_callback_);
EXPECT_TRUE(initialized_);
js().set_web_contents(controller()->web_contents_for_tests_);
}
void WaitForStep(const std::string& step_name) {
if (current_step_name_ == step_name)
return;
WaitUntilCalled(&on_step_shown_callback_);
EXPECT_EQ(current_step_name_, step_name);
}
void AdvanceStep() {
js().Evaluate("cr.FirstRun.currentStep_.nextButton_.click()");
}
void WaitForFinalization() {
if (!finalized_) {
WaitUntilCalled(&on_finalized_callback_);
EXPECT_TRUE(finalized_);
}
}
void WaitUntilCalled(base::OnceClosure* callback) {
scoped_refptr<content::MessageLoopRunner> runner =
new content::MessageLoopRunner;
*callback = runner->QuitClosure();
runner->Run();
}
test::JSChecker& js() { return js_; }
FirstRunController* controller() {
return FirstRunController::GetInstanceForTest();
}
bool IsTrayBubbleOpen() { return tray_test_api_->IsTrayBubbleOpen(); }
views::Widget* GetOverlayWidget() { return controller()->widget_.get(); }
private:
std::unique_ptr<ash::SystemTrayTestApi> tray_test_api_;
std::string current_step_name_;
bool initialized_;
bool finalized_;
base::test::ScopedFeatureList scoped_feature_list_;
base::OnceClosure on_initialized_callback_;
base::OnceClosure on_step_shown_callback_;
base::OnceClosure on_finalized_callback_;
test::JSChecker js_;
};
INSTANTIATE_TEST_SUITE_P(
All,
FirstRunUIBrowserTest,
::testing::Combine(
::testing::Bool() /*tablet mode*/,
::testing::Bool() /*home button hidden in tablet mode*/));
IN_PROC_BROWSER_TEST_P(FirstRunUIBrowserTest, FirstRunFlow) {
LaunchTutorial();
WaitForInitialization();
if (IsHomeButtonShown()) {
WaitForStep(first_run::kAppListStep);
EXPECT_FALSE(IsTrayBubbleOpen());
AdvanceStep();
}
WaitForStep(first_run::kTrayStep);
EXPECT_TRUE(IsTrayBubbleOpen());
AdvanceStep();
WaitForFinalization();
content::RunAllPendingInMessageLoop();
EXPECT_EQ(controller(), nullptr);
EXPECT_FALSE(IsTrayBubbleOpen());
}
// Tests that a modal window doesn't block events to the tutorial. A modal
// window might be open if enterprise policy forces a browser tab to open
// on first login and the web page opens a JavaScript alert.
// See https://crrev.com/99673003
IN_PROC_BROWSER_TEST_P(FirstRunUIBrowserTest, ModalWindowDoesNotBlock) {
// Start the tutorial.
LaunchTutorial();
WaitForInitialization();
WaitForStep(IsHomeButtonShown() ? first_run::kAppListStep
: first_run::kTrayStep);
// Simulate the browser opening a modal dialog.
views::Widget* modal_dialog = views::DialogDelegate::CreateDialogWidget(
new TestModalDialogDelegate(), /*context=*/nullptr,
/*parent=*/nullptr);
modal_dialog->Show();
// A mouse click is still received by the overlay widget.
int mouse_events = 0;
CountingEventHandler handler(&mouse_events);
aura::Window* overlay_window = GetOverlayWidget()->GetNativeView();
overlay_window->AddPreTargetHandler(&handler);
ui::test::EventGenerator event_generator(GetRootWindow(GetOverlayWidget()));
event_generator.PressLeftButton();
EXPECT_EQ(mouse_events, 1);
overlay_window->RemovePreTargetHandler(&handler);
modal_dialog->Close();
}
// Tests that the escape key cancels the tutorial.
IN_PROC_BROWSER_TEST_P(FirstRunUIBrowserTest, EscapeCancelsTutorial) {
// Run the tutorial for a couple steps, but don't finish it.
LaunchTutorial();
WaitForInitialization();
if (IsHomeButtonShown()) {
WaitForStep(first_run::kAppListStep);
AdvanceStep();
}
WaitForStep(first_run::kTrayStep);
EXPECT_TRUE(IsTrayBubbleOpen());
// Press the escape key.
ui::test::EventGenerator event_generator(GetRootWindow(GetOverlayWidget()));
event_generator.PressKey(ui::VKEY_ESCAPE, ui::EF_NONE);
content::RunAllPendingInMessageLoop();
// The tutorial stopped.
EXPECT_EQ(controller(), nullptr);
EXPECT_FALSE(IsTrayBubbleOpen());
}
} // namespace chromeos