1 // Copyright (c) 2011 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 #include <Cocoa/Cocoa.h>
7 #include "base/mac/scoped_nsobject.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/strings/utf_string_conversions.h"
11 #import "chrome/browser/ui/cocoa/bubble_view.h"
12 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
13 #import "chrome/browser/ui/cocoa/status_bubble_mac.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #import "testing/gtest_mac.h"
16 #include "testing/platform_test.h"
17 #include "ui/gfx/point.h"
20 // The test delegate records all of the status bubble object's state
22 @interface StatusBubbleMacTestDelegate : NSObject {
24 NSWindow* window_; // Weak.
25 NSPoint baseFrameOffset_;
26 std::vector<StatusBubbleMac::StatusBubbleState> states_;
28 - (id)initWithWindow:(NSWindow*)window;
29 - (void)forceBaseFrameOffset:(NSPoint)baseFrameOffset;
30 - (NSRect)statusBubbleBaseFrame;
31 - (void)statusBubbleWillEnterState:(StatusBubbleMac::StatusBubbleState)state;
33 @implementation StatusBubbleMacTestDelegate
34 - (id)initWithWindow:(NSWindow*)window {
35 if ((self = [super init])) {
37 baseFrameOffset_ = NSZeroPoint;
41 - (void)forceBaseFrameOffset:(NSPoint)baseFrameOffset {
42 baseFrameOffset_ = baseFrameOffset;
44 - (NSRect)statusBubbleBaseFrame {
45 NSView* contentView = [window_ contentView];
46 NSRect baseFrame = [contentView convertRect:[contentView frame] toView:nil];
47 if (baseFrameOffset_.x > 0 || baseFrameOffset_.y > 0) {
48 baseFrame = NSOffsetRect(baseFrame, baseFrameOffset_.x, baseFrameOffset_.y);
49 baseFrame.size.width -= baseFrameOffset_.x;
50 baseFrame.size.height -= baseFrameOffset_.y;
54 - (void)statusBubbleWillEnterState:(StatusBubbleMac::StatusBubbleState)state {
55 states_.push_back(state);
57 - (std::vector<StatusBubbleMac::StatusBubbleState>*)states {
62 // This class implements, for testing purposes, a subclass of |StatusBubbleMac|
63 // whose |MouseMoved()| method does nothing. This lets the tests fake the mouse
64 // position and avoid being affected by the true mouse position.
65 class StatusBubbleMacIgnoreMouseMoved : public StatusBubbleMac {
67 StatusBubbleMacIgnoreMouseMoved(NSWindow* parent, id delegate)
68 : StatusBubbleMac(parent, delegate), mouseLocation_(0, 0) {
69 // Set the fake mouse position to the top right of the content area.
70 NSRect contentBounds = [[parent contentView] bounds];
71 mouseLocation_.SetPoint(NSMaxX(contentBounds), NSMaxY(contentBounds));
74 virtual void MouseMoved(
75 const gfx::Point& location,
76 bool left_content) OVERRIDE {
79 virtual gfx::Point GetMouseLocation() OVERRIDE {
80 return mouseLocation_;
83 void SetMouseLocationForTesting(int x, int y) {
84 mouseLocation_.SetPoint(x, y);
85 StatusBubbleMac::MouseMoved(gfx::Point(x, y), false);
89 gfx::Point mouseLocation_;
92 class StatusBubbleMacTest : public CocoaTest {
94 virtual void SetUp() {
96 NSWindow* window = test_window();
99 [[StatusBubbleMacTestDelegate alloc] initWithWindow: window]);
100 EXPECT_TRUE(delegate_.get());
101 bubble_ = new StatusBubbleMacIgnoreMouseMoved(window, delegate_);
102 EXPECT_TRUE(bubble_);
104 // Turn off delays and transitions for test mode. This doesn't just speed
105 // things along, it's actually required to get StatusBubbleMac to behave
106 // synchronously, because the tests here don't know how to wait for
107 // results. This allows these tests to be much more complete with a
108 // minimal loss of coverage and without any heinous rearchitecting.
109 bubble_->immediate_ = true;
111 EXPECT_TRUE(bubble_->window_); // immediately creates window
114 virtual void TearDown() {
115 // Not using a scoped_ptr because bubble must be deleted before calling
116 // TearDown to get rid of bubble's window.
118 CocoaTest::TearDown();
122 if (![bubble_->window_ isVisible])
124 return [bubble_->window_ alphaValue] > 0.0;
126 NSString* GetText() {
127 return bubble_->status_text_;
129 NSString* GetURLText() {
130 return bubble_->url_text_;
132 NSString* GetBubbleViewText() {
133 BubbleView* bubbleView = [bubble_->window_ contentView];
134 return [bubbleView content];
136 NSWindow* GetWindow() {
137 return bubble_->window_;
140 return bubble_->parent_;
142 StatusBubbleMac::StatusBubbleState GetState() {
143 return bubble_->state_;
145 void SetState(StatusBubbleMac::StatusBubbleState state) {
146 bubble_->SetState(state);
148 std::vector<StatusBubbleMac::StatusBubbleState>* States() {
149 return [delegate_ states];
151 StatusBubbleMac::StatusBubbleState StateAt(int index) {
152 return (*States())[index];
155 bool IsPointInBubble(int x, int y) {
156 return NSPointInRect(NSMakePoint(x, y), [GetWindow() frame]);
159 void SetMouseLocation(int relative_x, int relative_y) {
160 // Convert to screen coordinates.
161 NSRect window_frame = [test_window() frame];
162 int x = relative_x + window_frame.origin.x;
163 int y = relative_y + window_frame.origin.y;
165 ((StatusBubbleMacIgnoreMouseMoved*)
166 bubble_)->SetMouseLocationForTesting(x, y);
169 // Test helper for moving the fake mouse location, and checking that
170 // the bubble avoids that location.
171 // For convenience & clarity, coordinates are relative to the main window.
172 bool CheckAvoidsMouse(int relative_x, int relative_y) {
173 SetMouseLocation(relative_x, relative_y);
174 return !IsPointInBubble(relative_x, relative_y);
177 base::MessageLoop message_loop_;
178 base::scoped_nsobject<StatusBubbleMacTestDelegate> delegate_;
179 StatusBubbleMac* bubble_; // Strong.
182 TEST_F(StatusBubbleMacTest, SetStatus) {
183 bubble_->SetStatus(string16());
184 bubble_->SetStatus(UTF8ToUTF16("This is a test"));
185 EXPECT_NSEQ(@"This is a test", GetText());
186 EXPECT_TRUE(IsVisible());
188 // Set the status to the exact same thing again
189 bubble_->SetStatus(UTF8ToUTF16("This is a test"));
190 EXPECT_NSEQ(@"This is a test", GetText());
193 bubble_->SetStatus(string16());
194 EXPECT_FALSE(IsVisible());
197 TEST_F(StatusBubbleMacTest, SetURL) {
198 bubble_->SetURL(GURL(), std::string());
199 EXPECT_FALSE(IsVisible());
200 bubble_->SetURL(GURL("bad url"), std::string());
201 EXPECT_FALSE(IsVisible());
202 bubble_->SetURL(GURL("http://"), std::string());
203 EXPECT_TRUE(IsVisible());
204 EXPECT_NSEQ(@"http:", GetURLText());
205 bubble_->SetURL(GURL("about:blank"), std::string());
206 EXPECT_TRUE(IsVisible());
207 EXPECT_NSEQ(@"about:blank", GetURLText());
208 bubble_->SetURL(GURL("foopy://"), std::string());
209 EXPECT_TRUE(IsVisible());
210 EXPECT_NSEQ(@"foopy://", GetURLText());
211 bubble_->SetURL(GURL("http://www.cnn.com"), std::string());
212 EXPECT_TRUE(IsVisible());
213 EXPECT_NSEQ(@"www.cnn.com", GetURLText());
216 // Test hiding bubble that's already hidden.
217 TEST_F(StatusBubbleMacTest, Hides) {
218 bubble_->SetStatus(UTF8ToUTF16("Showing"));
219 EXPECT_TRUE(IsVisible());
221 EXPECT_FALSE(IsVisible());
223 EXPECT_FALSE(IsVisible());
226 // Test the "main"/"backup" behavior in StatusBubbleMac::SetText().
227 TEST_F(StatusBubbleMacTest, SetStatusAndURL) {
228 EXPECT_FALSE(IsVisible());
229 bubble_->SetStatus(UTF8ToUTF16("Status"));
230 EXPECT_TRUE(IsVisible());
231 EXPECT_NSEQ(@"Status", GetBubbleViewText());
232 bubble_->SetURL(GURL("http://www.nytimes.com"), std::string());
233 EXPECT_TRUE(IsVisible());
234 EXPECT_NSEQ(@"www.nytimes.com", GetBubbleViewText());
235 bubble_->SetURL(GURL(), std::string());
236 EXPECT_TRUE(IsVisible());
237 EXPECT_NSEQ(@"Status", GetBubbleViewText());
238 bubble_->SetStatus(string16());
239 EXPECT_FALSE(IsVisible());
240 bubble_->SetURL(GURL("http://www.nytimes.com"), std::string());
241 EXPECT_TRUE(IsVisible());
242 EXPECT_NSEQ(@"www.nytimes.com", GetBubbleViewText());
243 bubble_->SetStatus(UTF8ToUTF16("Status"));
244 EXPECT_TRUE(IsVisible());
245 EXPECT_NSEQ(@"Status", GetBubbleViewText());
246 bubble_->SetStatus(string16());
247 EXPECT_TRUE(IsVisible());
248 EXPECT_NSEQ(@"www.nytimes.com", GetBubbleViewText());
249 bubble_->SetURL(GURL(), std::string());
250 EXPECT_FALSE(IsVisible());
253 // Test that the status bubble goes through the correct delay and fade states.
254 // The delay and fade duration are simulated and not actually experienced
255 // during the test because StatusBubbleMacTest sets immediate_ mode.
256 TEST_F(StatusBubbleMacTest, StateTransitions) {
257 // First, some sanity
259 EXPECT_FALSE(IsVisible());
260 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
263 EXPECT_TRUE(States()->empty());
265 bubble_->SetStatus(string16());
266 EXPECT_FALSE(IsVisible());
267 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
268 EXPECT_TRUE(States()->empty()); // no change from initial kBubbleHidden state
270 // Next, a few ordinary cases
272 // Test StartShowing from kBubbleHidden
273 bubble_->SetStatus(UTF8ToUTF16("Status"));
274 EXPECT_TRUE(IsVisible());
275 // Check GetState before checking States to make sure that all state
276 // transitions have been flushed to States.
277 EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState());
278 EXPECT_EQ(3u, States()->size());
279 EXPECT_EQ(StatusBubbleMac::kBubbleShowingTimer, StateAt(0));
280 EXPECT_EQ(StatusBubbleMac::kBubbleShowingFadeIn, StateAt(1));
281 EXPECT_EQ(StatusBubbleMac::kBubbleShown, StateAt(2));
283 // Test StartShowing from kBubbleShown with the same message
285 bubble_->SetStatus(UTF8ToUTF16("Status"));
286 EXPECT_TRUE(IsVisible());
287 EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState());
288 EXPECT_TRUE(States()->empty());
290 // Test StartShowing from kBubbleShown with a different message
291 bubble_->SetStatus(UTF8ToUTF16("New Status"));
292 EXPECT_TRUE(IsVisible());
293 EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState());
294 EXPECT_TRUE(States()->empty());
296 // Test StartHiding from kBubbleShown
297 bubble_->SetStatus(string16());
298 EXPECT_FALSE(IsVisible());
299 // Check GetState before checking States to make sure that all state
300 // transitions have been flushed to States.
301 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
302 EXPECT_EQ(3u, States()->size());
303 EXPECT_EQ(StatusBubbleMac::kBubbleHidingTimer, StateAt(0));
304 EXPECT_EQ(StatusBubbleMac::kBubbleHidingFadeOut, StateAt(1));
305 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(2));
307 // Test StartHiding from kBubbleHidden
309 bubble_->SetStatus(string16());
310 EXPECT_FALSE(IsVisible());
311 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
312 EXPECT_TRUE(States()->empty());
314 // Now, the edge cases
316 // Test StartShowing from kBubbleShowingTimer
317 bubble_->SetStatus(UTF8ToUTF16("Status"));
318 SetState(StatusBubbleMac::kBubbleShowingTimer);
319 [GetWindow() setAlphaValue:0.0];
321 EXPECT_TRUE(States()->empty());
322 bubble_->SetStatus(UTF8ToUTF16("Status"));
323 EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState());
324 EXPECT_EQ(2u, States()->size());
325 EXPECT_EQ(StatusBubbleMac::kBubbleShowingFadeIn, StateAt(0));
326 EXPECT_EQ(StatusBubbleMac::kBubbleShown, StateAt(1));
328 // Test StartShowing from kBubbleShowingFadeIn
329 bubble_->SetStatus(UTF8ToUTF16("Status"));
330 SetState(StatusBubbleMac::kBubbleShowingFadeIn);
331 [GetWindow() setAlphaValue:0.5];
333 EXPECT_TRUE(States()->empty());
334 bubble_->SetStatus(UTF8ToUTF16("Status"));
335 // The actual state values can't be tested in immediate_ mode because
336 // the window wasn't actually fading in. Without immediate_ mode,
337 // expect kBubbleShown.
338 bubble_->SetStatus(string16()); // Go back to a deterministic state.
340 // Test StartShowing from kBubbleHidingTimer
341 bubble_->SetStatus(string16());
342 SetState(StatusBubbleMac::kBubbleHidingTimer);
343 [GetWindow() setAlphaValue:1.0];
345 EXPECT_TRUE(States()->empty());
346 bubble_->SetStatus(UTF8ToUTF16("Status"));
347 EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState());
348 EXPECT_EQ(1u, States()->size());
349 EXPECT_EQ(StatusBubbleMac::kBubbleShown, StateAt(0));
351 // Test StartShowing from kBubbleHidingFadeOut
352 bubble_->SetStatus(string16());
353 SetState(StatusBubbleMac::kBubbleHidingFadeOut);
354 [GetWindow() setAlphaValue:0.5];
356 EXPECT_TRUE(States()->empty());
357 bubble_->SetStatus(UTF8ToUTF16("Status"));
358 EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState());
359 EXPECT_EQ(2u, States()->size());
360 EXPECT_EQ(StatusBubbleMac::kBubbleShowingFadeIn, StateAt(0));
361 EXPECT_EQ(StatusBubbleMac::kBubbleShown, StateAt(1));
363 // Test StartHiding from kBubbleShowingTimer
364 bubble_->SetStatus(UTF8ToUTF16("Status"));
365 SetState(StatusBubbleMac::kBubbleShowingTimer);
366 [GetWindow() setAlphaValue:0.0];
368 EXPECT_TRUE(States()->empty());
369 bubble_->SetStatus(string16());
370 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
371 EXPECT_EQ(1u, States()->size());
372 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0));
374 // Test StartHiding from kBubbleShowingFadeIn
375 bubble_->SetStatus(UTF8ToUTF16("Status"));
376 SetState(StatusBubbleMac::kBubbleShowingFadeIn);
377 [GetWindow() setAlphaValue:0.5];
379 EXPECT_TRUE(States()->empty());
380 bubble_->SetStatus(string16());
381 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
382 EXPECT_EQ(2u, States()->size());
383 EXPECT_EQ(StatusBubbleMac::kBubbleHidingFadeOut, StateAt(0));
384 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(1));
386 // Test StartHiding from kBubbleHidingTimer
387 bubble_->SetStatus(string16());
388 SetState(StatusBubbleMac::kBubbleHidingTimer);
389 [GetWindow() setAlphaValue:1.0];
391 EXPECT_TRUE(States()->empty());
392 bubble_->SetStatus(string16());
393 // The actual state values can't be tested in immediate_ mode because
394 // the timer wasn't actually running. Without immediate_ mode, expect
395 // kBubbleHidingFadeOut and kBubbleHidden.
396 // Go back to a deterministic state.
397 bubble_->SetStatus(UTF8ToUTF16("Status"));
399 // Test StartHiding from kBubbleHidingFadeOut
400 bubble_->SetStatus(string16());
401 SetState(StatusBubbleMac::kBubbleHidingFadeOut);
402 [GetWindow() setAlphaValue:0.5];
404 EXPECT_TRUE(States()->empty());
405 bubble_->SetStatus(string16());
406 // The actual state values can't be tested in immediate_ mode because
407 // the window wasn't actually fading out. Without immediate_ mode, expect
409 // Go back to a deterministic state.
410 bubble_->SetStatus(UTF8ToUTF16("Status"));
412 // Test Hide from kBubbleHidden
413 bubble_->SetStatus(string16());
414 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
416 EXPECT_TRUE(States()->empty());
418 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
419 EXPECT_TRUE(States()->empty());
421 // Test Hide from kBubbleShowingTimer
422 bubble_->SetStatus(UTF8ToUTF16("Status"));
423 SetState(StatusBubbleMac::kBubbleShowingTimer);
424 [GetWindow() setAlphaValue:0.0];
426 EXPECT_TRUE(States()->empty());
428 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
429 EXPECT_EQ(1u, States()->size());
430 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0));
432 // Test Hide from kBubbleShowingFadeIn
433 bubble_->SetStatus(UTF8ToUTF16("Status"));
434 SetState(StatusBubbleMac::kBubbleShowingFadeIn);
435 [GetWindow() setAlphaValue:0.5];
437 EXPECT_TRUE(States()->empty());
439 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
440 EXPECT_EQ(2u, States()->size());
441 EXPECT_EQ(StatusBubbleMac::kBubbleHidingFadeOut, StateAt(0));
442 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(1));
444 // Test Hide from kBubbleShown
445 bubble_->SetStatus(UTF8ToUTF16("Status"));
447 EXPECT_TRUE(States()->empty());
449 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
450 EXPECT_EQ(1u, States()->size());
451 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0));
453 // Test Hide from kBubbleHidingTimer
454 bubble_->SetStatus(UTF8ToUTF16("Status"));
455 SetState(StatusBubbleMac::kBubbleHidingTimer);
457 EXPECT_TRUE(States()->empty());
459 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
460 EXPECT_EQ(1u, States()->size());
461 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0));
463 // Test Hide from kBubbleHidingFadeOut
464 bubble_->SetStatus(UTF8ToUTF16("Status"));
465 SetState(StatusBubbleMac::kBubbleHidingFadeOut);
466 [GetWindow() setAlphaValue:0.5];
468 EXPECT_TRUE(States()->empty());
470 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
471 EXPECT_EQ(1u, States()->size());
472 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0));
475 TEST_F(StatusBubbleMacTest, Delete) {
476 NSWindow* window = test_window();
477 // Create and delete immediately.
478 StatusBubbleMac* bubble = new StatusBubbleMac(window, nil);
481 // Create then delete while visible.
482 bubble = new StatusBubbleMac(window, nil);
483 bubble->SetStatus(UTF8ToUTF16("showing"));
487 TEST_F(StatusBubbleMacTest, UpdateSizeAndPosition) {
488 // Test |UpdateSizeAndPosition()| when status bubble does not exist (shouldn't
489 // crash; shouldn't create window).
490 EXPECT_TRUE(GetWindow());
491 bubble_->UpdateSizeAndPosition();
492 EXPECT_TRUE(GetWindow());
494 // Create a status bubble (with contents) and call resize (without actually
495 // resizing); the frame size shouldn't change.
496 bubble_->SetStatus(UTF8ToUTF16("UpdateSizeAndPosition test"));
497 ASSERT_TRUE(GetWindow());
498 NSRect rect_before = [GetWindow() frame];
499 bubble_->UpdateSizeAndPosition();
500 NSRect rect_after = [GetWindow() frame];
501 EXPECT_TRUE(NSEqualRects(rect_before, rect_after));
503 // Move the window and call resize; only the origin should change.
504 NSWindow* window = test_window();
506 NSRect frame = [window frame];
507 rect_before = [GetWindow() frame];
508 frame.origin.x += 10.0; // (fairly arbitrary nonzero value)
509 frame.origin.y += 10.0; // (fairly arbitrary nonzero value)
510 [window setFrame:frame display:YES];
511 bubble_->UpdateSizeAndPosition();
512 rect_after = [GetWindow() frame];
513 EXPECT_NE(rect_before.origin.x, rect_after.origin.x);
514 EXPECT_NE(rect_before.origin.y, rect_after.origin.y);
515 EXPECT_EQ(rect_before.size.width, rect_after.size.width);
516 EXPECT_EQ(rect_before.size.height, rect_after.size.height);
518 // Resize the window (without moving). The origin shouldn't change. The width
519 // should change (in the current implementation), but not the height.
520 frame = [window frame];
521 rect_before = [GetWindow() frame];
522 frame.size.width += 50.0; // (fairly arbitrary nonzero value)
523 frame.size.height += 50.0; // (fairly arbitrary nonzero value)
524 [window setFrame:frame display:YES];
525 bubble_->UpdateSizeAndPosition();
526 rect_after = [GetWindow() frame];
527 EXPECT_EQ(rect_before.origin.x, rect_after.origin.x);
528 EXPECT_EQ(rect_before.origin.y, rect_after.origin.y);
529 EXPECT_NE(rect_before.size.width, rect_after.size.width);
530 EXPECT_EQ(rect_before.size.height, rect_after.size.height);
533 TEST_F(StatusBubbleMacTest, MovingWindowUpdatesPosition) {
534 NSWindow* window = test_window();
536 // Show the bubble and make sure it has the same origin as |window|.
537 bubble_->SetStatus(UTF8ToUTF16("Showing"));
538 NSWindow* child = GetWindow();
539 EXPECT_TRUE(NSEqualPoints([window frame].origin, [child frame].origin));
541 // Hide the bubble, move the window, and show it again.
543 NSRect frame = [window frame];
544 frame.origin.x += 50;
545 [window setFrame:frame display:YES];
546 bubble_->SetStatus(UTF8ToUTF16("Reshowing"));
548 // The bubble should reattach in the correct location.
550 EXPECT_TRUE(NSEqualPoints([window frame].origin, [child frame].origin));
553 TEST_F(StatusBubbleMacTest, StatuBubbleRespectsBaseFrameLimits) {
554 NSWindow* window = test_window();
556 // Show the bubble and make sure it has the same origin as |window|.
557 bubble_->SetStatus(UTF8ToUTF16("Showing"));
558 NSWindow* child = GetWindow();
559 EXPECT_TRUE(NSEqualPoints([window frame].origin, [child frame].origin));
561 // Hide the bubble, change base frame offset, and show it again.
564 NSPoint baseFrameOffset = NSMakePoint(0, [window frame].size.height / 3);
565 EXPECT_GT(baseFrameOffset.y, 0);
566 [delegate_ forceBaseFrameOffset:baseFrameOffset];
568 bubble_->SetStatus(UTF8ToUTF16("Reshowing"));
570 // The bubble should reattach in the correct location.
572 NSPoint expectedOrigin = [window frame].origin;
573 expectedOrigin.x += baseFrameOffset.x;
574 expectedOrigin.y += baseFrameOffset.y;
575 EXPECT_TRUE(NSEqualPoints(expectedOrigin, [child frame].origin));
578 TEST_F(StatusBubbleMacTest, ExpandBubble) {
579 NSWindow* window = test_window();
581 NSRect window_frame = [window frame];
582 window_frame.size.width = 600.0;
583 [window setFrame:window_frame display:YES];
585 // Check basic expansion
586 bubble_->SetStatus(UTF8ToUTF16("Showing"));
587 EXPECT_TRUE(IsVisible());
588 bubble_->SetURL(GURL("http://www.battersbox.com/peter_paul_and_mary.html"),
590 EXPECT_TRUE([GetURLText() hasSuffix:@"\u2026"]);
591 bubble_->ExpandBubble();
592 EXPECT_TRUE(IsVisible());
593 EXPECT_NSEQ(@"www.battersbox.com/peter_paul_and_mary.html", GetURLText());
596 // Make sure bubble resets after hide.
597 bubble_->SetStatus(UTF8ToUTF16("Showing"));
598 bubble_->SetURL(GURL("http://www.snickersnee.com/pioneer_fishstix.html"),
600 EXPECT_TRUE([GetURLText() hasSuffix:@"\u2026"]);
601 // ...and that it expands again properly.
602 bubble_->ExpandBubble();
603 EXPECT_NSEQ(@"www.snickersnee.com/pioneer_fishstix.html", GetURLText());
605 bubble_->SetURL(GURL("http://www.battersbox.com/peter_paul_and_mary.html"),
607 bubble_->ExpandBubble();
608 EXPECT_NSEQ(@"www.battersbox.com/peter_paul_and_mary.html", GetURLText());
611 window_frame = [window frame];
612 window_frame.size.width = 300.0;
613 [window setFrame:window_frame display:YES];
615 // Very long URL's will be cut off even in the expanded state.
616 bubble_->SetStatus(UTF8ToUTF16("Showing"));
617 const char veryLongUrl[] =
618 "http://www.diewahrscheinlichlaengstepralinederwelt.com/duuuuplo.html";
619 bubble_->SetURL(GURL(veryLongUrl), std::string());
620 EXPECT_TRUE([GetURLText() hasSuffix:@"\u2026"]);
621 bubble_->ExpandBubble();
622 EXPECT_TRUE([GetURLText() hasSuffix:@"\u2026"]);
625 TEST_F(StatusBubbleMacTest, BubbleAvoidsMouse) {
626 NSWindow* window = test_window();
628 // All coordinates here are relative to the window origin.
630 // Initially, the bubble should appear in the bottom left.
631 bubble_->SetStatus(UTF8ToUTF16("Showing"));
632 EXPECT_TRUE(IsPointInBubble(0, 0));
635 // Check that the bubble doesn't appear in the left corner if the
636 // mouse is currently located there.
637 SetMouseLocation(0, 0);
638 bubble_->SetStatus(UTF8ToUTF16("Showing"));
639 EXPECT_FALSE(IsPointInBubble(0, 0));
641 // Leave the bubble visible, and try moving the mouse around.
642 int smallValue = NSHeight([GetWindow() frame]) / 2;
643 EXPECT_TRUE(CheckAvoidsMouse(0, 0));
644 EXPECT_TRUE(CheckAvoidsMouse(smallValue, 0));
645 EXPECT_TRUE(CheckAvoidsMouse(0, smallValue));
646 EXPECT_TRUE(CheckAvoidsMouse(smallValue, smallValue));
648 // Simulate moving the mouse down from the top of the window.
649 for (int y = NSHeight([window frame]); y >= 0; y -= smallValue) {
650 ASSERT_TRUE(CheckAvoidsMouse(smallValue, y));
653 // Simulate moving the mouse from left to right.
654 int windowWidth = NSWidth([window frame]);
655 for (int x = 0; x < windowWidth; x += smallValue) {
656 ASSERT_TRUE(CheckAvoidsMouse(x, smallValue));
659 // Simulate moving the mouse from right to left.
660 for (int x = windowWidth; x >= 0; x -= smallValue) {
661 ASSERT_TRUE(CheckAvoidsMouse(x, smallValue));