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