1 // Copyright 2014 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 #import "chrome/browser/ui/cocoa/profiles/profile_chooser_controller.h"
7 #include "base/command_line.h"
8 #include "base/mac/scoped_nsobject.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/strings/sys_string_conversions.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/prefs/pref_service_syncable.h"
13 #include "chrome/browser/profiles/avatar_menu.h"
14 #include "chrome/browser/profiles/profile_info_cache.h"
15 #include "chrome/browser/signin/fake_profile_oauth2_token_service.h"
16 #include "chrome/browser/signin/fake_profile_oauth2_token_service_builder.h"
17 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
18 #include "chrome/browser/signin/signin_manager_factory.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
21 #include "chrome/common/chrome_switches.h"
22 #include "components/signin/core/browser/profile_oauth2_token_service.h"
23 #include "components/signin/core/browser/signin_manager.h"
25 const std::string kEmail = "user@gmail.com";
26 const std::string kSecondaryEmail = "user2@gmail.com";
27 const std::string kLoginToken = "oauth2_login_token";
29 class ProfileChooserControllerTest : public CocoaProfileTest {
31 ProfileChooserControllerTest() {
34 virtual void SetUp() OVERRIDE {
35 CocoaProfileTest::SetUp();
36 ASSERT_TRUE(browser()->profile());
38 TestingProfile::TestingFactories factories;
40 std::make_pair(ProfileOAuth2TokenServiceFactory::GetInstance(),
41 BuildFakeProfileOAuth2TokenService));
42 testing_profile_manager()->
43 CreateTestingProfile("test1", scoped_ptr<PrefServiceSyncable>(),
44 base::ASCIIToUTF16("Test 1"), 0, std::string(),
46 testing_profile_manager()->
47 CreateTestingProfile("test2", scoped_ptr<PrefServiceSyncable>(),
48 base::ASCIIToUTF16("Test 2"), 1, std::string(),
49 TestingProfile::TestingFactories());
51 menu_ = new AvatarMenu(testing_profile_manager()->profile_info_cache(),
55 // There should be the default profile + two profiles we created.
56 EXPECT_EQ(3U, menu_->GetNumberOfItems());
59 virtual void TearDown() OVERRIDE {
62 CocoaProfileTest::TearDown();
65 void StartProfileChooserController() {
66 NSRect frame = [test_window() frame];
67 NSPoint point = NSMakePoint(NSMidX(frame), NSMidY(frame));
68 controller_.reset([[ProfileChooserController alloc]
69 initWithBrowser:browser()
71 withMode:BUBBLE_VIEW_MODE_PROFILE_CHOOSER]);
72 [controller_ showWindow:nil];
75 void EnableNewProfileManagement() {
76 CommandLine::ForCurrentProcess()->AppendSwitch(
77 switches::kNewProfileManagement);
80 void EnableNewAvatarMenuOnly() {
81 CommandLine::ForCurrentProcess()->AppendSwitch(switches::kNewAvatarMenu);
84 void EnableFastUserSwitching() {
85 CommandLine::ForCurrentProcess()->AppendSwitch(
86 switches::kFastUserSwitching);
89 ProfileChooserController* controller() { return controller_; }
90 AvatarMenu* menu() { return menu_; }
93 base::scoped_nsobject<ProfileChooserController> controller_;
95 // Weak; owned by |controller_|.
98 DISALLOW_COPY_AND_ASSIGN(ProfileChooserControllerTest);
101 TEST_F(ProfileChooserControllerTest, InitialLayoutWithNewManagement) {
102 EnableNewProfileManagement();
103 StartProfileChooserController();
105 NSArray* subviews = [[[controller() window] contentView] subviews];
106 EXPECT_EQ(1U, [subviews count]);
107 subviews = [[subviews objectAtIndex:0] subviews];
109 // Three profiles means we should have one active card, one separator and
110 // one option buttons view.
111 EXPECT_EQ(3U, [subviews count]);
113 // For a local profile, there should be one button in the option buttons view.
114 NSArray* buttonSubviews = [[subviews objectAtIndex:0] subviews];
115 EXPECT_EQ(1U, [buttonSubviews count]);
116 NSButton* button = static_cast<NSButton*>([buttonSubviews objectAtIndex:0]);
117 EXPECT_EQ(@selector(showUserManager:), [button action]);
118 EXPECT_EQ(controller(), [button target]);
120 // There should be a separator.
121 EXPECT_TRUE([[subviews objectAtIndex:1] isKindOfClass:[NSBox class]]);
123 // There should be the profile avatar, name and links container in the active
124 // card view. The links displayed in the container are checked separately.
125 NSArray* activeCardSubviews = [[subviews objectAtIndex:2] subviews];
126 EXPECT_EQ(3U, [activeCardSubviews count]);
129 NSView* activeProfileImage = [activeCardSubviews objectAtIndex:2];
130 EXPECT_TRUE([activeProfileImage isKindOfClass:[NSImageView class]]);
133 NSView* activeProfileName = [activeCardSubviews objectAtIndex:1];
134 EXPECT_TRUE([activeProfileName isKindOfClass:[NSButton class]]);
135 EXPECT_EQ(menu()->GetItemAt(0).name, base::SysNSStringToUTF16(
136 [static_cast<NSButton*>(activeProfileName) title]));
138 // Profile links. This is a local profile, so there should be a signin button.
139 NSArray* linksSubviews = [[activeCardSubviews objectAtIndex:0] subviews];
140 EXPECT_EQ(1U, [linksSubviews count]);
141 NSButton* link = static_cast<NSButton*>([linksSubviews objectAtIndex:0]);
142 EXPECT_EQ(@selector(showInlineSigninPage:), [link action]);
143 EXPECT_EQ(controller(), [link target]);
146 TEST_F(ProfileChooserControllerTest, InitialLayoutWithNewMenu) {
147 EnableNewAvatarMenuOnly();
148 StartProfileChooserController();
150 NSArray* subviews = [[[controller() window] contentView] subviews];
151 EXPECT_EQ(1U, [subviews count]);
152 subviews = [[subviews objectAtIndex:0] subviews];
154 // Three profiles means we should have one active card and a
155 // fast user switcher which has two "other" profiles and 2 separators. In
156 // this flow we also have the tutorial view.
157 EXPECT_EQ(6U, [subviews count]);
159 // There should be two "other profiles" items. The items are drawn from the
160 // bottom up, so in the opposite order of those in the AvatarMenu.
161 int profileIndex = 1;
162 for (int i = 3; i >= 0; i -= 2) {
163 // Each profile button has a separator.
164 EXPECT_TRUE([[subviews objectAtIndex:i] isKindOfClass:[NSBox class]]);
166 NSButton* button = static_cast<NSButton*>([subviews objectAtIndex:i-1]);
167 EXPECT_EQ(menu()->GetItemAt(profileIndex).name,
168 base::SysNSStringToUTF16([button title]));
169 EXPECT_EQ(profileIndex, [button tag]);
170 EXPECT_EQ(@selector(switchToProfile:), [button action]);
171 EXPECT_EQ(controller(), [button target]);
175 // There should be the profile avatar, name and links container in the active
176 // card view. The links displayed in the container are checked separately.
177 NSArray* activeCardSubviews = [[subviews objectAtIndex:4] subviews];
178 EXPECT_EQ(3U, [activeCardSubviews count]);
181 NSView* activeProfileImage = [activeCardSubviews objectAtIndex:2];
182 EXPECT_TRUE([activeProfileImage isKindOfClass:[NSImageView class]]);
185 NSView* activeProfileName = [activeCardSubviews objectAtIndex:1];
186 EXPECT_TRUE([activeProfileName isKindOfClass:[NSButton class]]);
187 EXPECT_EQ(menu()->GetItemAt(0).name, base::SysNSStringToUTF16(
188 [static_cast<NSButton*>(activeProfileName) title]));
190 // Profile links. This is a local profile, so there should be a signin button.
191 NSArray* linksSubviews = [[activeCardSubviews objectAtIndex:0] subviews];
192 EXPECT_EQ(1U, [linksSubviews count]);
193 NSButton* link = static_cast<NSButton*>([linksSubviews objectAtIndex:0]);
194 EXPECT_EQ(@selector(showTabbedSigninPage:), [link action]);
195 EXPECT_EQ(controller(), [link target]);
197 // There is a tutorial view card at the top.
198 EXPECT_TRUE([[subviews objectAtIndex:5] isKindOfClass:[NSView class]]);
201 TEST_F(ProfileChooserControllerTest, InitialLayoutWithFastUserSwitcher) {
202 EnableNewProfileManagement();
203 EnableFastUserSwitching();
204 StartProfileChooserController();
206 NSArray* subviews = [[[controller() window] contentView] subviews];
207 EXPECT_EQ(1U, [subviews count]);
208 subviews = [[subviews objectAtIndex:0] subviews];
210 // Three profiles means we should have one active card, two "other" profiles,
211 // each with a separator, and one option buttons view.
212 EXPECT_EQ(7U, [subviews count]);
214 // For a local profile, there should be one button in the option buttons view.
215 NSArray* buttonSubviews = [[subviews objectAtIndex:0] subviews];
216 EXPECT_EQ(1U, [buttonSubviews count]);
217 NSButton* button = static_cast<NSButton*>([buttonSubviews objectAtIndex:0]);
218 EXPECT_EQ(@selector(showUserManager:), [button action]);
219 EXPECT_EQ(controller(), [button target]);
221 // There should be a separator.
222 EXPECT_TRUE([[subviews objectAtIndex:1] isKindOfClass:[NSBox class]]);
224 // There should be two "other profiles" items. The items are drawn from the
225 // bottom up, so in the opposite order of those in the AvatarMenu.
226 int profileIndex = 1;
227 for (int i = 5; i >= 2; i -= 2) {
228 // Each profile button has a separator.
229 EXPECT_TRUE([[subviews objectAtIndex:i] isKindOfClass:[NSBox class]]);
231 NSButton* button = static_cast<NSButton*>([subviews objectAtIndex:i-1]);
232 EXPECT_EQ(menu()->GetItemAt(profileIndex).name,
233 base::SysNSStringToUTF16([button title]));
234 EXPECT_EQ(profileIndex, [button tag]);
235 EXPECT_EQ(@selector(switchToProfile:), [button action]);
236 EXPECT_EQ(controller(), [button target]);
240 // There should be the profile avatar, name and links container in the active
241 // card view. These have been checked separately.
242 NSArray* activeCardSubviews = [[subviews objectAtIndex:6] subviews];
243 EXPECT_EQ(3U, [activeCardSubviews count]);
246 TEST_F(ProfileChooserControllerTest, OtherProfilesSortedAlphabetically) {
247 EnableNewAvatarMenuOnly();
249 // Add two extra profiles, to make sure sorting is alphabetical and not
250 // by order of creation.
251 testing_profile_manager()->
252 CreateTestingProfile("test3", scoped_ptr<PrefServiceSyncable>(),
253 base::ASCIIToUTF16("New Profile"), 1, std::string(),
254 TestingProfile::TestingFactories());
255 testing_profile_manager()->
256 CreateTestingProfile("test4", scoped_ptr<PrefServiceSyncable>(),
257 base::ASCIIToUTF16("Another Test"), 1, std::string(),
258 TestingProfile::TestingFactories());
259 StartProfileChooserController();
261 NSArray* subviews = [[[controller() window] contentView] subviews];
262 EXPECT_EQ(1U, [subviews count]);
263 subviews = [[subviews objectAtIndex:0] subviews];
264 NSString* sortedNames[] = { @"Another Test",
268 // There are four "other" profiles, each with a button and a separator, an
269 // active profile card, and a tutorial card.
270 EXPECT_EQ(10U, [subviews count]);
271 // There should be four "other profiles" items, sorted alphabetically.
272 // The "other profiles" start at index 0, and each have a separator. We
273 // need to iterate through the profiles in the order displayed in the bubble,
274 // which is opposite from the drawn order.
275 int sortedNameIndex = 0;
276 for (int i = 7; i >= 0; i -= 2) {
277 // The item at index i is the separator.
278 NSButton* button = static_cast<NSButton*>([subviews objectAtIndex:i-1]);
280 [[button title] isEqualToString:sortedNames[sortedNameIndex++]]);
284 TEST_F(ProfileChooserControllerTest,
285 LocalProfileActiveCardLinksWithNewManagement) {
286 EnableNewProfileManagement();
287 StartProfileChooserController();
288 NSArray* subviews = [[[controller() window] contentView] subviews];
289 EXPECT_EQ(1U, [subviews count]);
290 subviews = [[subviews objectAtIndex:0] subviews];
291 NSArray* activeCardSubviews = [[subviews objectAtIndex:2] subviews];
292 NSArray* activeCardLinks = [[activeCardSubviews objectAtIndex:0] subviews];
294 // There should be one "sign in" link.
295 EXPECT_EQ(1U, [activeCardLinks count]);
296 NSButton* signinLink =
297 static_cast<NSButton*>([activeCardLinks objectAtIndex:0]);
298 EXPECT_EQ(@selector(showInlineSigninPage:), [signinLink action]);
299 EXPECT_EQ(controller(), [signinLink target]);
302 TEST_F(ProfileChooserControllerTest,
303 LocalProfileActiveCardLinksWithNewMenu) {
304 EnableNewAvatarMenuOnly();
305 StartProfileChooserController();
306 NSArray* subviews = [[[controller() window] contentView] subviews];
307 EXPECT_EQ(1U, [subviews count]);
308 subviews = [[subviews objectAtIndex:0] subviews];
309 NSArray* activeCardSubviews = [[subviews objectAtIndex:4] subviews];
310 NSArray* activeCardLinks = [[activeCardSubviews objectAtIndex:0] subviews];
312 // There should be one "sign in" link.
313 EXPECT_EQ(1U, [activeCardLinks count]);
314 NSButton* signinLink =
315 static_cast<NSButton*>([activeCardLinks objectAtIndex:0]);
316 EXPECT_EQ(@selector(showTabbedSigninPage:), [signinLink action]);
317 EXPECT_EQ(controller(), [signinLink target]);
320 TEST_F(ProfileChooserControllerTest,
321 SignedInProfileActiveCardLinksWithNewManagement) {
322 EnableNewProfileManagement();
323 // Sign in the first profile.
324 ProfileInfoCache* cache = testing_profile_manager()->profile_info_cache();
325 cache->SetUserNameOfProfileAtIndex(0, base::ASCIIToUTF16(kEmail));
327 StartProfileChooserController();
328 NSArray* subviews = [[[controller() window] contentView] subviews];
329 EXPECT_EQ(1U, [subviews count]);
330 subviews = [[subviews objectAtIndex:0] subviews];
331 NSArray* activeCardSubviews = [[subviews objectAtIndex:2] subviews];
332 NSArray* activeCardLinks = [[activeCardSubviews objectAtIndex:0] subviews];
334 // There is one link: manage accounts.
335 EXPECT_EQ(1U, [activeCardLinks count]);
336 NSButton* manageAccountsLink =
337 static_cast<NSButton*>([activeCardLinks objectAtIndex:0]);
338 EXPECT_EQ(@selector(showAccountManagement:), [manageAccountsLink action]);
339 EXPECT_EQ(controller(), [manageAccountsLink target]);
342 TEST_F(ProfileChooserControllerTest,
343 SignedInProfileActiveCardLinksWithNewMenu) {
344 EnableNewAvatarMenuOnly();
345 // Sign in the first profile.
346 ProfileInfoCache* cache = testing_profile_manager()->profile_info_cache();
347 cache->SetUserNameOfProfileAtIndex(0, base::ASCIIToUTF16(kEmail));
349 StartProfileChooserController();
350 NSArray* subviews = [[[controller() window] contentView] subviews];
351 EXPECT_EQ(1U, [subviews count]);
352 subviews = [[subviews objectAtIndex:0] subviews];
353 NSArray* activeCardSubviews = [[subviews objectAtIndex:4] subviews];
354 NSArray* activeCardLinks = [[activeCardSubviews objectAtIndex:0] subviews];
356 // There is one link, without a target and with the user's email.
357 EXPECT_EQ(1U, [activeCardLinks count]);
358 NSButton* emailLink =
359 static_cast<NSButton*>([activeCardLinks objectAtIndex:0]);
360 EXPECT_EQ(nil, [emailLink action]);
361 EXPECT_EQ(kEmail, base::SysNSStringToUTF8([emailLink title]));
362 EXPECT_EQ(controller(), [emailLink target]);
365 TEST_F(ProfileChooserControllerTest, AccountManagementLayout) {
366 EnableNewProfileManagement();
367 // Sign in the first profile.
368 ProfileInfoCache* cache = testing_profile_manager()->profile_info_cache();
369 cache->SetUserNameOfProfileAtIndex(0, base::ASCIIToUTF16(kEmail));
371 // Set up the signin manager and the OAuth2Tokens.
372 Profile* profile = browser()->profile();
373 SigninManagerFactory::GetForProfile(profile)->
374 SetAuthenticatedUsername(kEmail);
375 ProfileOAuth2TokenServiceFactory::GetForProfile(profile)->
376 UpdateCredentials(kEmail, kLoginToken);
377 ProfileOAuth2TokenServiceFactory::GetForProfile(profile)->
378 UpdateCredentials(kSecondaryEmail, kLoginToken);
380 StartProfileChooserController();
381 [controller() initMenuContentsWithView:BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT];
383 NSArray* subviews = [[[controller() window] contentView] subviews];
384 EXPECT_EQ(1U, [subviews count]);
385 subviews = [[subviews objectAtIndex:0] subviews];
387 // There should be one active card, one accounts container, two separators
388 // and one option buttons view.
389 EXPECT_EQ(5U, [subviews count]);
391 // There should be two buttons in the option buttons view.
392 NSArray* buttonSubviews = [[subviews objectAtIndex:0] subviews];
393 const SEL buttonSelectors[] = { @selector(showUserManager:),
394 @selector(lockProfile:) };
395 EXPECT_EQ(2U, [buttonSubviews count]);
396 for (NSUInteger i = 0; i < [buttonSubviews count]; ++i) {
397 NSButton* button = static_cast<NSButton*>([buttonSubviews objectAtIndex:i]);
398 EXPECT_EQ(buttonSelectors[i], [button action]);
399 EXPECT_EQ(controller(), [button target]);
402 // There should be a separator.
403 EXPECT_TRUE([[subviews objectAtIndex:1] isKindOfClass:[NSBox class]]);
405 // In the accounts view, there should be the account list container
406 // accounts and one "add accounts" button.
407 NSArray* accountsSubviews = [[subviews objectAtIndex:2] subviews];
408 EXPECT_EQ(2U, [accountsSubviews count]);
410 NSButton* addAccountsButton =
411 static_cast<NSButton*>([accountsSubviews objectAtIndex:0]);
412 EXPECT_EQ(@selector(addAccount:), [addAccountsButton action]);
413 EXPECT_EQ(controller(), [addAccountsButton target]);
415 // There should be two accounts in the account list container.
416 NSArray* accountsListSubviews = [[accountsSubviews objectAtIndex:1] subviews];
417 EXPECT_EQ(2U, [accountsListSubviews count]);
419 NSButton* genericAccount =
420 static_cast<NSButton*>([accountsListSubviews objectAtIndex:0]);
421 NSButton* genericAccountDelete =
422 static_cast<NSButton*>([[genericAccount subviews] objectAtIndex:0]);
423 EXPECT_EQ(@selector(showAccountRemovalView:), [genericAccountDelete action]);
424 EXPECT_EQ(controller(), [genericAccountDelete target]);
425 EXPECT_NE(-1, [genericAccountDelete tag]);
427 // Primary accounts are always last.
428 NSButton* primaryAccount =
429 static_cast<NSButton*>([accountsListSubviews objectAtIndex:1]);
430 NSButton* primaryAccountDelete =
431 static_cast<NSButton*>([[primaryAccount subviews] objectAtIndex:0]);
432 EXPECT_EQ(@selector(showAccountRemovalView:), [primaryAccountDelete action]);
433 EXPECT_EQ(controller(), [primaryAccountDelete target]);
434 EXPECT_EQ(-1, [primaryAccountDelete tag]);
436 // There should be another separator.
437 EXPECT_TRUE([[subviews objectAtIndex:3] isKindOfClass:[NSBox class]]);
439 // There should be the profile avatar, name and a "hide accounts" link
440 // container in the active card view.
441 NSArray* activeCardSubviews = [[subviews objectAtIndex:4] subviews];
442 EXPECT_EQ(3U, [activeCardSubviews count]);
445 NSView* activeProfileImage = [activeCardSubviews objectAtIndex:2];
446 EXPECT_TRUE([activeProfileImage isKindOfClass:[NSImageView class]]);
449 NSView* activeProfileName = [activeCardSubviews objectAtIndex:1];
450 EXPECT_TRUE([activeProfileName isKindOfClass:[NSButton class]]);
451 EXPECT_EQ(menu()->GetItemAt(0).name, base::SysNSStringToUTF16(
452 [static_cast<NSButton*>(activeProfileName) title]));
454 // Profile links. This is a local profile, so there should be a signin button.
455 NSArray* linksSubviews = [[activeCardSubviews objectAtIndex:0] subviews];
456 EXPECT_EQ(1U, [linksSubviews count]);
457 NSButton* link = static_cast<NSButton*>([linksSubviews objectAtIndex:0]);
458 EXPECT_EQ(@selector(hideAccountManagement:), [link action]);
459 EXPECT_EQ(controller(), [link target]);