// Copyright (c) 2010 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 #include "base/scoped_nsobject.h" #import "chrome/browser/cocoa/autocomplete_text_field_cell.h" #import "chrome/browser/cocoa/cocoa_test_helper.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" namespace { // Width of the field so that we don't have to ask |field_| for it all // the time. const CGFloat kWidth(300.0); // A narrow width for tests which test things that don't fit. const CGFloat kNarrowWidth(5.0); // A testing subclass that doesn't bother hooking up the Page Action itself. class TestPageActionImageView : public LocationBarViewMac::PageActionImageView { public: TestPageActionImageView() {} ~TestPageActionImageView() {} }; class TestPageActionViewList : public LocationBarViewMac::PageActionViewList { public: TestPageActionViewList() : LocationBarViewMac::PageActionViewList(NULL, NULL, NULL) {} ~TestPageActionViewList() { // |~PageActionViewList()| calls delete on the contents of // |views_|, which here are refs to stack objects. views_.clear(); } void Add(LocationBarViewMac::PageActionImageView* view) { views_.push_back(view); } }; class AutocompleteTextFieldCellTest : public CocoaTest { public: AutocompleteTextFieldCellTest() : security_image_view_(NULL, NULL, NULL), page_action_views_() { // Make sure this is wide enough to play games with the cell // decorations. const NSRect frame = NSMakeRect(0, 0, kWidth, 30); scoped_nsobject view( [[NSTextField alloc] initWithFrame:frame]); view_ = view.get(); scoped_nsobject cell( [[AutocompleteTextFieldCell alloc] initTextCell:@"Testing"]); [cell setEditable:YES]; [cell setBordered:YES]; [cell setSecurityImageView:&security_image_view_]; [cell setPageActionViewList:&page_action_views_]; [view_ setCell:cell.get()]; [[test_window() contentView] addSubview:view_]; } NSTextField* view_; LocationBarViewMac::SecurityImageView security_image_view_; TestPageActionViewList page_action_views_; }; // Basic view tests (AddRemove, Display). TEST_VIEW(AutocompleteTextFieldCellTest, view_); // Test drawing, mostly to ensure nothing leaks or crashes. TEST_F(AutocompleteTextFieldCellTest, 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([view_ cell]); [cell setSearchHintString:@"Type to search" availableWidth:kWidth]; [view_ display]; NSImage* image = [NSImage imageNamed:@"NSApplicationIcon"]; [cell setKeywordHintPrefix:@"prefix" image:image suffix:@"suffix" availableWidth:kWidth]; [view_ display]; [cell setKeywordString:@"Search Engine:" partialString:@"Search Eng:" availableWidth:kWidth]; [view_ display]; } // Verify that transitions between states clear other states. TEST_F(AutocompleteTextFieldCellTest, StateTransitionsResetOthers) { AutocompleteTextFieldCell* cell = static_cast([view_ cell]); NSImage* image = [NSImage imageNamed:@"NSApplicationIcon"]; // Setting hint leaves keyword empty. [cell setSearchHintString:@"Type to search" availableWidth:kWidth]; EXPECT_TRUE([cell hintString]); EXPECT_FALSE([cell keywordString]); // Setting keyword clears hint. [cell setKeywordString:@"Search Engine:" partialString:@"Search Eng:" availableWidth:kWidth]; EXPECT_FALSE([cell hintString]); EXPECT_TRUE([cell keywordString]); // Setting hint clears keyword. [cell setKeywordHintPrefix:@"Press " image:image suffix:@" to search Engine" availableWidth:kWidth]; EXPECT_TRUE([cell hintString]); EXPECT_FALSE([cell keywordString]); // Clear clears keyword. [cell clearKeywordAndHint]; EXPECT_FALSE([cell hintString]); EXPECT_FALSE([cell keywordString]); // Clear clears hint. [cell setSearchHintString:@"Type to search" availableWidth:kWidth]; EXPECT_TRUE([cell hintString]); [cell clearKeywordAndHint]; EXPECT_FALSE([cell hintString]); EXPECT_FALSE([cell keywordString]); } TEST_F(AutocompleteTextFieldCellTest, TextFrame) { AutocompleteTextFieldCell* cell = static_cast([view_ cell]); const NSRect bounds([view_ bounds]); NSRect textFrame; // The cursor frame should stay the same throughout. const NSRect cursorFrame([cell textCursorFrameForFrame: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(NSEqualRects(cursorFrame, textFrame)); // Small search hint leaves text frame to left. [cell setSearchHintString:@"Search hint" availableWidth:kWidth]; textFrame = [cell textFrameForFrame:bounds]; EXPECT_FALSE(NSIsEmptyRect(textFrame)); EXPECT_TRUE(NSContainsRect(bounds, textFrame)); EXPECT_LT(NSMaxX(textFrame), NSMaxX(bounds)); EXPECT_TRUE(NSContainsRect(cursorFrame, textFrame)); // Save search-hint's frame for future reference. const CGFloat searchHintMaxX(NSMaxX(textFrame)); // Keyword hint also leaves text to left. Arrange for it to be // larger than the search hint just to make sure that things have // changed (keyword hint overrode search hint), and that they aren't // handled identically w/out reference to the actual button cell. NSImage* image = [NSImage imageNamed:@"NSApplicationIcon"]; [cell setKeywordHintPrefix:@"Keyword " image:image suffix:@" hint" availableWidth:kWidth]; textFrame = [cell textFrameForFrame:bounds]; EXPECT_FALSE(NSIsEmptyRect(textFrame)); EXPECT_TRUE(NSContainsRect(bounds, textFrame)); EXPECT_LT(NSMaxX(textFrame), NSMaxX(bounds)); EXPECT_LT(NSMaxX(textFrame), searchHintMaxX); EXPECT_TRUE(NSContainsRect(cursorFrame, textFrame)); // Keyword search leaves text area to right. [cell setKeywordString:@"Search Engine:" partialString:@"Search Eng:" availableWidth:kWidth]; textFrame = [cell textFrameForFrame:bounds]; EXPECT_FALSE(NSIsEmptyRect(textFrame)); EXPECT_TRUE(NSContainsRect(bounds, textFrame)); EXPECT_GT(NSMinX(textFrame), NSMinX(bounds)); EXPECT_LT(NSMinX(textFrame), searchHintMaxX); EXPECT_GT(NSMaxX(textFrame), searchHintMaxX); EXPECT_TRUE(NSContainsRect(cursorFrame, textFrame)); // Text frame should take everything over again on reset. [cell clearKeywordAndHint]; 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)); // Security icon takes up space on the right security_image_view_.SetImageShown( LocationBarViewMac::SecurityImageView::LOCK); security_image_view_.SetVisible(true); textFrame = [cell textFrameForFrame:bounds]; EXPECT_FALSE(NSIsEmptyRect(textFrame)); EXPECT_TRUE(NSContainsRect(bounds, textFrame)); EXPECT_LT(NSMaxX(textFrame), NSMaxX(bounds)); EXPECT_TRUE(NSContainsRect(cursorFrame, textFrame)); // Search hint text takes precedence over the hint icon; the text frame // should be smaller in order to accomodate the text that is wider than // the icon. [cell setSearchHintString:@"Search hint" availableWidth:kWidth]; NSRect textFrameWithHintText = [cell textFrameForFrame:bounds]; EXPECT_TRUE(NSContainsRect(textFrame, textFrameWithHintText)); EXPECT_LT(NSWidth(textFrameWithHintText), NSWidth(textFrame)); } // The editor frame should be slightly inset from the text frame. TEST_F(AutocompleteTextFieldCellTest, DrawingRectForBounds) { AutocompleteTextFieldCell* cell = static_cast([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); // TODO(shess): Do we really need to test every combination? [cell setSearchHintString:@"Search hint" availableWidth:kWidth]; textFrame = [cell textFrameForFrame:bounds]; drawingRect = [cell drawingRectForBounds:bounds]; EXPECT_FALSE(NSIsEmptyRect(drawingRect)); EXPECT_TRUE(NSContainsRect(textFrame, NSInsetRect(drawingRect, 1, 1))); NSImage* image = [NSImage imageNamed:@"NSApplicationIcon"]; [cell setKeywordHintPrefix:@"Keyword " image:image suffix:@" hint" availableWidth:kWidth]; textFrame = [cell textFrameForFrame:bounds]; drawingRect = [cell drawingRectForBounds:bounds]; EXPECT_FALSE(NSIsEmptyRect(drawingRect)); EXPECT_TRUE(NSContainsRect(NSInsetRect(textFrame, 1, 1), drawingRect)); [cell setKeywordString:@"Search Engine:" partialString:@"Search Eng:" availableWidth:kWidth]; textFrame = [cell textFrameForFrame:bounds]; drawingRect = [cell drawingRectForBounds:bounds]; EXPECT_FALSE(NSIsEmptyRect(drawingRect)); EXPECT_TRUE(NSContainsRect(NSInsetRect(textFrame, 1, 1), drawingRect)); [cell clearKeywordAndHint]; textFrame = [cell textFrameForFrame:bounds]; drawingRect = [cell drawingRectForBounds:bounds]; EXPECT_FALSE(NSIsEmptyRect(drawingRect)); EXPECT_TRUE(NSContainsRect(NSInsetRect(textFrame, 1, 1), drawingRect)); EXPECT_TRUE(NSEqualRects(drawingRect, originalDrawingRect)); security_image_view_.SetImageShown( LocationBarViewMac::SecurityImageView::LOCK); security_image_view_.SetVisible(true); textFrame = [cell textFrameForFrame:bounds]; drawingRect = [cell drawingRectForBounds:bounds]; EXPECT_FALSE(NSIsEmptyRect(drawingRect)); EXPECT_TRUE(NSContainsRect(NSInsetRect(textFrame, 1, 1), drawingRect)); } // Test that the security icon is at the right side of the cell. TEST_F(AutocompleteTextFieldCellTest, SecurityImageFrame) { AutocompleteTextFieldCell* cell = static_cast([view_ cell]); const NSRect bounds([view_ bounds]); security_image_view_.SetImageShown( LocationBarViewMac::SecurityImageView::LOCK); security_image_view_.SetVisible(false); NSRect iconRect = [cell securityImageFrameForFrame:bounds]; EXPECT_TRUE(NSIsEmptyRect(iconRect)); // Save the starting frame for after clear. const NSRect originalIconRect(iconRect); security_image_view_.SetVisible(true); iconRect = [cell securityImageFrameForFrame:bounds]; EXPECT_FALSE(NSIsEmptyRect(iconRect)); EXPECT_TRUE(NSContainsRect(bounds, iconRect)); // Make sure we are right of the |drawingRect|. NSRect drawingRect = [cell drawingRectForBounds:bounds]; EXPECT_LE(NSMaxX(drawingRect), NSMinX(iconRect)); // Make sure we're right of the |textFrame|. NSRect textFrame = [cell textFrameForFrame:bounds]; EXPECT_LE(NSMaxX(textFrame), NSMinX(iconRect)); // Now add a label. NSFont* font = [NSFont controlContentFontOfSize:12.0]; NSColor* color = [NSColor blackColor]; security_image_view_.SetLabel(@"Label", font, color); iconRect = [cell securityImageFrameForFrame:bounds]; EXPECT_FALSE(NSIsEmptyRect(iconRect)); EXPECT_TRUE(NSContainsRect(bounds, iconRect)); // Make sure we are right of the |drawingRect|. drawingRect = [cell drawingRectForBounds:bounds]; EXPECT_LE(NSMaxX(drawingRect), NSMinX(iconRect)); // Make sure we're right of the |textFrame|. textFrame = [cell textFrameForFrame:bounds]; EXPECT_LE(NSMaxX(textFrame), NSMinX(iconRect)); // Make sure we clear correctly. security_image_view_.SetVisible(false); iconRect = [cell securityImageFrameForFrame:bounds]; EXPECT_TRUE(NSEqualRects(iconRect, originalIconRect)); EXPECT_TRUE(NSIsEmptyRect(iconRect)); } // Test Page Action counts. TEST_F(AutocompleteTextFieldCellTest, PageActionCount) { AutocompleteTextFieldCell* cell = static_cast([view_ cell]); TestPageActionImageView page_action_view; TestPageActionImageView page_action_view2; TestPageActionViewList list; [cell setPageActionViewList:&list]; EXPECT_EQ(0u, [cell pageActionCount]); list.Add(&page_action_view); EXPECT_EQ(1u, [cell pageActionCount]); list.Add(&page_action_view2); EXPECT_EQ(2u, [cell pageActionCount]); } // Test that Page Action icons are properly placed. TEST_F(AutocompleteTextFieldCellTest, PageActionImageFrame) { AutocompleteTextFieldCell* cell = static_cast([view_ cell]); const NSRect bounds([view_ bounds]); security_image_view_.SetImageShown( LocationBarViewMac::SecurityImageView::LOCK); TestPageActionImageView page_action_view; // We'll assume that the extensions code enforces icons smaller than the // location bar. NSImage* image = [[[NSImage alloc] initWithSize:NSMakeSize(NSHeight(bounds) - 4, NSHeight(bounds) - 4)] autorelease]; page_action_view.SetImage(image); TestPageActionImageView page_action_view2; page_action_view2.SetImage(image); TestPageActionViewList list; list.Add(&page_action_view); list.Add(&page_action_view2); [cell setPageActionViewList:&list]; security_image_view_.SetVisible(false); page_action_view.SetVisible(false); page_action_view2.SetVisible(false); EXPECT_TRUE(NSIsEmptyRect([cell pageActionFrameForIndex:0 inFrame:bounds])); EXPECT_TRUE(NSIsEmptyRect([cell pageActionFrameForIndex:1 inFrame:bounds])); // Test preview page actions, as used by the extension installed bubble. TestPageActionImageView preview_view; list.Add(&preview_view); EXPECT_TRUE(NSIsEmptyRect([cell pageActionFrameForIndex:2 inFrame:bounds])); preview_view.set_preview_enabled(true); EXPECT_FALSE(NSIsEmptyRect([cell pageActionFrameForIndex:2 inFrame:bounds])); // One page action, no security icon. page_action_view.SetVisible(true); NSRect iconRect0 = [cell pageActionFrameForIndex:0 inFrame:bounds]; EXPECT_FALSE(NSIsEmptyRect(iconRect0)); EXPECT_TRUE(NSContainsRect(bounds, iconRect0)); // Make sure we are right of the |drawingRect|. NSRect drawingRect = [cell drawingRectForBounds:bounds]; EXPECT_LE(NSMaxX(drawingRect), NSMinX(iconRect0)); // Make sure we're right of the |textFrame|. NSRect textFrame = [cell textFrameForFrame:bounds]; EXPECT_LE(NSMaxX(textFrame), NSMinX(iconRect0)); // Two page actions plus a security icon. page_action_view2.SetVisible(true); security_image_view_.SetVisible(true); iconRect0 = [cell pageActionFrameForIndex:0 inFrame:bounds]; NSRect iconRect1 = [cell pageActionFrameForIndex:1 inFrame:bounds]; NSRect lockRect = [cell securityImageFrameForFrame:bounds]; // Make sure they're all in the expected order, and right of the |drawingRect| // and |textFrame|. drawingRect = [cell drawingRectForBounds:bounds]; textFrame = [cell textFrameForFrame:bounds]; EXPECT_FALSE(NSIsEmptyRect(iconRect0)); EXPECT_TRUE(NSContainsRect(bounds, iconRect0)); EXPECT_FALSE(NSIsEmptyRect(iconRect1)); EXPECT_TRUE(NSContainsRect(bounds, iconRect1)); EXPECT_FALSE(NSIsEmptyRect(lockRect)); EXPECT_TRUE(NSContainsRect(bounds, lockRect)); EXPECT_LE(NSMaxX(drawingRect), NSMinX(iconRect1)); EXPECT_LE(NSMaxX(textFrame), NSMinX(iconRect1)); EXPECT_LE(NSMaxX(iconRect1), NSMinX(iconRect0)); EXPECT_LE(NSMaxX(iconRect0), NSMinX(lockRect)); } // Test that the cell correctly chooses the partial keyword if there's // not enough room. TEST_F(AutocompleteTextFieldCellTest, UsesPartialKeywordIfNarrow) { AutocompleteTextFieldCell* cell = static_cast([view_ cell]); const NSString* kFullString = @"Search Engine:"; const NSString* kPartialString = @"Search Eng:"; // Wide width chooses the full string. [cell setKeywordString:kFullString partialString:kPartialString availableWidth:kWidth]; EXPECT_TRUE([cell keywordString]); EXPECT_TRUE([[[cell keywordString] string] isEqualToString:kFullString]); // Narrow width chooses the partial string. [cell setKeywordString:kFullString partialString:kPartialString availableWidth:kNarrowWidth]; EXPECT_TRUE([cell keywordString]); EXPECT_TRUE([[[cell keywordString] string] isEqualToString:kPartialString]); // But not if there isn't one! [cell setKeywordString:kFullString partialString:nil availableWidth:kNarrowWidth]; EXPECT_TRUE([cell keywordString]); EXPECT_TRUE([[[cell keywordString] string] isEqualToString:kFullString]); } // Test that the cell drops the search hint if there is no room for // it. TEST_F(AutocompleteTextFieldCellTest, OmitsSearchHintIfNarrow) { AutocompleteTextFieldCell* cell = static_cast([view_ cell]); const NSString* kSearchHint = @"Type to search"; // Wide width chooses the full string. [cell setSearchHintString:kSearchHint availableWidth:kWidth]; EXPECT_TRUE([cell hintString]); EXPECT_TRUE([[[cell hintString] string] isEqualToString:kSearchHint]); // Narrow width suppresses entirely. [cell setSearchHintString:kSearchHint availableWidth:kNarrowWidth]; EXPECT_FALSE([cell hintString]); } // Test that the cell drops all but the image if there is no room for // the entire keyword hint. TEST_F(AutocompleteTextFieldCellTest, TrimsKeywordHintIfNarrow) { AutocompleteTextFieldCell* cell = static_cast([view_ cell]); scoped_nsobject image( [[NSImage alloc] initWithSize:NSMakeSize(20, 20)]); const NSString* kHintPrefix = @"Press "; const NSString* kHintSuffix = @" to search Engine"; // Wide width chooses the full string. [cell setKeywordHintPrefix:kHintPrefix image:image suffix:kHintSuffix availableWidth:kWidth]; EXPECT_TRUE([cell hintString]); EXPECT_TRUE([[[cell hintString] string] hasPrefix:kHintPrefix]); EXPECT_TRUE([[[cell hintString] string] hasSuffix:kHintSuffix]); // Narrow width suppresses everything but the image. [cell setKeywordHintPrefix:kHintPrefix image:image suffix:kHintSuffix availableWidth:kNarrowWidth]; EXPECT_EQ([[cell hintString] length], 1U); } } // namespace