Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / website_settings / permission_bubble_controller_unittest.mm
blob914f0980e9c2d7a572db542a4756ad5624a00045
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/permission_bubble_controller.h"
7 #include <Carbon/Carbon.h>
9 #include "base/mac/foundation_util.h"
10 #import "base/mac/scoped_objc_class_swizzler.h"
11 #include "base/mac/sdk_forward_declarations.h"
12 #include "base/stl_util.h"
13 #include "base/strings/sys_string_conversions.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/browser/ui/browser_window.h"
17 #include "chrome/browser/ui/cocoa/browser_window_controller.h"
18 #import "chrome/browser/ui/cocoa/cocoa_profile_test.h"
19 #include "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
20 #include "chrome/browser/ui/cocoa/run_loop_testing.h"
21 #import "chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa.h"
22 #import "chrome/browser/ui/cocoa/website_settings/split_block_button.h"
23 #include "chrome/browser/ui/website_settings/mock_permission_bubble_request.h"
24 #include "chrome/grit/generated_resources.h"
25 #include "testing/gmock/include/gmock/gmock.h"
26 #import "testing/gtest_mac.h"
27 #include "ui/base/l10n/l10n_util.h"
28 #include "ui/base/l10n/l10n_util_mac.h"
29 #import "ui/events/test/cocoa_test_event_utils.h"
31 @class ConstrainedWindowButton;
33 @interface PermissionBubbleController (ExposedForTesting)
34 - (void)ok:(id)sender;
35 - (void)onAllow:(id)sender;
36 - (void)onBlock:(id)sender;
37 - (void)onCustomize:(id)sender;
38 - (void)onCheckboxChanged:(id)sender;
39 @end
41 @interface SplitBlockButton (ExposedForTesting)
42 - (NSMenu*)menu;
43 @end
45 @interface MockBubbleYesLocationBar : NSObject
46 @end
48 @implementation MockBubbleYesLocationBar
49 - (bool)hasLocationBar { return true; }
50 @end
52 @interface MockBubbleNoLocationBar : NSObject
53 @end
55 @implementation MockBubbleNoLocationBar
56 - (bool)hasLocationBar { return false; }
57 @end
59 namespace {
60 const char* const kPermissionA = "Permission A";
61 const char* const kPermissionB = "Permission B";
62 const char* const kPermissionC = "Permission C";
65 class PermissionBubbleControllerTest : public CocoaProfileTest,
66                                        public PermissionBubbleView::Delegate {
67  public:
69   MOCK_METHOD2(ToggleAccept, void(int, bool));
70   MOCK_METHOD0(SetCustomizationMode, void());
71   MOCK_METHOD0(Accept, void());
72   MOCK_METHOD0(Deny, void());
73   MOCK_METHOD0(Closing, void());
74   MOCK_METHOD1(SetView, void(PermissionBubbleView*));
76   void SetUp() override {
77     CocoaProfileTest::SetUp();
78     bridge_.reset(new PermissionBubbleCocoa(browser()));
79     AddRequest(kPermissionA);
80     controller_ =
81         [[PermissionBubbleController alloc] initWithBrowser:browser()
82                                                      bridge:bridge_.get()];
83   }
85   void TearDown() override {
86     [controller_ close];
87     chrome::testing::NSRunLoopRunAllPending();
88     STLDeleteElements(&requests_);
89     CocoaProfileTest::TearDown();
90   }
92   void AddRequest(const std::string& title) {
93     MockPermissionBubbleRequest* request = new MockPermissionBubbleRequest(
94         title,
95         l10n_util::GetStringUTF8(IDS_PERMISSION_ALLOW),
96         l10n_util::GetStringUTF8(IDS_PERMISSION_DENY));
97     requests_.push_back(request);
98   }
100   NSButton* FindButtonWithTitle(const std::string& title) {
101     return FindButtonWithTitle(base::SysUTF8ToNSString(title),
102                                [ConstrainedWindowButton class]);
103   }
105   NSButton* FindButtonWithTitle(int title_id) {
106     return FindButtonWithTitle(l10n_util::GetNSString(title_id),
107                                [ConstrainedWindowButton class]);
108   }
110   NSButton* FindMenuButtonWithTitle(int title_id) {
111     return FindButtonWithTitle(l10n_util::GetNSString(title_id),
112                                [NSPopUpButton class]);
113   }
115   // IDS_PERMISSION_ALLOW and IDS_PERMISSION_DENY are used for two distinct
116   // UI elements, both of which derive from NSButton.  So check the expected
117   // class, not just NSButton, as well as the title.
118   NSButton* FindButtonWithTitle(NSString* title, Class button_class) {
119     for (NSButton* view in [[controller_ bubble] subviews]) {
120       if ([view isKindOfClass:button_class] &&
121           [title isEqualToString:[view title]]) {
122         return view;
123       }
124     }
125     return nil;
126   }
128   NSTextField* FindTextFieldWithString(const std::string& text) {
129     NSView* parent = base::mac::ObjCCastStrict<NSView>([controller_ bubble]);
130     return FindTextFieldWithString(parent, base::SysUTF8ToNSString(text));
131   }
133   NSTextField* FindTextFieldWithString(NSView* view, NSString* text) {
134     NSTextField* textField = nil;
135     for (NSView* child in [view subviews]) {
136       textField = base::mac::ObjCCast<NSTextField>(child);
137       if (![[textField stringValue] hasSuffix:text]) {
138         textField = FindTextFieldWithString(child, text);
139         if (textField)
140           break;
141       }
142     }
143     return textField;
144   }
146   void ChangePermissionMenuSelection(NSButton* menu_button, int next_title_id) {
147     NSMenu* menu = [base::mac::ObjCCastStrict<NSPopUpButton>(menu_button) menu];
148     NSString* next_title = l10n_util::GetNSString(next_title_id);
149     EXPECT_EQ([[menu itemWithTitle:[menu_button title]] state], NSOnState);
150     NSMenuItem* next_item = [menu itemWithTitle:next_title];
151     EXPECT_EQ([next_item state], NSOffState);
152     [menu performActionForItemAtIndex:[menu indexOfItem:next_item]];
153   }
155  protected:
156   PermissionBubbleController* controller_;  // Weak;  it deletes itself.
157   scoped_ptr<PermissionBubbleCocoa> bridge_;
158   std::vector<PermissionBubbleRequest*> requests_;
159   std::vector<bool> accept_states_;
162 TEST_F(PermissionBubbleControllerTest, ShowAndClose) {
163   EXPECT_FALSE([[controller_ window] isVisible]);
164   [controller_ showWindow:nil];
165   EXPECT_TRUE([[controller_ window] isVisible]);
168 TEST_F(PermissionBubbleControllerTest, ShowSinglePermission) {
169   [controller_ showWithDelegate:this
170                     forRequests:requests_
171                    acceptStates:accept_states_];
173   EXPECT_TRUE(FindTextFieldWithString(kPermissionA));
174   EXPECT_TRUE(FindButtonWithTitle(IDS_PERMISSION_ALLOW));
175   EXPECT_TRUE(FindButtonWithTitle(IDS_PERMISSION_DENY));
176   EXPECT_FALSE(FindButtonWithTitle(IDS_OK));
179 TEST_F(PermissionBubbleControllerTest, ShowMultiplePermissions) {
180   AddRequest(kPermissionB);
181   AddRequest(kPermissionC);
183   accept_states_.push_back(true);  // A
184   accept_states_.push_back(true);  // B
185   accept_states_.push_back(true);  // C
187   [controller_ showWithDelegate:this
188                     forRequests:requests_
189                    acceptStates:accept_states_];
191   EXPECT_TRUE(FindTextFieldWithString(kPermissionA));
192   EXPECT_TRUE(FindTextFieldWithString(kPermissionB));
193   EXPECT_TRUE(FindTextFieldWithString(kPermissionC));
195   EXPECT_FALSE(FindButtonWithTitle(IDS_PERMISSION_ALLOW));
196   EXPECT_FALSE(FindButtonWithTitle(IDS_PERMISSION_DENY));
197   EXPECT_TRUE(FindButtonWithTitle(IDS_OK));
200 TEST_F(PermissionBubbleControllerTest, ShowMultiplePermissionsAllow) {
201   AddRequest(kPermissionB);
203   accept_states_.push_back(true);  // A
204   accept_states_.push_back(true);  // B
206   [controller_ showWithDelegate:this
207                     forRequests:requests_
208                    acceptStates:accept_states_];
210   // Test that all menus have 'Allow' visible.
211   EXPECT_TRUE(FindMenuButtonWithTitle(IDS_PERMISSION_ALLOW));
212   EXPECT_FALSE(FindMenuButtonWithTitle(IDS_PERMISSION_DENY));
214   EXPECT_TRUE(FindButtonWithTitle(IDS_OK));
215   EXPECT_FALSE(FindButtonWithTitle(IDS_PERMISSION_ALLOW));
216   EXPECT_FALSE(FindButtonWithTitle(IDS_PERMISSION_DENY));
219 TEST_F(PermissionBubbleControllerTest, ShowMultiplePermissionsBlock) {
220   AddRequest(kPermissionB);
222   accept_states_.push_back(false);  // A
223   accept_states_.push_back(false);  // B
225   [controller_ showWithDelegate:this
226                     forRequests:requests_
227                    acceptStates:accept_states_];
229   // Test that all menus have 'Block' visible.
230   EXPECT_TRUE(FindMenuButtonWithTitle(IDS_PERMISSION_DENY));
231   EXPECT_FALSE(FindMenuButtonWithTitle(IDS_PERMISSION_ALLOW));
233   EXPECT_TRUE(FindButtonWithTitle(IDS_OK));
234   EXPECT_FALSE(FindButtonWithTitle(IDS_PERMISSION_ALLOW));
235   EXPECT_FALSE(FindButtonWithTitle(IDS_PERMISSION_DENY));
238 TEST_F(PermissionBubbleControllerTest, ShowMultiplePermissionsMixed) {
239   AddRequest(kPermissionB);
240   AddRequest(kPermissionC);
242   accept_states_.push_back(false);  // A
243   accept_states_.push_back(false);  // B
244   accept_states_.push_back(true);   // C
246   [controller_ showWithDelegate:this
247                     forRequests:requests_
248                    acceptStates:accept_states_];
250   // Test that both 'allow' and 'deny' are visible.
251   EXPECT_TRUE(FindMenuButtonWithTitle(IDS_PERMISSION_DENY));
252   EXPECT_TRUE(FindMenuButtonWithTitle(IDS_PERMISSION_ALLOW));
254   EXPECT_TRUE(FindButtonWithTitle(IDS_OK));
255   EXPECT_FALSE(FindButtonWithTitle(IDS_PERMISSION_ALLOW));
256   EXPECT_FALSE(FindButtonWithTitle(IDS_PERMISSION_DENY));
259 TEST_F(PermissionBubbleControllerTest, OK) {
260   AddRequest(kPermissionB);
262   accept_states_.push_back(true);  // A
263   accept_states_.push_back(true);  // B
265   [controller_ showWithDelegate:this
266                     forRequests:requests_
267                    acceptStates:accept_states_];
269   EXPECT_CALL(*this, Accept()).Times(1);
270   [FindButtonWithTitle(IDS_OK) performClick:nil];
273 TEST_F(PermissionBubbleControllerTest, Allow) {
274   [controller_ showWithDelegate:this
275                     forRequests:requests_
276                    acceptStates:accept_states_];
278   EXPECT_CALL(*this, Accept()).Times(1);
279   [FindButtonWithTitle(IDS_PERMISSION_ALLOW) performClick:nil];
282 TEST_F(PermissionBubbleControllerTest, Deny) {
283   [controller_ showWithDelegate:this
284                     forRequests:requests_
285                    acceptStates:accept_states_];
287   EXPECT_CALL(*this, Deny()).Times(1);
288   [FindButtonWithTitle(IDS_PERMISSION_DENY) performClick:nil];
291 TEST_F(PermissionBubbleControllerTest, ChangePermissionSelection) {
292   AddRequest(kPermissionB);
294   accept_states_.push_back(true);   // A
295   accept_states_.push_back(false);  // B
297   [controller_ showWithDelegate:this
298                     forRequests:requests_
299                    acceptStates:accept_states_];
301   EXPECT_CALL(*this, ToggleAccept(0, false)).Times(1);
302   EXPECT_CALL(*this, ToggleAccept(1, true)).Times(1);
303   NSButton* menu_a = FindMenuButtonWithTitle(IDS_PERMISSION_ALLOW);
304   NSButton* menu_b = FindMenuButtonWithTitle(IDS_PERMISSION_DENY);
305   ChangePermissionMenuSelection(menu_a, IDS_PERMISSION_DENY);
306   ChangePermissionMenuSelection(menu_b, IDS_PERMISSION_ALLOW);
309 TEST_F(PermissionBubbleControllerTest, EscapeCloses) {
310   [controller_ showWithDelegate:this
311                     forRequests:requests_
312                    acceptStates:accept_states_];
314   EXPECT_TRUE([[controller_ window] isVisible]);
315   [[controller_ window]
316       performKeyEquivalent:cocoa_test_event_utils::KeyEventWithKeyCode(
317                                kVK_Escape, '\e', NSKeyDown, 0)];
318   EXPECT_FALSE([[controller_ window] isVisible]);
321 TEST_F(PermissionBubbleControllerTest, EnterFullscreen) {
322   [controller_ showWithDelegate:this
323                     forRequests:requests_
324                    acceptStates:accept_states_];
326   EXPECT_TRUE([[controller_ window] isVisible]);
328   // Post the "enter fullscreen" notification.
329   NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
330   [center postNotificationName:NSWindowWillEnterFullScreenNotification
331                         object:test_window()];
333   EXPECT_TRUE([[controller_ window] isVisible]);
336 TEST_F(PermissionBubbleControllerTest, ExitFullscreen) {
337   [controller_ showWithDelegate:this
338                     forRequests:requests_
339                    acceptStates:accept_states_];
341   EXPECT_TRUE([[controller_ window] isVisible]);
343   // Post the "enter fullscreen" notification.
344   NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
345   [center postNotificationName:NSWindowWillExitFullScreenNotification
346                         object:test_window()];
348   EXPECT_TRUE([[controller_ window] isVisible]);
351 TEST_F(PermissionBubbleControllerTest, AnchorPositionWithLocationBar) {
352   base::mac::ScopedObjCClassSwizzler locationSwizzle(
353       [PermissionBubbleController class],
354       [MockBubbleYesLocationBar class],
355       @selector(hasLocationBar));
357   NSPoint anchor = [controller_ getExpectedAnchorPoint];
359   // Expected anchor location will be the same as the page info bubble.
360   NSWindow* window = browser()->window()->GetNativeWindow();
361   BrowserWindowController* controller =
362       [BrowserWindowController browserWindowControllerForWindow:window];
363   LocationBarViewMac* location_bar_bridge = [controller locationBarBridge];
364   NSPoint expected = location_bar_bridge->GetPageInfoBubblePoint();
365   expected = [window convertBaseToScreen:expected];
366   EXPECT_NSEQ(expected, anchor);
369 TEST_F(PermissionBubbleControllerTest, AnchorPositionWithoutLocationBar) {
370   base::mac::ScopedObjCClassSwizzler locationSwizzle(
371       [PermissionBubbleController class],
372       [MockBubbleNoLocationBar class],
373       @selector(hasLocationBar));
375   NSPoint anchor = [controller_ getExpectedAnchorPoint];
377   // Expected anchor location will be top center when there's no location bar.
378   NSWindow* window = browser()->window()->GetNativeWindow();
379   NSRect frame = [window frame];
380   NSPoint expected = NSMakePoint(frame.size.width / 2, frame.size.height);
381   expected = [window convertBaseToScreen:expected];
382   EXPECT_NSEQ(expected, anchor);
385 TEST_F(PermissionBubbleControllerTest,
386     AnchorPositionDifferentWithAndWithoutLocationBar) {
387   NSPoint withLocationBar;
388   {
389     base::mac::ScopedObjCClassSwizzler locationSwizzle(
390         [PermissionBubbleController class],
391         [MockBubbleYesLocationBar class],
392         @selector(hasLocationBar));
393     withLocationBar = [controller_ getExpectedAnchorPoint];
394   }
396   NSPoint withoutLocationBar;
397   {
398     base::mac::ScopedObjCClassSwizzler locationSwizzle(
399         [PermissionBubbleController class],
400         [MockBubbleNoLocationBar class],
401         @selector(hasLocationBar));
402     withoutLocationBar = [controller_ getExpectedAnchorPoint];
403   }
405   // The bubble should be in different places depending if the location bar is
406   // available or not.
407   EXPECT_NSNE(withLocationBar, withoutLocationBar);