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"
24 using ::testing::InSequence;
25 using ::testing::Return;
26 using ::testing::ReturnArg;
27 using ::testing::StrictMock;
32 class MockDecoration : public LocationBarDecoration {
34 virtual CGFloat GetWidthForSpace(CGFloat width) { return 20.0; }
36 virtual void DrawInFrame(NSRect frame, NSView* control_view) { ; }
37 MOCK_METHOD0(AcceptsMousePress, bool());
38 MOCK_METHOD2(OnMousePressed, bool(NSRect frame, NSPoint location));
39 MOCK_METHOD0(GetMenu, NSMenu*());
42 class MockButtonDecoration : public ButtonDecoration {
44 // Note: It does not matter which images are used here - but ButtonDecoration
45 // needs _some_ images to work properly.
46 MockButtonDecoration()
47 : ButtonDecoration(IMAGE_GRID(IDR_OMNIBOX_EV_BUBBLE),
48 IDR_OMNIBOX_EV_BUBBLE_CENTER,
49 IMAGE_GRID(IDR_OMNIBOX_EV_BUBBLE),
50 IDR_OMNIBOX_EV_BUBBLE_CENTER,
51 IMAGE_GRID(IDR_OMNIBOX_EV_BUBBLE),
52 IDR_OMNIBOX_EV_BUBBLE_CENTER,
54 void Hide() { SetVisible(false); }
55 MOCK_METHOD2(OnMousePressed, bool(NSRect frame, NSPoint location));
58 // Mock up an incrementing event number.
59 NSUInteger eventNumber = 0;
61 // Create an event of the indicated |type| at |point| within |view|.
62 // TODO(shess): Would be nice to have a MockApplication which provided
63 // nifty accessors to create these things and inject them. It could
64 // even provide functions for "Click and drag mouse from point A to
66 // TODO(groby): This is very similar to cocoa_testing_utils - unify.
67 NSEvent* Event(NSView* view, const NSPoint point, const NSEventType type,
68 const NSUInteger clickCount) {
69 NSWindow* window([view window]);
70 const NSPoint locationInWindow([view convertPoint:point toView:nil]);
71 const NSPoint location([window convertBaseToScreen:locationInWindow]);
72 return [NSEvent mouseEventWithType:type
76 windowNumber:[window windowNumber]
78 eventNumber:eventNumber++
82 NSEvent* Event(NSView* view, const NSPoint point, const NSEventType type) {
83 return Event(view, point, type, 1);
86 // Width of the field so that we don't have to ask |field_| for it all
88 static const CGFloat kWidth(300.0);
90 class AutocompleteTextFieldTest : public CocoaTest {
92 AutocompleteTextFieldTest() {
93 // Make sure this is wide enough to play games with the cell
95 NSRect frame = NSMakeRect(0, 0, kWidth, 30);
96 base::scoped_nsobject<AutocompleteTextField> field(
97 [[AutocompleteTextField alloc] initWithFrame:frame]);
99 [field_ setStringValue:@"Test test"];
100 [[test_window() contentView] addSubview:field_];
102 AutocompleteTextFieldCell* cell = [field_ cell];
103 [cell clearDecorations];
105 mock_left_decoration_.SetVisible(false);
106 [cell addLeftDecoration:&mock_left_decoration_];
108 mock_right_decoration_.SetVisible(false);
109 [cell addRightDecoration:&mock_right_decoration_];
111 window_delegate_.reset(
112 [[AutocompleteTextFieldWindowTestDelegate alloc] init]);
113 [test_window() setDelegate:window_delegate_.get()];
116 NSEvent* KeyDownEventWithFlags(NSUInteger flags) {
117 return [NSEvent keyEventWithType:NSKeyDown
121 windowNumber:[test_window() windowNumber]
124 charactersIgnoringModifiers:@"a"
129 // Helper to return the field-editor frame being used w/in |field_|.
130 NSRect EditorFrame() {
131 EXPECT_TRUE([field_ currentEditor]);
132 EXPECT_EQ([[field_ subviews] count], 1U);
133 if ([[field_ subviews] count] > 0) {
134 return [[[field_ subviews] objectAtIndex:0] frame];
136 // Return something which won't work so the caller can soldier
142 AutocompleteTextFieldEditor* FieldEditor() {
143 return base::mac::ObjCCastStrict<AutocompleteTextFieldEditor>(
144 [field_ currentEditor]);
147 AutocompleteTextField* field_;
148 MockDecoration mock_left_decoration_;
149 MockDecoration mock_right_decoration_;
150 base::scoped_nsobject<AutocompleteTextFieldWindowTestDelegate>
154 TEST_VIEW(AutocompleteTextFieldTest, field_);
156 // Base class for testing AutocompleteTextFieldObserver messages.
157 class AutocompleteTextFieldObserverTest : public AutocompleteTextFieldTest {
159 virtual void SetUp() {
160 AutocompleteTextFieldTest::SetUp();
161 [field_ setObserver:&field_observer_];
164 virtual void TearDown() {
165 // Clear the observer so that we don't show output for
166 // uninteresting messages to the mock (for instance, if |field_| has
167 // focus at the end of the test).
168 [field_ setObserver:NULL];
170 AutocompleteTextFieldTest::TearDown();
173 StrictMock<MockAutocompleteTextFieldObserver> field_observer_;
176 // Test that we have the right cell class.
177 TEST_F(AutocompleteTextFieldTest, CellClass) {
178 EXPECT_TRUE([[field_ cell] isKindOfClass:[AutocompleteTextFieldCell class]]);
181 // Test that becoming first responder sets things up correctly.
182 TEST_F(AutocompleteTextFieldTest, FirstResponder) {
183 EXPECT_EQ(nil, [field_ currentEditor]);
184 EXPECT_EQ([[field_ subviews] count], 0U);
185 [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
186 EXPECT_FALSE(nil == [field_ currentEditor]);
187 EXPECT_EQ([[field_ subviews] count], 1U);
188 EXPECT_TRUE([[field_ currentEditor] isDescendantOf:field_]);
190 // Check that the window delegate is providing the right editor.
191 Class c = [AutocompleteTextFieldEditor class];
192 EXPECT_TRUE([[field_ currentEditor] isKindOfClass:c]);
195 TEST_F(AutocompleteTextFieldTest, AvailableDecorationWidth) {
196 // A fudge factor to account for how much space the border takes up.
197 // The test shouldn't be too dependent on the field's internals, but
198 // it also shouldn't let deranged cases fall through the cracks
199 // (like nothing available with no text, or everything available
201 const CGFloat kBorderWidth = 20.0;
203 // With no contents, almost the entire width is available for
205 [field_ setStringValue:@""];
206 CGFloat availableWidth = [field_ availableDecorationWidth];
207 EXPECT_LE(availableWidth, kWidth);
208 EXPECT_GT(availableWidth, kWidth - kBorderWidth);
210 // With minor contents, most of the remaining width is available for
212 NSDictionary* attributes =
213 [NSDictionary dictionaryWithObject:[field_ font]
214 forKey:NSFontAttributeName];
215 NSString* string = @"Hello world";
216 const NSSize size([string sizeWithAttributes:attributes]);
217 [field_ setStringValue:string];
218 availableWidth = [field_ availableDecorationWidth];
219 EXPECT_LE(availableWidth, kWidth - size.width);
220 EXPECT_GT(availableWidth, kWidth - size.width - kBorderWidth);
222 // With huge contents, nothing at all is left for decorations.
223 string = @"A long string which is surely wider than field_ can hold.";
224 [field_ setStringValue:string];
225 availableWidth = [field_ availableDecorationWidth];
226 EXPECT_LT(availableWidth, 0.0);
229 // Test drawing, mostly to ensure nothing leaks or crashes.
230 TEST_F(AutocompleteTextFieldTest, Display) {
233 // Test focussed drawing.
234 [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
238 // Test setting gray text, mostly to ensure nothing leaks or crashes.
239 TEST_F(AutocompleteTextFieldTest, GrayText) {
241 EXPECT_FALSE([field_ needsDisplay]);
242 [field_ setGrayTextAutocompletion:@"foo" textColor:[NSColor redColor]];
243 EXPECT_TRUE([field_ needsDisplay]);
247 TEST_F(AutocompleteTextFieldObserverTest, FlagsChanged) {
248 InSequence dummy; // Call mock in exactly the order specified.
250 // Test without Control key down, but some other modifier down.
251 EXPECT_CALL(field_observer_, OnControlKeyChanged(false));
252 [field_ flagsChanged:KeyDownEventWithFlags(NSShiftKeyMask)];
254 // Test with Control key down.
255 EXPECT_CALL(field_observer_, OnControlKeyChanged(true));
256 [field_ flagsChanged:KeyDownEventWithFlags(NSControlKeyMask)];
259 // This test is here rather than in the editor's tests because the
260 // field catches -flagsChanged: because it's on the responder chain,
261 // the field editor doesn't implement it.
262 TEST_F(AutocompleteTextFieldObserverTest, FieldEditorFlagsChanged) {
263 // Many of these methods try to change the selection.
264 EXPECT_CALL(field_observer_, SelectionRangeForProposedRange(A<NSRange>()))
265 .WillRepeatedly(ReturnArg<0>());
267 InSequence dummy; // Call mock in exactly the order specified.
268 EXPECT_CALL(field_observer_, OnSetFocus(false));
269 [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
270 NSResponder* firstResponder = [[field_ window] firstResponder];
271 EXPECT_EQ(firstResponder, [field_ currentEditor]);
273 // Test without Control key down, but some other modifier down.
274 EXPECT_CALL(field_observer_, OnControlKeyChanged(false));
275 [firstResponder flagsChanged:KeyDownEventWithFlags(NSShiftKeyMask)];
277 // Test with Control key down.
278 EXPECT_CALL(field_observer_, OnControlKeyChanged(true));
279 [firstResponder flagsChanged:KeyDownEventWithFlags(NSControlKeyMask)];
282 // Frame size changes are propagated to |observer_|.
283 TEST_F(AutocompleteTextFieldObserverTest, FrameChanged) {
284 EXPECT_CALL(field_observer_, OnFrameChanged());
285 NSRect frame = [field_ frame];
286 frame.size.width += 10.0;
287 [field_ setFrame:frame];
290 // Test that the field editor gets the same bounds when focus is
291 // delivered by the standard focusing machinery, or by
292 // -resetFieldEditorFrameIfNeeded.
293 TEST_F(AutocompleteTextFieldTest, ResetFieldEditorBase) {
294 // Capture the editor frame resulting from the standard focus
296 [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
297 const NSRect baseEditorFrame = EditorFrame();
299 // A decoration should result in a strictly smaller editor frame.
300 mock_left_decoration_.SetVisible(true);
301 [field_ resetFieldEditorFrameIfNeeded];
302 EXPECT_FALSE(NSEqualRects(baseEditorFrame, EditorFrame()));
303 EXPECT_TRUE(NSContainsRect(baseEditorFrame, EditorFrame()));
305 // Removing the decoration and using -resetFieldEditorFrameIfNeeded
306 // should result in the same frame as the standard focus machinery.
307 mock_left_decoration_.SetVisible(false);
308 [field_ resetFieldEditorFrameIfNeeded];
309 EXPECT_TRUE(NSEqualRects(baseEditorFrame, EditorFrame()));
312 // Test that the field editor gets the same bounds when focus is
313 // delivered by the standard focusing machinery, or by
314 // -resetFieldEditorFrameIfNeeded, this time with a decoration
316 TEST_F(AutocompleteTextFieldTest, ResetFieldEditorWithDecoration) {
317 AutocompleteTextFieldCell* cell = [field_ cell];
319 // Make sure decoration isn't already visible, then make it visible.
320 EXPECT_TRUE(NSIsEmptyRect([cell frameForDecoration:&mock_left_decoration_
321 inFrame:[field_ bounds]]));
322 mock_left_decoration_.SetVisible(true);
323 EXPECT_FALSE(NSIsEmptyRect([cell frameForDecoration:&mock_left_decoration_
324 inFrame:[field_ bounds]]));
326 // Capture the editor frame resulting from the standard focus
329 [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
330 const NSRect baseEditorFrame = EditorFrame();
332 // When the decoration is not visible the frame should be strictly larger.
333 mock_left_decoration_.SetVisible(false);
334 EXPECT_TRUE(NSIsEmptyRect([cell frameForDecoration:&mock_left_decoration_
335 inFrame:[field_ bounds]]));
336 [field_ resetFieldEditorFrameIfNeeded];
337 EXPECT_FALSE(NSEqualRects(baseEditorFrame, EditorFrame()));
338 EXPECT_TRUE(NSContainsRect(EditorFrame(), baseEditorFrame));
340 // When the decoration is visible, -resetFieldEditorFrameIfNeeded
341 // should result in the same frame as the standard focus machinery.
342 mock_left_decoration_.SetVisible(true);
343 EXPECT_FALSE(NSIsEmptyRect([cell frameForDecoration:&mock_left_decoration_
344 inFrame:[field_ bounds]]));
346 [field_ resetFieldEditorFrameIfNeeded];
347 EXPECT_TRUE(NSEqualRects(baseEditorFrame, EditorFrame()));
350 // Test that resetting the field editor bounds does not cause untoward
351 // messages to the field's observer.
352 TEST_F(AutocompleteTextFieldObserverTest, ResetFieldEditorContinuesEditing) {
353 // Many of these methods try to change the selection.
354 EXPECT_CALL(field_observer_, SelectionRangeForProposedRange(A<NSRange>()))
355 .WillRepeatedly(ReturnArg<0>());
357 EXPECT_CALL(field_observer_, OnSetFocus(false));
358 // Becoming first responder doesn't begin editing.
359 [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
360 const NSRect baseEditorFrame = EditorFrame();
361 NSTextView* editor = static_cast<NSTextView*>([field_ currentEditor]);
362 EXPECT_TRUE(nil != editor);
364 // This should begin editing and indicate a change.
365 EXPECT_CALL(field_observer_, OnDidBeginEditing());
366 EXPECT_CALL(field_observer_, OnBeforeChange());
367 EXPECT_CALL(field_observer_, OnDidChange());
368 [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
369 [editor didChangeText];
371 // No messages to |field_observer_| when the frame actually changes.
372 mock_left_decoration_.SetVisible(true);
373 [field_ resetFieldEditorFrameIfNeeded];
374 EXPECT_FALSE(NSEqualRects(baseEditorFrame, EditorFrame()));
377 // Clicking in a right-hand decoration which does not handle the mouse
378 // puts the caret rightmost.
379 TEST_F(AutocompleteTextFieldTest, ClickRightDecorationPutsCaretRightmost) {
380 // Decoration does not handle the mouse event, so the cell should
381 // process it. Called at least once.
382 EXPECT_CALL(mock_right_decoration_, AcceptsMousePress())
383 .WillOnce(Return(false))
384 .WillRepeatedly(Return(false));
386 // Set the decoration before becoming responder.
387 EXPECT_FALSE([field_ currentEditor]);
388 mock_right_decoration_.SetVisible(true);
390 // Make first responder should select all.
391 [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
392 EXPECT_TRUE([field_ currentEditor]);
393 const NSRange allRange = NSMakeRange(0, [[field_ stringValue] length]);
394 EXPECT_TRUE(NSEqualRanges(allRange, [[field_ currentEditor] selectedRange]));
396 // Generate a click on the decoration.
397 AutocompleteTextFieldCell* cell = [field_ cell];
398 const NSRect bounds = [field_ bounds];
399 const NSRect iconFrame =
400 [cell frameForDecoration:&mock_right_decoration_ inFrame:bounds];
401 const NSPoint point = NSMakePoint(NSMidX(iconFrame), NSMidY(iconFrame));
402 NSEvent* downEvent = Event(field_, point, NSLeftMouseDown);
403 NSEvent* upEvent = Event(field_, point, NSLeftMouseUp);
404 [NSApp postEvent:upEvent atStart:YES];
405 [field_ mouseDown:downEvent];
407 // Selection should be a right-hand-side caret.
408 EXPECT_TRUE(NSEqualRanges(NSMakeRange([[field_ stringValue] length], 0),
409 [[field_ currentEditor] selectedRange]));
412 // Clicking in a left-side decoration which doesn't handle the event
413 // puts the selection in the leftmost position.
414 TEST_F(AutocompleteTextFieldTest, ClickLeftDecorationPutsCaretLeftmost) {
415 // Decoration does not handle the mouse event, so the cell should
416 // process it. Called at least once.
417 EXPECT_CALL(mock_left_decoration_, AcceptsMousePress())
418 .WillOnce(Return(false))
419 .WillRepeatedly(Return(false));
421 // Set the decoration before becoming responder.
422 EXPECT_FALSE([field_ currentEditor]);
423 mock_left_decoration_.SetVisible(true);
425 // Make first responder should select all.
426 [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
427 EXPECT_TRUE([field_ currentEditor]);
428 const NSRange allRange = NSMakeRange(0, [[field_ stringValue] length]);
429 EXPECT_TRUE(NSEqualRanges(allRange, [[field_ currentEditor] selectedRange]));
431 // Generate a click on the decoration.
432 AutocompleteTextFieldCell* cell = [field_ cell];
433 const NSRect bounds = [field_ bounds];
434 const NSRect iconFrame =
435 [cell frameForDecoration:&mock_left_decoration_ inFrame:bounds];
436 const NSPoint point = NSMakePoint(NSMidX(iconFrame), NSMidY(iconFrame));
437 NSEvent* downEvent = Event(field_, point, NSLeftMouseDown);
438 NSEvent* upEvent = Event(field_, point, NSLeftMouseUp);
439 [NSApp postEvent:upEvent atStart:YES];
440 [field_ mouseDown:downEvent];
442 // Selection should be a left-hand-side caret.
443 EXPECT_TRUE(NSEqualRanges(NSMakeRange(0, 0),
444 [[field_ currentEditor] selectedRange]));
447 // Clicks not in the text area or the cell's decorations fall through
449 TEST_F(AutocompleteTextFieldTest, ClickBorderSelectsAll) {
450 // Can't rely on the window machinery to make us first responder,
452 [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
453 EXPECT_TRUE([field_ currentEditor]);
455 const NSPoint point(NSMakePoint(20.0, 1.0));
456 NSEvent* downEvent(Event(field_, point, NSLeftMouseDown));
457 NSEvent* upEvent(Event(field_, point, NSLeftMouseUp));
458 [NSApp postEvent:upEvent atStart:YES];
459 [field_ mouseDown:downEvent];
461 // Clicking in the narrow border area around a Cocoa NSTextField
462 // does a select-all. Regardless of whether this is a good call, it
463 // works as a test that things get passed down to the editor.
464 const NSRange selectedRange([[field_ currentEditor] selectedRange]);
465 EXPECT_EQ(selectedRange.location, 0U);
466 EXPECT_EQ(selectedRange.length, [[field_ stringValue] length]);
469 // Single-click with no drag should setup a field editor and
471 TEST_F(AutocompleteTextFieldTest, ClickSelectsAll) {
472 EXPECT_FALSE([field_ currentEditor]);
474 const NSPoint point = NSMakePoint(20.0, NSMidY([field_ bounds]));
475 NSEvent* downEvent(Event(field_, point, NSLeftMouseDown));
476 NSEvent* upEvent(Event(field_, point, NSLeftMouseUp));
477 [NSApp postEvent:upEvent atStart:YES];
478 [field_ mouseDown:downEvent];
479 EXPECT_TRUE([field_ currentEditor]);
480 const NSRange selectedRange([[field_ currentEditor] selectedRange]);
481 EXPECT_EQ(selectedRange.location, 0U);
482 EXPECT_EQ(selectedRange.length, [[field_ stringValue] length]);
485 // Click-drag selects text, not select all.
486 TEST_F(AutocompleteTextFieldTest, ClickDragSelectsText) {
487 EXPECT_FALSE([field_ currentEditor]);
489 NSEvent* downEvent(Event(field_, NSMakePoint(20.0, 5.0), NSLeftMouseDown));
490 NSEvent* upEvent(Event(field_, NSMakePoint(0.0, 5.0), NSLeftMouseUp));
491 [NSApp postEvent:upEvent atStart:YES];
492 [field_ mouseDown:downEvent];
493 EXPECT_TRUE([field_ currentEditor]);
495 // Expect this to have selected a prefix of the content. Mostly
496 // just don't want the select-all behavior.
497 const NSRange selectedRange([[field_ currentEditor] selectedRange]);
498 EXPECT_EQ(selectedRange.location, 0U);
499 EXPECT_LT(selectedRange.length, [[field_ stringValue] length]);
502 // TODO(shess): Test that click/pause/click allows cursor placement.
503 // In this case the first click goes to the field, but the second
504 // click goes to the field editor, so the current testing pattern
505 // can't work. What really needs to happen is to push through the
506 // NSWindow event machinery so that we can say "two independent clicks
507 // at the same location have the right effect". Once that is done, it
508 // might make sense to revise the other tests to use the same
511 // Double-click selects word, not select all.
512 TEST_F(AutocompleteTextFieldTest, DoubleClickSelectsWord) {
513 EXPECT_FALSE([field_ currentEditor]);
515 const NSPoint point = NSMakePoint(20.0, NSMidY([field_ bounds]));
516 NSEvent* downEvent(Event(field_, point, NSLeftMouseDown, 1));
517 NSEvent* upEvent(Event(field_, point, NSLeftMouseUp, 1));
518 NSEvent* downEvent2(Event(field_, point, NSLeftMouseDown, 2));
519 NSEvent* upEvent2(Event(field_, point, NSLeftMouseUp, 2));
520 [NSApp postEvent:upEvent atStart:YES];
521 [field_ mouseDown:downEvent];
522 [NSApp postEvent:upEvent2 atStart:YES];
523 [field_ mouseDown:downEvent2];
524 EXPECT_TRUE([field_ currentEditor]);
526 // Selected the first word.
527 const NSRange selectedRange([[field_ currentEditor] selectedRange]);
528 const NSRange spaceRange([[field_ stringValue] rangeOfString:@" "]);
529 EXPECT_GT(spaceRange.location, 0U);
530 EXPECT_LT(spaceRange.length, [[field_ stringValue] length]);
531 EXPECT_EQ(selectedRange.location, 0U);
532 EXPECT_EQ(selectedRange.length, spaceRange.location);
535 TEST_F(AutocompleteTextFieldTest, TripleClickSelectsAll) {
536 EXPECT_FALSE([field_ currentEditor]);
538 const NSPoint point(NSMakePoint(20.0, 5.0));
539 NSEvent* downEvent(Event(field_, point, NSLeftMouseDown, 1));
540 NSEvent* upEvent(Event(field_, point, NSLeftMouseUp, 1));
541 NSEvent* downEvent2(Event(field_, point, NSLeftMouseDown, 2));
542 NSEvent* upEvent2(Event(field_, point, NSLeftMouseUp, 2));
543 NSEvent* downEvent3(Event(field_, point, NSLeftMouseDown, 3));
544 NSEvent* upEvent3(Event(field_, point, NSLeftMouseUp, 3));
545 [NSApp postEvent:upEvent atStart:YES];
546 [field_ mouseDown:downEvent];
547 [NSApp postEvent:upEvent2 atStart:YES];
548 [field_ mouseDown:downEvent2];
549 [NSApp postEvent:upEvent3 atStart:YES];
550 [field_ mouseDown:downEvent3];
551 EXPECT_TRUE([field_ currentEditor]);
553 // Selected the first word.
554 const NSRange selectedRange([[field_ currentEditor] selectedRange]);
555 EXPECT_EQ(selectedRange.location, 0U);
556 EXPECT_EQ(selectedRange.length, [[field_ stringValue] length]);
559 // Clicking a decoration should call decoration's OnMousePressed.
560 TEST_F(AutocompleteTextFieldTest, LeftDecorationMouseDown) {
561 // At this point, not focussed.
562 EXPECT_FALSE([field_ currentEditor]);
564 mock_left_decoration_.SetVisible(true);
565 EXPECT_CALL(mock_left_decoration_, AcceptsMousePress())
566 .WillRepeatedly(Return(true));
568 AutocompleteTextFieldCell* cell = [field_ cell];
569 const NSRect iconFrame =
570 [cell frameForDecoration:&mock_left_decoration_ inFrame:[field_ bounds]];
571 const NSPoint location = NSMakePoint(NSMidX(iconFrame), NSMidY(iconFrame));
572 NSEvent* downEvent = Event(field_, location, NSLeftMouseDown, 1);
573 NSEvent* upEvent = Event(field_, location, NSLeftMouseUp, 1);
575 // Since decorations can be dragged, the mouse-press is sent on
577 [NSApp postEvent:upEvent atStart:YES];
579 EXPECT_CALL(mock_left_decoration_, OnMousePressed(_, _))
580 .WillOnce(Return(true));
581 [field_ mouseDown:downEvent];
583 // Focus the field and test that handled clicks don't affect selection.
584 [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
585 EXPECT_TRUE([field_ currentEditor]);
586 const NSRange allRange = NSMakeRange(0, [[field_ stringValue] length]);
587 EXPECT_TRUE(NSEqualRanges(allRange, [[field_ currentEditor] selectedRange]));
589 // Generate another click on the decoration.
590 downEvent = Event(field_, location, NSLeftMouseDown, 1);
591 upEvent = Event(field_, location, NSLeftMouseUp, 1);
592 [NSApp postEvent:upEvent atStart:YES];
593 EXPECT_CALL(mock_left_decoration_, OnMousePressed(_, _))
594 .WillOnce(Return(true));
595 [field_ mouseDown:downEvent];
597 // The selection should not have changed.
598 EXPECT_TRUE(NSEqualRanges(allRange, [[field_ currentEditor] selectedRange]));
600 // TODO(shess): Test that mouse drags are initiated if the next
601 // event is a drag, or if the mouse-up takes too long to arrive.
602 // IDEA: mock decoration to return a pasteboard which a mock
603 // AutocompleteTextField notes in -dragImage:*.
606 // Clicking a decoration should call decoration's OnMousePressed.
607 TEST_F(AutocompleteTextFieldTest, RightDecorationMouseDown) {
608 // At this point, not focussed.
609 EXPECT_FALSE([field_ currentEditor]);
611 mock_right_decoration_.SetVisible(true);
612 EXPECT_CALL(mock_right_decoration_, AcceptsMousePress())
613 .WillRepeatedly(Return(true));
615 AutocompleteTextFieldCell* cell = [field_ cell];
616 const NSRect bounds = [field_ bounds];
617 const NSRect iconFrame =
618 [cell frameForDecoration:&mock_right_decoration_ inFrame:bounds];
619 const NSPoint location = NSMakePoint(NSMidX(iconFrame), NSMidY(iconFrame));
620 NSEvent* downEvent = Event(field_, location, NSLeftMouseDown, 1);
621 NSEvent* upEvent = Event(field_, location, NSLeftMouseUp, 1);
623 // Since decorations can be dragged, the mouse-press is sent on
625 [NSApp postEvent:upEvent atStart:YES];
627 EXPECT_CALL(mock_right_decoration_, OnMousePressed(_, _))
628 .WillOnce(Return(true));
629 [field_ mouseDown:downEvent];
632 // Test that page action menus are properly returned.
633 // TODO(shess): Really, this should test that things are forwarded to
634 // the cell, and the cell tests should test that the right things are
635 // selected. It's easier to mock the event here, though. This code's
636 // event-mockers might be worth promoting to |cocoa_test_event_utils.h| or
637 // |cocoa_test_helper.h|.
638 TEST_F(AutocompleteTextFieldTest, DecorationMenu) {
639 AutocompleteTextFieldCell* cell = [field_ cell];
640 const NSRect bounds([field_ bounds]);
642 const CGFloat edge = NSHeight(bounds) - 4.0;
643 const NSSize size = NSMakeSize(edge, edge);
644 base::scoped_nsobject<NSImage> image([[NSImage alloc] initWithSize:size]);
646 base::scoped_nsobject<NSMenu> menu([[NSMenu alloc] initWithTitle:@"Menu"]);
648 mock_left_decoration_.SetVisible(true);
649 mock_right_decoration_.SetVisible(true);
651 // The item with a menu returns it.
652 NSRect actionFrame = [cell frameForDecoration:&mock_right_decoration_
654 NSPoint location = NSMakePoint(NSMidX(actionFrame), NSMidY(actionFrame));
655 NSEvent* event = Event(field_, location, NSRightMouseDown, 1);
657 // Check that the decoration is called, and the field returns the
659 EXPECT_CALL(mock_right_decoration_, GetMenu())
660 .WillOnce(Return(menu.get()));
661 NSMenu *decorationMenu = [field_ decorationMenuForEvent:event];
662 EXPECT_EQ(decorationMenu, menu);
664 // The item without a menu returns nil.
665 EXPECT_CALL(mock_left_decoration_, GetMenu())
666 .WillOnce(Return(static_cast<NSMenu*>(nil)));
667 actionFrame = [cell frameForDecoration:&mock_left_decoration_
669 location = NSMakePoint(NSMidX(actionFrame), NSMidY(actionFrame));
670 event = Event(field_, location, NSRightMouseDown, 1);
671 EXPECT_FALSE([field_ decorationMenuForEvent:event]);
673 // Something not in an action returns nil.
674 location = NSMakePoint(NSMidX(bounds), NSMidY(bounds));
675 event = Event(field_, location, NSRightMouseDown, 1);
676 EXPECT_FALSE([field_ decorationMenuForEvent:event]);
679 // Verify that -setAttributedStringValue: works as expected when
680 // focussed or when not focussed. Our code mostly depends on about
681 // whether -stringValue works right.
682 TEST_F(AutocompleteTextFieldTest, SetAttributedStringBaseline) {
683 EXPECT_EQ(nil, [field_ currentEditor]);
685 // So that we can set rich text.
686 [field_ setAllowsEditingTextAttributes:YES];
688 // Set an attribute different from the field's default so we can
689 // tell we got the same string out as we put in.
690 NSFont* font = [NSFont fontWithDescriptor:[[field_ font] fontDescriptor]
691 size:[[field_ font] pointSize] + 2];
692 NSDictionary* attributes =
693 [NSDictionary dictionaryWithObject:font
694 forKey:NSFontAttributeName];
695 NSString* const kString = @"This is a test";
696 base::scoped_nsobject<NSAttributedString> attributedString(
697 [[NSAttributedString alloc] initWithString:kString
698 attributes:attributes]);
700 // Check that what we get back looks like what we put in.
701 EXPECT_NSNE(kString, [field_ stringValue]);
702 [field_ setAttributedStringValue:attributedString];
703 EXPECT_TRUE([[field_ attributedStringValue]
704 isEqualToAttributedString:attributedString]);
705 EXPECT_NSEQ(kString, [field_ stringValue]);
707 // Try that again with focus.
708 [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
710 EXPECT_TRUE([field_ currentEditor]);
712 // Check that what we get back looks like what we put in.
713 [field_ setStringValue:@""];
714 EXPECT_NSNE(kString, [field_ stringValue]);
715 [field_ setAttributedStringValue:attributedString];
716 EXPECT_TRUE([[field_ attributedStringValue]
717 isEqualToAttributedString:attributedString]);
718 EXPECT_NSEQ(kString, [field_ stringValue]);
721 // -setAttributedStringValue: shouldn't reset the undo state if things
722 // are being editted.
723 TEST_F(AutocompleteTextFieldTest, SetAttributedStringUndo) {
724 NSColor* redColor = [NSColor redColor];
725 NSDictionary* attributes =
726 [NSDictionary dictionaryWithObject:redColor
727 forKey:NSForegroundColorAttributeName];
728 NSString* const kString = @"This is a test";
729 base::scoped_nsobject<NSAttributedString> attributedString(
730 [[NSAttributedString alloc] initWithString:kString
731 attributes:attributes]);
732 [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
733 EXPECT_TRUE([field_ currentEditor]);
734 NSTextView* editor = static_cast<NSTextView*>([field_ currentEditor]);
735 NSUndoManager* undoManager = [editor undoManager];
736 EXPECT_TRUE(undoManager);
738 // Nothing to undo, yet.
739 EXPECT_FALSE([undoManager canUndo]);
741 // Starting an editing action creates an undoable item.
742 [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
743 [editor didChangeText];
744 EXPECT_TRUE([undoManager canUndo]);
746 // -setStringValue: resets the editor's undo chain.
747 [field_ setStringValue:kString];
748 EXPECT_FALSE([undoManager canUndo]);
750 // Verify that -setAttributedStringValue: does not reset the
751 // editor's undo chain.
752 [field_ setStringValue:@""];
753 [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
754 [editor didChangeText];
755 EXPECT_TRUE([undoManager canUndo]);
756 [field_ setAttributedStringValue:attributedString];
757 EXPECT_TRUE([undoManager canUndo]);
759 // Verify that calling -clearUndoChain clears the undo chain.
760 [field_ clearUndoChain];
761 EXPECT_FALSE([undoManager canUndo]);
764 TEST_F(AutocompleteTextFieldTest, EditorGetsCorrectUndoManager) {
765 [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
767 NSTextView* editor = static_cast<NSTextView*>([field_ currentEditor]);
769 EXPECT_EQ([field_ undoManagerForTextView:editor], [editor undoManager]);
772 // Verify that hideFocusState correctly hides the focus ring and insertion
774 TEST_F(AutocompleteTextFieldTest, HideFocusState) {
775 [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
776 [[field_ cell] setShowsFirstResponder:YES];
778 EXPECT_TRUE([[field_ cell] showsFirstResponder]);
779 EXPECT_TRUE([FieldEditor() shouldDrawInsertionPoint]);
781 [[field_ cell] setHideFocusState:YES
783 EXPECT_FALSE([[field_ cell] showsFirstResponder]);
784 EXPECT_FALSE([FieldEditor() shouldDrawInsertionPoint]);
786 [[field_ cell] setHideFocusState:NO
788 EXPECT_TRUE([[field_ cell] showsFirstResponder]);
789 EXPECT_TRUE([FieldEditor() shouldDrawInsertionPoint]);
792 // Verify that OnSetFocus for button decorations is only sent after the
793 // decoration is picked as the target for the subsequent -mouseDown:. Otherwise
794 // hiding a ButtonDecoration in OnSetFocus will prevent a call to
795 // OnMousePressed, since it is already hidden at the time of mouseDown.
796 TEST_F(AutocompleteTextFieldObserverTest, ButtonDecorationFocus) {
797 // Add the mock button.
798 MockButtonDecoration mock_button;
799 mock_button.SetVisible(true);
800 AutocompleteTextFieldCell* cell = [field_ cell];
801 [cell addLeftDecoration:&mock_button];
803 // Ensure button is hidden when OnSetFocus() is called.
804 EXPECT_CALL(field_observer_, OnSetFocus(false)).WillOnce(
805 testing::InvokeWithoutArgs(&mock_button, &MockButtonDecoration::Hide));
807 // Ignore incidental calls.
808 EXPECT_CALL(field_observer_, SelectionRangeForProposedRange(_))
809 .WillRepeatedly(testing::Return(NSMakeRange(0, 0)));
810 EXPECT_CALL(field_observer_, OnMouseDown(_));
812 // Still expect an OnMousePressed on the button.
813 EXPECT_CALL(mock_button, OnMousePressed(_, _))
814 .WillOnce(testing::Return(true));
816 // Get click point for button decoration.
818 [cell frameForDecoration:&mock_button inFrame:[field_ bounds]];
819 EXPECT_FALSE(NSIsEmptyRect(button_rect));
820 NSPoint click_location =
821 NSMakePoint(NSMidX(button_rect), NSMidY(button_rect));
823 // Ensure the field is currently not first responder.
824 [test_window() makePretendKeyWindowAndSetFirstResponder:nil];
825 EXPECT_NSNE([[field_ window] firstResponder], field_);
827 // Execute button click event sequence.
828 NSEvent* downEvent = Event(field_, click_location, NSLeftMouseDown);
829 NSEvent* upEvent = Event(field_, click_location, NSLeftMouseUp);
831 // Can't just use -sendEvent:, since that doesn't populate -currentEvent.
832 [NSApp postEvent:downEvent atStart:YES];
833 [NSApp postEvent:upEvent atStart:NO];
834 NSEvent* next_event = [NSApp nextEventMatchingMask:NSAnyEventMask
836 inMode:NSDefaultRunLoopMode
838 [NSApp sendEvent:next_event];
840 // Expectations check that both OnSetFocus and OnMouseDown were called.
841 // Additionally, ensure button is hidden and field is firstResponder.
842 EXPECT_FALSE(mock_button.IsVisible());
843 EXPECT_TRUE(NSIsEmptyRect([cell frameForDecoration:&mock_left_decoration_
844 inFrame:[field_ bounds]]));
845 EXPECT_TRUE([base::mac::ObjCCastStrict<NSView>(
846 [[field_ window] firstResponder]) isDescendantOf:field_]);
849 TEST_F(AutocompleteTextFieldObserverTest, SendsEditingMessages) {
850 // Many of these methods try to change the selection.
851 EXPECT_CALL(field_observer_, SelectionRangeForProposedRange(A<NSRange>()))
852 .WillRepeatedly(ReturnArg<0>());
854 EXPECT_CALL(field_observer_, OnSetFocus(false));
855 // Becoming first responder doesn't begin editing.
856 [test_window() makePretendKeyWindowAndSetFirstResponder:field_];
857 NSTextView* editor = static_cast<NSTextView*>([field_ currentEditor]);
858 EXPECT_TRUE(nil != editor);
860 // This should begin editing and indicate a change.
861 EXPECT_CALL(field_observer_, OnDidBeginEditing());
862 EXPECT_CALL(field_observer_, OnBeforeChange());
863 EXPECT_CALL(field_observer_, OnDidChange());
864 [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
865 [editor didChangeText];
867 // Further changes don't send the begin message.
868 EXPECT_CALL(field_observer_, OnBeforeChange());
869 EXPECT_CALL(field_observer_, OnDidChange());
870 [editor shouldChangeTextInRange:NSMakeRange(0, 0) replacementString:@""];
871 [editor didChangeText];
873 // -doCommandBySelector: should forward to observer via |field_|.
874 // TODO(shess): Test with a fake arrow-key event?
875 const SEL cmd = @selector(moveDown:);
876 EXPECT_CALL(field_observer_, OnDoCommandBySelector(cmd))
877 .WillOnce(Return(true));
878 [editor doCommandBySelector:cmd];
880 // Finished with the changes.
881 EXPECT_CALL(field_observer_, OnKillFocus());
882 EXPECT_CALL(field_observer_, OnDidEndEditing());
883 [test_window() clearPretendKeyWindowAndFirstResponder];
886 // Test that the resign-key notification is forwarded right, and that
887 // the notification is registered and unregistered when the view moves
888 // in and out of the window.
889 // TODO(shess): Should this test the key window for realz? That would
890 // be really annoying to whoever is running the tests.
891 TEST_F(AutocompleteTextFieldObserverTest, ClosePopupOnResignKey) {
892 EXPECT_CALL(field_observer_, ClosePopup());
893 [test_window() resignKeyWindow];
895 base::scoped_nsobject<AutocompleteTextField> pin([field_ retain]);
896 [field_ removeFromSuperview];
897 [test_window() resignKeyWindow];
899 [[test_window() contentView] addSubview:field_];
900 EXPECT_CALL(field_observer_, ClosePopup());
901 [test_window() resignKeyWindow];