toolbar_controller_unittest.mm revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
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 <Cocoa/Cocoa.h>
6
7#import "base/mac/scoped_nsobject.h"
8#include "base/prefs/pref_service.h"
9#include "chrome/app/chrome_command_ids.h"
10#include "chrome/browser/command_updater.h"
11#include "chrome/browser/ui/browser.h"
12#include "chrome/browser/ui/browser_command_controller.h"
13#include "chrome/browser/ui/browser_commands.h"
14#include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
15#import "chrome/browser/ui/cocoa/gradient_button_cell.h"
16#import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
17#import "chrome/browser/ui/cocoa/view_resizer_pong.h"
18#include "chrome/common/pref_names.h"
19#include "chrome/test/base/testing_profile.h"
20#include "testing/gtest/include/gtest/gtest.h"
21#include "testing/platform_test.h"
22
23// An NSView that fakes out hitTest:.
24@interface HitView : NSView {
25  id hitTestReturn_;
26}
27@end
28
29@implementation HitView
30
31- (void)setHitTestReturn:(id)rtn {
32  hitTestReturn_ = rtn;
33}
34
35- (NSView *)hitTest:(NSPoint)aPoint {
36  return hitTestReturn_;
37}
38
39@end
40
41
42namespace {
43
44class ToolbarControllerTest : public CocoaProfileTest {
45 public:
46
47  // Indexes that match the ordering returned by the private ToolbarController
48  // |-toolbarViews| method.
49  enum {
50    kBackIndex, kForwardIndex, kReloadIndex, kHomeIndex,
51    kWrenchIndex, kLocationIndex, kBrowserActionContainerViewIndex
52  };
53
54  virtual void SetUp() OVERRIDE {
55    CocoaProfileTest::SetUp();
56    ASSERT_TRUE(browser());
57
58    CommandUpdater* updater =
59        browser()->command_controller()->command_updater();
60    // The default state for the commands is true, set a couple to false to
61    // ensure they get picked up correct on initialization
62    updater->UpdateCommandEnabled(IDC_BACK, false);
63    updater->UpdateCommandEnabled(IDC_FORWARD, false);
64    resizeDelegate_.reset([[ViewResizerPong alloc] init]);
65    bar_.reset(
66        [[ToolbarController alloc]
67            initWithCommands:browser()->command_controller()->command_updater()
68                     profile:profile()
69                     browser:browser()
70              resizeDelegate:resizeDelegate_.get()]);
71    EXPECT_TRUE([bar_ view]);
72    NSView* parent = [test_window() contentView];
73    [parent addSubview:[bar_ view]];
74  }
75
76  virtual void TearDown() OVERRIDE {
77    bar_.reset();  // browser() must outlive the ToolbarController.
78    CocoaProfileTest::TearDown();
79  }
80
81  // Make sure the enabled state of the view is the same as the corresponding
82  // command in the updater. The views are in the declaration order of outlets.
83  void CompareState(CommandUpdater* updater, NSArray* views) {
84    EXPECT_EQ(updater->IsCommandEnabled(IDC_BACK),
85              [[views objectAtIndex:kBackIndex] isEnabled] ? true : false);
86    EXPECT_EQ(updater->IsCommandEnabled(IDC_FORWARD),
87              [[views objectAtIndex:kForwardIndex] isEnabled] ? true : false);
88    EXPECT_EQ(updater->IsCommandEnabled(IDC_RELOAD),
89              [[views objectAtIndex:kReloadIndex] isEnabled] ? true : false);
90    EXPECT_EQ(updater->IsCommandEnabled(IDC_HOME),
91              [[views objectAtIndex:kHomeIndex] isEnabled] ? true : false);
92  }
93
94  base::scoped_nsobject<ViewResizerPong> resizeDelegate_;
95  base::scoped_nsobject<ToolbarController> bar_;
96};
97
98TEST_VIEW(ToolbarControllerTest, [bar_ view])
99
100// Test the initial state that everything is sync'd up
101TEST_F(ToolbarControllerTest, InitialState) {
102  CommandUpdater* updater = browser()->command_controller()->command_updater();
103  CompareState(updater, [bar_ toolbarViews]);
104}
105
106// Make sure a "titlebar only" toolbar with location bar works.
107TEST_F(ToolbarControllerTest, TitlebarOnly) {
108  NSView* view = [bar_ view];
109
110  [bar_ setHasToolbar:NO hasLocationBar:YES];
111  EXPECT_NE(view, [bar_ view]);
112
113  // Simulate a popup going fullscreen and back by performing the reparenting
114  // that happens during fullscreen transitions
115  NSView* superview = [view superview];
116  [view removeFromSuperview];
117  [superview addSubview:view];
118
119  [bar_ setHasToolbar:YES hasLocationBar:YES];
120  EXPECT_EQ(view, [bar_ view]);
121
122  // Leave it off to make sure that's fine
123  [bar_ setHasToolbar:NO hasLocationBar:YES];
124}
125
126// Make sure it works in the completely undecorated case.
127TEST_F(ToolbarControllerTest, NoLocationBar) {
128  NSView* view = [bar_ view];
129
130  [bar_ setHasToolbar:NO hasLocationBar:NO];
131  EXPECT_NE(view, [bar_ view]);
132  EXPECT_TRUE([[bar_ view] isHidden]);
133
134  // Simulate a popup going fullscreen and back by performing the reparenting
135  // that happens during fullscreen transitions
136  NSView* superview = [view superview];
137  [view removeFromSuperview];
138  [superview addSubview:view];
139}
140
141// Make some changes to the enabled state of a few of the buttons and ensure
142// that we're still in sync.
143TEST_F(ToolbarControllerTest, UpdateEnabledState) {
144  EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_BACK));
145  EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_FORWARD));
146  chrome::UpdateCommandEnabled(browser(), IDC_BACK, true);
147  chrome::UpdateCommandEnabled(browser(), IDC_FORWARD, true);
148  CommandUpdater* updater = browser()->command_controller()->command_updater();
149  CompareState(updater, [bar_ toolbarViews]);
150}
151
152// Focus the location bar and make sure that it's the first responder.
153TEST_F(ToolbarControllerTest, FocusLocation) {
154  NSWindow* window = test_window();
155  [window makeFirstResponder:[window contentView]];
156  EXPECT_EQ([window firstResponder], [window contentView]);
157  [bar_ focusLocationBar:YES];
158  EXPECT_NE([window firstResponder], [window contentView]);
159  NSView* locationBar = [[bar_ toolbarViews] objectAtIndex:kLocationIndex];
160  EXPECT_EQ([window firstResponder], [(id)locationBar currentEditor]);
161}
162
163TEST_F(ToolbarControllerTest, LoadingState) {
164  // In its initial state, the reload button has a tag of
165  // IDC_RELOAD. When loading, it should be IDC_STOP.
166  NSButton* reload = [[bar_ toolbarViews] objectAtIndex:kReloadIndex];
167  EXPECT_EQ([reload tag], IDC_RELOAD);
168  [bar_ setIsLoading:YES force:YES];
169  EXPECT_EQ([reload tag], IDC_STOP);
170  [bar_ setIsLoading:NO force:YES];
171  EXPECT_EQ([reload tag], IDC_RELOAD);
172}
173
174// Check that toggling the state of the home button changes the visible
175// state of the home button and moves the other items accordingly.
176TEST_F(ToolbarControllerTest, ToggleHome) {
177  PrefService* prefs = profile()->GetPrefs();
178  bool showHome = prefs->GetBoolean(prefs::kShowHomeButton);
179  NSView* homeButton = [[bar_ toolbarViews] objectAtIndex:kHomeIndex];
180  EXPECT_EQ(showHome, ![homeButton isHidden]);
181
182  NSView* locationBar = [[bar_ toolbarViews] objectAtIndex:kLocationIndex];
183  NSRect originalLocationBarFrame = [locationBar frame];
184
185  // Toggle the pref and make sure the button changed state and the other
186  // views moved.
187  prefs->SetBoolean(prefs::kShowHomeButton, !showHome);
188  EXPECT_EQ(showHome, [homeButton isHidden]);
189  EXPECT_NE(NSMinX(originalLocationBarFrame), NSMinX([locationBar frame]));
190  EXPECT_NE(NSWidth(originalLocationBarFrame), NSWidth([locationBar frame]));
191}
192
193// Ensure that we don't toggle the buttons when we have a strip marked as not
194// having the full toolbar. Also ensure that the location bar doesn't change
195// size.
196TEST_F(ToolbarControllerTest, DontToggleWhenNoToolbar) {
197  [bar_ setHasToolbar:NO hasLocationBar:YES];
198  NSView* homeButton = [[bar_ toolbarViews] objectAtIndex:kHomeIndex];
199  NSView* locationBar = [[bar_ toolbarViews] objectAtIndex:kLocationIndex];
200  NSRect locationBarFrame = [locationBar frame];
201  EXPECT_EQ([homeButton isHidden], YES);
202  [bar_ showOptionalHomeButton];
203  EXPECT_EQ([homeButton isHidden], YES);
204  NSRect newLocationBarFrame = [locationBar frame];
205  EXPECT_TRUE(NSEqualRects(locationBarFrame, newLocationBarFrame));
206  newLocationBarFrame = [locationBar frame];
207  EXPECT_TRUE(NSEqualRects(locationBarFrame, newLocationBarFrame));
208}
209
210TEST_F(ToolbarControllerTest, BookmarkBubblePoint) {
211  const NSPoint starPoint = [bar_ bookmarkBubblePoint];
212  const NSRect barFrame =
213      [[bar_ view] convertRect:[[bar_ view] bounds] toView:nil];
214
215  // Make sure the star is completely inside the location bar.
216  EXPECT_TRUE(NSPointInRect(starPoint, barFrame));
217}
218
219TEST_F(ToolbarControllerTest, HoverButtonForEvent) {
220  base::scoped_nsobject<HitView> view(
221      [[HitView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]);
222  [bar_ setView:view];
223  NSEvent* event = [NSEvent mouseEventWithType:NSMouseMoved
224                                      location:NSMakePoint(10,10)
225                                 modifierFlags:0
226                                     timestamp:0
227                                  windowNumber:0
228                                       context:nil
229                                   eventNumber:0
230                                    clickCount:0
231                                      pressure:0.0];
232
233  // NOT a match.
234  [view setHitTestReturn:bar_.get()];
235  EXPECT_FALSE([bar_ hoverButtonForEvent:event]);
236
237  // Not yet...
238  base::scoped_nsobject<NSButton> button([[NSButton alloc] init]);
239  [view setHitTestReturn:button];
240  EXPECT_FALSE([bar_ hoverButtonForEvent:event]);
241
242  // Now!
243  base::scoped_nsobject<GradientButtonCell> cell(
244      [[GradientButtonCell alloc] init]);
245  [button setCell:cell.get()];
246  EXPECT_TRUE([bar_ hoverButtonForEvent:nil]);
247}
248
249}  // namespace
250