Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / location_bar / autocomplete_text_field_unittest.mm
blob73fcb30e75e09ef6ae0d47851709e6f5304c4845
1 // Copyright (c) 2012 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.
5 #import <ApplicationServices/ApplicationServices.h>
6 #import <Cocoa/Cocoa.h>
8 #include "base/mac/foundation_util.h"
9 #include "base/mac/scoped_nsobject.h"
10 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
11 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field.h"
12 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_cell.h"
13 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor.h"
14 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_unittest_helper.h"
15 #import "chrome/browser/ui/cocoa/location_bar/location_bar_decoration.h"
16 #include "grit/theme_resources.h"
17 #include "testing/gmock/include/gmock/gmock.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #import "testing/gtest_mac.h"
20 #include "testing/platform_test.h"
22 using ::testing::A;
23 using ::testing::InSequence;
24 using ::testing::Return;
25 using ::testing::ReturnArg;
26 using ::testing::StrictMock;
27 using ::testing::_;
29 namespace {
31 class MockDecoration : public LocationBarDecoration {
32  public:
33   virtual CGFloat GetWidthForSpace(CGFloat width) { return 20.0; }
35   virtual void DrawInFrame(NSRect frame, NSView* control_view) { ; }
36   MOCK_METHOD0(AcceptsMousePress, bool());
37   MOCK_METHOD2(OnMousePressed, bool(NSRect frame, NSPoint location));
38   MOCK_METHOD0(GetMenu, NSMenu*());
41 // Mock up an incrementing event number.
42 NSUInteger eventNumber = 0;
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 // TODO(groby): This is very similar to cocoa_testing_utils - unify.
50 NSEvent* Event(NSView* view, const NSPoint point, const NSEventType type,
51                const NSUInteger clickCount) {
52   NSWindow* window([view window]);
53   const NSPoint locationInWindow([view convertPoint:point toView:nil]);
54   const NSPoint location([window convertBaseToScreen:locationInWindow]);
55   return [NSEvent mouseEventWithType:type
56                             location:location
57                        modifierFlags:0
58                            timestamp:0
59                         windowNumber:[window windowNumber]
60                              context:nil
61                          eventNumber:eventNumber++
62                           clickCount:clickCount
63                             pressure:0.0];
65 NSEvent* Event(NSView* view, const NSPoint point, const NSEventType type) {
66   return Event(view, point, type, 1);
69 // Width of the field so that we don't have to ask |field_| for it all
70 // the time.
71 static const CGFloat kWidth(300.0);
73 class AutocompleteTextFieldTest : public CocoaTest {
74  public:
75   AutocompleteTextFieldTest() {
76     // Make sure this is wide enough to play games with the cell
77     // decorations.
78     NSRect frame = NSMakeRect(0, 0, kWidth, 30);
79     base::scoped_nsobject<AutocompleteTextField> field(
80         [[AutocompleteTextField alloc] initWithFrame:frame]);
81     field_ = field.get();
82     [field_ setStringValue:@"Test test"];
83     [[test_window() contentView] addSubview:field_];
85     AutocompleteTextFieldCell* cell = [field_ cell];
86     [cell clearDecorations];
88     mock_left_decoration_.SetVisible(false);
89     [cell addLeftDecoration:&mock_left_decoration_];
91     mock_right_decoration_.SetVisible(false);
92     [cell addRightDecoration:&mock_right_decoration_];
94     window_delegate_.reset(
95         [[AutocompleteTextFieldWindowTestDelegate alloc] init]);
96     [test_window() setDelegate:window_delegate_.get()];
97   }
99   NSEvent* KeyDownEventWithFlags(NSUInteger flags) {
100     return [NSEvent keyEventWithType:NSKeyDown
101                             location:NSZeroPoint
102                        modifierFlags:flags
103                            timestamp:0.0
104                         windowNumber:[test_window() windowNumber]
105                              context:nil
106                           characters:@"a"
107          charactersIgnoringModifiers:@"a"
108                            isARepeat:NO
109                              keyCode:'a'];
110   }
112   // Helper to return the field-editor frame being used w/in |field_|.
113   NSRect EditorFrame() {
114     EXPECT_TRUE([field_ currentEditor]);
115     EXPECT_EQ([[field_ subviews] count], 1U);
116     if ([[field_ subviews] count] > 0) {
117       return [[[field_ subviews] objectAtIndex:0] frame];
118     } else {
119       // Return something which won't work so the caller can soldier
120       // on.
121       return NSZeroRect;
122     }
123   }
125   AutocompleteTextFieldEditor* FieldEditor() {
126     return base::mac::ObjCCastStrict<AutocompleteTextFieldEditor>(
127         [field_ currentEditor]);
128   }
130   AutocompleteTextField* field_;
131   MockDecoration mock_left_decoration_;
132   MockDecoration mock_right_decoration_;
133   base::scoped_nsobject<AutocompleteTextFieldWindowTestDelegate>
134       window_delegate_;
137 TEST_VIEW(AutocompleteTextFieldTest, field_);
139 // Base class for testing AutocompleteTextFieldObserver messages.
140 class AutocompleteTextFieldObserverTest : public AutocompleteTextFieldTest {
141  public:
142   virtual void SetUp() {
143     AutocompleteTextFieldTest::SetUp();
144     [field_ setObserver:&field_observer_];
145   }
147   virtual void TearDown() {
148     // Clear the observer so that we don't show output for
149     // uninteresting messages to the mock (for instance, if |field_| has
150     // focus at the end of the test).
151     [field_ setObserver:NULL];
153     AutocompleteTextFieldTest::TearDown();
154   }
156   // Returns the center point of the decoration.
157   NSPoint ClickLocationForDecoration(LocationBarDecoration* decoration) {
158     AutocompleteTextFieldCell* cell = [field_ cell];
159     NSRect decoration_rect =
160         [cell frameForDecoration:decoration inFrame:[field_ bounds]];
161     EXPECT_FALSE(NSIsEmptyRect(decoration_rect));
162     return NSMakePoint(NSMidX(decoration_rect), NSMidY(decoration_rect));
163   }
165   void SendMouseClickToDecoration(LocationBarDecoration* decoration) {
166     NSPoint point = ClickLocationForDecoration(decoration);
167     NSEvent* downEvent = Event(field_, point, NSLeftMouseDown);
168     NSEvent* upEvent = Event(field_, point, NSLeftMouseUp);
170     // Can't just use -sendEvent:, since that doesn't populate -currentEvent.
171     [NSApp postEvent:downEvent atStart:YES];
172     [NSApp postEvent:upEvent atStart:NO];
174     NSEvent* next_event = [NSApp nextEventMatchingMask:NSAnyEventMask
175                                              untilDate:nil
176                                                 inMode:NSDefaultRunLoopMode
177                                                dequeue:YES];
178     [NSApp sendEvent:next_event];
179   }
181   StrictMock<MockAutocompleteTextFieldObserver> field_observer_;
184 // Test that we have the right cell class.
185 TEST_F(AutocompleteTextFieldTest, CellClass) {
186   EXPECT_TRUE([[field_ cell] isKindOfClass:[AutocompleteTextFieldCell class]]);
189 // Test that becoming first responder sets things up correctly.
190 TEST_F(AutocompleteTextFieldTest, FirstResponder) {
191   EXPECT_EQ(nil, [field_ currentEditor]);
192   EXPECT_EQ([[field_ subviews] count], 0U);
193   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
194   EXPECT_FALSE(nil == [field_ currentEditor]);
195   EXPECT_EQ([[field_ subviews] count], 1U);
196   EXPECT_TRUE([[field_ currentEditor] isDescendantOf:field_]);
198   // Check that the window delegate is providing the right editor.
199   Class c = [AutocompleteTextFieldEditor class];
200   EXPECT_TRUE([[field_ currentEditor] isKindOfClass:c]);
203 TEST_F(AutocompleteTextFieldTest, AvailableDecorationWidth) {
204   // A fudge factor to account for how much space the border takes up.
205   // The test shouldn't be too dependent on the field's internals, but
206   // it also shouldn't let deranged cases fall through the cracks
207   // (like nothing available with no text, or everything available
208   // with some text).
209   const CGFloat kBorderWidth = 20.0;
211   // With no contents, almost the entire width is available for
212   // decorations.
213   [field_ setStringValue:@""];
214   CGFloat availableWidth = [field_ availableDecorationWidth];
215   EXPECT_LE(availableWidth, kWidth);
216   EXPECT_GT(availableWidth, kWidth - kBorderWidth);
218   // With minor contents, most of the remaining width is available for
219   // decorations.
220   NSDictionary* attributes =
221       [NSDictionary dictionaryWithObject:[field_ font]
222                                   forKey:NSFontAttributeName];
223   NSString* string = @"Hello world";
224   const NSSize size([string sizeWithAttributes:attributes]);
225   [field_ setStringValue:string];
226   availableWidth = [field_ availableDecorationWidth];
227   EXPECT_LE(availableWidth, kWidth - size.width);
228   EXPECT_GT(availableWidth, kWidth - size.width - kBorderWidth);
230   // With huge contents, nothing at all is left for decorations.
231   string = @"A long string which is surely wider than field_ can hold.";
232   [field_ setStringValue:string];
233   availableWidth = [field_ availableDecorationWidth];
234   EXPECT_LT(availableWidth, 0.0);
237 // Test drawing, mostly to ensure nothing leaks or crashes.
238 TEST_F(AutocompleteTextFieldTest, Display) {
239   [field_ display];
241   // Test focussed drawing.
242   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
243   [field_ display];
246 // Test setting gray text, mostly to ensure nothing leaks or crashes.
247 TEST_F(AutocompleteTextFieldTest, GrayText) {
248   [field_ display];
249   EXPECT_FALSE([field_ needsDisplay]);
250   [field_ setGrayTextAutocompletion:@"foo" textColor:[NSColor redColor]];
251   EXPECT_TRUE([field_ needsDisplay]);
252   [field_ display];
255 TEST_F(AutocompleteTextFieldObserverTest, FlagsChanged) {
256   InSequence dummy;  // Call mock in exactly the order specified.
258   // Test without Control key down, but some other modifier down.
259   EXPECT_CALL(field_observer_, OnControlKeyChanged(false));
260   [field_ flagsChanged:KeyDownEventWithFlags(NSShiftKeyMask)];
262   // Test with Control key down.
263   EXPECT_CALL(field_observer_, OnControlKeyChanged(true));
264   [field_ flagsChanged:KeyDownEventWithFlags(NSControlKeyMask)];
267 // This test is here rather than in the editor's tests because the
268 // field catches -flagsChanged: because it's on the responder chain,
269 // the field editor doesn't implement it.
270 TEST_F(AutocompleteTextFieldObserverTest, FieldEditorFlagsChanged) {
271   // Many of these methods try to change the selection.
272   EXPECT_CALL(field_observer_, SelectionRangeForProposedRange(A<NSRange>()))
273       .WillRepeatedly(ReturnArg<0>());
275   InSequence dummy;  // Call mock in exactly the order specified.
276   EXPECT_CALL(field_observer_, OnSetFocus(false));
277   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
278   NSResponder* firstResponder = [[field_ window] firstResponder];
279   EXPECT_EQ(firstResponder, [field_ currentEditor]);
281   // Test without Control key down, but some other modifier down.
282   EXPECT_CALL(field_observer_, OnControlKeyChanged(false));
283   [firstResponder flagsChanged:KeyDownEventWithFlags(NSShiftKeyMask)];
285   // Test with Control key down.
286   EXPECT_CALL(field_observer_, OnControlKeyChanged(true));
287   [firstResponder flagsChanged:KeyDownEventWithFlags(NSControlKeyMask)];
290 // Frame size changes are propagated to |observer_|.
291 TEST_F(AutocompleteTextFieldObserverTest, FrameChanged) {
292   EXPECT_CALL(field_observer_, OnFrameChanged());
293   NSRect frame = [field_ frame];
294   frame.size.width += 10.0;
295   [field_ setFrame:frame];
298 // Test that the field editor gets the same bounds when focus is
299 // delivered by the standard focusing machinery, or by
300 // -resetFieldEditorFrameIfNeeded.
301 TEST_F(AutocompleteTextFieldTest, ResetFieldEditorBase) {
302   // Capture the editor frame resulting from the standard focus
303   // machinery.
304   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
305   const NSRect baseEditorFrame = EditorFrame();
307   // A decoration should result in a strictly smaller editor frame.
308   mock_left_decoration_.SetVisible(true);
309   [field_ resetFieldEditorFrameIfNeeded];
310   EXPECT_NSNE(baseEditorFrame, EditorFrame());
311   EXPECT_TRUE(NSContainsRect(baseEditorFrame, EditorFrame()));
313   // Removing the decoration and using -resetFieldEditorFrameIfNeeded
314   // should result in the same frame as the standard focus machinery.
315   mock_left_decoration_.SetVisible(false);
316   [field_ resetFieldEditorFrameIfNeeded];
317   EXPECT_NSEQ(baseEditorFrame, EditorFrame());
320 // Test that the field editor gets the same bounds when focus is
321 // delivered by the standard focusing machinery, or by
322 // -resetFieldEditorFrameIfNeeded, this time with a decoration
323 // pre-loaded.
324 TEST_F(AutocompleteTextFieldTest, ResetFieldEditorWithDecoration) {
325   AutocompleteTextFieldCell* cell = [field_ cell];
327   // Make sure decoration isn't already visible, then make it visible.
328   EXPECT_TRUE(NSIsEmptyRect([cell frameForDecoration:&mock_left_decoration_
329                                              inFrame:[field_ bounds]]));
330   mock_left_decoration_.SetVisible(true);
331   EXPECT_FALSE(NSIsEmptyRect([cell frameForDecoration:&mock_left_decoration_
332                                               inFrame:[field_ bounds]]));
334   // Capture the editor frame resulting from the standard focus
335   // machinery.
337   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
338   const NSRect baseEditorFrame = EditorFrame();
340   // When the decoration is not visible the frame should be strictly larger.
341   mock_left_decoration_.SetVisible(false);
342   EXPECT_TRUE(NSIsEmptyRect([cell frameForDecoration:&mock_left_decoration_
343                                              inFrame:[field_ bounds]]));
344   [field_ resetFieldEditorFrameIfNeeded];
345   EXPECT_NSNE(baseEditorFrame, EditorFrame());
346   EXPECT_TRUE(NSContainsRect(EditorFrame(), baseEditorFrame));
348   // When the decoration is visible, -resetFieldEditorFrameIfNeeded
349   // should result in the same frame as the standard focus machinery.
350   mock_left_decoration_.SetVisible(true);
351   EXPECT_FALSE(NSIsEmptyRect([cell frameForDecoration:&mock_left_decoration_
352                                               inFrame:[field_ bounds]]));
354   [field_ resetFieldEditorFrameIfNeeded];
355   EXPECT_NSEQ(baseEditorFrame, EditorFrame());
358 // Test that resetting the field editor bounds does not cause untoward
359 // messages to the field's observer.
360 TEST_F(AutocompleteTextFieldObserverTest, ResetFieldEditorContinuesEditing) {
361   // Many of these methods try to change the selection.
362   EXPECT_CALL(field_observer_, SelectionRangeForProposedRange(A<NSRange>()))
363       .WillRepeatedly(ReturnArg<0>());
365   EXPECT_CALL(field_observer_, OnSetFocus(false));
366   // Becoming first responder doesn't begin editing.
367   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
368   const NSRect baseEditorFrame = EditorFrame();
369   NSTextView* editor = static_cast<NSTextView*>([field_ currentEditor]);
370   EXPECT_TRUE(nil != editor);
372   // This should begin editing and indicate a change.
373   EXPECT_CALL(field_observer_, OnDidBeginEditing());
374   EXPECT_CALL(field_observer_, OnBeforeChange());
375   EXPECT_CALL(field_observer_, OnDidChange());
376   [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
377   [editor didChangeText];
379   // No messages to |field_observer_| when the frame actually changes.
380   mock_left_decoration_.SetVisible(true);
381   [field_ resetFieldEditorFrameIfNeeded];
382   EXPECT_NSNE(baseEditorFrame, EditorFrame());
385 // Clicking in a right-hand decoration which does not handle the mouse
386 // puts the caret rightmost.
387 TEST_F(AutocompleteTextFieldTest, ClickRightDecorationPutsCaretRightmost) {
388   // Decoration does not handle the mouse event, so the cell should
389   // process it.  Called at least once.
390   EXPECT_CALL(mock_right_decoration_, AcceptsMousePress())
391       .WillOnce(Return(false))
392       .WillRepeatedly(Return(false));
394   // Set the decoration before becoming responder.
395   EXPECT_FALSE([field_ currentEditor]);
396   mock_right_decoration_.SetVisible(true);
398   // Make first responder should select all.
399   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
400   EXPECT_TRUE([field_ currentEditor]);
401   const NSRange allRange = NSMakeRange(0, [[field_ stringValue] length]);
402   EXPECT_TRUE(NSEqualRanges(allRange, [[field_ currentEditor] selectedRange]));
404   // Generate a click on the decoration.
405   AutocompleteTextFieldCell* cell = [field_ cell];
406   const NSRect bounds = [field_ bounds];
407   const NSRect iconFrame =
408       [cell frameForDecoration:&mock_right_decoration_ inFrame:bounds];
409   const NSPoint point = NSMakePoint(NSMidX(iconFrame), NSMidY(iconFrame));
410   NSEvent* downEvent = Event(field_, point, NSLeftMouseDown);
411   NSEvent* upEvent = Event(field_, point, NSLeftMouseUp);
412   [NSApp postEvent:upEvent atStart:YES];
413   [field_ mouseDown:downEvent];
415   // Selection should be a right-hand-side caret.
416   EXPECT_TRUE(NSEqualRanges(NSMakeRange([[field_ stringValue] length], 0),
417                             [[field_ currentEditor] selectedRange]));
420 // Clicking in a left-side decoration which doesn't handle the event
421 // puts the selection in the leftmost position.
422 TEST_F(AutocompleteTextFieldTest, ClickLeftDecorationPutsCaretLeftmost) {
423   // Decoration does not handle the mouse event, so the cell should
424   // process it.  Called at least once.
425   EXPECT_CALL(mock_left_decoration_, AcceptsMousePress())
426       .WillOnce(Return(false))
427       .WillRepeatedly(Return(false));
429   // Set the decoration before becoming responder.
430   EXPECT_FALSE([field_ currentEditor]);
431   mock_left_decoration_.SetVisible(true);
433   // Make first responder should select all.
434   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
435   EXPECT_TRUE([field_ currentEditor]);
436   const NSRange allRange = NSMakeRange(0, [[field_ stringValue] length]);
437   EXPECT_TRUE(NSEqualRanges(allRange, [[field_ currentEditor] selectedRange]));
439   // Generate a click on the decoration.
440   AutocompleteTextFieldCell* cell = [field_ cell];
441   const NSRect bounds = [field_ bounds];
442   const NSRect iconFrame =
443       [cell frameForDecoration:&mock_left_decoration_ inFrame:bounds];
444   const NSPoint point = NSMakePoint(NSMidX(iconFrame), NSMidY(iconFrame));
445   NSEvent* downEvent = Event(field_, point, NSLeftMouseDown);
446   NSEvent* upEvent = Event(field_, point, NSLeftMouseUp);
447   [NSApp postEvent:upEvent atStart:YES];
448   [field_ mouseDown:downEvent];
450   // Selection should be a left-hand-side caret.
451   EXPECT_TRUE(NSEqualRanges(NSMakeRange(0, 0),
452                             [[field_ currentEditor] selectedRange]));
455 // Clicks not in the text area or the cell's decorations fall through
456 // to the editor.
457 TEST_F(AutocompleteTextFieldTest, ClickBorderSelectsAll) {
458   // Can't rely on the window machinery to make us first responder,
459   // here.
460   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
461   EXPECT_TRUE([field_ currentEditor]);
463   const NSPoint point(NSMakePoint(20.0, 1.0));
464   NSEvent* downEvent(Event(field_, point, NSLeftMouseDown));
465   NSEvent* upEvent(Event(field_, point, NSLeftMouseUp));
466   [NSApp postEvent:upEvent atStart:YES];
467   [field_ mouseDown:downEvent];
469   // Clicking in the narrow border area around a Cocoa NSTextField
470   // does a select-all.  Regardless of whether this is a good call, it
471   // works as a test that things get passed down to the editor.
472   const NSRange selectedRange([[field_ currentEditor] selectedRange]);
473   EXPECT_EQ(selectedRange.location, 0U);
474   EXPECT_EQ(selectedRange.length, [[field_ stringValue] length]);
477 // Single-click with no drag should setup a field editor and
478 // select all.
479 TEST_F(AutocompleteTextFieldTest, ClickSelectsAll) {
480   EXPECT_FALSE([field_ currentEditor]);
482   const NSPoint point = NSMakePoint(20.0, NSMidY([field_ bounds]));
483   NSEvent* downEvent(Event(field_, point, NSLeftMouseDown));
484   NSEvent* upEvent(Event(field_, point, NSLeftMouseUp));
485   [NSApp postEvent:upEvent atStart:YES];
486   [field_ mouseDown:downEvent];
487   EXPECT_TRUE([field_ currentEditor]);
488   const NSRange selectedRange([[field_ currentEditor] selectedRange]);
489   EXPECT_EQ(selectedRange.location, 0U);
490   EXPECT_EQ(selectedRange.length, [[field_ stringValue] length]);
493 // Click-drag selects text, not select all.
494 TEST_F(AutocompleteTextFieldTest, ClickDragSelectsText) {
495   EXPECT_FALSE([field_ currentEditor]);
497   NSEvent* downEvent(Event(field_, NSMakePoint(20.0, 5.0), NSLeftMouseDown));
498   NSEvent* upEvent(Event(field_, NSMakePoint(0.0, 5.0), NSLeftMouseUp));
499   [NSApp postEvent:upEvent atStart:YES];
500   [field_ mouseDown:downEvent];
501   EXPECT_TRUE([field_ currentEditor]);
503   // Expect this to have selected a prefix of the content.  Mostly
504   // just don't want the select-all behavior.
505   const NSRange selectedRange([[field_ currentEditor] selectedRange]);
506   EXPECT_EQ(selectedRange.location, 0U);
507   EXPECT_LT(selectedRange.length, [[field_ stringValue] length]);
510 // TODO(shess): Test that click/pause/click allows cursor placement.
511 // In this case the first click goes to the field, but the second
512 // click goes to the field editor, so the current testing pattern
513 // can't work.  What really needs to happen is to push through the
514 // NSWindow event machinery so that we can say "two independent clicks
515 // at the same location have the right effect".  Once that is done, it
516 // might make sense to revise the other tests to use the same
517 // machinery.
519 // Double-click selects word, not select all.
520 TEST_F(AutocompleteTextFieldTest, DoubleClickSelectsWord) {
521   EXPECT_FALSE([field_ currentEditor]);
523   const NSPoint point = NSMakePoint(20.0, NSMidY([field_ bounds]));
524   NSEvent* downEvent(Event(field_, point, NSLeftMouseDown, 1));
525   NSEvent* upEvent(Event(field_, point, NSLeftMouseUp, 1));
526   NSEvent* downEvent2(Event(field_, point, NSLeftMouseDown, 2));
527   NSEvent* upEvent2(Event(field_, point, NSLeftMouseUp, 2));
528   [NSApp postEvent:upEvent atStart:YES];
529   [field_ mouseDown:downEvent];
530   [NSApp postEvent:upEvent2 atStart:YES];
531   [field_ mouseDown:downEvent2];
532   EXPECT_TRUE([field_ currentEditor]);
534   // Selected the first word.
535   const NSRange selectedRange([[field_ currentEditor] selectedRange]);
536   const NSRange spaceRange([[field_ stringValue] rangeOfString:@" "]);
537   EXPECT_GT(spaceRange.location, 0U);
538   EXPECT_LT(spaceRange.length, [[field_ stringValue] length]);
539   EXPECT_EQ(selectedRange.location, 0U);
540   EXPECT_EQ(selectedRange.length, spaceRange.location);
543 TEST_F(AutocompleteTextFieldTest, TripleClickSelectsAll) {
544   EXPECT_FALSE([field_ currentEditor]);
546   const NSPoint point(NSMakePoint(20.0, 5.0));
547   NSEvent* downEvent(Event(field_, point, NSLeftMouseDown, 1));
548   NSEvent* upEvent(Event(field_, point, NSLeftMouseUp, 1));
549   NSEvent* downEvent2(Event(field_, point, NSLeftMouseDown, 2));
550   NSEvent* upEvent2(Event(field_, point, NSLeftMouseUp, 2));
551   NSEvent* downEvent3(Event(field_, point, NSLeftMouseDown, 3));
552   NSEvent* upEvent3(Event(field_, point, NSLeftMouseUp, 3));
553   [NSApp postEvent:upEvent atStart:YES];
554   [field_ mouseDown:downEvent];
555   [NSApp postEvent:upEvent2 atStart:YES];
556   [field_ mouseDown:downEvent2];
557   [NSApp postEvent:upEvent3 atStart:YES];
558   [field_ mouseDown:downEvent3];
559   EXPECT_TRUE([field_ currentEditor]);
561   // Selected the first word.
562   const NSRange selectedRange([[field_ currentEditor] selectedRange]);
563   EXPECT_EQ(selectedRange.location, 0U);
564   EXPECT_EQ(selectedRange.length, [[field_ stringValue] length]);
567 // Clicking a decoration should call decoration's OnMousePressed.
568 TEST_F(AutocompleteTextFieldTest, LeftDecorationMouseDown) {
569   // At this point, not focussed.
570   EXPECT_FALSE([field_ currentEditor]);
572   mock_left_decoration_.SetVisible(true);
573   EXPECT_CALL(mock_left_decoration_, AcceptsMousePress())
574       .WillRepeatedly(Return(true));
576   AutocompleteTextFieldCell* cell = [field_ cell];
577   const NSRect iconFrame =
578       [cell frameForDecoration:&mock_left_decoration_ inFrame:[field_ bounds]];
579   const NSPoint location = NSMakePoint(NSMidX(iconFrame), NSMidY(iconFrame));
580   NSEvent* downEvent = Event(field_, location, NSLeftMouseDown, 1);
581   NSEvent* upEvent = Event(field_, location, NSLeftMouseUp, 1);
583   // Since decorations can be dragged, the mouse-press is sent on
584   // mouse-up.
585   [NSApp postEvent:upEvent atStart:YES];
587   EXPECT_CALL(mock_left_decoration_, OnMousePressed(_, _))
588       .WillOnce(Return(true));
589   [field_ mouseDown:downEvent];
591   // Focus the field and test that handled clicks don't affect selection.
592   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
593   EXPECT_TRUE([field_ currentEditor]);
594   const NSRange allRange = NSMakeRange(0, [[field_ stringValue] length]);
595   EXPECT_TRUE(NSEqualRanges(allRange, [[field_ currentEditor] selectedRange]));
597   // Generate another click on the decoration.
598   downEvent = Event(field_, location, NSLeftMouseDown, 1);
599   upEvent = Event(field_, location, NSLeftMouseUp, 1);
600   [NSApp postEvent:upEvent atStart:YES];
601   EXPECT_CALL(mock_left_decoration_, OnMousePressed(_, _))
602       .WillOnce(Return(true));
603   [field_ mouseDown:downEvent];
605   // The selection should not have changed.
606   EXPECT_TRUE(NSEqualRanges(allRange, [[field_ currentEditor] selectedRange]));
608   // TODO(shess): Test that mouse drags are initiated if the next
609   // event is a drag, or if the mouse-up takes too long to arrive.
610   // IDEA: mock decoration to return a pasteboard which a mock
611   // AutocompleteTextField notes in -dragImage:*.
614 // Clicking a decoration should call decoration's OnMousePressed.
615 TEST_F(AutocompleteTextFieldTest, RightDecorationMouseDown) {
616   // At this point, not focussed.
617   EXPECT_FALSE([field_ currentEditor]);
619   mock_right_decoration_.SetVisible(true);
620   EXPECT_CALL(mock_right_decoration_, AcceptsMousePress())
621       .WillRepeatedly(Return(true));
623   AutocompleteTextFieldCell* cell = [field_ cell];
624   const NSRect bounds = [field_ bounds];
625   const NSRect iconFrame =
626       [cell frameForDecoration:&mock_right_decoration_ inFrame:bounds];
627   const NSPoint location = NSMakePoint(NSMidX(iconFrame), NSMidY(iconFrame));
628   NSEvent* downEvent = Event(field_, location, NSLeftMouseDown, 1);
629   NSEvent* upEvent = Event(field_, location, NSLeftMouseUp, 1);
631   // Since decorations can be dragged, the mouse-press is sent on
632   // mouse-up.
633   [NSApp postEvent:upEvent atStart:YES];
635   EXPECT_CALL(mock_right_decoration_, OnMousePressed(_, _))
636       .WillOnce(Return(true));
637   [field_ mouseDown:downEvent];
640 // Test that page action menus are properly returned.
641 // TODO(shess): Really, this should test that things are forwarded to
642 // the cell, and the cell tests should test that the right things are
643 // selected.  It's easier to mock the event here, though.  This code's
644 // event-mockers might be worth promoting to |cocoa_test_event_utils.h| or
645 // |cocoa_test_helper.h|.
646 TEST_F(AutocompleteTextFieldTest, DecorationMenu) {
647   AutocompleteTextFieldCell* cell = [field_ cell];
648   const NSRect bounds([field_ bounds]);
650   const CGFloat edge = NSHeight(bounds) - 4.0;
651   const NSSize size = NSMakeSize(edge, edge);
652   base::scoped_nsobject<NSImage> image([[NSImage alloc] initWithSize:size]);
654   base::scoped_nsobject<NSMenu> menu([[NSMenu alloc] initWithTitle:@"Menu"]);
656   mock_left_decoration_.SetVisible(true);
657   mock_right_decoration_.SetVisible(true);
659   // The item with a menu returns it.
660   NSRect actionFrame = [cell frameForDecoration:&mock_right_decoration_
661                                         inFrame:bounds];
662   NSPoint location = NSMakePoint(NSMidX(actionFrame), NSMidY(actionFrame));
663   NSEvent* event = Event(field_, location, NSRightMouseDown, 1);
665   // Check that the decoration is called, and the field returns the
666   // menu.
667   EXPECT_CALL(mock_right_decoration_, GetMenu())
668       .WillOnce(Return(menu.get()));
669   NSMenu *decorationMenu = [field_ decorationMenuForEvent:event];
670   EXPECT_EQ(decorationMenu, menu);
672   // The item without a menu returns nil.
673   EXPECT_CALL(mock_left_decoration_, GetMenu())
674       .WillOnce(Return(static_cast<NSMenu*>(nil)));
675   actionFrame = [cell frameForDecoration:&mock_left_decoration_
676                                  inFrame:bounds];
677   location = NSMakePoint(NSMidX(actionFrame), NSMidY(actionFrame));
678   event = Event(field_, location, NSRightMouseDown, 1);
679   EXPECT_FALSE([field_ decorationMenuForEvent:event]);
681   // Something not in an action returns nil.
682   location = NSMakePoint(NSMidX(bounds), NSMidY(bounds));
683   event = Event(field_, location, NSRightMouseDown, 1);
684   EXPECT_FALSE([field_ decorationMenuForEvent:event]);
687 // Verify that -setAttributedStringValue: works as expected when
688 // focussed or when not focussed.  Our code mostly depends on about
689 // whether -stringValue works right.
690 TEST_F(AutocompleteTextFieldTest, SetAttributedStringBaseline) {
691   EXPECT_EQ(nil, [field_ currentEditor]);
693   // So that we can set rich text.
694   [field_ setAllowsEditingTextAttributes:YES];
696   // Set an attribute different from the field's default so we can
697   // tell we got the same string out as we put in.
698   NSFont* font = [NSFont fontWithDescriptor:[[field_ font] fontDescriptor]
699                                        size:[[field_ font] pointSize] + 2];
700   NSDictionary* attributes =
701       [NSDictionary dictionaryWithObject:font
702                                   forKey:NSFontAttributeName];
703   NSString* const kString = @"This is a test";
704   base::scoped_nsobject<NSAttributedString> attributedString(
705       [[NSAttributedString alloc] initWithString:kString
706                                       attributes:attributes]);
708   // Check that what we get back looks like what we put in.
709   EXPECT_NSNE(kString, [field_ stringValue]);
710   [field_ setAttributedStringValue:attributedString];
711   EXPECT_TRUE([[field_ attributedStringValue]
712                 isEqualToAttributedString:attributedString]);
713   EXPECT_NSEQ(kString, [field_ stringValue]);
715   // Try that again with focus.
716   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
718   EXPECT_TRUE([field_ currentEditor]);
720   // Check that what we get back looks like what we put in.
721   [field_ setStringValue:@""];
722   EXPECT_NSNE(kString, [field_ stringValue]);
723   [field_ setAttributedStringValue:attributedString];
724   EXPECT_TRUE([[field_ attributedStringValue]
725                 isEqualToAttributedString:attributedString]);
726   EXPECT_NSEQ(kString, [field_ stringValue]);
729 // -setAttributedStringValue: shouldn't reset the undo state if things
730 // are being editted.
731 TEST_F(AutocompleteTextFieldTest, SetAttributedStringUndo) {
732   NSColor* redColor = [NSColor redColor];
733   NSDictionary* attributes =
734       [NSDictionary dictionaryWithObject:redColor
735                                   forKey:NSForegroundColorAttributeName];
736   NSString* const kString = @"This is a test";
737   base::scoped_nsobject<NSAttributedString> attributedString(
738       [[NSAttributedString alloc] initWithString:kString
739                                       attributes:attributes]);
740   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
741   EXPECT_TRUE([field_ currentEditor]);
742   NSTextView* editor = static_cast<NSTextView*>([field_ currentEditor]);
743   NSUndoManager* undoManager = [editor undoManager];
744   EXPECT_TRUE(undoManager);
746   // Nothing to undo, yet.
747   EXPECT_FALSE([undoManager canUndo]);
749   // Starting an editing action creates an undoable item.
750   [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
751   [editor didChangeText];
752   EXPECT_TRUE([undoManager canUndo]);
754   // -setStringValue: resets the editor's undo chain.
755   [field_ setStringValue:kString];
756   EXPECT_FALSE([undoManager canUndo]);
758   // Verify that -setAttributedStringValue: does not reset the
759   // editor's undo chain.
760   [field_ setStringValue:@""];
761   [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
762   [editor didChangeText];
763   EXPECT_TRUE([undoManager canUndo]);
764   [field_ setAttributedStringValue:attributedString];
765   EXPECT_TRUE([undoManager canUndo]);
767   // Verify that calling -clearUndoChain clears the undo chain.
768   [field_ clearUndoChain];
769   EXPECT_FALSE([undoManager canUndo]);
772 TEST_F(AutocompleteTextFieldTest, EditorGetsCorrectUndoManager) {
773   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
775   NSTextView* editor = static_cast<NSTextView*>([field_ currentEditor]);
776   EXPECT_TRUE(editor);
777   EXPECT_EQ([field_ undoManagerForTextView:editor], [editor undoManager]);
780 // Verify that hideFocusState correctly hides the focus ring and insertion
781 // pointer.
782 TEST_F(AutocompleteTextFieldTest, HideFocusState) {
783   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
784   [[field_ cell] setShowsFirstResponder:YES];
786   EXPECT_TRUE([[field_ cell] showsFirstResponder]);
787   EXPECT_TRUE([FieldEditor() shouldDrawInsertionPoint]);
789   [[field_ cell] setHideFocusState:YES
790                             ofView:field_];
791   EXPECT_FALSE([[field_ cell] showsFirstResponder]);
792   EXPECT_FALSE([FieldEditor() shouldDrawInsertionPoint]);
794   [[field_ cell] setHideFocusState:NO
795                             ofView:field_];
796   EXPECT_TRUE([[field_ cell] showsFirstResponder]);
797   EXPECT_TRUE([FieldEditor() shouldDrawInsertionPoint]);
800 // Verify that clicking a decoration that accepts mouse clicks does not focus
801 // the Omnibox.
802 TEST_F(AutocompleteTextFieldObserverTest,
803        ClickingDecorationDoesNotFocusOmnibox) {
804   AutocompleteTextFieldCell* cell = [field_ cell];
806   // Set up a non-interactive decoration.
807   MockDecoration noninteractive_decoration;
808   noninteractive_decoration.SetVisible(true);
809   EXPECT_CALL(noninteractive_decoration, AcceptsMousePress())
810       .WillRepeatedly(testing::Return(false));
811   [cell addLeftDecoration:&noninteractive_decoration];
813   // Set up an interactive decoration.
814   MockDecoration interactive_decoration;
815   EXPECT_CALL(interactive_decoration, AcceptsMousePress())
816       .WillRepeatedly(testing::Return(true));
817   interactive_decoration.SetVisible(true);
818   [cell addLeftDecoration:&interactive_decoration];
819   EXPECT_CALL(interactive_decoration, OnMousePressed(_, _))
820       .WillRepeatedly(testing::Return(true));
822   // Ignore incidental calls. The exact frequency of these calls doesn't matter
823   // as they are auxiliary.
824   EXPECT_CALL(field_observer_, SelectionRangeForProposedRange(_))
825       .WillRepeatedly(testing::Return(NSMakeRange(0, 0)));
826   EXPECT_CALL(field_observer_, OnMouseDown(_)).Times(testing::AnyNumber());
827   EXPECT_CALL(field_observer_, OnSetFocus(false)).Times(testing::AnyNumber());
828   EXPECT_CALL(field_observer_, OnKillFocus()).Times(testing::AnyNumber());
829   EXPECT_CALL(field_observer_, OnDidEndEditing()).Times(testing::AnyNumber());
830   EXPECT_CALL(field_observer_, OnDidDrawRect()).Times(testing::AnyNumber());
832   // Ensure the field is currently not first responder.
833   [test_window() makePretendKeyWindowAndSetFirstResponder:nil];
834   NSResponder* firstResponder = [[field_ window] firstResponder];
835   EXPECT_FALSE(
836       [base::mac::ObjCCast<NSView>(firstResponder) isDescendantOf:field_]);
838   // Clicking an interactive decoration doesn't change the first responder.
839   SendMouseClickToDecoration(&interactive_decoration);
840   EXPECT_NSEQ(firstResponder, [[field_ window] firstResponder]);
842   // Clicking a non-interactive decoration focuses the Omnibox.
843   SendMouseClickToDecoration(&noninteractive_decoration);
844   firstResponder = [[field_ window] firstResponder];
845   EXPECT_TRUE(
846       [base::mac::ObjCCast<NSView>(firstResponder) isDescendantOf:field_]);
848   // Clicking an interactive decoration doesn't change the first responder.
849   SendMouseClickToDecoration(&interactive_decoration);
850   EXPECT_NSEQ(firstResponder, [[field_ window] firstResponder]);
853 TEST_F(AutocompleteTextFieldObserverTest, SendsEditingMessages) {
854   // Many of these methods try to change the selection.
855   EXPECT_CALL(field_observer_, SelectionRangeForProposedRange(A<NSRange>()))
856       .WillRepeatedly(ReturnArg<0>());
858   EXPECT_CALL(field_observer_, OnSetFocus(false));
859   // Becoming first responder doesn't begin editing.
860   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
861   NSTextView* editor = static_cast<NSTextView*>([field_ currentEditor]);
862   EXPECT_TRUE(nil != editor);
864   // This should begin editing and indicate a change.
865   EXPECT_CALL(field_observer_, OnDidBeginEditing());
866   EXPECT_CALL(field_observer_, OnBeforeChange());
867   EXPECT_CALL(field_observer_, OnDidChange());
868   [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
869   [editor didChangeText];
871   // Further changes don't send the begin message.
872   EXPECT_CALL(field_observer_, OnBeforeChange());
873   EXPECT_CALL(field_observer_, OnDidChange());
874   [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
875   [editor didChangeText];
877   // -doCommandBySelector: should forward to observer via |field_|.
878   // TODO(shess): Test with a fake arrow-key event?
879   const SEL cmd = @selector(moveDown:);
880   EXPECT_CALL(field_observer_, OnDoCommandBySelector(cmd))
881       .WillOnce(Return(true));
882   [editor doCommandBySelector:cmd];
884   // Finished with the changes.
885   EXPECT_CALL(field_observer_, OnKillFocus());
886   EXPECT_CALL(field_observer_, OnDidEndEditing());
887   [test_window() clearPretendKeyWindowAndFirstResponder];
890 // Test that the resign-key notification is forwarded right, and that
891 // the notification is registered and unregistered when the view moves
892 // in and out of the window.
893 // TODO(shess): Should this test the key window for realz?  That would
894 // be really annoying to whoever is running the tests.
895 TEST_F(AutocompleteTextFieldObserverTest, ClosePopupOnResignKey) {
896   EXPECT_CALL(field_observer_, ClosePopup());
897   [test_window() resignKeyWindow];
899   base::scoped_nsobject<AutocompleteTextField> pin([field_ retain]);
900   [field_ removeFromSuperview];
901   [test_window() resignKeyWindow];
903   [[test_window() contentView] addSubview:field_];
904   EXPECT_CALL(field_observer_, ClosePopup());
905   [test_window() resignKeyWindow];
908 }  // namespace