blob: 89f9251c5064c108454867d786dc0225f5c1205c [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/views/interaction/interaction_test_util_mouse.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_element_identifiers.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/toolbar/app_menu_model.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/tabs/tab_strip.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "content/public/test/browser_test.h"
#include "ui/base/interaction/element_tracker.h"
#include "ui/base/interaction/expect_call_in_scope.h"
#include "ui/base/interaction/interaction_sequence.h"
#include "ui/base/page_transition_types.h"
#include "ui/base/test/ui_controls.h"
#include "ui/views/interaction/element_tracker_views.h"
#include "ui/views/view_utils.h"
class InteractionTestUtilMouseUiTest
: public InProcessBrowserTest,
public testing::WithParamInterface<bool> {
public:
InteractionTestUtilMouseUiTest() = default;
~InteractionTestUtilMouseUiTest() override = default;
using Mouse = views::test::InteractionTestUtilMouse;
void SetUpOnMainThread() override {
InProcessBrowserTest::SetUpOnMainThread();
mouse_ = std::make_unique<Mouse>(
BrowserView::GetBrowserViewForBrowser(browser())->GetWidget());
CHECK(mouse_->SetTouchMode(GetParam()));
}
void TearDownOnMainThread() override {
mouse_.reset();
InProcessBrowserTest::TearDownOnMainThread();
}
protected:
std::unique_ptr<Mouse> mouse_;
};
#if BUILDFLAG(IS_CHROMEOS_ASH)
INSTANTIATE_TEST_SUITE_P(TouchMode,
InteractionTestUtilMouseUiTest,
testing::Bool());
#else
INSTANTIATE_TEST_SUITE_P(TouchMode,
InteractionTestUtilMouseUiTest,
testing::Values(false));
#endif
IN_PROC_BROWSER_TEST_P(InteractionTestUtilMouseUiTest, MoveAndClick) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
auto sequence =
ui::InteractionSequence::Builder()
.SetContext(browser()->window()->GetElementContext())
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
// Find the app menu button.
.AddStep(ui::InteractionSequence::StepBuilder()
.SetElementID(kToolbarAppMenuButtonElementId)
.SetStartCallback(base::BindLambdaForTesting(
[this](ui::InteractionSequence* seq,
ui::TrackedElement* el) {
auto* const view =
el->AsA<views::TrackedElementViews>()->view();
const gfx::Point pos =
view->GetBoundsInScreen().CenterPoint();
// Perform the following gesture:
// - move to the center point of the app menu
// button
// - click the left mouse button
if (!mouse_->PerformGestures(
view->GetWidget()->GetNativeWindow(),
Mouse::MoveTo(pos),
Mouse::Click(ui_controls::LEFT))) {
seq->FailForTesting();
}
})))
// Verify that the click opened the app menu, which should contain a
// known menu item.
.AddStep(ui::InteractionSequence::StepBuilder().SetElementID(
AppMenuModel::kMoreToolsMenuItem))
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_P(InteractionTestUtilMouseUiTest, GestureAborted) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
auto cancel =
base::BindLambdaForTesting([this]() { mouse_->CancelAllGestures(); });
auto sequence =
ui::InteractionSequence::Builder()
.SetContext(browser()->window()->GetElementContext())
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
// Find the app menu button.
.AddStep(ui::InteractionSequence::StepBuilder()
.SetElementID(kToolbarAppMenuButtonElementId)
.SetStartCallback(base::BindLambdaForTesting(
[this, &cancel](ui::TrackedElement* el) {
auto* const view =
el->AsA<views::TrackedElementViews>()->view();
const gfx::Point pos =
view->GetBoundsInScreen().CenterPoint();
// Queue a cancellation. This should execute
// sometime after the mouse move is sent.
base::SingleThreadTaskRunner::GetCurrentDefault()
->PostTask(FROM_HERE, cancel);
// Perform the following gesture:
// - move to the center point of the app menu
// button
// - click the left mouse button
EXPECT_FALSE(mouse_->PerformGestures(
view->GetWidget()->GetNativeWindow(),
Mouse::MoveTo(pos),
Mouse::Click(ui_controls::LEFT)));
})))
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}
IN_PROC_BROWSER_TEST_P(InteractionTestUtilMouseUiTest, Drag) {
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::AbortedCallback, aborted);
UNCALLED_MOCK_CALLBACK(ui::InteractionSequence::CompletedCallback, completed);
const GURL first_url =
browser()->tab_strip_model()->GetWebContentsAt(0)->GetURL();
const GURL kSecondUrl("chrome://version");
ASSERT_TRUE(AddTabAtIndex(-1, kSecondUrl, ui::PAGE_TRANSITION_LINK));
auto sequence =
ui::InteractionSequence::Builder()
.SetContext(browser()->window()->GetElementContext())
.SetAbortedCallback(aborted.Get())
.SetCompletedCallback(completed.Get())
// Find the tab strip.
.AddStep(
ui::InteractionSequence::StepBuilder()
.SetElementID(kTabStripElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* seq,
ui::TrackedElement* el) {
auto* const tab_strip = views::AsViewClass<TabStrip>(
el->AsA<views::TrackedElementViews>()->view());
// The second tab might still be animating in, which
// could cause weirdness if we try to drag.
tab_strip->StopAnimating(/* layout =*/true);
const gfx::Point start = tab_strip->tab_at(0)
->GetBoundsInScreen()
.CenterPoint();
const gfx::Point end = tab_strip->tab_at(1)
->GetBoundsInScreen()
.CenterPoint();
// Drag the first tab into the second spot.
if (!mouse_->PerformGestures(
tab_strip->GetWidget()->GetNativeWindow(),
Mouse::MoveTo(start),
Mouse::DragAndRelease(end))) {
seq->FailForTesting();
}
})))
// When the gesture is complete, check that the gesture succeeded.
.AddStep(
ui::InteractionSequence::StepBuilder()
.SetElementID(kTabStripElementId)
.SetStartCallback(base::BindLambdaForTesting(
[&](ui::InteractionSequence* seq,
ui::TrackedElement* el) {
// Stop any remaining animations, and verify that the
// tab was moved.
auto* const tab_strip = views::AsViewClass<TabStrip>(
el->AsA<views::TrackedElementViews>()->view());
tab_strip->StopAnimating(/* layout =*/true);
EXPECT_EQ(kSecondUrl, browser()
->tab_strip_model()
->GetWebContentsAt(0)
->GetURL());
EXPECT_EQ(first_url, browser()
->tab_strip_model()
->GetWebContentsAt(1)
->GetURL());
// Clean up any drag gestures that have not yet properly
// cleaned up (this is a platform implementation issue
// for drag event handling).
mouse_->CancelAllGestures();
})))
.Build();
EXPECT_CALL_IN_SCOPE(completed, Run, sequence->RunSynchronouslyForTesting());
}