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 "base/mac/foundation_util.h"
8 #include "base/stl_util.h"
9 #include "base/strings/sys_string_conversions.h"
10 #include "base/strings/utf_string_conversions.h"
11 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
12 #include "chrome/browser/ui/cocoa/run_loop_testing.h"
13 #import "chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa.h"
14 #import "chrome/browser/ui/cocoa/website_settings/split_block_button.h"
15 #include "chrome/browser/ui/website_settings/mock_permission_bubble_request.h"
16 #include "grit/generated_resources.h"
17 #include "ui/base/l10n/l10n_util.h"
18 #include "ui/base/l10n/l10n_util_mac.h"
19 #import "ui/events/test/cocoa_test_event_utils.h"
20 #include "testing/gmock/include/gmock/gmock.h"
22 @class ConstrainedWindowButton;
24 @interface PermissionBubbleController (ExposedForTesting)
25 - (void)ok:(id)sender;
26 - (void)onAllow:(id)sender;
27 - (void)onBlock:(id)sender;
28 - (void)onCustomize:(id)sender;
29 - (void)onCheckboxChanged:(id)sender;
32 @interface SplitBlockButton (ExposedForTesting)
37 const char* const kPermissionA = "Permission A";
38 const char* const kPermissionB = "Permission B";
39 const char* const kPermissionC = "Permission C";
42 class PermissionBubbleControllerTest : public CocoaTest,
43 public PermissionBubbleView::Delegate {
46 MOCK_METHOD2(ToggleAccept, void(int, bool));
47 MOCK_METHOD0(SetCustomizationMode, void());
48 MOCK_METHOD0(Accept, void());
49 MOCK_METHOD0(Deny, void());
50 MOCK_METHOD0(Closing, void());
51 MOCK_METHOD1(SetView, void(PermissionBubbleView*));
53 virtual void SetUp() OVERRIDE {
55 bridge_.reset(new PermissionBubbleCocoa(nil));
56 AddRequest(kPermissionA);
57 controller_ = [[PermissionBubbleController alloc]
58 initWithParentWindow:test_window()
59 bridge:bridge_.get()];
62 virtual void TearDown() OVERRIDE {
64 chrome::testing::NSRunLoopRunAllPending();
65 STLDeleteElements(&requests_);
66 CocoaTest::TearDown();
69 void AddRequest(const std::string& title) {
70 MockPermissionBubbleRequest* request = new MockPermissionBubbleRequest(
72 l10n_util::GetStringUTF8(IDS_PERMISSION_ALLOW),
73 l10n_util::GetStringUTF8(IDS_PERMISSION_DENY));
74 requests_.push_back(request);
77 NSButton* FindButtonWithTitle(const std::string& title) {
78 return FindButtonWithTitle(base::SysUTF8ToNSString(title),
79 [ConstrainedWindowButton class]);
82 NSButton* FindButtonWithTitle(int title_id) {
83 return FindButtonWithTitle(l10n_util::GetNSString(title_id),
84 [ConstrainedWindowButton class]);
87 NSButton* FindMenuButtonWithTitle(int title_id) {
88 return FindButtonWithTitle(l10n_util::GetNSString(title_id),
89 [NSPopUpButton class]);
92 // IDS_PERMISSION_ALLOW and IDS_PERMISSION_DENY are used for two distinct
93 // UI elements, both of which derive from NSButton. So check the expected
94 // class, not just NSButton, as well as the title.
95 NSButton* FindButtonWithTitle(NSString* title, Class button_class) {
96 for (NSButton* view in [[controller_ bubble] subviews]) {
97 if ([view isKindOfClass:button_class] &&
98 [title isEqualToString:[view title]]) {
105 NSTextField* FindTextFieldWithString(const std::string& text) {
106 NSView* parent = base::mac::ObjCCastStrict<NSView>([controller_ bubble]);
107 return FindTextFieldWithString(parent, base::SysUTF8ToNSString(text));
110 NSTextField* FindTextFieldWithString(NSView* view, NSString* text) {
111 NSTextField* textField = nil;
112 for (NSView* child in [view subviews]) {
113 textField = base::mac::ObjCCast<NSTextField>(child);
114 if (![[textField stringValue] hasSuffix:text]) {
115 textField = FindTextFieldWithString(child, text);
123 void ChangePermissionMenuSelection(NSButton* menu_button, int next_title_id) {
124 NSMenu* menu = [base::mac::ObjCCastStrict<NSPopUpButton>(menu_button) menu];
125 NSString* next_title = l10n_util::GetNSString(next_title_id);
126 EXPECT_EQ([[menu itemWithTitle:[menu_button title]] state], NSOnState);
127 NSMenuItem* next_item = [menu itemWithTitle:next_title];
128 EXPECT_EQ([next_item state], NSOffState);
129 [menu performActionForItemAtIndex:[menu indexOfItem:next_item]];
132 NSMenuItem* FindCustomizeMenuItem() {
133 NSButton* button = FindButtonWithTitle(IDS_PERMISSION_DENY);
134 if (!button || ![button isKindOfClass:[SplitBlockButton class]])
136 NSString* customize = l10n_util::GetNSString(IDS_PERMISSION_CUSTOMIZE);
137 SplitBlockButton* block_button =
138 base::mac::ObjCCast<SplitBlockButton>(button);
139 for (NSMenuItem* item in [[block_button menu] itemArray]) {
140 if ([[item title] isEqualToString:customize])
147 PermissionBubbleController* controller_; // Weak; it deletes itself.
148 scoped_ptr<PermissionBubbleCocoa> bridge_;
149 std::vector<PermissionBubbleRequest*> requests_;
150 std::vector<bool> accept_states_;
153 TEST_F(PermissionBubbleControllerTest, ShowAndClose) {
154 EXPECT_FALSE([[controller_ window] isVisible]);
155 [controller_ showWindow:nil];
156 EXPECT_TRUE([[controller_ window] isVisible]);
159 TEST_F(PermissionBubbleControllerTest, ShowSinglePermission) {
160 [controller_ showAtAnchor:NSZeroPoint
162 forRequests:requests_
163 acceptStates:accept_states_
164 customizationMode:NO];
166 EXPECT_TRUE(FindTextFieldWithString(kPermissionA));
167 EXPECT_TRUE(FindButtonWithTitle(IDS_PERMISSION_ALLOW));
168 EXPECT_TRUE(FindButtonWithTitle(IDS_PERMISSION_DENY));
169 EXPECT_FALSE(FindButtonWithTitle(IDS_OK));
170 EXPECT_FALSE(FindCustomizeMenuItem());
173 TEST_F(PermissionBubbleControllerTest, ShowMultiplePermissions) {
174 AddRequest(kPermissionB);
175 AddRequest(kPermissionC);
177 [controller_ showAtAnchor:NSZeroPoint
179 forRequests:requests_
180 acceptStates:accept_states_
181 customizationMode:NO];
183 EXPECT_TRUE(FindTextFieldWithString(kPermissionA));
184 EXPECT_TRUE(FindTextFieldWithString(kPermissionB));
185 EXPECT_TRUE(FindTextFieldWithString(kPermissionC));
187 EXPECT_TRUE(FindButtonWithTitle(IDS_PERMISSION_ALLOW));
188 EXPECT_TRUE(FindButtonWithTitle(IDS_PERMISSION_DENY));
189 EXPECT_TRUE(FindCustomizeMenuItem());
190 EXPECT_FALSE(FindButtonWithTitle(IDS_OK));
193 TEST_F(PermissionBubbleControllerTest, ShowCustomizationModeAllow) {
194 accept_states_.push_back(true);
195 [controller_ showAtAnchor:NSZeroPoint
197 forRequests:requests_
198 acceptStates:accept_states_
199 customizationMode:YES];
201 // Test that there is one menu, with 'Allow' visible.
202 EXPECT_TRUE(FindMenuButtonWithTitle(IDS_PERMISSION_ALLOW));
203 EXPECT_FALSE(FindMenuButtonWithTitle(IDS_PERMISSION_DENY));
205 EXPECT_TRUE(FindButtonWithTitle(IDS_OK));
206 EXPECT_FALSE(FindButtonWithTitle(IDS_PERMISSION_ALLOW));
207 EXPECT_FALSE(FindButtonWithTitle(IDS_PERMISSION_DENY));
208 EXPECT_FALSE(FindCustomizeMenuItem());
211 TEST_F(PermissionBubbleControllerTest, ShowCustomizationModeBlock) {
212 accept_states_.push_back(false);
213 [controller_ showAtAnchor:NSZeroPoint
215 forRequests:requests_
216 acceptStates:accept_states_
217 customizationMode:YES];
219 // Test that there is one menu, with 'Block' visible.
220 EXPECT_TRUE(FindMenuButtonWithTitle(IDS_PERMISSION_DENY));
221 EXPECT_FALSE(FindMenuButtonWithTitle(IDS_PERMISSION_ALLOW));
223 EXPECT_TRUE(FindButtonWithTitle(IDS_OK));
224 EXPECT_FALSE(FindButtonWithTitle(IDS_PERMISSION_ALLOW));
225 EXPECT_FALSE(FindButtonWithTitle(IDS_PERMISSION_DENY));
226 EXPECT_FALSE(FindCustomizeMenuItem());
229 TEST_F(PermissionBubbleControllerTest, OK) {
230 accept_states_.push_back(true);
231 [controller_ showAtAnchor:NSZeroPoint
233 forRequests:requests_
234 acceptStates:accept_states_
235 customizationMode:YES];
237 EXPECT_CALL(*this, Closing()).Times(1);
238 [FindButtonWithTitle(IDS_OK) performClick:nil];
241 TEST_F(PermissionBubbleControllerTest, Allow) {
242 [controller_ showAtAnchor:NSZeroPoint
244 forRequests:requests_
245 acceptStates:accept_states_
246 customizationMode:NO];
248 EXPECT_CALL(*this, Accept()).Times(1);
249 [FindButtonWithTitle(IDS_PERMISSION_ALLOW) performClick:nil];
252 TEST_F(PermissionBubbleControllerTest, Deny) {
253 [controller_ showAtAnchor:NSZeroPoint
255 forRequests:requests_
256 acceptStates:accept_states_
257 customizationMode:NO];
259 EXPECT_CALL(*this, Deny()).Times(1);
260 [FindButtonWithTitle(IDS_PERMISSION_DENY) performClick:nil];
263 TEST_F(PermissionBubbleControllerTest, ChangePermissionSelection) {
264 AddRequest(kPermissionB);
266 accept_states_.push_back(true);
267 accept_states_.push_back(false);
269 [controller_ showAtAnchor:NSZeroPoint
271 forRequests:requests_
272 acceptStates:accept_states_
273 customizationMode:YES];
275 EXPECT_CALL(*this, ToggleAccept(0, false)).Times(1);
276 EXPECT_CALL(*this, ToggleAccept(1, true)).Times(1);
277 NSButton* menu_a = FindMenuButtonWithTitle(IDS_PERMISSION_ALLOW);
278 NSButton* menu_b = FindMenuButtonWithTitle(IDS_PERMISSION_DENY);
279 ChangePermissionMenuSelection(menu_a, IDS_PERMISSION_DENY);
280 ChangePermissionMenuSelection(menu_b, IDS_PERMISSION_ALLOW);
283 TEST_F(PermissionBubbleControllerTest, ClickCustomize) {
284 AddRequest(kPermissionB);
285 [controller_ showAtAnchor:NSZeroPoint
287 forRequests:requests_
288 acceptStates:accept_states_
289 customizationMode:NO];
291 EXPECT_CALL(*this, SetCustomizationMode()).Times(1);
292 NSMenuItem* customize_item = FindCustomizeMenuItem();
293 EXPECT_TRUE(customize_item);
294 NSMenu* menu = [customize_item menu];
295 [menu performActionForItemAtIndex:[menu indexOfItem:customize_item]];