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 #include "base/prefs/pref_service.h"
6 #include "base/strings/utf_string_conversions.h"
7 #include "base/time/time.h"
8 #include "chrome/browser/ui/passwords/manage_passwords_bubble.h"
9 #include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h"
10 #include "chrome/browser/ui/passwords/manage_passwords_icon.h"
11 #include "chrome/browser/ui/passwords/manage_passwords_icon_mock.h"
12 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller_mock.h"
13 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
14 #include "chrome/test/base/testing_profile.h"
15 #include "components/autofill/core/common/password_form.h"
16 #include "components/password_manager/core/browser/password_form_manager.h"
17 #include "components/password_manager/core/browser/stub_password_manager_client.h"
18 #include "components/password_manager/core/browser/stub_password_manager_driver.h"
19 #include "components/password_manager/core/common/password_manager_ui.h"
20 #include "content/public/test/test_browser_thread_bundle.h"
21 #include "content/public/test/web_contents_tester.h"
22 #include "testing/gmock/include/gmock/gmock.h"
23 #include "testing/gtest/include/gtest/gtest.h"
27 const int64 kSlowNavigationDelayInMS
= 2000;
28 const int64 kQuickNavigationDelayInMS
= 500;
30 class MockElapsedTimer
: public base::ElapsedTimer
{
33 virtual base::TimeDelta
Elapsed() const OVERRIDE
{ return delta_
; }
35 void Advance(int64 ms
) { delta_
= base::TimeDelta::FromMilliseconds(ms
); }
38 base::TimeDelta delta_
;
40 DISALLOW_COPY_AND_ASSIGN(MockElapsedTimer
);
45 class ManagePasswordsUIControllerTest
: public ChromeRenderViewHostTestHarness
{
47 ManagePasswordsUIControllerTest() {}
49 virtual void SetUp() OVERRIDE
{
50 ChromeRenderViewHostTestHarness::SetUp();
52 // Create the test UIController here so that it's bound to
53 // |test_web_contents_|, and will be retrieved correctly via
54 // ManagePasswordsUIController::FromWebContents in |controller()|.
55 new ManagePasswordsUIControllerMock(web_contents());
57 test_form_
.origin
= GURL("http://example.com");
59 // We need to be on a "webby" URL for most tests.
60 content::WebContentsTester::For(web_contents())
61 ->NavigateAndCommit(GURL("http://example.com"));
64 autofill::PasswordForm
& test_form() { return test_form_
; }
66 ManagePasswordsUIControllerMock
* controller() {
67 return static_cast<ManagePasswordsUIControllerMock
*>(
68 ManagePasswordsUIController::FromWebContents(web_contents()));
72 autofill::PasswordForm test_form_
;
75 TEST_F(ManagePasswordsUIControllerTest
, DefaultState
) {
76 EXPECT_EQ(password_manager::ui::INACTIVE_STATE
, controller()->state());
77 EXPECT_FALSE(controller()->PasswordPendingUserDecision());
78 EXPECT_EQ(GURL::EmptyGURL(), controller()->origin());
80 ManagePasswordsIconMock mock
;
81 controller()->UpdateIconAndBubbleState(&mock
);
82 EXPECT_EQ(password_manager::ui::INACTIVE_STATE
, mock
.state());
85 TEST_F(ManagePasswordsUIControllerTest
, PasswordAutofilled
) {
86 base::string16 kTestUsername
= base::ASCIIToUTF16("test_username");
87 autofill::PasswordFormMap map
;
88 map
[kTestUsername
] = &test_form();
89 controller()->OnPasswordAutofilled(map
);
91 EXPECT_EQ(password_manager::ui::MANAGE_STATE
, controller()->state());
92 EXPECT_FALSE(controller()->PasswordPendingUserDecision());
93 EXPECT_EQ(test_form().origin
, controller()->origin());
94 EXPECT_EQ(1u, controller()->best_matches().size());
95 ASSERT_EQ(1u, controller()->best_matches().count(kTestUsername
));
97 // Controller should store a separate copy of the form as it doesn't own it.
98 EXPECT_NE(&test_form(),
99 controller()->best_matches().find(kTestUsername
)->second
);
101 ManagePasswordsIconMock mock
;
102 controller()->UpdateIconAndBubbleState(&mock
);
103 EXPECT_EQ(password_manager::ui::MANAGE_STATE
, mock
.state());
106 TEST_F(ManagePasswordsUIControllerTest
, PasswordSubmitted
) {
107 password_manager::StubPasswordManagerClient client
;
108 password_manager::StubPasswordManagerDriver driver
;
109 scoped_ptr
<password_manager::PasswordFormManager
> test_form_manager(
110 new password_manager::PasswordFormManager(
111 NULL
, &client
, &driver
, test_form(), false));
112 controller()->OnPasswordSubmitted(test_form_manager
.Pass());
113 EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_AND_BUBBLE_STATE
,
114 controller()->state());
115 EXPECT_TRUE(controller()->PasswordPendingUserDecision());
117 // TODO(mkwst): This should be the value of test_form().origin, but
118 // it's being masked by the stub implementation of
119 // ManagePasswordsUIControllerMock::PendingCredentials.
120 EXPECT_EQ(GURL::EmptyGURL(), controller()->origin());
122 ManagePasswordsIconMock mock
;
123 controller()->UpdateIconAndBubbleState(&mock
);
124 EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE
,
128 TEST_F(ManagePasswordsUIControllerTest
, PasswordSaved
) {
129 password_manager::StubPasswordManagerClient client
;
130 password_manager::StubPasswordManagerDriver driver
;
131 scoped_ptr
<password_manager::PasswordFormManager
> test_form_manager(
132 new password_manager::PasswordFormManager(
133 NULL
, &client
, &driver
, test_form(), false));
134 controller()->OnPasswordSubmitted(test_form_manager
.Pass());
136 ManagePasswordsIconMock mock
;
137 controller()->UpdateIconAndBubbleState(&mock
);
138 controller()->SavePassword();
139 controller()->UpdateIconAndBubbleState(&mock
);
140 EXPECT_EQ(password_manager::ui::MANAGE_STATE
, mock
.state());
143 TEST_F(ManagePasswordsUIControllerTest
, PasswordBlacklisted
) {
144 password_manager::StubPasswordManagerClient client
;
145 password_manager::StubPasswordManagerDriver driver
;
146 scoped_ptr
<password_manager::PasswordFormManager
> test_form_manager(
147 new password_manager::PasswordFormManager(
148 NULL
, &client
, &driver
, test_form(), false));
149 controller()->OnPasswordSubmitted(test_form_manager
.Pass());
151 ManagePasswordsIconMock mock
;
152 controller()->UpdateIconAndBubbleState(&mock
);
153 controller()->NeverSavePassword();
154 controller()->UpdateIconAndBubbleState(&mock
);
155 EXPECT_EQ(password_manager::ui::BLACKLIST_STATE
, mock
.state());
158 TEST_F(ManagePasswordsUIControllerTest
, QuickNavigations
) {
159 password_manager::StubPasswordManagerClient client
;
160 password_manager::StubPasswordManagerDriver driver
;
161 scoped_ptr
<password_manager::PasswordFormManager
> test_form_manager(
162 new password_manager::PasswordFormManager(
163 NULL
, &client
, &driver
, test_form(), false));
164 controller()->OnPasswordSubmitted(test_form_manager
.Pass());
165 ManagePasswordsIconMock mock
;
166 controller()->UpdateIconAndBubbleState(&mock
);
167 EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE
,
170 // Fake-navigate within a second. We expect the bubble's state to persist
171 // if a navigation occurs too quickly for a user to reasonably have been
172 // able to interact with the bubble. This happens on `accounts.google.com`,
174 scoped_ptr
<MockElapsedTimer
> timer(new MockElapsedTimer());
175 timer
->Advance(kQuickNavigationDelayInMS
);
176 controller()->SetTimer(timer
.release());
177 controller()->DidNavigateMainFrame(content::LoadCommittedDetails(),
178 content::FrameNavigateParams());
179 controller()->UpdateIconAndBubbleState(&mock
);
181 EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE
,
185 TEST_F(ManagePasswordsUIControllerTest
, SlowNavigations
) {
186 password_manager::StubPasswordManagerClient client
;
187 password_manager::StubPasswordManagerDriver driver
;
188 scoped_ptr
<password_manager::PasswordFormManager
> test_form_manager(
189 new password_manager::PasswordFormManager(
190 NULL
, &client
, &driver
, test_form(), false));
191 controller()->OnPasswordSubmitted(test_form_manager
.Pass());
192 ManagePasswordsIconMock mock
;
193 controller()->UpdateIconAndBubbleState(&mock
);
194 EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE
,
197 // Fake-navigate after a second. We expect the bubble's state to be reset
198 // if a navigation occurs after this limit.
199 scoped_ptr
<MockElapsedTimer
> timer(new MockElapsedTimer());
200 timer
->Advance(kSlowNavigationDelayInMS
);
201 controller()->SetTimer(timer
.release());
202 controller()->DidNavigateMainFrame(content::LoadCommittedDetails(),
203 content::FrameNavigateParams());
204 controller()->UpdateIconAndBubbleState(&mock
);
206 EXPECT_EQ(password_manager::ui::INACTIVE_STATE
, mock
.state());
209 TEST_F(ManagePasswordsUIControllerTest
, PasswordSubmittedToNonWebbyURL
) {
210 // Navigate to a non-webby URL, then see what happens!
211 content::WebContentsTester::For(web_contents())
212 ->NavigateAndCommit(GURL("chrome://sign-in"));
214 password_manager::StubPasswordManagerClient client
;
215 password_manager::StubPasswordManagerDriver driver
;
216 scoped_ptr
<password_manager::PasswordFormManager
> test_form_manager(
217 new password_manager::PasswordFormManager(
218 NULL
, &client
, &driver
, test_form(), false));
219 controller()->OnPasswordSubmitted(test_form_manager
.Pass());
220 EXPECT_EQ(password_manager::ui::INACTIVE_STATE
, controller()->state());
221 EXPECT_FALSE(controller()->PasswordPendingUserDecision());
223 // TODO(mkwst): This should be the value of test_form().origin, but
224 // it's being masked by the stub implementation of
225 // ManagePasswordsUIControllerMock::PendingCredentials.
226 EXPECT_EQ(GURL::EmptyGURL(), controller()->origin());
228 ManagePasswordsIconMock mock
;
229 controller()->UpdateIconAndBubbleState(&mock
);
230 EXPECT_EQ(password_manager::ui::INACTIVE_STATE
, mock
.state());
233 TEST_F(ManagePasswordsUIControllerTest
, BlacklistBlockedAutofill
) {
234 test_form().blacklisted_by_user
= true;
235 base::string16 kTestUsername
= base::ASCIIToUTF16("test_username");
236 autofill::PasswordFormMap map
;
237 map
[kTestUsername
] = &test_form();
238 controller()->OnBlacklistBlockedAutofill(map
);
240 EXPECT_EQ(password_manager::ui::BLACKLIST_STATE
, controller()->state());
241 EXPECT_FALSE(controller()->PasswordPendingUserDecision());
242 EXPECT_EQ(test_form().origin
, controller()->origin());
243 EXPECT_EQ(1u, controller()->best_matches().size());
244 ASSERT_EQ(1u, controller()->best_matches().count(kTestUsername
));
246 // Controller should store a separate copy of the form as it doesn't own it.
247 EXPECT_NE(&test_form(),
248 controller()->best_matches().find(kTestUsername
)->second
);
250 ManagePasswordsIconMock mock
;
251 controller()->UpdateIconAndBubbleState(&mock
);
252 EXPECT_EQ(password_manager::ui::BLACKLIST_STATE
, mock
.state());
255 TEST_F(ManagePasswordsUIControllerTest
, ClickedUnblacklist
) {
256 base::string16 kTestUsername
= base::ASCIIToUTF16("test_username");
257 autofill::PasswordFormMap map
;
258 map
[kTestUsername
] = &test_form();
259 controller()->OnBlacklistBlockedAutofill(map
);
260 controller()->UnblacklistSite();
262 EXPECT_EQ(password_manager::ui::MANAGE_STATE
, controller()->state());
263 EXPECT_FALSE(controller()->PasswordPendingUserDecision());
264 EXPECT_EQ(test_form().origin
, controller()->origin());
266 ManagePasswordsIconMock mock
;
267 controller()->UpdateIconAndBubbleState(&mock
);
268 EXPECT_EQ(password_manager::ui::MANAGE_STATE
, mock
.state());
271 TEST_F(ManagePasswordsUIControllerTest
, UnblacklistedElsewhere
) {
272 test_form().blacklisted_by_user
= true;
273 base::string16 kTestUsername
= base::ASCIIToUTF16("test_username");
274 autofill::PasswordFormMap map
;
275 map
[kTestUsername
] = &test_form();
276 controller()->OnBlacklistBlockedAutofill(map
);
278 password_manager::PasswordStoreChange
change(
279 password_manager::PasswordStoreChange::REMOVE
, test_form());
280 password_manager::PasswordStoreChangeList
list(1, change
);
281 controller()->OnLoginsChanged(list
);
283 EXPECT_EQ(password_manager::ui::MANAGE_STATE
, controller()->state());
284 EXPECT_FALSE(controller()->PasswordPendingUserDecision());
285 EXPECT_EQ(test_form().origin
, controller()->origin());
287 ManagePasswordsIconMock mock
;
288 controller()->UpdateIconAndBubbleState(&mock
);
289 EXPECT_EQ(password_manager::ui::MANAGE_STATE
, mock
.state());
292 TEST_F(ManagePasswordsUIControllerTest
, BlacklistedElsewhere
) {
293 base::string16 kTestUsername
= base::ASCIIToUTF16("test_username");
294 autofill::PasswordFormMap map
;
295 map
[kTestUsername
] = &test_form();
296 controller()->OnPasswordAutofilled(map
);
298 test_form().blacklisted_by_user
= true;
299 password_manager::PasswordStoreChange
change(
300 password_manager::PasswordStoreChange::ADD
, test_form());
301 password_manager::PasswordStoreChangeList
list(1, change
);
302 controller()->OnLoginsChanged(list
);
304 EXPECT_EQ(password_manager::ui::BLACKLIST_STATE
, controller()->state());
305 EXPECT_FALSE(controller()->PasswordPendingUserDecision());
306 EXPECT_EQ(test_form().origin
, controller()->origin());
308 ManagePasswordsIconMock mock
;
309 controller()->UpdateIconAndBubbleState(&mock
);
310 EXPECT_EQ(password_manager::ui::BLACKLIST_STATE
, mock
.state());
313 TEST_F(ManagePasswordsUIControllerTest
, AutomaticPasswordSave
) {
314 password_manager::StubPasswordManagerClient client
;
315 password_manager::StubPasswordManagerDriver driver
;
316 scoped_ptr
<password_manager::PasswordFormManager
> test_form_manager(
317 new password_manager::PasswordFormManager(
318 NULL
, &client
, &driver
, test_form(), false));
320 controller()->OnAutomaticPasswordSave(test_form_manager
.Pass());
321 EXPECT_EQ(password_manager::ui::CONFIRMATION_STATE
, controller()->state());
323 ManagePasswordsIconMock mock
;
324 controller()->UpdateIconAndBubbleState(&mock
);
325 EXPECT_EQ(password_manager::ui::MANAGE_STATE
, mock
.state());