Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(69)

Unified Diff: ios/chrome/browser/passwords/credential_manager_unittest.mm

Issue 2152593002: Upstream password manager unit tests (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix GYP Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698