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/files/file_path.h"
10 #include "base/files/file_util.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 void SetUp() override {
54 CocoaProfileTest::SetUp();
55 ASSERT_TRUE(browser());
56 window_ = browser()->window()->GetNativeWindow();
57 icon_ = LoadTestIcon();
58 base::CommandLine command_line(base::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 extension_input_value.Set(keys::kPageAction, action);
98 } else if (type == extension_installed_bubble::kBrowserAction) {
99 extension_input_value.SetString(keys::kName, "browser action extension");
100 base::DictionaryValue* browser_action = new base::DictionaryValue;
101 // An empty dictionary is enough to create a Browser Action.
102 extension_input_value.Set(keys::kBrowserAction, browser_action);
103 } else if (type == extension_installed_bubble::kApp) {
104 extension_input_value.SetString(keys::kName, "test app");
105 extension_input_value.SetString(keys::kLaunchWebURL,
106 "http://www.example.com");
110 scoped_refptr<Extension> extension =
111 Extension::Create(path, extensions::Manifest::INVALID_LOCATION,
112 extension_input_value, Extension::NO_FLAGS, &error);
113 extension_service_->AddExtension(extension.get());
117 // Required to initialize the extension installed bubble.
118 NSWindow* window_; // weak, owned by CocoaProfileTest.
120 ExtensionService* extension_service_;
122 // Skeleton extension to be tested; reinitialized for each test.
123 scoped_refptr<Extension> extension_;
125 // The icon_ to be loaded into the bubble window.
129 // Confirm that window sizes are set correctly for a page action extension.
130 TEST_F(ExtensionInstalledBubbleControllerTest, PageActionTest) {
131 extension_ = CreateExtension(extension_installed_bubble::kPageAction);
132 ExtensionInstalledBubbleControllerForTest* controller =
133 [[ExtensionInstalledBubbleControllerForTest alloc]
134 initWithParentWindow:window_
135 extension:extension_.get()
139 EXPECT_TRUE(controller);
141 // Initialize window without having to calculate tabstrip locations.
142 [controller initializeWindow];
143 EXPECT_TRUE([controller window]);
145 int height = [controller calculateWindowHeight];
146 // Height should equal the vertical padding + height of all messages.
147 int correctHeight = 2 * extension_installed_bubble::kOuterVerticalMargin +
148 2 * extension_installed_bubble::kInnerVerticalMargin +
149 NSHeight([controller headingFrame]) +
150 NSHeight([controller frameOfHowToUse]) +
151 NSHeight([controller frameOfHowToManage]);
152 if ([controller showSyncPromo]) {
153 correctHeight += NSHeight([controller frameOfSigninPromo]) +
154 extension_installed_bubble::kInnerVerticalMargin;
156 EXPECT_EQ(height, correctHeight);
157 [controller setMessageFrames:height];
159 NSRect msg1Frame = [controller headingFrame];
160 NSRect msg2Frame = [controller frameOfHowToUse];
161 NSRect msg3Frame = [controller frameOfHowToManage];
162 NSRect msg4Frame = [controller frameOfSigninPromo];
164 int next_y = extension_installed_bubble::kOuterVerticalMargin;
165 if ([controller showSyncPromo]) {
166 // Bottom message should be kOuterVerticalMargin pixels above window edge.
167 EXPECT_EQ(next_y, NSMinY(msg4Frame));
168 next_y = NSMinY(msg4Frame) + NSHeight(msg4Frame) +
169 extension_installed_bubble::kInnerVerticalMargin;
172 // HowToManage frame should be kInnerVerticalMargin pixels above sync promo,
173 // unless sync promo is hidden, then kOuterVerticalMargin pixels above.
174 EXPECT_EQ(next_y, NSMinY(msg3Frame));
176 // Page action message should be kInnerVerticalMargin pixels above bottom msg.
177 EXPECT_EQ(NSMinY(msg3Frame) + NSHeight(msg3Frame) +
178 extension_installed_bubble::kInnerVerticalMargin,
181 // Top message should be kInnerVerticalMargin pixels above Page action msg.
182 EXPECT_EQ(NSMinY(msg2Frame) + NSHeight(msg2Frame) +
183 extension_installed_bubble::kInnerVerticalMargin,
186 [controller setPageActionPreviewShowing:NO];
190 TEST_F(ExtensionInstalledBubbleControllerTest, BrowserActionTest) {
191 extension_ = CreateExtension(extension_installed_bubble::kBrowserAction);
192 ExtensionInstalledBubbleControllerForTest* controller =
193 [[ExtensionInstalledBubbleControllerForTest alloc]
194 initWithParentWindow:window_
195 extension:extension_.get()
199 EXPECT_TRUE(controller);
201 // Initialize window without having to calculate tabstrip locations.
202 [controller initializeWindow];
203 EXPECT_TRUE([controller window]);
205 int height = [controller calculateWindowHeight];
206 // Height should equal the vertical padding + height of all messages.
207 int correctHeight = 2 * extension_installed_bubble::kOuterVerticalMargin +
208 2 * extension_installed_bubble::kInnerVerticalMargin +
209 NSHeight([controller headingFrame]) +
210 NSHeight([controller frameOfHowToUse]) +
211 NSHeight([controller frameOfHowToManage]);
212 if ([controller showSyncPromo]) {
213 correctHeight += NSHeight([controller frameOfSigninPromo]) +
214 extension_installed_bubble::kInnerVerticalMargin;
216 EXPECT_EQ(height, correctHeight);
217 [controller setMessageFrames:height];
219 NSRect msg1Frame = [controller headingFrame];
220 NSRect msg2Frame = [controller frameOfHowToUse];
221 NSRect msg3Frame = [controller frameOfHowToManage];
222 NSRect msg4Frame = [controller frameOfSigninPromo];
224 int next_y = extension_installed_bubble::kOuterVerticalMargin;
225 if ([controller showSyncPromo]) {
226 // Bottom message should be kOuterVerticalMargin pixels above window edge.
227 EXPECT_EQ(next_y, NSMinY(msg4Frame));
228 next_y = NSMinY(msg4Frame) + NSHeight(msg4Frame) +
229 extension_installed_bubble::kInnerVerticalMargin;
232 // HowToManage frame should be kInnerVerticalMargin pixels above sync promo,
233 // unless sync promo is hidden, then kOuterVerticalMargin pixels above.
234 EXPECT_EQ(next_y, NSMinY(msg3Frame));
236 // Browser action message should be kInnerVerticalMargin pixels above bottom
238 EXPECT_EQ(NSMinY(msg3Frame) + NSHeight(msg3Frame) +
239 extension_installed_bubble::kInnerVerticalMargin,
242 // Top message should be kInnerVerticalMargin pixels above Browser action msg.
243 EXPECT_EQ(NSMinY(msg2Frame) + NSHeight(msg2Frame) +
244 extension_installed_bubble::kInnerVerticalMargin,
250 TEST_F(ExtensionInstalledBubbleControllerTest, ParentClose) {
251 extension_ = CreateExtension(extension_installed_bubble::kBrowserAction);
252 ExtensionInstalledBubbleControllerForTest* controller =
253 [[ExtensionInstalledBubbleControllerForTest alloc]
254 initWithParentWindow:window_
255 extension:extension_.get()
259 EXPECT_TRUE(controller);
261 // Bring up the window and disable close animation.
262 [controller showWindow:nil];
263 NSWindow* bubbleWindow = [controller window];
264 ASSERT_TRUE([bubbleWindow isKindOfClass:[InfoBubbleWindow class]]);
265 [static_cast<InfoBubbleWindow*>(bubbleWindow)
266 setAllowedAnimations:info_bubble::kAnimateNone];
268 // Observe whether the bubble window closes.
269 NSString* notification = NSWindowWillCloseNotification;
270 id observer = [OCMockObject observerMock];
271 [[observer expect] notificationWithName:notification object:bubbleWindow];
272 [[NSNotificationCenter defaultCenter]
273 addMockObserver:observer name:notification object:bubbleWindow];
275 // The bubble window goes from visible to not-visible.
276 EXPECT_TRUE([bubbleWindow isVisible]);
278 EXPECT_FALSE([bubbleWindow isVisible]);
280 [[NSNotificationCenter defaultCenter] removeObserver:observer];
282 // Check that the appropriate notification was received.
283 EXPECT_OCMOCK_VERIFY(observer);
286 TEST_F(ExtensionInstalledBubbleControllerTest, AppTest) {
287 extension_ = CreateExtension(extension_installed_bubble::kApp);
288 ExtensionInstalledBubbleControllerForTest* controller =
289 [[ExtensionInstalledBubbleControllerForTest alloc]
290 initWithParentWindow:window_
291 extension:extension_.get()
295 EXPECT_TRUE(controller);
296 [controller initializeWindow];
297 EXPECT_TRUE([controller window]);
299 int height = [controller calculateWindowHeight];
301 // Make sure there is always enough room for the icon and margin.
302 int minHeight = extension_installed_bubble::kIconSize +
303 (2 * extension_installed_bubble::kOuterVerticalMargin);
304 EXPECT_GT(height, minHeight);
306 // Make sure the "show me" link is visible.
307 EXPECT_FALSE([[controller appInstalledShortcutLink] isHidden]);