combobox_unittest.cc revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
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.
4
5#include "ui/views/controls/combobox/combobox.h"
6
7#include <set>
8
9#include "base/basictypes.h"
10#include "base/strings/utf_string_conversions.h"
11#include "ui/base/ime/text_input_client.h"
12#include "ui/base/models/combobox_model.h"
13#include "ui/events/event.h"
14#include "ui/events/event_constants.h"
15#include "ui/events/keycodes/keyboard_codes.h"
16#include "ui/views/controls/combobox/combobox_listener.h"
17#include "ui/views/controls/menu/menu_runner.h"
18#include "ui/views/controls/menu/menu_runner_handler.h"
19#include "ui/views/ime/mock_input_method.h"
20#include "ui/views/test/menu_runner_test_api.h"
21#include "ui/views/test/views_test_base.h"
22#include "ui/views/widget/widget.h"
23
24using base::ASCIIToUTF16;
25
26namespace views {
27
28namespace {
29
30// An dummy implementation of MenuRunnerHandler to check if the dropdown menu is
31// shown or not.
32class TestMenuRunnerHandler : public MenuRunnerHandler {
33 public:
34  TestMenuRunnerHandler() : executed_(false) {}
35
36  bool executed() const { return executed_; }
37
38  virtual MenuRunner::RunResult RunMenuAt(Widget* parent,
39                                          MenuButton* button,
40                                          const gfx::Rect& bounds,
41                                          MenuAnchorPosition anchor,
42                                          ui::MenuSourceType source_type,
43                                          int32 types) OVERRIDE {
44    executed_ = true;
45    return MenuRunner::NORMAL_EXIT;
46  }
47
48 private:
49  bool executed_;
50
51  DISALLOW_COPY_AND_ASSIGN(TestMenuRunnerHandler);
52};
53
54// A wrapper of Combobox to intercept the result of OnKeyPressed() and
55// OnKeyReleased() methods.
56class TestCombobox : public Combobox {
57 public:
58  explicit TestCombobox(ui::ComboboxModel* model)
59      : Combobox(model),
60        key_handled_(false),
61        key_received_(false) {}
62
63  virtual bool OnKeyPressed(const ui::KeyEvent& e) OVERRIDE {
64    key_received_ = true;
65    key_handled_ = Combobox::OnKeyPressed(e);
66    return key_handled_;
67  }
68
69  virtual bool OnKeyReleased(const ui::KeyEvent& e) OVERRIDE {
70    key_received_ = true;
71    key_handled_ = Combobox::OnKeyReleased(e);
72    return key_handled_;
73  }
74
75  bool key_handled() const { return key_handled_; }
76  bool key_received() const { return key_received_; }
77
78  void clear() {
79    key_received_ = key_handled_ = false;
80  }
81
82 private:
83  bool key_handled_;
84  bool key_received_;
85
86  DISALLOW_COPY_AND_ASSIGN(TestCombobox);
87};
88
89// A concrete class is needed to test the combobox.
90class TestComboboxModel : public ui::ComboboxModel {
91 public:
92  TestComboboxModel() {}
93  virtual ~TestComboboxModel() {}
94
95  // ui::ComboboxModel:
96  virtual int GetItemCount() const OVERRIDE {
97    return 10;
98  }
99  virtual base::string16 GetItemAt(int index) OVERRIDE {
100    if (IsItemSeparatorAt(index)) {
101      NOTREACHED();
102      return ASCIIToUTF16("SEPARATOR");
103    }
104    return ASCIIToUTF16(index % 2 == 0 ? "PEANUT BUTTER" : "JELLY");
105  }
106  virtual bool IsItemSeparatorAt(int index) OVERRIDE {
107    return separators_.find(index) != separators_.end();
108  }
109
110  void SetSeparators(const std::set<int>& separators) {
111    separators_ = separators;
112  }
113
114 private:
115  std::set<int> separators_;
116
117  DISALLOW_COPY_AND_ASSIGN(TestComboboxModel);
118};
119
120// A combobox model which refers to a vector.
121class VectorComboboxModel : public ui::ComboboxModel {
122 public:
123  explicit VectorComboboxModel(std::vector<std::string>* values)
124      : values_(values) {}
125  virtual ~VectorComboboxModel() {}
126
127  // ui::ComboboxModel:
128  virtual int GetItemCount() const OVERRIDE {
129    return (int)values_->size();
130  }
131  virtual base::string16 GetItemAt(int index) OVERRIDE {
132    return ASCIIToUTF16(values_->at(index));
133  }
134  virtual bool IsItemSeparatorAt(int index) OVERRIDE {
135    return false;
136  }
137
138 private:
139  std::vector<std::string>* values_;
140};
141
142class EvilListener : public ComboboxListener {
143 public:
144  EvilListener() : deleted_(false) {}
145  virtual ~EvilListener() {};
146
147  // ComboboxListener:
148  virtual void OnPerformAction(Combobox* combobox) OVERRIDE {
149    delete combobox;
150    deleted_ = true;
151  }
152
153  bool deleted() const { return deleted_; }
154
155 private:
156  bool deleted_;
157
158  DISALLOW_COPY_AND_ASSIGN(EvilListener);
159};
160
161class TestComboboxListener : public views::ComboboxListener {
162 public:
163  TestComboboxListener() : perform_action_index_(-1), actions_performed_(0) {}
164  virtual ~TestComboboxListener() {}
165
166  virtual void OnPerformAction(views::Combobox* combobox) OVERRIDE {
167    perform_action_index_ = combobox->selected_index();
168    actions_performed_++;
169  }
170
171  int perform_action_index() const {
172    return perform_action_index_;
173  }
174
175  bool on_perform_action_called() const {
176    return actions_performed_ > 0;
177  }
178
179  int actions_performed() const {
180    return actions_performed_;
181  }
182
183 private:
184  int perform_action_index_;
185  int actions_performed_;
186
187 private:
188  DISALLOW_COPY_AND_ASSIGN(TestComboboxListener);
189};
190
191}  // namespace
192
193class ComboboxTest : public ViewsTestBase {
194 public:
195  ComboboxTest() : widget_(NULL), combobox_(NULL) {}
196
197  virtual void TearDown() OVERRIDE {
198    if (widget_)
199      widget_->Close();
200    ViewsTestBase::TearDown();
201  }
202
203  void InitCombobox() {
204    model_.reset(new TestComboboxModel());
205
206    ASSERT_FALSE(combobox_);
207    combobox_ = new TestCombobox(model_.get());
208    combobox_->set_id(1);
209
210    widget_ = new Widget;
211    Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
212    params.bounds = gfx::Rect(200, 200, 200, 200);
213    widget_->Init(params);
214    View* container = new View();
215    widget_->SetContentsView(container);
216    container->AddChildView(combobox_);
217
218    widget_->ReplaceInputMethod(new MockInputMethod);
219
220    // Assumes the Widget is always focused.
221    widget_->GetInputMethod()->OnFocus();
222
223    combobox_->RequestFocus();
224    combobox_->SizeToPreferredSize();
225  }
226
227 protected:
228  void SendKeyEvent(ui::KeyboardCode key_code) {
229    SendKeyEventWithType(key_code, ui::ET_KEY_PRESSED);
230  }
231
232  void SendKeyEventWithType(ui::KeyboardCode key_code, ui::EventType type) {
233    ui::KeyEvent event(type, key_code, 0, false);
234    widget_->GetInputMethod()->DispatchKeyEvent(event);
235  }
236
237  View* GetFocusedView() {
238    return widget_->GetFocusManager()->GetFocusedView();
239  }
240
241  void PerformClick(const gfx::Point& point) {
242    ui::MouseEvent pressed_event = ui::MouseEvent(ui::ET_MOUSE_PRESSED, point,
243                                                  point,
244                                                  ui::EF_LEFT_MOUSE_BUTTON,
245                                                  ui::EF_LEFT_MOUSE_BUTTON);
246    widget_->OnMouseEvent(&pressed_event);
247    ui::MouseEvent released_event = ui::MouseEvent(ui::ET_MOUSE_RELEASED, point,
248                                                   point,
249                                                   ui::EF_LEFT_MOUSE_BUTTON,
250                                                   ui::EF_LEFT_MOUSE_BUTTON);
251    widget_->OnMouseEvent(&released_event);
252  }
253
254  // We need widget to populate wrapper class.
255  Widget* widget_;
256
257  // |combobox_| will be allocated InitCombobox() and then owned by |widget_|.
258  TestCombobox* combobox_;
259
260  // Combobox does not take ownership of the model, hence it needs to be scoped.
261  scoped_ptr<TestComboboxModel> model_;
262};
263
264TEST_F(ComboboxTest, KeyTest) {
265  InitCombobox();
266  SendKeyEvent(ui::VKEY_END);
267  EXPECT_EQ(combobox_->selected_index() + 1, model_->GetItemCount());
268  SendKeyEvent(ui::VKEY_HOME);
269  EXPECT_EQ(combobox_->selected_index(), 0);
270  SendKeyEvent(ui::VKEY_DOWN);
271  SendKeyEvent(ui::VKEY_DOWN);
272  EXPECT_EQ(combobox_->selected_index(), 2);
273  SendKeyEvent(ui::VKEY_RIGHT);
274  EXPECT_EQ(combobox_->selected_index(), 2);
275  SendKeyEvent(ui::VKEY_LEFT);
276  EXPECT_EQ(combobox_->selected_index(), 2);
277  SendKeyEvent(ui::VKEY_UP);
278  EXPECT_EQ(combobox_->selected_index(), 1);
279  SendKeyEvent(ui::VKEY_PRIOR);
280  EXPECT_EQ(combobox_->selected_index(), 0);
281  SendKeyEvent(ui::VKEY_NEXT);
282  EXPECT_EQ(combobox_->selected_index(), model_->GetItemCount() - 1);
283}
284
285// Check that if a combobox is disabled before it has a native wrapper, then the
286// native wrapper inherits the disabled state when it gets created.
287TEST_F(ComboboxTest, DisabilityTest) {
288  model_.reset(new TestComboboxModel());
289
290  ASSERT_FALSE(combobox_);
291  combobox_ = new TestCombobox(model_.get());
292  combobox_->SetEnabled(false);
293
294  widget_ = new Widget;
295  Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
296  params.bounds = gfx::Rect(100, 100, 100, 100);
297  widget_->Init(params);
298  View* container = new View();
299  widget_->SetContentsView(container);
300  container->AddChildView(combobox_);
301  EXPECT_FALSE(combobox_->enabled());
302}
303
304// Verifies that we don't select a separator line in combobox when navigating
305// through keyboard.
306TEST_F(ComboboxTest, SkipSeparatorSimple) {
307  InitCombobox();
308  std::set<int> separators;
309  separators.insert(2);
310  model_->SetSeparators(separators);
311  EXPECT_EQ(0, combobox_->selected_index());
312  SendKeyEvent(ui::VKEY_DOWN);
313  EXPECT_EQ(1, combobox_->selected_index());
314  SendKeyEvent(ui::VKEY_DOWN);
315  EXPECT_EQ(3, combobox_->selected_index());
316  SendKeyEvent(ui::VKEY_UP);
317  EXPECT_EQ(1, combobox_->selected_index());
318  SendKeyEvent(ui::VKEY_HOME);
319  EXPECT_EQ(0, combobox_->selected_index());
320  SendKeyEvent(ui::VKEY_PRIOR);
321  EXPECT_EQ(0, combobox_->selected_index());
322  SendKeyEvent(ui::VKEY_END);
323  EXPECT_EQ(9, combobox_->selected_index());
324}
325
326// Verifies that we never select the separator that is in the beginning of the
327// combobox list when navigating through keyboard.
328TEST_F(ComboboxTest, SkipSeparatorBeginning) {
329  InitCombobox();
330  std::set<int> separators;
331  separators.insert(0);
332  model_->SetSeparators(separators);
333  EXPECT_EQ(0, combobox_->selected_index());
334  SendKeyEvent(ui::VKEY_DOWN);
335  EXPECT_EQ(1, combobox_->selected_index());
336  SendKeyEvent(ui::VKEY_DOWN);
337  EXPECT_EQ(2, combobox_->selected_index());
338  SendKeyEvent(ui::VKEY_UP);
339  EXPECT_EQ(1, combobox_->selected_index());
340  SendKeyEvent(ui::VKEY_HOME);
341  EXPECT_EQ(1, combobox_->selected_index());
342  SendKeyEvent(ui::VKEY_PRIOR);
343  EXPECT_EQ(1, combobox_->selected_index());
344  SendKeyEvent(ui::VKEY_END);
345  EXPECT_EQ(9, combobox_->selected_index());
346}
347
348// Verifies that we never select the separator that is in the end of the
349// combobox list when navigating through keyboard.
350TEST_F(ComboboxTest, SkipSeparatorEnd) {
351  InitCombobox();
352  std::set<int> separators;
353  separators.insert(model_->GetItemCount() - 1);
354  model_->SetSeparators(separators);
355  combobox_->SetSelectedIndex(8);
356  SendKeyEvent(ui::VKEY_DOWN);
357  EXPECT_EQ(8, combobox_->selected_index());
358  SendKeyEvent(ui::VKEY_UP);
359  EXPECT_EQ(7, combobox_->selected_index());
360  SendKeyEvent(ui::VKEY_END);
361  EXPECT_EQ(8, combobox_->selected_index());
362}
363
364// Verifies that we never select any of the adjacent separators (multiple
365// consecutive) that appear in the beginning of the combobox list when
366// navigating through keyboard.
367TEST_F(ComboboxTest, SkipMultipleSeparatorsAtBeginning) {
368  InitCombobox();
369  std::set<int> separators;
370  separators.insert(0);
371  separators.insert(1);
372  separators.insert(2);
373  model_->SetSeparators(separators);
374  EXPECT_EQ(0, combobox_->selected_index());
375  SendKeyEvent(ui::VKEY_DOWN);
376  EXPECT_EQ(3, combobox_->selected_index());
377  SendKeyEvent(ui::VKEY_UP);
378  EXPECT_EQ(3, combobox_->selected_index());
379  SendKeyEvent(ui::VKEY_NEXT);
380  EXPECT_EQ(9, combobox_->selected_index());
381  SendKeyEvent(ui::VKEY_HOME);
382  EXPECT_EQ(3, combobox_->selected_index());
383  SendKeyEvent(ui::VKEY_END);
384  EXPECT_EQ(9, combobox_->selected_index());
385  SendKeyEvent(ui::VKEY_PRIOR);
386  EXPECT_EQ(3, combobox_->selected_index());
387}
388
389// Verifies that we never select any of the adjacent separators (multiple
390// consecutive) that appear in the middle of the combobox list when navigating
391// through keyboard.
392TEST_F(ComboboxTest, SkipMultipleAdjacentSeparatorsAtMiddle) {
393  InitCombobox();
394  std::set<int> separators;
395  separators.insert(4);
396  separators.insert(5);
397  separators.insert(6);
398  model_->SetSeparators(separators);
399  combobox_->SetSelectedIndex(3);
400  SendKeyEvent(ui::VKEY_DOWN);
401  EXPECT_EQ(7, combobox_->selected_index());
402  SendKeyEvent(ui::VKEY_UP);
403  EXPECT_EQ(3, combobox_->selected_index());
404}
405
406// Verifies that we never select any of the adjacent separators (multiple
407// consecutive) that appear in the end of the combobox list when navigating
408// through keyboard.
409TEST_F(ComboboxTest, SkipMultipleSeparatorsAtEnd) {
410  InitCombobox();
411  std::set<int> separators;
412  separators.insert(7);
413  separators.insert(8);
414  separators.insert(9);
415  model_->SetSeparators(separators);
416  combobox_->SetSelectedIndex(6);
417  SendKeyEvent(ui::VKEY_DOWN);
418  EXPECT_EQ(6, combobox_->selected_index());
419  SendKeyEvent(ui::VKEY_UP);
420  EXPECT_EQ(5, combobox_->selected_index());
421  SendKeyEvent(ui::VKEY_HOME);
422  EXPECT_EQ(0, combobox_->selected_index());
423  SendKeyEvent(ui::VKEY_NEXT);
424  EXPECT_EQ(6, combobox_->selected_index());
425  SendKeyEvent(ui::VKEY_PRIOR);
426  EXPECT_EQ(0, combobox_->selected_index());
427  SendKeyEvent(ui::VKEY_END);
428  EXPECT_EQ(6, combobox_->selected_index());
429}
430
431TEST_F(ComboboxTest, GetTextForRowTest) {
432  InitCombobox();
433  std::set<int> separators;
434  separators.insert(0);
435  separators.insert(1);
436  separators.insert(9);
437  model_->SetSeparators(separators);
438  for (int i = 0; i < combobox_->GetRowCount(); ++i) {
439    if (separators.count(i) != 0) {
440      EXPECT_TRUE(combobox_->GetTextForRow(i).empty()) << i;
441    } else {
442      EXPECT_EQ(ASCIIToUTF16(i % 2 == 0 ? "PEANUT BUTTER" : "JELLY"),
443                combobox_->GetTextForRow(i)) << i;
444    }
445  }
446}
447
448// Verifies selecting the first matching value (and returning whether found).
449TEST_F(ComboboxTest, SelectValue) {
450  InitCombobox();
451  ASSERT_EQ(model_->GetDefaultIndex(), combobox_->selected_index());
452  EXPECT_TRUE(combobox_->SelectValue(ASCIIToUTF16("PEANUT BUTTER")));
453  EXPECT_EQ(0, combobox_->selected_index());
454  EXPECT_TRUE(combobox_->SelectValue(ASCIIToUTF16("JELLY")));
455  EXPECT_EQ(1, combobox_->selected_index());
456  EXPECT_FALSE(combobox_->SelectValue(ASCIIToUTF16("BANANAS")));
457  EXPECT_EQ(1, combobox_->selected_index());
458
459  // With the action style, the selected index is always 0.
460  combobox_->SetStyle(Combobox::STYLE_ACTION);
461  EXPECT_FALSE(combobox_->SelectValue(ASCIIToUTF16("PEANUT BUTTER")));
462  EXPECT_EQ(0, combobox_->selected_index());
463  EXPECT_FALSE(combobox_->SelectValue(ASCIIToUTF16("JELLY")));
464  EXPECT_EQ(0, combobox_->selected_index());
465  EXPECT_FALSE(combobox_->SelectValue(ASCIIToUTF16("BANANAS")));
466  EXPECT_EQ(0, combobox_->selected_index());
467}
468
469TEST_F(ComboboxTest, SelectIndexActionStyle) {
470  InitCombobox();
471
472  // With the action style, the selected index is always 0.
473  combobox_->SetStyle(Combobox::STYLE_ACTION);
474  combobox_->SetSelectedIndex(1);
475  EXPECT_EQ(0, combobox_->selected_index());
476  combobox_->SetSelectedIndex(2);
477  EXPECT_EQ(0, combobox_->selected_index());
478  combobox_->SetSelectedIndex(3);
479  EXPECT_EQ(0, combobox_->selected_index());
480}
481
482TEST_F(ComboboxTest, ListenerHandlesDelete) {
483  TestComboboxModel model;
484
485  // |combobox| will be deleted on change.
486  TestCombobox* combobox = new TestCombobox(&model);
487  scoped_ptr<EvilListener> evil_listener(new EvilListener());
488  combobox->set_listener(evil_listener.get());
489  ASSERT_NO_FATAL_FAILURE(combobox->ExecuteCommand(2));
490  EXPECT_TRUE(evil_listener->deleted());
491
492  // With STYLE_ACTION
493  // |combobox| will be deleted on change.
494  combobox = new TestCombobox(&model);
495  evil_listener.reset(new EvilListener());
496  combobox->set_listener(evil_listener.get());
497  combobox->SetStyle(Combobox::STYLE_ACTION);
498  ASSERT_NO_FATAL_FAILURE(combobox->ExecuteCommand(2));
499  EXPECT_TRUE(evil_listener->deleted());
500}
501
502TEST_F(ComboboxTest, Click) {
503  InitCombobox();
504
505  TestComboboxListener listener;
506  combobox_->set_listener(&listener);
507
508  combobox_->Layout();
509
510  // Click the left side. The menu is shown.
511  TestMenuRunnerHandler* test_menu_runner_handler = new TestMenuRunnerHandler();
512  scoped_ptr<MenuRunnerHandler> menu_runner_handler(test_menu_runner_handler);
513  test::MenuRunnerTestAPI test_api(
514      combobox_->dropdown_list_menu_runner_.get());
515  test_api.SetMenuRunnerHandler(menu_runner_handler.Pass());
516  PerformClick(gfx::Point(combobox_->x() + 1,
517                          combobox_->y() + combobox_->height() / 2));
518  EXPECT_FALSE(listener.on_perform_action_called());
519  EXPECT_TRUE(test_menu_runner_handler->executed());
520}
521
522TEST_F(ComboboxTest, ClickButDisabled) {
523  InitCombobox();
524
525  TestComboboxListener listener;
526  combobox_->set_listener(&listener);
527
528  combobox_->Layout();
529  combobox_->SetEnabled(false);
530
531  // Click the left side, but nothing happens since the combobox is disabled.
532  TestMenuRunnerHandler* test_menu_runner_handler = new TestMenuRunnerHandler();
533  scoped_ptr<MenuRunnerHandler> menu_runner_handler(test_menu_runner_handler);
534  test::MenuRunnerTestAPI test_api(
535      combobox_->dropdown_list_menu_runner_.get());
536  test_api.SetMenuRunnerHandler(menu_runner_handler.Pass());
537  PerformClick(gfx::Point(combobox_->x() + 1,
538                          combobox_->y() + combobox_->height() / 2));
539  EXPECT_FALSE(listener.on_perform_action_called());
540  EXPECT_FALSE(test_menu_runner_handler->executed());
541}
542
543TEST_F(ComboboxTest, NotifyOnClickWithReturnKey) {
544  InitCombobox();
545
546  TestComboboxListener listener;
547  combobox_->set_listener(&listener);
548
549  // With STYLE_NORMAL, the click event is ignored.
550  SendKeyEvent(ui::VKEY_RETURN);
551  EXPECT_FALSE(listener.on_perform_action_called());
552
553  // With STYLE_ACTION, the click event is notified.
554  combobox_->SetStyle(Combobox::STYLE_ACTION);
555  SendKeyEvent(ui::VKEY_RETURN);
556  EXPECT_TRUE(listener.on_perform_action_called());
557  EXPECT_EQ(0, listener.perform_action_index());
558}
559
560TEST_F(ComboboxTest, NotifyOnClickWithSpaceKey) {
561  InitCombobox();
562
563  TestComboboxListener listener;
564  combobox_->set_listener(&listener);
565
566  // With STYLE_NORMAL, the click event is ignored.
567  SendKeyEvent(ui::VKEY_SPACE);
568  EXPECT_FALSE(listener.on_perform_action_called());
569  SendKeyEventWithType(ui::VKEY_SPACE, ui::ET_KEY_RELEASED);
570  EXPECT_FALSE(listener.on_perform_action_called());
571
572  // With STYLE_ACTION, the click event is notified after releasing.
573  combobox_->SetStyle(Combobox::STYLE_ACTION);
574  SendKeyEvent(ui::VKEY_SPACE);
575  EXPECT_FALSE(listener.on_perform_action_called());
576  SendKeyEventWithType(ui::VKEY_SPACE, ui::ET_KEY_RELEASED);
577  EXPECT_TRUE(listener.on_perform_action_called());
578  EXPECT_EQ(0, listener.perform_action_index());
579}
580
581TEST_F(ComboboxTest, NotifyOnClickWithMouse) {
582  InitCombobox();
583
584  TestComboboxListener listener;
585  combobox_->set_listener(&listener);
586
587  combobox_->SetStyle(Combobox::STYLE_ACTION);
588  combobox_->Layout();
589
590  // Click the right side (arrow button). The menu is shown.
591  TestMenuRunnerHandler* test_menu_runner_handler = new TestMenuRunnerHandler();
592  scoped_ptr<MenuRunnerHandler> menu_runner_handler(test_menu_runner_handler);
593  scoped_ptr<test::MenuRunnerTestAPI> test_api(
594      new test::MenuRunnerTestAPI(combobox_->dropdown_list_menu_runner_.get()));
595  test_api->SetMenuRunnerHandler(menu_runner_handler.Pass());
596
597  PerformClick(gfx::Point(combobox_->x() + combobox_->width() - 1,
598                          combobox_->y() + combobox_->height() / 2));
599  EXPECT_FALSE(listener.on_perform_action_called());
600  EXPECT_TRUE(test_menu_runner_handler->executed());
601
602  // Click the left side (text button). The click event is notified.
603  test_menu_runner_handler = new TestMenuRunnerHandler();
604  menu_runner_handler.reset(test_menu_runner_handler);
605  test_api.reset(
606      new test::MenuRunnerTestAPI(combobox_->dropdown_list_menu_runner_.get()));
607  test_api->SetMenuRunnerHandler(menu_runner_handler.Pass());
608  PerformClick(gfx::Point(combobox_->x() + 1,
609                          combobox_->y() + combobox_->height() / 2));
610  EXPECT_TRUE(listener.on_perform_action_called());
611  EXPECT_FALSE(test_menu_runner_handler->executed());
612  EXPECT_EQ(0, listener.perform_action_index());
613}
614
615TEST_F(ComboboxTest, ConsumingPressKeyEvents) {
616  InitCombobox();
617
618  EXPECT_FALSE(combobox_->OnKeyPressed(
619      ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_RETURN, 0, false)));
620  EXPECT_FALSE(combobox_->OnKeyPressed(
621      ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_SPACE, 0, false)));
622
623  // When the combobox's style is STYLE_ACTION, pressing events of a space key
624  // or an enter key will be consumed.
625  combobox_->SetStyle(Combobox::STYLE_ACTION);
626  EXPECT_TRUE(combobox_->OnKeyPressed(
627      ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_RETURN, 0, false)));
628  EXPECT_TRUE(combobox_->OnKeyPressed(
629      ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_SPACE, 0, false)));
630}
631
632TEST_F(ComboboxTest, ContentWidth) {
633  std::vector<std::string> values;
634  VectorComboboxModel model(&values);
635  TestCombobox combobox(&model);
636
637  std::string long_item = "this is the long item";
638  std::string short_item = "s";
639
640  values.resize(1);
641  values[0] = long_item;
642  combobox.ModelChanged();
643
644  const int long_item_width = combobox.content_size_.width();
645
646  values[0] = short_item;
647  combobox.ModelChanged();
648
649  const int short_item_width = combobox.content_size_.width();
650
651  values.resize(2);
652  values[0] = short_item;
653  values[1] = long_item;
654  combobox.ModelChanged();
655
656  // When the style is STYLE_NORMAL, the width will fit with the longest item.
657  combobox.SetStyle(Combobox::STYLE_NORMAL);
658  EXPECT_EQ(long_item_width, combobox.content_size_.width());
659
660  // When the style is STYLE_ACTION, the width will fit with the first items'
661  // width.
662  combobox.SetStyle(Combobox::STYLE_ACTION);
663  EXPECT_EQ(short_item_width, combobox.content_size_.width());
664}
665
666TEST_F(ComboboxTest, TypingPrefixNotifiesListener) {
667  InitCombobox();
668
669  TestComboboxListener listener;
670  combobox_->set_listener(&listener);
671
672  // Type the first character of the second menu item ("JELLY").
673  combobox_->GetTextInputClient()->InsertChar('J', ui::EF_NONE);
674  EXPECT_EQ(1, listener.actions_performed());
675  EXPECT_EQ(1, listener.perform_action_index());
676
677  // Type the second character of "JELLY", item shouldn't change and
678  // OnPerformAction() shouldn't be re-called.
679  combobox_->GetTextInputClient()->InsertChar('E', ui::EF_NONE);
680  EXPECT_EQ(1, listener.actions_performed());
681  EXPECT_EQ(1, listener.perform_action_index());
682
683  // Clears the typed text.
684  combobox_->OnBlur();
685
686  // Type the first character of "PEANUT BUTTER", which should change the
687  // selected index and perform an action.
688  combobox_->GetTextInputClient()->InsertChar('P', ui::EF_NONE);
689  EXPECT_EQ(2, listener.actions_performed());
690  EXPECT_EQ(2, listener.perform_action_index());
691}
692
693}  // namespace views
694