blob: cbde08cab736760defe6ef37311b9e7d4f4ef140 [file] [log] [blame]
// Copyright 2020 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/commander_frontend_views.h"
#include "base/macros.h"
#include "chrome/browser/ui/commander/commander_backend.h"
#include "chrome/browser/ui/commander/commander_view_model.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "content/public/test/browser_test.h"
#include "ui/views/test/widget_test.h"
#include "ui/views/widget/any_widget_observer.h"
#include "ui/views/widget/widget.h"
class CommanderFrontendViewsTest : public InProcessBrowserTest {
public:
// TODO(lgrey): This is copied over from CommanderControllerUnittest, with
// modifications. If we need it one more time, extract to a common file.
class TestBackend : public commander::CommanderBackend {
public:
void OnTextChanged(const std::u16string& text, Browser* browser) override {
text_changed_invocations_.push_back(text);
}
void OnCommandSelected(size_t command_index, int result_set_id) override {
command_selected_invocations_.push_back(command_index);
}
void OnCompositeCommandCancelled() override {
composite_command_cancelled_invocation_count_++;
}
void SetUpdateCallback(commander::CommanderBackend::ViewModelUpdateCallback
callback) override {
callback_ = std::move(callback);
}
void Reset() override { reset_invocation_count_++; }
void CallCallback() {
commander::CommanderViewModel vm;
CallCallback(vm);
}
void CallCallback(commander::CommanderViewModel vm) { callback_.Run(vm); }
const std::vector<std::u16string> text_changed_invocations() {
return text_changed_invocations_;
}
const std::vector<size_t> command_selected_invocations() {
return command_selected_invocations_;
}
int composite_command_cancelled_invocation_count() {
return composite_command_cancelled_invocation_count_;
}
int reset_invocation_count() { return reset_invocation_count_; }
private:
commander::CommanderBackend::ViewModelUpdateCallback callback_;
std::vector<std::u16string> text_changed_invocations_;
std::vector<size_t> command_selected_invocations_;
int composite_command_cancelled_invocation_count_ = 0;
int reset_invocation_count_ = 0;
};
protected:
views::Widget* WaitForCommanderWidgetAttachedTo(Browser* browser) {
if (IsWidgetAttachedToBrowser(browser)) {
expected_browser_ = nullptr;
return active_widget_;
}
expected_browser_ = browser;
run_loop_ = std::make_unique<base::RunLoop>();
run_loop_->Run();
EXPECT_TRUE(IsWidgetAttachedToBrowser(browser));
return active_widget_;
}
void WaitForCommanderWidgetToClose() {
if (!active_widget_)
return;
run_loop_ = std::make_unique<base::RunLoop>();
run_loop_->Run();
EXPECT_TRUE(!active_widget_);
}
std::unique_ptr<TestBackend> backend_;
private:
void SetUpOnMainThread() override {
backend_ = std::make_unique<TestBackend>();
observer_ = std::make_unique<views::AnyWidgetObserver>(
views::test::AnyWidgetTestPasskey());
// Unretained is safe since we own observer.
observer_->set_shown_callback(base::BindRepeating(
&CommanderFrontendViewsTest::OnWidgetShown, base::Unretained(this)));
observer_->set_closing_callback(base::BindRepeating(
&CommanderFrontendViewsTest::OnWidgetClosed, base::Unretained(this)));
}
void TearDownOnMainThread() override {
observer_.reset();
run_loop_.reset();
backend_.reset();
}
void OnWidgetShown(views::Widget* widget) {
if (widget->GetName() == "Commander") {
active_widget_ = widget;
if (IsWidgetAttachedToBrowser(expected_browser_)) {
expected_browser_ = nullptr;
if (run_loop_)
run_loop_->Quit();
}
}
}
void OnWidgetClosed(views::Widget* widget) {
if (widget == active_widget_) {
active_widget_ = nullptr;
if (run_loop_)
run_loop_->Quit();
}
}
bool IsWidgetAttachedToBrowser(const Browser* browser) {
if (!active_widget_ || !browser)
return false;
views::Widget* browser_widget =
BrowserView::GetBrowserViewForBrowser(browser)->GetWidget();
return active_widget_->parent() == browser_widget;
}
std::unique_ptr<views::AnyWidgetObserver> observer_;
views::Widget* active_widget_ = nullptr;
Browser* expected_browser_ = nullptr;
std::unique_ptr<base::RunLoop> run_loop_;
};
IN_PROC_BROWSER_TEST_F(CommanderFrontendViewsTest, ShowShowsWidget) {
auto frontend = std::make_unique<CommanderFrontendViews>(backend_.get());
frontend->Show(browser());
EXPECT_TRUE(WaitForCommanderWidgetAttachedTo(browser()));
frontend->Hide();
}
IN_PROC_BROWSER_TEST_F(CommanderFrontendViewsTest, HideHidesWidget) {
auto frontend = std::make_unique<CommanderFrontendViews>(backend_.get());
frontend->Show(browser());
EXPECT_TRUE(WaitForCommanderWidgetAttachedTo(browser()));
EXPECT_EQ(backend_->reset_invocation_count(), 0);
frontend->Hide();
WaitForCommanderWidgetToClose();
EXPECT_EQ(backend_->reset_invocation_count(), 1);
}
IN_PROC_BROWSER_TEST_F(CommanderFrontendViewsTest, DismissHidesWidget) {
auto frontend = std::make_unique<CommanderFrontendViews>(backend_.get());
frontend->Show(browser());
EXPECT_TRUE(WaitForCommanderWidgetAttachedTo(browser()));
EXPECT_EQ(backend_->reset_invocation_count(), 0);
frontend->OnDismiss();
WaitForCommanderWidgetToClose();
EXPECT_EQ(backend_->reset_invocation_count(), 1);
}
IN_PROC_BROWSER_TEST_F(CommanderFrontendViewsTest, ViewModelCloseHidesWidget) {
auto frontend = std::make_unique<CommanderFrontendViews>(backend_.get());
frontend->Show(browser());
EXPECT_TRUE(WaitForCommanderWidgetAttachedTo(browser()));
EXPECT_EQ(backend_->reset_invocation_count(), 0);
commander::CommanderViewModel vm;
vm.action = commander::CommanderViewModel::Action::kClose;
backend_->CallCallback(vm);
WaitForCommanderWidgetToClose();
EXPECT_EQ(backend_->reset_invocation_count(), 1);
}
IN_PROC_BROWSER_TEST_F(CommanderFrontendViewsTest, ToggleShowsWidget) {
auto frontend = std::make_unique<CommanderFrontendViews>(backend_.get());
frontend->ToggleForBrowser(browser());
EXPECT_TRUE(WaitForCommanderWidgetAttachedTo(browser()));
frontend->Hide();
}
IN_PROC_BROWSER_TEST_F(CommanderFrontendViewsTest, ToggleHidesWidget) {
auto frontend = std::make_unique<CommanderFrontendViews>(backend_.get());
frontend->Show(browser());
EXPECT_TRUE(WaitForCommanderWidgetAttachedTo(browser()));
frontend->ToggleForBrowser(browser());
WaitForCommanderWidgetToClose();
EXPECT_EQ(backend_->reset_invocation_count(), 1);
}
// Ensure that calling toggle twice in a row does the right thing.
IN_PROC_BROWSER_TEST_F(CommanderFrontendViewsTest, ToggleTogglesWidget) {
auto frontend = std::make_unique<CommanderFrontendViews>(backend_.get());
frontend->ToggleForBrowser(browser());
EXPECT_TRUE(WaitForCommanderWidgetAttachedTo(browser()));
frontend->ToggleForBrowser(browser());
WaitForCommanderWidgetToClose();
EXPECT_EQ(backend_->reset_invocation_count(), 1);
}
// When a commander widget is showing on browser A, toggling it on browser B
// should hide it on browser A and show it on browser B.
IN_PROC_BROWSER_TEST_F(CommanderFrontendViewsTest, ToggleReplacesWidget) {
auto frontend = std::make_unique<CommanderFrontendViews>(backend_.get());
frontend->ToggleForBrowser(browser());
views::Widget* commander_widget = WaitForCommanderWidgetAttachedTo(browser());
Browser* other_browser = CreateBrowser(browser()->profile());
views::test::WidgetDestroyedWaiter destroyed_waiter(commander_widget);
frontend->ToggleForBrowser(other_browser);
destroyed_waiter.Wait();
EXPECT_EQ(backend_->reset_invocation_count(), 1);
EXPECT_TRUE(WaitForCommanderWidgetAttachedTo(other_browser));
frontend->Hide();
}
IN_PROC_BROWSER_TEST_F(CommanderFrontendViewsTest, OnHeightChangedSizesWidget) {
auto frontend = std::make_unique<CommanderFrontendViews>(backend_.get());
frontend->Show(browser());
views::Widget* commander_widget = WaitForCommanderWidgetAttachedTo(browser());
int old_height = commander_widget->GetRootView()->height();
int new_height = 200;
// Ensure changing height isn't a no-op.
EXPECT_NE(old_height, new_height);
frontend->OnHeightChanged(200);
EXPECT_EQ(new_height, commander_widget->GetRootView()->height());
frontend->Hide();
}
IN_PROC_BROWSER_TEST_F(CommanderFrontendViewsTest, PassesOnOptionSelected) {
auto frontend = std::make_unique<CommanderFrontendViews>(backend_.get());
frontend->Show(browser());
ignore_result(WaitForCommanderWidgetAttachedTo(browser()));
frontend->OnOptionSelected(8, 13);
ASSERT_EQ(backend_->command_selected_invocations().size(), 1u);
EXPECT_EQ(backend_->command_selected_invocations().back(), 8u);
frontend->Hide();
}
IN_PROC_BROWSER_TEST_F(CommanderFrontendViewsTest, PassesOnTextChanged) {
auto frontend = std::make_unique<CommanderFrontendViews>(backend_.get());
frontend->Show(browser());
ignore_result(WaitForCommanderWidgetAttachedTo(browser()));
const std::u16string input = u"orange";
frontend->OnTextChanged(input);
ASSERT_EQ(backend_->text_changed_invocations().size(), 1u);
EXPECT_EQ(backend_->text_changed_invocations().back(), input);
frontend->Hide();
}
IN_PROC_BROWSER_TEST_F(CommanderFrontendViewsTest,
PassesOnCompositeCommandCancelled) {
auto frontend = std::make_unique<CommanderFrontendViews>(backend_.get());
frontend->Show(browser());
ignore_result(WaitForCommanderWidgetAttachedTo(browser()));
EXPECT_EQ(backend_->composite_command_cancelled_invocation_count(), 0);
frontend->OnCompositeCommandCancelled();
EXPECT_EQ(backend_->composite_command_cancelled_invocation_count(), 1);
frontend->Hide();
}