1 // Copyright 2013 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 "ui/views/controls/combobox/combobox.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "ui/base/models/combobox_model.h"
11 #include "ui/events/event.h"
12 #include "ui/events/keycodes/keyboard_codes.h"
13 #include "ui/views/ime/mock_input_method.h"
14 #include "ui/views/test/views_test_base.h"
15 #include "ui/views/widget/widget.h"
19 // A wrapper of Combobox to intercept the result of OnKeyPressed() and
20 // OnKeyReleased() methods.
21 class TestCombobox : public views::Combobox {
23 explicit TestCombobox(ui::ComboboxModel* model)
26 key_received_(false) {
29 virtual bool OnKeyPressed(const ui::KeyEvent& e) OVERRIDE {
31 key_handled_ = views::Combobox::OnKeyPressed(e);
35 virtual bool OnKeyReleased(const ui::KeyEvent& e) OVERRIDE {
37 key_handled_ = views::Combobox::OnKeyReleased(e);
41 bool key_handled() const { return key_handled_; }
42 bool key_received() const { return key_received_; }
45 key_received_ = key_handled_ = false;
52 DISALLOW_COPY_AND_ASSIGN(TestCombobox);
55 // A concrete class is needed to test the combobox.
56 class TestComboboxModel : public ui::ComboboxModel {
58 TestComboboxModel() {}
59 virtual ~TestComboboxModel() {}
62 virtual int GetItemCount() const OVERRIDE {
65 virtual string16 GetItemAt(int index) OVERRIDE {
66 DCHECK(!IsItemSeparatorAt(index));
67 return ASCIIToUTF16(IsItemSeparatorAt(index) ? "SEPARATOR" : "ITEM");
69 virtual bool IsItemSeparatorAt(int index) OVERRIDE {
70 return separators_.find(index) != separators_.end();
73 void SetSeparators(const std::set<int>& separators) {
74 separators_ = separators;
78 std::set<int> separators_;
80 DISALLOW_COPY_AND_ASSIGN(TestComboboxModel);
87 class ComboboxTest : public ViewsTestBase {
89 ComboboxTest() : widget_(NULL), combobox_(NULL), input_method_(NULL) {}
91 virtual void TearDown() OVERRIDE {
94 ViewsTestBase::TearDown();
98 model_.reset(new TestComboboxModel());
100 ASSERT_FALSE(combobox_);
101 combobox_ = new TestCombobox(model_.get());
102 combobox_->set_id(1);
104 widget_ = new Widget;
105 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
106 params.bounds = gfx::Rect(100, 100, 100, 100);
107 widget_->Init(params);
108 View* container = new View();
109 widget_->SetContentsView(container);
110 container->AddChildView(combobox_);
112 input_method_ = new MockInputMethod();
113 widget_->ReplaceInputMethod(input_method_);
115 // Assumes the Widget is always focused.
116 input_method_->OnFocus();
118 combobox_->RequestFocus();
122 void SendKeyEvent(ui::KeyboardCode key_code) {
123 ui::KeyEvent event(ui::ET_KEY_PRESSED, key_code, 0, false);
124 input_method_->DispatchKeyEvent(event);
127 View* GetFocusedView() {
128 return widget_->GetFocusManager()->GetFocusedView();
131 // We need widget to populate wrapper class.
134 // |combobox_| will be allocated InitCombobox() and then owned by |widget_|.
135 TestCombobox* combobox_;
137 // Combobox does not take ownership of the model, hence it needs to be scoped.
138 scoped_ptr<TestComboboxModel> model_;
140 // For testing input method related behaviors.
141 MockInputMethod* input_method_;
144 TEST_F(ComboboxTest, KeyTest) {
146 SendKeyEvent(ui::VKEY_END);
147 EXPECT_EQ(combobox_->selected_index() + 1, model_->GetItemCount());
148 SendKeyEvent(ui::VKEY_HOME);
149 EXPECT_EQ(combobox_->selected_index(), 0);
150 SendKeyEvent(ui::VKEY_DOWN);
151 SendKeyEvent(ui::VKEY_DOWN);
152 EXPECT_EQ(combobox_->selected_index(), 2);
153 SendKeyEvent(ui::VKEY_RIGHT);
154 EXPECT_EQ(combobox_->selected_index(), 2);
155 SendKeyEvent(ui::VKEY_LEFT);
156 EXPECT_EQ(combobox_->selected_index(), 2);
157 SendKeyEvent(ui::VKEY_UP);
158 EXPECT_EQ(combobox_->selected_index(), 1);
159 SendKeyEvent(ui::VKEY_PRIOR);
160 EXPECT_EQ(combobox_->selected_index(), 0);
161 SendKeyEvent(ui::VKEY_NEXT);
162 EXPECT_EQ(combobox_->selected_index(), model_->GetItemCount() - 1);
165 // Check that if a combobox is disabled before it has a native wrapper, then the
166 // native wrapper inherits the disabled state when it gets created.
167 TEST_F(ComboboxTest, DisabilityTest) {
168 model_.reset(new TestComboboxModel());
170 ASSERT_FALSE(combobox_);
171 combobox_ = new TestCombobox(model_.get());
172 combobox_->SetEnabled(false);
174 widget_ = new Widget;
175 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
176 params.bounds = gfx::Rect(100, 100, 100, 100);
177 widget_->Init(params);
178 View* container = new View();
179 widget_->SetContentsView(container);
180 container->AddChildView(combobox_);
181 EXPECT_FALSE(combobox_->enabled());
184 // Verifies that we don't select a separator line in combobox when navigating
186 TEST_F(ComboboxTest, SkipSeparatorSimple) {
188 std::set<int> separators;
189 separators.insert(2);
190 model_->SetSeparators(separators);
191 EXPECT_EQ(0, combobox_->selected_index());
192 SendKeyEvent(ui::VKEY_DOWN);
193 EXPECT_EQ(1, combobox_->selected_index());
194 SendKeyEvent(ui::VKEY_DOWN);
195 EXPECT_EQ(3, combobox_->selected_index());
196 SendKeyEvent(ui::VKEY_UP);
197 EXPECT_EQ(1, combobox_->selected_index());
198 SendKeyEvent(ui::VKEY_HOME);
199 EXPECT_EQ(0, combobox_->selected_index());
200 SendKeyEvent(ui::VKEY_PRIOR);
201 EXPECT_EQ(0, combobox_->selected_index());
202 SendKeyEvent(ui::VKEY_END);
203 EXPECT_EQ(9, combobox_->selected_index());
206 // Verifies that we never select the separator that is in the beginning of the
207 // combobox list when navigating through keyboard.
208 TEST_F(ComboboxTest, SkipSeparatorBeginning) {
210 std::set<int> separators;
211 separators.insert(0);
212 model_->SetSeparators(separators);
213 EXPECT_EQ(0, combobox_->selected_index());
214 SendKeyEvent(ui::VKEY_DOWN);
215 EXPECT_EQ(1, combobox_->selected_index());
216 SendKeyEvent(ui::VKEY_DOWN);
217 EXPECT_EQ(2, combobox_->selected_index());
218 SendKeyEvent(ui::VKEY_UP);
219 EXPECT_EQ(1, combobox_->selected_index());
220 SendKeyEvent(ui::VKEY_HOME);
221 EXPECT_EQ(1, combobox_->selected_index());
222 SendKeyEvent(ui::VKEY_PRIOR);
223 EXPECT_EQ(1, combobox_->selected_index());
224 SendKeyEvent(ui::VKEY_END);
225 EXPECT_EQ(9, combobox_->selected_index());
228 // Verifies that we never select the separator that is in the end of the
229 // combobox list when navigating through keyboard.
230 TEST_F(ComboboxTest, SkipSeparatorEnd) {
232 std::set<int> separators;
233 separators.insert(model_->GetItemCount() - 1);
234 model_->SetSeparators(separators);
235 combobox_->SetSelectedIndex(8);
236 SendKeyEvent(ui::VKEY_DOWN);
237 EXPECT_EQ(8, combobox_->selected_index());
238 SendKeyEvent(ui::VKEY_UP);
239 EXPECT_EQ(7, combobox_->selected_index());
240 SendKeyEvent(ui::VKEY_END);
241 EXPECT_EQ(8, combobox_->selected_index());
244 // Verifies that we never select any of the adjacent separators (multiple
245 // consecutive) that appear in the beginning of the combobox list when
246 // navigating through keyboard.
247 TEST_F(ComboboxTest, SkipMultipleSeparatorsAtBeginning) {
249 std::set<int> separators;
250 separators.insert(0);
251 separators.insert(1);
252 separators.insert(2);
253 model_->SetSeparators(separators);
254 EXPECT_EQ(0, combobox_->selected_index());
255 SendKeyEvent(ui::VKEY_DOWN);
256 EXPECT_EQ(3, combobox_->selected_index());
257 SendKeyEvent(ui::VKEY_UP);
258 EXPECT_EQ(3, combobox_->selected_index());
259 SendKeyEvent(ui::VKEY_NEXT);
260 EXPECT_EQ(9, combobox_->selected_index());
261 SendKeyEvent(ui::VKEY_HOME);
262 EXPECT_EQ(3, combobox_->selected_index());
263 SendKeyEvent(ui::VKEY_END);
264 EXPECT_EQ(9, combobox_->selected_index());
265 SendKeyEvent(ui::VKEY_PRIOR);
266 EXPECT_EQ(3, combobox_->selected_index());
269 // Verifies that we never select any of the adjacent separators (multiple
270 // consecutive) that appear in the middle of the combobox list when navigating
272 TEST_F(ComboboxTest, SkipMultipleAdjacentSeparatorsAtMiddle) {
274 std::set<int> separators;
275 separators.insert(4);
276 separators.insert(5);
277 separators.insert(6);
278 model_->SetSeparators(separators);
279 combobox_->SetSelectedIndex(3);
280 SendKeyEvent(ui::VKEY_DOWN);
281 EXPECT_EQ(7, combobox_->selected_index());
282 SendKeyEvent(ui::VKEY_UP);
283 EXPECT_EQ(3, combobox_->selected_index());
286 // Verifies that we never select any of the adjacent separators (multiple
287 // consecutive) that appear in the end of the combobox list when navigating
289 TEST_F(ComboboxTest, SkipMultipleSeparatorsAtEnd) {
291 std::set<int> separators;
292 separators.insert(7);
293 separators.insert(8);
294 separators.insert(9);
295 model_->SetSeparators(separators);
296 combobox_->SetSelectedIndex(6);
297 SendKeyEvent(ui::VKEY_DOWN);
298 EXPECT_EQ(6, combobox_->selected_index());
299 SendKeyEvent(ui::VKEY_UP);
300 EXPECT_EQ(5, combobox_->selected_index());
301 SendKeyEvent(ui::VKEY_HOME);
302 EXPECT_EQ(0, combobox_->selected_index());
303 SendKeyEvent(ui::VKEY_NEXT);
304 EXPECT_EQ(6, combobox_->selected_index());
305 SendKeyEvent(ui::VKEY_PRIOR);
306 EXPECT_EQ(0, combobox_->selected_index());
307 SendKeyEvent(ui::VKEY_END);
308 EXPECT_EQ(6, combobox_->selected_index());
311 TEST_F(ComboboxTest, GetTextForRowTest) {
313 std::set<int> separators;
314 separators.insert(0);
315 separators.insert(1);
316 separators.insert(9);
317 model_->SetSeparators(separators);
318 for (int i = 0; i < combobox_->GetRowCount(); ++i) {
319 if (separators.count(i) != 0)
320 EXPECT_TRUE(combobox_->GetTextForRow(i).empty()) << i;
322 EXPECT_EQ(ASCIIToUTF16("ITEM"), combobox_->GetTextForRow(i)) << i;