Home | History | Annotate | Download | only in location_bar
      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 #import "base/mac/cocoa_protocols.h"
      8 #include "base/memory/scoped_nsobject.h"
      9 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field.h"
     10 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_cell.h"
     11 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor.h"
     12 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_unittest_helper.h"
     13 #import "chrome/browser/ui/cocoa/location_bar/location_bar_decoration.h"
     14 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
     15 #include "grit/theme_resources.h"
     16 #include "testing/gmock/include/gmock/gmock.h"
     17 #include "testing/gtest/include/gtest/gtest.h"
     18 #import "testing/gtest_mac.h"
     19 #include "testing/platform_test.h"
     20 #include "ui/base/resource/resource_bundle.h"
     21 
     22 using ::testing::A;
     23 using ::testing::InSequence;
     24 using ::testing::Return;
     25 using ::testing::ReturnArg;
     26 using ::testing::StrictMock;
     27 using ::testing::_;
     28 
     29 namespace {
     30 
     31 class MockDecoration : public LocationBarDecoration {
     32  public:
     33   virtual CGFloat GetWidthForSpace(CGFloat width) { return 20.0; }
     34 
     35   virtual void DrawInFrame(NSRect frame, NSView* control_view) { ; }
     36   MOCK_METHOD0(AcceptsMousePress, bool());
     37   MOCK_METHOD1(OnMousePressed, bool(NSRect frame));
     38   MOCK_METHOD0(GetMenu, NSMenu*());
     39 };
     40 
     41 // Mock up an incrementing event number.
     42 NSUInteger eventNumber = 0;
     43 
     44 // Create an event of the indicated |type| at |point| within |view|.
     45 // TODO(shess): Would be nice to have a MockApplication which provided
     46 // nifty accessors to create these things and inject them.  It could
     47 // even provide functions for "Click and drag mouse from point A to
     48 // point B".
     49 NSEvent* Event(NSView* view, const NSPoint point, const NSEventType type,
     50                const NSUInteger clickCount) {
     51   NSWindow* window([view window]);
     52   const NSPoint locationInWindow([view convertPoint:point toView:nil]);
     53   const NSPoint location([window convertBaseToScreen:locationInWindow]);
     54   return [NSEvent mouseEventWithType:type
     55                             location:location
     56                        modifierFlags:0
     57                            timestamp:0
     58                         windowNumber:[window windowNumber]
     59                              context:nil
     60                          eventNumber:eventNumber++
     61                           clickCount:clickCount
     62                             pressure:0.0];
     63 }
     64 NSEvent* Event(NSView* view, const NSPoint point, const NSEventType type) {
     65   return Event(view, point, type, 1);
     66 }
     67 
     68 // Width of the field so that we don't have to ask |field_| for it all
     69 // the time.
     70 static const CGFloat kWidth(300.0);
     71 
     72 class AutocompleteTextFieldTest : public CocoaTest {
     73  public:
     74   AutocompleteTextFieldTest() {
     75     // Make sure this is wide enough to play games with the cell
     76     // decorations.
     77     NSRect frame = NSMakeRect(0, 0, kWidth, 30);
     78     scoped_nsobject<AutocompleteTextField> field(
     79         [[AutocompleteTextField alloc] initWithFrame:frame]);
     80     field_ = field.get();
     81     [field_ setStringValue:@"Test test"];
     82     [[test_window() contentView] addSubview:field_];
     83 
     84     AutocompleteTextFieldCell* cell = [field_ cell];
     85     [cell clearDecorations];
     86 
     87     mock_left_decoration_.SetVisible(false);
     88     [cell addLeftDecoration:&mock_left_decoration_];
     89 
     90     mock_right_decoration_.SetVisible(false);
     91     [cell addRightDecoration:&mock_right_decoration_];
     92 
     93     window_delegate_.reset(
     94         [[AutocompleteTextFieldWindowTestDelegate alloc] init]);
     95     [test_window() setDelegate:window_delegate_.get()];
     96   }
     97 
     98   NSEvent* KeyDownEventWithFlags(NSUInteger flags) {
     99     return [NSEvent keyEventWithType:NSKeyDown
    100                             location:NSZeroPoint
    101                        modifierFlags:flags
    102                            timestamp:0.0
    103                         windowNumber:[test_window() windowNumber]
    104                              context:nil
    105                           characters:@"a"
    106          charactersIgnoringModifiers:@"a"
    107                            isARepeat:NO
    108                              keyCode:'a'];
    109   }
    110 
    111   // Helper to return the field-editor frame being used w/in |field_|.
    112   NSRect EditorFrame() {
    113     EXPECT_TRUE([field_ currentEditor]);
    114     EXPECT_EQ([[field_ subviews] count], 1U);
    115     if ([[field_ subviews] count] > 0) {
    116       return [[[field_ subviews] objectAtIndex:0] frame];
    117     } else {
    118       // Return something which won't work so the caller can soldier
    119       // on.
    120       return NSZeroRect;
    121     }
    122   }
    123 
    124   AutocompleteTextField* field_;
    125   MockDecoration mock_left_decoration_;
    126   MockDecoration mock_right_decoration_;
    127   scoped_nsobject<AutocompleteTextFieldWindowTestDelegate> window_delegate_;
    128 };
    129 
    130 TEST_VIEW(AutocompleteTextFieldTest, field_);
    131 
    132 // Base class for testing AutocompleteTextFieldObserver messages.
    133 class AutocompleteTextFieldObserverTest : public AutocompleteTextFieldTest {
    134  public:
    135   virtual void SetUp() {
    136     AutocompleteTextFieldTest::SetUp();
    137     [field_ setObserver:&field_observer_];
    138   }
    139 
    140   virtual void TearDown() {
    141     // Clear the observer so that we don't show output for
    142     // uninteresting messages to the mock (for instance, if |field_| has
    143     // focus at the end of the test).
    144     [field_ setObserver:NULL];
    145 
    146     AutocompleteTextFieldTest::TearDown();
    147   }
    148 
    149   StrictMock<MockAutocompleteTextFieldObserver> field_observer_;
    150 };
    151 
    152 // Test that we have the right cell class.
    153 TEST_F(AutocompleteTextFieldTest, CellClass) {
    154   EXPECT_TRUE([[field_ cell] isKindOfClass:[AutocompleteTextFieldCell class]]);
    155 }
    156 
    157 // Test that becoming first responder sets things up correctly.
    158 TEST_F(AutocompleteTextFieldTest, FirstResponder) {
    159   EXPECT_EQ(nil, [field_ currentEditor]);
    160   EXPECT_EQ([[field_ subviews] count], 0U);
    161   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
    162   EXPECT_FALSE(nil == [field_ currentEditor]);
    163   EXPECT_EQ([[field_ subviews] count], 1U);
    164   EXPECT_TRUE([[field_ currentEditor] isDescendantOf:field_]);
    165 
    166   // Check that the window delegate is providing the right editor.
    167   Class c = [AutocompleteTextFieldEditor class];
    168   EXPECT_TRUE([[field_ currentEditor] isKindOfClass:c]);
    169 }
    170 
    171 TEST_F(AutocompleteTextFieldTest, AvailableDecorationWidth) {
    172   // A fudge factor to account for how much space the border takes up.
    173   // The test shouldn't be too dependent on the field's internals, but
    174   // it also shouldn't let deranged cases fall through the cracks
    175   // (like nothing available with no text, or everything available
    176   // with some text).
    177   const CGFloat kBorderWidth = 20.0;
    178 
    179   // With no contents, almost the entire width is available for
    180   // decorations.
    181   [field_ setStringValue:@""];
    182   CGFloat availableWidth = [field_ availableDecorationWidth];
    183   EXPECT_LE(availableWidth, kWidth);
    184   EXPECT_GT(availableWidth, kWidth - kBorderWidth);
    185 
    186   // With minor contents, most of the remaining width is available for
    187   // decorations.
    188   NSDictionary* attributes =
    189       [NSDictionary dictionaryWithObject:[field_ font]
    190                                   forKey:NSFontAttributeName];
    191   NSString* string = @"Hello world";
    192   const NSSize size([string sizeWithAttributes:attributes]);
    193   [field_ setStringValue:string];
    194   availableWidth = [field_ availableDecorationWidth];
    195   EXPECT_LE(availableWidth, kWidth - size.width);
    196   EXPECT_GT(availableWidth, kWidth - size.width - kBorderWidth);
    197 
    198   // With huge contents, nothing at all is left for decorations.
    199   string = @"A long string which is surely wider than field_ can hold.";
    200   [field_ setStringValue:string];
    201   availableWidth = [field_ availableDecorationWidth];
    202   EXPECT_LT(availableWidth, 0.0);
    203 }
    204 
    205 // Test drawing, mostly to ensure nothing leaks or crashes.
    206 TEST_F(AutocompleteTextFieldTest, Display) {
    207   [field_ display];
    208 
    209   // Test focussed drawing.
    210   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
    211   [field_ display];
    212   [test_window() clearPretendKeyWindowAndFirstResponder];
    213 }
    214 
    215 TEST_F(AutocompleteTextFieldObserverTest, FlagsChanged) {
    216   InSequence dummy;  // Call mock in exactly the order specified.
    217 
    218   // Test without Control key down, but some other modifier down.
    219   EXPECT_CALL(field_observer_, OnControlKeyChanged(false));
    220   [field_ flagsChanged:KeyDownEventWithFlags(NSShiftKeyMask)];
    221 
    222   // Test with Control key down.
    223   EXPECT_CALL(field_observer_, OnControlKeyChanged(true));
    224   [field_ flagsChanged:KeyDownEventWithFlags(NSControlKeyMask)];
    225 }
    226 
    227 // This test is here rather than in the editor's tests because the
    228 // field catches -flagsChanged: because it's on the responder chain,
    229 // the field editor doesn't implement it.
    230 TEST_F(AutocompleteTextFieldObserverTest, FieldEditorFlagsChanged) {
    231   // Many of these methods try to change the selection.
    232   EXPECT_CALL(field_observer_, SelectionRangeForProposedRange(A<NSRange>()))
    233       .WillRepeatedly(ReturnArg<0>());
    234 
    235   InSequence dummy;  // Call mock in exactly the order specified.
    236   EXPECT_CALL(field_observer_, OnSetFocus(false));
    237   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
    238   NSResponder* firstResponder = [[field_ window] firstResponder];
    239   EXPECT_EQ(firstResponder, [field_ currentEditor]);
    240 
    241   // Test without Control key down, but some other modifier down.
    242   EXPECT_CALL(field_observer_, OnControlKeyChanged(false));
    243   [firstResponder flagsChanged:KeyDownEventWithFlags(NSShiftKeyMask)];
    244 
    245   // Test with Control key down.
    246   EXPECT_CALL(field_observer_, OnControlKeyChanged(true));
    247   [firstResponder flagsChanged:KeyDownEventWithFlags(NSControlKeyMask)];
    248 }
    249 
    250 // Frame size changes are propagated to |observer_|.
    251 TEST_F(AutocompleteTextFieldObserverTest, FrameChanged) {
    252   EXPECT_CALL(field_observer_, OnFrameChanged());
    253   NSRect frame = [field_ frame];
    254   frame.size.width += 10.0;
    255   [field_ setFrame:frame];
    256 }
    257 
    258 // Test that the field editor gets the same bounds when focus is
    259 // delivered by the standard focusing machinery, or by
    260 // -resetFieldEditorFrameIfNeeded.
    261 TEST_F(AutocompleteTextFieldTest, ResetFieldEditorBase) {
    262   // Capture the editor frame resulting from the standard focus
    263   // machinery.
    264   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
    265   const NSRect baseEditorFrame = EditorFrame();
    266 
    267   // A decoration should result in a strictly smaller editor frame.
    268   mock_left_decoration_.SetVisible(true);
    269   [field_ resetFieldEditorFrameIfNeeded];
    270   EXPECT_FALSE(NSEqualRects(baseEditorFrame, EditorFrame()));
    271   EXPECT_TRUE(NSContainsRect(baseEditorFrame, EditorFrame()));
    272 
    273   // Removing the decoration and using -resetFieldEditorFrameIfNeeded
    274   // should result in the same frame as the standard focus machinery.
    275   mock_left_decoration_.SetVisible(false);
    276   [field_ resetFieldEditorFrameIfNeeded];
    277   EXPECT_TRUE(NSEqualRects(baseEditorFrame, EditorFrame()));
    278 }
    279 
    280 // Test that the field editor gets the same bounds when focus is
    281 // delivered by the standard focusing machinery, or by
    282 // -resetFieldEditorFrameIfNeeded, this time with a decoration
    283 // pre-loaded.
    284 TEST_F(AutocompleteTextFieldTest, ResetFieldEditorWithDecoration) {
    285   AutocompleteTextFieldCell* cell = [field_ cell];
    286 
    287   // Make sure decoration isn't already visible, then make it visible.
    288   EXPECT_TRUE(NSIsEmptyRect([cell frameForDecoration:&mock_left_decoration_
    289                                              inFrame:[field_ bounds]]));
    290   mock_left_decoration_.SetVisible(true);
    291   EXPECT_FALSE(NSIsEmptyRect([cell frameForDecoration:&mock_left_decoration_
    292                                               inFrame:[field_ bounds]]));
    293 
    294   // Capture the editor frame resulting from the standard focus
    295   // machinery.
    296 
    297   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
    298   const NSRect baseEditorFrame = EditorFrame();
    299 
    300   // When the decoration is not visible the frame should be strictly larger.
    301   mock_left_decoration_.SetVisible(false);
    302   EXPECT_TRUE(NSIsEmptyRect([cell frameForDecoration:&mock_left_decoration_
    303                                              inFrame:[field_ bounds]]));
    304   [field_ resetFieldEditorFrameIfNeeded];
    305   EXPECT_FALSE(NSEqualRects(baseEditorFrame, EditorFrame()));
    306   EXPECT_TRUE(NSContainsRect(EditorFrame(), baseEditorFrame));
    307 
    308   // When the decoration is visible, -resetFieldEditorFrameIfNeeded
    309   // should result in the same frame as the standard focus machinery.
    310   mock_left_decoration_.SetVisible(true);
    311   EXPECT_FALSE(NSIsEmptyRect([cell frameForDecoration:&mock_left_decoration_
    312                                               inFrame:[field_ bounds]]));
    313 
    314   [field_ resetFieldEditorFrameIfNeeded];
    315   EXPECT_TRUE(NSEqualRects(baseEditorFrame, EditorFrame()));
    316 }
    317 
    318 // Test that resetting the field editor bounds does not cause untoward
    319 // messages to the field's observer.
    320 TEST_F(AutocompleteTextFieldObserverTest, ResetFieldEditorContinuesEditing) {
    321   // Many of these methods try to change the selection.
    322   EXPECT_CALL(field_observer_, SelectionRangeForProposedRange(A<NSRange>()))
    323       .WillRepeatedly(ReturnArg<0>());
    324 
    325   EXPECT_CALL(field_observer_, OnSetFocus(false));
    326   // Becoming first responder doesn't begin editing.
    327   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
    328   const NSRect baseEditorFrame = EditorFrame();
    329   NSTextView* editor = static_cast<NSTextView*>([field_ currentEditor]);
    330   EXPECT_TRUE(nil != editor);
    331 
    332   // This should begin editing and indicate a change.
    333   EXPECT_CALL(field_observer_, OnDidBeginEditing());
    334   EXPECT_CALL(field_observer_, OnBeforeChange());
    335   EXPECT_CALL(field_observer_, OnDidChange());
    336   [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
    337   [editor didChangeText];
    338 
    339   // No messages to |field_observer_| when the frame actually changes.
    340   mock_left_decoration_.SetVisible(true);
    341   [field_ resetFieldEditorFrameIfNeeded];
    342   EXPECT_FALSE(NSEqualRects(baseEditorFrame, EditorFrame()));
    343 }
    344 
    345 // Clicking in a right-hand decoration which does not handle the mouse
    346 // puts the caret rightmost.
    347 TEST_F(AutocompleteTextFieldTest, ClickRightDecorationPutsCaretRightmost) {
    348   // Decoration does not handle the mouse event, so the cell should
    349   // process it.  Called at least once.
    350   EXPECT_CALL(mock_right_decoration_, AcceptsMousePress())
    351       .WillOnce(Return(false))
    352       .WillRepeatedly(Return(false));
    353 
    354   // Set the decoration before becoming responder.
    355   EXPECT_FALSE([field_ currentEditor]);
    356   mock_right_decoration_.SetVisible(true);
    357 
    358   // Make first responder should select all.
    359   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
    360   EXPECT_TRUE([field_ currentEditor]);
    361   const NSRange allRange = NSMakeRange(0, [[field_ stringValue] length]);
    362   EXPECT_TRUE(NSEqualRanges(allRange, [[field_ currentEditor] selectedRange]));
    363 
    364   // Generate a click on the decoration.
    365   AutocompleteTextFieldCell* cell = [field_ cell];
    366   const NSRect bounds = [field_ bounds];
    367   const NSRect iconFrame =
    368       [cell frameForDecoration:&mock_right_decoration_ inFrame:bounds];
    369   const NSPoint point = NSMakePoint(NSMidX(iconFrame), NSMidY(iconFrame));
    370   NSEvent* downEvent = Event(field_, point, NSLeftMouseDown);
    371   NSEvent* upEvent = Event(field_, point, NSLeftMouseUp);
    372   [NSApp postEvent:upEvent atStart:YES];
    373   [field_ mouseDown:downEvent];
    374 
    375   // Selection should be a right-hand-side caret.
    376   EXPECT_TRUE(NSEqualRanges(NSMakeRange([[field_ stringValue] length], 0),
    377                             [[field_ currentEditor] selectedRange]));
    378 }
    379 
    380 // Clicking in a left-side decoration which doesn't handle the event
    381 // puts the selection in the leftmost position.
    382 TEST_F(AutocompleteTextFieldTest, ClickLeftDecorationPutsCaretLeftmost) {
    383   // Decoration does not handle the mouse event, so the cell should
    384   // process it.  Called at least once.
    385   EXPECT_CALL(mock_left_decoration_, AcceptsMousePress())
    386       .WillOnce(Return(false))
    387       .WillRepeatedly(Return(false));
    388 
    389   // Set the decoration before becoming responder.
    390   EXPECT_FALSE([field_ currentEditor]);
    391   mock_left_decoration_.SetVisible(true);
    392 
    393   // Make first responder should select all.
    394   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
    395   EXPECT_TRUE([field_ currentEditor]);
    396   const NSRange allRange = NSMakeRange(0, [[field_ stringValue] length]);
    397   EXPECT_TRUE(NSEqualRanges(allRange, [[field_ currentEditor] selectedRange]));
    398 
    399   // Generate a click on the decoration.
    400   AutocompleteTextFieldCell* cell = [field_ cell];
    401   const NSRect bounds = [field_ bounds];
    402   const NSRect iconFrame =
    403       [cell frameForDecoration:&mock_left_decoration_ inFrame:bounds];
    404   const NSPoint point = NSMakePoint(NSMidX(iconFrame), NSMidY(iconFrame));
    405   NSEvent* downEvent = Event(field_, point, NSLeftMouseDown);
    406   NSEvent* upEvent = Event(field_, point, NSLeftMouseUp);
    407   [NSApp postEvent:upEvent atStart:YES];
    408   [field_ mouseDown:downEvent];
    409 
    410   // Selection should be a left-hand-side caret.
    411   EXPECT_TRUE(NSEqualRanges(NSMakeRange(0, 0),
    412                             [[field_ currentEditor] selectedRange]));
    413 }
    414 
    415 // Clicks not in the text area or the cell's decorations fall through
    416 // to the editor.
    417 TEST_F(AutocompleteTextFieldTest, ClickBorderSelectsAll) {
    418   // Can't rely on the window machinery to make us first responder,
    419   // here.
    420   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
    421   EXPECT_TRUE([field_ currentEditor]);
    422 
    423   const NSPoint point(NSMakePoint(20.0, 1.0));
    424   NSEvent* downEvent(Event(field_, point, NSLeftMouseDown));
    425   NSEvent* upEvent(Event(field_, point, NSLeftMouseUp));
    426   [NSApp postEvent:upEvent atStart:YES];
    427   [field_ mouseDown:downEvent];
    428 
    429   // Clicking in the narrow border area around a Cocoa NSTextField
    430   // does a select-all.  Regardless of whether this is a good call, it
    431   // works as a test that things get passed down to the editor.
    432   const NSRange selectedRange([[field_ currentEditor] selectedRange]);
    433   EXPECT_EQ(selectedRange.location, 0U);
    434   EXPECT_EQ(selectedRange.length, [[field_ stringValue] length]);
    435 }
    436 
    437 // Single-click with no drag should setup a field editor and
    438 // select all.
    439 TEST_F(AutocompleteTextFieldTest, ClickSelectsAll) {
    440   EXPECT_FALSE([field_ currentEditor]);
    441 
    442   const NSPoint point = NSMakePoint(20.0, NSMidY([field_ bounds]));
    443   NSEvent* downEvent(Event(field_, point, NSLeftMouseDown));
    444   NSEvent* upEvent(Event(field_, point, NSLeftMouseUp));
    445   [NSApp postEvent:upEvent atStart:YES];
    446   [field_ mouseDown:downEvent];
    447   EXPECT_TRUE([field_ currentEditor]);
    448   const NSRange selectedRange([[field_ currentEditor] selectedRange]);
    449   EXPECT_EQ(selectedRange.location, 0U);
    450   EXPECT_EQ(selectedRange.length, [[field_ stringValue] length]);
    451 }
    452 
    453 // Click-drag selects text, not select all.
    454 TEST_F(AutocompleteTextFieldTest, ClickDragSelectsText) {
    455   EXPECT_FALSE([field_ currentEditor]);
    456 
    457   NSEvent* downEvent(Event(field_, NSMakePoint(20.0, 5.0), NSLeftMouseDown));
    458   NSEvent* upEvent(Event(field_, NSMakePoint(0.0, 5.0), NSLeftMouseUp));
    459   [NSApp postEvent:upEvent atStart:YES];
    460   [field_ mouseDown:downEvent];
    461   EXPECT_TRUE([field_ currentEditor]);
    462 
    463   // Expect this to have selected a prefix of the content.  Mostly
    464   // just don't want the select-all behavior.
    465   const NSRange selectedRange([[field_ currentEditor] selectedRange]);
    466   EXPECT_EQ(selectedRange.location, 0U);
    467   EXPECT_LT(selectedRange.length, [[field_ stringValue] length]);
    468 }
    469 
    470 // TODO(shess): Test that click/pause/click allows cursor placement.
    471 // In this case the first click goes to the field, but the second
    472 // click goes to the field editor, so the current testing pattern
    473 // can't work.  What really needs to happen is to push through the
    474 // NSWindow event machinery so that we can say "two independent clicks
    475 // at the same location have the right effect".  Once that is done, it
    476 // might make sense to revise the other tests to use the same
    477 // machinery.
    478 
    479 // Double-click selects word, not select all.
    480 TEST_F(AutocompleteTextFieldTest, DoubleClickSelectsWord) {
    481   EXPECT_FALSE([field_ currentEditor]);
    482 
    483   const NSPoint point = NSMakePoint(20.0, NSMidY([field_ bounds]));
    484   NSEvent* downEvent(Event(field_, point, NSLeftMouseDown, 1));
    485   NSEvent* upEvent(Event(field_, point, NSLeftMouseUp, 1));
    486   NSEvent* downEvent2(Event(field_, point, NSLeftMouseDown, 2));
    487   NSEvent* upEvent2(Event(field_, point, NSLeftMouseUp, 2));
    488   [NSApp postEvent:upEvent atStart:YES];
    489   [field_ mouseDown:downEvent];
    490   [NSApp postEvent:upEvent2 atStart:YES];
    491   [field_ mouseDown:downEvent2];
    492   EXPECT_TRUE([field_ currentEditor]);
    493 
    494   // Selected the first word.
    495   const NSRange selectedRange([[field_ currentEditor] selectedRange]);
    496   const NSRange spaceRange([[field_ stringValue] rangeOfString:@" "]);
    497   EXPECT_GT(spaceRange.location, 0U);
    498   EXPECT_LT(spaceRange.length, [[field_ stringValue] length]);
    499   EXPECT_EQ(selectedRange.location, 0U);
    500   EXPECT_EQ(selectedRange.length, spaceRange.location);
    501 }
    502 
    503 TEST_F(AutocompleteTextFieldTest, TripleClickSelectsAll) {
    504   EXPECT_FALSE([field_ currentEditor]);
    505 
    506   const NSPoint point(NSMakePoint(20.0, 5.0));
    507   NSEvent* downEvent(Event(field_, point, NSLeftMouseDown, 1));
    508   NSEvent* upEvent(Event(field_, point, NSLeftMouseUp, 1));
    509   NSEvent* downEvent2(Event(field_, point, NSLeftMouseDown, 2));
    510   NSEvent* upEvent2(Event(field_, point, NSLeftMouseUp, 2));
    511   NSEvent* downEvent3(Event(field_, point, NSLeftMouseDown, 3));
    512   NSEvent* upEvent3(Event(field_, point, NSLeftMouseUp, 3));
    513   [NSApp postEvent:upEvent atStart:YES];
    514   [field_ mouseDown:downEvent];
    515   [NSApp postEvent:upEvent2 atStart:YES];
    516   [field_ mouseDown:downEvent2];
    517   [NSApp postEvent:upEvent3 atStart:YES];
    518   [field_ mouseDown:downEvent3];
    519   EXPECT_TRUE([field_ currentEditor]);
    520 
    521   // Selected the first word.
    522   const NSRange selectedRange([[field_ currentEditor] selectedRange]);
    523   EXPECT_EQ(selectedRange.location, 0U);
    524   EXPECT_EQ(selectedRange.length, [[field_ stringValue] length]);
    525 }
    526 
    527 // Clicking a decoration should call decoration's OnMousePressed.
    528 TEST_F(AutocompleteTextFieldTest, LeftDecorationMouseDown) {
    529   // At this point, not focussed.
    530   EXPECT_FALSE([field_ currentEditor]);
    531 
    532   mock_left_decoration_.SetVisible(true);
    533   EXPECT_CALL(mock_left_decoration_, AcceptsMousePress())
    534       .WillRepeatedly(Return(true));
    535 
    536   AutocompleteTextFieldCell* cell = [field_ cell];
    537   const NSRect iconFrame =
    538       [cell frameForDecoration:&mock_left_decoration_ inFrame:[field_ bounds]];
    539   const NSPoint location = NSMakePoint(NSMidX(iconFrame), NSMidY(iconFrame));
    540   NSEvent* downEvent = Event(field_, location, NSLeftMouseDown, 1);
    541   NSEvent* upEvent = Event(field_, location, NSLeftMouseUp, 1);
    542 
    543   // Since decorations can be dragged, the mouse-press is sent on
    544   // mouse-up.
    545   [NSApp postEvent:upEvent atStart:YES];
    546 
    547   EXPECT_CALL(mock_left_decoration_, OnMousePressed(_))
    548       .WillOnce(Return(true));
    549   [field_ mouseDown:downEvent];
    550 
    551   // Focus the field and test that handled clicks don't affect selection.
    552   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
    553   EXPECT_TRUE([field_ currentEditor]);
    554   const NSRange allRange = NSMakeRange(0, [[field_ stringValue] length]);
    555   EXPECT_TRUE(NSEqualRanges(allRange, [[field_ currentEditor] selectedRange]));
    556 
    557   // Generate another click on the decoration.
    558   downEvent = Event(field_, location, NSLeftMouseDown, 1);
    559   upEvent = Event(field_, location, NSLeftMouseUp, 1);
    560   [NSApp postEvent:upEvent atStart:YES];
    561   EXPECT_CALL(mock_left_decoration_, OnMousePressed(_))
    562       .WillOnce(Return(true));
    563   [field_ mouseDown:downEvent];
    564 
    565   // The selection should not have changed.
    566   EXPECT_TRUE(NSEqualRanges(allRange, [[field_ currentEditor] selectedRange]));
    567 
    568   // TODO(shess): Test that mouse drags are initiated if the next
    569   // event is a drag, or if the mouse-up takes too long to arrive.
    570   // IDEA: mock decoration to return a pasteboard which a mock
    571   // AutocompleteTextField notes in -dragImage:*.
    572 }
    573 
    574 // Clicking a decoration should call decoration's OnMousePressed.
    575 TEST_F(AutocompleteTextFieldTest, RightDecorationMouseDown) {
    576   // At this point, not focussed.
    577   EXPECT_FALSE([field_ currentEditor]);
    578 
    579   mock_right_decoration_.SetVisible(true);
    580   EXPECT_CALL(mock_right_decoration_, AcceptsMousePress())
    581       .WillRepeatedly(Return(true));
    582 
    583   AutocompleteTextFieldCell* cell = [field_ cell];
    584   const NSRect bounds = [field_ bounds];
    585   const NSRect iconFrame =
    586       [cell frameForDecoration:&mock_right_decoration_ inFrame:bounds];
    587   const NSPoint location = NSMakePoint(NSMidX(iconFrame), NSMidY(iconFrame));
    588   NSEvent* downEvent = Event(field_, location, NSLeftMouseDown, 1);
    589   NSEvent* upEvent = Event(field_, location, NSLeftMouseUp, 1);
    590 
    591   // Since decorations can be dragged, the mouse-press is sent on
    592   // mouse-up.
    593   [NSApp postEvent:upEvent atStart:YES];
    594 
    595   EXPECT_CALL(mock_right_decoration_, OnMousePressed(_))
    596       .WillOnce(Return(true));
    597   [field_ mouseDown:downEvent];
    598 }
    599 
    600 // Test that page action menus are properly returned.
    601 // TODO(shess): Really, this should test that things are forwarded to
    602 // the cell, and the cell tests should test that the right things are
    603 // selected.  It's easier to mock the event here, though.  This code's
    604 // event-mockers might be worth promoting to |test_event_utils.h| or
    605 // |cocoa_test_helper.h|.
    606 TEST_F(AutocompleteTextFieldTest, DecorationMenu) {
    607   AutocompleteTextFieldCell* cell = [field_ cell];
    608   const NSRect bounds([field_ bounds]);
    609 
    610   const CGFloat edge = NSHeight(bounds) - 4.0;
    611   const NSSize size = NSMakeSize(edge, edge);
    612   scoped_nsobject<NSImage> image([[NSImage alloc] initWithSize:size]);
    613 
    614   scoped_nsobject<NSMenu> menu([[NSMenu alloc] initWithTitle:@"Menu"]);
    615 
    616   mock_left_decoration_.SetVisible(true);
    617   mock_right_decoration_.SetVisible(true);
    618 
    619   // The item with a menu returns it.
    620   NSRect actionFrame = [cell frameForDecoration:&mock_right_decoration_
    621                                         inFrame:bounds];
    622   NSPoint location = NSMakePoint(NSMidX(actionFrame), NSMidY(actionFrame));
    623   NSEvent* event = Event(field_, location, NSRightMouseDown, 1);
    624 
    625   // Check that the decoration is called, and the field returns the
    626   // menu.
    627   EXPECT_CALL(mock_right_decoration_, GetMenu())
    628       .WillOnce(Return(menu.get()));
    629   NSMenu *decorationMenu = [field_ decorationMenuForEvent:event];
    630   EXPECT_EQ(decorationMenu, menu);
    631 
    632   // The item without a menu returns nil.
    633   EXPECT_CALL(mock_left_decoration_, GetMenu())
    634       .WillOnce(Return(static_cast<NSMenu*>(nil)));
    635   actionFrame = [cell frameForDecoration:&mock_left_decoration_
    636                                  inFrame:bounds];
    637   location = NSMakePoint(NSMidX(actionFrame), NSMidY(actionFrame));
    638   event = Event(field_, location, NSRightMouseDown, 1);
    639   EXPECT_FALSE([field_ decorationMenuForEvent:event]);
    640 
    641   // Something not in an action returns nil.
    642   location = NSMakePoint(NSMidX(bounds), NSMidY(bounds));
    643   event = Event(field_, location, NSRightMouseDown, 1);
    644   EXPECT_FALSE([field_ decorationMenuForEvent:event]);
    645 }
    646 
    647 // Verify that -setAttributedStringValue: works as expected when
    648 // focussed or when not focussed.  Our code mostly depends on about
    649 // whether -stringValue works right.
    650 TEST_F(AutocompleteTextFieldTest, SetAttributedStringBaseline) {
    651   EXPECT_EQ(nil, [field_ currentEditor]);
    652 
    653   // So that we can set rich text.
    654   [field_ setAllowsEditingTextAttributes:YES];
    655 
    656   // Set an attribute different from the field's default so we can
    657   // tell we got the same string out as we put in.
    658   NSFont* font = [NSFont fontWithDescriptor:[[field_ font] fontDescriptor]
    659                                        size:[[field_ font] pointSize] + 2];
    660   NSDictionary* attributes =
    661       [NSDictionary dictionaryWithObject:font
    662                                   forKey:NSFontAttributeName];
    663   NSString* const kString = @"This is a test";
    664   scoped_nsobject<NSAttributedString> attributedString(
    665       [[NSAttributedString alloc] initWithString:kString
    666                                       attributes:attributes]);
    667 
    668   // Check that what we get back looks like what we put in.
    669   EXPECT_NSNE(kString, [field_ stringValue]);
    670   [field_ setAttributedStringValue:attributedString];
    671   EXPECT_TRUE([[field_ attributedStringValue]
    672                 isEqualToAttributedString:attributedString]);
    673   EXPECT_NSEQ(kString, [field_ stringValue]);
    674 
    675   // Try that again with focus.
    676   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
    677 
    678   EXPECT_TRUE([field_ currentEditor]);
    679 
    680   // Check that what we get back looks like what we put in.
    681   [field_ setStringValue:@""];
    682   EXPECT_NSNE(kString, [field_ stringValue]);
    683   [field_ setAttributedStringValue:attributedString];
    684   EXPECT_TRUE([[field_ attributedStringValue]
    685                 isEqualToAttributedString:attributedString]);
    686   EXPECT_NSEQ(kString, [field_ stringValue]);
    687 }
    688 
    689 // -setAttributedStringValue: shouldn't reset the undo state if things
    690 // are being editted.
    691 TEST_F(AutocompleteTextFieldTest, SetAttributedStringUndo) {
    692   NSColor* redColor = [NSColor redColor];
    693   NSDictionary* attributes =
    694       [NSDictionary dictionaryWithObject:redColor
    695                                   forKey:NSForegroundColorAttributeName];
    696   NSString* const kString = @"This is a test";
    697   scoped_nsobject<NSAttributedString> attributedString(
    698       [[NSAttributedString alloc] initWithString:kString
    699                                       attributes:attributes]);
    700   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
    701   EXPECT_TRUE([field_ currentEditor]);
    702   NSTextView* editor = static_cast<NSTextView*>([field_ currentEditor]);
    703   NSUndoManager* undoManager = [editor undoManager];
    704   EXPECT_TRUE(undoManager);
    705 
    706   // Nothing to undo, yet.
    707   EXPECT_FALSE([undoManager canUndo]);
    708 
    709   // Starting an editing action creates an undoable item.
    710   [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
    711   [editor didChangeText];
    712   EXPECT_TRUE([undoManager canUndo]);
    713 
    714   // -setStringValue: resets the editor's undo chain.
    715   [field_ setStringValue:kString];
    716   EXPECT_FALSE([undoManager canUndo]);
    717 
    718   // Verify that -setAttributedStringValue: does not reset the
    719   // editor's undo chain.
    720   [field_ setStringValue:@""];
    721   [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
    722   [editor didChangeText];
    723   EXPECT_TRUE([undoManager canUndo]);
    724   [field_ setAttributedStringValue:attributedString];
    725   EXPECT_TRUE([undoManager canUndo]);
    726 
    727   // Verify that calling -clearUndoChain clears the undo chain.
    728   [field_ clearUndoChain];
    729   EXPECT_FALSE([undoManager canUndo]);
    730 }
    731 
    732 TEST_F(AutocompleteTextFieldTest, EditorGetsCorrectUndoManager) {
    733   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
    734 
    735   NSTextView* editor = static_cast<NSTextView*>([field_ currentEditor]);
    736   EXPECT_TRUE(editor);
    737   EXPECT_EQ([field_ undoManagerForTextView:editor], [editor undoManager]);
    738 }
    739 
    740 TEST_F(AutocompleteTextFieldObserverTest, SendsEditingMessages) {
    741   // Many of these methods try to change the selection.
    742   EXPECT_CALL(field_observer_, SelectionRangeForProposedRange(A<NSRange>()))
    743       .WillRepeatedly(ReturnArg<0>());
    744 
    745   EXPECT_CALL(field_observer_, OnSetFocus(false));
    746   // Becoming first responder doesn't begin editing.
    747   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
    748   NSTextView* editor = static_cast<NSTextView*>([field_ currentEditor]);
    749   EXPECT_TRUE(nil != editor);
    750 
    751   // This should begin editing and indicate a change.
    752   EXPECT_CALL(field_observer_, OnDidBeginEditing());
    753   EXPECT_CALL(field_observer_, OnBeforeChange());
    754   EXPECT_CALL(field_observer_, OnDidChange());
    755   [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
    756   [editor didChangeText];
    757 
    758   // Further changes don't send the begin message.
    759   EXPECT_CALL(field_observer_, OnBeforeChange());
    760   EXPECT_CALL(field_observer_, OnDidChange());
    761   [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
    762   [editor didChangeText];
    763 
    764   // -doCommandBySelector: should forward to observer via |field_|.
    765   // TODO(shess): Test with a fake arrow-key event?
    766   const SEL cmd = @selector(moveDown:);
    767   EXPECT_CALL(field_observer_, OnDoCommandBySelector(cmd))
    768       .WillOnce(Return(true));
    769   [editor doCommandBySelector:cmd];
    770 
    771   // Finished with the changes.
    772   EXPECT_CALL(field_observer_, OnKillFocus());
    773   EXPECT_CALL(field_observer_, OnDidEndEditing());
    774   [test_window() clearPretendKeyWindowAndFirstResponder];
    775 }
    776 
    777 // Test that the resign-key notification is forwarded right, and that
    778 // the notification is registered and unregistered when the view moves
    779 // in and out of the window.
    780 // TODO(shess): Should this test the key window for realz?  That would
    781 // be really annoying to whoever is running the tests.
    782 TEST_F(AutocompleteTextFieldObserverTest, ClosePopupOnResignKey) {
    783   EXPECT_CALL(field_observer_, ClosePopup());
    784   [test_window() resignKeyWindow];
    785 
    786   scoped_nsobject<AutocompleteTextField> pin([field_ retain]);
    787   [field_ removeFromSuperview];
    788   [test_window() resignKeyWindow];
    789 
    790   [[test_window() contentView] addSubview:field_];
    791   EXPECT_CALL(field_observer_, ClosePopup());
    792   [test_window() resignKeyWindow];
    793 }
    794 
    795 }  // namespace
    796