1 // Copyright (c) 2012 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 #import <Cocoa/Cocoa.h>
7 #include "base/basictypes.h"
8 #include "base/command_line.h"
9 #include "base/file_util.h"
10 #include "base/files/file_path.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/path_service.h"
13 #include "base/values.h"
14 #include "chrome/browser/extensions/extension_service.h"
15 #include "chrome/browser/extensions/test_extension_system.h"
16 #import "chrome/browser/ui/browser_window.h"
17 #include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
18 #import "chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller.h"
19 #import "chrome/browser/ui/cocoa/info_bubble_window.h"
20 #include "chrome/common/chrome_paths.h"
21 #include "chrome/test/base/testing_profile.h"
22 #include "extensions/common/extension.h"
23 #include "extensions/common/manifest_constants.h"
24 #import "third_party/ocmock/OCMock/OCMock.h"
25 #include "third_party/ocmock/gtest_support.h"
26 #include "ui/gfx/codec/png_codec.h"
28 using extensions::Extension;
30 // ExtensionInstalledBubbleController with removePageActionPreview overridden
31 // to a no-op, because pageActions are not yet hooked up in the test browser.
32 @interface ExtensionInstalledBubbleControllerForTest :
33 ExtensionInstalledBubbleController {
36 // Do nothing, because browser window is not set up with page actions
38 - (void)removePageActionPreview;
42 @implementation ExtensionInstalledBubbleControllerForTest
44 - (void)removePageActionPreview { }
48 namespace keys = extensions::manifest_keys;
50 class ExtensionInstalledBubbleControllerTest : public CocoaProfileTest {
53 virtual void SetUp() {
54 CocoaProfileTest::SetUp();
55 ASSERT_TRUE(browser());
56 window_ = browser()->window()->GetNativeWindow();
57 icon_ = LoadTestIcon();
58 CommandLine command_line(CommandLine::NO_PROGRAM);
59 extension_service_ = static_cast<extensions::TestExtensionSystem*>(
60 extensions::ExtensionSystem::Get(profile()))->CreateExtensionService(
61 &command_line, base::FilePath(), false);
64 // Load test icon from extension test directory.
65 SkBitmap LoadTestIcon() {
67 PathService::Get(chrome::DIR_TEST_DATA, &path);
68 path = path.AppendASCII("extensions").AppendASCII("icon1.png");
70 std::string file_contents;
71 base::ReadFileToString(path, &file_contents);
72 const unsigned char* data =
73 reinterpret_cast<const unsigned char*>(file_contents.data());
76 gfx::PNGCodec::Decode(data, file_contents.length(), &bitmap);
80 // Create a skeletal framework of either page action or browser action
81 // type. This extension only needs to have a type and a name to initialize
82 // the ExtensionInstalledBubble for unit testing.
83 scoped_refptr<Extension> CreateExtension(
84 extension_installed_bubble::ExtensionType type) {
86 PathService::Get(chrome::DIR_TEST_DATA, &path);
87 path = path.AppendASCII("extensions").AppendASCII("dummy");
89 base::DictionaryValue extension_input_value;
90 extension_input_value.SetString(keys::kVersion, "1.0.0.0");
91 if (type == extension_installed_bubble::kPageAction) {
92 extension_input_value.SetString(keys::kName, "page action extension");
93 base::DictionaryValue* action = new base::DictionaryValue;
94 action->SetString(keys::kPageActionId, "ExtensionActionId");
95 action->SetString(keys::kPageActionDefaultTitle, "ExtensionActionTitle");
96 action->SetString(keys::kPageActionDefaultIcon, "image1.png");
97 base::ListValue* action_list = new base::ListValue;
98 action_list->Append(action);
99 extension_input_value.Set(keys::kPageActions, action_list);
100 } else if (type == extension_installed_bubble::kBrowserAction) {
101 extension_input_value.SetString(keys::kName, "browser action extension");
102 base::DictionaryValue* browser_action = new base::DictionaryValue;
103 // An empty dictionary is enough to create a Browser Action.
104 extension_input_value.Set(keys::kBrowserAction, browser_action);
105 } else if (type == extension_installed_bubble::kApp) {
106 extension_input_value.SetString(keys::kName, "test app");
107 extension_input_value.SetString(keys::kLaunchWebURL,
108 "http://www.example.com");
112 scoped_refptr<Extension> extension =
113 Extension::Create(path, extensions::Manifest::INVALID_LOCATION,
114 extension_input_value, Extension::NO_FLAGS, &error);
115 extension_service_->AddExtension(extension.get());
119 // Required to initialize the extension installed bubble.
120 NSWindow* window_; // weak, owned by CocoaProfileTest.
122 ExtensionService* extension_service_;
124 // Skeleton extension to be tested; reinitialized for each test.
125 scoped_refptr<Extension> extension_;
127 // The icon_ to be loaded into the bubble window.
131 // Confirm that window sizes are set correctly for a page action extension.
132 TEST_F(ExtensionInstalledBubbleControllerTest, PageActionTest) {
133 extension_ = CreateExtension(extension_installed_bubble::kPageAction);
134 ExtensionInstalledBubbleControllerForTest* controller =
135 [[ExtensionInstalledBubbleControllerForTest alloc]
136 initWithParentWindow:window_
137 extension:extension_.get()
141 EXPECT_TRUE(controller);
143 // Initialize window without having to calculate tabstrip locations.
144 [controller initializeWindow];
145 EXPECT_TRUE([controller window]);
147 int height = [controller calculateWindowHeight];
148 // Height should equal the vertical padding + height of all messages.
149 int correctHeight = 2 * extension_installed_bubble::kOuterVerticalMargin +
150 2 * extension_installed_bubble::kInnerVerticalMargin +
151 NSHeight([controller headingFrame]) +
152 NSHeight([controller frameOfHowToUse]) +
153 NSHeight([controller frameOfHowToManage]);
154 if ([controller showSyncPromo]) {
155 correctHeight += NSHeight([controller frameOfSigninPromo]) +
156 extension_installed_bubble::kInnerVerticalMargin;
158 EXPECT_EQ(height, correctHeight);
159 [controller setMessageFrames:height];
161 NSRect msg1Frame = [controller headingFrame];
162 NSRect msg2Frame = [controller frameOfHowToUse];
163 NSRect msg3Frame = [controller frameOfHowToManage];
164 NSRect msg4Frame = [controller frameOfSigninPromo];
166 int next_y = extension_installed_bubble::kOuterVerticalMargin;
167 if ([controller showSyncPromo]) {
168 // Bottom message should be kOuterVerticalMargin pixels above window edge.
169 EXPECT_EQ(next_y, NSMinY(msg4Frame));
170 next_y = NSMinY(msg4Frame) + NSHeight(msg4Frame) +
171 extension_installed_bubble::kInnerVerticalMargin;
174 // HowToManage frame should be kInnerVerticalMargin pixels above sync promo,
175 // unless sync promo is hidden, then kOuterVerticalMargin pixels above.
176 EXPECT_EQ(next_y, NSMinY(msg3Frame));
178 // Page action message should be kInnerVerticalMargin pixels above bottom msg.
179 EXPECT_EQ(NSMinY(msg3Frame) + NSHeight(msg3Frame) +
180 extension_installed_bubble::kInnerVerticalMargin,
183 // Top message should be kInnerVerticalMargin pixels above Page action msg.
184 EXPECT_EQ(NSMinY(msg2Frame) + NSHeight(msg2Frame) +
185 extension_installed_bubble::kInnerVerticalMargin,
188 [controller setPageActionPreviewShowing:NO];
192 TEST_F(ExtensionInstalledBubbleControllerTest, BrowserActionTest) {
193 extension_ = CreateExtension(extension_installed_bubble::kBrowserAction);
194 ExtensionInstalledBubbleControllerForTest* controller =
195 [[ExtensionInstalledBubbleControllerForTest alloc]
196 initWithParentWindow:window_
197 extension:extension_.get()
201 EXPECT_TRUE(controller);
203 // Initialize window without having to calculate tabstrip locations.
204 [controller initializeWindow];
205 EXPECT_TRUE([controller window]);
207 int height = [controller calculateWindowHeight];
208 // Height should equal the vertical padding + height of all messages.
209 int correctHeight = 2 * extension_installed_bubble::kOuterVerticalMargin +
210 2 * extension_installed_bubble::kInnerVerticalMargin +
211 NSHeight([controller headingFrame]) +
212 NSHeight([controller frameOfHowToUse]) +
213 NSHeight([controller frameOfHowToManage]);
214 if ([controller showSyncPromo]) {
215 correctHeight += NSHeight([controller frameOfSigninPromo]) +
216 extension_installed_bubble::kInnerVerticalMargin;
218 EXPECT_EQ(height, correctHeight);
219 [controller setMessageFrames:height];
221 NSRect msg1Frame = [controller headingFrame];
222 NSRect msg2Frame = [controller frameOfHowToUse];
223 NSRect msg3Frame = [controller frameOfHowToManage];
224 NSRect msg4Frame = [controller frameOfSigninPromo];
226 int next_y = extension_installed_bubble::kOuterVerticalMargin;
227 if ([controller showSyncPromo]) {
228 // Bottom message should be kOuterVerticalMargin pixels above window edge.
229 EXPECT_EQ(next_y, NSMinY(msg4Frame));
230 next_y = NSMinY(msg4Frame) + NSHeight(msg4Frame) +
231 extension_installed_bubble::kInnerVerticalMargin;
234 // HowToManage frame should be kInnerVerticalMargin pixels above sync promo,
235 // unless sync promo is hidden, then kOuterVerticalMargin pixels above.
236 EXPECT_EQ(next_y, NSMinY(msg3Frame));
238 // Browser action message should be kInnerVerticalMargin pixels above bottom
240 EXPECT_EQ(NSMinY(msg3Frame) + NSHeight(msg3Frame) +
241 extension_installed_bubble::kInnerVerticalMargin,
244 // Top message should be kInnerVerticalMargin pixels above Browser action msg.
245 EXPECT_EQ(NSMinY(msg2Frame) + NSHeight(msg2Frame) +
246 extension_installed_bubble::kInnerVerticalMargin,
252 TEST_F(ExtensionInstalledBubbleControllerTest, ParentClose) {
253 extension_ = CreateExtension(extension_installed_bubble::kBrowserAction);
254 ExtensionInstalledBubbleControllerForTest* controller =
255 [[ExtensionInstalledBubbleControllerForTest alloc]
256 initWithParentWindow:window_
257 extension:extension_.get()
261 EXPECT_TRUE(controller);
263 // Bring up the window and disable close animation.
264 [controller showWindow:nil];
265 NSWindow* bubbleWindow = [controller window];
266 ASSERT_TRUE([bubbleWindow isKindOfClass:[InfoBubbleWindow class]]);
267 [static_cast<InfoBubbleWindow*>(bubbleWindow)
268 setAllowedAnimations:info_bubble::kAnimateNone];
270 // Observe whether the bubble window closes.
271 NSString* notification = NSWindowWillCloseNotification;
272 id observer = [OCMockObject observerMock];
273 [[observer expect] notificationWithName:notification object:bubbleWindow];
274 [[NSNotificationCenter defaultCenter]
275 addMockObserver:observer name:notification object:bubbleWindow];
277 // The bubble window goes from visible to not-visible.
278 EXPECT_TRUE([bubbleWindow isVisible]);
280 EXPECT_FALSE([bubbleWindow isVisible]);
282 [[NSNotificationCenter defaultCenter] removeObserver:observer];
284 // Check that the appropriate notification was received.
285 EXPECT_OCMOCK_VERIFY(observer);
288 TEST_F(ExtensionInstalledBubbleControllerTest, AppTest) {
289 extension_ = CreateExtension(extension_installed_bubble::kApp);
290 ExtensionInstalledBubbleControllerForTest* controller =
291 [[ExtensionInstalledBubbleControllerForTest alloc]
292 initWithParentWindow:window_
293 extension:extension_.get()
297 EXPECT_TRUE(controller);
298 [controller initializeWindow];
299 EXPECT_TRUE([controller window]);
301 int height = [controller calculateWindowHeight];
303 // Make sure there is always enough room for the icon and margin.
304 int minHeight = extension_installed_bubble::kIconSize +
305 (2 * extension_installed_bubble::kOuterVerticalMargin);
306 EXPECT_GT(height, minHeight);
308 // Make sure the "show me" link is visible.
309 EXPECT_FALSE([[controller appInstalledShortcutLink] isHidden]);