| Index: ios/chrome/browser/passwords/credential_manager_unittest.mm
|
| diff --git a/ios/chrome/browser/passwords/credential_manager_unittest.mm b/ios/chrome/browser/passwords/credential_manager_unittest.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..4b7e97b79bd2f1c0f4da9a15884dd8c9bbd8f428
|
| --- /dev/null
|
| +++ b/ios/chrome/browser/passwords/credential_manager_unittest.mm
|
| @@ -0,0 +1,610 @@
|
| +// Copyright 2015 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 "ios/chrome/browser/passwords/credential_manager.h"
|
| +
|
| +#include <memory>
|
| +#include <utility>
|
| +
|
| +#include "base/mac/bind_objc_block.h"
|
| +#include "base/memory/ref_counted.h"
|
| +#include "base/strings/sys_string_conversions.h"
|
| +#include "base/strings/utf_string_conversions.h"
|
| +#include "base/values.h"
|
| +#include "components/password_manager/core/browser/password_bubble_experiment.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/browser/test_password_store.h"
|
| +#include "components/password_manager/core/common/credential_manager_types.h"
|
| +#include "components/password_manager/core/common/password_manager_pref_names.h"
|
| +#include "components/prefs/pref_registry_simple.h"
|
| +#include "components/prefs/testing_pref_service.h"
|
| +#import "ios/web/public/web_state/web_state.h"
|
| +#import "ios/chrome/browser/passwords/js_credential_manager.h"
|
| +#import "ios/testing/ocmock_complex_type_helper.h"
|
| +#import "ios/web/public/test/web_test_with_web_state.h"
|
| +#include "testing/gmock/include/gmock/gmock.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +#include "testing/gtest_mac.h"
|
| +#include "third_party/ocmock/OCMock/OCMock.h"
|
| +#include "third_party/ocmock/gtest_support.h"
|
| +#include "url/gurl.h"
|
| +
|
| +using testing::Return;
|
| +
|
| +namespace {
|
| +// Type of a function invoked when a promise is resolved.
|
| +typedef void (^ResolvePromiseBlock)(NSInteger request_id,
|
| + const web::Credential& credential);
|
| +} // namespace
|
| +
|
| +// A helper to mock methods that have C++ object parameters.
|
| +@interface MockJSCredentialManager : OCMockComplexTypeHelper
|
| +@end
|
| +
|
| +@implementation MockJSCredentialManager
|
| +- (void)resolvePromiseWithRequestID:(NSInteger)requestID
|
| + credential:(const web::Credential&)credential
|
| + completionHandler:(void (^)(BOOL))completionHandler {
|
| + static_cast<ResolvePromiseBlock>([self blockForSelector:_cmd])(requestID,
|
| + credential);
|
| + completionHandler(YES);
|
| +}
|
| +@end
|
| +
|
| +namespace {
|
| +
|
| +// Returns a test credential.
|
| +autofill::PasswordForm GetTestPasswordForm1(bool zero_click_allowed) {
|
| + autofill::PasswordForm form;
|
| + form.username_value = base::ASCIIToUTF16("foo");
|
| + form.password_value = base::ASCIIToUTF16("bar");
|
| + form.skip_zero_click = !zero_click_allowed;
|
| + form.ssl_valid = true;
|
| + form.type = autofill::PasswordForm::Type::TYPE_API;
|
| + return form;
|
| +}
|
| +
|
| +// Returns a test credential matching |GetTestPasswordForm1()|.
|
| +web::Credential GetTestWebCredential1(bool zero_click_allowed) {
|
| + web::Credential credential;
|
| + autofill::PasswordForm form(GetTestPasswordForm1(zero_click_allowed));
|
| + credential.type = web::CredentialType::CREDENTIAL_TYPE_PASSWORD;
|
| + credential.id = form.username_value;
|
| + credential.password = form.password_value;
|
| + return credential;
|
| +}
|
| +
|
| +// Returns a different test credential.
|
| +autofill::PasswordForm GetTestPasswordForm2(bool zero_click_allowed) {
|
| + autofill::PasswordForm form;
|
| + form.username_value = base::ASCIIToUTF16("baz");
|
| + form.password_value = base::ASCIIToUTF16("bah");
|
| + form.skip_zero_click = !zero_click_allowed;
|
| + form.ssl_valid = true;
|
| + return form;
|
| +}
|
| +
|
| +// Returns a test credential matching |GetTestPasswordForm2()|.
|
| +web::Credential GetTestWebCredential2(bool zero_click_allowed) {
|
| + web::Credential credential;
|
| + autofill::PasswordForm form(GetTestPasswordForm2(zero_click_allowed));
|
| + credential.type = web::CredentialType::CREDENTIAL_TYPE_PASSWORD;
|
| + credential.id = form.username_value;
|
| + credential.password = form.password_value;
|
| + return credential;
|
| +}
|
| +
|
| +typedef BOOL (^StringPredicate)(NSString*);
|
| +
|
| +// Returns a block that takes a string argument and returns whether it is equal
|
| +// to |string|.
|
| +StringPredicate EqualsString(const char* string) {
|
| + return [[^BOOL(NSString* other) {
|
| + return [base::SysUTF8ToNSString(string) isEqualToString:other];
|
| + } copy] autorelease];
|
| +}
|
| +
|
| +// A stub PasswordManagerClient for testing.
|
| +class StubPasswordManagerClient
|
| + : public password_manager::StubPasswordManagerClient {
|
| + public:
|
| + StubPasswordManagerClient()
|
| + : password_manager::StubPasswordManagerClient(),
|
| + password_store_(nullptr) {
|
| + prefs_.registry()->RegisterBooleanPref(
|
| + password_manager::prefs::kCredentialsEnableAutosignin, false);
|
| + password_bubble_experiment::RegisterPrefs(prefs_.registry());
|
| + }
|
| +
|
| + ~StubPasswordManagerClient() override {
|
| + if (password_store_)
|
| + password_store_->ShutdownOnUIThread();
|
| + }
|
| +
|
| + MOCK_CONST_METHOD0(IsSavingAndFillingEnabledForCurrentPage, bool());
|
| +
|
| + void SetPasswordStore(
|
| + scoped_refptr<password_manager::PasswordStore> password_store) {
|
| + password_store_ = password_store;
|
| + }
|
| +
|
| + password_manager::PasswordStore* GetPasswordStore() const override {
|
| + return password_store_.get();
|
| + }
|
| +
|
| + void SetUserChosenCredential(
|
| + const password_manager::CredentialInfo& credential) {
|
| + user_chosen_credential_ = credential;
|
| + }
|
| +
|
| + bool PromptUserToChooseCredentials(
|
| + ScopedVector<autofill::PasswordForm> local_forms,
|
| + ScopedVector<autofill::PasswordForm> federated_forms,
|
| + const GURL& origin,
|
| + const CredentialsCallback& callback) override {
|
| + return false;
|
| + }
|
| +
|
| + bool PromptUserToSaveOrUpdatePassword(
|
| + std::unique_ptr<password_manager::PasswordFormManager> form_to_save,
|
| + password_manager::CredentialSourceType type,
|
| + bool update_password) override {
|
| + if (!update_password)
|
| + saved_form_ = std::move(form_to_save);
|
| + return true;
|
| + }
|
| +
|
| + PrefService* GetPrefs() override { return &prefs_; }
|
| +
|
| + password_manager::PasswordFormManager* saved_form() {
|
| + return saved_form_.get();
|
| + }
|
| +
|
| + private:
|
| + // PrefService for testing.
|
| + TestingPrefServiceSimple prefs_;
|
| +
|
| + // PasswordStore for testing.
|
| + scoped_refptr<password_manager::PasswordStore> password_store_;
|
| +
|
| + // The password form shown to the user for saving.
|
| + std::unique_ptr<password_manager::PasswordFormManager> saved_form_;
|
| +
|
| + // The credential to be returned to callers of PromptUserToChooseCredentials.
|
| + password_manager::CredentialInfo user_chosen_credential_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(StubPasswordManagerClient);
|
| +};
|
| +
|
| +// Tests for CredentialManager.
|
| +class CredentialManagerTest : public web::WebTestWithWebState {
|
| + public:
|
| + CredentialManagerTest() {}
|
| + ~CredentialManagerTest() override {}
|
| +
|
| + void SetUp() override {
|
| + web::WebTestWithWebState::SetUp();
|
| + id originalMock =
|
| + [OCMockObject niceMockForClass:[JSCredentialManager class]];
|
| + mock_js_credential_manager_.reset([[MockJSCredentialManager alloc]
|
| + initWithRepresentedObject:originalMock]);
|
| + credential_manager_.reset(new CredentialManager(
|
| + web_state(), &stub_client_, &stub_driver_,
|
| + static_cast<id>(mock_js_credential_manager_.get())));
|
| + }
|
| +
|
| + // Sets up an expectation that the promise identified by |request_id| will be
|
| + // resolved with |credential|. |verified| must point to a variable that will
|
| + // be checked by the caller to ensure that the expectations were run. (This
|
| + // is necessary because OCMock doesn't handle methods with C++ object
|
| + // parameters.)
|
| + void ExpectPromiseResolved(bool* verified,
|
| + int request_id,
|
| + const web::Credential& credential) {
|
| + SEL selector =
|
| + @selector(resolvePromiseWithRequestID:credential:completionHandler:);
|
| + web::Credential strong_credential(credential);
|
| + [mock_js_credential_manager_
|
| + onSelector:selector
|
| + callBlockExpectation:^(NSInteger block_request_id,
|
| + const web::Credential& block_credential) {
|
| + EXPECT_EQ(request_id, block_request_id);
|
| + EXPECT_TRUE(CredentialsEqual(strong_credential, block_credential));
|
| + *verified = true;
|
| + }];
|
| + }
|
| +
|
| + // Same as |ExpectPromiseResolved(bool*, int, const web::Credential&)| but
|
| + // does not expect a credential to be passed.
|
| + void ExpectPromiseResolved(int request_id) {
|
| + [[[mock_js_credential_manager_ representedObject] expect]
|
| + resolvePromiseWithRequestID:request_id
|
| + completionHandler:nil];
|
| + }
|
| +
|
| + // Clears the expectations set up by |ExpectPromiseResolved()|.
|
| + void ClearPromiseResolutionExpectations() {
|
| + SEL selector =
|
| + @selector(resolvePromiseWithRequestID:credential:completionHandler:);
|
| + [mock_js_credential_manager_ removeBlockExpectationOnSelector:selector];
|
| + }
|
| +
|
| + // Sets up an expectation that the promise identified by |request_id| will be
|
| + // rejected with the given |error_type| and |message|. The caller should use
|
| + // EXPECT_OCMOCK_VERIFY to verify that the expectations were run.
|
| + void ExpectPromiseRejected(int request_id,
|
| + const char* error_type,
|
| + const char* message) {
|
| + [[[mock_js_credential_manager_ representedObject] expect]
|
| + rejectPromiseWithRequestID:request_id
|
| + errorType:[OCMArg
|
| + checkWithBlock:EqualsString(error_type)]
|
| + message:[OCMArg checkWithBlock:EqualsString(message)]
|
| + completionHandler:[OCMArg any]];
|
| + }
|
| +
|
| + protected:
|
| + // Mock for PasswordManagerClient.
|
| + StubPasswordManagerClient stub_client_;
|
| +
|
| + // Stub for PasswordManagerDriver.
|
| + password_manager::StubPasswordManagerDriver stub_driver_;
|
| +
|
| + // Mock for JSCredentialManager.
|
| + base::scoped_nsobject<MockJSCredentialManager> mock_js_credential_manager_;
|
| +
|
| + // CredentialManager for testing.
|
| + std::unique_ptr<CredentialManager> credential_manager_;
|
| +
|
| + private:
|
| + explicit CredentialManagerTest(const CredentialManagerTest&) = delete;
|
| + CredentialManagerTest& operator=(const CredentialManagerTest&) = delete;
|
| +};
|
| +
|
| +// Tests that a credential request is rejected properly when the PasswordStore
|
| +// is unavailable.
|
| +TEST_F(CredentialManagerTest, RequestRejectedWhenPasswordStoreUnavailable) {
|
| + // Clear the password store.
|
| + stub_client_.SetPasswordStore(nullptr);
|
| +
|
| + // Requesting a credential should reject the request with an error.
|
| + const int request_id = 0;
|
| + ExpectPromiseRejected(request_id,
|
| + kCredentialsPasswordStoreUnavailableErrorType,
|
| + kCredentialsPasswordStoreUnavailableErrorMessage);
|
| + credential_manager_->CredentialsRequested(request_id,
|
| + GURL("http://foo.com/login"), false,
|
| + std::vector<std::string>(), true);
|
| +
|
| + // Pump the message loop and verify.
|
| + WaitForBackgroundTasks();
|
| + EXPECT_OCMOCK_VERIFY([mock_js_credential_manager_ representedObject]);
|
| +}
|
| +
|
| +// Tests that a credential request is rejected when another request is pending.
|
| +TEST_F(CredentialManagerTest, RequestRejectedWhenExistingRequestIsPending) {
|
| + // Set a password store, but prevent requests from completing.
|
| + stub_client_.SetPasswordStore(new password_manager::TestPasswordStore);
|
| +
|
| + // Make an initial request. Don't pump the message loop, so that the task
|
| + // doesn't complete. Expect the request to resolve with an empty credential
|
| + // after the message loop is pumped.
|
| + const int first_request_id = 0;
|
| + bool first_verified = false;
|
| + ExpectPromiseResolved(&first_verified, first_request_id, web::Credential());
|
| + credential_manager_->CredentialsRequested(first_request_id,
|
| + GURL("http://foo.com/login"), false,
|
| + std::vector<std::string>(), true);
|
| +
|
| + // Making a second request and then pumping the message loop should reject the
|
| + // request with an error.
|
| + const int second_request_id = 0;
|
| + ExpectPromiseRejected(second_request_id, kCredentialsPendingRequestErrorType,
|
| + kCredentialsPendingRequestErrorMessage);
|
| + credential_manager_->CredentialsRequested(second_request_id,
|
| + GURL("http://foo.com/login"), false,
|
| + std::vector<std::string>(), true);
|
| +
|
| + // Pump the message loop and verify.
|
| + WaitForBackgroundTasks();
|
| + EXPECT_TRUE(first_verified);
|
| + EXPECT_OCMOCK_VERIFY([mock_js_credential_manager_ representedObject]);
|
| +}
|
| +
|
| +// Tests that a zero-click credential request is resolved properly with an empty
|
| +// credential when zero-click sign-in is disabled.
|
| +TEST_F(CredentialManagerTest,
|
| + ZeroClickRequestResolvedWithEmptyCredentialWhenZeroClickSignInDisabled) {
|
| + // Set a password store, but request a zero-click credential with zero-click
|
| + // disabled.
|
| + stub_client_.SetPasswordStore(new password_manager::TestPasswordStore);
|
| + const bool zero_click = true;
|
| + static_cast<TestingPrefServiceSimple*>(stub_client_.GetPrefs())
|
| + ->SetUserPref(password_manager::prefs::kCredentialsEnableAutosignin,
|
| + new base::FundamentalValue(!zero_click));
|
| +
|
| + // Requesting a zero-click credential should immediately resolve the request
|
| + // with an empty credential.
|
| + const int request_id = 0;
|
| + bool verified = false;
|
| + ExpectPromiseResolved(&verified, request_id, web::Credential());
|
| + credential_manager_->CredentialsRequested(
|
| + request_id, GURL("http://foo.com/login"), zero_click,
|
| + std::vector<std::string>(), true);
|
| +
|
| + // Pump the message loop and verify.
|
| + WaitForBackgroundTasks();
|
| + EXPECT_TRUE(verified);
|
| +}
|
| +
|
| +// Tests that a credential request is properly resolved with an empty credential
|
| +// when no credentials are available.
|
| +TEST_F(CredentialManagerTest,
|
| + RequestResolvedWithEmptyCredentialWhenNoneAvailable) {
|
| + // Set a password store with no credentials, enable zero-click, and request a
|
| + // zero-click credential.
|
| + stub_client_.SetPasswordStore(new password_manager::TestPasswordStore);
|
| + const bool zero_click = true;
|
| + static_cast<TestingPrefServiceSimple*>(stub_client_.GetPrefs())
|
| + ->SetUserPref(password_manager::prefs::kCredentialsEnableAutosignin,
|
| + new base::FundamentalValue(zero_click));
|
| +
|
| + // Requesting a zero-click credential should try to retrieve PasswordForms
|
| + // from the PasswordStore and resolve the request with an empty Credential
|
| + // when none are found.
|
| + const int request_id = 0;
|
| + bool verified = false;
|
| + ExpectPromiseResolved(&verified, request_id, web::Credential());
|
| + credential_manager_->CredentialsRequested(
|
| + request_id, GURL("http://foo.com/login"), zero_click,
|
| + std::vector<std::string>(), true);
|
| +
|
| + // Pump the message loop and verify.
|
| + WaitForBackgroundTasks();
|
| + EXPECT_TRUE(verified);
|
| +}
|
| +
|
| +// Tests that a zero-click credential request properly resolves.
|
| +TEST_F(CredentialManagerTest, ZeroClickRequestResolved) {
|
| + // Set a password store with a credential, enable zero-click, and request a
|
| + // zero-click credential.
|
| + scoped_refptr<password_manager::TestPasswordStore> store(
|
| + new password_manager::TestPasswordStore);
|
| + const bool zero_click = true;
|
| + store->AddLogin(GetTestPasswordForm1(zero_click));
|
| + stub_client_.SetPasswordStore(store);
|
| + static_cast<TestingPrefServiceSimple*>(stub_client_.GetPrefs())
|
| + ->SetUserPref(password_manager::prefs::kCredentialsEnableAutosignin,
|
| + new base::FundamentalValue(zero_click));
|
| + // Without the first run experience, the zero-click credentials won't be
|
| + // passed to the site.
|
| + static_cast<TestingPrefServiceSimple*>(stub_client_.GetPrefs())
|
| + ->SetUserPref(
|
| + password_manager::prefs::kWasAutoSignInFirstRunExperienceShown,
|
| + new base::FundamentalValue(true));
|
| + WaitForBackgroundTasks();
|
| +
|
| + // Requesting a zero-click credential should retrieve a PasswordForm from the
|
| + // PasswordStore and resolve the request with a corresponding Credential.
|
| + const int request_id = 0;
|
| + bool verified = false;
|
| + ExpectPromiseResolved(&verified, request_id,
|
| + GetTestWebCredential1(zero_click));
|
| + credential_manager_->CredentialsRequested(
|
| + request_id, GURL("http://foo.com/login"), zero_click,
|
| + std::vector<std::string>(), true);
|
| +
|
| + // Pump the message loop and verify.
|
| + WaitForBackgroundTasks();
|
| + EXPECT_TRUE(verified);
|
| +}
|
| +
|
| +// Tests that a credential request properly resolves.
|
| +// TODO(crbug.com/598851): Reenabled this test.
|
| +TEST_F(CredentialManagerTest, DISABLED_RequestResolved) {
|
| + // Set a password store with two credentials and set a credential to be
|
| + // returned by the PasswordManagerClient as if chosen by the user.
|
| + scoped_refptr<password_manager::TestPasswordStore> store(
|
| + new password_manager::TestPasswordStore);
|
| + const bool zero_click = false;
|
| + store->AddLogin(GetTestPasswordForm1(zero_click));
|
| + store->AddLogin(GetTestPasswordForm2(zero_click));
|
| + stub_client_.SetPasswordStore(store);
|
| + stub_client_.SetUserChosenCredential(password_manager::CredentialInfo(
|
| + GetTestPasswordForm2(zero_click),
|
| + password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD));
|
| + WaitForBackgroundTasks();
|
| +
|
| + // Request a credential. The request should be resolved with the credential
|
| + // set on the stub client.
|
| + const int request_id = 0;
|
| + bool verified = false;
|
| + ExpectPromiseResolved(&verified, request_id,
|
| + GetTestWebCredential2(zero_click));
|
| + credential_manager_->CredentialsRequested(
|
| + request_id, GURL("http://foo.com/login"), zero_click,
|
| + std::vector<std::string>(), true);
|
| +
|
| + // Pump the message loop and verify.
|
| + WaitForBackgroundTasks();
|
| + EXPECT_TRUE(verified);
|
| +}
|
| +
|
| +// Tests that two requests back-to-back succeed when they wait to be resolved.
|
| +// TODO(crbug.com/598851): Reenable this test.
|
| +TEST_F(CredentialManagerTest, DISABLED_ConsecutiveRequestsResolve) {
|
| + // Set a password store with two credentials and set a credential to be
|
| + // returned by the PasswordManagerClient as if chosen by the user.
|
| + scoped_refptr<password_manager::TestPasswordStore> store(
|
| + new password_manager::TestPasswordStore);
|
| + const bool zero_click = false;
|
| + store->AddLogin(GetTestPasswordForm1(zero_click));
|
| + stub_client_.SetPasswordStore(store);
|
| + stub_client_.SetUserChosenCredential(password_manager::CredentialInfo(
|
| + GetTestPasswordForm1(zero_click),
|
| + password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD));
|
| + WaitForBackgroundTasks();
|
| +
|
| + // Request a credential. The request should be resolved with the credential
|
| + // set on the stub client.
|
| + const int first_request_id = 0;
|
| + bool first_verified = false;
|
| + ExpectPromiseResolved(&first_verified, first_request_id,
|
| + GetTestWebCredential1(zero_click));
|
| + credential_manager_->CredentialsRequested(
|
| + first_request_id, GURL("http://foo.com/login"), zero_click,
|
| + std::vector<std::string>(), true);
|
| +
|
| + // Pump the message loop and verify.
|
| + WaitForBackgroundTasks();
|
| + EXPECT_TRUE(first_verified);
|
| +
|
| + ClearPromiseResolutionExpectations();
|
| +
|
| + // Make a second request. It should be resolved again.
|
| + const int second_request_id = 1;
|
| + bool second_verified = false;
|
| + ExpectPromiseResolved(&second_verified, second_request_id,
|
| + GetTestWebCredential1(zero_click));
|
| + credential_manager_->CredentialsRequested(
|
| + second_request_id, GURL("http://foo.com/login"), zero_click,
|
| + std::vector<std::string>(), true);
|
| +
|
| + // Pump the message loop and verify.
|
| + WaitForBackgroundTasks();
|
| + EXPECT_TRUE(second_verified);
|
| +}
|
| +
|
| +// Tests that notifySignedIn prompts the user to save a password.
|
| +TEST_F(CredentialManagerTest,
|
| + SignInResolvesAndPromptsUserWhenSavingEnabledAndNotBlacklisted) {
|
| + // Set a password store so the PasswordFormManager can retrieve credentials.
|
| + scoped_refptr<password_manager::TestPasswordStore> store(
|
| + new password_manager::TestPasswordStore);
|
| + stub_client_.SetPasswordStore(store);
|
| + const bool saving_enabled = true;
|
| + EXPECT_CALL(stub_client_, IsSavingAndFillingEnabledForCurrentPage())
|
| + .WillOnce(Return(saving_enabled))
|
| + .WillOnce(Return(saving_enabled));
|
| +
|
| + // Notify the browser that the user signed in.
|
| + const int request_id = 0;
|
| + ExpectPromiseResolved(request_id);
|
| + const bool zero_click = false;
|
| + credential_manager_->SignedIn(request_id, GURL("http://foo.com/login"),
|
| + GetTestWebCredential1(zero_click));
|
| +
|
| + // Pump the message loop and verify.
|
| + WaitForBackgroundTasks();
|
| + autofill::PasswordForm expected_observed_form(
|
| + GetTestPasswordForm1(zero_click));
|
| + expected_observed_form.username_value.clear();
|
| + expected_observed_form.password_value.clear();
|
| + EXPECT_EQ(expected_observed_form, stub_client_.saved_form()->observed_form());
|
| +}
|
| +
|
| +// Tests that notifySignedIn doesn't prompt the user to save a password when the
|
| +// password manager is disabled for the current page.
|
| +TEST_F(CredentialManagerTest,
|
| + SignInResolvesAndDoesNotPromptsUserWhenSavingDisabledAndNotBlacklisted) {
|
| + // Disable saving.
|
| + scoped_refptr<password_manager::TestPasswordStore> store(
|
| + new password_manager::TestPasswordStore);
|
| + stub_client_.SetPasswordStore(store);
|
| + const bool saving_enabled = false;
|
| + EXPECT_CALL(stub_client_, IsSavingAndFillingEnabledForCurrentPage())
|
| + .WillOnce(Return(saving_enabled));
|
| +
|
| + // Notify the browser that the user signed in.
|
| + const int request_id = 0;
|
| + ExpectPromiseResolved(request_id);
|
| + const bool zero_click = false;
|
| + credential_manager_->SignedIn(request_id, GURL("http://foo.com/login"),
|
| + GetTestWebCredential1(zero_click));
|
| +
|
| + // Pump the message loop and verify that no form was saved.
|
| + WaitForBackgroundTasks();
|
| + EXPECT_FALSE(stub_client_.saved_form());
|
| +}
|
| +
|
| +// Tests that notifySignedIn doesn't prompt the user to save a password when the
|
| +// submitted form is blacklisted by the password manager.
|
| +TEST_F(CredentialManagerTest,
|
| + SignInResolvesAndDoesNotPromptUserWhenSavingEnabledAndBlacklisted) {
|
| + // Disable saving.
|
| + scoped_refptr<password_manager::TestPasswordStore> store(
|
| + new password_manager::TestPasswordStore);
|
| + stub_client_.SetPasswordStore(store);
|
| + const bool saving_enabled = true;
|
| + EXPECT_CALL(stub_client_, IsSavingAndFillingEnabledForCurrentPage())
|
| + .WillOnce(Return(saving_enabled))
|
| + .WillOnce(Return(saving_enabled));
|
| +
|
| + // Save the credential that will be signed in and mark it blacklisted.
|
| + const bool zero_click = false;
|
| + autofill::PasswordForm blacklisted_form(GetTestPasswordForm1(zero_click));
|
| + blacklisted_form.blacklisted_by_user = true;
|
| + store->AddLogin(blacklisted_form);
|
| + WaitForBackgroundTasks();
|
| +
|
| + // Notify the browser that the user signed in.
|
| + const int request_id = 0;
|
| + ExpectPromiseResolved(request_id);
|
| + credential_manager_->SignedIn(request_id, GURL("http://foo.com/login"),
|
| + GetTestWebCredential1(zero_click));
|
| +
|
| + // Pump the message loop and verify that no form was saved.
|
| + WaitForBackgroundTasks();
|
| + EXPECT_FALSE(stub_client_.saved_form());
|
| +}
|
| +
|
| +// Tests that notifySignedOut marks credentials as non-zero-click.
|
| +TEST_F(CredentialManagerTest, SignOutResolvesAndMarksFormsNonZeroClick) {
|
| + // Create two zero-click credentials for the current page.
|
| + std::string current_origin(credential_manager_->GetOrigin().spec());
|
| + autofill::PasswordForm form1(GetTestPasswordForm1(true));
|
| + form1.signon_realm = current_origin;
|
| + autofill::PasswordForm form2(GetTestPasswordForm2(true));
|
| + form2.signon_realm = current_origin;
|
| +
|
| + // Add both credentials to the store.
|
| + scoped_refptr<password_manager::TestPasswordStore> store(
|
| + new password_manager::TestPasswordStore);
|
| + stub_client_.SetPasswordStore(store);
|
| + store->AddLogin(form1);
|
| + store->AddLogin(form2);
|
| + WaitForBackgroundTasks();
|
| +
|
| + // Check that both credentials in the store are zero-click.
|
| + EXPECT_EQ(1U, store->stored_passwords().size());
|
| + const std::vector<autofill::PasswordForm>& passwords_before_signout =
|
| + store->stored_passwords().find(current_origin)->second;
|
| + EXPECT_EQ(2U, passwords_before_signout.size());
|
| + for (const autofill::PasswordForm& form : passwords_before_signout)
|
| + EXPECT_FALSE(form.skip_zero_click);
|
| +
|
| + // Sign out the current origin, which has credentials, and a second origin,
|
| + // which doesnt.
|
| + const int first_request_id = 0;
|
| + ExpectPromiseResolved(first_request_id);
|
| + credential_manager_->SignedOut(first_request_id,
|
| + credential_manager_->GetOrigin());
|
| + const int second_request_id = 1;
|
| + ExpectPromiseResolved(second_request_id);
|
| + credential_manager_->SignedOut(second_request_id, GURL("https://foo.com"));
|
| +
|
| + // Pump the message loop to let the promises resolve. Check that the
|
| + // credentials in the store are now non-zero-click and that signing out an
|
| + // origin with no credentials had no effect.
|
| + WaitForBackgroundTasks();
|
| + EXPECT_EQ(1U, store->stored_passwords().size());
|
| + const std::vector<autofill::PasswordForm>& passwords_after_signout =
|
| + store->stored_passwords().find(current_origin)->second;
|
| + EXPECT_EQ(2U, passwords_after_signout.size());
|
| + for (const autofill::PasswordForm& form : passwords_after_signout)
|
| + EXPECT_TRUE(form.skip_zero_click);
|
| +}
|
| +
|
| +} // namespace
|
|
|