// Copyright (c) 2012 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. #import "chrome/browser/ui/cocoa/base_bubble_controller.h" #include "base/mac/mac_util.h" #import "base/memory/scoped_nsobject.h" #import "chrome/browser/ui/cocoa/cocoa_test_helper.h" #import "chrome/browser/ui/cocoa/info_bubble_view.h" #import "chrome/browser/ui/cocoa/run_loop_testing.h" #import "ui/base/test/cocoa_test_event_utils.h" namespace { const CGFloat kBubbleWindowWidth = 100; const CGFloat kBubbleWindowHeight = 50; const CGFloat kAnchorPointX = 400; const CGFloat kAnchorPointY = 300; } // namespace class BaseBubbleControllerTest : public CocoaTest { public: virtual void SetUp() OVERRIDE { bubbleWindow_.reset([[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, kBubbleWindowWidth, kBubbleWindowHeight) styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES]); // The bubble controller will release itself when the window closes. controller_ = [[BaseBubbleController alloc] initWithWindow:bubbleWindow_.get() parentWindow:test_window() anchoredAt:NSMakePoint(kAnchorPointX, kAnchorPointY)]; EXPECT_TRUE([controller_ bubble]); } virtual void TearDown() OVERRIDE { // Close our windows. [controller_ close]; bubbleWindow_.reset(NULL); CocoaTest::TearDown(); } public: scoped_nsobject bubbleWindow_; BaseBubbleController* controller_; }; // Test that kAlignEdgeToAnchorEdge and a left bubble arrow correctly aligns the // left edge of the buble to the anchor point. TEST_F(BaseBubbleControllerTest, LeftAlign) { [[controller_ bubble] setArrowLocation:info_bubble::kTopLeft]; [[controller_ bubble] setAlignment:info_bubble::kAlignEdgeToAnchorEdge]; [controller_ showWindow:nil]; NSRect frame = [[controller_ window] frame]; // Make sure the bubble size hasn't changed. EXPECT_EQ(frame.size.width, kBubbleWindowWidth); EXPECT_EQ(frame.size.height, kBubbleWindowHeight); // Make sure the bubble is left aligned. EXPECT_EQ(NSMinX(frame), kAnchorPointX); EXPECT_GE(NSMaxY(frame), kAnchorPointY); } // Test that kAlignEdgeToAnchorEdge and a right bubble arrow correctly aligns // the right edge of the buble to the anchor point. TEST_F(BaseBubbleControllerTest, RightAlign) { [[controller_ bubble] setArrowLocation:info_bubble::kTopRight]; [[controller_ bubble] setAlignment:info_bubble::kAlignEdgeToAnchorEdge]; [controller_ showWindow:nil]; NSRect frame = [[controller_ window] frame]; // Make sure the bubble size hasn't changed. EXPECT_EQ(frame.size.width, kBubbleWindowWidth); EXPECT_EQ(frame.size.height, kBubbleWindowHeight); // Make sure the bubble is left aligned. EXPECT_EQ(NSMaxX(frame), kAnchorPointX); EXPECT_GE(NSMaxY(frame), kAnchorPointY); } // Test that kAlignArrowToAnchor and a left bubble arrow correctly aligns // the bubble arrow to the anchor point. TEST_F(BaseBubbleControllerTest, AnchorAlignLeftArrow) { [[controller_ bubble] setArrowLocation:info_bubble::kTopLeft]; [[controller_ bubble] setAlignment:info_bubble::kAlignArrowToAnchor]; [controller_ showWindow:nil]; NSRect frame = [[controller_ window] frame]; // Make sure the bubble size hasn't changed. EXPECT_EQ(frame.size.width, kBubbleWindowWidth); EXPECT_EQ(frame.size.height, kBubbleWindowHeight); // Make sure the bubble arrow points to the anchor. EXPECT_EQ(NSMinX(frame) + info_bubble::kBubbleArrowXOffset + roundf(info_bubble::kBubbleArrowWidth / 2.0), kAnchorPointX); EXPECT_GE(NSMaxY(frame), kAnchorPointY); } // Test that kAlignArrowToAnchor and a right bubble arrow correctly aligns // the bubble arrow to the anchor point. TEST_F(BaseBubbleControllerTest, AnchorAlignRightArrow) { [[controller_ bubble] setArrowLocation:info_bubble::kTopRight]; [[controller_ bubble] setAlignment:info_bubble::kAlignArrowToAnchor]; [controller_ showWindow:nil]; NSRect frame = [[controller_ window] frame]; // Make sure the bubble size hasn't changed. EXPECT_EQ(frame.size.width, kBubbleWindowWidth); EXPECT_EQ(frame.size.height, kBubbleWindowHeight); // Make sure the bubble arrow points to the anchor. EXPECT_EQ(NSMaxX(frame) - info_bubble::kBubbleArrowXOffset - floorf(info_bubble::kBubbleArrowWidth / 2.0), kAnchorPointX); EXPECT_GE(NSMaxY(frame), kAnchorPointY); } // Tests that when a new window gets key state (and the bubble resigns) that // the key window changes. TEST_F(BaseBubbleControllerTest, ResignKeyCloses) { // Closing the bubble will autorelease the controller. scoped_nsobject keep_alive([controller_ retain]); NSWindow* bubble_window = [controller_ window]; EXPECT_FALSE([bubble_window isVisible]); scoped_nsobject other_window( [[NSWindow alloc] initWithContentRect:NSMakeRect(500, 500, 500, 500) styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:YES]); EXPECT_FALSE([other_window isVisible]); [controller_ showWindow:nil]; EXPECT_TRUE([bubble_window isVisible]); EXPECT_FALSE([other_window isVisible]); [other_window makeKeyAndOrderFront:nil]; // Fake the key state notification. Because unit_tests is a "daemon" process // type, its windows can never become key (nor can the app become active). // Instead of the hacks below, one could make a browser_test or transform the // process type, but this seems easiest and is best suited to a unit test. // // On Lion and above, which have the event taps, simply post a notification // that will cause the controller to call |-windowDidResignKey:|. Earlier // OSes can call through directly. NSNotification* notif = [NSNotification notificationWithName:NSWindowDidResignKeyNotification object:bubble_window]; if (base::mac::IsOSLionOrLater()) [[NSNotificationCenter defaultCenter] postNotification:notif]; else [controller_ windowDidResignKey:notif]; EXPECT_FALSE([bubble_window isVisible]); EXPECT_TRUE([other_window isVisible]); } // Test that clicking outside the window causes the bubble to close. TEST_F(BaseBubbleControllerTest, LionClickOutsideCloses) { // The event tap is only installed on 10.7+. if (!base::mac::IsOSLionOrLater()) return; // Closing the bubble will autorelease the controller. scoped_nsobject keep_alive([controller_ retain]); NSWindow* window = [controller_ window]; EXPECT_FALSE([window isVisible]); [controller_ showWindow:nil]; EXPECT_TRUE([window isVisible]); NSEvent* event = cocoa_test_event_utils::LeftMouseDownAtPointInWindow( NSMakePoint(10, 10), test_window()); [NSApp sendEvent:event]; chrome::testing::NSRunLoopRunAllPending(); EXPECT_FALSE([window isVisible]); }