1 // Copyright 2019 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/tabs/color_picker_view.h"
6 
7 #include <array>
8 #include <memory>
9 #include <utility>
10 
11 #include "base/strings/string16.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/test/mock_callback.h"
14 #include "base/time/time.h"
15 #include "chrome/browser/ui/views/tabs/tab_group_editor_bubble_view.h"
16 #include "chrome/test/views/chrome_views_test_base.h"
17 #include "components/tab_groups/tab_group_color.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "third_party/skia/include/core/SkColor.h"
20 #include "ui/events/event.h"
21 #include "ui/events/keycodes/dom/dom_codes.h"
22 #include "ui/events/keycodes/keyboard_code_conversion.h"
23 #include "ui/gfx/canvas.h"
24 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
25 #include "ui/views/controls/button/button.h"
26 #include "ui/views/focus/focus_manager.h"
27 #include "ui/views/test/widget_test.h"
28 #include "ui/views/widget/widget.h"
29 
30 namespace {
31 
32 static const TabGroupEditorBubbleView::Colors kTestColors({
33     {tab_groups::TabGroupColorId::kRed, base::ASCIIToUTF16("Red")},
34     {tab_groups::TabGroupColorId::kGreen, base::ASCIIToUTF16("Green")},
35     {tab_groups::TabGroupColorId::kBlue, base::ASCIIToUTF16("Blue")},
36 });
37 
38 }  // namespace
39 
40 class ColorPickerViewTest : public ChromeViewsTestBase {
41  protected:
SetUp()42   void SetUp() override {
43     ChromeViewsTestBase::SetUp();
44 
45     widget_ = CreateTestWidget();
46     bubble_view_ = std::make_unique<views::BubbleDialogDelegateView>();
47 
48     auto color_picker = std::make_unique<ColorPickerView>(
49         bubble_view(), kTestColors, tab_groups::TabGroupColorId::kBlue,
50         color_selected_callback_.Get());
51     color_picker->SizeToPreferredSize();
52     color_picker_ = widget_->SetContentsView(std::move(color_picker));
53     widget_->Show();
54   }
55 
TearDown()56   void TearDown() override {
57     widget_.reset();
58 
59     ChromeViewsTestBase::TearDown();
60   }
61 
ClickColorElement(views::Button * element)62   void ClickColorElement(views::Button* element) {
63     gfx::Point center = element->GetLocalBounds().CenterPoint();
64     gfx::Point root_center = center;
65     views::View::ConvertPointToWidget(color_picker_, &root_center);
66 
67     ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED, center, root_center,
68                                  base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON,
69                                  0);
70     element->OnMousePressed(pressed_event);
71 
72     ui::MouseEvent released_event(ui::ET_MOUSE_RELEASED, center, root_center,
73                                   base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON,
74                                   0);
75     element->OnMouseReleased(released_event);
76   }
77 
ClickColorAtIndex(int index)78   void ClickColorAtIndex(int index) {
79     ClickColorElement(color_picker_->GetElementAtIndexForTesting(index));
80   }
81 
bubble_view()82   const views::BubbleDialogDelegateView* bubble_view() {
83     return bubble_view_.get();
84   }
85 
86   ::testing::NiceMock<
87       base::MockCallback<ColorPickerView::ColorSelectedCallback>>
88       color_selected_callback_;
89   ColorPickerView* color_picker_;
90 
91  private:
92   std::unique_ptr<views::Widget> widget_;
93   std::unique_ptr<views::BubbleDialogDelegateView> bubble_view_;
94 };
95 
TEST_F(ColorPickerViewTest,ColorSelectedByDefaultIfMatching)96 TEST_F(ColorPickerViewTest, ColorSelectedByDefaultIfMatching) {
97   std::unique_ptr<views::Widget> widget = CreateTestWidget();
98 
99   ColorPickerView* color_picker =
100       widget->SetContentsView(std::make_unique<ColorPickerView>(
101           bubble_view(), kTestColors, tab_groups::TabGroupColorId::kRed,
102           color_selected_callback_.Get()));
103 
104   color_picker->SizeToPreferredSize();
105 
106   EXPECT_TRUE(color_picker->GetSelectedElement().has_value());
107   // Expect the index to match that of TabGroupId::kRed.
108   EXPECT_EQ(color_picker->GetSelectedElement().value(), 0);
109 }
110 
TEST_F(ColorPickerViewTest,ClickingSelectsColor)111 TEST_F(ColorPickerViewTest, ClickingSelectsColor) {
112   ClickColorAtIndex(0);
113   EXPECT_EQ(0, color_picker_->GetSelectedElement());
114 
115   ClickColorAtIndex(1);
116   EXPECT_EQ(1, color_picker_->GetSelectedElement());
117 }
118 
TEST_F(ColorPickerViewTest,ColorNotDeselected)119 TEST_F(ColorPickerViewTest, ColorNotDeselected) {
120   ClickColorAtIndex(0);
121   ClickColorAtIndex(0);
122   EXPECT_EQ(0, color_picker_->GetSelectedElement());
123 }
124 
TEST_F(ColorPickerViewTest,SelectingColorNotifiesCallback)125 TEST_F(ColorPickerViewTest, SelectingColorNotifiesCallback) {
126   EXPECT_CALL(color_selected_callback_, Run()).Times(2);
127 
128   ClickColorAtIndex(0);
129   ClickColorAtIndex(1);
130 }
131 
TEST_F(ColorPickerViewTest,CallbackNotifiedOnce)132 TEST_F(ColorPickerViewTest, CallbackNotifiedOnce) {
133   EXPECT_CALL(color_selected_callback_, Run()).Times(1);
134 
135   ClickColorAtIndex(0);
136   ClickColorAtIndex(0);
137 }
138 
TEST_F(ColorPickerViewTest,KeyboardFocusBehavesLikeRadioButtons)139 TEST_F(ColorPickerViewTest, KeyboardFocusBehavesLikeRadioButtons) {
140   views::FocusManager* focus_manager = color_picker_->GetFocusManager();
141 
142   // Focus should start at the selected element.
143   focus_manager->AdvanceFocus(false);
144   EXPECT_EQ(color_picker_->GetElementAtIndexForTesting(2),
145             focus_manager->GetFocusedView());
146 
147   // Pressing arrow keys should cycle through the elements.
148   ui::KeyEvent arrow_event(
149       ui::EventType::ET_KEY_PRESSED,
150       ui::DomCodeToUsLayoutKeyboardCode(ui::DomCode::ARROW_RIGHT),
151       ui::DomCode::ARROW_RIGHT, ui::EF_NONE);
152   EXPECT_FALSE(focus_manager->OnKeyEvent(arrow_event));
153   EXPECT_EQ(color_picker_->GetElementAtIndexForTesting(0),
154             focus_manager->GetFocusedView());
155 
156   focus_manager->ClearFocus();
157   ClickColorAtIndex(1);
158 
159   // Re-entering should restore focus to the currently selected color.
160   focus_manager->AdvanceFocus(false);
161   EXPECT_EQ(color_picker_->GetElementAtIndexForTesting(1),
162             focus_manager->GetFocusedView());
163 }
164