Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / browser / avatar_menu_bubble_controller_unittest.mm
1 // Copyright (c) 2012 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 "chrome/browser/ui/cocoa/browser/avatar_menu_bubble_controller.h"
6
7 #include "base/mac/scoped_nsobject.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/message_loop/message_pump_mac.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/prefs/pref_service_syncable.h"
12 #include "chrome/browser/profiles/avatar_menu.h"
13 #include "chrome/browser/profiles/avatar_menu_observer.h"
14 #include "chrome/browser/profiles/profile_info_cache.h"
15 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
16 #include "chrome/test/base/testing_browser_process.h"
17 #include "chrome/test/base/testing_profile_manager.h"
18 #include "testing/gtest_mac.h"
19 #import "ui/base/cocoa/controls/hyperlink_button_cell.h"
20 #include "ui/events/test/cocoa_test_event_utils.h"
21
22 class AvatarMenuBubbleControllerTest : public CocoaTest {
23  public:
24   AvatarMenuBubbleControllerTest()
25       : manager_(TestingBrowserProcess::GetGlobal()) {
26   }
27
28   virtual void SetUp() {
29     CocoaTest::SetUp();
30     ASSERT_TRUE(manager_.SetUp());
31
32     manager_.CreateTestingProfile("test1", scoped_ptr<PrefServiceSyncable>(),
33                                   base::ASCIIToUTF16("Test 1"), 1,
34                                   std::string(),
35                                   TestingProfile::TestingFactories());
36     manager_.CreateTestingProfile("test2", scoped_ptr<PrefServiceSyncable>(),
37                                   base::ASCIIToUTF16("Test 2"), 0,
38                                   std::string(),
39                                   TestingProfile::TestingFactories());
40
41     menu_ = new AvatarMenu(manager_.profile_info_cache(), NULL, NULL);
42     menu_->RebuildMenu();
43
44     NSRect frame = [test_window() frame];
45     NSPoint point = NSMakePoint(NSMidX(frame), NSMidY(frame));
46     controller_ =
47         [[AvatarMenuBubbleController alloc] initWithMenu:menu()
48                                              parentWindow:test_window()
49                                                anchoredAt:point];
50   }
51
52   TestingProfileManager* manager() { return &manager_; }
53   AvatarMenuBubbleController* controller() { return controller_; }
54   AvatarMenu* menu() { return menu_; }
55
56   AvatarMenuItemController* GetHighlightedItem() {
57     for (AvatarMenuItemController* item in [controller() items]) {
58       if ([item isHighlighted])
59         return item;
60     }
61     return nil;
62   }
63
64  private:
65   TestingProfileManager manager_;
66
67   // Weak; releases self.
68   AvatarMenuBubbleController* controller_;
69
70   // Weak; owned by |controller_|.
71   AvatarMenu* menu_;
72 };
73
74 TEST_F(AvatarMenuBubbleControllerTest, InitialLayout) {
75   [controller() showWindow:nil];
76
77   // Two profiles means two item views and the new button with separator.
78   NSView* contents = [[controller() window] contentView];
79   EXPECT_EQ(4U, [[contents subviews] count]);
80
81   // Loop over the itmes and match the viewController views to subviews.
82   NSMutableArray* subviews =
83       [NSMutableArray arrayWithArray:[contents subviews]];
84   for (AvatarMenuItemController* viewController in [controller() items]) {
85     for (NSView* subview in subviews) {
86       if ([viewController view] == subview) {
87         [subviews removeObject:subview];
88         break;
89       }
90     }
91   }
92
93   // The one remaining subview should be the new user button.
94   EXPECT_EQ(2U, [subviews count]);
95
96   BOOL hasButton = NO;
97   BOOL hasSeparator = NO;
98   for (NSView* subview in subviews) {
99     if ([subview isKindOfClass:[NSButton class]]) {
100       EXPECT_FALSE(hasButton);
101       hasButton = YES;
102
103       NSButton* button = static_cast<NSButton*>(subview);
104       EXPECT_EQ(@selector(newProfile:), [button action]);
105       EXPECT_EQ(controller(), [button target]);
106       EXPECT_TRUE([[button cell] isKindOfClass:[HyperlinkButtonCell class]]);
107     } else if ([subview isKindOfClass:[NSBox class]]) {
108       EXPECT_FALSE(hasSeparator);
109       hasSeparator = YES;
110     } else {
111       EXPECT_FALSE(subview) << "Unexpected subview: "
112                             << [[subview description] UTF8String];
113     }
114   }
115
116   [controller() close];
117 }
118
119 TEST_F(AvatarMenuBubbleControllerTest, PerformLayout) {
120   [controller() showWindow:nil];
121
122   NSView* contents = [[controller() window] contentView];
123   EXPECT_EQ(4U, [[contents subviews] count]);
124
125   base::scoped_nsobject<NSMutableArray> oldItems([[controller() items] copy]);
126
127   // Now create a new profile and notify the delegate.
128   manager()->CreateTestingProfile("test3", scoped_ptr<PrefServiceSyncable>(),
129                                   base::ASCIIToUTF16("Test 3"), 0,
130                                   std::string(),
131                                   TestingProfile::TestingFactories());
132
133   // Testing the bridge is not worth the effort...
134   [controller() performLayout];
135
136   EXPECT_EQ(5U, [[contents subviews] count]);
137
138   // Make sure that none of the old items exit.
139   NSArray* newItems = [controller() items];
140   for (AvatarMenuItemController* oldVC in oldItems.get()) {
141     EXPECT_FALSE([newItems containsObject:oldVC]);
142     EXPECT_FALSE([[contents subviews] containsObject:[oldVC view]]);
143   }
144
145   [controller() close];
146 }
147
148 // This subclass is used to inject a delegate into the hide/show edit link
149 // animation.
150 @interface TestingAvatarMenuItemController : AvatarMenuItemController
151                                                  <NSAnimationDelegate> {
152  @private
153   scoped_ptr<base::MessagePumpNSRunLoop> pump_;
154 }
155 // After calling |-highlightForEventType:| an animation will possibly be
156 // started. Since the animation is non-blocking, the run loop will need to be
157 // spun (via the MessagePump) until the animation has finished.
158 - (void)runMessagePump;
159 @end
160
161 @implementation TestingAvatarMenuItemController
162 - (void)runMessagePump {
163   if (!pump_)
164     pump_.reset(new base::MessagePumpNSRunLoop);
165   pump_->Run(NULL);
166 }
167
168 - (void)willStartAnimation:(NSAnimation*)anim {
169   [anim setDelegate:self];
170 }
171
172 - (void)animationDidEnd:(NSAnimation*)anim {
173   [super animationDidEnd:anim];
174   pump_->Quit();
175 }
176
177 - (void)animationDidStop:(NSAnimation*)anim {
178   [super animationDidStop:anim];
179   FAIL() << "Animation stopped before it completed its run";
180   pump_->Quit();
181 }
182
183 - (void)sendHighlightMessageForMouseExited {
184   [self highlightForEventType:NSMouseExited];
185   // Quit the pump because the animation was cancelled before it even ran.
186   pump_->Quit();
187 }
188 @end
189
190 TEST_F(AvatarMenuBubbleControllerTest, HighlightForEventType) {
191   base::scoped_nsobject<TestingAvatarMenuItemController> item(
192       [[TestingAvatarMenuItemController alloc] initWithMenuIndex:0
193                                                    menuController:nil]);
194   // Test non-active states first.
195   [[item activeView] setHidden:YES];
196
197   NSView* editButton = [item editButton];
198   NSView* emailField = [item emailField];
199
200   // The edit link remains hidden.
201   [item setIsHighlighted:YES];
202   EXPECT_TRUE(editButton.isHidden);
203   EXPECT_FALSE(emailField.isHidden);
204
205   [item setIsHighlighted:NO];
206   EXPECT_TRUE(editButton.isHidden);
207   EXPECT_FALSE(emailField.isHidden);
208
209   // Make the item "active" and re-test.
210   [[item activeView] setHidden:NO];
211
212   [item setIsHighlighted:YES];
213   [item runMessagePump];
214
215   EXPECT_FALSE(editButton.isHidden);
216   EXPECT_TRUE(emailField.isHidden);
217
218   [item setIsHighlighted:NO];
219   [item runMessagePump];
220
221   EXPECT_TRUE(editButton.isHidden);
222   EXPECT_FALSE(emailField.isHidden);
223
224   // Now mouse over and out quickly, as if scrubbing through the menu, to test
225   // the hover dwell delay.
226   [item highlightForEventType:NSMouseEntered];
227   [item performSelector:@selector(sendHighlightMessageForMouseExited)
228              withObject:nil
229              afterDelay:0];
230   [item runMessagePump];
231
232   EXPECT_TRUE(editButton.isHidden);
233   EXPECT_FALSE(emailField.isHidden);
234 }
235
236 TEST_F(AvatarMenuBubbleControllerTest, DownArrow) {
237   EXPECT_NSEQ(nil, GetHighlightedItem());
238
239   NSEvent* event =
240       cocoa_test_event_utils::KeyEventWithCharacter(NSDownArrowFunctionKey);
241   // Going down with no item selected should start the selection at the first
242   // item.
243   [controller() keyDown:event];
244   EXPECT_EQ([[controller() items] objectAtIndex:1], GetHighlightedItem());
245
246   [controller() keyDown:event];
247   EXPECT_EQ([[controller() items] objectAtIndex:0], GetHighlightedItem());
248
249   // There are no more items now so going down should stay at the last item.
250   [controller() keyDown:event];
251   EXPECT_EQ([[controller() items] objectAtIndex:0], GetHighlightedItem());
252 }
253
254 TEST_F(AvatarMenuBubbleControllerTest, UpArrow) {
255   EXPECT_NSEQ(nil, GetHighlightedItem());
256
257   NSEvent* event =
258       cocoa_test_event_utils::KeyEventWithCharacter(NSUpArrowFunctionKey);
259   // Going up with no item selected should start the selection at the last
260   // item.
261   [controller() keyDown:event];
262   EXPECT_EQ([[controller() items] objectAtIndex:0], GetHighlightedItem());
263
264   [controller() keyDown:event];
265   EXPECT_EQ([[controller() items] objectAtIndex:1], GetHighlightedItem());
266
267   // There are no more items now so going up should stay at the first item.
268   [controller() keyDown:event];
269   EXPECT_EQ([[controller() items] objectAtIndex:1], GetHighlightedItem());
270 }