| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <Cocoa/Cocoa.h> |
| |
| #include "base/mac/scoped_nsobject.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/strings/utf_string_conversions.h" |
| #import "chrome/browser/ui/cocoa/bubble_view.h" |
| #import "chrome/browser/ui/cocoa/cocoa_test_helper.h" |
| #import "chrome/browser/ui/cocoa/status_bubble_mac.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #import "testing/gtest_mac.h" |
| #include "testing/platform_test.h" |
| #include "ui/gfx/point.h" |
| #include "url/gurl.h" |
| |
| // The test delegate records all of the status bubble object's state |
| // transitions. |
| @interface StatusBubbleMacTestDelegate : NSObject { |
| @private |
| NSWindow* window_; // Weak. |
| NSPoint baseFrameOffset_; |
| std::vector<StatusBubbleMac::StatusBubbleState> states_; |
| } |
| - (id)initWithWindow:(NSWindow*)window; |
| - (void)forceBaseFrameOffset:(NSPoint)baseFrameOffset; |
| - (NSRect)statusBubbleBaseFrame; |
| - (void)statusBubbleWillEnterState:(StatusBubbleMac::StatusBubbleState)state; |
| @end |
| @implementation StatusBubbleMacTestDelegate |
| - (id)initWithWindow:(NSWindow*)window { |
| if ((self = [super init])) { |
| window_ = window; |
| baseFrameOffset_ = NSZeroPoint; |
| } |
| return self; |
| } |
| - (void)forceBaseFrameOffset:(NSPoint)baseFrameOffset { |
| baseFrameOffset_ = baseFrameOffset; |
| } |
| - (NSRect)statusBubbleBaseFrame { |
| NSView* contentView = [window_ contentView]; |
| NSRect baseFrame = [contentView convertRect:[contentView frame] toView:nil]; |
| if (baseFrameOffset_.x > 0 || baseFrameOffset_.y > 0) { |
| baseFrame = NSOffsetRect(baseFrame, baseFrameOffset_.x, baseFrameOffset_.y); |
| baseFrame.size.width -= baseFrameOffset_.x; |
| baseFrame.size.height -= baseFrameOffset_.y; |
| } |
| return baseFrame; |
| } |
| - (void)statusBubbleWillEnterState:(StatusBubbleMac::StatusBubbleState)state { |
| states_.push_back(state); |
| } |
| - (std::vector<StatusBubbleMac::StatusBubbleState>*)states { |
| return &states_; |
| } |
| @end |
| |
| // This class implements, for testing purposes, a subclass of |StatusBubbleMac| |
| // whose |MouseMoved()| method does nothing. This lets the tests fake the mouse |
| // position and avoid being affected by the true mouse position. |
| class StatusBubbleMacIgnoreMouseMoved : public StatusBubbleMac { |
| public: |
| StatusBubbleMacIgnoreMouseMoved(NSWindow* parent, id delegate) |
| : StatusBubbleMac(parent, delegate), mouseLocation_(0, 0) { |
| // Set the fake mouse position to the top right of the content area. |
| NSRect contentBounds = [[parent contentView] bounds]; |
| mouseLocation_.SetPoint(NSMaxX(contentBounds), NSMaxY(contentBounds)); |
| } |
| |
| virtual void MouseMoved( |
| const gfx::Point& location, |
| bool left_content) OVERRIDE { |
| } |
| |
| virtual gfx::Point GetMouseLocation() OVERRIDE { |
| return mouseLocation_; |
| } |
| |
| void SetMouseLocationForTesting(int x, int y) { |
| mouseLocation_.SetPoint(x, y); |
| StatusBubbleMac::MouseMoved(gfx::Point(x, y), false); |
| } |
| |
| private: |
| gfx::Point mouseLocation_; |
| }; |
| |
| class StatusBubbleMacTest : public CocoaTest { |
| public: |
| virtual void SetUp() { |
| CocoaTest::SetUp(); |
| NSWindow* window = test_window(); |
| EXPECT_TRUE(window); |
| delegate_.reset( |
| [[StatusBubbleMacTestDelegate alloc] initWithWindow: window]); |
| EXPECT_TRUE(delegate_.get()); |
| bubble_ = new StatusBubbleMacIgnoreMouseMoved(window, delegate_); |
| EXPECT_TRUE(bubble_); |
| |
| // Turn off delays and transitions for test mode. This doesn't just speed |
| // things along, it's actually required to get StatusBubbleMac to behave |
| // synchronously, because the tests here don't know how to wait for |
| // results. This allows these tests to be much more complete with a |
| // minimal loss of coverage and without any heinous rearchitecting. |
| bubble_->immediate_ = true; |
| |
| EXPECT_TRUE(bubble_->window_); // immediately creates window |
| } |
| |
| virtual void TearDown() { |
| // Not using a scoped_ptr because bubble must be deleted before calling |
| // TearDown to get rid of bubble's window. |
| delete bubble_; |
| CocoaTest::TearDown(); |
| } |
| |
| bool IsVisible() { |
| if (![bubble_->window_ isVisible]) |
| return false; |
| return [bubble_->window_ alphaValue] > 0.0; |
| } |
| NSString* GetText() { |
| return bubble_->status_text_; |
| } |
| NSString* GetURLText() { |
| return bubble_->url_text_; |
| } |
| NSString* GetBubbleViewText() { |
| BubbleView* bubbleView = [bubble_->window_ contentView]; |
| return [bubbleView content]; |
| } |
| NSWindow* GetWindow() { |
| return bubble_->window_; |
| } |
| NSWindow* parent() { |
| return bubble_->parent_; |
| } |
| StatusBubbleMac::StatusBubbleState GetState() { |
| return bubble_->state_; |
| } |
| void SetState(StatusBubbleMac::StatusBubbleState state) { |
| bubble_->SetState(state); |
| } |
| std::vector<StatusBubbleMac::StatusBubbleState>* States() { |
| return [delegate_ states]; |
| } |
| StatusBubbleMac::StatusBubbleState StateAt(int index) { |
| return (*States())[index]; |
| } |
| |
| bool IsPointInBubble(int x, int y) { |
| return NSPointInRect(NSMakePoint(x, y), [GetWindow() frame]); |
| } |
| |
| void SetMouseLocation(int relative_x, int relative_y) { |
| // Convert to screen coordinates. |
| NSRect window_frame = [test_window() frame]; |
| int x = relative_x + window_frame.origin.x; |
| int y = relative_y + window_frame.origin.y; |
| |
| ((StatusBubbleMacIgnoreMouseMoved*) |
| bubble_)->SetMouseLocationForTesting(x, y); |
| } |
| |
| // Test helper for moving the fake mouse location, and checking that |
| // the bubble avoids that location. |
| // For convenience & clarity, coordinates are relative to the main window. |
| bool CheckAvoidsMouse(int relative_x, int relative_y) { |
| SetMouseLocation(relative_x, relative_y); |
| return !IsPointInBubble(relative_x, relative_y); |
| } |
| |
| base::MessageLoop message_loop_; |
| base::scoped_nsobject<StatusBubbleMacTestDelegate> delegate_; |
| StatusBubbleMac* bubble_; // Strong. |
| }; |
| |
| TEST_F(StatusBubbleMacTest, SetStatus) { |
| bubble_->SetStatus(string16()); |
| bubble_->SetStatus(UTF8ToUTF16("This is a test")); |
| EXPECT_NSEQ(@"This is a test", GetText()); |
| EXPECT_TRUE(IsVisible()); |
| |
| // Set the status to the exact same thing again |
| bubble_->SetStatus(UTF8ToUTF16("This is a test")); |
| EXPECT_NSEQ(@"This is a test", GetText()); |
| |
| // Hide it |
| bubble_->SetStatus(string16()); |
| EXPECT_FALSE(IsVisible()); |
| } |
| |
| TEST_F(StatusBubbleMacTest, SetURL) { |
| bubble_->SetURL(GURL(), std::string()); |
| EXPECT_FALSE(IsVisible()); |
| bubble_->SetURL(GURL("bad url"), std::string()); |
| EXPECT_FALSE(IsVisible()); |
| bubble_->SetURL(GURL("http://"), std::string()); |
| EXPECT_TRUE(IsVisible()); |
| EXPECT_NSEQ(@"http:", GetURLText()); |
| bubble_->SetURL(GURL("about:blank"), std::string()); |
| EXPECT_TRUE(IsVisible()); |
| EXPECT_NSEQ(@"about:blank", GetURLText()); |
| bubble_->SetURL(GURL("foopy://"), std::string()); |
| EXPECT_TRUE(IsVisible()); |
| EXPECT_NSEQ(@"foopy://", GetURLText()); |
| bubble_->SetURL(GURL("http://www.cnn.com"), std::string()); |
| EXPECT_TRUE(IsVisible()); |
| EXPECT_NSEQ(@"www.cnn.com", GetURLText()); |
| } |
| |
| // Test hiding bubble that's already hidden. |
| TEST_F(StatusBubbleMacTest, Hides) { |
| bubble_->SetStatus(UTF8ToUTF16("Showing")); |
| EXPECT_TRUE(IsVisible()); |
| bubble_->Hide(); |
| EXPECT_FALSE(IsVisible()); |
| bubble_->Hide(); |
| EXPECT_FALSE(IsVisible()); |
| } |
| |
| // Test the "main"/"backup" behavior in StatusBubbleMac::SetText(). |
| TEST_F(StatusBubbleMacTest, SetStatusAndURL) { |
| EXPECT_FALSE(IsVisible()); |
| bubble_->SetStatus(UTF8ToUTF16("Status")); |
| EXPECT_TRUE(IsVisible()); |
| EXPECT_NSEQ(@"Status", GetBubbleViewText()); |
| bubble_->SetURL(GURL("http://www.nytimes.com"), std::string()); |
| EXPECT_TRUE(IsVisible()); |
| EXPECT_NSEQ(@"www.nytimes.com", GetBubbleViewText()); |
| bubble_->SetURL(GURL(), std::string()); |
| EXPECT_TRUE(IsVisible()); |
| EXPECT_NSEQ(@"Status", GetBubbleViewText()); |
| bubble_->SetStatus(string16()); |
| EXPECT_FALSE(IsVisible()); |
| bubble_->SetURL(GURL("http://www.nytimes.com"), std::string()); |
| EXPECT_TRUE(IsVisible()); |
| EXPECT_NSEQ(@"www.nytimes.com", GetBubbleViewText()); |
| bubble_->SetStatus(UTF8ToUTF16("Status")); |
| EXPECT_TRUE(IsVisible()); |
| EXPECT_NSEQ(@"Status", GetBubbleViewText()); |
| bubble_->SetStatus(string16()); |
| EXPECT_TRUE(IsVisible()); |
| EXPECT_NSEQ(@"www.nytimes.com", GetBubbleViewText()); |
| bubble_->SetURL(GURL(), std::string()); |
| EXPECT_FALSE(IsVisible()); |
| } |
| |
| // Test that the status bubble goes through the correct delay and fade states. |
| // The delay and fade duration are simulated and not actually experienced |
| // during the test because StatusBubbleMacTest sets immediate_ mode. |
| TEST_F(StatusBubbleMacTest, StateTransitions) { |
| // First, some sanity |
| |
| EXPECT_FALSE(IsVisible()); |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); |
| |
| States()->clear(); |
| EXPECT_TRUE(States()->empty()); |
| |
| bubble_->SetStatus(string16()); |
| EXPECT_FALSE(IsVisible()); |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); |
| EXPECT_TRUE(States()->empty()); // no change from initial kBubbleHidden state |
| |
| // Next, a few ordinary cases |
| |
| // Test StartShowing from kBubbleHidden |
| bubble_->SetStatus(UTF8ToUTF16("Status")); |
| EXPECT_TRUE(IsVisible()); |
| // Check GetState before checking States to make sure that all state |
| // transitions have been flushed to States. |
| EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState()); |
| EXPECT_EQ(3u, States()->size()); |
| EXPECT_EQ(StatusBubbleMac::kBubbleShowingTimer, StateAt(0)); |
| EXPECT_EQ(StatusBubbleMac::kBubbleShowingFadeIn, StateAt(1)); |
| EXPECT_EQ(StatusBubbleMac::kBubbleShown, StateAt(2)); |
| |
| // Test StartShowing from kBubbleShown with the same message |
| States()->clear(); |
| bubble_->SetStatus(UTF8ToUTF16("Status")); |
| EXPECT_TRUE(IsVisible()); |
| EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState()); |
| EXPECT_TRUE(States()->empty()); |
| |
| // Test StartShowing from kBubbleShown with a different message |
| bubble_->SetStatus(UTF8ToUTF16("New Status")); |
| EXPECT_TRUE(IsVisible()); |
| EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState()); |
| EXPECT_TRUE(States()->empty()); |
| |
| // Test StartHiding from kBubbleShown |
| bubble_->SetStatus(string16()); |
| EXPECT_FALSE(IsVisible()); |
| // Check GetState before checking States to make sure that all state |
| // transitions have been flushed to States. |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); |
| EXPECT_EQ(3u, States()->size()); |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidingTimer, StateAt(0)); |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidingFadeOut, StateAt(1)); |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(2)); |
| |
| // Test StartHiding from kBubbleHidden |
| States()->clear(); |
| bubble_->SetStatus(string16()); |
| EXPECT_FALSE(IsVisible()); |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); |
| EXPECT_TRUE(States()->empty()); |
| |
| // Now, the edge cases |
| |
| // Test StartShowing from kBubbleShowingTimer |
| bubble_->SetStatus(UTF8ToUTF16("Status")); |
| SetState(StatusBubbleMac::kBubbleShowingTimer); |
| [GetWindow() setAlphaValue:0.0]; |
| States()->clear(); |
| EXPECT_TRUE(States()->empty()); |
| bubble_->SetStatus(UTF8ToUTF16("Status")); |
| EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState()); |
| EXPECT_EQ(2u, States()->size()); |
| EXPECT_EQ(StatusBubbleMac::kBubbleShowingFadeIn, StateAt(0)); |
| EXPECT_EQ(StatusBubbleMac::kBubbleShown, StateAt(1)); |
| |
| // Test StartShowing from kBubbleShowingFadeIn |
| bubble_->SetStatus(UTF8ToUTF16("Status")); |
| SetState(StatusBubbleMac::kBubbleShowingFadeIn); |
| [GetWindow() setAlphaValue:0.5]; |
| States()->clear(); |
| EXPECT_TRUE(States()->empty()); |
| bubble_->SetStatus(UTF8ToUTF16("Status")); |
| // The actual state values can't be tested in immediate_ mode because |
| // the window wasn't actually fading in. Without immediate_ mode, |
| // expect kBubbleShown. |
| bubble_->SetStatus(string16()); // Go back to a deterministic state. |
| |
| // Test StartShowing from kBubbleHidingTimer |
| bubble_->SetStatus(string16()); |
| SetState(StatusBubbleMac::kBubbleHidingTimer); |
| [GetWindow() setAlphaValue:1.0]; |
| States()->clear(); |
| EXPECT_TRUE(States()->empty()); |
| bubble_->SetStatus(UTF8ToUTF16("Status")); |
| EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState()); |
| EXPECT_EQ(1u, States()->size()); |
| EXPECT_EQ(StatusBubbleMac::kBubbleShown, StateAt(0)); |
| |
| // Test StartShowing from kBubbleHidingFadeOut |
| bubble_->SetStatus(string16()); |
| SetState(StatusBubbleMac::kBubbleHidingFadeOut); |
| [GetWindow() setAlphaValue:0.5]; |
| States()->clear(); |
| EXPECT_TRUE(States()->empty()); |
| bubble_->SetStatus(UTF8ToUTF16("Status")); |
| EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState()); |
| EXPECT_EQ(2u, States()->size()); |
| EXPECT_EQ(StatusBubbleMac::kBubbleShowingFadeIn, StateAt(0)); |
| EXPECT_EQ(StatusBubbleMac::kBubbleShown, StateAt(1)); |
| |
| // Test StartHiding from kBubbleShowingTimer |
| bubble_->SetStatus(UTF8ToUTF16("Status")); |
| SetState(StatusBubbleMac::kBubbleShowingTimer); |
| [GetWindow() setAlphaValue:0.0]; |
| States()->clear(); |
| EXPECT_TRUE(States()->empty()); |
| bubble_->SetStatus(string16()); |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); |
| EXPECT_EQ(1u, States()->size()); |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0)); |
| |
| // Test StartHiding from kBubbleShowingFadeIn |
| bubble_->SetStatus(UTF8ToUTF16("Status")); |
| SetState(StatusBubbleMac::kBubbleShowingFadeIn); |
| [GetWindow() setAlphaValue:0.5]; |
| States()->clear(); |
| EXPECT_TRUE(States()->empty()); |
| bubble_->SetStatus(string16()); |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); |
| EXPECT_EQ(2u, States()->size()); |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidingFadeOut, StateAt(0)); |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(1)); |
| |
| // Test StartHiding from kBubbleHidingTimer |
| bubble_->SetStatus(string16()); |
| SetState(StatusBubbleMac::kBubbleHidingTimer); |
| [GetWindow() setAlphaValue:1.0]; |
| States()->clear(); |
| EXPECT_TRUE(States()->empty()); |
| bubble_->SetStatus(string16()); |
| // The actual state values can't be tested in immediate_ mode because |
| // the timer wasn't actually running. Without immediate_ mode, expect |
| // kBubbleHidingFadeOut and kBubbleHidden. |
| // Go back to a deterministic state. |
| bubble_->SetStatus(UTF8ToUTF16("Status")); |
| |
| // Test StartHiding from kBubbleHidingFadeOut |
| bubble_->SetStatus(string16()); |
| SetState(StatusBubbleMac::kBubbleHidingFadeOut); |
| [GetWindow() setAlphaValue:0.5]; |
| States()->clear(); |
| EXPECT_TRUE(States()->empty()); |
| bubble_->SetStatus(string16()); |
| // The actual state values can't be tested in immediate_ mode because |
| // the window wasn't actually fading out. Without immediate_ mode, expect |
| // kBubbleHidden. |
| // Go back to a deterministic state. |
| bubble_->SetStatus(UTF8ToUTF16("Status")); |
| |
| // Test Hide from kBubbleHidden |
| bubble_->SetStatus(string16()); |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); |
| States()->clear(); |
| EXPECT_TRUE(States()->empty()); |
| bubble_->Hide(); |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); |
| EXPECT_TRUE(States()->empty()); |
| |
| // Test Hide from kBubbleShowingTimer |
| bubble_->SetStatus(UTF8ToUTF16("Status")); |
| SetState(StatusBubbleMac::kBubbleShowingTimer); |
| [GetWindow() setAlphaValue:0.0]; |
| States()->clear(); |
| EXPECT_TRUE(States()->empty()); |
| bubble_->Hide(); |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); |
| EXPECT_EQ(1u, States()->size()); |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0)); |
| |
| // Test Hide from kBubbleShowingFadeIn |
| bubble_->SetStatus(UTF8ToUTF16("Status")); |
| SetState(StatusBubbleMac::kBubbleShowingFadeIn); |
| [GetWindow() setAlphaValue:0.5]; |
| States()->clear(); |
| EXPECT_TRUE(States()->empty()); |
| bubble_->Hide(); |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); |
| EXPECT_EQ(2u, States()->size()); |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidingFadeOut, StateAt(0)); |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(1)); |
| |
| // Test Hide from kBubbleShown |
| bubble_->SetStatus(UTF8ToUTF16("Status")); |
| States()->clear(); |
| EXPECT_TRUE(States()->empty()); |
| bubble_->Hide(); |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); |
| EXPECT_EQ(1u, States()->size()); |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0)); |
| |
| // Test Hide from kBubbleHidingTimer |
| bubble_->SetStatus(UTF8ToUTF16("Status")); |
| SetState(StatusBubbleMac::kBubbleHidingTimer); |
| States()->clear(); |
| EXPECT_TRUE(States()->empty()); |
| bubble_->Hide(); |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); |
| EXPECT_EQ(1u, States()->size()); |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0)); |
| |
| // Test Hide from kBubbleHidingFadeOut |
| bubble_->SetStatus(UTF8ToUTF16("Status")); |
| SetState(StatusBubbleMac::kBubbleHidingFadeOut); |
| [GetWindow() setAlphaValue:0.5]; |
| States()->clear(); |
| EXPECT_TRUE(States()->empty()); |
| bubble_->Hide(); |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); |
| EXPECT_EQ(1u, States()->size()); |
| EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0)); |
| } |
| |
| TEST_F(StatusBubbleMacTest, Delete) { |
| NSWindow* window = test_window(); |
| // Create and delete immediately. |
| StatusBubbleMac* bubble = new StatusBubbleMac(window, nil); |
| delete bubble; |
| |
| // Create then delete while visible. |
| bubble = new StatusBubbleMac(window, nil); |
| bubble->SetStatus(UTF8ToUTF16("showing")); |
| delete bubble; |
| } |
| |
| TEST_F(StatusBubbleMacTest, UpdateSizeAndPosition) { |
| // Test |UpdateSizeAndPosition()| when status bubble does not exist (shouldn't |
| // crash; shouldn't create window). |
| EXPECT_TRUE(GetWindow()); |
| bubble_->UpdateSizeAndPosition(); |
| EXPECT_TRUE(GetWindow()); |
| |
| // Create a status bubble (with contents) and call resize (without actually |
| // resizing); the frame size shouldn't change. |
| bubble_->SetStatus(UTF8ToUTF16("UpdateSizeAndPosition test")); |
| ASSERT_TRUE(GetWindow()); |
| NSRect rect_before = [GetWindow() frame]; |
| bubble_->UpdateSizeAndPosition(); |
| NSRect rect_after = [GetWindow() frame]; |
| EXPECT_TRUE(NSEqualRects(rect_before, rect_after)); |
| |
| // Move the window and call resize; only the origin should change. |
| NSWindow* window = test_window(); |
| ASSERT_TRUE(window); |
| NSRect frame = [window frame]; |
| rect_before = [GetWindow() frame]; |
| frame.origin.x += 10.0; // (fairly arbitrary nonzero value) |
| frame.origin.y += 10.0; // (fairly arbitrary nonzero value) |
| [window setFrame:frame display:YES]; |
| bubble_->UpdateSizeAndPosition(); |
| rect_after = [GetWindow() frame]; |
| EXPECT_NE(rect_before.origin.x, rect_after.origin.x); |
| EXPECT_NE(rect_before.origin.y, rect_after.origin.y); |
| EXPECT_EQ(rect_before.size.width, rect_after.size.width); |
| EXPECT_EQ(rect_before.size.height, rect_after.size.height); |
| |
| // Resize the window (without moving). The origin shouldn't change. The width |
| // should change (in the current implementation), but not the height. |
| frame = [window frame]; |
| rect_before = [GetWindow() frame]; |
| frame.size.width += 50.0; // (fairly arbitrary nonzero value) |
| frame.size.height += 50.0; // (fairly arbitrary nonzero value) |
| [window setFrame:frame display:YES]; |
| bubble_->UpdateSizeAndPosition(); |
| rect_after = [GetWindow() frame]; |
| EXPECT_EQ(rect_before.origin.x, rect_after.origin.x); |
| EXPECT_EQ(rect_before.origin.y, rect_after.origin.y); |
| EXPECT_NE(rect_before.size.width, rect_after.size.width); |
| EXPECT_EQ(rect_before.size.height, rect_after.size.height); |
| } |
| |
| TEST_F(StatusBubbleMacTest, MovingWindowUpdatesPosition) { |
| NSWindow* window = test_window(); |
| |
| // Show the bubble and make sure it has the same origin as |window|. |
| bubble_->SetStatus(UTF8ToUTF16("Showing")); |
| NSWindow* child = GetWindow(); |
| EXPECT_TRUE(NSEqualPoints([window frame].origin, [child frame].origin)); |
| |
| // Hide the bubble, move the window, and show it again. |
| bubble_->Hide(); |
| NSRect frame = [window frame]; |
| frame.origin.x += 50; |
| [window setFrame:frame display:YES]; |
| bubble_->SetStatus(UTF8ToUTF16("Reshowing")); |
| |
| // The bubble should reattach in the correct location. |
| child = GetWindow(); |
| EXPECT_TRUE(NSEqualPoints([window frame].origin, [child frame].origin)); |
| } |
| |
| TEST_F(StatusBubbleMacTest, StatuBubbleRespectsBaseFrameLimits) { |
| NSWindow* window = test_window(); |
| |
| // Show the bubble and make sure it has the same origin as |window|. |
| bubble_->SetStatus(UTF8ToUTF16("Showing")); |
| NSWindow* child = GetWindow(); |
| EXPECT_TRUE(NSEqualPoints([window frame].origin, [child frame].origin)); |
| |
| // Hide the bubble, change base frame offset, and show it again. |
| bubble_->Hide(); |
| |
| NSPoint baseFrameOffset = NSMakePoint(0, [window frame].size.height / 3); |
| EXPECT_GT(baseFrameOffset.y, 0); |
| [delegate_ forceBaseFrameOffset:baseFrameOffset]; |
| |
| bubble_->SetStatus(UTF8ToUTF16("Reshowing")); |
| |
| // The bubble should reattach in the correct location. |
| child = GetWindow(); |
| NSPoint expectedOrigin = [window frame].origin; |
| expectedOrigin.x += baseFrameOffset.x; |
| expectedOrigin.y += baseFrameOffset.y; |
| EXPECT_TRUE(NSEqualPoints(expectedOrigin, [child frame].origin)); |
| } |
| |
| TEST_F(StatusBubbleMacTest, ExpandBubble) { |
| NSWindow* window = test_window(); |
| ASSERT_TRUE(window); |
| NSRect window_frame = [window frame]; |
| window_frame.size.width = 600.0; |
| [window setFrame:window_frame display:YES]; |
| |
| // Check basic expansion |
| bubble_->SetStatus(UTF8ToUTF16("Showing")); |
| EXPECT_TRUE(IsVisible()); |
| bubble_->SetURL(GURL("http://www.battersbox.com/peter_paul_and_mary.html"), |
| std::string()); |
| EXPECT_TRUE([GetURLText() hasSuffix:@"\u2026"]); |
| bubble_->ExpandBubble(); |
| EXPECT_TRUE(IsVisible()); |
| EXPECT_NSEQ(@"www.battersbox.com/peter_paul_and_mary.html", GetURLText()); |
| bubble_->Hide(); |
| |
| // Make sure bubble resets after hide. |
| bubble_->SetStatus(UTF8ToUTF16("Showing")); |
| bubble_->SetURL(GURL("http://www.snickersnee.com/pioneer_fishstix.html"), |
| std::string()); |
| EXPECT_TRUE([GetURLText() hasSuffix:@"\u2026"]); |
| // ...and that it expands again properly. |
| bubble_->ExpandBubble(); |
| EXPECT_NSEQ(@"www.snickersnee.com/pioneer_fishstix.html", GetURLText()); |
| // ...again, again! |
| bubble_->SetURL(GURL("http://www.battersbox.com/peter_paul_and_mary.html"), |
| std::string()); |
| bubble_->ExpandBubble(); |
| EXPECT_NSEQ(@"www.battersbox.com/peter_paul_and_mary.html", GetURLText()); |
| bubble_->Hide(); |
| |
| window_frame = [window frame]; |
| window_frame.size.width = 300.0; |
| [window setFrame:window_frame display:YES]; |
| |
| // Very long URL's will be cut off even in the expanded state. |
| bubble_->SetStatus(UTF8ToUTF16("Showing")); |
| const char veryLongUrl[] = |
| "http://www.diewahrscheinlichlaengstepralinederwelt.com/duuuuplo.html"; |
| bubble_->SetURL(GURL(veryLongUrl), std::string()); |
| EXPECT_TRUE([GetURLText() hasSuffix:@"\u2026"]); |
| bubble_->ExpandBubble(); |
| EXPECT_TRUE([GetURLText() hasSuffix:@"\u2026"]); |
| } |
| |
| TEST_F(StatusBubbleMacTest, BubbleAvoidsMouse) { |
| NSWindow* window = test_window(); |
| |
| // All coordinates here are relative to the window origin. |
| |
| // Initially, the bubble should appear in the bottom left. |
| bubble_->SetStatus(UTF8ToUTF16("Showing")); |
| EXPECT_TRUE(IsPointInBubble(0, 0)); |
| bubble_->Hide(); |
| |
| // Check that the bubble doesn't appear in the left corner if the |
| // mouse is currently located there. |
| SetMouseLocation(0, 0); |
| bubble_->SetStatus(UTF8ToUTF16("Showing")); |
| EXPECT_FALSE(IsPointInBubble(0, 0)); |
| |
| // Leave the bubble visible, and try moving the mouse around. |
| int smallValue = NSHeight([GetWindow() frame]) / 2; |
| EXPECT_TRUE(CheckAvoidsMouse(0, 0)); |
| EXPECT_TRUE(CheckAvoidsMouse(smallValue, 0)); |
| EXPECT_TRUE(CheckAvoidsMouse(0, smallValue)); |
| EXPECT_TRUE(CheckAvoidsMouse(smallValue, smallValue)); |
| |
| // Simulate moving the mouse down from the top of the window. |
| for (int y = NSHeight([window frame]); y >= 0; y -= smallValue) { |
| ASSERT_TRUE(CheckAvoidsMouse(smallValue, y)); |
| } |
| |
| // Simulate moving the mouse from left to right. |
| int windowWidth = NSWidth([window frame]); |
| for (int x = 0; x < windowWidth; x += smallValue) { |
| ASSERT_TRUE(CheckAvoidsMouse(x, smallValue)); |
| } |
| |
| // Simulate moving the mouse from right to left. |
| for (int x = windowWidth; x >= 0; x -= smallValue) { |
| ASSERT_TRUE(CheckAvoidsMouse(x, smallValue)); |
| } |
| } |