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 using base::UTF8ToUTF16;
22 // The test delegate records all of the status bubble object's state
24 @interface StatusBubbleMacTestDelegate : NSObject {
26 NSWindow* window_; // Weak.
27 NSPoint baseFrameOffset_;
28 std::vector<StatusBubbleMac::StatusBubbleState> states_;
30 - (id)initWithWindow:(NSWindow*)window;
31 - (void)forceBaseFrameOffset:(NSPoint)baseFrameOffset;
32 - (NSRect)statusBubbleBaseFrame;
33 - (void)statusBubbleWillEnterState:(StatusBubbleMac::StatusBubbleState)state;
35 @implementation StatusBubbleMacTestDelegate
36 - (id)initWithWindow:(NSWindow*)window {
37 if ((self = [super init])) {
39 baseFrameOffset_ = NSZeroPoint;
43 - (void)forceBaseFrameOffset:(NSPoint)baseFrameOffset {
44 baseFrameOffset_ = baseFrameOffset;
46 - (NSRect)statusBubbleBaseFrame {
47 NSView* contentView = [window_ contentView];
48 NSRect baseFrame = [contentView convertRect:[contentView frame] toView:nil];
49 if (baseFrameOffset_.x > 0 || baseFrameOffset_.y > 0) {
50 baseFrame = NSOffsetRect(baseFrame, baseFrameOffset_.x, baseFrameOffset_.y);
51 baseFrame.size.width -= baseFrameOffset_.x;
52 baseFrame.size.height -= baseFrameOffset_.y;
56 - (void)statusBubbleWillEnterState:(StatusBubbleMac::StatusBubbleState)state {
57 states_.push_back(state);
59 - (std::vector<StatusBubbleMac::StatusBubbleState>*)states {
64 // This class implements, for testing purposes, a subclass of |StatusBubbleMac|
65 // whose |MouseMoved()| method does nothing. This lets the tests fake the mouse
66 // position and avoid being affected by the true mouse position.
67 class StatusBubbleMacIgnoreMouseMoved : public StatusBubbleMac {
69 StatusBubbleMacIgnoreMouseMoved(NSWindow* parent, id delegate)
70 : StatusBubbleMac(parent, delegate), mouseLocation_(0, 0) {
71 // Set the fake mouse position to the top right of the content area.
72 NSRect contentBounds = [[parent contentView] bounds];
73 mouseLocation_.SetPoint(NSMaxX(contentBounds), NSMaxY(contentBounds));
76 virtual void MouseMoved(
77 const gfx::Point& location,
78 bool left_content) OVERRIDE {
81 virtual gfx::Point GetMouseLocation() OVERRIDE {
82 return mouseLocation_;
85 void SetMouseLocationForTesting(int x, int y) {
86 mouseLocation_.SetPoint(x, y);
87 StatusBubbleMac::MouseMoved(gfx::Point(x, y), false);
91 gfx::Point mouseLocation_;
94 class StatusBubbleMacTest : public CocoaTest {
96 virtual void SetUp() {
98 NSWindow* window = test_window();
101 [[StatusBubbleMacTestDelegate alloc] initWithWindow: window]);
102 EXPECT_TRUE(delegate_.get());
103 bubble_ = new StatusBubbleMacIgnoreMouseMoved(window, delegate_);
104 EXPECT_TRUE(bubble_);
106 // Turn off delays and transitions for test mode. This doesn't just speed
107 // things along, it's actually required to get StatusBubbleMac to behave
108 // synchronously, because the tests here don't know how to wait for
109 // results. This allows these tests to be much more complete with a
110 // minimal loss of coverage and without any heinous rearchitecting.
111 bubble_->immediate_ = true;
113 EXPECT_TRUE(bubble_->window_); // immediately creates window
116 virtual void TearDown() {
117 // Not using a scoped_ptr because bubble must be deleted before calling
118 // TearDown to get rid of bubble's window.
120 CocoaTest::TearDown();
124 if (![bubble_->window_ isVisible])
126 return [bubble_->window_ alphaValue] > 0.0;
128 NSString* GetText() {
129 return bubble_->status_text_;
131 NSString* GetURLText() {
132 return bubble_->url_text_;
134 NSString* GetBubbleViewText() {
135 BubbleView* bubbleView = [bubble_->window_ contentView];
136 return [bubbleView content];
138 NSWindow* GetWindow() {
139 return bubble_->window_;
142 return bubble_->parent_;
144 StatusBubbleMac::StatusBubbleState GetState() {
145 return bubble_->state_;
147 void SetState(StatusBubbleMac::StatusBubbleState state) {
148 bubble_->SetState(state);
150 std::vector<StatusBubbleMac::StatusBubbleState>* States() {
151 return [delegate_ states];
153 StatusBubbleMac::StatusBubbleState StateAt(int index) {
154 return (*States())[index];
157 bool IsPointInBubble(int x, int y) {
158 return NSPointInRect(NSMakePoint(x, y), [GetWindow() frame]);
161 void SetMouseLocation(int relative_x, int relative_y) {
162 // Convert to screen coordinates.
163 NSRect window_frame = [test_window() frame];
164 int x = relative_x + window_frame.origin.x;
165 int y = relative_y + window_frame.origin.y;
167 ((StatusBubbleMacIgnoreMouseMoved*)
168 bubble_)->SetMouseLocationForTesting(x, y);
171 // Test helper for moving the fake mouse location, and checking that
172 // the bubble avoids that location.
173 // For convenience & clarity, coordinates are relative to the main window.
174 bool CheckAvoidsMouse(int relative_x, int relative_y) {
175 SetMouseLocation(relative_x, relative_y);
176 return !IsPointInBubble(relative_x, relative_y);
179 base::MessageLoop message_loop_;
180 base::scoped_nsobject<StatusBubbleMacTestDelegate> delegate_;
181 StatusBubbleMac* bubble_; // Strong.
184 TEST_F(StatusBubbleMacTest, SetStatus) {
185 bubble_->SetStatus(base::string16());
186 bubble_->SetStatus(UTF8ToUTF16("This is a test"));
187 EXPECT_NSEQ(@"This is a test", GetText());
188 EXPECT_TRUE(IsVisible());
190 // Set the status to the exact same thing again
191 bubble_->SetStatus(UTF8ToUTF16("This is a test"));
192 EXPECT_NSEQ(@"This is a test", GetText());
195 bubble_->SetStatus(base::string16());
196 EXPECT_FALSE(IsVisible());
199 TEST_F(StatusBubbleMacTest, SetURL) {
200 bubble_->SetURL(GURL(), std::string());
201 EXPECT_FALSE(IsVisible());
202 bubble_->SetURL(GURL("bad url"), std::string());
203 EXPECT_FALSE(IsVisible());
204 bubble_->SetURL(GURL("http://"), std::string());
205 EXPECT_TRUE(IsVisible());
206 EXPECT_NSEQ(@"http:", GetURLText());
207 bubble_->SetURL(GURL("about:blank"), std::string());
208 EXPECT_TRUE(IsVisible());
209 EXPECT_NSEQ(@"about:blank", GetURLText());
210 bubble_->SetURL(GURL("foopy://"), std::string());
211 EXPECT_TRUE(IsVisible());
212 EXPECT_NSEQ(@"foopy://", GetURLText());
213 bubble_->SetURL(GURL("http://www.cnn.com"), std::string());
214 EXPECT_TRUE(IsVisible());
215 EXPECT_NSEQ(@"www.cnn.com", GetURLText());
218 // Test hiding bubble that's already hidden.
219 TEST_F(StatusBubbleMacTest, Hides) {
220 bubble_->SetStatus(UTF8ToUTF16("Showing"));
221 EXPECT_TRUE(IsVisible());
223 EXPECT_FALSE(IsVisible());
225 EXPECT_FALSE(IsVisible());
228 // Test the "main"/"backup" behavior in StatusBubbleMac::SetText().
229 TEST_F(StatusBubbleMacTest, SetStatusAndURL) {
230 EXPECT_FALSE(IsVisible());
231 bubble_->SetStatus(UTF8ToUTF16("Status"));
232 EXPECT_TRUE(IsVisible());
233 EXPECT_NSEQ(@"Status", GetBubbleViewText());
234 bubble_->SetURL(GURL("http://www.nytimes.com"), std::string());
235 EXPECT_TRUE(IsVisible());
236 EXPECT_NSEQ(@"www.nytimes.com", GetBubbleViewText());
237 bubble_->SetURL(GURL(), std::string());
238 EXPECT_TRUE(IsVisible());
239 EXPECT_NSEQ(@"Status", GetBubbleViewText());
240 bubble_->SetStatus(base::string16());
241 EXPECT_FALSE(IsVisible());
242 bubble_->SetURL(GURL("http://www.nytimes.com"), std::string());
243 EXPECT_TRUE(IsVisible());
244 EXPECT_NSEQ(@"www.nytimes.com", GetBubbleViewText());
245 bubble_->SetStatus(UTF8ToUTF16("Status"));
246 EXPECT_TRUE(IsVisible());
247 EXPECT_NSEQ(@"Status", GetBubbleViewText());
248 bubble_->SetStatus(base::string16());
249 EXPECT_TRUE(IsVisible());
250 EXPECT_NSEQ(@"www.nytimes.com", GetBubbleViewText());
251 bubble_->SetURL(GURL(), std::string());
252 EXPECT_FALSE(IsVisible());
255 // Test that the status bubble goes through the correct delay and fade states.
256 // The delay and fade duration are simulated and not actually experienced
257 // during the test because StatusBubbleMacTest sets immediate_ mode.
258 TEST_F(StatusBubbleMacTest, StateTransitions) {
259 // First, some sanity
261 EXPECT_FALSE(IsVisible());
262 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
265 EXPECT_TRUE(States()->empty());
267 bubble_->SetStatus(base::string16());
268 EXPECT_FALSE(IsVisible());
269 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
270 EXPECT_TRUE(States()->empty()); // no change from initial kBubbleHidden state
272 // Next, a few ordinary cases
274 // Test StartShowing from kBubbleHidden
275 bubble_->SetStatus(UTF8ToUTF16("Status"));
276 EXPECT_TRUE(IsVisible());
277 // Check GetState before checking States to make sure that all state
278 // transitions have been flushed to States.
279 EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState());
280 EXPECT_EQ(3u, States()->size());
281 EXPECT_EQ(StatusBubbleMac::kBubbleShowingTimer, StateAt(0));
282 EXPECT_EQ(StatusBubbleMac::kBubbleShowingFadeIn, StateAt(1));
283 EXPECT_EQ(StatusBubbleMac::kBubbleShown, StateAt(2));
285 // Test StartShowing from kBubbleShown with the same message
287 bubble_->SetStatus(UTF8ToUTF16("Status"));
288 EXPECT_TRUE(IsVisible());
289 EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState());
290 EXPECT_TRUE(States()->empty());
292 // Test StartShowing from kBubbleShown with a different message
293 bubble_->SetStatus(UTF8ToUTF16("New Status"));
294 EXPECT_TRUE(IsVisible());
295 EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState());
296 EXPECT_TRUE(States()->empty());
298 // Test StartHiding from kBubbleShown
299 bubble_->SetStatus(base::string16());
300 EXPECT_FALSE(IsVisible());
301 // Check GetState before checking States to make sure that all state
302 // transitions have been flushed to States.
303 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
304 EXPECT_EQ(3u, States()->size());
305 EXPECT_EQ(StatusBubbleMac::kBubbleHidingTimer, StateAt(0));
306 EXPECT_EQ(StatusBubbleMac::kBubbleHidingFadeOut, StateAt(1));
307 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(2));
309 // Test StartHiding from kBubbleHidden
311 bubble_->SetStatus(base::string16());
312 EXPECT_FALSE(IsVisible());
313 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
314 EXPECT_TRUE(States()->empty());
316 // Now, the edge cases
318 // Test StartShowing from kBubbleShowingTimer
319 bubble_->SetStatus(UTF8ToUTF16("Status"));
320 SetState(StatusBubbleMac::kBubbleShowingTimer);
321 [GetWindow() setAlphaValue:0.0];
323 EXPECT_TRUE(States()->empty());
324 bubble_->SetStatus(UTF8ToUTF16("Status"));
325 EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState());
326 EXPECT_EQ(2u, States()->size());
327 EXPECT_EQ(StatusBubbleMac::kBubbleShowingFadeIn, StateAt(0));
328 EXPECT_EQ(StatusBubbleMac::kBubbleShown, StateAt(1));
330 // Test StartShowing from kBubbleShowingFadeIn
331 bubble_->SetStatus(UTF8ToUTF16("Status"));
332 SetState(StatusBubbleMac::kBubbleShowingFadeIn);
333 [GetWindow() setAlphaValue:0.5];
335 EXPECT_TRUE(States()->empty());
336 bubble_->SetStatus(UTF8ToUTF16("Status"));
337 // The actual state values can't be tested in immediate_ mode because
338 // the window wasn't actually fading in. Without immediate_ mode,
339 // expect kBubbleShown.
340 bubble_->SetStatus(base::string16()); // Go back to a deterministic state.
342 // Test StartShowing from kBubbleHidingTimer
343 bubble_->SetStatus(base::string16());
344 SetState(StatusBubbleMac::kBubbleHidingTimer);
345 [GetWindow() setAlphaValue:1.0];
347 EXPECT_TRUE(States()->empty());
348 bubble_->SetStatus(UTF8ToUTF16("Status"));
349 EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState());
350 EXPECT_EQ(1u, States()->size());
351 EXPECT_EQ(StatusBubbleMac::kBubbleShown, StateAt(0));
353 // Test StartShowing from kBubbleHidingFadeOut
354 bubble_->SetStatus(base::string16());
355 SetState(StatusBubbleMac::kBubbleHidingFadeOut);
356 [GetWindow() setAlphaValue:0.5];
358 EXPECT_TRUE(States()->empty());
359 bubble_->SetStatus(UTF8ToUTF16("Status"));
360 EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState());
361 EXPECT_EQ(2u, States()->size());
362 EXPECT_EQ(StatusBubbleMac::kBubbleShowingFadeIn, StateAt(0));
363 EXPECT_EQ(StatusBubbleMac::kBubbleShown, StateAt(1));
365 // Test StartHiding from kBubbleShowingTimer
366 bubble_->SetStatus(UTF8ToUTF16("Status"));
367 SetState(StatusBubbleMac::kBubbleShowingTimer);
368 [GetWindow() setAlphaValue:0.0];
370 EXPECT_TRUE(States()->empty());
371 bubble_->SetStatus(base::string16());
372 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
373 EXPECT_EQ(1u, States()->size());
374 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0));
376 // Test StartHiding from kBubbleShowingFadeIn
377 bubble_->SetStatus(UTF8ToUTF16("Status"));
378 SetState(StatusBubbleMac::kBubbleShowingFadeIn);
379 [GetWindow() setAlphaValue:0.5];
381 EXPECT_TRUE(States()->empty());
382 bubble_->SetStatus(base::string16());
383 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
384 EXPECT_EQ(2u, States()->size());
385 EXPECT_EQ(StatusBubbleMac::kBubbleHidingFadeOut, StateAt(0));
386 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(1));
388 // Test StartHiding from kBubbleHidingTimer
389 bubble_->SetStatus(base::string16());
390 SetState(StatusBubbleMac::kBubbleHidingTimer);
391 [GetWindow() setAlphaValue:1.0];
393 EXPECT_TRUE(States()->empty());
394 bubble_->SetStatus(base::string16());
395 // The actual state values can't be tested in immediate_ mode because
396 // the timer wasn't actually running. Without immediate_ mode, expect
397 // kBubbleHidingFadeOut and kBubbleHidden.
398 // Go back to a deterministic state.
399 bubble_->SetStatus(UTF8ToUTF16("Status"));
401 // Test StartHiding from kBubbleHidingFadeOut
402 bubble_->SetStatus(base::string16());
403 SetState(StatusBubbleMac::kBubbleHidingFadeOut);
404 [GetWindow() setAlphaValue:0.5];
406 EXPECT_TRUE(States()->empty());
407 bubble_->SetStatus(base::string16());
408 // The actual state values can't be tested in immediate_ mode because
409 // the window wasn't actually fading out. Without immediate_ mode, expect
411 // Go back to a deterministic state.
412 bubble_->SetStatus(UTF8ToUTF16("Status"));
414 // Test Hide from kBubbleHidden
415 bubble_->SetStatus(base::string16());
416 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
418 EXPECT_TRUE(States()->empty());
420 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
421 EXPECT_TRUE(States()->empty());
423 // Test Hide from kBubbleShowingTimer
424 bubble_->SetStatus(UTF8ToUTF16("Status"));
425 SetState(StatusBubbleMac::kBubbleShowingTimer);
426 [GetWindow() setAlphaValue:0.0];
428 EXPECT_TRUE(States()->empty());
430 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
431 EXPECT_EQ(1u, States()->size());
432 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0));
434 // Test Hide from kBubbleShowingFadeIn
435 bubble_->SetStatus(UTF8ToUTF16("Status"));
436 SetState(StatusBubbleMac::kBubbleShowingFadeIn);
437 [GetWindow() setAlphaValue:0.5];
439 EXPECT_TRUE(States()->empty());
441 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
442 EXPECT_EQ(2u, States()->size());
443 EXPECT_EQ(StatusBubbleMac::kBubbleHidingFadeOut, StateAt(0));
444 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(1));
446 // Test Hide from kBubbleShown
447 bubble_->SetStatus(UTF8ToUTF16("Status"));
449 EXPECT_TRUE(States()->empty());
451 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
452 EXPECT_EQ(1u, States()->size());
453 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0));
455 // Test Hide from kBubbleHidingTimer
456 bubble_->SetStatus(UTF8ToUTF16("Status"));
457 SetState(StatusBubbleMac::kBubbleHidingTimer);
459 EXPECT_TRUE(States()->empty());
461 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
462 EXPECT_EQ(1u, States()->size());
463 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0));
465 // Test Hide from kBubbleHidingFadeOut
466 bubble_->SetStatus(UTF8ToUTF16("Status"));
467 SetState(StatusBubbleMac::kBubbleHidingFadeOut);
468 [GetWindow() setAlphaValue:0.5];
470 EXPECT_TRUE(States()->empty());
472 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
473 EXPECT_EQ(1u, States()->size());
474 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0));
477 TEST_F(StatusBubbleMacTest, Delete) {
478 NSWindow* window = test_window();
479 // Create and delete immediately.
480 StatusBubbleMac* bubble = new StatusBubbleMac(window, nil);
483 // Create then delete while visible.
484 bubble = new StatusBubbleMac(window, nil);
485 bubble->SetStatus(UTF8ToUTF16("showing"));
489 TEST_F(StatusBubbleMacTest, UpdateSizeAndPosition) {
490 // Test |UpdateSizeAndPosition()| when status bubble does not exist (shouldn't
491 // crash; shouldn't create window).
492 EXPECT_TRUE(GetWindow());
493 bubble_->UpdateSizeAndPosition();
494 EXPECT_TRUE(GetWindow());
496 // Create a status bubble (with contents) and call resize (without actually
497 // resizing); the frame size shouldn't change.
498 bubble_->SetStatus(UTF8ToUTF16("UpdateSizeAndPosition test"));
499 ASSERT_TRUE(GetWindow());
500 NSRect rect_before = [GetWindow() frame];
501 bubble_->UpdateSizeAndPosition();
502 NSRect rect_after = [GetWindow() frame];
503 EXPECT_TRUE(NSEqualRects(rect_before, rect_after));
505 // Move the window and call resize; only the origin should change.
506 NSWindow* window = test_window();
508 NSRect frame = [window frame];
509 rect_before = [GetWindow() frame];
510 frame.origin.x += 10.0; // (fairly arbitrary nonzero value)
511 frame.origin.y += 10.0; // (fairly arbitrary nonzero value)
512 [window setFrame:frame display:YES];
513 bubble_->UpdateSizeAndPosition();
514 rect_after = [GetWindow() frame];
515 EXPECT_NE(rect_before.origin.x, rect_after.origin.x);
516 EXPECT_NE(rect_before.origin.y, rect_after.origin.y);
517 EXPECT_EQ(rect_before.size.width, rect_after.size.width);
518 EXPECT_EQ(rect_before.size.height, rect_after.size.height);
520 // Resize the window (without moving). The origin shouldn't change. The width
521 // should change (in the current implementation), but not the height.
522 frame = [window frame];
523 rect_before = [GetWindow() frame];
524 frame.size.width += 50.0; // (fairly arbitrary nonzero value)
525 frame.size.height += 50.0; // (fairly arbitrary nonzero value)
526 [window setFrame:frame display:YES];
527 bubble_->UpdateSizeAndPosition();
528 rect_after = [GetWindow() frame];
529 EXPECT_EQ(rect_before.origin.x, rect_after.origin.x);
530 EXPECT_EQ(rect_before.origin.y, rect_after.origin.y);
531 EXPECT_NE(rect_before.size.width, rect_after.size.width);
532 EXPECT_EQ(rect_before.size.height, rect_after.size.height);
535 TEST_F(StatusBubbleMacTest, MovingWindowUpdatesPosition) {
536 NSWindow* window = test_window();
538 // Show the bubble and make sure it has the same origin as |window|.
539 bubble_->SetStatus(UTF8ToUTF16("Showing"));
540 NSWindow* child = GetWindow();
541 EXPECT_TRUE(NSEqualPoints([window frame].origin, [child frame].origin));
543 // Hide the bubble, move the window, and show it again.
545 NSRect frame = [window frame];
546 frame.origin.x += 50;
547 [window setFrame:frame display:YES];
548 bubble_->SetStatus(UTF8ToUTF16("Reshowing"));
550 // The bubble should reattach in the correct location.
552 EXPECT_TRUE(NSEqualPoints([window frame].origin, [child frame].origin));
555 TEST_F(StatusBubbleMacTest, StatuBubbleRespectsBaseFrameLimits) {
556 NSWindow* window = test_window();
558 // Show the bubble and make sure it has the same origin as |window|.
559 bubble_->SetStatus(UTF8ToUTF16("Showing"));
560 NSWindow* child = GetWindow();
561 EXPECT_TRUE(NSEqualPoints([window frame].origin, [child frame].origin));
563 // Hide the bubble, change base frame offset, and show it again.
566 NSPoint baseFrameOffset = NSMakePoint(0, [window frame].size.height / 3);
567 EXPECT_GT(baseFrameOffset.y, 0);
568 [delegate_ forceBaseFrameOffset:baseFrameOffset];
570 bubble_->SetStatus(UTF8ToUTF16("Reshowing"));
572 // The bubble should reattach in the correct location.
574 NSPoint expectedOrigin = [window frame].origin;
575 expectedOrigin.x += baseFrameOffset.x;
576 expectedOrigin.y += baseFrameOffset.y;
577 EXPECT_TRUE(NSEqualPoints(expectedOrigin, [child frame].origin));
580 TEST_F(StatusBubbleMacTest, ExpandBubble) {
581 NSWindow* window = test_window();
583 NSRect window_frame = [window frame];
584 window_frame.size.width = 600.0;
585 [window setFrame:window_frame display:YES];
587 // Check basic expansion
588 bubble_->SetStatus(UTF8ToUTF16("Showing"));
589 EXPECT_TRUE(IsVisible());
590 bubble_->SetURL(GURL("http://www.battersbox.com/peter_paul_and_mary.html"),
592 EXPECT_TRUE([GetURLText() hasSuffix:@"\u2026"]);
593 bubble_->ExpandBubble();
594 EXPECT_TRUE(IsVisible());
595 EXPECT_NSEQ(@"www.battersbox.com/peter_paul_and_mary.html", GetURLText());
598 // Make sure bubble resets after hide.
599 bubble_->SetStatus(UTF8ToUTF16("Showing"));
600 bubble_->SetURL(GURL("http://www.snickersnee.com/pioneer_fishstix.html"),
602 EXPECT_TRUE([GetURLText() hasSuffix:@"\u2026"]);
603 // ...and that it expands again properly.
604 bubble_->ExpandBubble();
605 EXPECT_NSEQ(@"www.snickersnee.com/pioneer_fishstix.html", GetURLText());
607 bubble_->SetURL(GURL("http://www.battersbox.com/peter_paul_and_mary.html"),
609 bubble_->ExpandBubble();
610 EXPECT_NSEQ(@"www.battersbox.com/peter_paul_and_mary.html", GetURLText());
613 window_frame = [window frame];
614 window_frame.size.width = 300.0;
615 [window setFrame:window_frame display:YES];
617 // Very long URL's will be cut off even in the expanded state.
618 bubble_->SetStatus(UTF8ToUTF16("Showing"));
619 const char veryLongUrl[] =
620 "http://www.diewahrscheinlichlaengstepralinederwelt.com/duuuuplo.html";
621 bubble_->SetURL(GURL(veryLongUrl), std::string());
622 EXPECT_TRUE([GetURLText() hasSuffix:@"\u2026"]);
623 bubble_->ExpandBubble();
624 EXPECT_TRUE([GetURLText() hasSuffix:@"\u2026"]);
627 TEST_F(StatusBubbleMacTest, BubbleAvoidsMouse) {
628 NSWindow* window = test_window();
630 // All coordinates here are relative to the window origin.
632 // Initially, the bubble should appear in the bottom left.
633 bubble_->SetStatus(UTF8ToUTF16("Showing"));
634 EXPECT_TRUE(IsPointInBubble(0, 0));
637 // Check that the bubble doesn't appear in the left corner if the
638 // mouse is currently located there.
639 SetMouseLocation(0, 0);
640 bubble_->SetStatus(UTF8ToUTF16("Showing"));
641 EXPECT_FALSE(IsPointInBubble(0, 0));
643 // Leave the bubble visible, and try moving the mouse around.
644 int smallValue = NSHeight([GetWindow() frame]) / 2;
645 EXPECT_TRUE(CheckAvoidsMouse(0, 0));
646 EXPECT_TRUE(CheckAvoidsMouse(smallValue, 0));
647 EXPECT_TRUE(CheckAvoidsMouse(0, smallValue));
648 EXPECT_TRUE(CheckAvoidsMouse(smallValue, smallValue));
650 // Simulate moving the mouse down from the top of the window.
651 for (int y = NSHeight([window frame]); y >= 0; y -= smallValue) {
652 ASSERT_TRUE(CheckAvoidsMouse(smallValue, y));
655 // Simulate moving the mouse from left to right.
656 int windowWidth = NSWidth([window frame]);
657 for (int x = 0; x < windowWidth; x += smallValue) {
658 ASSERT_TRUE(CheckAvoidsMouse(x, smallValue));
661 // Simulate moving the mouse from right to left.
662 for (int x = windowWidth; x >= 0; x -= smallValue) {
663 ASSERT_TRUE(CheckAvoidsMouse(x, smallValue));