// 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> #include "base/mac/scoped_nsobject.h" #include "base/strings/utf_string_conversions.h" #import "chrome/browser/ui/cocoa/cocoa_test_helper.h" #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field.h" #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_cell.h" #import "chrome/browser/ui/cocoa/location_bar/ev_bubble_decoration.h" #import "chrome/browser/ui/cocoa/location_bar/keyword_hint_decoration.h" #import "chrome/browser/ui/cocoa/location_bar/location_bar_decoration.h" #import "chrome/browser/ui/cocoa/location_bar/location_icon_decoration.h" #import "chrome/browser/ui/cocoa/location_bar/selected_keyword_decoration.h" #import "chrome/browser/ui/cocoa/location_bar/star_decoration.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #import "testing/gtest_mac.h" #include "testing/platform_test.h" #import "third_party/ocmock/OCMock/OCMock.h" #include "third_party/ocmock/gtest_support.h" using ::testing::Return; using ::testing::StrictMock; using ::testing::_; namespace { // Width of the field so that we don't have to ask |field_| for it all // the time. const CGFloat kWidth(300.0); class MockDecoration : public LocationBarDecoration { public: virtual CGFloat GetWidthForSpace(CGFloat width) { return 20.0; } MOCK_METHOD2(DrawInFrame, void(NSRect frame, NSView* control_view)); MOCK_METHOD0(GetToolTip, NSString*()); }; class AutocompleteTextFieldCellTest : public CocoaTest { public: AutocompleteTextFieldCellTest() { // Make sure this is wide enough to play games with the cell // decorations. const NSRect frame = NSMakeRect(0, 0, kWidth, 30); base::scoped_nsobject<NSTextField> view( [[NSTextField alloc] initWithFrame:frame]); view_ = view.get(); base::scoped_nsobject<AutocompleteTextFieldCell> cell( [[AutocompleteTextFieldCell alloc] initTextCell:@"Testing"]); [cell setEditable:YES]; [cell setBordered:YES]; [cell clearDecorations]; mock_left_decoration_.SetVisible(false); [cell addLeftDecoration:&mock_left_decoration_]; mock_right_decoration0_.SetVisible(false); mock_right_decoration1_.SetVisible(false); [cell addRightDecoration:&mock_right_decoration0_]; [cell addRightDecoration:&mock_right_decoration1_]; [view_ setCell:cell.get()]; [[test_window() contentView] addSubview:view_]; } NSTextField* view_; MockDecoration mock_left_decoration_; MockDecoration mock_right_decoration0_; MockDecoration mock_right_decoration1_; }; // Basic view tests (AddRemove, Display). TEST_VIEW(AutocompleteTextFieldCellTest, view_); // Test drawing, mostly to ensure nothing leaks or crashes. // Flaky, disabled. Bug http://crbug.com/49522 TEST_F(AutocompleteTextFieldCellTest, DISABLED_FocusedDisplay) { [view_ display]; // Test focused drawing. [test_window() makePretendKeyWindowAndSetFirstResponder:view_]; [view_ display]; [test_window() clearPretendKeyWindowAndFirstResponder]; // Test display of various cell configurations. AutocompleteTextFieldCell* cell = static_cast<AutocompleteTextFieldCell*>([view_ cell]); // Load available decorations and try drawing. To make sure that // they are actually drawn, check that |GetWidthForSpace()| doesn't // indicate that they should be omitted. const CGFloat kVeryWide = 1000.0; SelectedKeywordDecoration selected_keyword_decoration; selected_keyword_decoration.SetVisible(true); selected_keyword_decoration.SetKeyword(base::ASCIIToUTF16("Google"), false); [cell addLeftDecoration:&selected_keyword_decoration]; EXPECT_NE(selected_keyword_decoration.GetWidthForSpace(kVeryWide), LocationBarDecoration::kOmittedWidth); // TODO(shess): This really wants a |LocationBarViewMac|, but only a // few methods reference it, so this works well enough. But // something better would be nice. LocationIconDecoration location_icon_decoration(NULL); location_icon_decoration.SetVisible(true); location_icon_decoration.SetImage([NSImage imageNamed:@"NSApplicationIcon"]); [cell addLeftDecoration:&location_icon_decoration]; EXPECT_NE(location_icon_decoration.GetWidthForSpace(kVeryWide), LocationBarDecoration::kOmittedWidth); EVBubbleDecoration ev_bubble_decoration(&location_icon_decoration); ev_bubble_decoration.SetVisible(true); ev_bubble_decoration.SetImage([NSImage imageNamed:@"NSApplicationIcon"]); ev_bubble_decoration.SetLabel(@"Application"); [cell addLeftDecoration:&ev_bubble_decoration]; EXPECT_NE(ev_bubble_decoration.GetWidthForSpace(kVeryWide), LocationBarDecoration::kOmittedWidth); StarDecoration star_decoration(NULL); star_decoration.SetVisible(true); [cell addRightDecoration:&star_decoration]; EXPECT_NE(star_decoration.GetWidthForSpace(kVeryWide), LocationBarDecoration::kOmittedWidth); KeywordHintDecoration keyword_hint_decoration; keyword_hint_decoration.SetVisible(true); keyword_hint_decoration.SetKeyword(base::ASCIIToUTF16("google"), false); [cell addRightDecoration:&keyword_hint_decoration]; EXPECT_NE(keyword_hint_decoration.GetWidthForSpace(kVeryWide), LocationBarDecoration::kOmittedWidth); // Make sure we're actually calling |DrawInFrame()|. StrictMock<MockDecoration> mock_decoration; mock_decoration.SetVisible(true); [cell addLeftDecoration:&mock_decoration]; EXPECT_CALL(mock_decoration, DrawInFrame(_, _)); EXPECT_NE(mock_decoration.GetWidthForSpace(kVeryWide), LocationBarDecoration::kOmittedWidth); [view_ display]; [cell clearDecorations]; } TEST_F(AutocompleteTextFieldCellTest, TextFrame) { AutocompleteTextFieldCell* cell = static_cast<AutocompleteTextFieldCell*>([view_ cell]); const NSRect bounds([view_ bounds]); NSRect textFrame; // The cursor frame should stay the same throughout. const NSRect cursorFrame([cell textCursorFrameForFrame:bounds]); EXPECT_NSEQ(cursorFrame, bounds); // At default settings, everything goes to the text area. textFrame = [cell textFrameForFrame:bounds]; EXPECT_FALSE(NSIsEmptyRect(textFrame)); EXPECT_TRUE(NSContainsRect(bounds, textFrame)); EXPECT_EQ(NSMinX(bounds), NSMinX(textFrame)); EXPECT_EQ(NSMaxX(bounds), NSMaxX(textFrame)); EXPECT_TRUE(NSContainsRect(cursorFrame, textFrame)); // Decoration on the left takes up space. mock_left_decoration_.SetVisible(true); textFrame = [cell textFrameForFrame:bounds]; EXPECT_FALSE(NSIsEmptyRect(textFrame)); EXPECT_TRUE(NSContainsRect(bounds, textFrame)); EXPECT_GT(NSMinX(textFrame), NSMinX(bounds)); EXPECT_TRUE(NSContainsRect(cursorFrame, textFrame)); } // The editor frame should be slightly inset from the text frame. TEST_F(AutocompleteTextFieldCellTest, DrawingRectForBounds) { AutocompleteTextFieldCell* cell = static_cast<AutocompleteTextFieldCell*>([view_ cell]); const NSRect bounds([view_ bounds]); NSRect textFrame, drawingRect; textFrame = [cell textFrameForFrame:bounds]; drawingRect = [cell drawingRectForBounds:bounds]; EXPECT_FALSE(NSIsEmptyRect(drawingRect)); EXPECT_TRUE(NSContainsRect(textFrame, NSInsetRect(drawingRect, 1, 1))); // Save the starting frame for after clear. const NSRect originalDrawingRect = drawingRect; mock_left_decoration_.SetVisible(true); textFrame = [cell textFrameForFrame:bounds]; drawingRect = [cell drawingRectForBounds:bounds]; EXPECT_FALSE(NSIsEmptyRect(drawingRect)); EXPECT_TRUE(NSContainsRect(NSInsetRect(textFrame, 1, 1), drawingRect)); mock_right_decoration0_.SetVisible(true); textFrame = [cell textFrameForFrame:bounds]; drawingRect = [cell drawingRectForBounds:bounds]; EXPECT_FALSE(NSIsEmptyRect(drawingRect)); EXPECT_TRUE(NSContainsRect(NSInsetRect(textFrame, 1, 1), drawingRect)); mock_left_decoration_.SetVisible(false); mock_right_decoration0_.SetVisible(false); drawingRect = [cell drawingRectForBounds:bounds]; EXPECT_FALSE(NSIsEmptyRect(drawingRect)); EXPECT_NSEQ(drawingRect, originalDrawingRect); } // Test that left decorations are at the correct edge of the cell. TEST_F(AutocompleteTextFieldCellTest, LeftDecorationFrame) { AutocompleteTextFieldCell* cell = static_cast<AutocompleteTextFieldCell*>([view_ cell]); const NSRect bounds = [view_ bounds]; mock_left_decoration_.SetVisible(true); const NSRect decorationRect = [cell frameForDecoration:&mock_left_decoration_ inFrame:bounds]; EXPECT_FALSE(NSIsEmptyRect(decorationRect)); EXPECT_TRUE(NSContainsRect(bounds, decorationRect)); // Decoration should be left of |drawingRect|. const NSRect drawingRect = [cell drawingRectForBounds:bounds]; EXPECT_GT(NSMinX(drawingRect), NSMinX(decorationRect)); // Decoration should be left of |textFrame|. const NSRect textFrame = [cell textFrameForFrame:bounds]; EXPECT_GT(NSMinX(textFrame), NSMinX(decorationRect)); } // Test that right decorations are at the correct edge of the cell. TEST_F(AutocompleteTextFieldCellTest, RightDecorationFrame) { AutocompleteTextFieldCell* cell = static_cast<AutocompleteTextFieldCell*>([view_ cell]); const NSRect bounds = [view_ bounds]; mock_right_decoration0_.SetVisible(true); mock_right_decoration1_.SetVisible(true); const NSRect decoration0Rect = [cell frameForDecoration:&mock_right_decoration0_ inFrame:bounds]; EXPECT_FALSE(NSIsEmptyRect(decoration0Rect)); EXPECT_TRUE(NSContainsRect(bounds, decoration0Rect)); // Right-side decorations are ordered from rightmost to leftmost. // Outer decoration (0) to right of inner decoration (1). const NSRect decoration1Rect = [cell frameForDecoration:&mock_right_decoration1_ inFrame:bounds]; EXPECT_FALSE(NSIsEmptyRect(decoration1Rect)); EXPECT_TRUE(NSContainsRect(bounds, decoration1Rect)); EXPECT_LT(NSMinX(decoration1Rect), NSMinX(decoration0Rect)); // Decoration should be right of |drawingRect|. const NSRect drawingRect = [cell drawingRectForBounds:bounds]; EXPECT_LT(NSMinX(drawingRect), NSMinX(decoration1Rect)); // Decoration should be right of |textFrame|. const NSRect textFrame = [cell textFrameForFrame:bounds]; EXPECT_LT(NSMinX(textFrame), NSMinX(decoration1Rect)); } // Verify -[AutocompleteTextFieldCell updateToolTipsInRect:ofView:]. TEST_F(AutocompleteTextFieldCellTest, UpdateToolTips) { NSString* tooltip = @"tooltip"; // Left decoration returns a tooltip, make sure it is called at // least once. mock_left_decoration_.SetVisible(true); EXPECT_CALL(mock_left_decoration_, GetToolTip()) .WillOnce(Return(tooltip)) .WillRepeatedly(Return(tooltip)); // Right decoration returns no tooltip, make sure it is called at // least once. mock_right_decoration0_.SetVisible(true); EXPECT_CALL(mock_right_decoration0_, GetToolTip()) .WillOnce(Return((NSString*)nil)) .WillRepeatedly(Return((NSString*)nil)); AutocompleteTextFieldCell* cell = static_cast<AutocompleteTextFieldCell*>([view_ cell]); const NSRect bounds = [view_ bounds]; const NSRect leftDecorationRect = [cell frameForDecoration:&mock_left_decoration_ inFrame:bounds]; // |controlView| gets the tooltip for the left decoration. id controlView = [OCMockObject mockForClass:[AutocompleteTextField class]]; [[controlView expect] addToolTip:tooltip forRect:leftDecorationRect]; [cell updateToolTipsInRect:bounds ofView:controlView]; EXPECT_OCMOCK_VERIFY(controlView); } } // namespace