blob: 674c779c11537a9113692d63597b987dd1cb1497 [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 "chrome/browser/ui/views/confirm_quit_bubble_controller.h"
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/macros.h"
#include "base/timer/mock_timer.h"
#include "chrome/browser/ui/views/confirm_quit_bubble.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/events/event.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/gfx/animation/slide_animation.h"
class TestConfirmQuitBubble : public ConfirmQuitBubbleBase {
public:
TestConfirmQuitBubble() {}
~TestConfirmQuitBubble() override {}
void Show() override {}
void Hide() override {}
private:
DISALLOW_COPY_AND_ASSIGN(TestConfirmQuitBubble);
};
class TestConfirmQuitBubbleController : public ConfirmQuitBubbleController {
public:
TestConfirmQuitBubbleController(
std::unique_ptr<ConfirmQuitBubbleBase> bubble,
std::unique_ptr<base::OneShotTimer> hide_timer,
std::unique_ptr<gfx::SlideAnimation> animation)
: ConfirmQuitBubbleController(std::move(bubble),
std::move(hide_timer),
std::move(animation)) {}
void DeactivateBrowser() { OnBrowserNoLongerActive(nullptr); }
bool quit_called() const { return quit_called_; }
private:
void DoQuit() override { quit_called_ = true; }
bool IsFeatureEnabled() override { return true; }
bool quit_called_ = false;
DISALLOW_COPY_AND_ASSIGN(TestConfirmQuitBubbleController);
};
class TestSlideAnimation : public gfx::SlideAnimation {
public:
TestSlideAnimation() : gfx::SlideAnimation(nullptr) {}
~TestSlideAnimation() override {}
void Reset() override {}
void Reset(double value) override {}
void Show() override {}
void Hide() override {}
void SetSlideDuration(int duration) override {}
private:
DISALLOW_COPY_AND_ASSIGN(TestSlideAnimation);
};
class ConfirmQuitBubbleControllerTest : public testing::Test {
protected:
void SetUp() override {
std::unique_ptr<TestConfirmQuitBubble> bubble =
std::make_unique<TestConfirmQuitBubble>();
auto timer = std::make_unique<base::MockOneShotTimer>();
bubble_ = bubble.get();
timer_ = timer.get();
controller_.reset(new TestConfirmQuitBubbleController(
std::move(bubble), std::move(timer),
std::make_unique<TestSlideAnimation>()));
}
void TearDown() override { controller_.reset(); }
void SendKeyEvent(ui::KeyEvent* event) { controller_->OnKeyEvent(event); }
void SendAccelerator(bool quit, bool press, bool repeat) {
ui::KeyboardCode key = quit ? ui::VKEY_Q : ui::VKEY_P;
int modifiers = ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN;
if (repeat)
modifiers |= ui::EF_IS_REPEAT;
ui::EventType type = press ? ui::ET_KEY_PRESSED : ui::ET_KEY_RELEASED;
ui::KeyEvent event(type, key, modifiers);
SendKeyEvent(&event);
}
void PressQuitAccelerator() { SendAccelerator(true, true, false); }
void ReleaseQuitAccelerator() { SendAccelerator(true, false, false); }
void RepeatQuitAccelerator() { SendAccelerator(true, true, true); }
void PressOtherAccelerator() { SendAccelerator(false, true, false); }
void ReleaseOtherAccelerator() { SendAccelerator(false, false, false); }
std::unique_ptr<TestConfirmQuitBubbleController> controller_;
// Owned by |controller_|.
TestConfirmQuitBubble* bubble_;
// Owned by |controller_|.
base::MockOneShotTimer* timer_;
bool quit_called_ = false;
};
// Pressing and holding the shortcut should quit.
TEST_F(ConfirmQuitBubbleControllerTest, PressAndHold) {
PressQuitAccelerator();
EXPECT_TRUE(timer_->IsRunning());
timer_->Fire();
EXPECT_FALSE(controller_->quit_called());
ReleaseQuitAccelerator();
EXPECT_TRUE(controller_->quit_called());
}
// Pressing the shortcut twice should quit.
TEST_F(ConfirmQuitBubbleControllerTest, DoublePress) {
PressQuitAccelerator();
ReleaseQuitAccelerator();
EXPECT_TRUE(timer_->IsRunning());
PressQuitAccelerator();
EXPECT_FALSE(timer_->IsRunning());
EXPECT_FALSE(controller_->quit_called());
ReleaseQuitAccelerator();
EXPECT_TRUE(controller_->quit_called());
}
// Pressing the shortcut once should not quit.
TEST_F(ConfirmQuitBubbleControllerTest, SinglePress) {
PressQuitAccelerator();
ReleaseQuitAccelerator();
EXPECT_TRUE(timer_->IsRunning());
timer_->Fire();
EXPECT_FALSE(controller_->quit_called());
}
// Repeated presses should not be counted.
TEST_F(ConfirmQuitBubbleControllerTest, RepeatedPresses) {
PressQuitAccelerator();
RepeatQuitAccelerator();
ReleaseQuitAccelerator();
EXPECT_TRUE(timer_->IsRunning());
timer_->Fire();
EXPECT_FALSE(controller_->quit_called());
}
// Other keys shouldn't matter.
TEST_F(ConfirmQuitBubbleControllerTest, OtherKeyPress) {
PressQuitAccelerator();
ReleaseQuitAccelerator();
PressOtherAccelerator();
ReleaseOtherAccelerator();
EXPECT_TRUE(timer_->IsRunning());
PressQuitAccelerator();
EXPECT_FALSE(timer_->IsRunning());
EXPECT_FALSE(controller_->quit_called());
ReleaseQuitAccelerator();
EXPECT_TRUE(controller_->quit_called());
}
// The controller state should be reset when the browser loses focus.
TEST_F(ConfirmQuitBubbleControllerTest, BrowserLosesFocus) {
// Press but don't release the accelerator.
PressQuitAccelerator();
EXPECT_TRUE(timer_->IsRunning());
controller_->DeactivateBrowser();
EXPECT_FALSE(timer_->IsRunning());
EXPECT_FALSE(controller_->quit_called());
ReleaseQuitAccelerator();
// Press and release the accelerator.
PressQuitAccelerator();
ReleaseQuitAccelerator();
EXPECT_TRUE(timer_->IsRunning());
controller_->DeactivateBrowser();
EXPECT_FALSE(timer_->IsRunning());
EXPECT_FALSE(controller_->quit_called());
// Press and hold the accelerator.
PressQuitAccelerator();
EXPECT_TRUE(timer_->IsRunning());
timer_->Fire();
EXPECT_FALSE(timer_->IsRunning());
controller_->DeactivateBrowser();
ReleaseQuitAccelerator();
EXPECT_FALSE(controller_->quit_called());
}
// The controller should not consume keyup events on the 'Q' key
// (https://crbug.com/856868).
TEST_F(ConfirmQuitBubbleControllerTest, ControllerDoesNotHandleQKeyUp) {
ui::KeyEvent press_event(ui::ET_KEY_PRESSED, ui::VKEY_Q, 0);
SendKeyEvent(&press_event);
EXPECT_FALSE(press_event.handled());
ui::KeyEvent release_event(ui::ET_KEY_RELEASED, ui::VKEY_Q, 0);
SendKeyEvent(&release_event);
EXPECT_FALSE(release_event.handled());
}