Home | History | Annotate | Download | only in passwords
      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.
      4 
      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"
     26 
     27 namespace {
     28 
     29 const int64 kSlowNavigationDelayInMS = 2000;
     30 const int64 kQuickNavigationDelayInMS = 500;
     31 
     32 class MockElapsedTimer : public base::ElapsedTimer {
     33  public:
     34   MockElapsedTimer() {}
     35   virtual base::TimeDelta Elapsed() const OVERRIDE { return delta_; }
     36 
     37   void Advance(int64 ms) { delta_ = base::TimeDelta::FromMilliseconds(ms); }
     38 
     39  private:
     40   base::TimeDelta delta_;
     41 
     42   DISALLOW_COPY_AND_ASSIGN(MockElapsedTimer);
     43 };
     44 
     45 }  // namespace
     46 
     47 class ManagePasswordsUIControllerTest : public ChromeRenderViewHostTestHarness {
     48  public:
     49   ManagePasswordsUIControllerTest() {}
     50 
     51   virtual void SetUp() OVERRIDE {
     52     ChromeRenderViewHostTestHarness::SetUp();
     53 
     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());
     58 
     59     test_form_.origin = GURL("http://example.com");
     60 
     61     // We need to be on a "webby" URL for most tests.
     62     content::WebContentsTester::For(web_contents())
     63         ->NavigateAndCommit(GURL("http://example.com"));
     64   }
     65 
     66   autofill::PasswordForm& test_form() { return test_form_; }
     67 
     68   ManagePasswordsUIControllerMock* controller() {
     69     return static_cast<ManagePasswordsUIControllerMock*>(
     70         ManagePasswordsUIController::FromWebContents(web_contents()));
     71   }
     72 
     73  private:
     74   autofill::PasswordForm test_form_;
     75 };
     76 
     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());
     81 
     82   ManagePasswordsIconMock mock;
     83   controller()->UpdateIconAndBubbleState(&mock);
     84   EXPECT_EQ(password_manager::ui::INACTIVE_STATE, mock.state());
     85 }
     86 
     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);
     92 
     93   EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->state());
     94   EXPECT_FALSE(controller()->PasswordPendingUserDecision());
     95   EXPECT_EQ(test_form().origin, controller()->origin());
     96 
     97   ManagePasswordsIconMock mock;
     98   controller()->UpdateIconAndBubbleState(&mock);
     99   EXPECT_EQ(password_manager::ui::MANAGE_STATE, mock.state());
    100 }
    101 
    102 TEST_F(ManagePasswordsUIControllerTest, PasswordSubmitted) {
    103   password_manager::StubPasswordManagerClient client;
    104   password_manager::StubPasswordManagerDriver driver;
    105   password_manager::PasswordFormManager* test_form_manager =
    106       new password_manager::PasswordFormManager(
    107           NULL, &client, &driver, test_form(), false);
    108   controller()->OnPasswordSubmitted(test_form_manager);
    109   EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_AND_BUBBLE_STATE,
    110             controller()->state());
    111   EXPECT_TRUE(controller()->PasswordPendingUserDecision());
    112 
    113   // TODO(mkwst): This should be the value of test_form().origin, but
    114   // it's being masked by the stub implementation of
    115   // ManagePasswordsUIControllerMock::PendingCredentials.
    116   EXPECT_EQ(GURL::EmptyGURL(), controller()->origin());
    117 
    118   ManagePasswordsIconMock mock;
    119   controller()->UpdateIconAndBubbleState(&mock);
    120   EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE, mock.state());
    121 }
    122 
    123 TEST_F(ManagePasswordsUIControllerTest, QuickNavigations) {
    124   password_manager::StubPasswordManagerClient client;
    125   password_manager::StubPasswordManagerDriver driver;
    126   password_manager::PasswordFormManager* test_form_manager =
    127       new password_manager::PasswordFormManager(
    128           NULL, &client, &driver, test_form(), false);
    129   controller()->OnPasswordSubmitted(test_form_manager);
    130   ManagePasswordsIconMock mock;
    131   controller()->UpdateIconAndBubbleState(&mock);
    132   EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE, mock.state());
    133 
    134   // Fake-navigate within a second. We expect the bubble's state to persist
    135   // if a navigation occurs too quickly for a user to reasonably have been
    136   // able to interact with the bubble. This happens on `accounts.google.com`,
    137   // for instance.
    138   scoped_ptr<MockElapsedTimer> timer(new MockElapsedTimer());
    139   timer->Advance(kQuickNavigationDelayInMS);
    140   controller()->SetTimer(timer.release());
    141   controller()->DidNavigateMainFrame(content::LoadCommittedDetails(),
    142                                      content::FrameNavigateParams());
    143   controller()->UpdateIconAndBubbleState(&mock);
    144 
    145   EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE, mock.state());
    146 }
    147 
    148 TEST_F(ManagePasswordsUIControllerTest, SlowNavigations) {
    149   password_manager::StubPasswordManagerClient client;
    150   password_manager::StubPasswordManagerDriver driver;
    151   password_manager::PasswordFormManager* test_form_manager =
    152       new password_manager::PasswordFormManager(
    153           NULL, &client, &driver, test_form(), false);
    154   controller()->OnPasswordSubmitted(test_form_manager);
    155   ManagePasswordsIconMock mock;
    156   controller()->UpdateIconAndBubbleState(&mock);
    157   EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE, mock.state());
    158 
    159   // Fake-navigate after a second. We expect the bubble's state to be reset
    160   // if a navigation occurs after this limit.
    161   scoped_ptr<MockElapsedTimer> timer(new MockElapsedTimer());
    162   timer->Advance(kSlowNavigationDelayInMS);
    163   controller()->SetTimer(timer.release());
    164   controller()->DidNavigateMainFrame(content::LoadCommittedDetails(),
    165                                      content::FrameNavigateParams());
    166   controller()->UpdateIconAndBubbleState(&mock);
    167 
    168   EXPECT_EQ(password_manager::ui::INACTIVE_STATE, mock.state());
    169 }
    170 
    171 TEST_F(ManagePasswordsUIControllerTest, PasswordSubmittedToNonWebbyURL) {
    172   // Navigate to a non-webby URL, then see what happens!
    173   content::WebContentsTester::For(web_contents())
    174       ->NavigateAndCommit(GURL("chrome://sign-in"));
    175 
    176   password_manager::StubPasswordManagerClient client;
    177   password_manager::StubPasswordManagerDriver driver;
    178   password_manager::PasswordFormManager* test_form_manager =
    179       new password_manager::PasswordFormManager(
    180           NULL, &client, &driver, test_form(), false);
    181   controller()->OnPasswordSubmitted(test_form_manager);
    182   EXPECT_EQ(password_manager::ui::INACTIVE_STATE, controller()->state());
    183   EXPECT_FALSE(controller()->PasswordPendingUserDecision());
    184 
    185   // TODO(mkwst): This should be the value of test_form().origin, but
    186   // it's being masked by the stub implementation of
    187   // ManagePasswordsUIControllerMock::PendingCredentials.
    188   EXPECT_EQ(GURL::EmptyGURL(), controller()->origin());
    189 
    190   ManagePasswordsIconMock mock;
    191   controller()->UpdateIconAndBubbleState(&mock);
    192   EXPECT_EQ(password_manager::ui::INACTIVE_STATE, mock.state());
    193 }
    194 
    195 TEST_F(ManagePasswordsUIControllerTest, BlacklistBlockedAutofill) {
    196   test_form().blacklisted_by_user = true;
    197   base::string16 kTestUsername = base::ASCIIToUTF16("test_username");
    198   autofill::PasswordFormMap map;
    199   map[kTestUsername] = &test_form();
    200   controller()->OnBlacklistBlockedAutofill(map);
    201 
    202   EXPECT_EQ(password_manager::ui::BLACKLIST_STATE, controller()->state());
    203   EXPECT_FALSE(controller()->PasswordPendingUserDecision());
    204   EXPECT_EQ(test_form().origin, controller()->origin());
    205 
    206   ManagePasswordsIconMock mock;
    207   controller()->UpdateIconAndBubbleState(&mock);
    208   EXPECT_EQ(password_manager::ui::BLACKLIST_STATE, mock.state());
    209 }
    210 
    211 TEST_F(ManagePasswordsUIControllerTest, ClickedUnblacklist) {
    212   base::string16 kTestUsername = base::ASCIIToUTF16("test_username");
    213   autofill::PasswordFormMap map;
    214   map[kTestUsername] = &test_form();
    215   controller()->OnBlacklistBlockedAutofill(map);
    216   controller()->UnblacklistSite();
    217 
    218   EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->state());
    219   EXPECT_FALSE(controller()->PasswordPendingUserDecision());
    220   EXPECT_EQ(test_form().origin, controller()->origin());
    221 
    222   ManagePasswordsIconMock mock;
    223   controller()->UpdateIconAndBubbleState(&mock);
    224   EXPECT_EQ(password_manager::ui::MANAGE_STATE, mock.state());
    225 }
    226 
    227 TEST_F(ManagePasswordsUIControllerTest, UnblacklistedElsewhere) {
    228   test_form().blacklisted_by_user = true;
    229   base::string16 kTestUsername = base::ASCIIToUTF16("test_username");
    230   autofill::PasswordFormMap map;
    231   map[kTestUsername] = &test_form();
    232   controller()->OnBlacklistBlockedAutofill(map);
    233 
    234   password_manager::PasswordStoreChange change(
    235       password_manager::PasswordStoreChange::REMOVE, test_form());
    236   password_manager::PasswordStoreChangeList list(1, change);
    237   controller()->OnLoginsChanged(list);
    238 
    239   EXPECT_EQ(password_manager::ui::MANAGE_STATE, controller()->state());
    240   EXPECT_FALSE(controller()->PasswordPendingUserDecision());
    241   EXPECT_EQ(test_form().origin, controller()->origin());
    242 
    243   ManagePasswordsIconMock mock;
    244   controller()->UpdateIconAndBubbleState(&mock);
    245   EXPECT_EQ(password_manager::ui::MANAGE_STATE, mock.state());
    246 }
    247 
    248 TEST_F(ManagePasswordsUIControllerTest, BlacklistedElsewhere) {
    249   base::string16 kTestUsername = base::ASCIIToUTF16("test_username");
    250   autofill::PasswordFormMap map;
    251   map[kTestUsername] = &test_form();
    252   controller()->OnPasswordAutofilled(map);
    253 
    254   test_form().blacklisted_by_user = true;
    255   password_manager::PasswordStoreChange change(
    256       password_manager::PasswordStoreChange::ADD, test_form());
    257   password_manager::PasswordStoreChangeList list(1, change);
    258   controller()->OnLoginsChanged(list);
    259 
    260   EXPECT_EQ(password_manager::ui::BLACKLIST_STATE, controller()->state());
    261   EXPECT_FALSE(controller()->PasswordPendingUserDecision());
    262   EXPECT_EQ(test_form().origin, controller()->origin());
    263 
    264   ManagePasswordsIconMock mock;
    265   controller()->UpdateIconAndBubbleState(&mock);
    266   EXPECT_EQ(password_manager::ui::BLACKLIST_STATE, mock.state());
    267 }
    268 
    269