blob: d3b7b6f8934480d9ce699ca88832664ca5918046 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// 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/omnibox/omnibox_result_view.h"
#include <memory>
#include "base/memory/raw_ptr.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/ui/omnibox/omnibox_theme.h"
#include "chrome/browser/ui/views/omnibox/omnibox_popup_view_views.h"
#include "chrome/test/views/chrome_views_test_base.h"
#include "components/omnibox/browser/omnibox_controller.h"
#include "components/omnibox/browser/test_omnibox_client.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/display/test/test_screen.h"
#include "ui/events/event.h"
#include "ui/events/event_constants.h"
#include "ui/events/event_utils.h"
#include "ui/events/types/event_type.h"
#include "ui/gfx/image/image.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/widget/widget.h"
#if defined(USE_AURA)
#include "ui/aura/env.h"
#endif
namespace {
// An arbitrary index for the result view under test. Used to test the selection
// state. There are 6 results total so the index should be in the range 0-5.
static constexpr size_t kTestResultViewIndex = 4;
class TestOmniboxPopupViewViews : public OmniboxPopupViewViews {
public:
explicit TestOmniboxPopupViewViews(OmniboxController* controller)
: OmniboxPopupViewViews(/*omnibox_view=*/nullptr,
controller,
/*location_bar_view=*/nullptr),
selection_(OmniboxPopupSelection(0, OmniboxPopupSelection::NORMAL)) {}
TestOmniboxPopupViewViews(const TestOmniboxPopupViewViews&) = delete;
TestOmniboxPopupViewViews& operator=(const TestOmniboxPopupViewViews&) =
delete;
void SetSelectedIndex(size_t index) override { selection_.line = index; }
size_t GetSelectedIndex() const override { return selection_.line; }
OmniboxPopupSelection GetSelection() const override { return selection_; }
private:
OmniboxPopupSelection selection_;
};
} // namespace
class OmniboxResultViewTest : public ChromeViewsTestBase {
public:
void SetUp() override {
#if !defined(USE_AURA)
test_screen_ = std::make_unique<display::test::TestScreen>();
display::Screen::SetScreenInstance(test_screen_.get());
#endif
ChromeViewsTestBase::SetUp();
// Create a widget and assign bounds to support calls to HitTestPoint.
widget_ = CreateTestWidget();
omnibox_controller_ = std::make_unique<OmniboxController>(
/*view=*/nullptr, std::make_unique<TestOmniboxClient>());
popup_view_ =
std::make_unique<TestOmniboxPopupViewViews>(omnibox_controller_.get());
result_view_ =
new OmniboxResultView(popup_view_.get(), kTestResultViewIndex);
views::View* root_view = widget_->GetRootView();
root_view->SetBoundsRect(gfx::Rect(0, 0, 500, 500));
result_view_->SetBoundsRect(gfx::Rect(0, 0, 100, 100));
root_view->AddChildView(result_view_.get());
// Start by not hovering over the result view.
FakeMouseEvent(ui::ET_MOUSE_MOVED, 0, 200, 200);
}
void TearDown() override {
widget_.reset();
ChromeViewsTestBase::TearDown();
display::Screen::SetScreenInstance(nullptr);
test_screen_.reset();
}
// Also sets the fake screen's mouse cursor to 0, 0.
ui::MouseEvent FakeMouseEvent(ui::EventType type, int flags) {
return FakeMouseEvent(type, flags, 0, 0);
}
// Also sets the fake screen's mouse cursor to |x|, |y|.
ui::MouseEvent FakeMouseEvent(ui::EventType type,
int flags,
float x,
float y) {
#if !defined(USE_AURA)
test_screen_->set_cursor_screen_point(gfx::Point(x, y));
#else
aura::Env::GetInstance()->SetLastMouseLocation(gfx::Point(x, y));
#endif
return ui::MouseEvent(type, gfx::Point(x, y), gfx::Point(),
ui::EventTimeForNow(), flags, 0);
}
OmniboxEditModel* edit_model() { return omnibox_controller_->edit_model(); }
OmniboxPopupViewViews* popup_view() { return popup_view_.get(); }
OmniboxResultView* result_view() { return result_view_; }
private:
std::unique_ptr<OmniboxController> omnibox_controller_;
std::unique_ptr<TestOmniboxPopupViewViews> popup_view_;
raw_ptr<OmniboxResultView, DanglingUntriaged> result_view_;
std::unique_ptr<views::Widget> widget_;
std::unique_ptr<display::test::TestScreen> test_screen_;
};
TEST_F(OmniboxResultViewTest, MousePressedWithLeftButtonSelectsThisResult) {
EXPECT_NE(OmniboxPartState::SELECTED, result_view()->GetThemeState());
EXPECT_NE(popup_view()->GetSelectedIndex(), kTestResultViewIndex);
// Right button press should not select.
result_view()->OnMousePressed(
FakeMouseEvent(ui::ET_MOUSE_PRESSED, ui::EF_RIGHT_MOUSE_BUTTON));
EXPECT_NE(OmniboxPartState::SELECTED, result_view()->GetThemeState());
EXPECT_NE(popup_view()->GetSelectedIndex(), kTestResultViewIndex);
// Middle button press should not select.
result_view()->OnMousePressed(
FakeMouseEvent(ui::ET_MOUSE_PRESSED, ui::EF_MIDDLE_MOUSE_BUTTON));
EXPECT_NE(OmniboxPartState::SELECTED, result_view()->GetThemeState());
EXPECT_NE(popup_view()->GetSelectedIndex(), kTestResultViewIndex);
// Multi-button press should not select.
result_view()->OnMousePressed(
FakeMouseEvent(ui::ET_MOUSE_PRESSED,
ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON));
EXPECT_NE(OmniboxPartState::SELECTED, result_view()->GetThemeState());
EXPECT_NE(popup_view()->GetSelectedIndex(), kTestResultViewIndex);
// Left button press should select.
result_view()->OnMousePressed(
FakeMouseEvent(ui::ET_MOUSE_PRESSED, ui::EF_LEFT_MOUSE_BUTTON));
EXPECT_EQ(OmniboxPartState::SELECTED, result_view()->GetThemeState());
EXPECT_EQ(popup_view()->GetSelectedIndex(), kTestResultViewIndex);
}
TEST_F(OmniboxResultViewTest, MouseDragWithLeftButtonSelectsThisResult) {
EXPECT_NE(OmniboxPartState::SELECTED, result_view()->GetThemeState());
EXPECT_NE(popup_view()->GetSelectedIndex(), kTestResultViewIndex);
// Right button drag should not select.
result_view()->OnMouseDragged(
FakeMouseEvent(ui::ET_MOUSE_DRAGGED, ui::EF_RIGHT_MOUSE_BUTTON));
EXPECT_NE(OmniboxPartState::SELECTED, result_view()->GetThemeState());
EXPECT_NE(popup_view()->GetSelectedIndex(), kTestResultViewIndex);
// Middle button drag should not select.
result_view()->OnMouseDragged(
FakeMouseEvent(ui::ET_MOUSE_DRAGGED, ui::EF_MIDDLE_MOUSE_BUTTON));
EXPECT_NE(OmniboxPartState::SELECTED, result_view()->GetThemeState());
EXPECT_NE(popup_view()->GetSelectedIndex(), kTestResultViewIndex);
// Multi-button drag should not select.
result_view()->OnMouseDragged(
FakeMouseEvent(ui::ET_MOUSE_DRAGGED,
ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON));
EXPECT_NE(OmniboxPartState::SELECTED, result_view()->GetThemeState());
EXPECT_NE(popup_view()->GetSelectedIndex(), kTestResultViewIndex);
// Left button drag should select.
result_view()->OnMouseDragged(
FakeMouseEvent(ui::ET_MOUSE_DRAGGED, ui::EF_LEFT_MOUSE_BUTTON));
EXPECT_EQ(OmniboxPartState::SELECTED, result_view()->GetThemeState());
EXPECT_EQ(popup_view()->GetSelectedIndex(), kTestResultViewIndex);
}
TEST_F(OmniboxResultViewTest, MouseDragWithNonLeftButtonSetsHoveredState) {
EXPECT_NE(OmniboxPartState::HOVERED, result_view()->GetThemeState());
// Right button drag should put the view in the HOVERED state.
result_view()->OnMouseDragged(
FakeMouseEvent(ui::ET_MOUSE_DRAGGED, ui::EF_RIGHT_MOUSE_BUTTON, 50, 50));
EXPECT_EQ(OmniboxPartState::HOVERED, result_view()->GetThemeState());
// Left button drag should take the view out of the HOVERED state.
result_view()->OnMouseDragged(
FakeMouseEvent(ui::ET_MOUSE_DRAGGED, ui::EF_LEFT_MOUSE_BUTTON, 200, 200));
EXPECT_NE(OmniboxPartState::HOVERED, result_view()->GetThemeState());
}
TEST_F(OmniboxResultViewTest, MouseDragOutOfViewCancelsHoverState) {
EXPECT_NE(OmniboxPartState::HOVERED, result_view()->GetThemeState());
// Right button drag in the view should put the view in the HOVERED state.
result_view()->OnMouseDragged(
FakeMouseEvent(ui::ET_MOUSE_DRAGGED, ui::EF_RIGHT_MOUSE_BUTTON, 50, 50));
EXPECT_EQ(OmniboxPartState::HOVERED, result_view()->GetThemeState());
// Right button drag outside of the view should revert the HOVERED state.
result_view()->OnMouseDragged(FakeMouseEvent(
ui::ET_MOUSE_DRAGGED, ui::EF_RIGHT_MOUSE_BUTTON, 200, 200));
EXPECT_NE(OmniboxPartState::HOVERED, result_view()->GetThemeState());
}
TEST_F(OmniboxResultViewTest, MouseEnterAndExitSetsHoveredState) {
EXPECT_NE(OmniboxPartState::HOVERED, result_view()->GetThemeState());
// The mouse entering the view should put the view in the HOVERED state.
result_view()->OnMouseMoved(FakeMouseEvent(ui::ET_MOUSE_MOVED, 0, 50, 50));
EXPECT_EQ(OmniboxPartState::HOVERED, result_view()->GetThemeState());
// Continuing to move over the view should not change the state.
result_view()->OnMouseMoved(FakeMouseEvent(ui::ET_MOUSE_MOVED, 0, 50, 50));
EXPECT_EQ(OmniboxPartState::HOVERED, result_view()->GetThemeState());
// But exiting should revert the HOVERED state.
result_view()->OnMouseExited(FakeMouseEvent(ui::ET_MOUSE_MOVED, 0, 200, 200));
EXPECT_NE(OmniboxPartState::HOVERED, result_view()->GetThemeState());
}
TEST_F(OmniboxResultViewTest, AccessibleNodeData) {
// Check accessibility of result.
std::u16string match_url = u"https://google.com";
AutocompleteMatch match(nullptr, 500, false,
AutocompleteMatchType::HISTORY_TITLE);
match.contents = match_url;
match.contents_class.push_back(
ACMatchClassification(0, ACMatchClassification::URL));
match.destination_url = GURL(match_url);
match.description = u"Google";
match.allowed_to_be_default_match = true;
result_view()->SetMatch(match);
ui::AXNodeData result_node_data;
result_view()->GetAccessibleNodeData(&result_node_data);
EXPECT_TRUE(
result_node_data.HasBoolAttribute(ax::mojom::BoolAttribute::kSelected));
EXPECT_FALSE(
result_node_data.GetBoolAttribute(ax::mojom::BoolAttribute::kSelected));
EXPECT_EQ(result_node_data.role, ax::mojom::Role::kListBoxOption);
// TODO(tommycli) Find a way to test this.
// EXPECT_EQ(
// result_node_data.GetString16Attribute(ax::mojom::StringAttribute::kName),
// u"Google https://google.com location from history");
EXPECT_EQ(
result_node_data.GetIntAttribute(ax::mojom::IntAttribute::kPosInSet),
int{kTestResultViewIndex} + 1);
// TODO(accessibility) Find a way to test this.
// EXPECT_EQ(result_node_data.GetIntAttribute(
// ax::mojom::IntAttribute::kSetSize), 1);
// Select it and check selected state.
ui::AXNodeData result_after_click;
result_view()->OnMousePressed(
FakeMouseEvent(ui::ET_MOUSE_PRESSED, ui::EF_LEFT_MOUSE_BUTTON));
result_view()->GetAccessibleNodeData(&result_after_click);
EXPECT_TRUE(
result_after_click.GetBoolAttribute(ax::mojom::BoolAttribute::kSelected));
// Check accessibility of list box.
ui::AXNodeData popup_node_data;
popup_view()->GetAccessibleNodeData(&popup_node_data);
EXPECT_EQ(popup_node_data.role, ax::mojom::Role::kListBox);
EXPECT_FALSE(popup_node_data.HasState(ax::mojom::State::kExpanded));
EXPECT_TRUE(popup_node_data.HasState(ax::mojom::State::kCollapsed));
EXPECT_TRUE(popup_node_data.HasState(ax::mojom::State::kInvisible));
EXPECT_FALSE(
popup_node_data.HasIntAttribute(ax::mojom::IntAttribute::kPopupForId));
}
TEST_F(OmniboxResultViewTest, StarterPackMatch) {
AutocompleteMatch match(nullptr, 1350, false,
AutocompleteMatchType::STARTER_PACK);
result_view()->SetMatch(match);
// No assertions necessary; just exercising code paths for starter pack match.
}