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/avatar_button_controller.h"
7 #include "base/command_line.h"
8 #include "base/mac/scoped_nsobject.h"
9 #include "base/strings/sys_string_conversions.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/profiles/profile_manager.h"
12 #include "chrome/browser/profiles/profiles_state.h"
13 #include "chrome/browser/ui/browser.h"
14 #include "chrome/browser/ui/browser_window.h"
15 #import "chrome/browser/ui/cocoa/base_bubble_controller.h"
16 #include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
17 #include "chrome/browser/ui/cocoa/info_bubble_window.h"
18 #import "chrome/browser/ui/cocoa/profiles/profile_chooser_controller.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "chrome/grit/generated_resources.h"
21 #include "components/signin/core/common/profile_management_switches.h"
22 #include "grit/theme_resources.h"
23 #import "testing/gtest_mac.h"
24 #include "ui/base/l10n/l10n_util.h"
25 #include "ui/base/resource/resource_bundle.h"
27 // Defined in the AvatarButtonController implementation.
28 @interface AvatarButtonController (ExposedForTesting)
29 - (void)updateErrorStatus:(BOOL)hasError;
32 // Subclassing AvatarButtonController to be able to control the state of
33 // keyboard modifierFlags.
34 @interface AvatarButtonControllerForTesting : AvatarButtonController {
40 @interface AvatarButtonControllerForTesting (ExposedForTesting)
41 - (void)setIsCtrlPressed:(BOOL)isPressed;
42 - (BOOL)isCtrlPressed;
45 @implementation AvatarButtonControllerForTesting
46 - (void)setIsCtrlPressed:(BOOL)isPressed {
47 isCtrlPressed_ = isPressed;
50 - (BOOL)isCtrlPressed {
51 // Always report that Cmd is not pressed since that's the case we're testing
52 // and otherwise running the test while holding the Cmd key makes it fail.
53 return isCtrlPressed_;
57 class AvatarButtonControllerTest : public CocoaProfileTest {
59 void SetUp() override {
60 switches::EnableNewAvatarMenuForTesting(
61 base::CommandLine::ForCurrentProcess());
62 DCHECK(profiles::IsMultipleProfilesEnabled());
64 CocoaProfileTest::SetUp();
65 ASSERT_TRUE(browser());
68 [[AvatarButtonControllerForTesting alloc] initWithBrowser:browser()]);
69 [controller_ setIsCtrlPressed:false];
72 void TearDown() override {
73 browser()->window()->Close();
74 CocoaProfileTest::TearDown();
77 NSButton* button() { return [controller_ buttonView]; }
79 NSView* view() { return [controller_ view]; }
81 AvatarButtonControllerForTesting* controller() { return controller_.get(); }
84 base::scoped_nsobject<AvatarButtonControllerForTesting> controller_;
87 TEST_F(AvatarButtonControllerTest, GenericButtonShown) {
88 ASSERT_FALSE([view() isHidden]);
89 // There is only one local profile, which means displaying the generic
91 EXPECT_NSEQ(@"", [button() title]);
94 TEST_F(AvatarButtonControllerTest, ProfileButtonShown) {
95 // Create a second profile, to force the button to display the profile name.
96 testing_profile_manager()->CreateTestingProfile("batman");
98 ASSERT_FALSE([view() isHidden]);
99 EXPECT_NSEQ(@"Person 1", [button() title]);
102 TEST_F(AvatarButtonControllerTest, ProfileButtonWithErrorShown) {
103 // Create a second profile, to force the button to display the profile name.
104 testing_profile_manager()->CreateTestingProfile("batman");
106 EXPECT_EQ(0, [button() image].size.width);
107 [controller() updateErrorStatus:true];
109 ASSERT_FALSE([view() isHidden]);
110 EXPECT_NSEQ(@"Person 1", [button() title]);
112 // If the button has an authentication error, it should display an error icon.
113 int errorWidth = ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
114 IDR_ICON_PROFILES_AVATAR_BUTTON_ERROR).Width();
115 EXPECT_EQ(errorWidth, [button() image].size.width);
118 TEST_F(AvatarButtonControllerTest, DoubleOpen) {
119 EXPECT_FALSE([controller() menuController]);
121 [button() performClick:button()];
123 BaseBubbleController* menu = [controller() menuController];
125 EXPECT_TRUE([menu isKindOfClass:[ProfileChooserController class]]);
127 [button() performClick:button()];
128 EXPECT_EQ(menu, [controller() menuController]);
130 // Do not animate out because that is hard to test around.
131 static_cast<InfoBubbleWindow*>(menu.window).allowedAnimations =
132 info_bubble::kAnimateNone;
134 EXPECT_FALSE([controller() menuController]);
137 TEST_F(AvatarButtonControllerTest, DontOpenFastSwitcherWithoutTarget) {
138 EXPECT_FALSE([controller() menuController]);
140 [controller() setIsCtrlPressed:YES];
141 [button() performClick:button()];
143 // If there's only one profile and the fast user switcher is requested,
144 // nothing should happen.
145 EXPECT_FALSE([controller() menuController]);
148 TEST_F(AvatarButtonControllerTest, OpenFastUserSwitcherWithTarget) {
149 testing_profile_manager()->CreateTestingProfile("batman");
150 EXPECT_FALSE([controller() menuController]);
152 [controller() setIsCtrlPressed:YES];
153 [button() performClick:button()];
155 BaseBubbleController* menu = [controller() menuController];
157 EXPECT_TRUE([menu isKindOfClass:[ProfileChooserController class]]);
159 // Do not animate out because that is hard to test around.
160 static_cast<InfoBubbleWindow*>(menu.window).allowedAnimations =
161 info_bubble::kAnimateNone;
163 EXPECT_FALSE([controller() menuController]);