Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ui / views / controls / combobox / combobox_unittest.cc
blob2a488a2215afc80ed145f73107bbb9f8f09d7d53
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"
7 #include <set>
9 #include "base/basictypes.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "ui/base/ime/input_method.h"
12 #include "ui/base/ime/text_input_client.h"
13 #include "ui/base/models/combobox_model.h"
14 #include "ui/base/models/menu_model.h"
15 #include "ui/events/event.h"
16 #include "ui/events/event_constants.h"
17 #include "ui/events/event_utils.h"
18 #include "ui/events/keycodes/keyboard_codes.h"
19 #include "ui/views/controls/combobox/combobox_listener.h"
20 #include "ui/views/controls/menu/menu_runner.h"
21 #include "ui/views/controls/menu/menu_runner_handler.h"
22 #include "ui/views/test/menu_runner_test_api.h"
23 #include "ui/views/test/views_test_base.h"
24 #include "ui/views/widget/widget.h"
26 using base::ASCIIToUTF16;
28 namespace views {
29 namespace test {
31 class ComboboxTestApi {
32 public:
33 explicit ComboboxTestApi(Combobox* combobox) : combobox_(combobox) {}
35 void PerformActionAt(int index) { menu_model()->ActivatedAt(index); }
36 void InstallTestMenuRunner(int* menu_show_count);
38 gfx::Size content_size() { return combobox_->content_size_; }
39 ui::MenuModel* menu_model() { return combobox_->menu_model_adapter_.get(); }
41 private:
42 Combobox* combobox_;
44 DISALLOW_COPY_AND_ASSIGN(ComboboxTestApi);
47 } // namespace test
49 using test::ComboboxTestApi;
51 namespace {
53 // An dummy implementation of MenuRunnerHandler to check if the dropdown menu is
54 // shown or not.
55 class TestMenuRunnerHandler : public MenuRunnerHandler {
56 public:
57 explicit TestMenuRunnerHandler(int* show_counter)
58 : show_counter_(show_counter) {}
59 MenuRunner::RunResult RunMenuAt(Widget* parent,
60 MenuButton* button,
61 const gfx::Rect& bounds,
62 MenuAnchorPosition anchor,
63 ui::MenuSourceType source_type,
64 int32 types) override {
65 *show_counter_ += 1;
66 return MenuRunner::NORMAL_EXIT;
69 private:
70 int* show_counter_;
72 DISALLOW_COPY_AND_ASSIGN(TestMenuRunnerHandler);
75 // A wrapper of Combobox to intercept the result of OnKeyPressed() and
76 // OnKeyReleased() methods.
77 class TestCombobox : public Combobox {
78 public:
79 explicit TestCombobox(ui::ComboboxModel* model)
80 : Combobox(model),
81 key_handled_(false),
82 key_received_(false) {}
84 bool OnKeyPressed(const ui::KeyEvent& e) override {
85 key_received_ = true;
86 key_handled_ = Combobox::OnKeyPressed(e);
87 return key_handled_;
90 bool OnKeyReleased(const ui::KeyEvent& e) override {
91 key_received_ = true;
92 key_handled_ = Combobox::OnKeyReleased(e);
93 return key_handled_;
96 bool key_handled() const { return key_handled_; }
97 bool key_received() const { return key_received_; }
99 void clear() {
100 key_received_ = key_handled_ = false;
103 private:
104 bool key_handled_;
105 bool key_received_;
107 DISALLOW_COPY_AND_ASSIGN(TestCombobox);
110 // A concrete class is needed to test the combobox.
111 class TestComboboxModel : public ui::ComboboxModel {
112 public:
113 TestComboboxModel() {}
114 ~TestComboboxModel() override {}
116 enum { kItemCount = 10 };
118 // ui::ComboboxModel:
119 int GetItemCount() const override { return item_count_; }
120 base::string16 GetItemAt(int index) override {
121 if (IsItemSeparatorAt(index)) {
122 NOTREACHED();
123 return ASCIIToUTF16("SEPARATOR");
125 return ASCIIToUTF16(index % 2 == 0 ? "PEANUT BUTTER" : "JELLY");
127 bool IsItemSeparatorAt(int index) override {
128 return separators_.find(index) != separators_.end();
131 int GetDefaultIndex() const override {
132 // Return the first index that is not a separator.
133 for (int index = 0; index < kItemCount; ++index) {
134 if (separators_.find(index) == separators_.end())
135 return index;
137 NOTREACHED();
138 return 0;
141 void SetSeparators(const std::set<int>& separators) {
142 separators_ = separators;
145 void set_item_count(int item_count) { item_count_ = item_count; }
147 private:
148 std::set<int> separators_;
149 int item_count_ = kItemCount;
151 DISALLOW_COPY_AND_ASSIGN(TestComboboxModel);
154 // A combobox model which refers to a vector.
155 class VectorComboboxModel : public ui::ComboboxModel {
156 public:
157 explicit VectorComboboxModel(std::vector<std::string>* values)
158 : values_(values) {}
159 ~VectorComboboxModel() override {}
161 void set_default_index(int default_index) { default_index_ = default_index; }
163 // ui::ComboboxModel:
164 int GetItemCount() const override { return (int)values_->size(); }
165 base::string16 GetItemAt(int index) override {
166 return ASCIIToUTF16(values_->at(index));
168 bool IsItemSeparatorAt(int index) override { return false; }
169 int GetDefaultIndex() const override { return default_index_; }
171 private:
172 std::vector<std::string>* values_;
173 int default_index_ = 0;
175 DISALLOW_COPY_AND_ASSIGN(VectorComboboxModel);
178 class EvilListener : public ComboboxListener {
179 public:
180 EvilListener() : deleted_(false) {}
181 ~EvilListener() override{};
183 // ComboboxListener:
184 void OnPerformAction(Combobox* combobox) override {
185 delete combobox;
186 deleted_ = true;
189 bool deleted() const { return deleted_; }
191 private:
192 bool deleted_;
194 DISALLOW_COPY_AND_ASSIGN(EvilListener);
197 class TestComboboxListener : public views::ComboboxListener {
198 public:
199 TestComboboxListener() : perform_action_index_(-1), actions_performed_(0) {}
200 ~TestComboboxListener() override {}
202 void OnPerformAction(views::Combobox* combobox) override {
203 perform_action_index_ = combobox->selected_index();
204 actions_performed_++;
207 int perform_action_index() const {
208 return perform_action_index_;
211 bool on_perform_action_called() const {
212 return actions_performed_ > 0;
215 int actions_performed() const {
216 return actions_performed_;
219 private:
220 int perform_action_index_;
221 int actions_performed_;
223 private:
224 DISALLOW_COPY_AND_ASSIGN(TestComboboxListener);
227 } // namespace
229 void ComboboxTestApi::InstallTestMenuRunner(int* menu_show_count) {
230 combobox_->menu_runner_.reset(
231 new MenuRunner(menu_model(), MenuRunner::COMBOBOX));
232 test::MenuRunnerTestAPI test_api(combobox_->menu_runner_.get());
233 test_api.SetMenuRunnerHandler(
234 make_scoped_ptr(new TestMenuRunnerHandler(menu_show_count)));
237 class ComboboxTest : public ViewsTestBase {
238 public:
239 ComboboxTest() : widget_(NULL), combobox_(NULL) {}
241 void TearDown() override {
242 if (widget_)
243 widget_->Close();
244 ViewsTestBase::TearDown();
247 void InitCombobox(const std::set<int>* separators) {
248 model_.reset(new TestComboboxModel());
250 if (separators)
251 model_->SetSeparators(*separators);
253 ASSERT_FALSE(combobox_);
254 combobox_ = new TestCombobox(model_.get());
255 test_api_.reset(new ComboboxTestApi(combobox_));
256 combobox_->set_id(1);
258 widget_ = new Widget;
259 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
260 params.bounds = gfx::Rect(200, 200, 200, 200);
261 widget_->Init(params);
262 View* container = new View();
263 widget_->SetContentsView(container);
264 container->AddChildView(combobox_);
266 combobox_->RequestFocus();
267 combobox_->SizeToPreferredSize();
270 protected:
271 void SendKeyEvent(ui::KeyboardCode key_code) {
272 SendKeyEventWithType(key_code, ui::ET_KEY_PRESSED);
275 void SendKeyEventWithType(ui::KeyboardCode key_code, ui::EventType type) {
276 ui::KeyEvent event(type, key_code, ui::EF_NONE);
277 FocusManager* focus_manager = widget_->GetFocusManager();
278 widget_->OnKeyEvent(&event);
279 if (!event.handled() && focus_manager)
280 focus_manager->OnKeyEvent(event);
283 View* GetFocusedView() {
284 return widget_->GetFocusManager()->GetFocusedView();
287 void PerformClick(const gfx::Point& point) {
288 ui::MouseEvent pressed_event = ui::MouseEvent(
289 ui::ET_MOUSE_PRESSED, point, point, ui::EventTimeForNow(),
290 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
291 widget_->OnMouseEvent(&pressed_event);
292 ui::MouseEvent released_event = ui::MouseEvent(
293 ui::ET_MOUSE_RELEASED, point, point, ui::EventTimeForNow(),
294 ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
295 widget_->OnMouseEvent(&released_event);
298 // We need widget to populate wrapper class.
299 Widget* widget_;
301 // |combobox_| will be allocated InitCombobox() and then owned by |widget_|.
302 TestCombobox* combobox_;
303 scoped_ptr<ComboboxTestApi> test_api_;
305 // Combobox does not take ownership of the model, hence it needs to be scoped.
306 scoped_ptr<TestComboboxModel> model_;
308 private:
309 DISALLOW_COPY_AND_ASSIGN(ComboboxTest);
312 TEST_F(ComboboxTest, KeyTest) {
313 InitCombobox(NULL);
314 SendKeyEvent(ui::VKEY_END);
315 EXPECT_EQ(combobox_->selected_index() + 1, model_->GetItemCount());
316 SendKeyEvent(ui::VKEY_HOME);
317 EXPECT_EQ(combobox_->selected_index(), 0);
318 SendKeyEvent(ui::VKEY_DOWN);
319 SendKeyEvent(ui::VKEY_DOWN);
320 EXPECT_EQ(combobox_->selected_index(), 2);
321 SendKeyEvent(ui::VKEY_RIGHT);
322 EXPECT_EQ(combobox_->selected_index(), 2);
323 SendKeyEvent(ui::VKEY_LEFT);
324 EXPECT_EQ(combobox_->selected_index(), 2);
325 SendKeyEvent(ui::VKEY_UP);
326 EXPECT_EQ(combobox_->selected_index(), 1);
327 SendKeyEvent(ui::VKEY_PRIOR);
328 EXPECT_EQ(combobox_->selected_index(), 0);
329 SendKeyEvent(ui::VKEY_NEXT);
330 EXPECT_EQ(combobox_->selected_index(), model_->GetItemCount() - 1);
333 // Check that if a combobox is disabled before it has a native wrapper, then the
334 // native wrapper inherits the disabled state when it gets created.
335 TEST_F(ComboboxTest, DisabilityTest) {
336 model_.reset(new TestComboboxModel());
338 ASSERT_FALSE(combobox_);
339 combobox_ = new TestCombobox(model_.get());
340 combobox_->SetEnabled(false);
342 widget_ = new Widget;
343 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
344 params.bounds = gfx::Rect(100, 100, 100, 100);
345 widget_->Init(params);
346 View* container = new View();
347 widget_->SetContentsView(container);
348 container->AddChildView(combobox_);
349 EXPECT_FALSE(combobox_->enabled());
352 // Verifies that we don't select a separator line in combobox when navigating
353 // through keyboard.
354 TEST_F(ComboboxTest, SkipSeparatorSimple) {
355 std::set<int> separators;
356 separators.insert(2);
357 InitCombobox(&separators);
358 EXPECT_EQ(0, combobox_->selected_index());
359 SendKeyEvent(ui::VKEY_DOWN);
360 EXPECT_EQ(1, combobox_->selected_index());
361 SendKeyEvent(ui::VKEY_DOWN);
362 EXPECT_EQ(3, combobox_->selected_index());
363 SendKeyEvent(ui::VKEY_UP);
364 EXPECT_EQ(1, combobox_->selected_index());
365 SendKeyEvent(ui::VKEY_HOME);
366 EXPECT_EQ(0, combobox_->selected_index());
367 SendKeyEvent(ui::VKEY_PRIOR);
368 EXPECT_EQ(0, combobox_->selected_index());
369 SendKeyEvent(ui::VKEY_END);
370 EXPECT_EQ(9, combobox_->selected_index());
373 // Verifies that we never select the separator that is in the beginning of the
374 // combobox list when navigating through keyboard.
375 TEST_F(ComboboxTest, SkipSeparatorBeginning) {
376 std::set<int> separators;
377 separators.insert(0);
378 InitCombobox(&separators);
379 EXPECT_EQ(1, combobox_->selected_index());
380 SendKeyEvent(ui::VKEY_DOWN);
381 EXPECT_EQ(2, combobox_->selected_index());
382 SendKeyEvent(ui::VKEY_DOWN);
383 EXPECT_EQ(3, combobox_->selected_index());
384 SendKeyEvent(ui::VKEY_UP);
385 EXPECT_EQ(2, combobox_->selected_index());
386 SendKeyEvent(ui::VKEY_HOME);
387 EXPECT_EQ(1, combobox_->selected_index());
388 SendKeyEvent(ui::VKEY_PRIOR);
389 EXPECT_EQ(1, combobox_->selected_index());
390 SendKeyEvent(ui::VKEY_END);
391 EXPECT_EQ(9, combobox_->selected_index());
394 // Verifies that we never select the separator that is in the end of the
395 // combobox list when navigating through keyboard.
396 TEST_F(ComboboxTest, SkipSeparatorEnd) {
397 std::set<int> separators;
398 separators.insert(TestComboboxModel::kItemCount - 1);
399 InitCombobox(&separators);
400 combobox_->SetSelectedIndex(8);
401 SendKeyEvent(ui::VKEY_DOWN);
402 EXPECT_EQ(8, combobox_->selected_index());
403 SendKeyEvent(ui::VKEY_UP);
404 EXPECT_EQ(7, combobox_->selected_index());
405 SendKeyEvent(ui::VKEY_END);
406 EXPECT_EQ(8, combobox_->selected_index());
409 // Verifies that we never select any of the adjacent separators (multiple
410 // consecutive) that appear in the beginning of the combobox list when
411 // navigating through keyboard.
412 TEST_F(ComboboxTest, SkipMultipleSeparatorsAtBeginning) {
413 std::set<int> separators;
414 separators.insert(0);
415 separators.insert(1);
416 separators.insert(2);
417 InitCombobox(&separators);
418 EXPECT_EQ(3, combobox_->selected_index());
419 SendKeyEvent(ui::VKEY_DOWN);
420 EXPECT_EQ(4, combobox_->selected_index());
421 SendKeyEvent(ui::VKEY_UP);
422 EXPECT_EQ(3, combobox_->selected_index());
423 SendKeyEvent(ui::VKEY_NEXT);
424 EXPECT_EQ(9, combobox_->selected_index());
425 SendKeyEvent(ui::VKEY_HOME);
426 EXPECT_EQ(3, combobox_->selected_index());
427 SendKeyEvent(ui::VKEY_END);
428 EXPECT_EQ(9, combobox_->selected_index());
429 SendKeyEvent(ui::VKEY_PRIOR);
430 EXPECT_EQ(3, combobox_->selected_index());
433 // Verifies that we never select any of the adjacent separators (multiple
434 // consecutive) that appear in the middle of the combobox list when navigating
435 // through keyboard.
436 TEST_F(ComboboxTest, SkipMultipleAdjacentSeparatorsAtMiddle) {
437 std::set<int> separators;
438 separators.insert(4);
439 separators.insert(5);
440 separators.insert(6);
441 InitCombobox(&separators);
442 combobox_->SetSelectedIndex(3);
443 SendKeyEvent(ui::VKEY_DOWN);
444 EXPECT_EQ(7, combobox_->selected_index());
445 SendKeyEvent(ui::VKEY_UP);
446 EXPECT_EQ(3, combobox_->selected_index());
449 // Verifies that we never select any of the adjacent separators (multiple
450 // consecutive) that appear in the end of the combobox list when navigating
451 // through keyboard.
452 TEST_F(ComboboxTest, SkipMultipleSeparatorsAtEnd) {
453 std::set<int> separators;
454 separators.insert(7);
455 separators.insert(8);
456 separators.insert(9);
457 InitCombobox(&separators);
458 combobox_->SetSelectedIndex(6);
459 SendKeyEvent(ui::VKEY_DOWN);
460 EXPECT_EQ(6, combobox_->selected_index());
461 SendKeyEvent(ui::VKEY_UP);
462 EXPECT_EQ(5, combobox_->selected_index());
463 SendKeyEvent(ui::VKEY_HOME);
464 EXPECT_EQ(0, combobox_->selected_index());
465 SendKeyEvent(ui::VKEY_NEXT);
466 EXPECT_EQ(6, combobox_->selected_index());
467 SendKeyEvent(ui::VKEY_PRIOR);
468 EXPECT_EQ(0, combobox_->selected_index());
469 SendKeyEvent(ui::VKEY_END);
470 EXPECT_EQ(6, combobox_->selected_index());
473 TEST_F(ComboboxTest, GetTextForRowTest) {
474 std::set<int> separators;
475 separators.insert(0);
476 separators.insert(1);
477 separators.insert(9);
478 InitCombobox(&separators);
479 for (int i = 0; i < combobox_->GetRowCount(); ++i) {
480 if (separators.count(i) != 0) {
481 EXPECT_TRUE(combobox_->GetTextForRow(i).empty()) << i;
482 } else {
483 EXPECT_EQ(ASCIIToUTF16(i % 2 == 0 ? "PEANUT BUTTER" : "JELLY"),
484 combobox_->GetTextForRow(i)) << i;
489 // Verifies selecting the first matching value (and returning whether found).
490 TEST_F(ComboboxTest, SelectValue) {
491 InitCombobox(NULL);
492 ASSERT_EQ(model_->GetDefaultIndex(), combobox_->selected_index());
493 EXPECT_TRUE(combobox_->SelectValue(ASCIIToUTF16("PEANUT BUTTER")));
494 EXPECT_EQ(0, combobox_->selected_index());
495 EXPECT_TRUE(combobox_->SelectValue(ASCIIToUTF16("JELLY")));
496 EXPECT_EQ(1, combobox_->selected_index());
497 EXPECT_FALSE(combobox_->SelectValue(ASCIIToUTF16("BANANAS")));
498 EXPECT_EQ(1, combobox_->selected_index());
500 // With the action style, the selected index is always 0.
501 combobox_->SetStyle(Combobox::STYLE_ACTION);
502 EXPECT_FALSE(combobox_->SelectValue(ASCIIToUTF16("PEANUT BUTTER")));
503 EXPECT_EQ(0, combobox_->selected_index());
504 EXPECT_FALSE(combobox_->SelectValue(ASCIIToUTF16("JELLY")));
505 EXPECT_EQ(0, combobox_->selected_index());
506 EXPECT_FALSE(combobox_->SelectValue(ASCIIToUTF16("BANANAS")));
507 EXPECT_EQ(0, combobox_->selected_index());
510 TEST_F(ComboboxTest, SelectIndexActionStyle) {
511 InitCombobox(NULL);
513 // With the action style, the selected index is always 0.
514 combobox_->SetStyle(Combobox::STYLE_ACTION);
515 combobox_->SetSelectedIndex(1);
516 EXPECT_EQ(0, combobox_->selected_index());
517 combobox_->SetSelectedIndex(2);
518 EXPECT_EQ(0, combobox_->selected_index());
519 combobox_->SetSelectedIndex(3);
520 EXPECT_EQ(0, combobox_->selected_index());
523 TEST_F(ComboboxTest, ListenerHandlesDelete) {
524 TestComboboxModel model;
526 // |combobox| will be deleted on change.
527 TestCombobox* combobox = new TestCombobox(&model);
528 scoped_ptr<EvilListener> evil_listener(new EvilListener());
529 combobox->set_listener(evil_listener.get());
530 ASSERT_NO_FATAL_FAILURE(ComboboxTestApi(combobox).PerformActionAt(2));
531 EXPECT_TRUE(evil_listener->deleted());
533 // With STYLE_ACTION
534 // |combobox| will be deleted on change.
535 combobox = new TestCombobox(&model);
536 evil_listener.reset(new EvilListener());
537 combobox->set_listener(evil_listener.get());
538 combobox->SetStyle(Combobox::STYLE_ACTION);
539 ASSERT_NO_FATAL_FAILURE(ComboboxTestApi(combobox).PerformActionAt(2));
540 EXPECT_TRUE(evil_listener->deleted());
543 TEST_F(ComboboxTest, Click) {
544 InitCombobox(NULL);
546 TestComboboxListener listener;
547 combobox_->set_listener(&listener);
549 combobox_->Layout();
550 int menu_show_count = 0;
551 test_api_->InstallTestMenuRunner(&menu_show_count);
553 // Click the left side. The menu is shown.
554 EXPECT_EQ(0, menu_show_count);
555 PerformClick(gfx::Point(combobox_->x() + 1,
556 combobox_->y() + combobox_->height() / 2));
557 EXPECT_FALSE(listener.on_perform_action_called());
558 EXPECT_EQ(1, menu_show_count);
561 TEST_F(ComboboxTest, ClickButDisabled) {
562 InitCombobox(NULL);
564 TestComboboxListener listener;
565 combobox_->set_listener(&listener);
567 combobox_->Layout();
568 combobox_->SetEnabled(false);
570 // Click the left side, but nothing happens since the combobox is disabled.
571 int menu_show_count = 0;
572 test_api_->InstallTestMenuRunner(&menu_show_count);
573 PerformClick(gfx::Point(combobox_->x() + 1,
574 combobox_->y() + combobox_->height() / 2));
575 EXPECT_FALSE(listener.on_perform_action_called());
576 EXPECT_EQ(0, menu_show_count);
579 TEST_F(ComboboxTest, NotifyOnClickWithReturnKey) {
580 InitCombobox(NULL);
582 TestComboboxListener listener;
583 combobox_->set_listener(&listener);
585 // With STYLE_NORMAL, the click event is ignored.
586 SendKeyEvent(ui::VKEY_RETURN);
587 EXPECT_FALSE(listener.on_perform_action_called());
589 // With STYLE_ACTION, the click event is notified.
590 combobox_->SetStyle(Combobox::STYLE_ACTION);
591 SendKeyEvent(ui::VKEY_RETURN);
592 EXPECT_TRUE(listener.on_perform_action_called());
593 EXPECT_EQ(0, listener.perform_action_index());
596 TEST_F(ComboboxTest, NotifyOnClickWithSpaceKey) {
597 InitCombobox(NULL);
599 TestComboboxListener listener;
600 combobox_->set_listener(&listener);
602 // With STYLE_NORMAL, the click event is ignored.
603 SendKeyEvent(ui::VKEY_SPACE);
604 EXPECT_FALSE(listener.on_perform_action_called());
605 SendKeyEventWithType(ui::VKEY_SPACE, ui::ET_KEY_RELEASED);
606 EXPECT_FALSE(listener.on_perform_action_called());
608 // With STYLE_ACTION, the click event is notified after releasing.
609 combobox_->SetStyle(Combobox::STYLE_ACTION);
610 SendKeyEvent(ui::VKEY_SPACE);
611 EXPECT_FALSE(listener.on_perform_action_called());
612 SendKeyEventWithType(ui::VKEY_SPACE, ui::ET_KEY_RELEASED);
613 EXPECT_TRUE(listener.on_perform_action_called());
614 EXPECT_EQ(0, listener.perform_action_index());
617 TEST_F(ComboboxTest, NotifyOnClickWithMouse) {
618 InitCombobox(NULL);
620 TestComboboxListener listener;
621 combobox_->set_listener(&listener);
623 combobox_->SetStyle(Combobox::STYLE_ACTION);
624 combobox_->Layout();
626 // Click the right side (arrow button). The menu is shown.
627 int menu_show_count = 0;
628 test_api_->InstallTestMenuRunner(&menu_show_count);
629 EXPECT_EQ(0, menu_show_count);
630 PerformClick(gfx::Point(combobox_->x() + combobox_->width() - 1,
631 combobox_->y() + combobox_->height() / 2));
632 EXPECT_FALSE(listener.on_perform_action_called());
633 EXPECT_EQ(1, menu_show_count);
635 // Click the left side (text button). The click event is notified.
636 test_api_->InstallTestMenuRunner(&menu_show_count);
637 PerformClick(gfx::Point(combobox_->x() + 1,
638 combobox_->y() + combobox_->height() / 2));
639 EXPECT_TRUE(listener.on_perform_action_called());
640 EXPECT_EQ(1, menu_show_count); // Unchanged.
641 EXPECT_EQ(0, listener.perform_action_index());
644 TEST_F(ComboboxTest, ConsumingPressKeyEvents) {
645 InitCombobox(NULL);
647 EXPECT_FALSE(combobox_->OnKeyPressed(
648 ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_RETURN, ui::EF_NONE)));
649 EXPECT_FALSE(combobox_->OnKeyPressed(
650 ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_SPACE, ui::EF_NONE)));
652 // When the combobox's style is STYLE_ACTION, pressing events of a space key
653 // or an enter key will be consumed.
654 combobox_->SetStyle(Combobox::STYLE_ACTION);
655 EXPECT_TRUE(combobox_->OnKeyPressed(
656 ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_RETURN, ui::EF_NONE)));
657 EXPECT_TRUE(combobox_->OnKeyPressed(
658 ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_SPACE, ui::EF_NONE)));
661 TEST_F(ComboboxTest, ContentWidth) {
662 std::vector<std::string> values;
663 VectorComboboxModel model(&values);
664 TestCombobox combobox(&model);
665 ComboboxTestApi test_api(&combobox);
667 std::string long_item = "this is the long item";
668 std::string short_item = "s";
670 values.resize(1);
671 values[0] = long_item;
672 combobox.ModelChanged();
674 const int long_item_width = test_api.content_size().width();
676 values[0] = short_item;
677 combobox.ModelChanged();
679 const int short_item_width = test_api.content_size().width();
681 values.resize(2);
682 values[0] = short_item;
683 values[1] = long_item;
684 combobox.ModelChanged();
686 // When the style is STYLE_NORMAL, the width will fit with the longest item.
687 combobox.SetStyle(Combobox::STYLE_NORMAL);
688 EXPECT_EQ(long_item_width, test_api.content_size().width());
690 // When the style is STYLE_ACTION, the width will fit with the selected item's
691 // width.
692 combobox.SetStyle(Combobox::STYLE_ACTION);
693 EXPECT_EQ(short_item_width, test_api.content_size().width());
696 // Test that model updates preserve the selected index, so long as it is in
697 // range.
698 TEST_F(ComboboxTest, ModelChanged) {
699 InitCombobox(nullptr);
701 EXPECT_EQ(0, combobox_->GetSelectedRow());
702 EXPECT_EQ(10, combobox_->GetRowCount());
704 combobox_->SetSelectedIndex(4);
705 EXPECT_EQ(4, combobox_->GetSelectedRow());
707 model_->set_item_count(5);
708 combobox_->ModelChanged();
709 EXPECT_EQ(5, combobox_->GetRowCount());
710 EXPECT_EQ(4, combobox_->GetSelectedRow()); // Unchanged.
712 model_->set_item_count(4);
713 combobox_->ModelChanged();
714 EXPECT_EQ(4, combobox_->GetRowCount());
715 EXPECT_EQ(0, combobox_->GetSelectedRow()); // Resets.
717 // Restore a non-zero selection.
718 combobox_->SetSelectedIndex(2);
719 EXPECT_EQ(2, combobox_->GetSelectedRow());
721 // Make the selected index a separator.
722 std::set<int> separators;
723 separators.insert(2);
724 model_->SetSeparators(separators);
725 combobox_->ModelChanged();
726 EXPECT_EQ(4, combobox_->GetRowCount());
727 EXPECT_EQ(0, combobox_->GetSelectedRow()); // Resets.
729 // Restore a non-zero selection.
730 combobox_->SetSelectedIndex(1);
731 EXPECT_EQ(1, combobox_->GetSelectedRow());
733 // Test an empty model.
734 model_->set_item_count(0);
735 combobox_->ModelChanged();
736 EXPECT_EQ(0, combobox_->GetRowCount());
737 EXPECT_EQ(0, combobox_->GetSelectedRow()); // Resets.
740 TEST_F(ComboboxTest, TypingPrefixNotifiesListener) {
741 InitCombobox(NULL);
743 TestComboboxListener listener;
744 combobox_->set_listener(&listener);
745 ui::TextInputClient* input_client =
746 widget_->GetInputMethod()->GetTextInputClient();
748 // Type the first character of the second menu item ("JELLY").
749 input_client->InsertChar('J', ui::EF_NONE);
750 EXPECT_EQ(1, listener.actions_performed());
751 EXPECT_EQ(1, listener.perform_action_index());
753 // Type the second character of "JELLY", item shouldn't change and
754 // OnPerformAction() shouldn't be re-called.
755 input_client->InsertChar('E', ui::EF_NONE);
756 EXPECT_EQ(1, listener.actions_performed());
757 EXPECT_EQ(1, listener.perform_action_index());
759 // Clears the typed text.
760 combobox_->OnBlur();
761 combobox_->RequestFocus();
763 // Type the first character of "PEANUT BUTTER", which should change the
764 // selected index and perform an action.
765 input_client->InsertChar('P', ui::EF_NONE);
766 EXPECT_EQ(2, listener.actions_performed());
767 EXPECT_EQ(2, listener.perform_action_index());
770 // Test properties on the Combobox menu model.
771 TEST_F(ComboboxTest, MenuModel) {
772 const int kSeparatorIndex = 3;
773 std::set<int> separators;
774 separators.insert(kSeparatorIndex);
775 InitCombobox(&separators);
777 ui::MenuModel* menu_model = test_api_->menu_model();
779 EXPECT_EQ(TestComboboxModel::kItemCount, menu_model->GetItemCount());
780 EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR,
781 menu_model->GetTypeAt(kSeparatorIndex));
783 #if defined(OS_MACOSX)
784 // Comboboxes on Mac should have checkmarks, with the selected item checked,
785 EXPECT_EQ(ui::MenuModel::TYPE_CHECK, menu_model->GetTypeAt(0));
786 EXPECT_EQ(ui::MenuModel::TYPE_CHECK, menu_model->GetTypeAt(1));
787 EXPECT_TRUE(menu_model->IsItemCheckedAt(0));
788 EXPECT_FALSE(menu_model->IsItemCheckedAt(1));
790 combobox_->SetSelectedIndex(1);
791 EXPECT_FALSE(menu_model->IsItemCheckedAt(0));
792 EXPECT_TRUE(menu_model->IsItemCheckedAt(1));
793 #else
794 EXPECT_EQ(ui::MenuModel::TYPE_COMMAND, menu_model->GetTypeAt(0));
795 EXPECT_EQ(ui::MenuModel::TYPE_COMMAND, menu_model->GetTypeAt(1));
796 #endif
798 EXPECT_EQ(ASCIIToUTF16("PEANUT BUTTER"), menu_model->GetLabelAt(0));
799 EXPECT_EQ(ASCIIToUTF16("JELLY"), menu_model->GetLabelAt(1));
801 // Check that with STYLE_ACTION, the first item (only) is not shown.
802 EXPECT_TRUE(menu_model->IsVisibleAt(0));
803 combobox_->SetStyle(Combobox::STYLE_ACTION);
804 EXPECT_FALSE(menu_model->IsVisibleAt(0));
805 EXPECT_TRUE(menu_model->IsVisibleAt(1));
808 } // namespace views