1 // Copyright (c) 2011 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 #include "base/memory/scoped_nsobject.h" 8 #include "base/utf_string_conversions.h" 9 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h" 10 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field.h" 11 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_cell.h" 12 #import "chrome/browser/ui/cocoa/location_bar/ev_bubble_decoration.h" 13 #import "chrome/browser/ui/cocoa/location_bar/keyword_hint_decoration.h" 14 #import "chrome/browser/ui/cocoa/location_bar/location_bar_decoration.h" 15 #import "chrome/browser/ui/cocoa/location_bar/location_icon_decoration.h" 16 #import "chrome/browser/ui/cocoa/location_bar/selected_keyword_decoration.h" 17 #import "chrome/browser/ui/cocoa/location_bar/star_decoration.h" 18 #include "grit/theme_resources.h" 19 #include "testing/gmock/include/gmock/gmock.h" 20 #include "testing/gtest/include/gtest/gtest.h" 21 #include "testing/platform_test.h" 22 #import "third_party/ocmock/OCMock/OCMock.h" 23 #include "ui/base/resource/resource_bundle.h" 24 25 using ::testing::Return; 26 using ::testing::StrictMock; 27 using ::testing::_; 28 29 namespace { 30 31 // Width of the field so that we don't have to ask |field_| for it all 32 // the time. 33 const CGFloat kWidth(300.0); 34 35 // A narrow width for tests which test things that don't fit. 36 const CGFloat kNarrowWidth(5.0); 37 38 class MockDecoration : public LocationBarDecoration { 39 public: 40 virtual CGFloat GetWidthForSpace(CGFloat width) { return 20.0; } 41 42 MOCK_METHOD2(DrawInFrame, void(NSRect frame, NSView* control_view)); 43 MOCK_METHOD0(GetToolTip, NSString*()); 44 }; 45 46 class AutocompleteTextFieldCellTest : public CocoaTest { 47 public: 48 AutocompleteTextFieldCellTest() { 49 // Make sure this is wide enough to play games with the cell 50 // decorations. 51 const NSRect frame = NSMakeRect(0, 0, kWidth, 30); 52 53 scoped_nsobject<NSTextField> view( 54 [[NSTextField alloc] initWithFrame:frame]); 55 view_ = view.get(); 56 57 scoped_nsobject<AutocompleteTextFieldCell> cell( 58 [[AutocompleteTextFieldCell alloc] initTextCell:@"Testing"]); 59 [cell setEditable:YES]; 60 [cell setBordered:YES]; 61 62 [cell clearDecorations]; 63 mock_left_decoration_.SetVisible(false); 64 [cell addLeftDecoration:&mock_left_decoration_]; 65 mock_right_decoration0_.SetVisible(false); 66 mock_right_decoration1_.SetVisible(false); 67 [cell addRightDecoration:&mock_right_decoration0_]; 68 [cell addRightDecoration:&mock_right_decoration1_]; 69 70 [view_ setCell:cell.get()]; 71 72 [[test_window() contentView] addSubview:view_]; 73 } 74 75 NSTextField* view_; 76 MockDecoration mock_left_decoration_; 77 MockDecoration mock_right_decoration0_; 78 MockDecoration mock_right_decoration1_; 79 }; 80 81 // Basic view tests (AddRemove, Display). 82 TEST_VIEW(AutocompleteTextFieldCellTest, view_); 83 84 // Test drawing, mostly to ensure nothing leaks or crashes. 85 // Flaky, disabled. Bug http://crbug.com/49522 86 TEST_F(AutocompleteTextFieldCellTest, DISABLED_FocusedDisplay) { 87 [view_ display]; 88 89 // Test focused drawing. 90 [test_window() makePretendKeyWindowAndSetFirstResponder:view_]; 91 [view_ display]; 92 [test_window() clearPretendKeyWindowAndFirstResponder]; 93 94 // Test display of various cell configurations. 95 AutocompleteTextFieldCell* cell = 96 static_cast<AutocompleteTextFieldCell*>([view_ cell]); 97 98 // Load available decorations and try drawing. To make sure that 99 // they are actually drawn, check that |GetWidthForSpace()| doesn't 100 // indicate that they should be omitted. 101 const CGFloat kVeryWide = 1000.0; 102 103 SelectedKeywordDecoration selected_keyword_decoration([view_ font]); 104 selected_keyword_decoration.SetVisible(true); 105 selected_keyword_decoration.SetKeyword(ASCIIToUTF16("Google"), false); 106 [cell addLeftDecoration:&selected_keyword_decoration]; 107 EXPECT_NE(selected_keyword_decoration.GetWidthForSpace(kVeryWide), 108 LocationBarDecoration::kOmittedWidth); 109 110 // TODO(shess): This really wants a |LocationBarViewMac|, but only a 111 // few methods reference it, so this works well enough. But 112 // something better would be nice. 113 LocationIconDecoration location_icon_decoration(NULL); 114 location_icon_decoration.SetVisible(true); 115 location_icon_decoration.SetImage([NSImage imageNamed:@"NSApplicationIcon"]); 116 [cell addLeftDecoration:&location_icon_decoration]; 117 EXPECT_NE(location_icon_decoration.GetWidthForSpace(kVeryWide), 118 LocationBarDecoration::kOmittedWidth); 119 120 EVBubbleDecoration ev_bubble_decoration(&location_icon_decoration, 121 [view_ font]); 122 ev_bubble_decoration.SetVisible(true); 123 ev_bubble_decoration.SetImage([NSImage imageNamed:@"NSApplicationIcon"]); 124 ev_bubble_decoration.SetLabel(@"Application"); 125 [cell addLeftDecoration:&ev_bubble_decoration]; 126 EXPECT_NE(ev_bubble_decoration.GetWidthForSpace(kVeryWide), 127 LocationBarDecoration::kOmittedWidth); 128 129 StarDecoration star_decoration(NULL); 130 star_decoration.SetVisible(true); 131 [cell addRightDecoration:&star_decoration]; 132 EXPECT_NE(star_decoration.GetWidthForSpace(kVeryWide), 133 LocationBarDecoration::kOmittedWidth); 134 135 KeywordHintDecoration keyword_hint_decoration([view_ font]); 136 keyword_hint_decoration.SetVisible(true); 137 keyword_hint_decoration.SetKeyword(ASCIIToUTF16("google"), false); 138 [cell addRightDecoration:&keyword_hint_decoration]; 139 EXPECT_NE(keyword_hint_decoration.GetWidthForSpace(kVeryWide), 140 LocationBarDecoration::kOmittedWidth); 141 142 // Make sure we're actually calling |DrawInFrame()|. 143 StrictMock<MockDecoration> mock_decoration; 144 mock_decoration.SetVisible(true); 145 [cell addLeftDecoration:&mock_decoration]; 146 EXPECT_CALL(mock_decoration, DrawInFrame(_, _)); 147 EXPECT_NE(mock_decoration.GetWidthForSpace(kVeryWide), 148 LocationBarDecoration::kOmittedWidth); 149 150 [view_ display]; 151 152 [cell clearDecorations]; 153 } 154 155 TEST_F(AutocompleteTextFieldCellTest, TextFrame) { 156 AutocompleteTextFieldCell* cell = 157 static_cast<AutocompleteTextFieldCell*>([view_ cell]); 158 const NSRect bounds([view_ bounds]); 159 NSRect textFrame; 160 161 // The cursor frame should stay the same throughout. 162 const NSRect cursorFrame([cell textCursorFrameForFrame:bounds]); 163 EXPECT_TRUE(NSEqualRects(cursorFrame, bounds)); 164 165 // At default settings, everything goes to the text area. 166 textFrame = [cell textFrameForFrame:bounds]; 167 EXPECT_FALSE(NSIsEmptyRect(textFrame)); 168 EXPECT_TRUE(NSContainsRect(bounds, textFrame)); 169 EXPECT_EQ(NSMinX(bounds), NSMinX(textFrame)); 170 EXPECT_EQ(NSMaxX(bounds), NSMaxX(textFrame)); 171 EXPECT_TRUE(NSContainsRect(cursorFrame, textFrame)); 172 173 // Decoration on the left takes up space. 174 mock_left_decoration_.SetVisible(true); 175 textFrame = [cell textFrameForFrame:bounds]; 176 EXPECT_FALSE(NSIsEmptyRect(textFrame)); 177 EXPECT_TRUE(NSContainsRect(bounds, textFrame)); 178 EXPECT_GT(NSMinX(textFrame), NSMinX(bounds)); 179 EXPECT_TRUE(NSContainsRect(cursorFrame, textFrame)); 180 } 181 182 // The editor frame should be slightly inset from the text frame. 183 TEST_F(AutocompleteTextFieldCellTest, DrawingRectForBounds) { 184 AutocompleteTextFieldCell* cell = 185 static_cast<AutocompleteTextFieldCell*>([view_ cell]); 186 const NSRect bounds([view_ bounds]); 187 NSRect textFrame, drawingRect; 188 189 textFrame = [cell textFrameForFrame:bounds]; 190 drawingRect = [cell drawingRectForBounds:bounds]; 191 EXPECT_FALSE(NSIsEmptyRect(drawingRect)); 192 EXPECT_TRUE(NSContainsRect(textFrame, NSInsetRect(drawingRect, 1, 1))); 193 194 // Save the starting frame for after clear. 195 const NSRect originalDrawingRect = drawingRect; 196 197 mock_left_decoration_.SetVisible(true); 198 textFrame = [cell textFrameForFrame:bounds]; 199 drawingRect = [cell drawingRectForBounds:bounds]; 200 EXPECT_FALSE(NSIsEmptyRect(drawingRect)); 201 EXPECT_TRUE(NSContainsRect(NSInsetRect(textFrame, 1, 1), drawingRect)); 202 203 mock_right_decoration0_.SetVisible(true); 204 textFrame = [cell textFrameForFrame:bounds]; 205 drawingRect = [cell drawingRectForBounds:bounds]; 206 EXPECT_FALSE(NSIsEmptyRect(drawingRect)); 207 EXPECT_TRUE(NSContainsRect(NSInsetRect(textFrame, 1, 1), drawingRect)); 208 209 mock_left_decoration_.SetVisible(false); 210 mock_right_decoration0_.SetVisible(false); 211 drawingRect = [cell drawingRectForBounds:bounds]; 212 EXPECT_FALSE(NSIsEmptyRect(drawingRect)); 213 EXPECT_TRUE(NSEqualRects(drawingRect, originalDrawingRect)); 214 } 215 216 // Test that left decorations are at the correct edge of the cell. 217 TEST_F(AutocompleteTextFieldCellTest, LeftDecorationFrame) { 218 AutocompleteTextFieldCell* cell = 219 static_cast<AutocompleteTextFieldCell*>([view_ cell]); 220 const NSRect bounds = [view_ bounds]; 221 222 mock_left_decoration_.SetVisible(true); 223 const NSRect decorationRect = 224 [cell frameForDecoration:&mock_left_decoration_ inFrame:bounds]; 225 EXPECT_FALSE(NSIsEmptyRect(decorationRect)); 226 EXPECT_TRUE(NSContainsRect(bounds, decorationRect)); 227 228 // Decoration should be left of |drawingRect|. 229 const NSRect drawingRect = [cell drawingRectForBounds:bounds]; 230 EXPECT_GT(NSMinX(drawingRect), NSMinX(decorationRect)); 231 232 // Decoration should be left of |textFrame|. 233 const NSRect textFrame = [cell textFrameForFrame:bounds]; 234 EXPECT_GT(NSMinX(textFrame), NSMinX(decorationRect)); 235 } 236 237 // Test that right decorations are at the correct edge of the cell. 238 TEST_F(AutocompleteTextFieldCellTest, RightDecorationFrame) { 239 AutocompleteTextFieldCell* cell = 240 static_cast<AutocompleteTextFieldCell*>([view_ cell]); 241 const NSRect bounds = [view_ bounds]; 242 243 mock_right_decoration0_.SetVisible(true); 244 mock_right_decoration1_.SetVisible(true); 245 246 const NSRect decoration0Rect = 247 [cell frameForDecoration:&mock_right_decoration0_ inFrame:bounds]; 248 EXPECT_FALSE(NSIsEmptyRect(decoration0Rect)); 249 EXPECT_TRUE(NSContainsRect(bounds, decoration0Rect)); 250 251 // Right-side decorations are ordered from rightmost to leftmost. 252 // Outer decoration (0) to right of inner decoration (1). 253 const NSRect decoration1Rect = 254 [cell frameForDecoration:&mock_right_decoration1_ inFrame:bounds]; 255 EXPECT_FALSE(NSIsEmptyRect(decoration1Rect)); 256 EXPECT_TRUE(NSContainsRect(bounds, decoration1Rect)); 257 EXPECT_LT(NSMinX(decoration1Rect), NSMinX(decoration0Rect)); 258 259 // Decoration should be right of |drawingRect|. 260 const NSRect drawingRect = [cell drawingRectForBounds:bounds]; 261 EXPECT_LT(NSMinX(drawingRect), NSMinX(decoration1Rect)); 262 263 // Decoration should be right of |textFrame|. 264 const NSRect textFrame = [cell textFrameForFrame:bounds]; 265 EXPECT_LT(NSMinX(textFrame), NSMinX(decoration1Rect)); 266 } 267 268 // Verify -[AutocompleteTextFieldCell updateToolTipsInRect:ofView:]. 269 TEST_F(AutocompleteTextFieldCellTest, UpdateToolTips) { 270 NSString* tooltip = @"tooltip"; 271 272 // Left decoration returns a tooltip, make sure it is called at 273 // least once. 274 mock_left_decoration_.SetVisible(true); 275 EXPECT_CALL(mock_left_decoration_, GetToolTip()) 276 .WillOnce(Return(tooltip)) 277 .WillRepeatedly(Return(tooltip)); 278 279 // Right decoration returns no tooltip, make sure it is called at 280 // least once. 281 mock_right_decoration0_.SetVisible(true); 282 EXPECT_CALL(mock_right_decoration0_, GetToolTip()) 283 .WillOnce(Return((NSString*)nil)) 284 .WillRepeatedly(Return((NSString*)nil)); 285 286 AutocompleteTextFieldCell* cell = 287 static_cast<AutocompleteTextFieldCell*>([view_ cell]); 288 const NSRect bounds = [view_ bounds]; 289 const NSRect leftDecorationRect = 290 [cell frameForDecoration:&mock_left_decoration_ inFrame:bounds]; 291 292 // |controlView| gets the tooltip for the left decoration. 293 id controlView = [OCMockObject mockForClass:[AutocompleteTextField class]]; 294 [[controlView expect] addToolTip:tooltip forRect:leftDecorationRect]; 295 296 [cell updateToolTipsInRect:bounds ofView:controlView]; 297 298 [controlView verify]; 299 } 300 301 } // namespace 302