1 // Copyright 2016 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.
4 
5 #include "chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.h"
6 
7 #include <memory>
8 
9 #include "base/run_loop.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/ui/toolbar/test_toolbar_actions_bar_bubble_delegate.h"
12 #include "chrome/browser/ui/toolbar/toolbar_actions_bar_bubble_delegate.h"
13 #include "chrome/grit/generated_resources.h"
14 #include "chrome/test/views/chrome_views_test_base.h"
15 #include "components/vector_icons/vector_icons.h"
16 #include "ui/base/l10n/l10n_util.h"
17 #include "ui/base/resource/resource_bundle.h"
18 #include "ui/events/event_utils.h"
19 #include "ui/gfx/color_palette.h"
20 #include "ui/gfx/image/image.h"
21 #include "ui/gfx/image/image_unittest_util.h"
22 #include "ui/gfx/paint_vector_icon.h"
23 #include "ui/views/controls/button/image_button.h"
24 #include "ui/views/controls/button/label_button.h"
25 #include "ui/views/controls/link.h"
26 #include "ui/views/test/test_widget_observer.h"
27 #include "ui/views/widget/widget.h"
28 
29 namespace {
30 const int kIconSize = 16;
31 }
32 
33 class ToolbarActionsBarBubbleViewsTest : public ChromeViewsTestBase {
34  protected:
ToolbarActionsBarBubbleViewsTest()35   ToolbarActionsBarBubbleViewsTest() {}
36   ToolbarActionsBarBubbleViewsTest(const ToolbarActionsBarBubbleViewsTest&) =
37       delete;
38   ToolbarActionsBarBubbleViewsTest& operator=(
39       const ToolbarActionsBarBubbleViewsTest&) = delete;
40   ~ToolbarActionsBarBubbleViewsTest() override = default;
41 
TearDown()42   void TearDown() override {
43     anchor_widget_.reset();
44     ChromeViewsTestBase::TearDown();
45   }
46 
CreateAnchorWidget()47   std::unique_ptr<views::Widget> CreateAnchorWidget() {
48     std::unique_ptr<views::Widget> anchor_widget =
49         CreateTestWidget(views::Widget::InitParams::TYPE_WINDOW);
50     anchor_widget->Show();
51     return anchor_widget;
52   }
53 
ShowBubble(TestToolbarActionsBarBubbleDelegate * delegate)54   void ShowBubble(TestToolbarActionsBarBubbleDelegate* delegate) {
55     ASSERT_TRUE(delegate);
56     ASSERT_FALSE(bubble_widget_);
57     ASSERT_FALSE(bubble_);
58     anchor_widget_ = CreateAnchorWidget();
59     bool anchored_to_action = false;
60     bubble_ = new ToolbarActionsBarBubbleViews(
61         anchor_widget_->GetContentsView(), anchored_to_action,
62         delegate->GetDelegate());
63     bubble_widget_ = views::BubbleDialogDelegateView::CreateBubble(bubble_);
64     bubble_widget_->Show();
65   }
66 
CloseBubble()67   void CloseBubble() {
68     ASSERT_TRUE(bubble_);
69     bubble_->GetWidget()->Close();
70     base::RunLoop().RunUntilIdle();
71     bubble_ = nullptr;
72     bubble_widget_ = nullptr;
73   }
74 
ClickButton(views::Button * button)75   void ClickButton(views::Button* button) {
76     bubble()->ResetViewShownTimeStampForTesting();
77 
78     ASSERT_TRUE(button);
79     const gfx::Point point(10, 10);
80     const ui::MouseEvent event(ui::ET_MOUSE_PRESSED, point, point,
81                                ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
82                                ui::EF_LEFT_MOUSE_BUTTON);
83     button->OnMousePressed(event);
84     button->OnMouseReleased(event);
85     base::RunLoop().RunUntilIdle();
86   }
87 
HeadingString()88   base::string16 HeadingString() { return base::ASCIIToUTF16("Heading"); }
BodyString()89   base::string16 BodyString() { return base::ASCIIToUTF16("Body"); }
ActionString()90   base::string16 ActionString() { return base::ASCIIToUTF16("Action"); }
DismissString()91   base::string16 DismissString() { return base::ASCIIToUTF16("Dismiss"); }
LearnMoreString()92   base::string16 LearnMoreString() { return base::ASCIIToUTF16("Learn"); }
ItemListString()93   base::string16 ItemListString() {
94     return base::ASCIIToUTF16("Item 1\nItem2");
95   }
96 
anchor_widget()97   views::Widget* anchor_widget() { return anchor_widget_.get(); }
bubble_widget()98   views::Widget* bubble_widget() { return bubble_widget_; }
bubble()99   ToolbarActionsBarBubbleViews* bubble() { return bubble_; }
100 
101  private:
102   std::unique_ptr<views::Widget> anchor_widget_;
103   views::Widget* bubble_widget_ = nullptr;
104   ToolbarActionsBarBubbleViews* bubble_ = nullptr;
105 };
106 
TEST_F(ToolbarActionsBarBubbleViewsTest,TestBubbleLayoutActionButton)107 TEST_F(ToolbarActionsBarBubbleViewsTest, TestBubbleLayoutActionButton) {
108   TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
109                                                ActionString());
110   ShowBubble(&delegate);
111 
112   EXPECT_TRUE(bubble()->GetOkButton());
113   EXPECT_EQ(ActionString(), bubble()->GetOkButton()->GetText());
114   EXPECT_FALSE(bubble()->GetCancelButton());
115 
116   CloseBubble();
117 }
118 
TEST_F(ToolbarActionsBarBubbleViewsTest,TestBubbleLayoutNoButtons)119 TEST_F(ToolbarActionsBarBubbleViewsTest, TestBubbleLayoutNoButtons) {
120   TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
121                                                ActionString());
122   std::unique_ptr<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>
123       extra_view_info =
124           std::make_unique<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>();
125   delegate.set_extra_view_info(std::move(extra_view_info));
126   delegate.set_dismiss_button_text(base::string16());
127   delegate.set_action_button_text(base::string16());
128   ShowBubble(&delegate);
129 
130   EXPECT_EQ(nullptr, bubble()->GetExtraView());
131   EXPECT_FALSE(bubble()->GetOkButton());
132   EXPECT_FALSE(bubble()->GetCancelButton());
133   EXPECT_FALSE(bubble()->learn_more_button());
134 
135   CloseBubble();
136 }
137 
TEST_F(ToolbarActionsBarBubbleViewsTest,TestBubbleLayoutActionAndDismissButton)138 TEST_F(ToolbarActionsBarBubbleViewsTest,
139        TestBubbleLayoutActionAndDismissButton) {
140   TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
141                                                ActionString());
142   delegate.set_dismiss_button_text(DismissString());
143   ShowBubble(&delegate);
144 
145   EXPECT_TRUE(bubble()->GetOkButton());
146   EXPECT_EQ(ActionString(), bubble()->GetOkButton()->GetText());
147   EXPECT_TRUE(bubble()->GetCancelButton());
148   EXPECT_EQ(DismissString(), bubble()->GetCancelButton()->GetText());
149 
150   EXPECT_FALSE(bubble()->learn_more_button());
151   EXPECT_FALSE(bubble()->item_list());
152 
153   CloseBubble();
154 }
155 
TEST_F(ToolbarActionsBarBubbleViewsTest,TestBubbleLayoutActionDismissAndLearnMoreButton)156 TEST_F(ToolbarActionsBarBubbleViewsTest,
157        TestBubbleLayoutActionDismissAndLearnMoreButton) {
158   TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
159                                                ActionString());
160   delegate.set_dismiss_button_text(DismissString());
161   std::unique_ptr<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>
162       extra_view_info_linked_text =
163           std::make_unique<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>();
164   extra_view_info_linked_text->text = LearnMoreString();
165   extra_view_info_linked_text->is_learn_more = true;
166   delegate.set_extra_view_info(std::move(extra_view_info_linked_text));
167 
168   ShowBubble(&delegate);
169 
170   EXPECT_TRUE(bubble()->GetOkButton());
171   EXPECT_EQ(ActionString(), bubble()->GetOkButton()->GetText());
172   EXPECT_TRUE(bubble()->GetCancelButton());
173   EXPECT_EQ(DismissString(), bubble()->GetCancelButton()->GetText());
174   EXPECT_TRUE(bubble()->learn_more_button());
175   EXPECT_EQ(LearnMoreString(),
176             bubble()->learn_more_button()->GetTooltipText(gfx::Point(0, 0)));
177   EXPECT_FALSE(bubble()->item_list());
178 
179   CloseBubble();
180 }
181 
TEST_F(ToolbarActionsBarBubbleViewsTest,TestBubbleLayoutListView)182 TEST_F(ToolbarActionsBarBubbleViewsTest, TestBubbleLayoutListView) {
183   TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
184                                                ActionString());
185   delegate.set_item_list_text(ItemListString());
186   ShowBubble(&delegate);
187 
188   EXPECT_TRUE(bubble()->GetOkButton());
189   EXPECT_EQ(ActionString(), bubble()->GetOkButton()->GetText());
190   EXPECT_FALSE(bubble()->GetCancelButton());
191   EXPECT_FALSE(bubble()->learn_more_button());
192   EXPECT_TRUE(bubble()->item_list());
193   EXPECT_EQ(ItemListString(), bubble()->item_list()->GetText());
194 
195   CloseBubble();
196 }
197 
TEST_F(ToolbarActionsBarBubbleViewsTest,TestBubbleLayoutNoBodyText)198 TEST_F(ToolbarActionsBarBubbleViewsTest, TestBubbleLayoutNoBodyText) {
199   TestToolbarActionsBarBubbleDelegate delegate(
200       HeadingString(), base::string16(), ActionString());
201   ShowBubble(&delegate);
202 
203   EXPECT_TRUE(bubble()->GetOkButton());
204   EXPECT_EQ(ActionString(), bubble()->GetOkButton()->GetText());
205   EXPECT_FALSE(bubble()->GetCancelButton());
206   EXPECT_FALSE(bubble()->learn_more_button());
207   EXPECT_FALSE(bubble()->body_text());
208   EXPECT_FALSE(bubble()->item_list());
209 
210   CloseBubble();
211 }
212 
TEST_F(ToolbarActionsBarBubbleViewsTest,TestBubbleDefaultDialogButtons)213 TEST_F(ToolbarActionsBarBubbleViewsTest, TestBubbleDefaultDialogButtons) {
214   TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
215                                                ActionString());
216   delegate.set_dismiss_button_text(DismissString());
217   delegate.set_default_dialog_button(ui::DIALOG_BUTTON_OK);
218   ShowBubble(&delegate);
219 
220   ASSERT_TRUE(bubble()->GetOkButton());
221   EXPECT_TRUE(bubble()->GetOkButton()->GetIsDefault());
222 
223   ASSERT_TRUE(bubble()->GetCancelButton());
224   EXPECT_FALSE(bubble()->GetCancelButton()->GetIsDefault());
225 
226   CloseBubble();
227 }
228 
TEST_F(ToolbarActionsBarBubbleViewsTest,TestShowAndCloseBubble)229 TEST_F(ToolbarActionsBarBubbleViewsTest, TestShowAndCloseBubble) {
230   std::unique_ptr<views::Widget> anchor_widget = CreateAnchorWidget();
231   TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
232                                                ActionString());
233   delegate.set_dismiss_button_text(DismissString());
234   ToolbarActionsBarBubbleViews* bubble = new ToolbarActionsBarBubbleViews(
235       anchor_widget->GetContentsView(), false, delegate.GetDelegate());
236 
237   EXPECT_FALSE(delegate.shown());
238   EXPECT_FALSE(delegate.close_action());
239   views::Widget* bubble_widget =
240       views::BubbleDialogDelegateView::CreateBubble(bubble);
241   views::test::TestWidgetObserver bubble_observer(bubble_widget);
242   bubble_widget->Show();
243   EXPECT_TRUE(delegate.shown());
244   EXPECT_FALSE(delegate.close_action());
245 
246   bubble->CancelDialog();
247   base::RunLoop().RunUntilIdle();
248   ASSERT_TRUE(delegate.close_action());
249   EXPECT_EQ(ToolbarActionsBarBubbleDelegate::CLOSE_DISMISS_USER_ACTION,
250             *delegate.close_action());
251   EXPECT_TRUE(bubble_observer.widget_closed());
252 }
253 
TEST_F(ToolbarActionsBarBubbleViewsTest,TestClickActionButton)254 TEST_F(ToolbarActionsBarBubbleViewsTest, TestClickActionButton) {
255   TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
256                                                ActionString());
257   delegate.set_dismiss_button_text(DismissString());
258   delegate.set_learn_more_button_text(LearnMoreString());
259   ShowBubble(&delegate);
260   views::test::TestWidgetObserver bubble_observer(bubble_widget());
261 
262   EXPECT_FALSE(delegate.close_action());
263 
264   ClickButton(bubble()->GetOkButton());
265   ASSERT_TRUE(delegate.close_action());
266   EXPECT_EQ(ToolbarActionsBarBubbleDelegate::CLOSE_EXECUTE,
267             *delegate.close_action());
268   EXPECT_TRUE(bubble_observer.widget_closed());
269 }
270 
TEST_F(ToolbarActionsBarBubbleViewsTest,TestClickDismissButton)271 TEST_F(ToolbarActionsBarBubbleViewsTest, TestClickDismissButton) {
272   TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
273                                                ActionString());
274   delegate.set_dismiss_button_text(DismissString());
275   delegate.set_learn_more_button_text(LearnMoreString());
276   ShowBubble(&delegate);
277   views::test::TestWidgetObserver bubble_observer(bubble_widget());
278 
279   EXPECT_FALSE(delegate.close_action());
280 
281   ClickButton(bubble()->GetCancelButton());
282   ASSERT_TRUE(delegate.close_action());
283   EXPECT_EQ(ToolbarActionsBarBubbleDelegate::CLOSE_DISMISS_USER_ACTION,
284             *delegate.close_action());
285 }
286 
TEST_F(ToolbarActionsBarBubbleViewsTest,TestClickLearnMoreLink)287 TEST_F(ToolbarActionsBarBubbleViewsTest, TestClickLearnMoreLink) {
288   TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
289                                                ActionString());
290   delegate.set_dismiss_button_text(DismissString());
291   delegate.set_learn_more_button_text(LearnMoreString());
292   ShowBubble(&delegate);
293   views::test::TestWidgetObserver bubble_observer(bubble_widget());
294 
295   EXPECT_FALSE(delegate.close_action());
296 
297   ClickButton(bubble()->learn_more_button());
298   ASSERT_TRUE(delegate.close_action());
299   EXPECT_EQ(ToolbarActionsBarBubbleDelegate::CLOSE_LEARN_MORE,
300             *delegate.close_action());
301 }
302 
TEST_F(ToolbarActionsBarBubbleViewsTest,TestCloseOnDeactivation)303 TEST_F(ToolbarActionsBarBubbleViewsTest, TestCloseOnDeactivation) {
304   TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
305                                                ActionString());
306   ShowBubble(&delegate);
307   views::test::TestWidgetObserver bubble_observer(bubble_widget());
308 
309   EXPECT_FALSE(delegate.close_action());
310   // Close the bubble by activating another widget. The delegate should be
311   // told it was dismissed.
312   anchor_widget()->Activate();
313   base::RunLoop().RunUntilIdle();
314   ASSERT_TRUE(delegate.close_action());
315   EXPECT_EQ(ToolbarActionsBarBubbleDelegate::CLOSE_DISMISS_DEACTIVATION,
316             *delegate.close_action());
317   EXPECT_TRUE(bubble_observer.widget_closed());
318 }
319 
TEST_F(ToolbarActionsBarBubbleViewsTest,TestDontCloseOnDeactivation)320 TEST_F(ToolbarActionsBarBubbleViewsTest, TestDontCloseOnDeactivation) {
321   TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
322                                                ActionString());
323   delegate.set_close_on_deactivate(false);
324   ShowBubble(&delegate);
325   views::test::TestWidgetObserver bubble_observer(bubble_widget());
326 
327   EXPECT_FALSE(delegate.close_action());
328   // Activate another widget. The bubble shouldn't close.
329   anchor_widget()->Activate();
330   base::RunLoop().RunUntilIdle();
331   EXPECT_FALSE(delegate.close_action());
332   CloseBubble();
333 }
334 
TEST_F(ToolbarActionsBarBubbleViewsTest,TestNullExtraView)335 TEST_F(ToolbarActionsBarBubbleViewsTest, TestNullExtraView) {
336   TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
337                                                ActionString());
338   ShowBubble(&delegate);
339   EXPECT_EQ(nullptr, bubble()->GetExtraView());
340   CloseBubble();
341 }
342 
TEST_F(ToolbarActionsBarBubbleViewsTest,TestCreateExtraViewIconOnly)343 TEST_F(ToolbarActionsBarBubbleViewsTest, TestCreateExtraViewIconOnly) {
344   TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
345                                                ActionString());
346   std::unique_ptr<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>
347       extra_view_info =
348           std::make_unique<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>();
349   extra_view_info->resource = &vector_icons::kBusinessIcon;
350   delegate.set_extra_view_info(std::move(extra_view_info));
351   ShowBubble(&delegate);
352   const views::View* const extra_view = bubble()->GetExtraView();
353   ASSERT_TRUE(extra_view);
354   ASSERT_EQ("ImageView", std::string(extra_view->GetClassName()));
355   EXPECT_TRUE(gfx::test::AreImagesEqual(
356       gfx::Image(static_cast<const views::ImageView*>(extra_view)->GetImage()),
357       gfx::Image(gfx::CreateVectorIcon(vector_icons::kBusinessIcon, kIconSize,
358                                        gfx::kChromeIconGrey))));
359   CloseBubble();
360 }
361 
TEST_F(ToolbarActionsBarBubbleViewsTest,TestCreateExtraViewLinkedTextOnly)362 TEST_F(ToolbarActionsBarBubbleViewsTest, TestCreateExtraViewLinkedTextOnly) {
363   TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
364                                                ActionString());
365   std::unique_ptr<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>
366       extra_view_info_linked_text =
367           std::make_unique<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>();
368   extra_view_info_linked_text->text =
369       l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSTALLED_BY_ADMIN);
370   extra_view_info_linked_text->is_learn_more = true;
371   delegate.set_extra_view_info(std::move(extra_view_info_linked_text));
372 
373   ShowBubble(&delegate);
374 
375   const views::View* const extra_view = bubble()->GetExtraView();
376   ASSERT_TRUE(extra_view);
377   ASSERT_EQ("ImageButton", std::string(extra_view->GetClassName()));
378   EXPECT_EQ(l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSTALLED_BY_ADMIN),
379             extra_view->GetTooltipText(gfx::Point(0, 0)));
380   CloseBubble();
381 }
382 
TEST_F(ToolbarActionsBarBubbleViewsTest,TestCreateExtraViewLabelTextOnly)383 TEST_F(ToolbarActionsBarBubbleViewsTest, TestCreateExtraViewLabelTextOnly) {
384   TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
385                                                ActionString());
386   std::unique_ptr<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>
387       extra_view_info =
388           std::make_unique<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>();
389   extra_view_info->text =
390       l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSTALLED_BY_ADMIN);
391   extra_view_info->is_learn_more = false;
392   delegate.set_extra_view_info(std::move(extra_view_info));
393 
394   ShowBubble(&delegate);
395 
396   const views::View* const extra_view = bubble()->GetExtraView();
397   ASSERT_TRUE(extra_view);
398   EXPECT_EQ("Label", std::string(extra_view->GetClassName()));
399   EXPECT_EQ(l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSTALLED_BY_ADMIN),
400             static_cast<const views::Label*>(extra_view)->GetText());
401   CloseBubble();
402 }
403 
TEST_F(ToolbarActionsBarBubbleViewsTest,TestCreateExtraViewImageAndText)404 TEST_F(ToolbarActionsBarBubbleViewsTest, TestCreateExtraViewImageAndText) {
405   TestToolbarActionsBarBubbleDelegate delegate(HeadingString(), BodyString(),
406                                                ActionString());
407   std::unique_ptr<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>
408       extra_view_info =
409           std::make_unique<ToolbarActionsBarBubbleDelegate::ExtraViewInfo>();
410   extra_view_info->resource = &vector_icons::kBusinessIcon;
411   extra_view_info->text =
412       l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSTALLED_BY_ADMIN);
413   extra_view_info->is_learn_more = false;
414   delegate.set_extra_view_info(std::move(extra_view_info));
415 
416   ShowBubble(&delegate);
417 
418   const views::View* const extra_view = bubble()->GetExtraView();
419   ASSERT_TRUE(extra_view);
420   EXPECT_STREQ("View", extra_view->GetClassName());
421   EXPECT_EQ(2u, extra_view->children().size());
422 
423   for (const views::View* v : extra_view->children()) {
424     std::string class_name = v->GetClassName();
425     if (class_name == "Label") {
426       EXPECT_EQ(l10n_util::GetStringUTF16(IDS_EXTENSIONS_INSTALLED_BY_ADMIN),
427                 static_cast<const views::Label*>(v)->GetText());
428     } else {
429       ASSERT_EQ("ImageView", class_name);
430       EXPECT_TRUE(gfx::test::AreImagesEqual(
431           gfx::Image(static_cast<const views::ImageView*>(v)->GetImage()),
432           gfx::Image(gfx::CreateVectorIcon(vector_icons::kBusinessIcon,
433                                            kIconSize, gfx::kChromeIconGrey))));
434     }
435   }
436 
437   CloseBubble();
438 }
439