Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / location_bar / autocomplete_text_field_unittest.mm
blobf6673977520a2fa81cc82bfe5111263fb0ccd6e8
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 <Cocoa/Cocoa.h>
7 #include "base/mac/foundation_util.h"
8 #include "base/mac/scoped_nsobject.h"
9 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
10 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field.h"
11 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_cell.h"
12 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor.h"
13 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_unittest_helper.h"
14 #import "chrome/browser/ui/cocoa/location_bar/location_bar_decoration.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"
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_METHOD1(OnMousePressed, bool(NSRect frame));
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 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];
64 NSEvent* Event(NSView* view, const NSPoint point, const NSEventType type) {
65   return Event(view, point, type, 1);
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);
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     base::scoped_nsobject<AutocompleteTextField> field(
79         [[AutocompleteTextField alloc] initWithFrame:frame]);
80     field_ = field.get();
81     [field_ setStringValue:@"Test test"];
82     [[test_window() contentView] addSubview:field_];
84     AutocompleteTextFieldCell* cell = [field_ cell];
85     [cell clearDecorations];
87     mock_left_decoration_.SetVisible(false);
88     [cell addLeftDecoration:&mock_left_decoration_];
90     mock_right_decoration_.SetVisible(false);
91     [cell addRightDecoration:&mock_right_decoration_];
93     window_delegate_.reset(
94         [[AutocompleteTextFieldWindowTestDelegate alloc] init]);
95     [test_window() setDelegate:window_delegate_.get()];
96   }
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   }
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   }
124   AutocompleteTextFieldEditor* FieldEditor() {
125     return base::mac::ObjCCastStrict<AutocompleteTextFieldEditor>(
126         [field_ currentEditor]);
127   }
129   AutocompleteTextField* field_;
130   MockDecoration mock_left_decoration_;
131   MockDecoration mock_right_decoration_;
132   base::scoped_nsobject<AutocompleteTextFieldWindowTestDelegate>
133       window_delegate_;
136 TEST_VIEW(AutocompleteTextFieldTest, field_);
138 // Base class for testing AutocompleteTextFieldObserver messages.
139 class AutocompleteTextFieldObserverTest : public AutocompleteTextFieldTest {
140  public:
141   virtual void SetUp() {
142     AutocompleteTextFieldTest::SetUp();
143     [field_ setObserver:&field_observer_];
144   }
146   virtual void TearDown() {
147     // Clear the observer so that we don't show output for
148     // uninteresting messages to the mock (for instance, if |field_| has
149     // focus at the end of the test).
150     [field_ setObserver:NULL];
152     AutocompleteTextFieldTest::TearDown();
153   }
155   StrictMock<MockAutocompleteTextFieldObserver> field_observer_;
158 // Test that we have the right cell class.
159 TEST_F(AutocompleteTextFieldTest, CellClass) {
160   EXPECT_TRUE([[field_ cell] isKindOfClass:[AutocompleteTextFieldCell class]]);
163 // Test that becoming first responder sets things up correctly.
164 TEST_F(AutocompleteTextFieldTest, FirstResponder) {
165   EXPECT_EQ(nil, [field_ currentEditor]);
166   EXPECT_EQ([[field_ subviews] count], 0U);
167   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
168   EXPECT_FALSE(nil == [field_ currentEditor]);
169   EXPECT_EQ([[field_ subviews] count], 1U);
170   EXPECT_TRUE([[field_ currentEditor] isDescendantOf:field_]);
172   // Check that the window delegate is providing the right editor.
173   Class c = [AutocompleteTextFieldEditor class];
174   EXPECT_TRUE([[field_ currentEditor] isKindOfClass:c]);
177 TEST_F(AutocompleteTextFieldTest, AvailableDecorationWidth) {
178   // A fudge factor to account for how much space the border takes up.
179   // The test shouldn't be too dependent on the field's internals, but
180   // it also shouldn't let deranged cases fall through the cracks
181   // (like nothing available with no text, or everything available
182   // with some text).
183   const CGFloat kBorderWidth = 20.0;
185   // With no contents, almost the entire width is available for
186   // decorations.
187   [field_ setStringValue:@""];
188   CGFloat availableWidth = [field_ availableDecorationWidth];
189   EXPECT_LE(availableWidth, kWidth);
190   EXPECT_GT(availableWidth, kWidth - kBorderWidth);
192   // With minor contents, most of the remaining width is available for
193   // decorations.
194   NSDictionary* attributes =
195       [NSDictionary dictionaryWithObject:[field_ font]
196                                   forKey:NSFontAttributeName];
197   NSString* string = @"Hello world";
198   const NSSize size([string sizeWithAttributes:attributes]);
199   [field_ setStringValue:string];
200   availableWidth = [field_ availableDecorationWidth];
201   EXPECT_LE(availableWidth, kWidth - size.width);
202   EXPECT_GT(availableWidth, kWidth - size.width - kBorderWidth);
204   // With huge contents, nothing at all is left for decorations.
205   string = @"A long string which is surely wider than field_ can hold.";
206   [field_ setStringValue:string];
207   availableWidth = [field_ availableDecorationWidth];
208   EXPECT_LT(availableWidth, 0.0);
211 // Test drawing, mostly to ensure nothing leaks or crashes.
212 TEST_F(AutocompleteTextFieldTest, Display) {
213   [field_ display];
215   // Test focussed drawing.
216   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
217   [field_ display];
220 // Test setting gray text, mostly to ensure nothing leaks or crashes.
221 TEST_F(AutocompleteTextFieldTest, GrayText) {
222   [field_ display];
223   EXPECT_FALSE([field_ needsDisplay]);
224   [field_ setGrayTextAutocompletion:@"foo" textColor:[NSColor redColor]];
225   EXPECT_TRUE([field_ needsDisplay]);
226   [field_ display];
229 TEST_F(AutocompleteTextFieldObserverTest, FlagsChanged) {
230   InSequence dummy;  // Call mock in exactly the order specified.
232   // Test without Control key down, but some other modifier down.
233   EXPECT_CALL(field_observer_, OnControlKeyChanged(false));
234   [field_ flagsChanged:KeyDownEventWithFlags(NSShiftKeyMask)];
236   // Test with Control key down.
237   EXPECT_CALL(field_observer_, OnControlKeyChanged(true));
238   [field_ flagsChanged:KeyDownEventWithFlags(NSControlKeyMask)];
241 // This test is here rather than in the editor's tests because the
242 // field catches -flagsChanged: because it's on the responder chain,
243 // the field editor doesn't implement it.
244 TEST_F(AutocompleteTextFieldObserverTest, FieldEditorFlagsChanged) {
245   // Many of these methods try to change the selection.
246   EXPECT_CALL(field_observer_, SelectionRangeForProposedRange(A<NSRange>()))
247       .WillRepeatedly(ReturnArg<0>());
249   InSequence dummy;  // Call mock in exactly the order specified.
250   EXPECT_CALL(field_observer_, OnSetFocus(false));
251   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
252   NSResponder* firstResponder = [[field_ window] firstResponder];
253   EXPECT_EQ(firstResponder, [field_ currentEditor]);
255   // Test without Control key down, but some other modifier down.
256   EXPECT_CALL(field_observer_, OnControlKeyChanged(false));
257   [firstResponder flagsChanged:KeyDownEventWithFlags(NSShiftKeyMask)];
259   // Test with Control key down.
260   EXPECT_CALL(field_observer_, OnControlKeyChanged(true));
261   [firstResponder flagsChanged:KeyDownEventWithFlags(NSControlKeyMask)];
264 // Frame size changes are propagated to |observer_|.
265 TEST_F(AutocompleteTextFieldObserverTest, FrameChanged) {
266   EXPECT_CALL(field_observer_, OnFrameChanged());
267   NSRect frame = [field_ frame];
268   frame.size.width += 10.0;
269   [field_ setFrame:frame];
272 // Test that the field editor gets the same bounds when focus is
273 // delivered by the standard focusing machinery, or by
274 // -resetFieldEditorFrameIfNeeded.
275 TEST_F(AutocompleteTextFieldTest, ResetFieldEditorBase) {
276   // Capture the editor frame resulting from the standard focus
277   // machinery.
278   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
279   const NSRect baseEditorFrame = EditorFrame();
281   // A decoration should result in a strictly smaller editor frame.
282   mock_left_decoration_.SetVisible(true);
283   [field_ resetFieldEditorFrameIfNeeded];
284   EXPECT_FALSE(NSEqualRects(baseEditorFrame, EditorFrame()));
285   EXPECT_TRUE(NSContainsRect(baseEditorFrame, EditorFrame()));
287   // Removing the decoration and using -resetFieldEditorFrameIfNeeded
288   // should result in the same frame as the standard focus machinery.
289   mock_left_decoration_.SetVisible(false);
290   [field_ resetFieldEditorFrameIfNeeded];
291   EXPECT_TRUE(NSEqualRects(baseEditorFrame, EditorFrame()));
294 // Test that the field editor gets the same bounds when focus is
295 // delivered by the standard focusing machinery, or by
296 // -resetFieldEditorFrameIfNeeded, this time with a decoration
297 // pre-loaded.
298 TEST_F(AutocompleteTextFieldTest, ResetFieldEditorWithDecoration) {
299   AutocompleteTextFieldCell* cell = [field_ cell];
301   // Make sure decoration isn't already visible, then make it visible.
302   EXPECT_TRUE(NSIsEmptyRect([cell frameForDecoration:&mock_left_decoration_
303                                              inFrame:[field_ bounds]]));
304   mock_left_decoration_.SetVisible(true);
305   EXPECT_FALSE(NSIsEmptyRect([cell frameForDecoration:&mock_left_decoration_
306                                               inFrame:[field_ bounds]]));
308   // Capture the editor frame resulting from the standard focus
309   // machinery.
311   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
312   const NSRect baseEditorFrame = EditorFrame();
314   // When the decoration is not visible the frame should be strictly larger.
315   mock_left_decoration_.SetVisible(false);
316   EXPECT_TRUE(NSIsEmptyRect([cell frameForDecoration:&mock_left_decoration_
317                                              inFrame:[field_ bounds]]));
318   [field_ resetFieldEditorFrameIfNeeded];
319   EXPECT_FALSE(NSEqualRects(baseEditorFrame, EditorFrame()));
320   EXPECT_TRUE(NSContainsRect(EditorFrame(), baseEditorFrame));
322   // When the decoration is visible, -resetFieldEditorFrameIfNeeded
323   // should result in the same frame as the standard focus machinery.
324   mock_left_decoration_.SetVisible(true);
325   EXPECT_FALSE(NSIsEmptyRect([cell frameForDecoration:&mock_left_decoration_
326                                               inFrame:[field_ bounds]]));
328   [field_ resetFieldEditorFrameIfNeeded];
329   EXPECT_TRUE(NSEqualRects(baseEditorFrame, EditorFrame()));
332 // Test that resetting the field editor bounds does not cause untoward
333 // messages to the field's observer.
334 TEST_F(AutocompleteTextFieldObserverTest, ResetFieldEditorContinuesEditing) {
335   // Many of these methods try to change the selection.
336   EXPECT_CALL(field_observer_, SelectionRangeForProposedRange(A<NSRange>()))
337       .WillRepeatedly(ReturnArg<0>());
339   EXPECT_CALL(field_observer_, OnSetFocus(false));
340   // Becoming first responder doesn't begin editing.
341   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
342   const NSRect baseEditorFrame = EditorFrame();
343   NSTextView* editor = static_cast<NSTextView*>([field_ currentEditor]);
344   EXPECT_TRUE(nil != editor);
346   // This should begin editing and indicate a change.
347   EXPECT_CALL(field_observer_, OnDidBeginEditing());
348   EXPECT_CALL(field_observer_, OnBeforeChange());
349   EXPECT_CALL(field_observer_, OnDidChange());
350   [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
351   [editor didChangeText];
353   // No messages to |field_observer_| when the frame actually changes.
354   mock_left_decoration_.SetVisible(true);
355   [field_ resetFieldEditorFrameIfNeeded];
356   EXPECT_FALSE(NSEqualRects(baseEditorFrame, EditorFrame()));
359 // Clicking in a right-hand decoration which does not handle the mouse
360 // puts the caret rightmost.
361 TEST_F(AutocompleteTextFieldTest, ClickRightDecorationPutsCaretRightmost) {
362   // Decoration does not handle the mouse event, so the cell should
363   // process it.  Called at least once.
364   EXPECT_CALL(mock_right_decoration_, AcceptsMousePress())
365       .WillOnce(Return(false))
366       .WillRepeatedly(Return(false));
368   // Set the decoration before becoming responder.
369   EXPECT_FALSE([field_ currentEditor]);
370   mock_right_decoration_.SetVisible(true);
372   // Make first responder should select all.
373   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
374   EXPECT_TRUE([field_ currentEditor]);
375   const NSRange allRange = NSMakeRange(0, [[field_ stringValue] length]);
376   EXPECT_TRUE(NSEqualRanges(allRange, [[field_ currentEditor] selectedRange]));
378   // Generate a click on the decoration.
379   AutocompleteTextFieldCell* cell = [field_ cell];
380   const NSRect bounds = [field_ bounds];
381   const NSRect iconFrame =
382       [cell frameForDecoration:&mock_right_decoration_ inFrame:bounds];
383   const NSPoint point = NSMakePoint(NSMidX(iconFrame), NSMidY(iconFrame));
384   NSEvent* downEvent = Event(field_, point, NSLeftMouseDown);
385   NSEvent* upEvent = Event(field_, point, NSLeftMouseUp);
386   [NSApp postEvent:upEvent atStart:YES];
387   [field_ mouseDown:downEvent];
389   // Selection should be a right-hand-side caret.
390   EXPECT_TRUE(NSEqualRanges(NSMakeRange([[field_ stringValue] length], 0),
391                             [[field_ currentEditor] selectedRange]));
394 // Clicking in a left-side decoration which doesn't handle the event
395 // puts the selection in the leftmost position.
396 TEST_F(AutocompleteTextFieldTest, ClickLeftDecorationPutsCaretLeftmost) {
397   // Decoration does not handle the mouse event, so the cell should
398   // process it.  Called at least once.
399   EXPECT_CALL(mock_left_decoration_, AcceptsMousePress())
400       .WillOnce(Return(false))
401       .WillRepeatedly(Return(false));
403   // Set the decoration before becoming responder.
404   EXPECT_FALSE([field_ currentEditor]);
405   mock_left_decoration_.SetVisible(true);
407   // Make first responder should select all.
408   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
409   EXPECT_TRUE([field_ currentEditor]);
410   const NSRange allRange = NSMakeRange(0, [[field_ stringValue] length]);
411   EXPECT_TRUE(NSEqualRanges(allRange, [[field_ currentEditor] selectedRange]));
413   // Generate a click on the decoration.
414   AutocompleteTextFieldCell* cell = [field_ cell];
415   const NSRect bounds = [field_ bounds];
416   const NSRect iconFrame =
417       [cell frameForDecoration:&mock_left_decoration_ inFrame:bounds];
418   const NSPoint point = NSMakePoint(NSMidX(iconFrame), NSMidY(iconFrame));
419   NSEvent* downEvent = Event(field_, point, NSLeftMouseDown);
420   NSEvent* upEvent = Event(field_, point, NSLeftMouseUp);
421   [NSApp postEvent:upEvent atStart:YES];
422   [field_ mouseDown:downEvent];
424   // Selection should be a left-hand-side caret.
425   EXPECT_TRUE(NSEqualRanges(NSMakeRange(0, 0),
426                             [[field_ currentEditor] selectedRange]));
429 // Clicks not in the text area or the cell's decorations fall through
430 // to the editor.
431 TEST_F(AutocompleteTextFieldTest, ClickBorderSelectsAll) {
432   // Can't rely on the window machinery to make us first responder,
433   // here.
434   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
435   EXPECT_TRUE([field_ currentEditor]);
437   const NSPoint point(NSMakePoint(20.0, 1.0));
438   NSEvent* downEvent(Event(field_, point, NSLeftMouseDown));
439   NSEvent* upEvent(Event(field_, point, NSLeftMouseUp));
440   [NSApp postEvent:upEvent atStart:YES];
441   [field_ mouseDown:downEvent];
443   // Clicking in the narrow border area around a Cocoa NSTextField
444   // does a select-all.  Regardless of whether this is a good call, it
445   // works as a test that things get passed down to the editor.
446   const NSRange selectedRange([[field_ currentEditor] selectedRange]);
447   EXPECT_EQ(selectedRange.location, 0U);
448   EXPECT_EQ(selectedRange.length, [[field_ stringValue] length]);
451 // Single-click with no drag should setup a field editor and
452 // select all.
453 TEST_F(AutocompleteTextFieldTest, ClickSelectsAll) {
454   EXPECT_FALSE([field_ currentEditor]);
456   const NSPoint point = NSMakePoint(20.0, NSMidY([field_ bounds]));
457   NSEvent* downEvent(Event(field_, point, NSLeftMouseDown));
458   NSEvent* upEvent(Event(field_, point, NSLeftMouseUp));
459   [NSApp postEvent:upEvent atStart:YES];
460   [field_ mouseDown:downEvent];
461   EXPECT_TRUE([field_ currentEditor]);
462   const NSRange selectedRange([[field_ currentEditor] selectedRange]);
463   EXPECT_EQ(selectedRange.location, 0U);
464   EXPECT_EQ(selectedRange.length, [[field_ stringValue] length]);
467 // Click-drag selects text, not select all.
468 TEST_F(AutocompleteTextFieldTest, ClickDragSelectsText) {
469   EXPECT_FALSE([field_ currentEditor]);
471   NSEvent* downEvent(Event(field_, NSMakePoint(20.0, 5.0), NSLeftMouseDown));
472   NSEvent* upEvent(Event(field_, NSMakePoint(0.0, 5.0), NSLeftMouseUp));
473   [NSApp postEvent:upEvent atStart:YES];
474   [field_ mouseDown:downEvent];
475   EXPECT_TRUE([field_ currentEditor]);
477   // Expect this to have selected a prefix of the content.  Mostly
478   // just don't want the select-all behavior.
479   const NSRange selectedRange([[field_ currentEditor] selectedRange]);
480   EXPECT_EQ(selectedRange.location, 0U);
481   EXPECT_LT(selectedRange.length, [[field_ stringValue] length]);
484 // TODO(shess): Test that click/pause/click allows cursor placement.
485 // In this case the first click goes to the field, but the second
486 // click goes to the field editor, so the current testing pattern
487 // can't work.  What really needs to happen is to push through the
488 // NSWindow event machinery so that we can say "two independent clicks
489 // at the same location have the right effect".  Once that is done, it
490 // might make sense to revise the other tests to use the same
491 // machinery.
493 // Double-click selects word, not select all.
494 TEST_F(AutocompleteTextFieldTest, DoubleClickSelectsWord) {
495   EXPECT_FALSE([field_ currentEditor]);
497   const NSPoint point = NSMakePoint(20.0, NSMidY([field_ bounds]));
498   NSEvent* downEvent(Event(field_, point, NSLeftMouseDown, 1));
499   NSEvent* upEvent(Event(field_, point, NSLeftMouseUp, 1));
500   NSEvent* downEvent2(Event(field_, point, NSLeftMouseDown, 2));
501   NSEvent* upEvent2(Event(field_, point, NSLeftMouseUp, 2));
502   [NSApp postEvent:upEvent atStart:YES];
503   [field_ mouseDown:downEvent];
504   [NSApp postEvent:upEvent2 atStart:YES];
505   [field_ mouseDown:downEvent2];
506   EXPECT_TRUE([field_ currentEditor]);
508   // Selected the first word.
509   const NSRange selectedRange([[field_ currentEditor] selectedRange]);
510   const NSRange spaceRange([[field_ stringValue] rangeOfString:@" "]);
511   EXPECT_GT(spaceRange.location, 0U);
512   EXPECT_LT(spaceRange.length, [[field_ stringValue] length]);
513   EXPECT_EQ(selectedRange.location, 0U);
514   EXPECT_EQ(selectedRange.length, spaceRange.location);
517 TEST_F(AutocompleteTextFieldTest, TripleClickSelectsAll) {
518   EXPECT_FALSE([field_ currentEditor]);
520   const NSPoint point(NSMakePoint(20.0, 5.0));
521   NSEvent* downEvent(Event(field_, point, NSLeftMouseDown, 1));
522   NSEvent* upEvent(Event(field_, point, NSLeftMouseUp, 1));
523   NSEvent* downEvent2(Event(field_, point, NSLeftMouseDown, 2));
524   NSEvent* upEvent2(Event(field_, point, NSLeftMouseUp, 2));
525   NSEvent* downEvent3(Event(field_, point, NSLeftMouseDown, 3));
526   NSEvent* upEvent3(Event(field_, point, NSLeftMouseUp, 3));
527   [NSApp postEvent:upEvent atStart:YES];
528   [field_ mouseDown:downEvent];
529   [NSApp postEvent:upEvent2 atStart:YES];
530   [field_ mouseDown:downEvent2];
531   [NSApp postEvent:upEvent3 atStart:YES];
532   [field_ mouseDown:downEvent3];
533   EXPECT_TRUE([field_ currentEditor]);
535   // Selected the first word.
536   const NSRange selectedRange([[field_ currentEditor] selectedRange]);
537   EXPECT_EQ(selectedRange.location, 0U);
538   EXPECT_EQ(selectedRange.length, [[field_ stringValue] length]);
541 // Clicking a decoration should call decoration's OnMousePressed.
542 TEST_F(AutocompleteTextFieldTest, LeftDecorationMouseDown) {
543   // At this point, not focussed.
544   EXPECT_FALSE([field_ currentEditor]);
546   mock_left_decoration_.SetVisible(true);
547   EXPECT_CALL(mock_left_decoration_, AcceptsMousePress())
548       .WillRepeatedly(Return(true));
550   AutocompleteTextFieldCell* cell = [field_ cell];
551   const NSRect iconFrame =
552       [cell frameForDecoration:&mock_left_decoration_ inFrame:[field_ bounds]];
553   const NSPoint location = NSMakePoint(NSMidX(iconFrame), NSMidY(iconFrame));
554   NSEvent* downEvent = Event(field_, location, NSLeftMouseDown, 1);
555   NSEvent* upEvent = Event(field_, location, NSLeftMouseUp, 1);
557   // Since decorations can be dragged, the mouse-press is sent on
558   // mouse-up.
559   [NSApp postEvent:upEvent atStart:YES];
561   EXPECT_CALL(mock_left_decoration_, OnMousePressed(_))
562       .WillOnce(Return(true));
563   [field_ mouseDown:downEvent];
565   // Focus the field and test that handled clicks don't affect selection.
566   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
567   EXPECT_TRUE([field_ currentEditor]);
568   const NSRange allRange = NSMakeRange(0, [[field_ stringValue] length]);
569   EXPECT_TRUE(NSEqualRanges(allRange, [[field_ currentEditor] selectedRange]));
571   // Generate another click on the decoration.
572   downEvent = Event(field_, location, NSLeftMouseDown, 1);
573   upEvent = Event(field_, location, NSLeftMouseUp, 1);
574   [NSApp postEvent:upEvent atStart:YES];
575   EXPECT_CALL(mock_left_decoration_, OnMousePressed(_))
576       .WillOnce(Return(true));
577   [field_ mouseDown:downEvent];
579   // The selection should not have changed.
580   EXPECT_TRUE(NSEqualRanges(allRange, [[field_ currentEditor] selectedRange]));
582   // TODO(shess): Test that mouse drags are initiated if the next
583   // event is a drag, or if the mouse-up takes too long to arrive.
584   // IDEA: mock decoration to return a pasteboard which a mock
585   // AutocompleteTextField notes in -dragImage:*.
588 // Clicking a decoration should call decoration's OnMousePressed.
589 TEST_F(AutocompleteTextFieldTest, RightDecorationMouseDown) {
590   // At this point, not focussed.
591   EXPECT_FALSE([field_ currentEditor]);
593   mock_right_decoration_.SetVisible(true);
594   EXPECT_CALL(mock_right_decoration_, AcceptsMousePress())
595       .WillRepeatedly(Return(true));
597   AutocompleteTextFieldCell* cell = [field_ cell];
598   const NSRect bounds = [field_ bounds];
599   const NSRect iconFrame =
600       [cell frameForDecoration:&mock_right_decoration_ inFrame:bounds];
601   const NSPoint location = NSMakePoint(NSMidX(iconFrame), NSMidY(iconFrame));
602   NSEvent* downEvent = Event(field_, location, NSLeftMouseDown, 1);
603   NSEvent* upEvent = Event(field_, location, NSLeftMouseUp, 1);
605   // Since decorations can be dragged, the mouse-press is sent on
606   // mouse-up.
607   [NSApp postEvent:upEvent atStart:YES];
609   EXPECT_CALL(mock_right_decoration_, OnMousePressed(_))
610       .WillOnce(Return(true));
611   [field_ mouseDown:downEvent];
614 // Test that page action menus are properly returned.
615 // TODO(shess): Really, this should test that things are forwarded to
616 // the cell, and the cell tests should test that the right things are
617 // selected.  It's easier to mock the event here, though.  This code's
618 // event-mockers might be worth promoting to |cocoa_test_event_utils.h| or
619 // |cocoa_test_helper.h|.
620 TEST_F(AutocompleteTextFieldTest, DecorationMenu) {
621   AutocompleteTextFieldCell* cell = [field_ cell];
622   const NSRect bounds([field_ bounds]);
624   const CGFloat edge = NSHeight(bounds) - 4.0;
625   const NSSize size = NSMakeSize(edge, edge);
626   base::scoped_nsobject<NSImage> image([[NSImage alloc] initWithSize:size]);
628   base::scoped_nsobject<NSMenu> menu([[NSMenu alloc] initWithTitle:@"Menu"]);
630   mock_left_decoration_.SetVisible(true);
631   mock_right_decoration_.SetVisible(true);
633   // The item with a menu returns it.
634   NSRect actionFrame = [cell frameForDecoration:&mock_right_decoration_
635                                         inFrame:bounds];
636   NSPoint location = NSMakePoint(NSMidX(actionFrame), NSMidY(actionFrame));
637   NSEvent* event = Event(field_, location, NSRightMouseDown, 1);
639   // Check that the decoration is called, and the field returns the
640   // menu.
641   EXPECT_CALL(mock_right_decoration_, GetMenu())
642       .WillOnce(Return(menu.get()));
643   NSMenu *decorationMenu = [field_ decorationMenuForEvent:event];
644   EXPECT_EQ(decorationMenu, menu);
646   // The item without a menu returns nil.
647   EXPECT_CALL(mock_left_decoration_, GetMenu())
648       .WillOnce(Return(static_cast<NSMenu*>(nil)));
649   actionFrame = [cell frameForDecoration:&mock_left_decoration_
650                                  inFrame:bounds];
651   location = NSMakePoint(NSMidX(actionFrame), NSMidY(actionFrame));
652   event = Event(field_, location, NSRightMouseDown, 1);
653   EXPECT_FALSE([field_ decorationMenuForEvent:event]);
655   // Something not in an action returns nil.
656   location = NSMakePoint(NSMidX(bounds), NSMidY(bounds));
657   event = Event(field_, location, NSRightMouseDown, 1);
658   EXPECT_FALSE([field_ decorationMenuForEvent:event]);
661 // Verify that -setAttributedStringValue: works as expected when
662 // focussed or when not focussed.  Our code mostly depends on about
663 // whether -stringValue works right.
664 TEST_F(AutocompleteTextFieldTest, SetAttributedStringBaseline) {
665   EXPECT_EQ(nil, [field_ currentEditor]);
667   // So that we can set rich text.
668   [field_ setAllowsEditingTextAttributes:YES];
670   // Set an attribute different from the field's default so we can
671   // tell we got the same string out as we put in.
672   NSFont* font = [NSFont fontWithDescriptor:[[field_ font] fontDescriptor]
673                                        size:[[field_ font] pointSize] + 2];
674   NSDictionary* attributes =
675       [NSDictionary dictionaryWithObject:font
676                                   forKey:NSFontAttributeName];
677   NSString* const kString = @"This is a test";
678   base::scoped_nsobject<NSAttributedString> attributedString(
679       [[NSAttributedString alloc] initWithString:kString
680                                       attributes:attributes]);
682   // Check that what we get back looks like what we put in.
683   EXPECT_NSNE(kString, [field_ stringValue]);
684   [field_ setAttributedStringValue:attributedString];
685   EXPECT_TRUE([[field_ attributedStringValue]
686                 isEqualToAttributedString:attributedString]);
687   EXPECT_NSEQ(kString, [field_ stringValue]);
689   // Try that again with focus.
690   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
692   EXPECT_TRUE([field_ currentEditor]);
694   // Check that what we get back looks like what we put in.
695   [field_ setStringValue:@""];
696   EXPECT_NSNE(kString, [field_ stringValue]);
697   [field_ setAttributedStringValue:attributedString];
698   EXPECT_TRUE([[field_ attributedStringValue]
699                 isEqualToAttributedString:attributedString]);
700   EXPECT_NSEQ(kString, [field_ stringValue]);
703 // -setAttributedStringValue: shouldn't reset the undo state if things
704 // are being editted.
705 TEST_F(AutocompleteTextFieldTest, SetAttributedStringUndo) {
706   NSColor* redColor = [NSColor redColor];
707   NSDictionary* attributes =
708       [NSDictionary dictionaryWithObject:redColor
709                                   forKey:NSForegroundColorAttributeName];
710   NSString* const kString = @"This is a test";
711   base::scoped_nsobject<NSAttributedString> attributedString(
712       [[NSAttributedString alloc] initWithString:kString
713                                       attributes:attributes]);
714   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
715   EXPECT_TRUE([field_ currentEditor]);
716   NSTextView* editor = static_cast<NSTextView*>([field_ currentEditor]);
717   NSUndoManager* undoManager = [editor undoManager];
718   EXPECT_TRUE(undoManager);
720   // Nothing to undo, yet.
721   EXPECT_FALSE([undoManager canUndo]);
723   // Starting an editing action creates an undoable item.
724   [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
725   [editor didChangeText];
726   EXPECT_TRUE([undoManager canUndo]);
728   // -setStringValue: resets the editor's undo chain.
729   [field_ setStringValue:kString];
730   EXPECT_FALSE([undoManager canUndo]);
732   // Verify that -setAttributedStringValue: does not reset the
733   // editor's undo chain.
734   [field_ setStringValue:@""];
735   [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
736   [editor didChangeText];
737   EXPECT_TRUE([undoManager canUndo]);
738   [field_ setAttributedStringValue:attributedString];
739   EXPECT_TRUE([undoManager canUndo]);
741   // Verify that calling -clearUndoChain clears the undo chain.
742   [field_ clearUndoChain];
743   EXPECT_FALSE([undoManager canUndo]);
746 TEST_F(AutocompleteTextFieldTest, EditorGetsCorrectUndoManager) {
747   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
749   NSTextView* editor = static_cast<NSTextView*>([field_ currentEditor]);
750   EXPECT_TRUE(editor);
751   EXPECT_EQ([field_ undoManagerForTextView:editor], [editor undoManager]);
754 // Verify that hideFocusState correctly hides the focus ring and insertion
755 // pointer.
756 TEST_F(AutocompleteTextFieldTest, HideFocusState) {
757   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
758   [[field_ cell] setShowsFirstResponder:YES];
760   EXPECT_TRUE([[field_ cell] showsFirstResponder]);
761   EXPECT_TRUE([FieldEditor() shouldDrawInsertionPoint]);
763   [[field_ cell] setHideFocusState:YES
764                             ofView:field_];
765   EXPECT_FALSE([[field_ cell] showsFirstResponder]);
766   EXPECT_FALSE([FieldEditor() shouldDrawInsertionPoint]);
768   [[field_ cell] setHideFocusState:NO
769                             ofView:field_];
770   EXPECT_TRUE([[field_ cell] showsFirstResponder]);
771   EXPECT_TRUE([FieldEditor() shouldDrawInsertionPoint]);
774 TEST_F(AutocompleteTextFieldObserverTest, SendsEditingMessages) {
775   // Many of these methods try to change the selection.
776   EXPECT_CALL(field_observer_, SelectionRangeForProposedRange(A<NSRange>()))
777       .WillRepeatedly(ReturnArg<0>());
779   EXPECT_CALL(field_observer_, OnSetFocus(false));
780   // Becoming first responder doesn't begin editing.
781   [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
782   NSTextView* editor = static_cast<NSTextView*>([field_ currentEditor]);
783   EXPECT_TRUE(nil != editor);
785   // This should begin editing and indicate a change.
786   EXPECT_CALL(field_observer_, OnDidBeginEditing());
787   EXPECT_CALL(field_observer_, OnBeforeChange());
788   EXPECT_CALL(field_observer_, OnDidChange());
789   [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
790   [editor didChangeText];
792   // Further changes don't send the begin message.
793   EXPECT_CALL(field_observer_, OnBeforeChange());
794   EXPECT_CALL(field_observer_, OnDidChange());
795   [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
796   [editor didChangeText];
798   // -doCommandBySelector: should forward to observer via |field_|.
799   // TODO(shess): Test with a fake arrow-key event?
800   const SEL cmd = @selector(moveDown:);
801   EXPECT_CALL(field_observer_, OnDoCommandBySelector(cmd))
802       .WillOnce(Return(true));
803   [editor doCommandBySelector:cmd];
805   // Finished with the changes.
806   EXPECT_CALL(field_observer_, OnKillFocus());
807   EXPECT_CALL(field_observer_, OnDidEndEditing());
808   [test_window() clearPretendKeyWindowAndFirstResponder];
811 // Test that the resign-key notification is forwarded right, and that
812 // the notification is registered and unregistered when the view moves
813 // in and out of the window.
814 // TODO(shess): Should this test the key window for realz?  That would
815 // be really annoying to whoever is running the tests.
816 TEST_F(AutocompleteTextFieldObserverTest, ClosePopupOnResignKey) {
817   EXPECT_CALL(field_observer_, ClosePopup());
818   [test_window() resignKeyWindow];
820   base::scoped_nsobject<AutocompleteTextField> pin([field_ retain]);
821   [field_ removeFromSuperview];
822   [test_window() resignKeyWindow];
824   [[test_window() contentView] addSubview:field_];
825   EXPECT_CALL(field_observer_, ClosePopup());
826   [test_window() resignKeyWindow];
829 }  // namespace