blob: 4e5d5fe63c5cf093bab127d73a876029cfb4ba9e [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/autofill/content/browser/email_verifier_delegate.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "components/autofill/core/browser/autofill_field.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/foundations/autofill_driver_test_api.h"
#include "components/autofill/core/browser/foundations/test_autofill_client.h"
#include "components/autofill/core/browser/foundations/test_autofill_driver.h"
#include "components/autofill/core/browser/foundations/test_browser_autofill_manager.h"
#include "components/autofill/core/browser/test_utils/autofill_form_test_utils.h"
#include "components/autofill/core/common/form_field_data.h"
#include "content/public/browser/webid/email_verifier.h"
#include "content/public/common/content_features.h"
#include "content/public/test/test_renderer_host.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace autofill {
namespace {
using ::testing::_;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::WithArgs;
class MockEmailVerifier : public content::webid::EmailVerifier {
public:
MOCK_METHOD(void,
Verify,
(const std::string&, const std::string&, OnEmailVerifiedCallback),
(override));
};
class MockAutofillDriver : public TestAutofillDriver {
public:
using TestAutofillDriver::TestAutofillDriver;
MOCK_METHOD(void,
DispatchEmailVerifiedEvent,
(FieldGlobalId field_id, const std::string& presentation_token),
(override));
};
FormData ValidForm() {
return test::GetFormData(
{.fields = {
{.label = u"Email",
.name = u"email",
.nonce = u"test_nonce",
.value = u"Triggering field (filled)",
.form_control_type = FormControlType::kInputEmail},
}});
}
} // namespace
class MockAutofillClient : public TestAutofillClient {
public:
MockAutofillClient() = default;
~MockAutofillClient() override = default;
MOCK_METHOD(void, ShowEmailVerifiedToast, (), (override));
};
class EmailVerifierDelegateTest : public testing::Test {
public:
void SetUp() override {
driver_ = std::make_unique<NiceMock<MockAutofillDriver>>(&client_);
manager_ = std::make_unique<TestBrowserAutofillManager>(driver_.get());
email_verifier_ = std::make_unique<NiceMock<MockEmailVerifier>>();
delegate_ = std::make_unique<EmailVerifierDelegate>(
&client_,
base::BindRepeating([](content::webid::EmailVerifier* verifier,
AutofillManager&) { return verifier; },
email_verifier_.get()));
}
void TearDown() override {
delegate_.reset();
email_verifier_.reset();
manager_.reset();
AutofillDriverTestApi(driver_.get())
.SetLifecycleState(AutofillDriver::LifecycleState::kPendingDeletion);
driver_.reset();
}
protected:
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
test::AutofillUnitTestEnvironment autofill_test_environment_;
MockAutofillClient client_;
std::unique_ptr<MockAutofillDriver> driver_;
std::unique_ptr<TestBrowserAutofillManager> manager_;
std::unique_ptr<MockEmailVerifier> email_verifier_;
std::unique_ptr<EmailVerifierDelegate> delegate_;
};
// Verifies that the success test case works as expected: the form conforms to
// all requirements, the user autofills an email field and the
// renderer is notified with the presentation token to dispatch an event.
TEST_F(EmailVerifierDelegateTest, VerificationTriggered) {
base::test::ScopedFeatureList feature_list{::features::kFedCmDelegation};
FormData form_data = ValidForm();
manager_->AddSeenForm(form_data, {EMAIL_ADDRESS});
FormStructure* form = manager_->FindCachedFormById(form_data.global_id());
ASSERT_TRUE(form);
form->field(0)->set_autofilled_type(EMAIL_ADDRESS);
EXPECT_CALL(*email_verifier_, Verify("test@example.com", "test_nonce", _))
.WillOnce(WithArgs<2>(
[](content::webid::EmailVerifier::OnEmailVerifiedCallback callback) {
std::move(callback).Run("test_token");
}));
EXPECT_CALL(*driver_, DispatchEmailVerifiedEvent(form->field(0)->global_id(),
"test_token"));
EXPECT_CALL(client_, ShowEmailVerifiedToast);
AutofillProfile profile = test::GetFullProfile();
profile.SetRawInfo(EMAIL_ADDRESS, u"test@example.com");
base::flat_set<FieldGlobalId> filled_field_ids = {
form->field(0)->global_id()};
delegate_->OnFillOrPreviewForm(*manager_, form->global_id(),
mojom::ActionPersistence::kFill,
filled_field_ids, &profile);
}
// Verifies that if the feature is disabled, no verification is triggered.
TEST_F(EmailVerifierDelegateTest, FeatureDisabled) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndDisableFeature(::features::kFedCmDelegation);
FormData form_data = ValidForm();
manager_->AddSeenForm(form_data, {EMAIL_ADDRESS});
FormStructure* form = manager_->FindCachedFormById(form_data.global_id());
ASSERT_TRUE(form);
EXPECT_CALL(*email_verifier_, Verify).Times(0);
EXPECT_CALL(*driver_, DispatchEmailVerifiedEvent).Times(0);
EXPECT_CALL(client_, ShowEmailVerifiedToast).Times(0);
base::flat_set<FieldGlobalId> filled_field_ids = {
form->field(0)->global_id()};
AutofillProfile profile = test::GetFullProfile();
delegate_->OnFillOrPreviewForm(*manager_, form->global_id(),
mojom::ActionPersistence::kFill,
filled_field_ids, &profile);
}
// Verifies that if the action is not "fill", no verification is triggered.
TEST_F(EmailVerifierDelegateTest, NotFillAction) {
base::test::ScopedFeatureList feature_list{::features::kFedCmDelegation};
FormData form_data = ValidForm();
manager_->AddSeenForm(form_data, {EMAIL_ADDRESS});
FormStructure* form = manager_->FindCachedFormById(form_data.global_id());
ASSERT_TRUE(form);
EXPECT_CALL(*email_verifier_, Verify).Times(0);
EXPECT_CALL(*driver_, DispatchEmailVerifiedEvent).Times(0);
EXPECT_CALL(client_, ShowEmailVerifiedToast).Times(0);
AutofillProfile profile = test::GetFullProfile();
base::flat_set<FieldGlobalId> filled_field_ids = {
form->field(0)->global_id()};
delegate_->OnFillOrPreviewForm(*manager_, form->global_id(),
mojom::ActionPersistence::kPreview,
filled_field_ids, &profile);
}
// Verifies that if the form isn't comformant (no nonce), no verification is
// triggered.
TEST_F(EmailVerifierDelegateTest, NoNonce) {
base::test::ScopedFeatureList feature_list{::features::kFedCmDelegation};
FormData form_data = test::GetFormData(
{.fields = {
{.role = EMAIL_ADDRESS,
.label = u"Email",
.name = u"email",
.value = u"Triggering field (filled)",
.form_control_type = FormControlType::kInputEmail},
}});
manager_->AddSeenForm(form_data, {EMAIL_ADDRESS});
FormStructure* form = manager_->FindCachedFormById(form_data.global_id());
ASSERT_TRUE(form);
EXPECT_CALL(*email_verifier_, Verify).Times(0);
EXPECT_CALL(*driver_, DispatchEmailVerifiedEvent).Times(0);
EXPECT_CALL(client_, ShowEmailVerifiedToast).Times(0);
AutofillProfile profile = test::GetFullProfile();
base::flat_set<FieldGlobalId> filled_field_ids = {
form->field(0)->global_id()};
delegate_->OnFillOrPreviewForm(*manager_, form->global_id(),
mojom::ActionPersistence::kFill,
filled_field_ids, &profile);
}
// Verifies that if the filled field is not an email field, no verification is
// triggered.
TEST_F(EmailVerifierDelegateTest, NotEmailField) {
base::test::ScopedFeatureList feature_list{::features::kFedCmDelegation};
FormData form_data =
test::GetFormData({.fields = {
{.label = u"Email",
.name = u"email",
.nonce = u"test_nonce",
.value = u"Triggering field (filled)"},
}});
manager_->AddSeenForm(form_data, {NAME_FULL});
FormStructure* form = manager_->FindCachedFormById(form_data.global_id());
ASSERT_TRUE(form);
EXPECT_CALL(*email_verifier_, Verify).Times(0);
EXPECT_CALL(*driver_, DispatchEmailVerifiedEvent).Times(0);
EXPECT_CALL(client_, ShowEmailVerifiedToast).Times(0);
AutofillProfile profile = test::GetFullProfile();
base::flat_set<FieldGlobalId> filled_field_ids = {
form->field(0)->global_id()};
delegate_->OnFillOrPreviewForm(*manager_, form->global_id(),
mojom::ActionPersistence::kFill,
filled_field_ids, &profile);
}
// Verifies that if the verification fails, no event is dispatched to the
// renderer.
TEST_F(EmailVerifierDelegateTest, VerificationFails) {
base::test::ScopedFeatureList feature_list{::features::kFedCmDelegation};
FormData form_data = ValidForm();
manager_->AddSeenForm(form_data, {EMAIL_ADDRESS});
FormStructure* form = manager_->FindCachedFormById(form_data.global_id());
ASSERT_TRUE(form);
form->field(0)->set_autofilled_type(EMAIL_ADDRESS);
EXPECT_CALL(*email_verifier_, Verify)
.WillOnce(WithArgs<2>(
[](content::webid::EmailVerifier::OnEmailVerifiedCallback callback) {
std::move(callback).Run(std::nullopt);
}));
// When the verification fails, the event is not dispatched.
EXPECT_CALL(*driver_, DispatchEmailVerifiedEvent).Times(0);
EXPECT_CALL(client_, ShowEmailVerifiedToast).Times(0);
AutofillProfile profile = test::GetFullProfile();
base::flat_set<FieldGlobalId> filled_field_ids = {
form->field(0)->global_id()};
delegate_->OnFillOrPreviewForm(*manager_, form->global_id(),
mojom::ActionPersistence::kFill,
filled_field_ids, &profile);
}
} // namespace autofill