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