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"
23 using ::testing::InSequence;
24 using ::testing::Return;
25 using ::testing::ReturnArg;
26 using ::testing::StrictMock;
31 class MockDecoration : public LocationBarDecoration {
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
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
59 windowNumber:[window windowNumber]
61 eventNumber:eventNumber++
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
71 static const CGFloat kWidth(300.0);
73 class AutocompleteTextFieldTest : public CocoaTest {
75 AutocompleteTextFieldTest() {
76 // Make sure this is wide enough to play games with the cell
78 NSRect frame = NSMakeRect(0, 0, kWidth, 30);
79 base::scoped_nsobject<AutocompleteTextField> field(
80 [[AutocompleteTextField alloc] initWithFrame:frame]);
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()];
99 NSEvent* KeyDownEventWithFlags(NSUInteger flags) {
100 return [NSEvent keyEventWithType:NSKeyDown
104 windowNumber:[test_window() windowNumber]
107 charactersIgnoringModifiers:@"a"
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];
119 // Return something which won't work so the caller can soldier
125 AutocompleteTextFieldEditor* FieldEditor() {
126 return base::mac::ObjCCastStrict<AutocompleteTextFieldEditor>(
127 [field_ currentEditor]);
130 AutocompleteTextField* field_;
131 MockDecoration mock_left_decoration_;
132 MockDecoration mock_right_decoration_;
133 base::scoped_nsobject<AutocompleteTextFieldWindowTestDelegate>
137 TEST_VIEW(AutocompleteTextFieldTest, field_);
139 // Base class for testing AutocompleteTextFieldObserver messages.
140 class AutocompleteTextFieldObserverTest : public AutocompleteTextFieldTest {
142 virtual void SetUp() {
143 AutocompleteTextFieldTest::SetUp();
144 [field_ setObserver:&field_observer_];
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();
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));
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
176 inMode:NSDefaultRunLoopMode
178 [NSApp sendEvent:next_event];
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
209 const CGFloat kBorderWidth = 20.0;
211 // With no contents, almost the entire width is available for
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
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) {
241 // Test focussed drawing.
242 [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
246 // Test setting gray text, mostly to ensure nothing leaks or crashes.
247 TEST_F(AutocompleteTextFieldTest, GrayText) {
249 EXPECT_FALSE([field_ needsDisplay]);
250 [field_ setGrayTextAutocompletion:@"foo" textColor:[NSColor redColor]];
251 EXPECT_TRUE([field_ needsDisplay]);
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
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
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
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
457 TEST_F(AutocompleteTextFieldTest, ClickBorderSelectsAll) {
458 // Can't rely on the window machinery to make us first responder,
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
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
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
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
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_
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
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_
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]);
777 EXPECT_EQ([field_ undoManagerForTextView:editor], [editor undoManager]);
780 // Verify that hideFocusState correctly hides the focus ring and insertion
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
791 EXPECT_FALSE([[field_ cell] showsFirstResponder]);
792 EXPECT_FALSE([FieldEditor() shouldDrawInsertionPoint]);
794 [[field_ cell] setHideFocusState:NO
796 EXPECT_TRUE([[field_ cell] showsFirstResponder]);
797 EXPECT_TRUE([FieldEditor() shouldDrawInsertionPoint]);
800 // Verify that clicking a decoration that accepts mouse clicks does not focus
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];
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];
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];