// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #import <Cocoa/Cocoa.h> #import "base/memory/scoped_nsobject.h" #include "chrome/app/chrome_command_ids.h" #include "chrome/browser/ui/cocoa/browser_test_helper.h" #import "chrome/browser/ui/cocoa/cocoa_test_helper.h" #import "chrome/browser/ui/cocoa/gradient_button_cell.h" #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h" #import "chrome/browser/ui/cocoa/view_resizer_pong.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/common/pref_names.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" // An NSView that fakes out hitTest:. @interface HitView : NSView { id hitTestReturn_; } @end @implementation HitView - (void)setHitTestReturn:(id)rtn { hitTestReturn_ = rtn; } - (NSView *)hitTest:(NSPoint)aPoint { return hitTestReturn_; } @end namespace { class ToolbarControllerTest : public CocoaTest { public: // Indexes that match the ordering returned by the private ToolbarController // |-toolbarViews| method. enum { kBackIndex, kForwardIndex, kReloadIndex, kHomeIndex, kWrenchIndex, kLocationIndex, kBrowserActionContainerViewIndex }; ToolbarControllerTest() { Browser* browser = helper_.browser(); CommandUpdater* updater = browser->command_updater(); // The default state for the commands is true, set a couple to false to // ensure they get picked up correct on initialization updater->UpdateCommandEnabled(IDC_BACK, false); updater->UpdateCommandEnabled(IDC_FORWARD, false); resizeDelegate_.reset([[ViewResizerPong alloc] init]); bar_.reset( [[ToolbarController alloc] initWithModel:browser->toolbar_model() commands:browser->command_updater() profile:helper_.profile() browser:browser resizeDelegate:resizeDelegate_.get()]); EXPECT_TRUE([bar_ view]); NSView* parent = [test_window() contentView]; [parent addSubview:[bar_ view]]; } // Make sure the enabled state of the view is the same as the corresponding // command in the updater. The views are in the declaration order of outlets. void CompareState(CommandUpdater* updater, NSArray* views) { EXPECT_EQ(updater->IsCommandEnabled(IDC_BACK), [[views objectAtIndex:kBackIndex] isEnabled] ? true : false); EXPECT_EQ(updater->IsCommandEnabled(IDC_FORWARD), [[views objectAtIndex:kForwardIndex] isEnabled] ? true : false); EXPECT_EQ(updater->IsCommandEnabled(IDC_RELOAD), [[views objectAtIndex:kReloadIndex] isEnabled] ? true : false); EXPECT_EQ(updater->IsCommandEnabled(IDC_HOME), [[views objectAtIndex:kHomeIndex] isEnabled] ? true : false); } BrowserTestHelper helper_; scoped_nsobject<ViewResizerPong> resizeDelegate_; scoped_nsobject<ToolbarController> bar_; }; TEST_VIEW(ToolbarControllerTest, [bar_ view]) // Test the initial state that everything is sync'd up TEST_F(ToolbarControllerTest, InitialState) { CommandUpdater* updater = helper_.browser()->command_updater(); CompareState(updater, [bar_ toolbarViews]); } // Make sure a "titlebar only" toolbar with location bar works. TEST_F(ToolbarControllerTest, TitlebarOnly) { NSView* view = [bar_ view]; [bar_ setHasToolbar:NO hasLocationBar:YES]; EXPECT_NE(view, [bar_ view]); // Simulate a popup going fullscreen and back by performing the reparenting // that happens during fullscreen transitions NSView* superview = [view superview]; [view removeFromSuperview]; [superview addSubview:view]; [bar_ setHasToolbar:YES hasLocationBar:YES]; EXPECT_EQ(view, [bar_ view]); // Leave it off to make sure that's fine [bar_ setHasToolbar:NO hasLocationBar:YES]; } // Make sure it works in the completely undecorated case. TEST_F(ToolbarControllerTest, NoLocationBar) { NSView* view = [bar_ view]; [bar_ setHasToolbar:NO hasLocationBar:NO]; EXPECT_NE(view, [bar_ view]); EXPECT_TRUE([[bar_ view] isHidden]); // Simulate a popup going fullscreen and back by performing the reparenting // that happens during fullscreen transitions NSView* superview = [view superview]; [view removeFromSuperview]; [superview addSubview:view]; } // Make some changes to the enabled state of a few of the buttons and ensure // that we're still in sync. TEST_F(ToolbarControllerTest, UpdateEnabledState) { CommandUpdater* updater = helper_.browser()->command_updater(); EXPECT_FALSE(updater->IsCommandEnabled(IDC_BACK)); EXPECT_FALSE(updater->IsCommandEnabled(IDC_FORWARD)); updater->UpdateCommandEnabled(IDC_BACK, true); updater->UpdateCommandEnabled(IDC_FORWARD, true); CompareState(updater, [bar_ toolbarViews]); } // Focus the location bar and make sure that it's the first responder. TEST_F(ToolbarControllerTest, FocusLocation) { NSWindow* window = test_window(); [window makeFirstResponder:[window contentView]]; EXPECT_EQ([window firstResponder], [window contentView]); [bar_ focusLocationBar:YES]; EXPECT_NE([window firstResponder], [window contentView]); NSView* locationBar = [[bar_ toolbarViews] objectAtIndex:kLocationIndex]; EXPECT_EQ([window firstResponder], [(id)locationBar currentEditor]); } TEST_F(ToolbarControllerTest, LoadingState) { // In its initial state, the reload button has a tag of // IDC_RELOAD. When loading, it should be IDC_STOP. NSButton* reload = [[bar_ toolbarViews] objectAtIndex:kReloadIndex]; EXPECT_EQ([reload tag], IDC_RELOAD); [bar_ setIsLoading:YES force:YES]; EXPECT_EQ([reload tag], IDC_STOP); [bar_ setIsLoading:NO force:YES]; EXPECT_EQ([reload tag], IDC_RELOAD); } // Check that toggling the state of the home button changes the visible // state of the home button and moves the other items accordingly. TEST_F(ToolbarControllerTest, ToggleHome) { PrefService* prefs = helper_.profile()->GetPrefs(); bool showHome = prefs->GetBoolean(prefs::kShowHomeButton); NSView* homeButton = [[bar_ toolbarViews] objectAtIndex:kHomeIndex]; EXPECT_EQ(showHome, ![homeButton isHidden]); NSView* locationBar = [[bar_ toolbarViews] objectAtIndex:kLocationIndex]; NSRect originalLocationBarFrame = [locationBar frame]; // Toggle the pref and make sure the button changed state and the other // views moved. prefs->SetBoolean(prefs::kShowHomeButton, !showHome); EXPECT_EQ(showHome, [homeButton isHidden]); EXPECT_NE(NSMinX(originalLocationBarFrame), NSMinX([locationBar frame])); EXPECT_NE(NSWidth(originalLocationBarFrame), NSWidth([locationBar frame])); } // Ensure that we don't toggle the buttons when we have a strip marked as not // having the full toolbar. Also ensure that the location bar doesn't change // size. TEST_F(ToolbarControllerTest, DontToggleWhenNoToolbar) { [bar_ setHasToolbar:NO hasLocationBar:YES]; NSView* homeButton = [[bar_ toolbarViews] objectAtIndex:kHomeIndex]; NSView* locationBar = [[bar_ toolbarViews] objectAtIndex:kLocationIndex]; NSRect locationBarFrame = [locationBar frame]; EXPECT_EQ([homeButton isHidden], YES); [bar_ showOptionalHomeButton]; EXPECT_EQ([homeButton isHidden], YES); NSRect newLocationBarFrame = [locationBar frame]; EXPECT_TRUE(NSEqualRects(locationBarFrame, newLocationBarFrame)); newLocationBarFrame = [locationBar frame]; EXPECT_TRUE(NSEqualRects(locationBarFrame, newLocationBarFrame)); } TEST_F(ToolbarControllerTest, BookmarkBubblePoint) { const NSPoint starPoint = [bar_ bookmarkBubblePoint]; const NSRect barFrame = [[bar_ view] convertRect:[[bar_ view] bounds] toView:nil]; // Make sure the star is completely inside the location bar. EXPECT_TRUE(NSPointInRect(starPoint, barFrame)); } TEST_F(ToolbarControllerTest, HoverButtonForEvent) { scoped_nsobject<HitView> view([[HitView alloc] initWithFrame:NSMakeRect(0,0,100,100)]); [bar_ setView:view]; NSEvent* event = [NSEvent mouseEventWithType:NSMouseMoved location:NSMakePoint(10,10) modifierFlags:0 timestamp:0 windowNumber:0 context:nil eventNumber:0 clickCount:0 pressure:0.0]; // NOT a match. [view setHitTestReturn:bar_.get()]; EXPECT_FALSE([bar_ hoverButtonForEvent:event]); // Not yet... scoped_nsobject<NSButton> button([[NSButton alloc] init]); [view setHitTestReturn:button]; EXPECT_FALSE([bar_ hoverButtonForEvent:event]); // Now! scoped_nsobject<GradientButtonCell> cell([[GradientButtonCell alloc] init]); [button setCell:cell.get()]; EXPECT_TRUE([bar_ hoverButtonForEvent:nil]); } } // namespace