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