chromium/chrome/browser/password_manager/android/account_chooser_dialog_android_unittest.cc

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/password_manager/android/account_chooser_dialog_android.h"

#include "base/android/build_info.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/mock_callback.h"
#include "chrome/browser/password_manager/chrome_password_manager_client.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "components/device_reauth/device_authenticator.h"
#include "components/device_reauth/mock_device_authenticator.h"
#include "components/password_manager/core/browser/features/password_features.h"
#include "components/password_manager/core/browser/password_form.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_manager_test_utils.h"
#include "components/password_manager/core/browser/stub_password_manager_client.h"
#include "content/public/browser/visibility.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

using base::test::RunOnceCallback;
using device_reauth::MockDeviceAuthenticator;
using testing::_;
using testing::Eq;
using testing::Pointee;
using testing::Return;

password_manager::PasswordFormData kFormData1 = {
    password_manager::PasswordForm::Scheme::kHtml,
    "http://example.com/",
    "http://example.com/origin",
    "http://example.com/action",
    u"submit_element",
    u"username_element",
    u"password_element",
    u"",
    u"",
    true,
    1,
};

password_manager::PasswordFormData kFormData2 = {
    password_manager::PasswordForm::Scheme::kHtml,
    "http://test.com/",
    "http://test.com/origin",
    "http://test.com/action",
    u"submit_element",
    u"username_element",
    u"password_element",
    u"",
    u"",
    true,
    1,
};

class MockPasswordManagerClient
    : public password_manager::StubPasswordManagerClient {
 public:
  MOCK_METHOD(std::unique_ptr<device_reauth::DeviceAuthenticator>,
              GetDeviceAuthenticator,
              (),
              (override));
  MOCK_METHOD(bool,
              IsReauthBeforeFillingRequired,
              (device_reauth::DeviceAuthenticator*),
              (override));
};

}  // namespace

class AccountChooserDialogAndroidTest : public ChromeRenderViewHostTestHarness {
 public:
  AccountChooserDialogAndroidTest();

  AccountChooserDialogAndroidTest(const AccountChooserDialogAndroidTest&) =
      delete;
  AccountChooserDialogAndroidTest& operator=(
      const AccountChooserDialogAndroidTest&) = delete;

  ~AccountChooserDialogAndroidTest() override {}

  void SetUp() override;

 protected:
  AccountChooserDialogAndroid* CreateDialogManyAccounts();

  AccountChooserDialogAndroid* CreateDialog(
      std::vector<std::unique_ptr<password_manager::PasswordForm>> credentials);

  MockPasswordManagerClient client_;

  base::MockCallback<ManagePasswordsState::CredentialsCallback>
      credential_callback_;

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

AccountChooserDialogAndroidTest::AccountChooserDialogAndroidTest() {
  scoped_feature_list_.InitAndEnableFeature(
      password_manager::features::kBiometricTouchToFill);
}

void AccountChooserDialogAndroidTest::SetUp() {
  ChromeRenderViewHostTestHarness::SetUp();
}

AccountChooserDialogAndroid* AccountChooserDialogAndroidTest::CreateDialog(
    std::vector<std::unique_ptr<password_manager::PasswordForm>> credentials) {
  return new AccountChooserDialogAndroid(
      web_contents(), &client_, std::move(credentials),
      url::Origin::Create(GURL("https://example.com")),
      credential_callback_.Get());
}

AccountChooserDialogAndroid*
AccountChooserDialogAndroidTest::CreateDialogManyAccounts() {
  std::vector<std::unique_ptr<password_manager::PasswordForm>> credentials;
  credentials.push_back(
      FillPasswordFormWithData(kFormData1, /*is_account_store=*/false));
  credentials.push_back(
      FillPasswordFormWithData(kFormData2, /*is_account_store=*/false));
  return CreateDialog(std::move(credentials));
}

TEST_F(AccountChooserDialogAndroidTest, SendsCredentialIfAuthNotAvailable) {
  // Auth is required to fill passwords in Android automotive.
  if (base::android::BuildInfo::GetInstance()->is_automotive()) {
    GTEST_SKIP();
  }

  AccountChooserDialogAndroid* dialog = CreateDialogManyAccounts();

  auto authenticator = std::make_unique<MockDeviceAuthenticator>();

  EXPECT_CALL(client_, IsReauthBeforeFillingRequired).WillOnce(Return(false));
  EXPECT_CALL(client_, GetDeviceAuthenticator)
      .WillOnce(Return(testing::ByMove(std::move(authenticator))));
  std::unique_ptr<password_manager::PasswordForm> form =
      FillPasswordFormWithData(kFormData2, /*is_account_store=*/false);

  EXPECT_CALL(credential_callback_, Run(Pointee(*form.get())));

  dialog->OnCredentialClicked(base::android::AttachCurrentThread(),
                              nullptr /* obj */, 1 /* credential_item */,
                              false /* signin_button_clicked */);
}

TEST_F(AccountChooserDialogAndroidTest, SendsCredentialIfAuthSuccessful) {
  AccountChooserDialogAndroid* dialog = CreateDialogManyAccounts();

  auto authenticator = std::make_unique<MockDeviceAuthenticator>();

  ON_CALL(client_, IsReauthBeforeFillingRequired).WillByDefault(Return(true));
  EXPECT_CALL(*authenticator, AuthenticateWithMessage)
      .WillOnce(RunOnceCallback<1>(true));
  EXPECT_CALL(client_, GetDeviceAuthenticator)
      .WillOnce(Return(testing::ByMove(std::move(authenticator))));

  std::unique_ptr<password_manager::PasswordForm> form =
      FillPasswordFormWithData(kFormData2, /*is_account_store=*/false);
  EXPECT_CALL(credential_callback_, Run(Pointee(*form.get())));

  dialog->OnCredentialClicked(base::android::AttachCurrentThread(),
                              nullptr /* obj */, 1 /* credential_item */,
                              false /* signin_button_clicked */);
}

TEST_F(AccountChooserDialogAndroidTest, DoesntSendCredentialIfAuthFailed) {
  AccountChooserDialogAndroid* dialog = CreateDialogManyAccounts();

  auto authenticator = std::make_unique<MockDeviceAuthenticator>();

  ON_CALL(client_, IsReauthBeforeFillingRequired).WillByDefault(Return(true));
  EXPECT_CALL(*authenticator, AuthenticateWithMessage)
      .WillOnce(RunOnceCallback<1>(false));
  EXPECT_CALL(client_, GetDeviceAuthenticator)
      .WillOnce(Return(testing::ByMove(std::move(authenticator))));

  std::unique_ptr<password_manager::PasswordForm> form =
      FillPasswordFormWithData(kFormData2, /*is_account_store=*/false);
  EXPECT_CALL(credential_callback_, Run(nullptr));

  dialog->OnCredentialClicked(base::android::AttachCurrentThread(),
                              nullptr /* obj */, 1 /* credential_item */,
                              false /* signin_button_clicked */);
}

TEST_F(AccountChooserDialogAndroidTest, CancelsAuthIfDestroyed) {
  AccountChooserDialogAndroid* dialog = CreateDialogManyAccounts();

  auto authenticator = std::make_unique<MockDeviceAuthenticator>();
  auto* authenticator_ptr = authenticator.get();

  ON_CALL(client_, IsReauthBeforeFillingRequired).WillByDefault(Return(true));
  EXPECT_CALL(*authenticator_ptr, AuthenticateWithMessage);
  EXPECT_CALL(client_, GetDeviceAuthenticator)
      .WillOnce(Return(testing::ByMove(std::move(authenticator))));

  dialog->OnCredentialClicked(base::android::AttachCurrentThread(),
                              nullptr /* obj */, 1 /* credential_item */,
                              false /* signin_button_clicked */);

  EXPECT_CALL(*authenticator_ptr, Cancel());
  dialog->OnVisibilityChanged(content::Visibility::HIDDEN);
}