Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / website_settings / website_settings_bubble_controller_unittest.mm
blob627c937a02477832d15afb751cfa9f4a0e0d5f38
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/i18n/rtl.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/ui/cocoa/cocoa_test_helper.h"
10 #include "testing/gtest_mac.h"
12 @interface WebsiteSettingsBubbleController (ExposedForTesting)
13 - (NSSegmentedControl*)segmentedControl;
14 - (NSTabView*)tabView;
15 - (NSView*)permissionsView;
16 - (NSView*)connectionTabContentView;
17 - (NSImageView*)identityStatusIcon;
18 - (NSTextField*)identityStatusDescriptionField;
19 - (NSImageView*)connectionStatusIcon;
20 - (NSTextField*)connectionStatusDescriptionField;
21 - (NSButton*)helpButton;
22 @end
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 - (NSButton*)helpButton {
56   return helpButton_;
58 @end
60 @interface WebsiteSettingsBubbleControllerForTesting
61     : WebsiteSettingsBubbleController {
62  @private
63   CGFloat defaultWindowWidth_;
65 @end
67 @implementation WebsiteSettingsBubbleControllerForTesting
68 - (void)setDefaultWindowWidth:(CGFloat)width {
69   defaultWindowWidth_ = width;
71 - (CGFloat)defaultWindowWidth {
72   // If |defaultWindowWidth_| is 0, use the superclass implementation.
73   return defaultWindowWidth_ ?
74       defaultWindowWidth_ : [super defaultWindowWidth];
76 @end
78 namespace {
80 // Indices of the menu items in the permission menu.
81 enum PermissionMenuIndices {
82   kMenuIndexContentSettingAllow = 0,
83   kMenuIndexContentSettingBlock,
84   kMenuIndexContentSettingDefault
87 const ContentSettingsType kTestPermissionTypes[] = {
88   // NOTE: FULLSCREEN does not support "Always block", so it must appear as
89   // one of the first three permissions.
90   CONTENT_SETTINGS_TYPE_FULLSCREEN,
91   CONTENT_SETTINGS_TYPE_IMAGES,
92   CONTENT_SETTINGS_TYPE_JAVASCRIPT,
93   CONTENT_SETTINGS_TYPE_PLUGINS,
94   CONTENT_SETTINGS_TYPE_POPUPS,
95   CONTENT_SETTINGS_TYPE_GEOLOCATION,
96   CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
97   CONTENT_SETTINGS_TYPE_MOUSELOCK,
98   CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
101 const ContentSetting kTestSettings[] = {
102   CONTENT_SETTING_DEFAULT,
103   CONTENT_SETTING_DEFAULT,
104   CONTENT_SETTING_DEFAULT,
105   CONTENT_SETTING_ALLOW,
106   CONTENT_SETTING_BLOCK,
107   CONTENT_SETTING_ALLOW,
108   CONTENT_SETTING_BLOCK,
109   CONTENT_SETTING_ALLOW,
110   CONTENT_SETTING_BLOCK
113 const ContentSetting kTestDefaultSettings[] = {
114   CONTENT_SETTING_ALLOW,
115   CONTENT_SETTING_BLOCK,
116   CONTENT_SETTING_ASK
119 const content_settings::SettingSource kTestSettingSources[] = {
120   content_settings::SETTING_SOURCE_USER,
121   content_settings::SETTING_SOURCE_USER,
122   content_settings::SETTING_SOURCE_USER,
123   content_settings::SETTING_SOURCE_USER,
124   content_settings::SETTING_SOURCE_USER,
125   content_settings::SETTING_SOURCE_POLICY,
126   content_settings::SETTING_SOURCE_POLICY,
127   content_settings::SETTING_SOURCE_EXTENSION,
128   content_settings::SETTING_SOURCE_EXTENSION
131 class WebsiteSettingsBubbleControllerTest : public CocoaTest {
132  public:
133   WebsiteSettingsBubbleControllerTest() {
134     controller_ = nil;
135   }
137   void TearDown() override {
138     [controller_ close];
139     CocoaTest::TearDown();
140   }
142  protected:
143   WebsiteSettingsUIBridge* bridge_;  // Weak, owned by controller.
145   enum MatchType {
146     TEXT_EQUAL = 0,
147     TEXT_NOT_EQUAL
148   };
150   // Creates a new website settings bubble, with the given default width.
151   // If |default_width| is 0, the *default* default width will be used.
152   void CreateBubbleWithWidth(CGFloat default_width) {
153     bridge_ = new WebsiteSettingsUIBridge(nullptr);
155     // The controller cleans up after itself when the window closes.
156     controller_ = [WebsiteSettingsBubbleControllerForTesting alloc];
157     [controller_ setDefaultWindowWidth:default_width];
158     [controller_ initWithParentWindow:test_window()
159                  websiteSettingsUIBridge:bridge_
160                  webContents:nil
161                  isInternalPage:NO];
162     window_ = [controller_ window];
163     [controller_ showWindow:nil];
164   }
166   void CreateBubble() {
167     CreateBubbleWithWidth(0.0);
168   }
170   // Return a pointer to the first NSTextField found that either matches, or
171   // doesn't match, the given text.
172   NSTextField* FindTextField(MatchType match_type, NSString* text) {
173     // The window's only immediate child is an invisible view that has a flipped
174     // coordinate origin. It is into this that all views get placed.
175     NSArray* window_subviews = [[window_ contentView] subviews];
176     EXPECT_EQ(1U, [window_subviews count]);
177     NSArray* subviews = [[window_subviews lastObject] subviews];
179     // Expect 4 views: the identity, identity status, the segmented control
180     // (which implements the tab strip), and the tab view.
181     EXPECT_EQ(4U, [subviews count]);
183     bool desired_result = match_type == TEXT_EQUAL;
185     for (NSView* view in subviews) {
186       if ([view isKindOfClass:[NSTextField class]]) {
187         NSTextField* text_field = static_cast<NSTextField*>(view);
188         if ([[text_field stringValue] isEqual:text] == desired_result)
189           return text_field;
190       }
191     }
192     return nil;
193   }
195   NSMutableArray* FindAllSubviewsOfClass(NSView* parent_view, Class a_class) {
196     NSMutableArray* views = [NSMutableArray array];
197     for (NSView* view in [parent_view subviews]) {
198       if ([view isKindOfClass:a_class])
199         [views addObject:view];
200     }
201     return views;
202   }
204   // Sets up the dialog with some test permission settings.
205   void SetTestPermissions() {
206     // Create a list of 5 different permissions, corresponding to all the
207     // possible settings:
208     // - [allow, block, ask] by default
209     // - [block, allow] * [by user, by policy, by extension]
210     PermissionInfoList list;
211     WebsiteSettingsUI::PermissionInfo info;
212     for (size_t i = 0; i < arraysize(kTestPermissionTypes); ++i) {
213       info.type = kTestPermissionTypes[i];
214       info.setting = kTestSettings[i];
215       if (info.setting == CONTENT_SETTING_DEFAULT)
216         info.default_setting = kTestDefaultSettings[i];
217       info.source = kTestSettingSources[i];
218       list.push_back(info);
219     }
220     bridge_->SetPermissionInfo(list);
221   }
223   WebsiteSettingsBubbleControllerForTesting* controller_;  // Weak, owns self.
224   NSWindow* window_;  // Weak, owned by controller.
227 TEST_F(WebsiteSettingsBubbleControllerTest, BasicIdentity) {
228   WebsiteSettingsUI::IdentityInfo info;
229   info.site_identity = std::string("nhl.com");
230   info.identity_status = WebsiteSettings::SITE_IDENTITY_STATUS_UNKNOWN;
232   CreateBubble();
234   // Test setting the site identity.
235   bridge_->SetIdentityInfo(const_cast<WebsiteSettingsUI::IdentityInfo&>(info));
236   NSTextField* identity_field = FindTextField(TEXT_EQUAL, @"nhl.com");
237   ASSERT_TRUE(identity_field != nil);
239   // Test changing the site identity, and ensure that the UI is updated.
240   info.site_identity = std::string("google.com");
241   bridge_->SetIdentityInfo(const_cast<WebsiteSettingsUI::IdentityInfo&>(info));
242   EXPECT_EQ(identity_field, FindTextField(TEXT_EQUAL, @"google.com"));
244   // Find the identity status field.
245   NSTextField* identity_status_field =
246       FindTextField(TEXT_NOT_EQUAL, @"google.com");
247   ASSERT_NE(identity_field, identity_status_field);
249   // Ensure the text of the identity status field changes when the status does.
250   NSString* status = [identity_status_field stringValue];
251   info.identity_status = WebsiteSettings::SITE_IDENTITY_STATUS_CERT;
252   bridge_->SetIdentityInfo(const_cast<WebsiteSettingsUI::IdentityInfo&>(info));
253   EXPECT_NSNE(status, [identity_status_field stringValue]);
256 TEST_F(WebsiteSettingsBubbleControllerTest, SetIdentityInfo) {
257   WebsiteSettingsUI::IdentityInfo info;
258   info.site_identity = std::string("nhl.com");
259   info.identity_status = WebsiteSettings::SITE_IDENTITY_STATUS_UNKNOWN;
260   info.identity_status_description = std::string("Identity1");
261   info.connection_status = WebsiteSettings::SITE_CONNECTION_STATUS_UNKNOWN;
262   info.connection_status_description = std::string("Connection1");
263   info.cert_id = 0;
265   CreateBubble();
267   // Set the identity, and test that the description fields on the Connection
268   // tab are set properly.
269   bridge_->SetIdentityInfo(const_cast<WebsiteSettingsUI::IdentityInfo&>(info));
270   EXPECT_NSEQ(@"Identity1",
271               [[controller_ identityStatusDescriptionField] stringValue]);
272   EXPECT_NSEQ(@"Connection1",
273               [[controller_ connectionStatusDescriptionField] stringValue]);
275   // Check the contents of the images, and make sure they change after the
276   // status changes.
278   NSImage* identity_icon = [[controller_ identityStatusIcon] image];
279   NSImage* connection_icon = [[controller_ connectionStatusIcon] image];
280   // Icons should be the same when they are both unknown.
281   EXPECT_EQ(identity_icon, connection_icon);
283   // Ensure that the link button for certificate info is not there -- the
284   // help link button should be the first one found.
285   NSMutableArray* buttons = FindAllSubviewsOfClass(
286       [controller_ connectionTabContentView], [NSButton class]);
287   ASSERT_EQ(1U, [buttons count]);
288   EXPECT_NSEQ([controller_ helpButton], [buttons objectAtIndex:0]);
290   // Check that it has a target and action linked up.
291   NSButton* link_button = static_cast<NSButton*>([buttons objectAtIndex:0]);
292   EXPECT_NSEQ(controller_, [link_button target]);
293   EXPECT_TRUE([link_button action] == @selector(showHelpPage:));
295   info.identity_status = WebsiteSettings::SITE_IDENTITY_STATUS_CERT;
296   info.connection_status = WebsiteSettings::SITE_CONNECTION_STATUS_ENCRYPTED;
297   info.cert_id = 1;
298   bridge_->SetIdentityInfo(const_cast<WebsiteSettingsUI::IdentityInfo&>(info));
300   EXPECT_NE(identity_icon, [[controller_ identityStatusIcon] image]);
301   EXPECT_NE(connection_icon, [[controller_ connectionStatusIcon] image]);
303   // The certificate info button should be there now.
304   buttons = FindAllSubviewsOfClass(
305       [controller_ connectionTabContentView], [NSButton class]);
306   ASSERT_EQ(2U, [buttons count]);
307   EXPECT_NSNE([controller_ helpButton], [buttons objectAtIndex:1]);
309   // Check that it has a target and action linked up.
310   link_button = static_cast<NSButton*>([buttons objectAtIndex:1]);
311   EXPECT_NSEQ(controller_, [link_button target]);
312   EXPECT_TRUE([link_button action] == @selector(showCertificateInfo:));
315 TEST_F(WebsiteSettingsBubbleControllerTest, SetPermissionInfo) {
316   CreateBubble();
317   SetTestPermissions();
319   // There should be three subviews per permission (an icon, a label and a
320   // select box), plus a text label for the Permission section.
321   NSArray* subviews = [[controller_ permissionsView] subviews];
322   EXPECT_EQ(arraysize(kTestPermissionTypes) * 3 + 1, [subviews count]);
324   // Ensure that there is a distinct label for each permission.
325   NSMutableSet* labels = [NSMutableSet set];
326   for (NSView* view in subviews) {
327     if ([view isKindOfClass:[NSTextField class]])
328       [labels addObject:[static_cast<NSTextField*>(view) stringValue]];
329   }
330   // The section header ("Permissions") will also be found, hence the +1.
331   EXPECT_EQ(arraysize(kTestPermissionTypes) + 1, [labels count]);
333   // Ensure that the button labels are distinct, and look for the correct
334   // number of disabled buttons.
335   int disabled_count = 0;
336   [labels removeAllObjects];
337   for (NSView* view in subviews) {
338     if ([view isKindOfClass:[NSPopUpButton class]]) {
339       NSPopUpButton* button = static_cast<NSPopUpButton*>(view);
340       [labels addObject:[[button selectedCell] title]];
342       if (![button isEnabled])
343         ++disabled_count;
344     }
345   }
346   EXPECT_EQ(arraysize(kTestPermissionTypes), [labels count]);
348   // 4 of the buttons should be disabled -- the ones that have a setting source
349   // of SETTING_SOURCE_POLICY or SETTING_SOURCE_EXTENSION.
350   EXPECT_EQ(4, disabled_count);
353 TEST_F(WebsiteSettingsBubbleControllerTest, SetSelectedTab) {
354   CreateBubble();
355   NSSegmentedControl* segmentedControl = [controller_ segmentedControl];
356   NSTabView* tabView = [controller_ tabView];
358   // Test whether SetSelectedTab properly changes both the segmented control
359   // (which implements the tabs) as well as the visible tab contents.
360   // NOTE: This implicitly (and deliberately) tests that the tabs appear in a
361   // specific order: Permissions, Connection.
362   EXPECT_EQ(0, [segmentedControl selectedSegment]);
363   EXPECT_EQ(0, [tabView indexOfTabViewItem:[tabView selectedTabViewItem]]);
364   bridge_->SetSelectedTab(WebsiteSettingsUI::TAB_ID_CONNECTION);
365   EXPECT_EQ(1, [segmentedControl selectedSegment]);
366   EXPECT_EQ(1, [tabView indexOfTabViewItem:[tabView selectedTabViewItem]]);
369 TEST_F(WebsiteSettingsBubbleControllerTest, WindowWidth) {
370   const CGFloat kBigEnoughBubbleWidth = 310;
371   // Creating a window that should fit everything.
372   CreateBubbleWithWidth(kBigEnoughBubbleWidth);
373   SetTestPermissions();
375   CGFloat window_width = NSWidth([[controller_ window] frame]);
377   // Check the window was made bigger to fit the content.
378   EXPECT_EQ(kBigEnoughBubbleWidth, window_width);
380   // Check that the window is wider than the right edge of all the permission
381   // popup buttons (LTR locales) or wider than the left edge (RTL locales).
382   bool is_rtl = base::i18n::IsRTL();
383   for (NSView* view in [[controller_ permissionsView] subviews]) {
384     if (is_rtl) {
385       if ([view isKindOfClass:[NSPopUpButton class]]) {
386         NSPopUpButton* button = static_cast<NSPopUpButton*>(view);
387         EXPECT_GT(NSMinX([button frame]), 0);
388       }
389       if ([view isKindOfClass:[NSImageView class]]) {
390         NSImageView* icon = static_cast<NSImageView*>(view);
391         EXPECT_LT(NSMaxX([icon frame]), window_width);
392       }
393     } else {
394       if ([view isKindOfClass:[NSImageView class]]) {
395         NSImageView* icon = static_cast<NSImageView*>(view);
396         EXPECT_GT(NSMinX([icon frame]), 0);
397       }
398       if ([view isKindOfClass:[NSPopUpButton class]]) {
399         NSPopUpButton* button = static_cast<NSPopUpButton*>(view);
400         EXPECT_LT(NSMaxX([button frame]), window_width);
401       }
402     }
403   }
406 }  // namespace