| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/metrics/histogram_samples.h" |
| #include "base/prefs/pref_service.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/statistics_delta_reader.h" |
| #include "base/time/time.h" |
| #include "chrome/browser/ui/passwords/manage_passwords_bubble.h" |
| #include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h" |
| #include "chrome/browser/ui/passwords/manage_passwords_icon.h" |
| #include "chrome/browser/ui/passwords/manage_passwords_icon_mock.h" |
| #include "chrome/browser/ui/passwords/manage_passwords_ui_controller_mock.h" |
| #include "chrome/test/base/chrome_render_view_host_test_harness.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "components/autofill/core/common/password_form.h" |
| #include "components/password_manager/core/browser/password_form_manager.h" |
| #include "components/password_manager/core/browser/stub_password_manager_client.h" |
| #include "components/password_manager/core/browser/stub_password_manager_driver.h" |
| #include "components/password_manager/core/common/password_manager_ui.h" |
| #include "content/public/test/test_browser_thread_bundle.h" |
| #include "content/public/test/web_contents_tester.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace { |
| |
| const int64 kSlowNavigationDelayInMS = 2000; |
| const int64 kQuickNavigationDelayInMS = 500; |
| |
| class MockElapsedTimer : public base::ElapsedTimer { |
| public: |
| MockElapsedTimer() {} |
| virtual base::TimeDelta Elapsed() const OVERRIDE { return delta_; } |
| |
| void Advance(int64 ms) { delta_ = base::TimeDelta::FromMilliseconds(ms); } |
| |
| private: |
| base::TimeDelta delta_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MockElapsedTimer); |
| }; |
| |
| } // namespace |
| |
| class ManagePasswordsUIControllerTest : public ChromeRenderViewHostTestHarness { |
| public: |
| ManagePasswordsUIControllerTest() {} |
| |
| virtual void SetUp() OVERRIDE { |
| ChromeRenderViewHostTestHarness::SetUp(); |
| |
| // Create the test UIController here so that it's bound to |
| // |test_web_contents_|, and will be retrieved correctly via |
| // ManagePasswordsUIController::FromWebContents in |controller()|. |
| new ManagePasswordsUIControllerMock(web_contents()); |
| |
| test_form_.origin = GURL("http://example.com"); |
| |
| // We need to be on a "webby" URL for most tests. |
| content::WebContentsTester::For(web_contents()) |
| ->NavigateAndCommit(GURL("http://example.com")); |
| } |
| |
| autofill::PasswordForm& test_form() { return test_form_; } |
| |
| ManagePasswordsUIControllerMock* controller() { |
| return static_cast<ManagePasswordsUIControllerMock*>( |
| ManagePasswordsUIController::FromWebContents(web_contents())); |
| } |
| |
| private: |
| autofill::PasswordForm test_form_; |
| }; |
| |
| TEST_F(ManagePasswordsUIControllerTest, DefaultState) { |
| EXPECT_EQ(password_manager::ui::INACTIVE_STATE, controller()->state()); |
| EXPECT_FALSE(controller()->PasswordPendingUserDecision()); |
| EXPECT_EQ(GURL::EmptyGURL(), controller()->origin()); |
| |
| ManagePasswordsIconMock mock; |
| controller()->UpdateIconAndBubbleState(&mock); |
| EXPECT_EQ(password_manager::ui::INACTIVE_STATE, mock.state()); |
| } |
| |
| TEST_F(ManagePasswordsUIControllerTest, PasswordAutofilled) { |
| base::string16 kTestUsername = base::ASCIIToUTF16("test_username"); |
| autofill::PasswordFormMap map; |
| map[kTestUsername] = &test_form(); |
| controller()->OnPasswordAutofilled(map); |
| |
| EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->state()); |
| EXPECT_FALSE(controller()->PasswordPendingUserDecision()); |
| EXPECT_EQ(test_form().origin, controller()->origin()); |
| |
| ManagePasswordsIconMock mock; |
| controller()->UpdateIconAndBubbleState(&mock); |
| EXPECT_EQ(password_manager::ui::MANAGE_STATE, mock.state()); |
| } |
| |
| TEST_F(ManagePasswordsUIControllerTest, PasswordSubmitted) { |
| password_manager::StubPasswordManagerClient client; |
| password_manager::StubPasswordManagerDriver driver; |
| password_manager::PasswordFormManager* test_form_manager = |
| new password_manager::PasswordFormManager( |
| NULL, &client, &driver, test_form(), false); |
| controller()->OnPasswordSubmitted(test_form_manager); |
| EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_AND_BUBBLE_STATE, |
| controller()->state()); |
| EXPECT_TRUE(controller()->PasswordPendingUserDecision()); |
| |
| // TODO(mkwst): This should be the value of test_form().origin, but |
| // it's being masked by the stub implementation of |
| // ManagePasswordsUIControllerMock::PendingCredentials. |
| EXPECT_EQ(GURL::EmptyGURL(), controller()->origin()); |
| |
| ManagePasswordsIconMock mock; |
| controller()->UpdateIconAndBubbleState(&mock); |
| EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE, mock.state()); |
| } |
| |
| TEST_F(ManagePasswordsUIControllerTest, QuickNavigations) { |
| password_manager::StubPasswordManagerClient client; |
| password_manager::StubPasswordManagerDriver driver; |
| password_manager::PasswordFormManager* test_form_manager = |
| new password_manager::PasswordFormManager( |
| NULL, &client, &driver, test_form(), false); |
| controller()->OnPasswordSubmitted(test_form_manager); |
| ManagePasswordsIconMock mock; |
| controller()->UpdateIconAndBubbleState(&mock); |
| EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE, mock.state()); |
| |
| // Fake-navigate within a second. We expect the bubble's state to persist |
| // if a navigation occurs too quickly for a user to reasonably have been |
| // able to interact with the bubble. This happens on `accounts.google.com`, |
| // for instance. |
| scoped_ptr<MockElapsedTimer> timer(new MockElapsedTimer()); |
| timer->Advance(kQuickNavigationDelayInMS); |
| controller()->SetTimer(timer.release()); |
| controller()->DidNavigateMainFrame(content::LoadCommittedDetails(), |
| content::FrameNavigateParams()); |
| controller()->UpdateIconAndBubbleState(&mock); |
| |
| EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE, mock.state()); |
| } |
| |
| TEST_F(ManagePasswordsUIControllerTest, SlowNavigations) { |
| password_manager::StubPasswordManagerClient client; |
| password_manager::StubPasswordManagerDriver driver; |
| password_manager::PasswordFormManager* test_form_manager = |
| new password_manager::PasswordFormManager( |
| NULL, &client, &driver, test_form(), false); |
| controller()->OnPasswordSubmitted(test_form_manager); |
| ManagePasswordsIconMock mock; |
| controller()->UpdateIconAndBubbleState(&mock); |
| EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE, mock.state()); |
| |
| // Fake-navigate after a second. We expect the bubble's state to be reset |
| // if a navigation occurs after this limit. |
| scoped_ptr<MockElapsedTimer> timer(new MockElapsedTimer()); |
| timer->Advance(kSlowNavigationDelayInMS); |
| controller()->SetTimer(timer.release()); |
| controller()->DidNavigateMainFrame(content::LoadCommittedDetails(), |
| content::FrameNavigateParams()); |
| controller()->UpdateIconAndBubbleState(&mock); |
| |
| EXPECT_EQ(password_manager::ui::INACTIVE_STATE, mock.state()); |
| } |
| |
| TEST_F(ManagePasswordsUIControllerTest, PasswordSubmittedToNonWebbyURL) { |
| // Navigate to a non-webby URL, then see what happens! |
| content::WebContentsTester::For(web_contents()) |
| ->NavigateAndCommit(GURL("chrome://sign-in")); |
| |
| password_manager::StubPasswordManagerClient client; |
| password_manager::StubPasswordManagerDriver driver; |
| password_manager::PasswordFormManager* test_form_manager = |
| new password_manager::PasswordFormManager( |
| NULL, &client, &driver, test_form(), false); |
| controller()->OnPasswordSubmitted(test_form_manager); |
| EXPECT_EQ(password_manager::ui::INACTIVE_STATE, controller()->state()); |
| EXPECT_FALSE(controller()->PasswordPendingUserDecision()); |
| |
| // TODO(mkwst): This should be the value of test_form().origin, but |
| // it's being masked by the stub implementation of |
| // ManagePasswordsUIControllerMock::PendingCredentials. |
| EXPECT_EQ(GURL::EmptyGURL(), controller()->origin()); |
| |
| ManagePasswordsIconMock mock; |
| controller()->UpdateIconAndBubbleState(&mock); |
| EXPECT_EQ(password_manager::ui::INACTIVE_STATE, mock.state()); |
| } |
| |
| TEST_F(ManagePasswordsUIControllerTest, BlacklistBlockedAutofill) { |
| test_form().blacklisted_by_user = true; |
| base::string16 kTestUsername = base::ASCIIToUTF16("test_username"); |
| autofill::PasswordFormMap map; |
| map[kTestUsername] = &test_form(); |
| controller()->OnBlacklistBlockedAutofill(map); |
| |
| EXPECT_EQ(password_manager::ui::BLACKLIST_STATE, controller()->state()); |
| EXPECT_FALSE(controller()->PasswordPendingUserDecision()); |
| EXPECT_EQ(test_form().origin, controller()->origin()); |
| |
| ManagePasswordsIconMock mock; |
| controller()->UpdateIconAndBubbleState(&mock); |
| EXPECT_EQ(password_manager::ui::BLACKLIST_STATE, mock.state()); |
| } |
| |
| TEST_F(ManagePasswordsUIControllerTest, ClickedUnblacklist) { |
| base::string16 kTestUsername = base::ASCIIToUTF16("test_username"); |
| autofill::PasswordFormMap map; |
| map[kTestUsername] = &test_form(); |
| controller()->OnBlacklistBlockedAutofill(map); |
| controller()->UnblacklistSite(); |
| |
| EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->state()); |
| EXPECT_FALSE(controller()->PasswordPendingUserDecision()); |
| EXPECT_EQ(test_form().origin, controller()->origin()); |
| |
| ManagePasswordsIconMock mock; |
| controller()->UpdateIconAndBubbleState(&mock); |
| EXPECT_EQ(password_manager::ui::MANAGE_STATE, mock.state()); |
| } |
| |
| TEST_F(ManagePasswordsUIControllerTest, UnblacklistedElsewhere) { |
| test_form().blacklisted_by_user = true; |
| base::string16 kTestUsername = base::ASCIIToUTF16("test_username"); |
| autofill::PasswordFormMap map; |
| map[kTestUsername] = &test_form(); |
| controller()->OnBlacklistBlockedAutofill(map); |
| |
| password_manager::PasswordStoreChange change( |
| password_manager::PasswordStoreChange::REMOVE, test_form()); |
| password_manager::PasswordStoreChangeList list(1, change); |
| controller()->OnLoginsChanged(list); |
| |
| EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->state()); |
| EXPECT_FALSE(controller()->PasswordPendingUserDecision()); |
| EXPECT_EQ(test_form().origin, controller()->origin()); |
| |
| ManagePasswordsIconMock mock; |
| controller()->UpdateIconAndBubbleState(&mock); |
| EXPECT_EQ(password_manager::ui::MANAGE_STATE, mock.state()); |
| } |
| |
| TEST_F(ManagePasswordsUIControllerTest, BlacklistedElsewhere) { |
| base::string16 kTestUsername = base::ASCIIToUTF16("test_username"); |
| autofill::PasswordFormMap map; |
| map[kTestUsername] = &test_form(); |
| controller()->OnPasswordAutofilled(map); |
| |
| test_form().blacklisted_by_user = true; |
| password_manager::PasswordStoreChange change( |
| password_manager::PasswordStoreChange::ADD, test_form()); |
| password_manager::PasswordStoreChangeList list(1, change); |
| controller()->OnLoginsChanged(list); |
| |
| EXPECT_EQ(password_manager::ui::BLACKLIST_STATE, controller()->state()); |
| EXPECT_FALSE(controller()->PasswordPendingUserDecision()); |
| EXPECT_EQ(test_form().origin, controller()->origin()); |
| |
| ManagePasswordsIconMock mock; |
| controller()->UpdateIconAndBubbleState(&mock); |
| EXPECT_EQ(password_manager::ui::BLACKLIST_STATE, mock.state()); |
| } |
| |