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/metrics/histogram_samples.h"
6 #include "base/prefs/pref_service.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "base/test/statistics_delta_reader.h"
9 #include "base/time/time.h"
10 #include "chrome/browser/ui/passwords/manage_passwords_bubble.h"
11 #include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h"
12 #include "chrome/browser/ui/passwords/manage_passwords_icon.h"
13 #include "chrome/browser/ui/passwords/manage_passwords_icon_mock.h"
14 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller_mock.h"
15 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
16 #include "chrome/test/base/testing_profile.h"
17 #include "components/autofill/core/common/password_form.h"
18 #include "components/password_manager/core/browser/password_form_manager.h"
19 #include "components/password_manager/core/browser/stub_password_manager_client.h"
20 #include "components/password_manager/core/browser/stub_password_manager_driver.h"
21 #include "components/password_manager/core/common/password_manager_ui.h"
22 #include "content/public/test/test_browser_thread_bundle.h"
23 #include "content/public/test/web_contents_tester.h"
24 #include "testing/gmock/include/gmock/gmock.h"
25 #include "testing/gtest/include/gtest/gtest.h"
29 const int64 kSlowNavigationDelayInMS = 2000;
30 const int64 kQuickNavigationDelayInMS = 500;
32 class MockElapsedTimer : public base::ElapsedTimer {
35 virtual base::TimeDelta Elapsed() const OVERRIDE { return delta_; }
37 void Advance(int64 ms) { delta_ = base::TimeDelta::FromMilliseconds(ms); }
40 base::TimeDelta delta_;
42 DISALLOW_COPY_AND_ASSIGN(MockElapsedTimer);
47 class ManagePasswordsUIControllerTest : public ChromeRenderViewHostTestHarness {
49 ManagePasswordsUIControllerTest() {}
51 virtual void SetUp() OVERRIDE {
52 ChromeRenderViewHostTestHarness::SetUp();
54 // Create the test UIController here so that it's bound to
55 // |test_web_contents_|, and will be retrieved correctly via
56 // ManagePasswordsUIController::FromWebContents in |controller()|.
57 new ManagePasswordsUIControllerMock(web_contents());
59 test_form_.origin = GURL("http://example.com");
61 // We need to be on a "webby" URL for most tests.
62 content::WebContentsTester::For(web_contents())
63 ->NavigateAndCommit(GURL("http://example.com"));
66 autofill::PasswordForm& test_form() { return test_form_; }
68 ManagePasswordsUIControllerMock* controller() {
69 return static_cast<ManagePasswordsUIControllerMock*>(
70 ManagePasswordsUIController::FromWebContents(web_contents()));
74 autofill::PasswordForm test_form_;
77 TEST_F(ManagePasswordsUIControllerTest, DefaultState) {
78 EXPECT_EQ(password_manager::ui::INACTIVE_STATE, controller()->state());
79 EXPECT_FALSE(controller()->PasswordPendingUserDecision());
80 EXPECT_EQ(GURL::EmptyGURL(), controller()->origin());
82 ManagePasswordsIconMock mock;
83 controller()->UpdateIconAndBubbleState(&mock);
84 EXPECT_EQ(password_manager::ui::INACTIVE_STATE, mock.state());
87 TEST_F(ManagePasswordsUIControllerTest, PasswordAutofilled) {
88 base::string16 kTestUsername = base::ASCIIToUTF16("test_username");
89 autofill::PasswordFormMap map;
90 map[kTestUsername] = &test_form();
91 controller()->OnPasswordAutofilled(map);
93 EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->state());
94 EXPECT_FALSE(controller()->PasswordPendingUserDecision());
95 EXPECT_EQ(test_form().origin, controller()->origin());
96 EXPECT_EQ(1u, controller()->best_matches().size());
97 ASSERT_EQ(1u, controller()->best_matches().count(kTestUsername));
99 // Controller should store a separate copy of the form as it doesn't own it.
100 EXPECT_NE(&test_form(),
101 controller()->best_matches().find(kTestUsername)->second);
103 ManagePasswordsIconMock mock;
104 controller()->UpdateIconAndBubbleState(&mock);
105 EXPECT_EQ(password_manager::ui::MANAGE_STATE, mock.state());
108 TEST_F(ManagePasswordsUIControllerTest, PasswordSubmitted) {
109 password_manager::StubPasswordManagerClient client;
110 password_manager::StubPasswordManagerDriver driver;
111 scoped_ptr<password_manager::PasswordFormManager> test_form_manager(
112 new password_manager::PasswordFormManager(
113 NULL, &client, &driver, test_form(), false));
114 controller()->OnPasswordSubmitted(test_form_manager.Pass());
115 EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_AND_BUBBLE_STATE,
116 controller()->state());
117 EXPECT_TRUE(controller()->PasswordPendingUserDecision());
119 // TODO(mkwst): This should be the value of test_form().origin, but
120 // it's being masked by the stub implementation of
121 // ManagePasswordsUIControllerMock::PendingCredentials.
122 EXPECT_EQ(GURL::EmptyGURL(), controller()->origin());
124 ManagePasswordsIconMock mock;
125 controller()->UpdateIconAndBubbleState(&mock);
126 EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
130 TEST_F(ManagePasswordsUIControllerTest, PasswordSaved) {
131 password_manager::StubPasswordManagerClient client;
132 password_manager::StubPasswordManagerDriver driver;
133 scoped_ptr<password_manager::PasswordFormManager> test_form_manager(
134 new password_manager::PasswordFormManager(
135 NULL, &client, &driver, test_form(), false));
136 controller()->OnPasswordSubmitted(test_form_manager.Pass());
138 ManagePasswordsIconMock mock;
139 controller()->UpdateIconAndBubbleState(&mock);
140 controller()->SavePassword();
141 controller()->UpdateIconAndBubbleState(&mock);
142 EXPECT_EQ(password_manager::ui::MANAGE_STATE, mock.state());
145 TEST_F(ManagePasswordsUIControllerTest, PasswordBlacklisted) {
146 password_manager::StubPasswordManagerClient client;
147 password_manager::StubPasswordManagerDriver driver;
148 scoped_ptr<password_manager::PasswordFormManager> test_form_manager(
149 new password_manager::PasswordFormManager(
150 NULL, &client, &driver, test_form(), false));
151 controller()->OnPasswordSubmitted(test_form_manager.Pass());
153 ManagePasswordsIconMock mock;
154 controller()->UpdateIconAndBubbleState(&mock);
155 controller()->NeverSavePassword();
156 controller()->UpdateIconAndBubbleState(&mock);
157 EXPECT_EQ(password_manager::ui::BLACKLIST_STATE, mock.state());
160 TEST_F(ManagePasswordsUIControllerTest, QuickNavigations) {
161 password_manager::StubPasswordManagerClient client;
162 password_manager::StubPasswordManagerDriver driver;
163 scoped_ptr<password_manager::PasswordFormManager> test_form_manager(
164 new password_manager::PasswordFormManager(
165 NULL, &client, &driver, test_form(), false));
166 controller()->OnPasswordSubmitted(test_form_manager.Pass());
167 ManagePasswordsIconMock mock;
168 controller()->UpdateIconAndBubbleState(&mock);
169 EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
172 // Fake-navigate within a second. We expect the bubble's state to persist
173 // if a navigation occurs too quickly for a user to reasonably have been
174 // able to interact with the bubble. This happens on `accounts.google.com`,
176 scoped_ptr<MockElapsedTimer> timer(new MockElapsedTimer());
177 timer->Advance(kQuickNavigationDelayInMS);
178 controller()->SetTimer(timer.release());
179 controller()->DidNavigateMainFrame(content::LoadCommittedDetails(),
180 content::FrameNavigateParams());
181 controller()->UpdateIconAndBubbleState(&mock);
183 EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
187 TEST_F(ManagePasswordsUIControllerTest, SlowNavigations) {
188 password_manager::StubPasswordManagerClient client;
189 password_manager::StubPasswordManagerDriver driver;
190 scoped_ptr<password_manager::PasswordFormManager> test_form_manager(
191 new password_manager::PasswordFormManager(
192 NULL, &client, &driver, test_form(), false));
193 controller()->OnPasswordSubmitted(test_form_manager.Pass());
194 ManagePasswordsIconMock mock;
195 controller()->UpdateIconAndBubbleState(&mock);
196 EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
199 // Fake-navigate after a second. We expect the bubble's state to be reset
200 // if a navigation occurs after this limit.
201 scoped_ptr<MockElapsedTimer> timer(new MockElapsedTimer());
202 timer->Advance(kSlowNavigationDelayInMS);
203 controller()->SetTimer(timer.release());
204 controller()->DidNavigateMainFrame(content::LoadCommittedDetails(),
205 content::FrameNavigateParams());
206 controller()->UpdateIconAndBubbleState(&mock);
208 EXPECT_EQ(password_manager::ui::INACTIVE_STATE, mock.state());
211 TEST_F(ManagePasswordsUIControllerTest, PasswordSubmittedToNonWebbyURL) {
212 // Navigate to a non-webby URL, then see what happens!
213 content::WebContentsTester::For(web_contents())
214 ->NavigateAndCommit(GURL("chrome://sign-in"));
216 password_manager::StubPasswordManagerClient client;
217 password_manager::StubPasswordManagerDriver driver;
218 scoped_ptr<password_manager::PasswordFormManager> test_form_manager(
219 new password_manager::PasswordFormManager(
220 NULL, &client, &driver, test_form(), false));
221 controller()->OnPasswordSubmitted(test_form_manager.Pass());
222 EXPECT_EQ(password_manager::ui::INACTIVE_STATE, controller()->state());
223 EXPECT_FALSE(controller()->PasswordPendingUserDecision());
225 // TODO(mkwst): This should be the value of test_form().origin, but
226 // it's being masked by the stub implementation of
227 // ManagePasswordsUIControllerMock::PendingCredentials.
228 EXPECT_EQ(GURL::EmptyGURL(), controller()->origin());
230 ManagePasswordsIconMock mock;
231 controller()->UpdateIconAndBubbleState(&mock);
232 EXPECT_EQ(password_manager::ui::INACTIVE_STATE, mock.state());
235 TEST_F(ManagePasswordsUIControllerTest, BlacklistBlockedAutofill) {
236 test_form().blacklisted_by_user = true;
237 base::string16 kTestUsername = base::ASCIIToUTF16("test_username");
238 autofill::PasswordFormMap map;
239 map[kTestUsername] = &test_form();
240 controller()->OnBlacklistBlockedAutofill(map);
242 EXPECT_EQ(password_manager::ui::BLACKLIST_STATE, controller()->state());
243 EXPECT_FALSE(controller()->PasswordPendingUserDecision());
244 EXPECT_EQ(test_form().origin, controller()->origin());
245 EXPECT_EQ(1u, controller()->best_matches().size());
246 ASSERT_EQ(1u, controller()->best_matches().count(kTestUsername));
248 // Controller should store a separate copy of the form as it doesn't own it.
249 EXPECT_NE(&test_form(),
250 controller()->best_matches().find(kTestUsername)->second);
252 ManagePasswordsIconMock mock;
253 controller()->UpdateIconAndBubbleState(&mock);
254 EXPECT_EQ(password_manager::ui::BLACKLIST_STATE, mock.state());
257 TEST_F(ManagePasswordsUIControllerTest, ClickedUnblacklist) {
258 base::string16 kTestUsername = base::ASCIIToUTF16("test_username");
259 autofill::PasswordFormMap map;
260 map[kTestUsername] = &test_form();
261 controller()->OnBlacklistBlockedAutofill(map);
262 controller()->UnblacklistSite();
264 EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->state());
265 EXPECT_FALSE(controller()->PasswordPendingUserDecision());
266 EXPECT_EQ(test_form().origin, controller()->origin());
268 ManagePasswordsIconMock mock;
269 controller()->UpdateIconAndBubbleState(&mock);
270 EXPECT_EQ(password_manager::ui::MANAGE_STATE, mock.state());
273 TEST_F(ManagePasswordsUIControllerTest, UnblacklistedElsewhere) {
274 test_form().blacklisted_by_user = true;
275 base::string16 kTestUsername = base::ASCIIToUTF16("test_username");
276 autofill::PasswordFormMap map;
277 map[kTestUsername] = &test_form();
278 controller()->OnBlacklistBlockedAutofill(map);
280 password_manager::PasswordStoreChange change(
281 password_manager::PasswordStoreChange::REMOVE, test_form());
282 password_manager::PasswordStoreChangeList list(1, change);
283 controller()->OnLoginsChanged(list);
285 EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->state());
286 EXPECT_FALSE(controller()->PasswordPendingUserDecision());
287 EXPECT_EQ(test_form().origin, controller()->origin());
289 ManagePasswordsIconMock mock;
290 controller()->UpdateIconAndBubbleState(&mock);
291 EXPECT_EQ(password_manager::ui::MANAGE_STATE, mock.state());
294 TEST_F(ManagePasswordsUIControllerTest, BlacklistedElsewhere) {
295 base::string16 kTestUsername = base::ASCIIToUTF16("test_username");
296 autofill::PasswordFormMap map;
297 map[kTestUsername] = &test_form();
298 controller()->OnPasswordAutofilled(map);
300 test_form().blacklisted_by_user = true;
301 password_manager::PasswordStoreChange change(
302 password_manager::PasswordStoreChange::ADD, test_form());
303 password_manager::PasswordStoreChangeList list(1, change);
304 controller()->OnLoginsChanged(list);
306 EXPECT_EQ(password_manager::ui::BLACKLIST_STATE, controller()->state());
307 EXPECT_FALSE(controller()->PasswordPendingUserDecision());
308 EXPECT_EQ(test_form().origin, controller()->origin());
310 ManagePasswordsIconMock mock;
311 controller()->UpdateIconAndBubbleState(&mock);
312 EXPECT_EQ(password_manager::ui::BLACKLIST_STATE, mock.state());
315 TEST_F(ManagePasswordsUIControllerTest, AutomaticPasswordSave) {
316 password_manager::StubPasswordManagerClient client;
317 password_manager::StubPasswordManagerDriver driver;
318 scoped_ptr<password_manager::PasswordFormManager> test_form_manager(
319 new password_manager::PasswordFormManager(
320 NULL, &client, &driver, test_form(), false));
322 controller()->OnAutomaticPasswordSave(test_form_manager.Pass());
323 EXPECT_EQ(password_manager::ui::CONFIRMATION_STATE, controller()->state());
325 ManagePasswordsIconMock mock;
326 controller()->UpdateIconAndBubbleState(&mock);
327 EXPECT_EQ(password_manager::ui::MANAGE_STATE, mock.state());