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