[Metrics] Make MetricsStateManager take a callback param to check if UMA is enabled.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / location_bar / autocomplete_text_field_unittest.mm
blobde117368dc04e6dd4dd0b5eb48d13f9da08b7a3d
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/button_decoration.h"
16 #import "chrome/browser/ui/cocoa/location_bar/location_bar_decoration.h"
17 #include "grit/theme_resources.h"
18 #include "testing/gmock/include/gmock/gmock.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20 #import "testing/gtest_mac.h"
21 #include "testing/platform_test.h"
22 #include "ui/base/resource/resource_bundle.h"
24 using ::testing::A;
25 using ::testing::InSequence;
26 using ::testing::Return;
27 using ::testing::ReturnArg;
28 using ::testing::StrictMock;
29 using ::testing::_;
31 namespace {
33 class MockDecoration : public LocationBarDecoration {
34  public:
35   virtual CGFloat GetWidthForSpace(CGFloat width) { return 20.0; }
37   virtual void DrawInFrame(NSRect frame, NSView* control_view) { ; }
38   MOCK_METHOD0(AcceptsMousePress, bool());
39   MOCK_METHOD2(OnMousePressed, bool(NSRect frame, NSPoint location));
40   MOCK_METHOD0(GetMenu, NSMenu*());
43 class MockButtonDecoration : public ButtonDecoration {
44  public:
45   MockButtonDecoration()
46       : ButtonDecoration(IMAGE_GRID(IDR_OMNIBOX_SEARCH_BUTTON),
47                          IDR_OMNIBOX_SEARCH_BUTTON_LOUPE,
48                          IMAGE_GRID(IDR_OMNIBOX_SEARCH_BUTTON_HOVER),
49                          IDR_OMNIBOX_SEARCH_BUTTON_LOUPE,
50                          IMAGE_GRID(IDR_OMNIBOX_SEARCH_BUTTON_PRESSED),
51                          IDR_OMNIBOX_SEARCH_BUTTON_LOUPE,
52                          3) {}
53   void Hide() { SetVisible(false); }
54   MOCK_METHOD2(OnMousePressed, bool(NSRect frame, NSPoint location));
57 // Mock up an incrementing event number.
58 NSUInteger eventNumber = 0;
60 // Create an event of the indicated |type| at |point| within |view|.
61 // TODO(shess): Would be nice to have a MockApplication which provided
62 // nifty accessors to create these things and inject them.  It could
63 // even provide functions for "Click and drag mouse from point A to
64 // point B".
65 // TODO(groby): This is very similar to cocoa_testing_utils - unify.
66 NSEvent* Event(NSView* view, const NSPoint point, const NSEventType type,
67                const NSUInteger clickCount) {
68   NSWindow* window([view window]);
69   const NSPoint locationInWindow([view convertPoint:point toView:nil]);
70   const NSPoint location([window convertBaseToScreen:locationInWindow]);
71   return [NSEvent mouseEventWithType:type
72                             location:location
73                        modifierFlags:0
74                            timestamp:0
75                         windowNumber:[window windowNumber]
76                              context:nil
77                          eventNumber:eventNumber++
78                           clickCount:clickCount
79                             pressure:0.0];
81 NSEvent* Event(NSView* view, const NSPoint point, const NSEventType type) {
82   return Event(view, point, type, 1);
85 // Width of the field so that we don't have to ask |field_| for it all
86 // the time.
87 static const CGFloat kWidth(300.0);
89 class AutocompleteTextFieldTest : public CocoaTest {
90  public:
91   AutocompleteTextFieldTest() {
92     // Make sure this is wide enough to play games with the cell
93     // decorations.
94     NSRect frame = NSMakeRect(0, 0, kWidth, 30);
95     base::scoped_nsobject<AutocompleteTextField> field(
96         [[AutocompleteTextField alloc] initWithFrame:frame]);
97     field_ = field.get();
98     [field_ setStringValue:@"Test test"];
99     [[test_window() contentView] addSubview:field_];
101     AutocompleteTextFieldCell* cell = [field_ cell];
102     [cell clearDecorations];
104     mock_left_decoration_.SetVisible(false);
105     [cell addLeftDecoration:&mock_left_decoration_];
107     mock_right_decoration_.SetVisible(false);
108     [cell addRightDecoration:&mock_right_decoration_];
110     window_delegate_.reset(
111         [[AutocompleteTextFieldWindowTestDelegate alloc] init]);
112     [test_window() setDelegate:window_delegate_.get()];
113   }
115   NSEvent* KeyDownEventWithFlags(NSUInteger flags) {
116     return [NSEvent keyEventWithType:NSKeyDown
117                             location:NSZeroPoint
118                        modifierFlags:flags
119                            timestamp:0.0
120                         windowNumber:[test_window() windowNumber]
121                              context:nil
122                           characters:@"a"
123          charactersIgnoringModifiers:@"a"
124                            isARepeat:NO
125                              keyCode:'a'];
126   }
128   // Helper to return the field-editor frame being used w/in |field_|.
129   NSRect EditorFrame() {
130     EXPECT_TRUE([field_ currentEditor]);
131     EXPECT_EQ([[field_ subviews] count], 1U);
132     if ([[field_ subviews] count] > 0) {
133       return [[[field_ subviews] objectAtIndex:0] frame];
134     } else {
135       // Return something which won't work so the caller can soldier
136       // on.
137       return NSZeroRect;
138     }
139   }
141   AutocompleteTextFieldEditor* FieldEditor() {
142     return base::mac::ObjCCastStrict<AutocompleteTextFieldEditor>(
143         [field_ currentEditor]);
144   }
146   AutocompleteTextField* field_;
147   MockDecoration mock_left_decoration_;
148   MockDecoration mock_right_decoration_;
149   base::scoped_nsobject<AutocompleteTextFieldWindowTestDelegate>
150       window_delegate_;
153 TEST_VIEW(AutocompleteTextFieldTest, field_);
155 // Base class for testing AutocompleteTextFieldObserver messages.
156 class AutocompleteTextFieldObserverTest : public AutocompleteTextFieldTest {
157  public:
158   virtual void SetUp() {
159     AutocompleteTextFieldTest::SetUp();
160     [field_ setObserver:&field_observer_];
161   }
163   virtual void TearDown() {
164     // Clear the observer so that we don't show output for
165     // uninteresting messages to the mock (for instance, if |field_| has
166     // focus at the end of the test).
167     [field_ setObserver:NULL];
169     AutocompleteTextFieldTest::TearDown();
170   }
172   StrictMock<MockAutocompleteTextFieldObserver> field_observer_;
175 // Test that we have the right cell class.
176 TEST_F(AutocompleteTextFieldTest, CellClass) {
177   EXPECT_TRUE([[field_ cell] isKindOfClass:[AutocompleteTextFieldCell class]]);
180 // Test that becoming first responder sets things up correctly.
181 TEST_F(AutocompleteTextFieldTest, FirstResponder) {
182   EXPECT_EQ(nil, [field_ currentEditor]);
183   EXPECT_EQ([[field_ subviews] count], 0U);
184   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
185   EXPECT_FALSE(nil == [field_ currentEditor]);
186   EXPECT_EQ([[field_ subviews] count], 1U);
187   EXPECT_TRUE([[field_ currentEditor] isDescendantOf:field_]);
189   // Check that the window delegate is providing the right editor.
190   Class c = [AutocompleteTextFieldEditor class];
191   EXPECT_TRUE([[field_ currentEditor] isKindOfClass:c]);
194 TEST_F(AutocompleteTextFieldTest, AvailableDecorationWidth) {
195   // A fudge factor to account for how much space the border takes up.
196   // The test shouldn't be too dependent on the field's internals, but
197   // it also shouldn't let deranged cases fall through the cracks
198   // (like nothing available with no text, or everything available
199   // with some text).
200   const CGFloat kBorderWidth = 20.0;
202   // With no contents, almost the entire width is available for
203   // decorations.
204   [field_ setStringValue:@""];
205   CGFloat availableWidth = [field_ availableDecorationWidth];
206   EXPECT_LE(availableWidth, kWidth);
207   EXPECT_GT(availableWidth, kWidth - kBorderWidth);
209   // With minor contents, most of the remaining width is available for
210   // decorations.
211   NSDictionary* attributes =
212       [NSDictionary dictionaryWithObject:[field_ font]
213                                   forKey:NSFontAttributeName];
214   NSString* string = @"Hello world";
215   const NSSize size([string sizeWithAttributes:attributes]);
216   [field_ setStringValue:string];
217   availableWidth = [field_ availableDecorationWidth];
218   EXPECT_LE(availableWidth, kWidth - size.width);
219   EXPECT_GT(availableWidth, kWidth - size.width - kBorderWidth);
221   // With huge contents, nothing at all is left for decorations.
222   string = @"A long string which is surely wider than field_ can hold.";
223   [field_ setStringValue:string];
224   availableWidth = [field_ availableDecorationWidth];
225   EXPECT_LT(availableWidth, 0.0);
228 // Test drawing, mostly to ensure nothing leaks or crashes.
229 TEST_F(AutocompleteTextFieldTest, Display) {
230   [field_ display];
232   // Test focussed drawing.
233   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
234   [field_ display];
237 // Test setting gray text, mostly to ensure nothing leaks or crashes.
238 TEST_F(AutocompleteTextFieldTest, GrayText) {
239   [field_ display];
240   EXPECT_FALSE([field_ needsDisplay]);
241   [field_ setGrayTextAutocompletion:@"foo" textColor:[NSColor redColor]];
242   EXPECT_TRUE([field_ needsDisplay]);
243   [field_ display];
246 TEST_F(AutocompleteTextFieldObserverTest, FlagsChanged) {
247   InSequence dummy;  // Call mock in exactly the order specified.
249   // Test without Control key down, but some other modifier down.
250   EXPECT_CALL(field_observer_, OnControlKeyChanged(false));
251   [field_ flagsChanged:KeyDownEventWithFlags(NSShiftKeyMask)];
253   // Test with Control key down.
254   EXPECT_CALL(field_observer_, OnControlKeyChanged(true));
255   [field_ flagsChanged:KeyDownEventWithFlags(NSControlKeyMask)];
258 // This test is here rather than in the editor's tests because the
259 // field catches -flagsChanged: because it's on the responder chain,
260 // the field editor doesn't implement it.
261 TEST_F(AutocompleteTextFieldObserverTest, FieldEditorFlagsChanged) {
262   // Many of these methods try to change the selection.
263   EXPECT_CALL(field_observer_, SelectionRangeForProposedRange(A<NSRange>()))
264       .WillRepeatedly(ReturnArg<0>());
266   InSequence dummy;  // Call mock in exactly the order specified.
267   EXPECT_CALL(field_observer_, OnSetFocus(false));
268   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
269   NSResponder* firstResponder = [[field_ window] firstResponder];
270   EXPECT_EQ(firstResponder, [field_ currentEditor]);
272   // Test without Control key down, but some other modifier down.
273   EXPECT_CALL(field_observer_, OnControlKeyChanged(false));
274   [firstResponder flagsChanged:KeyDownEventWithFlags(NSShiftKeyMask)];
276   // Test with Control key down.
277   EXPECT_CALL(field_observer_, OnControlKeyChanged(true));
278   [firstResponder flagsChanged:KeyDownEventWithFlags(NSControlKeyMask)];
281 // Frame size changes are propagated to |observer_|.
282 TEST_F(AutocompleteTextFieldObserverTest, FrameChanged) {
283   EXPECT_CALL(field_observer_, OnFrameChanged());
284   NSRect frame = [field_ frame];
285   frame.size.width += 10.0;
286   [field_ setFrame:frame];
289 // Test that the field editor gets the same bounds when focus is
290 // delivered by the standard focusing machinery, or by
291 // -resetFieldEditorFrameIfNeeded.
292 TEST_F(AutocompleteTextFieldTest, ResetFieldEditorBase) {
293   // Capture the editor frame resulting from the standard focus
294   // machinery.
295   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
296   const NSRect baseEditorFrame = EditorFrame();
298   // A decoration should result in a strictly smaller editor frame.
299   mock_left_decoration_.SetVisible(true);
300   [field_ resetFieldEditorFrameIfNeeded];
301   EXPECT_FALSE(NSEqualRects(baseEditorFrame, EditorFrame()));
302   EXPECT_TRUE(NSContainsRect(baseEditorFrame, EditorFrame()));
304   // Removing the decoration and using -resetFieldEditorFrameIfNeeded
305   // should result in the same frame as the standard focus machinery.
306   mock_left_decoration_.SetVisible(false);
307   [field_ resetFieldEditorFrameIfNeeded];
308   EXPECT_TRUE(NSEqualRects(baseEditorFrame, EditorFrame()));
311 // Test that the field editor gets the same bounds when focus is
312 // delivered by the standard focusing machinery, or by
313 // -resetFieldEditorFrameIfNeeded, this time with a decoration
314 // pre-loaded.
315 TEST_F(AutocompleteTextFieldTest, ResetFieldEditorWithDecoration) {
316   AutocompleteTextFieldCell* cell = [field_ cell];
318   // Make sure decoration isn't already visible, then make it visible.
319   EXPECT_TRUE(NSIsEmptyRect([cell frameForDecoration:&mock_left_decoration_
320                                              inFrame:[field_ bounds]]));
321   mock_left_decoration_.SetVisible(true);
322   EXPECT_FALSE(NSIsEmptyRect([cell frameForDecoration:&mock_left_decoration_
323                                               inFrame:[field_ bounds]]));
325   // Capture the editor frame resulting from the standard focus
326   // machinery.
328   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
329   const NSRect baseEditorFrame = EditorFrame();
331   // When the decoration is not visible the frame should be strictly larger.
332   mock_left_decoration_.SetVisible(false);
333   EXPECT_TRUE(NSIsEmptyRect([cell frameForDecoration:&mock_left_decoration_
334                                              inFrame:[field_ bounds]]));
335   [field_ resetFieldEditorFrameIfNeeded];
336   EXPECT_FALSE(NSEqualRects(baseEditorFrame, EditorFrame()));
337   EXPECT_TRUE(NSContainsRect(EditorFrame(), baseEditorFrame));
339   // When the decoration is visible, -resetFieldEditorFrameIfNeeded
340   // should result in the same frame as the standard focus machinery.
341   mock_left_decoration_.SetVisible(true);
342   EXPECT_FALSE(NSIsEmptyRect([cell frameForDecoration:&mock_left_decoration_
343                                               inFrame:[field_ bounds]]));
345   [field_ resetFieldEditorFrameIfNeeded];
346   EXPECT_TRUE(NSEqualRects(baseEditorFrame, EditorFrame()));
349 // Test that resetting the field editor bounds does not cause untoward
350 // messages to the field's observer.
351 TEST_F(AutocompleteTextFieldObserverTest, ResetFieldEditorContinuesEditing) {
352   // Many of these methods try to change the selection.
353   EXPECT_CALL(field_observer_, SelectionRangeForProposedRange(A<NSRange>()))
354       .WillRepeatedly(ReturnArg<0>());
356   EXPECT_CALL(field_observer_, OnSetFocus(false));
357   // Becoming first responder doesn't begin editing.
358   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
359   const NSRect baseEditorFrame = EditorFrame();
360   NSTextView* editor = static_cast<NSTextView*>([field_ currentEditor]);
361   EXPECT_TRUE(nil != editor);
363   // This should begin editing and indicate a change.
364   EXPECT_CALL(field_observer_, OnDidBeginEditing());
365   EXPECT_CALL(field_observer_, OnBeforeChange());
366   EXPECT_CALL(field_observer_, OnDidChange());
367   [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
368   [editor didChangeText];
370   // No messages to |field_observer_| when the frame actually changes.
371   mock_left_decoration_.SetVisible(true);
372   [field_ resetFieldEditorFrameIfNeeded];
373   EXPECT_FALSE(NSEqualRects(baseEditorFrame, EditorFrame()));
376 // Clicking in a right-hand decoration which does not handle the mouse
377 // puts the caret rightmost.
378 TEST_F(AutocompleteTextFieldTest, ClickRightDecorationPutsCaretRightmost) {
379   // Decoration does not handle the mouse event, so the cell should
380   // process it.  Called at least once.
381   EXPECT_CALL(mock_right_decoration_, AcceptsMousePress())
382       .WillOnce(Return(false))
383       .WillRepeatedly(Return(false));
385   // Set the decoration before becoming responder.
386   EXPECT_FALSE([field_ currentEditor]);
387   mock_right_decoration_.SetVisible(true);
389   // Make first responder should select all.
390   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
391   EXPECT_TRUE([field_ currentEditor]);
392   const NSRange allRange = NSMakeRange(0, [[field_ stringValue] length]);
393   EXPECT_TRUE(NSEqualRanges(allRange, [[field_ currentEditor] selectedRange]));
395   // Generate a click on the decoration.
396   AutocompleteTextFieldCell* cell = [field_ cell];
397   const NSRect bounds = [field_ bounds];
398   const NSRect iconFrame =
399       [cell frameForDecoration:&mock_right_decoration_ inFrame:bounds];
400   const NSPoint point = NSMakePoint(NSMidX(iconFrame), NSMidY(iconFrame));
401   NSEvent* downEvent = Event(field_, point, NSLeftMouseDown);
402   NSEvent* upEvent = Event(field_, point, NSLeftMouseUp);
403   [NSApp postEvent:upEvent atStart:YES];
404   [field_ mouseDown:downEvent];
406   // Selection should be a right-hand-side caret.
407   EXPECT_TRUE(NSEqualRanges(NSMakeRange([[field_ stringValue] length], 0),
408                             [[field_ currentEditor] selectedRange]));
411 // Clicking in a left-side decoration which doesn't handle the event
412 // puts the selection in the leftmost position.
413 TEST_F(AutocompleteTextFieldTest, ClickLeftDecorationPutsCaretLeftmost) {
414   // Decoration does not handle the mouse event, so the cell should
415   // process it.  Called at least once.
416   EXPECT_CALL(mock_left_decoration_, AcceptsMousePress())
417       .WillOnce(Return(false))
418       .WillRepeatedly(Return(false));
420   // Set the decoration before becoming responder.
421   EXPECT_FALSE([field_ currentEditor]);
422   mock_left_decoration_.SetVisible(true);
424   // Make first responder should select all.
425   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
426   EXPECT_TRUE([field_ currentEditor]);
427   const NSRange allRange = NSMakeRange(0, [[field_ stringValue] length]);
428   EXPECT_TRUE(NSEqualRanges(allRange, [[field_ currentEditor] selectedRange]));
430   // Generate a click on the decoration.
431   AutocompleteTextFieldCell* cell = [field_ cell];
432   const NSRect bounds = [field_ bounds];
433   const NSRect iconFrame =
434       [cell frameForDecoration:&mock_left_decoration_ inFrame:bounds];
435   const NSPoint point = NSMakePoint(NSMidX(iconFrame), NSMidY(iconFrame));
436   NSEvent* downEvent = Event(field_, point, NSLeftMouseDown);
437   NSEvent* upEvent = Event(field_, point, NSLeftMouseUp);
438   [NSApp postEvent:upEvent atStart:YES];
439   [field_ mouseDown:downEvent];
441   // Selection should be a left-hand-side caret.
442   EXPECT_TRUE(NSEqualRanges(NSMakeRange(0, 0),
443                             [[field_ currentEditor] selectedRange]));
446 // Clicks not in the text area or the cell's decorations fall through
447 // to the editor.
448 TEST_F(AutocompleteTextFieldTest, ClickBorderSelectsAll) {
449   // Can't rely on the window machinery to make us first responder,
450   // here.
451   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
452   EXPECT_TRUE([field_ currentEditor]);
454   const NSPoint point(NSMakePoint(20.0, 1.0));
455   NSEvent* downEvent(Event(field_, point, NSLeftMouseDown));
456   NSEvent* upEvent(Event(field_, point, NSLeftMouseUp));
457   [NSApp postEvent:upEvent atStart:YES];
458   [field_ mouseDown:downEvent];
460   // Clicking in the narrow border area around a Cocoa NSTextField
461   // does a select-all.  Regardless of whether this is a good call, it
462   // works as a test that things get passed down to the editor.
463   const NSRange selectedRange([[field_ currentEditor] selectedRange]);
464   EXPECT_EQ(selectedRange.location, 0U);
465   EXPECT_EQ(selectedRange.length, [[field_ stringValue] length]);
468 // Single-click with no drag should setup a field editor and
469 // select all.
470 TEST_F(AutocompleteTextFieldTest, ClickSelectsAll) {
471   EXPECT_FALSE([field_ currentEditor]);
473   const NSPoint point = NSMakePoint(20.0, NSMidY([field_ bounds]));
474   NSEvent* downEvent(Event(field_, point, NSLeftMouseDown));
475   NSEvent* upEvent(Event(field_, point, NSLeftMouseUp));
476   [NSApp postEvent:upEvent atStart:YES];
477   [field_ mouseDown:downEvent];
478   EXPECT_TRUE([field_ currentEditor]);
479   const NSRange selectedRange([[field_ currentEditor] selectedRange]);
480   EXPECT_EQ(selectedRange.location, 0U);
481   EXPECT_EQ(selectedRange.length, [[field_ stringValue] length]);
484 // Click-drag selects text, not select all.
485 TEST_F(AutocompleteTextFieldTest, ClickDragSelectsText) {
486   EXPECT_FALSE([field_ currentEditor]);
488   NSEvent* downEvent(Event(field_, NSMakePoint(20.0, 5.0), NSLeftMouseDown));
489   NSEvent* upEvent(Event(field_, NSMakePoint(0.0, 5.0), NSLeftMouseUp));
490   [NSApp postEvent:upEvent atStart:YES];
491   [field_ mouseDown:downEvent];
492   EXPECT_TRUE([field_ currentEditor]);
494   // Expect this to have selected a prefix of the content.  Mostly
495   // just don't want the select-all behavior.
496   const NSRange selectedRange([[field_ currentEditor] selectedRange]);
497   EXPECT_EQ(selectedRange.location, 0U);
498   EXPECT_LT(selectedRange.length, [[field_ stringValue] length]);
501 // TODO(shess): Test that click/pause/click allows cursor placement.
502 // In this case the first click goes to the field, but the second
503 // click goes to the field editor, so the current testing pattern
504 // can't work.  What really needs to happen is to push through the
505 // NSWindow event machinery so that we can say "two independent clicks
506 // at the same location have the right effect".  Once that is done, it
507 // might make sense to revise the other tests to use the same
508 // machinery.
510 // Double-click selects word, not select all.
511 TEST_F(AutocompleteTextFieldTest, DoubleClickSelectsWord) {
512   EXPECT_FALSE([field_ currentEditor]);
514   const NSPoint point = NSMakePoint(20.0, NSMidY([field_ bounds]));
515   NSEvent* downEvent(Event(field_, point, NSLeftMouseDown, 1));
516   NSEvent* upEvent(Event(field_, point, NSLeftMouseUp, 1));
517   NSEvent* downEvent2(Event(field_, point, NSLeftMouseDown, 2));
518   NSEvent* upEvent2(Event(field_, point, NSLeftMouseUp, 2));
519   [NSApp postEvent:upEvent atStart:YES];
520   [field_ mouseDown:downEvent];
521   [NSApp postEvent:upEvent2 atStart:YES];
522   [field_ mouseDown:downEvent2];
523   EXPECT_TRUE([field_ currentEditor]);
525   // Selected the first word.
526   const NSRange selectedRange([[field_ currentEditor] selectedRange]);
527   const NSRange spaceRange([[field_ stringValue] rangeOfString:@" "]);
528   EXPECT_GT(spaceRange.location, 0U);
529   EXPECT_LT(spaceRange.length, [[field_ stringValue] length]);
530   EXPECT_EQ(selectedRange.location, 0U);
531   EXPECT_EQ(selectedRange.length, spaceRange.location);
534 TEST_F(AutocompleteTextFieldTest, TripleClickSelectsAll) {
535   EXPECT_FALSE([field_ currentEditor]);
537   const NSPoint point(NSMakePoint(20.0, 5.0));
538   NSEvent* downEvent(Event(field_, point, NSLeftMouseDown, 1));
539   NSEvent* upEvent(Event(field_, point, NSLeftMouseUp, 1));
540   NSEvent* downEvent2(Event(field_, point, NSLeftMouseDown, 2));
541   NSEvent* upEvent2(Event(field_, point, NSLeftMouseUp, 2));
542   NSEvent* downEvent3(Event(field_, point, NSLeftMouseDown, 3));
543   NSEvent* upEvent3(Event(field_, point, NSLeftMouseUp, 3));
544   [NSApp postEvent:upEvent atStart:YES];
545   [field_ mouseDown:downEvent];
546   [NSApp postEvent:upEvent2 atStart:YES];
547   [field_ mouseDown:downEvent2];
548   [NSApp postEvent:upEvent3 atStart:YES];
549   [field_ mouseDown:downEvent3];
550   EXPECT_TRUE([field_ currentEditor]);
552   // Selected the first word.
553   const NSRange selectedRange([[field_ currentEditor] selectedRange]);
554   EXPECT_EQ(selectedRange.location, 0U);
555   EXPECT_EQ(selectedRange.length, [[field_ stringValue] length]);
558 // Clicking a decoration should call decoration's OnMousePressed.
559 TEST_F(AutocompleteTextFieldTest, LeftDecorationMouseDown) {
560   // At this point, not focussed.
561   EXPECT_FALSE([field_ currentEditor]);
563   mock_left_decoration_.SetVisible(true);
564   EXPECT_CALL(mock_left_decoration_, AcceptsMousePress())
565       .WillRepeatedly(Return(true));
567   AutocompleteTextFieldCell* cell = [field_ cell];
568   const NSRect iconFrame =
569       [cell frameForDecoration:&mock_left_decoration_ inFrame:[field_ bounds]];
570   const NSPoint location = NSMakePoint(NSMidX(iconFrame), NSMidY(iconFrame));
571   NSEvent* downEvent = Event(field_, location, NSLeftMouseDown, 1);
572   NSEvent* upEvent = Event(field_, location, NSLeftMouseUp, 1);
574   // Since decorations can be dragged, the mouse-press is sent on
575   // mouse-up.
576   [NSApp postEvent:upEvent atStart:YES];
578   EXPECT_CALL(mock_left_decoration_, OnMousePressed(_, _))
579       .WillOnce(Return(true));
580   [field_ mouseDown:downEvent];
582   // Focus the field and test that handled clicks don't affect selection.
583   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
584   EXPECT_TRUE([field_ currentEditor]);
585   const NSRange allRange = NSMakeRange(0, [[field_ stringValue] length]);
586   EXPECT_TRUE(NSEqualRanges(allRange, [[field_ currentEditor] selectedRange]));
588   // Generate another click on the decoration.
589   downEvent = Event(field_, location, NSLeftMouseDown, 1);
590   upEvent = Event(field_, location, NSLeftMouseUp, 1);
591   [NSApp postEvent:upEvent atStart:YES];
592   EXPECT_CALL(mock_left_decoration_, OnMousePressed(_, _))
593       .WillOnce(Return(true));
594   [field_ mouseDown:downEvent];
596   // The selection should not have changed.
597   EXPECT_TRUE(NSEqualRanges(allRange, [[field_ currentEditor] selectedRange]));
599   // TODO(shess): Test that mouse drags are initiated if the next
600   // event is a drag, or if the mouse-up takes too long to arrive.
601   // IDEA: mock decoration to return a pasteboard which a mock
602   // AutocompleteTextField notes in -dragImage:*.
605 // Clicking a decoration should call decoration's OnMousePressed.
606 TEST_F(AutocompleteTextFieldTest, RightDecorationMouseDown) {
607   // At this point, not focussed.
608   EXPECT_FALSE([field_ currentEditor]);
610   mock_right_decoration_.SetVisible(true);
611   EXPECT_CALL(mock_right_decoration_, AcceptsMousePress())
612       .WillRepeatedly(Return(true));
614   AutocompleteTextFieldCell* cell = [field_ cell];
615   const NSRect bounds = [field_ bounds];
616   const NSRect iconFrame =
617       [cell frameForDecoration:&mock_right_decoration_ inFrame:bounds];
618   const NSPoint location = NSMakePoint(NSMidX(iconFrame), NSMidY(iconFrame));
619   NSEvent* downEvent = Event(field_, location, NSLeftMouseDown, 1);
620   NSEvent* upEvent = Event(field_, location, NSLeftMouseUp, 1);
622   // Since decorations can be dragged, the mouse-press is sent on
623   // mouse-up.
624   [NSApp postEvent:upEvent atStart:YES];
626   EXPECT_CALL(mock_right_decoration_, OnMousePressed(_, _))
627       .WillOnce(Return(true));
628   [field_ mouseDown:downEvent];
631 // Test that page action menus are properly returned.
632 // TODO(shess): Really, this should test that things are forwarded to
633 // the cell, and the cell tests should test that the right things are
634 // selected.  It's easier to mock the event here, though.  This code's
635 // event-mockers might be worth promoting to |cocoa_test_event_utils.h| or
636 // |cocoa_test_helper.h|.
637 TEST_F(AutocompleteTextFieldTest, DecorationMenu) {
638   AutocompleteTextFieldCell* cell = [field_ cell];
639   const NSRect bounds([field_ bounds]);
641   const CGFloat edge = NSHeight(bounds) - 4.0;
642   const NSSize size = NSMakeSize(edge, edge);
643   base::scoped_nsobject<NSImage> image([[NSImage alloc] initWithSize:size]);
645   base::scoped_nsobject<NSMenu> menu([[NSMenu alloc] initWithTitle:@"Menu"]);
647   mock_left_decoration_.SetVisible(true);
648   mock_right_decoration_.SetVisible(true);
650   // The item with a menu returns it.
651   NSRect actionFrame = [cell frameForDecoration:&mock_right_decoration_
652                                         inFrame:bounds];
653   NSPoint location = NSMakePoint(NSMidX(actionFrame), NSMidY(actionFrame));
654   NSEvent* event = Event(field_, location, NSRightMouseDown, 1);
656   // Check that the decoration is called, and the field returns the
657   // menu.
658   EXPECT_CALL(mock_right_decoration_, GetMenu())
659       .WillOnce(Return(menu.get()));
660   NSMenu *decorationMenu = [field_ decorationMenuForEvent:event];
661   EXPECT_EQ(decorationMenu, menu);
663   // The item without a menu returns nil.
664   EXPECT_CALL(mock_left_decoration_, GetMenu())
665       .WillOnce(Return(static_cast<NSMenu*>(nil)));
666   actionFrame = [cell frameForDecoration:&mock_left_decoration_
667                                  inFrame:bounds];
668   location = NSMakePoint(NSMidX(actionFrame), NSMidY(actionFrame));
669   event = Event(field_, location, NSRightMouseDown, 1);
670   EXPECT_FALSE([field_ decorationMenuForEvent:event]);
672   // Something not in an action returns nil.
673   location = NSMakePoint(NSMidX(bounds), NSMidY(bounds));
674   event = Event(field_, location, NSRightMouseDown, 1);
675   EXPECT_FALSE([field_ decorationMenuForEvent:event]);
678 // Verify that -setAttributedStringValue: works as expected when
679 // focussed or when not focussed.  Our code mostly depends on about
680 // whether -stringValue works right.
681 TEST_F(AutocompleteTextFieldTest, SetAttributedStringBaseline) {
682   EXPECT_EQ(nil, [field_ currentEditor]);
684   // So that we can set rich text.
685   [field_ setAllowsEditingTextAttributes:YES];
687   // Set an attribute different from the field's default so we can
688   // tell we got the same string out as we put in.
689   NSFont* font = [NSFont fontWithDescriptor:[[field_ font] fontDescriptor]
690                                        size:[[field_ font] pointSize] + 2];
691   NSDictionary* attributes =
692       [NSDictionary dictionaryWithObject:font
693                                   forKey:NSFontAttributeName];
694   NSString* const kString = @"This is a test";
695   base::scoped_nsobject<NSAttributedString> attributedString(
696       [[NSAttributedString alloc] initWithString:kString
697                                       attributes:attributes]);
699   // Check that what we get back looks like what we put in.
700   EXPECT_NSNE(kString, [field_ stringValue]);
701   [field_ setAttributedStringValue:attributedString];
702   EXPECT_TRUE([[field_ attributedStringValue]
703                 isEqualToAttributedString:attributedString]);
704   EXPECT_NSEQ(kString, [field_ stringValue]);
706   // Try that again with focus.
707   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
709   EXPECT_TRUE([field_ currentEditor]);
711   // Check that what we get back looks like what we put in.
712   [field_ setStringValue:@""];
713   EXPECT_NSNE(kString, [field_ stringValue]);
714   [field_ setAttributedStringValue:attributedString];
715   EXPECT_TRUE([[field_ attributedStringValue]
716                 isEqualToAttributedString:attributedString]);
717   EXPECT_NSEQ(kString, [field_ stringValue]);
720 // -setAttributedStringValue: shouldn't reset the undo state if things
721 // are being editted.
722 TEST_F(AutocompleteTextFieldTest, SetAttributedStringUndo) {
723   NSColor* redColor = [NSColor redColor];
724   NSDictionary* attributes =
725       [NSDictionary dictionaryWithObject:redColor
726                                   forKey:NSForegroundColorAttributeName];
727   NSString* const kString = @"This is a test";
728   base::scoped_nsobject<NSAttributedString> attributedString(
729       [[NSAttributedString alloc] initWithString:kString
730                                       attributes:attributes]);
731   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
732   EXPECT_TRUE([field_ currentEditor]);
733   NSTextView* editor = static_cast<NSTextView*>([field_ currentEditor]);
734   NSUndoManager* undoManager = [editor undoManager];
735   EXPECT_TRUE(undoManager);
737   // Nothing to undo, yet.
738   EXPECT_FALSE([undoManager canUndo]);
740   // Starting an editing action creates an undoable item.
741   [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
742   [editor didChangeText];
743   EXPECT_TRUE([undoManager canUndo]);
745   // -setStringValue: resets the editor's undo chain.
746   [field_ setStringValue:kString];
747   EXPECT_FALSE([undoManager canUndo]);
749   // Verify that -setAttributedStringValue: does not reset the
750   // editor's undo chain.
751   [field_ setStringValue:@""];
752   [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
753   [editor didChangeText];
754   EXPECT_TRUE([undoManager canUndo]);
755   [field_ setAttributedStringValue:attributedString];
756   EXPECT_TRUE([undoManager canUndo]);
758   // Verify that calling -clearUndoChain clears the undo chain.
759   [field_ clearUndoChain];
760   EXPECT_FALSE([undoManager canUndo]);
763 TEST_F(AutocompleteTextFieldTest, EditorGetsCorrectUndoManager) {
764   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
766   NSTextView* editor = static_cast<NSTextView*>([field_ currentEditor]);
767   EXPECT_TRUE(editor);
768   EXPECT_EQ([field_ undoManagerForTextView:editor], [editor undoManager]);
771 // Verify that hideFocusState correctly hides the focus ring and insertion
772 // pointer.
773 TEST_F(AutocompleteTextFieldTest, HideFocusState) {
774   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
775   [[field_ cell] setShowsFirstResponder:YES];
777   EXPECT_TRUE([[field_ cell] showsFirstResponder]);
778   EXPECT_TRUE([FieldEditor() shouldDrawInsertionPoint]);
780   [[field_ cell] setHideFocusState:YES
781                             ofView:field_];
782   EXPECT_FALSE([[field_ cell] showsFirstResponder]);
783   EXPECT_FALSE([FieldEditor() shouldDrawInsertionPoint]);
785   [[field_ cell] setHideFocusState:NO
786                             ofView:field_];
787   EXPECT_TRUE([[field_ cell] showsFirstResponder]);
788   EXPECT_TRUE([FieldEditor() shouldDrawInsertionPoint]);
791 // Verify that OnSetFocus for button decorations is only sent after the
792 // decoration is picked as the target for the subsequent -mouseDown:. Otherwise
793 // hiding a ButtonDecoration in OnSetFocus will prevent a call to
794 // OnMousePressed, since it is already hidden at the time of mouseDown.
795 TEST_F(AutocompleteTextFieldObserverTest, ButtonDecorationFocus) {
796   // Add the mock button.
797   MockButtonDecoration mock_button;
798   mock_button.SetVisible(true);
799   AutocompleteTextFieldCell* cell = [field_ cell];
800   [cell addLeftDecoration:&mock_button];
802   // Ensure button is hidden when OnSetFocus() is called.
803   EXPECT_CALL(field_observer_, OnSetFocus(false)).WillOnce(
804       testing::InvokeWithoutArgs(&mock_button, &MockButtonDecoration::Hide));
806   // Ignore incidental calls.
807   EXPECT_CALL(field_observer_, SelectionRangeForProposedRange(_))
808       .WillRepeatedly(testing::Return(NSMakeRange(0, 0)));
809   EXPECT_CALL(field_observer_, OnMouseDown(_));
811   // Still expect an OnMousePressed on the button.
812   EXPECT_CALL(mock_button, OnMousePressed(_, _))
813       .WillOnce(testing::Return(true));
815   // Get click point for button decoration.
816   NSRect button_rect =
817       [cell frameForDecoration:&mock_button inFrame:[field_ bounds]];
818   EXPECT_FALSE(NSIsEmptyRect(button_rect));
819   NSPoint click_location =
820       NSMakePoint(NSMidX(button_rect), NSMidY(button_rect));
822   // Ensure the field is currently not first responder.
823   [test_window() makePretendKeyWindowAndSetFirstResponder:nil];
824   EXPECT_NSNE([[field_ window] firstResponder], field_);
826   // Execute button click event sequence.
827   NSEvent* downEvent = Event(field_, click_location, NSLeftMouseDown);
828   NSEvent* upEvent = Event(field_, click_location, NSLeftMouseUp);
830   // Can't just use -sendEvent:, since that doesn't populate -currentEvent.
831   [NSApp postEvent:downEvent atStart:YES];
832   [NSApp postEvent:upEvent atStart:NO];
833   NSEvent* next_event = [NSApp nextEventMatchingMask:NSAnyEventMask
834                                            untilDate:nil
835                                               inMode:NSDefaultRunLoopMode
836                                              dequeue:YES];
837   [NSApp sendEvent:next_event];
839   // Expectations check that both OnSetFocus and OnMouseDown were called.
840   // Additionally, ensure button is hidden and field is firstResponder.
841   EXPECT_FALSE(mock_button.IsVisible());
842   EXPECT_TRUE(NSIsEmptyRect([cell frameForDecoration:&mock_left_decoration_
843                                              inFrame:[field_ bounds]]));
844   EXPECT_TRUE([base::mac::ObjCCastStrict<NSView>(
845       [[field_ window] firstResponder]) isDescendantOf:field_]);
848 TEST_F(AutocompleteTextFieldObserverTest, SendsEditingMessages) {
849   // Many of these methods try to change the selection.
850   EXPECT_CALL(field_observer_, SelectionRangeForProposedRange(A<NSRange>()))
851       .WillRepeatedly(ReturnArg<0>());
853   EXPECT_CALL(field_observer_, OnSetFocus(false));
854   // Becoming first responder doesn't begin editing.
855   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
856   NSTextView* editor = static_cast<NSTextView*>([field_ currentEditor]);
857   EXPECT_TRUE(nil != editor);
859   // This should begin editing and indicate a change.
860   EXPECT_CALL(field_observer_, OnDidBeginEditing());
861   EXPECT_CALL(field_observer_, OnBeforeChange());
862   EXPECT_CALL(field_observer_, OnDidChange());
863   [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
864   [editor didChangeText];
866   // Further changes don't send the begin message.
867   EXPECT_CALL(field_observer_, OnBeforeChange());
868   EXPECT_CALL(field_observer_, OnDidChange());
869   [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
870   [editor didChangeText];
872   // -doCommandBySelector: should forward to observer via |field_|.
873   // TODO(shess): Test with a fake arrow-key event?
874   const SEL cmd = @selector(moveDown:);
875   EXPECT_CALL(field_observer_, OnDoCommandBySelector(cmd))
876       .WillOnce(Return(true));
877   [editor doCommandBySelector:cmd];
879   // Finished with the changes.
880   EXPECT_CALL(field_observer_, OnKillFocus());
881   EXPECT_CALL(field_observer_, OnDidEndEditing());
882   [test_window() clearPretendKeyWindowAndFirstResponder];
885 // Test that the resign-key notification is forwarded right, and that
886 // the notification is registered and unregistered when the view moves
887 // in and out of the window.
888 // TODO(shess): Should this test the key window for realz?  That would
889 // be really annoying to whoever is running the tests.
890 TEST_F(AutocompleteTextFieldObserverTest, ClosePopupOnResignKey) {
891   EXPECT_CALL(field_observer_, ClosePopup());
892   [test_window() resignKeyWindow];
894   base::scoped_nsobject<AutocompleteTextField> pin([field_ retain]);
895   [field_ removeFromSuperview];
896   [test_window() resignKeyWindow];
898   [[test_window() contentView] addSubview:field_];
899   EXPECT_CALL(field_observer_, ClosePopup());
900   [test_window() resignKeyWindow];
903 }  // namespace