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