| // Copyright 2014 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/website_settings/website_settings_bubble_controller.h" |
| |
| #include "base/strings/utf_string_conversions.h" |
| #include "chrome/browser/ui/cocoa/cocoa_test_helper.h" |
| #include "testing/gtest_mac.h" |
| |
| @interface WebsiteSettingsBubbleController (ExposedForTesting) |
| - (NSSegmentedControl*)segmentedControl; |
| - (NSTabView*)tabView; |
| - (NSView*)permissionsView; |
| - (NSView*)connectionTabContentView; |
| - (NSImageView*)identityStatusIcon; |
| - (NSTextField*)identityStatusDescriptionField; |
| - (NSImageView*)connectionStatusIcon; |
| - (NSTextField*)connectionStatusDescriptionField; |
| - (NSButton*)helpButton; |
| @end |
| |
| @implementation WebsiteSettingsBubbleController (ExposedForTesting) |
| - (NSSegmentedControl*)segmentedControl { |
| return segmentedControl_.get(); |
| } |
| - (NSTabView*)tabView { |
| return tabView_.get(); |
| } |
| - (NSView*)permissionsView { |
| return permissionsView_; |
| } |
| |
| - (NSView*)connectionTabContentView { |
| return connectionTabContentView_; |
| } |
| |
| - (NSImageView*)identityStatusIcon { |
| return identityStatusIcon_; |
| } |
| |
| - (NSTextField*)identityStatusDescriptionField { |
| return identityStatusDescriptionField_; |
| } |
| |
| - (NSImageView*)connectionStatusIcon { |
| return connectionStatusIcon_; |
| } |
| |
| - (NSTextField*)connectionStatusDescriptionField { |
| return connectionStatusDescriptionField_; |
| } |
| |
| - (NSButton*)helpButton { |
| return helpButton_; |
| } |
| @end |
| |
| @interface WebsiteSettingsBubbleControllerForTesting |
| : WebsiteSettingsBubbleController { |
| @private |
| CGFloat defaultWindowWidth_; |
| } |
| @end |
| |
| @implementation WebsiteSettingsBubbleControllerForTesting |
| - (void)setDefaultWindowWidth:(CGFloat)width { |
| defaultWindowWidth_ = width; |
| } |
| - (CGFloat)defaultWindowWidth { |
| // If |defaultWindowWidth_| is 0, use the superclass implementation. |
| return defaultWindowWidth_ ? |
| defaultWindowWidth_ : [super defaultWindowWidth]; |
| } |
| @end |
| |
| namespace { |
| |
| // Indices of the menu items in the permission menu. |
| enum PermissionMenuIndices { |
| kMenuIndexContentSettingAllow = 0, |
| kMenuIndexContentSettingBlock, |
| kMenuIndexContentSettingDefault |
| }; |
| |
| const ContentSettingsType kTestPermissionTypes[] = { |
| // NOTE: FULLSCREEN does not support "Always block", so it must appear as |
| // one of the first three permissions. |
| CONTENT_SETTINGS_TYPE_FULLSCREEN, |
| CONTENT_SETTINGS_TYPE_IMAGES, |
| CONTENT_SETTINGS_TYPE_JAVASCRIPT, |
| CONTENT_SETTINGS_TYPE_PLUGINS, |
| CONTENT_SETTINGS_TYPE_POPUPS, |
| CONTENT_SETTINGS_TYPE_GEOLOCATION, |
| CONTENT_SETTINGS_TYPE_NOTIFICATIONS, |
| CONTENT_SETTINGS_TYPE_MOUSELOCK, |
| CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC |
| }; |
| |
| const ContentSetting kTestSettings[] = { |
| CONTENT_SETTING_DEFAULT, |
| CONTENT_SETTING_DEFAULT, |
| CONTENT_SETTING_DEFAULT, |
| CONTENT_SETTING_ALLOW, |
| CONTENT_SETTING_BLOCK, |
| CONTENT_SETTING_ALLOW, |
| CONTENT_SETTING_BLOCK, |
| CONTENT_SETTING_ALLOW, |
| CONTENT_SETTING_BLOCK |
| }; |
| |
| const ContentSetting kTestDefaultSettings[] = { |
| CONTENT_SETTING_ALLOW, |
| CONTENT_SETTING_BLOCK, |
| CONTENT_SETTING_ASK |
| }; |
| |
| const content_settings::SettingSource kTestSettingSources[] = { |
| content_settings::SETTING_SOURCE_USER, |
| content_settings::SETTING_SOURCE_USER, |
| content_settings::SETTING_SOURCE_USER, |
| content_settings::SETTING_SOURCE_USER, |
| content_settings::SETTING_SOURCE_USER, |
| content_settings::SETTING_SOURCE_POLICY, |
| content_settings::SETTING_SOURCE_POLICY, |
| content_settings::SETTING_SOURCE_EXTENSION, |
| content_settings::SETTING_SOURCE_EXTENSION |
| }; |
| |
| class WebsiteSettingsBubbleControllerTest : public CocoaTest { |
| public: |
| WebsiteSettingsBubbleControllerTest() { |
| controller_ = nil; |
| } |
| |
| void TearDown() override { |
| [controller_ close]; |
| CocoaTest::TearDown(); |
| } |
| |
| protected: |
| WebsiteSettingsUIBridge* bridge_; // Weak, owned by controller. |
| |
| enum MatchType { |
| TEXT_EQUAL = 0, |
| TEXT_NOT_EQUAL |
| }; |
| |
| // Creates a new website settings bubble, with the given default width. |
| // If |default_width| is 0, the *default* default width will be used. |
| void CreateBubbleWithWidth(CGFloat default_width) { |
| bridge_ = new WebsiteSettingsUIBridge(nullptr); |
| |
| // The controller cleans up after itself when the window closes. |
| controller_ = [WebsiteSettingsBubbleControllerForTesting alloc]; |
| [controller_ setDefaultWindowWidth:default_width]; |
| [controller_ initWithParentWindow:test_window() |
| websiteSettingsUIBridge:bridge_ |
| webContents:nil |
| isInternalPage:NO]; |
| window_ = [controller_ window]; |
| [controller_ showWindow:nil]; |
| } |
| |
| void CreateBubble() { |
| CreateBubbleWithWidth(0.0); |
| } |
| |
| // Return a pointer to the first NSTextField found that either matches, or |
| // doesn't match, the given text. |
| NSTextField* FindTextField(MatchType match_type, NSString* text) { |
| // The window's only immediate child is an invisible view that has a flipped |
| // coordinate origin. It is into this that all views get placed. |
| NSArray* window_subviews = [[window_ contentView] subviews]; |
| EXPECT_EQ(1U, [window_subviews count]); |
| NSArray* subviews = [[window_subviews lastObject] subviews]; |
| |
| // Expect 4 views: the identity, identity status, the segmented control |
| // (which implements the tab strip), and the tab view. |
| EXPECT_EQ(4U, [subviews count]); |
| |
| bool desired_result = match_type == TEXT_EQUAL; |
| |
| for (NSView* view in subviews) { |
| if ([view isKindOfClass:[NSTextField class]]) { |
| NSTextField* text_field = static_cast<NSTextField*>(view); |
| if ([[text_field stringValue] isEqual:text] == desired_result) |
| return text_field; |
| } |
| } |
| return nil; |
| } |
| |
| NSMutableArray* FindAllSubviewsOfClass(NSView* parent_view, Class a_class) { |
| NSMutableArray* views = [NSMutableArray array]; |
| for (NSView* view in [parent_view subviews]) { |
| if ([view isKindOfClass:a_class]) |
| [views addObject:view]; |
| } |
| return views; |
| } |
| |
| // Sets up the dialog with some test permission settings. |
| void SetTestPermissions() { |
| // Create a list of 5 different permissions, corresponding to all the |
| // possible settings: |
| // - [allow, block, ask] by default |
| // - [block, allow] * [by user, by policy, by extension] |
| PermissionInfoList list; |
| WebsiteSettingsUI::PermissionInfo info; |
| for (size_t i = 0; i < arraysize(kTestPermissionTypes); ++i) { |
| info.type = kTestPermissionTypes[i]; |
| info.setting = kTestSettings[i]; |
| if (info.setting == CONTENT_SETTING_DEFAULT) |
| info.default_setting = kTestDefaultSettings[i]; |
| info.source = kTestSettingSources[i]; |
| list.push_back(info); |
| } |
| bridge_->SetPermissionInfo(list); |
| } |
| |
| WebsiteSettingsBubbleControllerForTesting* controller_; // Weak, owns self. |
| NSWindow* window_; // Weak, owned by controller. |
| }; |
| |
| TEST_F(WebsiteSettingsBubbleControllerTest, BasicIdentity) { |
| WebsiteSettingsUI::IdentityInfo info; |
| info.site_identity = std::string("nhl.com"); |
| info.identity_status = WebsiteSettings::SITE_IDENTITY_STATUS_UNKNOWN; |
| |
| CreateBubble(); |
| |
| // Test setting the site identity. |
| bridge_->SetIdentityInfo(const_cast<WebsiteSettingsUI::IdentityInfo&>(info)); |
| NSTextField* identity_field = FindTextField(TEXT_EQUAL, @"nhl.com"); |
| ASSERT_TRUE(identity_field != nil); |
| |
| // Test changing the site identity, and ensure that the UI is updated. |
| info.site_identity = std::string("google.com"); |
| bridge_->SetIdentityInfo(const_cast<WebsiteSettingsUI::IdentityInfo&>(info)); |
| EXPECT_EQ(identity_field, FindTextField(TEXT_EQUAL, @"google.com")); |
| |
| // Find the identity status field. |
| NSTextField* identity_status_field = |
| FindTextField(TEXT_NOT_EQUAL, @"google.com"); |
| ASSERT_NE(identity_field, identity_status_field); |
| |
| // Ensure the text of the identity status field changes when the status does. |
| NSString* status = [identity_status_field stringValue]; |
| info.identity_status = WebsiteSettings::SITE_IDENTITY_STATUS_CERT; |
| bridge_->SetIdentityInfo(const_cast<WebsiteSettingsUI::IdentityInfo&>(info)); |
| EXPECT_NSNE(status, [identity_status_field stringValue]); |
| } |
| |
| TEST_F(WebsiteSettingsBubbleControllerTest, SetIdentityInfo) { |
| WebsiteSettingsUI::IdentityInfo info; |
| info.site_identity = std::string("nhl.com"); |
| info.identity_status = WebsiteSettings::SITE_IDENTITY_STATUS_UNKNOWN; |
| info.identity_status_description = std::string("Identity1"); |
| info.connection_status = WebsiteSettings::SITE_CONNECTION_STATUS_UNKNOWN; |
| info.connection_status_description = std::string("Connection1"); |
| info.cert_id = 0; |
| |
| CreateBubble(); |
| |
| // Set the identity, and test that the description fields on the Connection |
| // tab are set properly. |
| bridge_->SetIdentityInfo(const_cast<WebsiteSettingsUI::IdentityInfo&>(info)); |
| EXPECT_NSEQ(@"Identity1", |
| [[controller_ identityStatusDescriptionField] stringValue]); |
| EXPECT_NSEQ(@"Connection1", |
| [[controller_ connectionStatusDescriptionField] stringValue]); |
| |
| // Check the contents of the images, and make sure they change after the |
| // status changes. |
| |
| NSImage* identity_icon = [[controller_ identityStatusIcon] image]; |
| NSImage* connection_icon = [[controller_ connectionStatusIcon] image]; |
| // Icons should be the same when they are both unknown. |
| EXPECT_EQ(identity_icon, connection_icon); |
| |
| // Ensure that the link button for certificate info is not there -- the |
| // help link button should be the first one found. |
| NSMutableArray* buttons = FindAllSubviewsOfClass( |
| [controller_ connectionTabContentView], [NSButton class]); |
| ASSERT_EQ(1U, [buttons count]); |
| EXPECT_NSEQ([controller_ helpButton], [buttons objectAtIndex:0]); |
| |
| // Check that it has a target and action linked up. |
| NSButton* link_button = static_cast<NSButton*>([buttons objectAtIndex:0]); |
| EXPECT_NSEQ(controller_, [link_button target]); |
| EXPECT_TRUE([link_button action] == @selector(showHelpPage:)); |
| |
| info.identity_status = WebsiteSettings::SITE_IDENTITY_STATUS_CERT; |
| info.connection_status = WebsiteSettings::SITE_CONNECTION_STATUS_ENCRYPTED; |
| info.cert_id = 1; |
| bridge_->SetIdentityInfo(const_cast<WebsiteSettingsUI::IdentityInfo&>(info)); |
| |
| EXPECT_NE(identity_icon, [[controller_ identityStatusIcon] image]); |
| EXPECT_NE(connection_icon, [[controller_ connectionStatusIcon] image]); |
| |
| // The certificate info button should be there now. |
| buttons = FindAllSubviewsOfClass( |
| [controller_ connectionTabContentView], [NSButton class]); |
| ASSERT_EQ(2U, [buttons count]); |
| EXPECT_NSNE([controller_ helpButton], [buttons objectAtIndex:1]); |
| |
| // Check that it has a target and action linked up. |
| link_button = static_cast<NSButton*>([buttons objectAtIndex:1]); |
| EXPECT_NSEQ(controller_, [link_button target]); |
| EXPECT_TRUE([link_button action] == @selector(showCertificateInfo:)); |
| } |
| |
| TEST_F(WebsiteSettingsBubbleControllerTest, SetPermissionInfo) { |
| CreateBubble(); |
| SetTestPermissions(); |
| |
| // There should be three subviews per permission (an icon, a label and a |
| // select box), plus a text label for the Permission section. |
| NSArray* subviews = [[controller_ permissionsView] subviews]; |
| EXPECT_EQ(arraysize(kTestPermissionTypes) * 3 + 1, [subviews count]); |
| |
| // Ensure that there is a distinct label for each permission. |
| NSMutableSet* labels = [NSMutableSet set]; |
| for (NSView* view in subviews) { |
| if ([view isKindOfClass:[NSTextField class]]) |
| [labels addObject:[static_cast<NSTextField*>(view) stringValue]]; |
| } |
| // The section header ("Permissions") will also be found, hence the +1. |
| EXPECT_EQ(arraysize(kTestPermissionTypes) + 1, [labels count]); |
| |
| // Ensure that the button labels are distinct, and look for the correct |
| // number of disabled buttons. |
| int disabled_count = 0; |
| [labels removeAllObjects]; |
| for (NSView* view in subviews) { |
| if ([view isKindOfClass:[NSPopUpButton class]]) { |
| NSPopUpButton* button = static_cast<NSPopUpButton*>(view); |
| [labels addObject:[[button selectedCell] title]]; |
| |
| if (![button isEnabled]) |
| ++disabled_count; |
| } |
| } |
| EXPECT_EQ(arraysize(kTestPermissionTypes), [labels count]); |
| |
| // 4 of the buttons should be disabled -- the ones that have a setting source |
| // of SETTING_SOURCE_POLICY or SETTING_SOURCE_EXTENSION. |
| EXPECT_EQ(4, disabled_count); |
| } |
| |
| TEST_F(WebsiteSettingsBubbleControllerTest, SetSelectedTab) { |
| CreateBubble(); |
| NSSegmentedControl* segmentedControl = [controller_ segmentedControl]; |
| NSTabView* tabView = [controller_ tabView]; |
| |
| // Test whether SetSelectedTab properly changes both the segmented control |
| // (which implements the tabs) as well as the visible tab contents. |
| // NOTE: This implicitly (and deliberately) tests that the tabs appear in a |
| // specific order: Permissions, Connection. |
| EXPECT_EQ(0, [segmentedControl selectedSegment]); |
| EXPECT_EQ(0, [tabView indexOfTabViewItem:[tabView selectedTabViewItem]]); |
| bridge_->SetSelectedTab(WebsiteSettingsUI::TAB_ID_CONNECTION); |
| EXPECT_EQ(1, [segmentedControl selectedSegment]); |
| EXPECT_EQ(1, [tabView indexOfTabViewItem:[tabView selectedTabViewItem]]); |
| } |
| |
| TEST_F(WebsiteSettingsBubbleControllerTest, WindowWidth) { |
| // Try creating a window that is obviously too small. |
| CreateBubbleWithWidth(30.0); |
| SetTestPermissions(); |
| |
| CGFloat window_width = NSWidth([[controller_ window] frame]); |
| |
| // Check the window was made bigger to fit the content. |
| EXPECT_LT(30.0, window_width); |
| |
| // Check that the window is wider than the right edge of all the permission |
| // popup buttons. |
| for (NSView* view in [[controller_ permissionsView] subviews]) { |
| if ([view isKindOfClass:[NSPopUpButton class]]) { |
| NSPopUpButton* button = static_cast<NSPopUpButton*>(view); |
| EXPECT_LT(NSMaxX([button frame]), window_width); |
| } |
| } |
| } |
| |
| } // namespace |