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 "chrome/browser/ui/cocoa/base_bubble_controller.h"
7 #include "base/mac/mac_util.h"
8 #import "base/mac/scoped_nsobject.h"
9 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
10 #import "chrome/browser/ui/cocoa/info_bubble_view.h"
11 #import "chrome/browser/ui/cocoa/run_loop_testing.h"
12 #import "ui/events/test/cocoa_test_event_utils.h"
15 const CGFloat kBubbleWindowWidth = 100;
16 const CGFloat kBubbleWindowHeight = 50;
17 const CGFloat kAnchorPointX = 400;
18 const CGFloat kAnchorPointY = 300;
21 class BaseBubbleControllerTest : public CocoaTest {
23 virtual void SetUp() OVERRIDE {
24 bubbleWindow_.reset([[NSWindow alloc]
25 initWithContentRect:NSMakeRect(0, 0, kBubbleWindowWidth,
27 styleMask:NSBorderlessWindowMask
28 backing:NSBackingStoreBuffered
31 // The bubble controller will release itself when the window closes.
32 controller_ = [[BaseBubbleController alloc]
33 initWithWindow:bubbleWindow_.get()
34 parentWindow:test_window()
35 anchoredAt:NSMakePoint(kAnchorPointX, kAnchorPointY)];
36 EXPECT_TRUE([controller_ bubble]);
39 virtual void TearDown() OVERRIDE {
42 bubbleWindow_.reset(NULL);
43 CocoaTest::TearDown();
47 base::scoped_nsobject<NSWindow> bubbleWindow_;
48 BaseBubbleController* controller_;
51 // Test that kAlignEdgeToAnchorEdge and a left bubble arrow correctly aligns the
52 // left edge of the buble to the anchor point.
53 TEST_F(BaseBubbleControllerTest, LeftAlign) {
54 [[controller_ bubble] setArrowLocation:info_bubble::kTopLeft];
55 [[controller_ bubble] setAlignment:info_bubble::kAlignEdgeToAnchorEdge];
56 [controller_ showWindow:nil];
58 NSRect frame = [[controller_ window] frame];
59 // Make sure the bubble size hasn't changed.
60 EXPECT_EQ(frame.size.width, kBubbleWindowWidth);
61 EXPECT_EQ(frame.size.height, kBubbleWindowHeight);
62 // Make sure the bubble is left aligned.
63 EXPECT_EQ(NSMinX(frame), kAnchorPointX);
64 EXPECT_GE(NSMaxY(frame), kAnchorPointY);
67 // Test that kAlignEdgeToAnchorEdge and a right bubble arrow correctly aligns
68 // the right edge of the buble to the anchor point.
69 TEST_F(BaseBubbleControllerTest, RightAlign) {
70 [[controller_ bubble] setArrowLocation:info_bubble::kTopRight];
71 [[controller_ bubble] setAlignment:info_bubble::kAlignEdgeToAnchorEdge];
72 [controller_ showWindow:nil];
74 NSRect frame = [[controller_ window] frame];
75 // Make sure the bubble size hasn't changed.
76 EXPECT_EQ(frame.size.width, kBubbleWindowWidth);
77 EXPECT_EQ(frame.size.height, kBubbleWindowHeight);
78 // Make sure the bubble is left aligned.
79 EXPECT_EQ(NSMaxX(frame), kAnchorPointX);
80 EXPECT_GE(NSMaxY(frame), kAnchorPointY);
83 // Test that kAlignArrowToAnchor and a left bubble arrow correctly aligns
84 // the bubble arrow to the anchor point.
85 TEST_F(BaseBubbleControllerTest, AnchorAlignLeftArrow) {
86 [[controller_ bubble] setArrowLocation:info_bubble::kTopLeft];
87 [[controller_ bubble] setAlignment:info_bubble::kAlignArrowToAnchor];
88 [controller_ showWindow:nil];
90 NSRect frame = [[controller_ window] frame];
91 // Make sure the bubble size hasn't changed.
92 EXPECT_EQ(frame.size.width, kBubbleWindowWidth);
93 EXPECT_EQ(frame.size.height, kBubbleWindowHeight);
94 // Make sure the bubble arrow points to the anchor.
95 EXPECT_EQ(NSMinX(frame) + info_bubble::kBubbleArrowXOffset +
96 roundf(info_bubble::kBubbleArrowWidth / 2.0), kAnchorPointX);
97 EXPECT_GE(NSMaxY(frame), kAnchorPointY);
100 // Test that kAlignArrowToAnchor and a right bubble arrow correctly aligns
101 // the bubble arrow to the anchor point.
102 TEST_F(BaseBubbleControllerTest, AnchorAlignRightArrow) {
103 [[controller_ bubble] setArrowLocation:info_bubble::kTopRight];
104 [[controller_ bubble] setAlignment:info_bubble::kAlignArrowToAnchor];
105 [controller_ showWindow:nil];
107 NSRect frame = [[controller_ window] frame];
108 // Make sure the bubble size hasn't changed.
109 EXPECT_EQ(frame.size.width, kBubbleWindowWidth);
110 EXPECT_EQ(frame.size.height, kBubbleWindowHeight);
111 // Make sure the bubble arrow points to the anchor.
112 EXPECT_EQ(NSMaxX(frame) - info_bubble::kBubbleArrowXOffset -
113 floorf(info_bubble::kBubbleArrowWidth / 2.0), kAnchorPointX);
114 EXPECT_GE(NSMaxY(frame), kAnchorPointY);
117 // Test that kAlignArrowToAnchor and a center bubble arrow correctly align
118 // the bubble towards the anchor point.
119 TEST_F(BaseBubbleControllerTest, AnchorAlignCenterArrow) {
120 [[controller_ bubble] setArrowLocation:info_bubble::kTopCenter];
121 [[controller_ bubble] setAlignment:info_bubble::kAlignArrowToAnchor];
122 [controller_ showWindow:nil];
124 NSRect frame = [[controller_ window] frame];
125 // Make sure the bubble size hasn't changed.
126 EXPECT_EQ(frame.size.width, kBubbleWindowWidth);
127 EXPECT_EQ(frame.size.height, kBubbleWindowHeight);
128 // Make sure the bubble arrow points to the anchor.
129 EXPECT_EQ(NSMidX(frame), kAnchorPointX);
130 EXPECT_GE(NSMaxY(frame), kAnchorPointY);
133 // Tests that when a new window gets key state (and the bubble resigns) that
134 // the key window changes.
135 TEST_F(BaseBubbleControllerTest, ResignKeyCloses) {
136 // Closing the bubble will autorelease the controller.
137 base::scoped_nsobject<BaseBubbleController> keep_alive([controller_ retain]);
139 NSWindow* bubble_window = [controller_ window];
140 EXPECT_FALSE([bubble_window isVisible]);
142 base::scoped_nsobject<NSWindow> other_window(
143 [[NSWindow alloc] initWithContentRect:NSMakeRect(500, 500, 500, 500)
144 styleMask:NSTitledWindowMask
145 backing:NSBackingStoreBuffered
147 EXPECT_FALSE([other_window isVisible]);
149 [controller_ showWindow:nil];
150 EXPECT_TRUE([bubble_window isVisible]);
151 EXPECT_FALSE([other_window isVisible]);
153 [other_window makeKeyAndOrderFront:nil];
154 // Fake the key state notification. Because unit_tests is a "daemon" process
155 // type, its windows can never become key (nor can the app become active).
156 // Instead of the hacks below, one could make a browser_test or transform the
157 // process type, but this seems easiest and is best suited to a unit test.
159 // On Lion and above, which have the event taps, simply post a notification
160 // that will cause the controller to call |-windowDidResignKey:|. Earlier
161 // OSes can call through directly.
162 NSNotification* notif =
163 [NSNotification notificationWithName:NSWindowDidResignKeyNotification
164 object:bubble_window];
165 if (base::mac::IsOSLionOrLater())
166 [[NSNotificationCenter defaultCenter] postNotification:notif];
168 [controller_ windowDidResignKey:notif];
171 EXPECT_FALSE([bubble_window isVisible]);
172 EXPECT_TRUE([other_window isVisible]);
175 // Test that clicking outside the window causes the bubble to close if
176 // shouldCloseOnResignKey is YES.
177 TEST_F(BaseBubbleControllerTest, LionClickOutsideCloses) {
178 // The event tap is only installed on 10.7+.
179 if (!base::mac::IsOSLionOrLater())
182 // Closing the bubble will autorelease the controller.
183 base::scoped_nsobject<BaseBubbleController> keep_alive([controller_ retain]);
184 NSWindow* window = [controller_ window];
186 EXPECT_TRUE([controller_ shouldCloseOnResignKey]); // Verify default value.
187 EXPECT_FALSE([window isVisible]);
189 [controller_ showWindow:nil];
191 EXPECT_TRUE([window isVisible]);
193 [controller_ setShouldCloseOnResignKey:NO];
194 NSEvent* event = cocoa_test_event_utils::LeftMouseDownAtPointInWindow(
195 NSMakePoint(10, 10), test_window());
196 [NSApp sendEvent:event];
197 chrome::testing::NSRunLoopRunAllPending();
199 EXPECT_TRUE([window isVisible]);
201 [controller_ setShouldCloseOnResignKey:YES];
202 event = cocoa_test_event_utils::LeftMouseDownAtPointInWindow(
203 NSMakePoint(10, 10), test_window());
204 [NSApp sendEvent:event];
205 chrome::testing::NSRunLoopRunAllPending();
207 EXPECT_FALSE([window isVisible]);