1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/run_loop.h"
6 #include "base/strings/string16.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "chrome/browser/sessions/session_tab_helper.h"
9 #include "chrome/browser/ui/toolbar/test_toolbar_action_view_controller.h"
10 #include "chrome/browser/ui/toolbar/toolbar_action_view_controller.h"
11 #include "chrome/browser/ui/views/toolbar/toolbar_action_view.h"
12 #include "chrome/test/base/testing_profile.h"
13 #include "content/public/test/test_browser_thread.h"
14 #include "content/public/test/test_web_contents_factory.h"
15 #include "ui/accessibility/ax_view_state.h"
16 #include "ui/events/test/event_generator.h"
17 #include "ui/views/test/views_test_base.h"
21 // A test delegate for a toolbar action view.
22 class TestToolbarActionViewDelegate
: public ToolbarActionView::Delegate
{
24 TestToolbarActionViewDelegate() : shown_in_menu_(false),
25 overflow_reference_view_(nullptr),
26 web_contents_(nullptr) {}
27 ~TestToolbarActionViewDelegate() override
{}
29 // ToolbarActionView::Delegate:
30 content::WebContents
* GetCurrentWebContents() override
{
33 bool ShownInsideMenu() const override
{ return shown_in_menu_
; }
34 void OnToolbarActionViewDragDone() override
{}
35 views::MenuButton
* GetOverflowReferenceView() override
{
36 return overflow_reference_view_
;
38 void OnMouseEnteredToolbarActionView() override
{}
39 void WriteDragDataForView(views::View
* sender
,
40 const gfx::Point
& press_pt
,
41 ui::OSExchangeData
* data
) override
{}
42 int GetDragOperationsForView(views::View
* sender
,
43 const gfx::Point
& p
) override
{
44 return ui::DragDropTypes::DRAG_NONE
;
46 bool CanStartDragForView(views::View
* sender
,
47 const gfx::Point
& press_pt
,
48 const gfx::Point
& p
) override
{ return false; }
50 void set_shown_in_menu(bool shown_in_menu
) { shown_in_menu_
= shown_in_menu
; }
51 void set_overflow_reference_view(views::MenuButton
* overflow_reference_view
) {
52 overflow_reference_view_
= overflow_reference_view
;
54 void set_web_contents(content::WebContents
* web_contents
) {
55 web_contents_
= web_contents
;
61 views::MenuButton
* overflow_reference_view_
;
63 content::WebContents
* web_contents_
;
65 DISALLOW_COPY_AND_ASSIGN(TestToolbarActionViewDelegate
);
68 class OpenMenuListener
: public views::ContextMenuController
{
70 explicit OpenMenuListener(views::View
* view
)
73 view_
->set_context_menu_controller(this);
75 ~OpenMenuListener() override
{
76 view_
->set_context_menu_controller(nullptr);
79 void ShowContextMenuForView(views::View
* source
,
80 const gfx::Point
& point
,
81 ui::MenuSourceType source_type
) override
{
85 bool opened_menu() const { return opened_menu_
; }
92 DISALLOW_COPY_AND_ASSIGN(OpenMenuListener
);
97 class ToolbarActionViewUnitTest
: public views::ViewsTestBase
{
99 ToolbarActionViewUnitTest()
101 ui_thread_(content::BrowserThread::UI
, message_loop()) {}
102 ~ToolbarActionViewUnitTest() override
{}
104 void SetUp() override
{
105 views::ViewsTestBase::SetUp();
107 widget_
= new views::Widget
;
108 views::Widget::InitParams params
=
109 CreateParams(views::Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
110 params
.bounds
= gfx::Rect(0, 0, 200, 200);
111 widget_
->Init(params
);
113 void TearDown() override
{
114 if (!widget_
->IsClosed())
116 views::ViewsTestBase::TearDown();
119 views::Widget
* widget() { return widget_
; }
122 // The widget managed by this test.
123 views::Widget
* widget_
;
125 // Web contents need a fake ui thread.
126 content::TestBrowserThread ui_thread_
;
128 DISALLOW_COPY_AND_ASSIGN(ToolbarActionViewUnitTest
);
131 // Test the basic ui of a ToolbarActionView and that it responds correctly to
132 // a controller's state.
133 TEST_F(ToolbarActionViewUnitTest
, BasicToolbarActionViewTest
) {
134 TestingProfile profile
;
136 // ViewsTestBase initializees the aura environment, so the factory shouldn't.
137 content::TestWebContentsFactory web_contents_factory
;
139 TestToolbarActionViewController
controller("fake controller");
140 TestToolbarActionViewDelegate action_view_delegate
;
142 // Configure the test controller and delegate.
143 base::string16 name
= base::ASCIIToUTF16("name");
144 controller
.SetAccessibleName(name
);
145 base::string16 tooltip
= base::ASCIIToUTF16("tooltip");
146 controller
.SetTooltip(tooltip
);
147 content::WebContents
* web_contents
=
148 web_contents_factory
.CreateWebContents(&profile
);
149 SessionTabHelper::CreateForWebContents(web_contents
);
150 action_view_delegate
.set_web_contents(web_contents
);
152 // Move the mouse off the not-yet-existent button.
153 ui::test::EventGenerator
generator(GetContext(), widget()->GetNativeWindow());
154 generator
.MoveMouseTo(gfx::Point(300, 300));
156 // Create a new toolbar action view.
157 ToolbarActionView
view(&controller
, &profile
, &action_view_delegate
);
158 view
.set_owned_by_client();
159 view
.SetBoundsRect(gfx::Rect(0, 0, 200, 20));
160 widget()->SetContentsView(&view
);
163 // Check that the tooltip and accessible state of the view match the
165 base::string16 tooltip_test
;
166 EXPECT_TRUE(view
.GetTooltipText(gfx::Point(), &tooltip_test
));
167 EXPECT_EQ(tooltip
, tooltip_test
);
168 ui::AXViewState ax_state
;
169 view
.GetAccessibleState(&ax_state
);
170 EXPECT_EQ(name
, ax_state
.name
);
172 // The button should start in normal state, with no actions executed.
173 EXPECT_EQ(views::Button::STATE_NORMAL
, view
.state());
174 EXPECT_EQ(0, controller
.execute_action_count());
176 // Click the button. This should execute it.
177 generator
.MoveMouseTo(gfx::Point(10, 10));
178 generator
.ClickLeftButton();
179 EXPECT_EQ(1, controller
.execute_action_count());
181 // Move the mouse off the button, and show a popup through a non-user action.
182 // Since this was not a user action, the button should not be pressed.
183 generator
.MoveMouseTo(gfx::Point(300, 300));
184 controller
.ShowPopup(false);
185 EXPECT_EQ(views::Button::STATE_NORMAL
, view
.state());
186 controller
.HidePopup();
188 // Show the popup through a user action - the button should be pressed.
189 controller
.ShowPopup(true);
190 EXPECT_EQ(views::Button::STATE_PRESSED
, view
.state());
191 controller
.HidePopup();
192 EXPECT_EQ(views::Button::STATE_NORMAL
, view
.state());
194 // Ensure that the button's enabled state reflects that of the controller.
195 controller
.SetEnabled(false);
196 EXPECT_EQ(views::Button::STATE_DISABLED
, view
.state());
197 controller
.SetEnabled(true);
198 EXPECT_EQ(views::Button::STATE_NORMAL
, view
.state());
200 // Ensure that clicking on an otherwise-disabled action optionally opens the
202 controller
.SetDisabledClickOpensMenu(true);
203 controller
.SetEnabled(false);
204 EXPECT_EQ(views::Button::STATE_NORMAL
, view
.state());
205 int old_execute_action_count
= controller
.execute_action_count();
207 OpenMenuListener
menu_listener(&view
);
209 EXPECT_TRUE(menu_listener
.opened_menu());
210 EXPECT_EQ(old_execute_action_count
, controller
.execute_action_count());
213 // Ensure that the button's want-to-run state reflects that of the controller.
214 controller
.SetWantsToRun(true);
215 EXPECT_TRUE(view
.wants_to_run_for_testing());
216 controller
.SetWantsToRun(false);
217 EXPECT_FALSE(view
.wants_to_run_for_testing());
219 // Create an overflow button.
220 views::MenuButton
overflow_button(nullptr, base::string16(), nullptr, false);
221 overflow_button
.set_owned_by_client();
222 action_view_delegate
.set_overflow_reference_view(&overflow_button
);
224 // If the view isn't visible, the overflow button should be pressed for
226 view
.SetVisible(false);
227 controller
.ShowPopup(true);
228 EXPECT_EQ(views::Button::STATE_NORMAL
, view
.state());
229 EXPECT_EQ(views::Button::STATE_PRESSED
, overflow_button
.state());
230 controller
.HidePopup();
231 EXPECT_EQ(views::Button::STATE_NORMAL
, view
.state());
232 EXPECT_EQ(views::Button::STATE_NORMAL
, overflow_button
.state());