Home | History | Annotate | Download | only in cocoa
      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 #import "ui/app_list/cocoa/apps_search_box_controller.h"
      6 
      7 #include "base/mac/scoped_nsobject.h"
      8 #include "base/strings/sys_string_conversions.h"
      9 #include "base/strings/utf_string_conversions.h"
     10 #import "testing/gtest_mac.h"
     11 #include "ui/app_list/app_list_menu.h"
     12 #include "ui/app_list/app_list_model_observer.h"
     13 #include "ui/app_list/search_box_model.h"
     14 #include "ui/app_list/test/app_list_test_model.h"
     15 #include "ui/app_list/test/app_list_test_view_delegate.h"
     16 #import "ui/base/cocoa/menu_controller.h"
     17 #import "ui/gfx/test/ui_cocoa_test_helper.h"
     18 
     19 using base::ASCIIToUTF16;
     20 
     21 @interface TestAppsSearchBoxDelegate : NSObject<AppsSearchBoxDelegate> {
     22  @private
     23   app_list::SearchBoxModel searchBoxModel_;
     24   app_list::test::AppListTestViewDelegate appListDelegate_;
     25   app_list::test::AppListTestModel appListModel_;
     26   int textChangeCount_;
     27 }
     28 
     29 @property(assign, nonatomic) int textChangeCount;
     30 
     31 @end
     32 
     33 @implementation TestAppsSearchBoxDelegate
     34 
     35 @synthesize textChangeCount = textChangeCount_;
     36 
     37 - (id)init {
     38   if ((self = [super init])) {
     39     app_list::AppListViewDelegate::Users users(2);
     40     users[0].name = ASCIIToUTF16("user1");
     41     users[1].name = ASCIIToUTF16("user2");
     42     users[1].email = ASCIIToUTF16("user2 (a] chromium.org");
     43     users[1].active = true;
     44     appListDelegate_.SetUsers(users);
     45   }
     46   return self;
     47 }
     48 
     49 - (app_list::SearchBoxModel*)searchBoxModel {
     50   return &searchBoxModel_;
     51 }
     52 
     53 - (app_list::AppListViewDelegate*)appListDelegate {
     54   return &appListDelegate_;
     55 }
     56 
     57 - (app_list::test::AppListTestViewDelegate*)appListTestViewDelegate {
     58   return &appListDelegate_;
     59 }
     60 
     61 
     62 - (BOOL)control:(NSControl*)control
     63                textView:(NSTextView*)textView
     64     doCommandBySelector:(SEL)command {
     65   return NO;
     66 }
     67 
     68 - (void)modelTextDidChange {
     69   ++textChangeCount_;
     70 }
     71 
     72 - (CGFloat)bubbleCornerRadius {
     73   return 3;
     74 }
     75 
     76 - (app_list::AppListModel*)appListModel {
     77   return &appListModel_;
     78 }
     79 
     80 @end
     81 
     82 namespace app_list {
     83 namespace test {
     84 
     85 class AppsSearchBoxControllerTest : public ui::CocoaTest {
     86  public:
     87   AppsSearchBoxControllerTest() {
     88     Init();
     89   }
     90 
     91   virtual void SetUp() OVERRIDE {
     92     apps_search_box_controller_.reset(
     93         [[AppsSearchBoxController alloc] initWithFrame:
     94             NSMakeRect(0, 0, 400, 100)]);
     95     delegate_.reset([[TestAppsSearchBoxDelegate alloc] init]);
     96     [apps_search_box_controller_ setDelegate:delegate_];
     97 
     98     ui::CocoaTest::SetUp();
     99     [[test_window() contentView] addSubview:[apps_search_box_controller_ view]];
    100   }
    101 
    102   virtual void TearDown() OVERRIDE {
    103     [apps_search_box_controller_ setDelegate:nil];
    104     ui::CocoaTest::TearDown();
    105   }
    106 
    107   void SimulateKeyAction(SEL c) {
    108     NSControl* control = [apps_search_box_controller_ searchTextField];
    109     [apps_search_box_controller_ control:control
    110                                 textView:nil
    111                      doCommandBySelector:c];
    112   }
    113 
    114  protected:
    115   base::scoped_nsobject<TestAppsSearchBoxDelegate> delegate_;
    116   base::scoped_nsobject<AppsSearchBoxController> apps_search_box_controller_;
    117 
    118  private:
    119   DISALLOW_COPY_AND_ASSIGN(AppsSearchBoxControllerTest);
    120 };
    121 
    122 TEST_VIEW(AppsSearchBoxControllerTest, [apps_search_box_controller_ view]);
    123 
    124 // Test the search box initialization, and search input and clearing.
    125 TEST_F(AppsSearchBoxControllerTest, SearchBoxModel) {
    126   app_list::SearchBoxModel* model = [delegate_ searchBoxModel];
    127   // Usually localized "Search".
    128   const base::string16 hit_text(ASCIIToUTF16("hint"));
    129   model->SetHintText(hit_text);
    130   EXPECT_NSEQ(base::SysUTF16ToNSString(hit_text),
    131       [[[apps_search_box_controller_ searchTextField] cell] placeholderString]);
    132 
    133   const base::string16 search_text(ASCIIToUTF16("test"));
    134   model->SetText(search_text);
    135   EXPECT_NSEQ(base::SysUTF16ToNSString(search_text),
    136               [[apps_search_box_controller_ searchTextField] stringValue]);
    137   // Updates coming via the model should notify the delegate.
    138   EXPECT_EQ(1, [delegate_ textChangeCount]);
    139 
    140   // Updates from the view should update the model and notify the delegate.
    141   [apps_search_box_controller_ clearSearch];
    142   EXPECT_EQ(base::string16(), model->text());
    143   EXPECT_NSEQ([NSString string],
    144               [[apps_search_box_controller_ searchTextField] stringValue]);
    145   EXPECT_EQ(2, [delegate_ textChangeCount]);
    146 
    147   // Test pressing escape clears the search. First add some text.
    148   model->SetText(search_text);
    149   EXPECT_EQ(3, [delegate_ textChangeCount]);
    150 
    151   EXPECT_NSEQ(base::SysUTF16ToNSString(search_text),
    152               [[apps_search_box_controller_ searchTextField] stringValue]);
    153   SimulateKeyAction(@selector(complete:));
    154   EXPECT_NSEQ([NSString string],
    155               [[apps_search_box_controller_ searchTextField] stringValue]);
    156   EXPECT_EQ(4, [delegate_ textChangeCount]);
    157 }
    158 
    159 // Test the popup menu items when there is only one user..
    160 TEST_F(AppsSearchBoxControllerTest, SearchBoxMenuSingleUser) {
    161   // Set a single user. We need to set the delegate again because the
    162   // AppListModel observer isn't hooked up in these tests.
    163   [delegate_ appListTestViewDelegate]->SetUsers(
    164       app_list::AppListViewDelegate::Users(1));
    165   [apps_search_box_controller_ setDelegate:delegate_];
    166 
    167   NSPopUpButton* menu_control = [apps_search_box_controller_ menuControl];
    168   EXPECT_TRUE([apps_search_box_controller_ appListMenu]);
    169   ui::MenuModel* menu_model
    170       = [apps_search_box_controller_ appListMenu]->menu_model();
    171   // Add one to the item count to account for the blank, first item that Cocoa
    172   // has in its popup menus.
    173   EXPECT_EQ(menu_model->GetItemCount() + 1,
    174             [[menu_control menu] numberOfItems]);
    175 
    176   // All command ids should be less than |SELECT_PROFILE| as no user menu items
    177   // are being shown.
    178   for (int i = 0; i < menu_model->GetItemCount(); ++i)
    179     EXPECT_LT(menu_model->GetCommandIdAt(i), AppListMenu::SELECT_PROFILE);
    180 
    181   // The number of items should match the index that starts profile items.
    182   EXPECT_EQ(AppListMenu::SELECT_PROFILE, menu_model->GetItemCount());
    183 }
    184 
    185 // Test the popup menu items for the multi-profile case.
    186 TEST_F(AppsSearchBoxControllerTest, SearchBoxMenu) {
    187   const app_list::AppListViewDelegate::Users& users =
    188       [delegate_ appListDelegate]->GetUsers();
    189   NSPopUpButton* menu_control = [apps_search_box_controller_ menuControl];
    190   EXPECT_TRUE([apps_search_box_controller_ appListMenu]);
    191   ui::MenuModel* menu_model
    192       = [apps_search_box_controller_ appListMenu]->menu_model();
    193   // Add one to the item count to account for the blank, first item that Cocoa
    194   // has in its popup menus.
    195   EXPECT_EQ(menu_model->GetItemCount() + 1,
    196             [[menu_control menu] numberOfItems]);
    197 
    198   ui::MenuModel* found_menu_model = menu_model;
    199   int index;
    200   MenuController* controller = [[menu_control menu] delegate];
    201 
    202   // The first user item is an unchecked label.
    203   EXPECT_TRUE(ui::MenuModel::GetModelAndIndexForCommandId(
    204       AppListMenu::SELECT_PROFILE, &menu_model, &index));
    205   EXPECT_EQ(found_menu_model, menu_model);
    206   NSMenuItem* unchecked_user_item = [[menu_control menu] itemAtIndex:index + 1];
    207   [controller validateUserInterfaceItem:unchecked_user_item];
    208   // The profile name should be shown if there is no email available.
    209   EXPECT_NSEQ(base::SysUTF16ToNSString(users[0].name),
    210               [unchecked_user_item title]);
    211   EXPECT_EQ(NSOffState, [unchecked_user_item state]);
    212 
    213   // The second user item is a checked label because it is the active profile.
    214   EXPECT_TRUE(ui::MenuModel::GetModelAndIndexForCommandId(
    215       AppListMenu::SELECT_PROFILE + 1, &menu_model, &index));
    216   EXPECT_EQ(found_menu_model, menu_model);
    217   NSMenuItem* checked_user_item = [[menu_control menu] itemAtIndex:index + 1];
    218   [controller validateUserInterfaceItem:checked_user_item];
    219   // The email is shown when available.
    220   EXPECT_NSEQ(base::SysUTF16ToNSString(users[1].email),
    221               [checked_user_item title]);
    222   EXPECT_EQ(NSOnState, [checked_user_item state]);
    223 
    224   // A regular item should have just the label.
    225   EXPECT_TRUE(ui::MenuModel::GetModelAndIndexForCommandId(
    226       AppListMenu::SHOW_SETTINGS, &menu_model, &index));
    227   EXPECT_EQ(found_menu_model, menu_model);
    228   NSMenuItem* settings_item = [[menu_control menu] itemAtIndex:index + 1];
    229   EXPECT_FALSE([settings_item view]);
    230   EXPECT_NSEQ(base::SysUTF16ToNSString(menu_model->GetLabelAt(index)),
    231               [settings_item title]);
    232 }
    233 
    234 // Test adding another user, and changing an existing one.
    235 TEST_F(AppsSearchBoxControllerTest, SearchBoxMenuChangingUsers) {
    236   app_list::AppListViewDelegate::Users users =
    237       [delegate_ appListDelegate]->GetUsers();
    238   EXPECT_EQ(2u, users.size());
    239   ui::MenuModel* menu_model
    240       = [apps_search_box_controller_ appListMenu]->menu_model();
    241   // Adding one to account for the empty item at index 0 in Cocoa popup menus.
    242   int non_user_items = menu_model->GetItemCount() - users.size() + 1;
    243 
    244   NSPopUpButton* menu_control = [apps_search_box_controller_ menuControl];
    245   EXPECT_EQ(2, [[menu_control menu] numberOfItems] - non_user_items);
    246   EXPECT_NSEQ(base::SysUTF16ToNSString(users[0].name),
    247               [[[menu_control menu] itemAtIndex:1] title]);
    248 
    249   users[0].name = ASCIIToUTF16("renamed user");
    250   app_list::AppListViewDelegate::User new_user;
    251   new_user.name = ASCIIToUTF16("user3");
    252   users.push_back(new_user);
    253   [delegate_ appListTestViewDelegate]->SetUsers(users);
    254   // Note: menu does not automatically get rebuilt. Force a rebuild (which
    255   // would normally occur when the UI is closed / re-opend).
    256   [apps_search_box_controller_ rebuildMenu];
    257 
    258   // Should now be an extra item, and it should have correct titles.
    259   EXPECT_EQ(3, [[menu_control menu] numberOfItems] - non_user_items);
    260   EXPECT_NSEQ(base::SysUTF16ToNSString(users[0].name),
    261               [[[menu_control menu] itemAtIndex:1] title]);
    262   EXPECT_NSEQ(base::SysUTF16ToNSString(new_user.name),
    263               [[[menu_control menu] itemAtIndex:3] title]);
    264 }
    265 
    266 }  // namespace test
    267 }  // namespace app_list
    268