1 // Copyright 2014 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 "chrome/browser/ui/cocoa/website_settings/website_settings_bubble_controller.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "chrome/browser/ui/cocoa/cocoa_test_helper.h"
9 #include "testing/gtest_mac.h"
11 @interface WebsiteSettingsBubbleController (ExposedForTesting)
12 - (NSSegmentedControl*)segmentedControl;
13 - (NSTabView*)tabView;
14 - (NSView*)permissionsView;
15 - (NSView*)connectionTabContentView;
16 - (NSImageView*)identityStatusIcon;
17 - (NSTextField*)identityStatusDescriptionField;
18 - (NSImageView*)connectionStatusIcon;
19 - (NSTextField*)connectionStatusDescriptionField;
20 - (NSTextField*)firstVisitDescriptionField;
21 - (NSButton*)helpButton;
24 @implementation WebsiteSettingsBubbleController (ExposedForTesting)
25 - (NSSegmentedControl*)segmentedControl {
26 return segmentedControl_.get();
28 - (NSTabView*)tabView {
29 return tabView_.get();
31 - (NSView*)permissionsView {
32 return permissionsView_;
35 - (NSView*)connectionTabContentView {
36 return connectionTabContentView_;
39 - (NSImageView*)identityStatusIcon {
40 return identityStatusIcon_;
43 - (NSTextField*)identityStatusDescriptionField {
44 return identityStatusDescriptionField_;
47 - (NSImageView*)connectionStatusIcon {
48 return connectionStatusIcon_;
51 - (NSTextField*)connectionStatusDescriptionField {
52 return connectionStatusDescriptionField_;
55 - (NSTextField*)firstVisitDescriptionField {
56 return firstVisitDescriptionField_;
59 - (NSButton*)helpButton {
64 @interface WebsiteSettingsBubbleControllerForTesting
65 : WebsiteSettingsBubbleController {
67 CGFloat defaultWindowWidth_;
71 @implementation WebsiteSettingsBubbleControllerForTesting
72 - (void)setDefaultWindowWidth:(CGFloat)width {
73 defaultWindowWidth_ = width;
75 - (CGFloat)defaultWindowWidth {
76 // If |defaultWindowWidth_| is 0, use the superclass implementation.
77 return defaultWindowWidth_ ?
78 defaultWindowWidth_ : [super defaultWindowWidth];
84 // Indices of the menu items in the permission menu.
85 enum PermissionMenuIndices {
86 kMenuIndexContentSettingAllow = 0,
87 kMenuIndexContentSettingBlock,
88 kMenuIndexContentSettingDefault
91 const ContentSettingsType kTestPermissionTypes[] = {
92 // NOTE: FULLSCREEN does not support "Always block", so it must appear as
93 // one of the first three permissions.
94 CONTENT_SETTINGS_TYPE_FULLSCREEN,
95 CONTENT_SETTINGS_TYPE_IMAGES,
96 CONTENT_SETTINGS_TYPE_JAVASCRIPT,
97 CONTENT_SETTINGS_TYPE_PLUGINS,
98 CONTENT_SETTINGS_TYPE_POPUPS,
99 CONTENT_SETTINGS_TYPE_GEOLOCATION,
100 CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
101 CONTENT_SETTINGS_TYPE_MOUSELOCK,
102 CONTENT_SETTINGS_TYPE_MEDIASTREAM,
105 const ContentSetting kTestSettings[] = {
106 CONTENT_SETTING_DEFAULT,
107 CONTENT_SETTING_DEFAULT,
108 CONTENT_SETTING_DEFAULT,
109 CONTENT_SETTING_ALLOW,
110 CONTENT_SETTING_BLOCK,
111 CONTENT_SETTING_ALLOW,
112 CONTENT_SETTING_BLOCK,
113 CONTENT_SETTING_ALLOW,
114 CONTENT_SETTING_BLOCK,
117 const ContentSetting kTestDefaultSettings[] = {
118 CONTENT_SETTING_ALLOW,
119 CONTENT_SETTING_BLOCK,
123 const content_settings::SettingSource kTestSettingSources[] = {
124 content_settings::SETTING_SOURCE_USER,
125 content_settings::SETTING_SOURCE_USER,
126 content_settings::SETTING_SOURCE_USER,
127 content_settings::SETTING_SOURCE_USER,
128 content_settings::SETTING_SOURCE_USER,
129 content_settings::SETTING_SOURCE_POLICY,
130 content_settings::SETTING_SOURCE_POLICY,
131 content_settings::SETTING_SOURCE_EXTENSION,
132 content_settings::SETTING_SOURCE_EXTENSION,
135 class WebsiteSettingsBubbleControllerTest : public CocoaTest {
137 WebsiteSettingsBubbleControllerTest() {
141 virtual void TearDown() {
143 CocoaTest::TearDown();
147 WebsiteSettingsUIBridge* bridge_; // Weak, owned by controller.
154 // Creates a new website settings bubble, with the given default width.
155 // If |default_width| is 0, the *default* default width will be used.
156 void CreateBubbleWithWidth(CGFloat default_width) {
157 bridge_ = new WebsiteSettingsUIBridge();
159 // The controller cleans up after itself when the window closes.
160 controller_ = [WebsiteSettingsBubbleControllerForTesting alloc];
161 [controller_ setDefaultWindowWidth:default_width];
162 [controller_ initWithParentWindow:test_window()
163 websiteSettingsUIBridge:bridge_
166 window_ = [controller_ window];
167 [controller_ showWindow:nil];
170 void CreateBubble() {
171 CreateBubbleWithWidth(0.0);
174 // Return a pointer to the first NSTextField found that either matches, or
175 // doesn't match, the given text.
176 NSTextField* FindTextField(MatchType match_type, NSString* text) {
177 // The window's only immediate child is an invisible view that has a flipped
178 // coordinate origin. It is into this that all views get placed.
179 NSArray* window_subviews = [[window_ contentView] subviews];
180 EXPECT_EQ(1U, [window_subviews count]);
181 NSArray* subviews = [[window_subviews lastObject] subviews];
183 // Expect 4 views: the identity, identity status, the segmented control
184 // (which implements the tab strip), and the tab view.
185 EXPECT_EQ(4U, [subviews count]);
187 bool desired_result = match_type == TEXT_EQUAL;
189 for (NSView* view in subviews) {
190 if ([view isKindOfClass:[NSTextField class]]) {
191 NSTextField* text_field = static_cast<NSTextField*>(view);
192 if ([[text_field stringValue] isEqual:text] == desired_result)
199 NSMutableArray* FindAllSubviewsOfClass(NSView* parent_view, Class a_class) {
200 NSMutableArray* views = [NSMutableArray array];
201 for (NSView* view in [parent_view subviews]) {
202 if ([view isKindOfClass:a_class])
203 [views addObject:view];
208 // Sets up the dialog with some test permission settings.
209 void SetTestPermissions() {
210 // Create a list of 5 different permissions, corresponding to all the
211 // possible settings:
212 // - [allow, block, ask] by default
213 // - [block, allow] * [by user, by policy, by extension]
214 PermissionInfoList list;
215 WebsiteSettingsUI::PermissionInfo info;
216 for (size_t i = 0; i < arraysize(kTestPermissionTypes); ++i) {
217 info.type = kTestPermissionTypes[i];
218 info.setting = kTestSettings[i];
219 if (info.setting == CONTENT_SETTING_DEFAULT)
220 info.default_setting = kTestDefaultSettings[i];
221 info.source = kTestSettingSources[i];
222 list.push_back(info);
224 bridge_->SetPermissionInfo(list);
227 WebsiteSettingsBubbleControllerForTesting* controller_; // Weak, owns self.
228 NSWindow* window_; // Weak, owned by controller.
231 TEST_F(WebsiteSettingsBubbleControllerTest, BasicIdentity) {
232 WebsiteSettingsUI::IdentityInfo info;
233 info.site_identity = std::string("nhl.com");
234 info.identity_status = WebsiteSettings::SITE_IDENTITY_STATUS_UNKNOWN;
238 // Test setting the site identity.
239 bridge_->SetIdentityInfo(const_cast<WebsiteSettingsUI::IdentityInfo&>(info));
240 NSTextField* identity_field = FindTextField(TEXT_EQUAL, @"nhl.com");
241 ASSERT_TRUE(identity_field != nil);
243 // Test changing the site identity, and ensure that the UI is updated.
244 info.site_identity = std::string("google.com");
245 bridge_->SetIdentityInfo(const_cast<WebsiteSettingsUI::IdentityInfo&>(info));
246 EXPECT_EQ(identity_field, FindTextField(TEXT_EQUAL, @"google.com"));
248 // Find the identity status field.
249 NSTextField* identity_status_field =
250 FindTextField(TEXT_NOT_EQUAL, @"google.com");
251 ASSERT_NE(identity_field, identity_status_field);
253 // Ensure the text of the identity status field changes when the status does.
254 NSString* status = [identity_status_field stringValue];
255 info.identity_status = WebsiteSettings::SITE_IDENTITY_STATUS_CERT;
256 bridge_->SetIdentityInfo(const_cast<WebsiteSettingsUI::IdentityInfo&>(info));
257 EXPECT_NSNE(status, [identity_status_field stringValue]);
260 TEST_F(WebsiteSettingsBubbleControllerTest, SetIdentityInfo) {
261 WebsiteSettingsUI::IdentityInfo info;
262 info.site_identity = std::string("nhl.com");
263 info.identity_status = WebsiteSettings::SITE_IDENTITY_STATUS_UNKNOWN;
264 info.identity_status_description = std::string("Identity1");
265 info.connection_status = WebsiteSettings::SITE_CONNECTION_STATUS_UNKNOWN;
266 info.connection_status_description = std::string("Connection1");
271 // Set the identity, and test that the description fields on the Connection
272 // tab are set properly.
273 bridge_->SetIdentityInfo(const_cast<WebsiteSettingsUI::IdentityInfo&>(info));
274 EXPECT_NSEQ(@"Identity1",
275 [[controller_ identityStatusDescriptionField] stringValue]);
276 EXPECT_NSEQ(@"Connection1",
277 [[controller_ connectionStatusDescriptionField] stringValue]);
279 // Check the contents of the images, and make sure they change after the
282 NSImage* identity_icon = [[controller_ identityStatusIcon] image];
283 NSImage* connection_icon = [[controller_ connectionStatusIcon] image];
284 // Icons should be the same when they are both unknown.
285 EXPECT_EQ(identity_icon, connection_icon);
287 // Ensure that the link button for certificate info is not there -- the
288 // help link button should be the first one found.
289 NSMutableArray* buttons = FindAllSubviewsOfClass(
290 [controller_ connectionTabContentView], [NSButton class]);
291 ASSERT_EQ(1U, [buttons count]);
292 EXPECT_NSEQ([controller_ helpButton], [buttons objectAtIndex:0]);
294 // Check that it has a target and action linked up.
295 NSButton* link_button = static_cast<NSButton*>([buttons objectAtIndex:0]);
296 EXPECT_NSEQ(controller_, [link_button target]);
297 EXPECT_TRUE([link_button action] == @selector(showHelpPage:));
299 info.identity_status = WebsiteSettings::SITE_IDENTITY_STATUS_CERT;
300 info.connection_status = WebsiteSettings::SITE_CONNECTION_STATUS_ENCRYPTED;
302 bridge_->SetIdentityInfo(const_cast<WebsiteSettingsUI::IdentityInfo&>(info));
304 EXPECT_NE(identity_icon, [[controller_ identityStatusIcon] image]);
305 EXPECT_NE(connection_icon, [[controller_ connectionStatusIcon] image]);
307 // The certificate info button should be there now.
308 buttons = FindAllSubviewsOfClass(
309 [controller_ connectionTabContentView], [NSButton class]);
310 ASSERT_EQ(2U, [buttons count]);
311 EXPECT_NSNE([controller_ helpButton], [buttons objectAtIndex:1]);
313 // Check that it has a target and action linked up.
314 link_button = static_cast<NSButton*>([buttons objectAtIndex:1]);
315 EXPECT_NSEQ(controller_, [link_button target]);
316 EXPECT_TRUE([link_button action] == @selector(showCertificateInfo:));
319 TEST_F(WebsiteSettingsBubbleControllerTest, SetFirstVisit) {
321 bridge_->SetFirstVisit(base::ASCIIToUTF16("Yesterday"));
322 EXPECT_NSEQ(@"Yesterday",
323 [[controller_ firstVisitDescriptionField] stringValue]);
326 TEST_F(WebsiteSettingsBubbleControllerTest, SetPermissionInfo) {
328 SetTestPermissions();
330 // There should be three subviews per permission (an icon, a label and a
331 // select box), plus a text label for the Permission section.
332 NSArray* subviews = [[controller_ permissionsView] subviews];
333 EXPECT_EQ(arraysize(kTestPermissionTypes) * 3 + 1, [subviews count]);
335 // Ensure that there is a distinct label for each permission.
336 NSMutableSet* labels = [NSMutableSet set];
337 for (NSView* view in subviews) {
338 if ([view isKindOfClass:[NSTextField class]])
339 [labels addObject:[static_cast<NSTextField*>(view) stringValue]];
341 // The section header ("Permissions") will also be found, hence the +1.
342 EXPECT_EQ(arraysize(kTestPermissionTypes) + 1, [labels count]);
344 // Ensure that the button labels are distinct, and look for the correct
345 // number of disabled buttons.
346 int disabled_count = 0;
347 [labels removeAllObjects];
348 for (NSView* view in subviews) {
349 if ([view isKindOfClass:[NSPopUpButton class]]) {
350 NSPopUpButton* button = static_cast<NSPopUpButton*>(view);
351 [labels addObject:[[button selectedCell] title]];
353 if (![button isEnabled])
357 EXPECT_EQ(arraysize(kTestPermissionTypes), [labels count]);
359 // 4 of the buttons should be disabled -- the ones that have a setting source
360 // of SETTING_SOURCE_POLICY or SETTING_SOURCE_EXTENSION.
361 EXPECT_EQ(4, disabled_count);
364 TEST_F(WebsiteSettingsBubbleControllerTest, SetSelectedTab) {
366 NSSegmentedControl* segmentedControl = [controller_ segmentedControl];
367 NSTabView* tabView = [controller_ tabView];
369 // Test whether SetSelectedTab properly changes both the segmented control
370 // (which implements the tabs) as well as the visible tab contents.
371 // NOTE: This implicitly (and deliberately) tests that the tabs appear in a
372 // specific order: Permissions, Connection.
373 EXPECT_EQ(0, [segmentedControl selectedSegment]);
374 EXPECT_EQ(0, [tabView indexOfTabViewItem:[tabView selectedTabViewItem]]);
375 bridge_->SetSelectedTab(WebsiteSettingsUI::TAB_ID_CONNECTION);
376 EXPECT_EQ(1, [segmentedControl selectedSegment]);
377 EXPECT_EQ(1, [tabView indexOfTabViewItem:[tabView selectedTabViewItem]]);
380 TEST_F(WebsiteSettingsBubbleControllerTest, WindowWidth) {
381 // Try creating a window that is obviously too small.
382 CreateBubbleWithWidth(30.0);
383 SetTestPermissions();
385 CGFloat window_width = NSWidth([[controller_ window] frame]);
387 // Check the window was made bigger to fit the content.
388 EXPECT_LT(30.0, window_width);
390 // Check that the window is wider than the right edge of all the permission
392 for (NSView* view in [[controller_ permissionsView] subviews]) {
393 if ([view isKindOfClass:[NSPopUpButton class]]) {
394 NSPopUpButton* button = static_cast<NSPopUpButton*>(view);
395 EXPECT_LT(NSMaxX([button frame]), window_width);