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 #import "third_party/ocmock/OCMock/OCMock.h"
18 #include "ui/gfx/geometry/point.h"
21 using base::UTF8ToUTF16;
23 // The test delegate records all of the status bubble object's state
25 @interface StatusBubbleMacTestDelegate : NSObject {
27 NSWindow* window_; // Weak.
28 NSPoint baseFrameOffset_;
29 std::vector<StatusBubbleMac::StatusBubbleState> states_;
31 - (id)initWithWindow:(NSWindow*)window;
32 - (void)forceBaseFrameOffset:(NSPoint)baseFrameOffset;
33 - (NSRect)statusBubbleBaseFrame;
34 - (void)statusBubbleWillEnterState:(StatusBubbleMac::StatusBubbleState)state;
36 @implementation StatusBubbleMacTestDelegate
37 - (id)initWithWindow:(NSWindow*)window {
38 if ((self = [super init])) {
40 baseFrameOffset_ = NSZeroPoint;
44 - (void)forceBaseFrameOffset:(NSPoint)baseFrameOffset {
45 baseFrameOffset_ = baseFrameOffset;
47 - (NSRect)statusBubbleBaseFrame {
48 NSView* contentView = [window_ contentView];
49 NSRect baseFrame = [contentView convertRect:[contentView frame] toView:nil];
50 if (baseFrameOffset_.x > 0 || baseFrameOffset_.y > 0) {
51 baseFrame = NSOffsetRect(baseFrame, baseFrameOffset_.x, baseFrameOffset_.y);
52 baseFrame.size.width -= baseFrameOffset_.x;
53 baseFrame.size.height -= baseFrameOffset_.y;
57 - (void)statusBubbleWillEnterState:(StatusBubbleMac::StatusBubbleState)state {
58 states_.push_back(state);
60 - (std::vector<StatusBubbleMac::StatusBubbleState>*)states {
65 // This class implements, for testing purposes, a subclass of |StatusBubbleMac|
66 // whose |MouseMoved()| method does nothing. This lets the tests fake the mouse
67 // position and avoid being affected by the true mouse position.
68 class StatusBubbleMacIgnoreMouseMoved : public StatusBubbleMac {
70 StatusBubbleMacIgnoreMouseMoved(NSWindow* parent, id delegate)
71 : StatusBubbleMac(parent, delegate), mouseLocation_(0, 0) {
72 // Set the fake mouse position to the top right of the content area.
73 NSRect contentBounds = [[parent contentView] bounds];
74 mouseLocation_.SetPoint(NSMaxX(contentBounds), NSMaxY(contentBounds));
77 void MouseMoved(const gfx::Point& location, bool left_content) override {}
79 gfx::Point GetMouseLocation() override { return mouseLocation_; }
81 void SetMouseLocationForTesting(int x, int y) {
82 mouseLocation_.SetPoint(x, y);
83 StatusBubbleMac::MouseMoved(gfx::Point(x, y), false);
87 gfx::Point mouseLocation_;
90 class StatusBubbleMacTest : public CocoaTest {
92 void SetUp() override {
94 NSWindow* window = test_window();
97 [[StatusBubbleMacTestDelegate alloc] initWithWindow: window]);
98 EXPECT_TRUE(delegate_.get());
99 bubble_ = new StatusBubbleMacIgnoreMouseMoved(window, delegate_);
100 EXPECT_TRUE(bubble_);
102 // Turn off delays and transitions for test mode. This doesn't just speed
103 // things along, it's actually required to get StatusBubbleMac to behave
104 // synchronously, because the tests here don't know how to wait for
105 // results. This allows these tests to be much more complete with a
106 // minimal loss of coverage and without any heinous rearchitecting.
107 bubble_->immediate_ = true;
109 EXPECT_TRUE(bubble_->window_); // immediately creates window
112 void TearDown() override {
113 // Not using a scoped_ptr because bubble must be deleted before calling
114 // TearDown to get rid of bubble's window.
116 CocoaTest::TearDown();
120 if (![bubble_->window_ isVisible])
122 return [bubble_->window_ alphaValue] > 0.0;
124 NSString* GetText() {
125 return bubble_->status_text_;
127 NSString* GetURLText() {
128 return bubble_->url_text_;
130 NSString* GetBubbleViewText() {
131 BubbleView* bubbleView = [bubble_->window_ contentView];
132 return [bubbleView content];
134 StatusBubbleWindow* GetWindow() {
135 return bubble_->window_;
138 return bubble_->parent_;
140 StatusBubbleMac::StatusBubbleState GetState() {
141 return bubble_->state_;
143 void SetState(StatusBubbleMac::StatusBubbleState state) {
144 bubble_->SetState(state);
146 std::vector<StatusBubbleMac::StatusBubbleState>* States() {
147 return [delegate_ states];
149 StatusBubbleMac::StatusBubbleState StateAt(int index) {
150 return (*States())[index];
153 bool IsPointInBubble(int x, int y) {
154 return NSPointInRect(NSMakePoint(x, y), [GetWindow() frame]);
157 void SetMouseLocation(int relative_x, int relative_y) {
158 // Convert to screen coordinates.
159 NSRect window_frame = [test_window() frame];
160 int x = relative_x + window_frame.origin.x;
161 int y = relative_y + window_frame.origin.y;
163 ((StatusBubbleMacIgnoreMouseMoved*)
164 bubble_)->SetMouseLocationForTesting(x, y);
167 // Test helper for moving the fake mouse location, and checking that
168 // the bubble avoids that location.
169 // For convenience & clarity, coordinates are relative to the main window.
170 bool CheckAvoidsMouse(int relative_x, int relative_y) {
171 SetMouseLocation(relative_x, relative_y);
172 return !IsPointInBubble(relative_x, relative_y);
175 base::MessageLoop message_loop_;
176 base::scoped_nsobject<StatusBubbleMacTestDelegate> delegate_;
177 StatusBubbleMac* bubble_; // Strong.
180 TEST_F(StatusBubbleMacTest, SetStatus) {
181 bubble_->SetStatus(base::string16());
182 bubble_->SetStatus(UTF8ToUTF16("This is a test"));
183 EXPECT_NSEQ(@"This is a test", GetText());
184 EXPECT_TRUE(IsVisible());
186 // Set the status to the exact same thing again
187 bubble_->SetStatus(UTF8ToUTF16("This is a test"));
188 EXPECT_NSEQ(@"This is a test", GetText());
191 bubble_->SetStatus(base::string16());
192 EXPECT_FALSE(IsVisible());
195 TEST_F(StatusBubbleMacTest, SetURL) {
196 bubble_->SetURL(GURL(), std::string());
197 EXPECT_FALSE(IsVisible());
198 bubble_->SetURL(GURL("bad url"), std::string());
199 EXPECT_FALSE(IsVisible());
200 bubble_->SetURL(GURL("http://"), std::string());
201 EXPECT_TRUE(IsVisible());
202 EXPECT_NSEQ(@"http:", GetURLText());
203 bubble_->SetURL(GURL("about:blank"), std::string());
204 EXPECT_TRUE(IsVisible());
205 EXPECT_NSEQ(@"about:blank", GetURLText());
206 bubble_->SetURL(GURL("foopy://"), std::string());
207 EXPECT_TRUE(IsVisible());
208 EXPECT_NSEQ(@"foopy://", GetURLText());
209 bubble_->SetURL(GURL("http://www.cnn.com"), std::string());
210 EXPECT_TRUE(IsVisible());
211 EXPECT_NSEQ(@"www.cnn.com", GetURLText());
214 // Test hiding bubble that's already hidden.
215 TEST_F(StatusBubbleMacTest, Hides) {
216 bubble_->SetStatus(UTF8ToUTF16("Showing"));
217 EXPECT_TRUE(IsVisible());
219 EXPECT_FALSE(IsVisible());
221 EXPECT_FALSE(IsVisible());
224 // Test the "main"/"backup" behavior in StatusBubbleMac::SetText().
225 TEST_F(StatusBubbleMacTest, SetStatusAndURL) {
226 EXPECT_FALSE(IsVisible());
227 bubble_->SetStatus(UTF8ToUTF16("Status"));
228 EXPECT_TRUE(IsVisible());
229 EXPECT_NSEQ(@"Status", GetBubbleViewText());
230 bubble_->SetURL(GURL("http://www.nytimes.com"), std::string());
231 EXPECT_TRUE(IsVisible());
232 EXPECT_NSEQ(@"www.nytimes.com", GetBubbleViewText());
233 bubble_->SetURL(GURL(), std::string());
234 EXPECT_TRUE(IsVisible());
235 EXPECT_NSEQ(@"Status", GetBubbleViewText());
236 bubble_->SetStatus(base::string16());
237 EXPECT_FALSE(IsVisible());
238 bubble_->SetURL(GURL("http://www.nytimes.com"), std::string());
239 EXPECT_TRUE(IsVisible());
240 EXPECT_NSEQ(@"www.nytimes.com", GetBubbleViewText());
241 bubble_->SetStatus(UTF8ToUTF16("Status"));
242 EXPECT_TRUE(IsVisible());
243 EXPECT_NSEQ(@"Status", GetBubbleViewText());
244 bubble_->SetStatus(base::string16());
245 EXPECT_TRUE(IsVisible());
246 EXPECT_NSEQ(@"www.nytimes.com", GetBubbleViewText());
247 bubble_->SetURL(GURL(), std::string());
248 EXPECT_FALSE(IsVisible());
251 // Test that the status bubble goes through the correct delay and fade states.
252 // The delay and fade duration are simulated and not actually experienced
253 // during the test because StatusBubbleMacTest sets immediate_ mode.
254 TEST_F(StatusBubbleMacTest, StateTransitions) {
255 // First, some sanity
257 EXPECT_FALSE(IsVisible());
258 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
261 EXPECT_TRUE(States()->empty());
263 bubble_->SetStatus(base::string16());
264 EXPECT_FALSE(IsVisible());
265 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
266 EXPECT_TRUE(States()->empty()); // no change from initial kBubbleHidden state
268 // Next, a few ordinary cases
270 // Test StartShowing from kBubbleHidden
271 bubble_->SetStatus(UTF8ToUTF16("Status"));
272 EXPECT_TRUE(IsVisible());
273 // Check GetState before checking States to make sure that all state
274 // transitions have been flushed to States.
275 EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState());
276 EXPECT_EQ(3u, States()->size());
277 EXPECT_EQ(StatusBubbleMac::kBubbleShowingTimer, StateAt(0));
278 EXPECT_EQ(StatusBubbleMac::kBubbleShowingFadeIn, StateAt(1));
279 EXPECT_EQ(StatusBubbleMac::kBubbleShown, StateAt(2));
281 // Test StartShowing from kBubbleShown with the same message
283 bubble_->SetStatus(UTF8ToUTF16("Status"));
284 EXPECT_TRUE(IsVisible());
285 EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState());
286 EXPECT_TRUE(States()->empty());
288 // Test StartShowing from kBubbleShown with a different message
289 bubble_->SetStatus(UTF8ToUTF16("New Status"));
290 EXPECT_TRUE(IsVisible());
291 EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState());
292 EXPECT_TRUE(States()->empty());
294 // Test StartHiding from kBubbleShown
295 bubble_->SetStatus(base::string16());
296 EXPECT_FALSE(IsVisible());
297 // Check GetState before checking States to make sure that all state
298 // transitions have been flushed to States.
299 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
300 EXPECT_EQ(3u, States()->size());
301 EXPECT_EQ(StatusBubbleMac::kBubbleHidingTimer, StateAt(0));
302 EXPECT_EQ(StatusBubbleMac::kBubbleHidingFadeOut, StateAt(1));
303 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(2));
305 // Test StartHiding from kBubbleHidden
307 bubble_->SetStatus(base::string16());
308 EXPECT_FALSE(IsVisible());
309 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
310 EXPECT_TRUE(States()->empty());
312 // Now, the edge cases
314 // Test StartShowing from kBubbleShowingTimer
315 bubble_->SetStatus(UTF8ToUTF16("Status"));
316 SetState(StatusBubbleMac::kBubbleShowingTimer);
317 [GetWindow() setAlphaValue:0.0];
319 EXPECT_TRUE(States()->empty());
320 bubble_->SetStatus(UTF8ToUTF16("Status"));
321 EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState());
322 EXPECT_EQ(2u, States()->size());
323 EXPECT_EQ(StatusBubbleMac::kBubbleShowingFadeIn, StateAt(0));
324 EXPECT_EQ(StatusBubbleMac::kBubbleShown, StateAt(1));
326 // Test StartShowing from kBubbleShowingFadeIn
327 bubble_->SetStatus(UTF8ToUTF16("Status"));
328 SetState(StatusBubbleMac::kBubbleShowingFadeIn);
329 [GetWindow() setAlphaValue:0.5];
331 EXPECT_TRUE(States()->empty());
332 bubble_->SetStatus(UTF8ToUTF16("Status"));
333 // The actual state values can't be tested in immediate_ mode because
334 // the window wasn't actually fading in. Without immediate_ mode,
335 // expect kBubbleShown.
336 bubble_->SetStatus(base::string16()); // Go back to a deterministic state.
338 // Test StartShowing from kBubbleHidingTimer
339 bubble_->SetStatus(base::string16());
340 SetState(StatusBubbleMac::kBubbleHidingTimer);
341 [GetWindow() setAlphaValue:1.0];
343 EXPECT_TRUE(States()->empty());
344 bubble_->SetStatus(UTF8ToUTF16("Status"));
345 EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState());
346 EXPECT_EQ(1u, States()->size());
347 EXPECT_EQ(StatusBubbleMac::kBubbleShown, StateAt(0));
349 // Test StartShowing from kBubbleHidingFadeOut
350 bubble_->SetStatus(base::string16());
351 SetState(StatusBubbleMac::kBubbleHidingFadeOut);
352 [GetWindow() setAlphaValue:0.5];
354 EXPECT_TRUE(States()->empty());
355 bubble_->SetStatus(UTF8ToUTF16("Status"));
356 EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState());
357 EXPECT_EQ(2u, States()->size());
358 EXPECT_EQ(StatusBubbleMac::kBubbleShowingFadeIn, StateAt(0));
359 EXPECT_EQ(StatusBubbleMac::kBubbleShown, StateAt(1));
361 // Test StartHiding from kBubbleShowingTimer
362 bubble_->SetStatus(UTF8ToUTF16("Status"));
363 SetState(StatusBubbleMac::kBubbleShowingTimer);
364 [GetWindow() setAlphaValue:0.0];
366 EXPECT_TRUE(States()->empty());
367 bubble_->SetStatus(base::string16());
368 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
369 EXPECT_EQ(1u, States()->size());
370 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0));
372 // Test StartHiding from kBubbleShowingFadeIn
373 bubble_->SetStatus(UTF8ToUTF16("Status"));
374 SetState(StatusBubbleMac::kBubbleShowingFadeIn);
375 [GetWindow() setAlphaValue:0.5];
377 EXPECT_TRUE(States()->empty());
378 bubble_->SetStatus(base::string16());
379 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
380 EXPECT_EQ(2u, States()->size());
381 EXPECT_EQ(StatusBubbleMac::kBubbleHidingFadeOut, StateAt(0));
382 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(1));
384 // Test StartHiding from kBubbleHidingTimer
385 bubble_->SetStatus(base::string16());
386 SetState(StatusBubbleMac::kBubbleHidingTimer);
387 [GetWindow() setAlphaValue:1.0];
389 EXPECT_TRUE(States()->empty());
390 bubble_->SetStatus(base::string16());
391 // The actual state values can't be tested in immediate_ mode because
392 // the timer wasn't actually running. Without immediate_ mode, expect
393 // kBubbleHidingFadeOut and kBubbleHidden.
394 // Go back to a deterministic state.
395 bubble_->SetStatus(UTF8ToUTF16("Status"));
397 // Test StartHiding from kBubbleHidingFadeOut
398 bubble_->SetStatus(base::string16());
399 SetState(StatusBubbleMac::kBubbleHidingFadeOut);
400 [GetWindow() setAlphaValue:0.5];
402 EXPECT_TRUE(States()->empty());
403 bubble_->SetStatus(base::string16());
404 // The actual state values can't be tested in immediate_ mode because
405 // the window wasn't actually fading out. Without immediate_ mode, expect
407 // Go back to a deterministic state.
408 bubble_->SetStatus(UTF8ToUTF16("Status"));
410 // Test Hide from kBubbleHidden
411 bubble_->SetStatus(base::string16());
412 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
414 EXPECT_TRUE(States()->empty());
416 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
417 EXPECT_TRUE(States()->empty());
419 // Test Hide from kBubbleShowingTimer
420 bubble_->SetStatus(UTF8ToUTF16("Status"));
421 SetState(StatusBubbleMac::kBubbleShowingTimer);
422 [GetWindow() setAlphaValue:0.0];
424 EXPECT_TRUE(States()->empty());
426 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
427 EXPECT_EQ(1u, States()->size());
428 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0));
430 // Test Hide from kBubbleShowingFadeIn
431 bubble_->SetStatus(UTF8ToUTF16("Status"));
432 SetState(StatusBubbleMac::kBubbleShowingFadeIn);
433 [GetWindow() setAlphaValue:0.5];
435 EXPECT_TRUE(States()->empty());
437 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
438 EXPECT_EQ(2u, States()->size());
439 EXPECT_EQ(StatusBubbleMac::kBubbleHidingFadeOut, StateAt(0));
440 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(1));
442 // Test Hide from kBubbleShown
443 bubble_->SetStatus(UTF8ToUTF16("Status"));
445 EXPECT_TRUE(States()->empty());
447 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
448 EXPECT_EQ(1u, States()->size());
449 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0));
451 // Test Hide from kBubbleHidingTimer
452 bubble_->SetStatus(UTF8ToUTF16("Status"));
453 SetState(StatusBubbleMac::kBubbleHidingTimer);
455 EXPECT_TRUE(States()->empty());
457 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
458 EXPECT_EQ(1u, States()->size());
459 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0));
461 // Test Hide from kBubbleHidingFadeOut
462 bubble_->SetStatus(UTF8ToUTF16("Status"));
463 SetState(StatusBubbleMac::kBubbleHidingFadeOut);
464 [GetWindow() setAlphaValue:0.5];
466 EXPECT_TRUE(States()->empty());
468 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState());
469 EXPECT_EQ(1u, States()->size());
470 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0));
473 TEST_F(StatusBubbleMacTest, Delete) {
474 NSWindow* window = test_window();
475 // Create and delete immediately.
476 StatusBubbleMac* bubble = new StatusBubbleMac(window, nil);
479 // Create then delete while visible.
480 bubble = new StatusBubbleMac(window, nil);
481 bubble->SetStatus(UTF8ToUTF16("showing"));
485 TEST_F(StatusBubbleMacTest, UpdateSizeAndPosition) {
486 // Test |UpdateSizeAndPosition()| when status bubble does not exist (shouldn't
487 // crash; shouldn't create window).
488 EXPECT_TRUE(GetWindow());
489 bubble_->UpdateSizeAndPosition();
490 EXPECT_TRUE(GetWindow());
492 // Create a status bubble (with contents) and call resize (without actually
493 // resizing); the frame size shouldn't change.
494 bubble_->SetStatus(UTF8ToUTF16("UpdateSizeAndPosition test"));
495 ASSERT_TRUE(GetWindow());
496 NSRect rect_before = [GetWindow() frame];
497 bubble_->UpdateSizeAndPosition();
498 NSRect rect_after = [GetWindow() frame];
499 EXPECT_TRUE(NSEqualRects(rect_before, rect_after));
501 // Move the window and call resize; only the origin should change.
502 NSWindow* window = test_window();
504 NSRect frame = [window frame];
505 rect_before = [GetWindow() frame];
506 frame.origin.x += 10.0; // (fairly arbitrary nonzero value)
507 frame.origin.y += 10.0; // (fairly arbitrary nonzero value)
508 [window setFrame:frame display:YES];
509 bubble_->UpdateSizeAndPosition();
510 rect_after = [GetWindow() frame];
511 EXPECT_NE(rect_before.origin.x, rect_after.origin.x);
512 EXPECT_NE(rect_before.origin.y, rect_after.origin.y);
513 EXPECT_EQ(rect_before.size.width, rect_after.size.width);
514 EXPECT_EQ(rect_before.size.height, rect_after.size.height);
516 // Resize the window (without moving). The origin shouldn't change. The width
517 // should change (in the current implementation), but not the height.
518 frame = [window frame];
519 rect_before = [GetWindow() frame];
520 frame.size.width += 50.0; // (fairly arbitrary nonzero value)
521 frame.size.height += 50.0; // (fairly arbitrary nonzero value)
522 [window setFrame:frame display:YES];
523 bubble_->UpdateSizeAndPosition();
524 rect_after = [GetWindow() frame];
525 EXPECT_EQ(rect_before.origin.x, rect_after.origin.x);
526 EXPECT_EQ(rect_before.origin.y, rect_after.origin.y);
527 EXPECT_NE(rect_before.size.width, rect_after.size.width);
528 EXPECT_EQ(rect_before.size.height, rect_after.size.height);
531 TEST_F(StatusBubbleMacTest, MovingWindowUpdatesPosition) {
532 NSWindow* window = test_window();
534 // Show the bubble and make sure it has the same origin as |window|.
535 bubble_->SetStatus(UTF8ToUTF16("Showing"));
536 StatusBubbleWindow* child = GetWindow();
537 EXPECT_TRUE(NSEqualPoints([window frame].origin, [child frame].origin));
539 // Hide the bubble, move the window, and show it again.
541 NSRect frame = [window frame];
542 frame.origin.x += 50;
543 [window setFrame:frame display:YES];
544 bubble_->SetStatus(UTF8ToUTF16("Reshowing"));
546 // The bubble should reattach in the correct location.
548 EXPECT_TRUE(NSEqualPoints([window frame].origin, [child frame].origin));
551 TEST_F(StatusBubbleMacTest, StatuBubbleRespectsBaseFrameLimits) {
552 NSWindow* window = test_window();
554 // Show the bubble and make sure it has the same origin as |window|.
555 bubble_->SetStatus(UTF8ToUTF16("Showing"));
556 StatusBubbleWindow* child = GetWindow();
557 EXPECT_TRUE(NSEqualPoints([window frame].origin, [child frame].origin));
559 // Hide the bubble, change base frame offset, and show it again.
562 NSPoint baseFrameOffset = NSMakePoint(0, [window frame].size.height / 3);
563 EXPECT_GT(baseFrameOffset.y, 0);
564 [delegate_ forceBaseFrameOffset:baseFrameOffset];
566 bubble_->SetStatus(UTF8ToUTF16("Reshowing"));
568 // The bubble should reattach in the correct location.
570 NSPoint expectedOrigin = [window frame].origin;
571 expectedOrigin.x += baseFrameOffset.x;
572 expectedOrigin.y += baseFrameOffset.y;
573 EXPECT_TRUE(NSEqualPoints(expectedOrigin, [child frame].origin));
576 TEST_F(StatusBubbleMacTest, ExpandBubble) {
577 NSWindow* window = test_window();
579 // The system font changes between OSX 10.9 and OSX 10.10. Use the system
580 // font from OSX 10.9 for this test.
582 [OCMockObject partialMockForObject:[GetWindow() contentView]];
583 [[[mockContentView stub]
584 andReturn:[NSFont fontWithName:@"Lucida Grande" size:11]] font];
587 NSRect window_frame = [window frame];
588 window_frame.size.width = 600.0;
589 [window setFrame:window_frame display:YES];
591 // Check basic expansion
592 bubble_->SetStatus(UTF8ToUTF16("Showing"));
593 EXPECT_TRUE(IsVisible());
594 bubble_->SetURL(GURL("http://www.battersbox.com/peter_paul_and_mary.html"),
596 EXPECT_TRUE([GetURLText() hasSuffix:@"\u2026"]);
597 bubble_->ExpandBubble();
598 EXPECT_TRUE(IsVisible());
599 EXPECT_NSEQ(@"www.battersbox.com/peter_paul_and_mary.html", GetURLText());
602 // Make sure bubble resets after hide.
603 bubble_->SetStatus(UTF8ToUTF16("Showing"));
604 bubble_->SetURL(GURL("http://www.snickersnee.com/pioneer_fishstix.html"),
606 EXPECT_TRUE([GetURLText() hasSuffix:@"\u2026"]);
607 // ...and that it expands again properly.
608 bubble_->ExpandBubble();
609 EXPECT_NSEQ(@"www.snickersnee.com/pioneer_fishstix.html", GetURLText());
611 bubble_->SetURL(GURL("http://www.battersbox.com/peter_paul_and_mary.html"),
613 bubble_->ExpandBubble();
614 EXPECT_NSEQ(@"www.battersbox.com/peter_paul_and_mary.html", GetURLText());
617 window_frame = [window frame];
618 window_frame.size.width = 300.0;
619 [window setFrame:window_frame display:YES];
621 // Very long URL's will be cut off even in the expanded state.
622 bubble_->SetStatus(UTF8ToUTF16("Showing"));
623 const char veryLongUrl[] =
624 "http://www.diewahrscheinlichlaengstepralinederwelt.com/duuuuplo.html";
625 bubble_->SetURL(GURL(veryLongUrl), std::string());
626 EXPECT_TRUE([GetURLText() hasSuffix:@"\u2026"]);
627 bubble_->ExpandBubble();
628 EXPECT_TRUE([GetURLText() hasSuffix:@"\u2026"]);
631 TEST_F(StatusBubbleMacTest, BubbleAvoidsMouse) {
632 NSWindow* window = test_window();
634 // All coordinates here are relative to the window origin.
636 // Initially, the bubble should appear in the bottom left.
637 bubble_->SetStatus(UTF8ToUTF16("Showing"));
638 EXPECT_TRUE(IsPointInBubble(0, 0));
641 // Check that the bubble doesn't appear in the left corner if the
642 // mouse is currently located there.
643 SetMouseLocation(0, 0);
644 bubble_->SetStatus(UTF8ToUTF16("Showing"));
645 EXPECT_FALSE(IsPointInBubble(0, 0));
647 // Leave the bubble visible, and try moving the mouse around.
648 int smallValue = NSHeight([GetWindow() frame]) / 2;
649 EXPECT_TRUE(CheckAvoidsMouse(0, 0));
650 EXPECT_TRUE(CheckAvoidsMouse(smallValue, 0));
651 EXPECT_TRUE(CheckAvoidsMouse(0, smallValue));
652 EXPECT_TRUE(CheckAvoidsMouse(smallValue, smallValue));
654 // Simulate moving the mouse down from the top of the window.
655 for (int y = NSHeight([window frame]); y >= 0; y -= smallValue) {
656 ASSERT_TRUE(CheckAvoidsMouse(smallValue, y));
659 // Simulate moving the mouse from left to right.
660 int windowWidth = NSWidth([window frame]);
661 for (int x = 0; x < windowWidth; x += smallValue) {
662 ASSERT_TRUE(CheckAvoidsMouse(x, smallValue));
665 // Simulate moving the mouse from right to left.
666 for (int x = windowWidth; x >= 0; x -= smallValue) {
667 ASSERT_TRUE(CheckAvoidsMouse(x, smallValue));
671 TEST_F(StatusBubbleMacTest, ReparentBubble) {
672 // The second window is borderless, like the window used in fullscreen mode.
673 base::scoped_nsobject<NSWindow> fullscreenParent(
674 [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600)
675 styleMask:NSBorderlessWindowMask
676 backing:NSBackingStoreBuffered
679 // Switch parents with the bubble hidden.
680 bubble_->SwitchParentWindow(fullscreenParent);
682 // Switch back to the original parent with the bubble showing.
683 bubble_->SetStatus(UTF8ToUTF16("Showing"));
684 bubble_->SwitchParentWindow(test_window());