blob: e3e78f29fb1c3a9a809ba0d377d10c4b130ca066 [file] [log] [blame]
// Copyright 2022 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/ui/autofill/autofill_context_menu_manager.h"
#include <memory>
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/autofill/personal_data_manager_factory.h"
#include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "components/autofill/content/browser/content_autofill_driver_factory.h"
#include "components/autofill/content/browser/content_autofill_driver_factory_test_api.h"
#include "components/autofill/content/browser/test_autofill_client_injector.h"
#include "components/autofill/content/browser/test_autofill_driver_injector.h"
#include "components/autofill/content/browser/test_content_autofill_client.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/test_autofill_driver.h"
#include "components/autofill/core/browser/test_personal_data_manager.h"
#include "components/autofill/core/common/autofill_features.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/context_menu/context_menu.mojom.h"
#include "ui/base/l10n/l10n_util.h"
using testing::_;
namespace autofill {
namespace {
// Generates a ContextMenuParams for the Autofill context menu options.
content::ContextMenuParams CreateContextMenuParams(
absl::optional<autofill::FormRendererId> form_renderer_id = absl::nullopt,
autofill::FieldRendererId field_render_id = autofill::FieldRendererId(0)) {
content::ContextMenuParams rv;
rv.is_editable = true;
rv.page_url = GURL("http://test.page/");
rv.input_field_type = blink::mojom::ContextMenuDataInputFieldType::kPlainText;
if (form_renderer_id)
rv.form_renderer_id = form_renderer_id->value();
rv.field_renderer_id = field_render_id.value();
return rv;
}
class MockAutofillDriver : public ContentAutofillDriver {
public:
using ContentAutofillDriver::ContentAutofillDriver;
// Mock methods to enable testability.
MOCK_METHOD(void,
RendererShouldFillFieldWithValue,
(const FieldGlobalId& field_id, const std::u16string& value),
(override));
MOCK_METHOD(void,
OnContextMenuShownInField,
(const FormGlobalId& form_global_id,
const FieldGlobalId& field_global_id),
(override));
};
} // namespace
class AutofillContextMenuManagerTest : public ChromeRenderViewHostTestHarness {
public:
AutofillContextMenuManagerTest() {
feature_.InitWithFeatures(
{features::kAutofillShowManualFallbackInContextMenu,
features::kAutofillFeedback},
{});
}
AutofillContextMenuManagerTest(const AutofillContextMenuManagerTest&) =
delete;
AutofillContextMenuManagerTest& operator=(
const AutofillContextMenuManagerTest&) = delete;
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
PersonalDataManagerFactory::GetInstance()->SetTestingFactory(
profile(), BrowserContextKeyedServiceFactory::TestingFactory());
NavigateAndCommit(GURL("about:blank"));
autofill_client()->GetPersonalDataManager()->SetPrefService(
profile()->GetPrefs());
autofill_client()->GetPersonalDataManager()->AddProfile(
test::GetFullProfile());
autofill_client()->GetPersonalDataManager()->AddCreditCard(
test::GetCreditCard());
menu_model_ = std::make_unique<ui::SimpleMenuModel>(nullptr);
render_view_context_menu_ = std::make_unique<TestRenderViewContextMenu>(
*main_rfh(), content::ContextMenuParams());
render_view_context_menu_->Init();
autofill_context_menu_manager_ =
std::make_unique<AutofillContextMenuManager>(
autofill_client()->GetPersonalDataManager(),
render_view_context_menu_.get(), menu_model_.get(), nullptr,
std::make_unique<ScopedNewBadgeTracker>(profile()));
autofill_context_menu_manager()->set_params_for_testing(
CreateContextMenuParams());
}
void TearDown() override {
autofill_context_menu_manager_.reset();
render_view_context_menu_.reset();
ChromeRenderViewHostTestHarness::TearDown();
}
protected:
TestContentAutofillClient* autofill_client() {
return autofill_client_injector_[web_contents()];
}
MockAutofillDriver* driver() { return autofill_driver_injector_[main_rfh()]; }
ui::SimpleMenuModel* menu_model() const { return menu_model_.get(); }
AutofillContextMenuManager* autofill_context_menu_manager() const {
return autofill_context_menu_manager_.get();
}
private:
TestAutofillClientInjector<TestContentAutofillClient>
autofill_client_injector_;
TestAutofillDriverInjector<MockAutofillDriver> autofill_driver_injector_;
std::unique_ptr<TestRenderViewContextMenu> render_view_context_menu_;
std::unique_ptr<ui::SimpleMenuModel> menu_model_;
std::unique_ptr<AutofillContextMenuManager> autofill_context_menu_manager_;
base::test::ScopedFeatureList feature_;
test::AutofillUnitTestEnvironment autofill_test_environment_;
};
// Tests that the Autofill context menu is correctly set up.
TEST_F(AutofillContextMenuManagerTest, AutofillContextMenuContents) {
autofill_context_menu_manager()->AppendItems();
std::vector<std::u16string> all_added_strings;
// Check for top level menu with autofill options.
ASSERT_EQ(5u, menu_model()->GetItemCount());
ASSERT_EQ(u"Fill Address Info", menu_model()->GetLabelAt(0));
ASSERT_EQ(u"Fill Payment", menu_model()->GetLabelAt(1));
ASSERT_EQ(l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_AUTOFILL_FEEDBACK),
menu_model()->GetLabelAt(3));
ASSERT_EQ(menu_model()->GetTypeAt(0), ui::MenuModel::ItemType::TYPE_SUBMENU);
ASSERT_EQ(menu_model()->GetTypeAt(1), ui::MenuModel::ItemType::TYPE_SUBMENU);
ASSERT_EQ(menu_model()->GetTypeAt(2),
ui::MenuModel::ItemType::TYPE_SEPARATOR);
ASSERT_EQ(menu_model()->GetTypeAt(3), ui::MenuModel::ItemType::TYPE_COMMAND);
ASSERT_EQ(menu_model()->GetTypeAt(4),
ui::MenuModel::ItemType::TYPE_SEPARATOR);
// Check for submenu with address descriptions.
auto* address_menu_model = menu_model()->GetSubmenuModelAt(0);
ASSERT_EQ(address_menu_model->GetItemCount(), 3u);
ASSERT_EQ(u"John H. Doe, 666 Erebus St.", address_menu_model->GetLabelAt(0));
ASSERT_EQ(address_menu_model->GetTypeAt(0),
ui::MenuModel::ItemType::TYPE_SUBMENU);
ASSERT_EQ(address_menu_model->GetTypeAt(1),
ui::MenuModel::ItemType::TYPE_SEPARATOR);
ASSERT_EQ(u"Manage addresses", address_menu_model->GetLabelAt(2));
// Check for submenu with address details.
auto* address_details_submenu = address_menu_model->GetSubmenuModelAt(0);
ASSERT_EQ(address_details_submenu->GetItemCount(), 10u);
static constexpr std::array expected_address_values = {
u"John H. Doe",
u"",
u"666 Erebus St.\nApt 8",
u"Elysium",
u"91111",
u"",
u"16502111111",
u"johndoe@hades.com",
u"",
u"Other"};
for (size_t i = 0; i < expected_address_values.size(); i++) {
SCOPED_TRACE(testing::Message() << "Index " << i);
ASSERT_EQ(address_details_submenu->GetLabelAt(i),
expected_address_values[i]);
all_added_strings.push_back(expected_address_values[i]);
}
// Check for submenu with address other section.
auto* address_other_submenu = address_details_submenu->GetSubmenuModelAt(9);
ASSERT_EQ(address_other_submenu->GetItemCount(), 5u);
static constexpr std::array expected_address_other_section_values = {
u"John", u"Doe", u"", u"666 Erebus St.", u"Apt 8"};
for (size_t i = 0; i < expected_address_other_section_values.size(); i++) {
SCOPED_TRACE(testing::Message() << "Index " << i);
ASSERT_EQ(address_other_submenu->GetLabelAt(i),
expected_address_other_section_values[i]);
all_added_strings.push_back(expected_address_other_section_values[i]);
}
// Check for submenu with credit card descriptions.
auto* card_menu_model = menu_model()->GetSubmenuModelAt(1);
ASSERT_EQ(card_menu_model->GetItemCount(), 3u);
ASSERT_EQ(
u"Visa "
u"\x202A\x2022\x2060\x2006\x2060\x2022\x2060\x2006\x2060\x2022\x2060"
u"\x2006\x2060\x2022\x2060\x2006\x2060"
u"1111\x202C",
card_menu_model->GetLabelAt(0));
ASSERT_EQ(card_menu_model->GetTypeAt(0),
ui::MenuModel::ItemType::TYPE_SUBMENU);
ASSERT_EQ(card_menu_model->GetTypeAt(1),
ui::MenuModel::ItemType::TYPE_SEPARATOR);
ASSERT_EQ(u"Manage payment methods", card_menu_model->GetLabelAt(2));
// Check for submenu with credit card details.
auto* card_details_submenu = card_menu_model->GetSubmenuModelAt(0);
ASSERT_EQ(card_details_submenu->GetItemCount(), 5u);
static constexpr std::array expected_credit_card_values = {
u"Test User",
u"‪•⁠ ⁠•⁠ ⁠•⁠ ⁠•⁠ ⁠1111‬", u""};
for (size_t i = 0; i < expected_credit_card_values.size(); i++) {
SCOPED_TRACE(testing::Message() << "Index " << i);
ASSERT_EQ(card_details_submenu->GetLabelAt(i),
expected_credit_card_values[i]);
all_added_strings.push_back(expected_credit_card_values[i]);
}
all_added_strings.push_back(base::ASCIIToUTF16(test::NextMonth().c_str()));
ASSERT_EQ(card_details_submenu->GetLabelAt(3), all_added_strings.back());
all_added_strings.push_back(
base::ASCIIToUTF16(test::NextYear().c_str()).substr(2));
ASSERT_EQ(card_details_submenu->GetLabelAt(4), all_added_strings.back());
// Test all strings added to the command_id_to_menu_item_value_mapper were
// added to the context menu.
auto mapper = autofill_context_menu_manager()
->command_id_to_menu_item_value_mapper_for_testing();
base::ranges::sort(all_added_strings);
EXPECT_TRUE(base::ranges::all_of(mapper, [&](const auto& p) {
return base::Contains(all_added_strings, p.second.fill_value);
}));
}
// For all the command ids that are used to set up the context menu, initiating
// filling for each one of them results in the call to
// `RendererShouldFillFieldWithValue`.
TEST_F(AutofillContextMenuManagerTest, ExecuteCommand) {
DCHECK(driver());
autofill_context_menu_manager()->AppendItems();
auto mapper = autofill_context_menu_manager()
->command_id_to_menu_item_value_mapper_for_testing();
ASSERT_FALSE(mapper.empty());
for (auto const& [command_id, map_value] : mapper) {
// Requires a browser instance which is not available in this test.
if (map_value.is_manage_item)
continue;
SCOPED_TRACE(testing::Message() << "Command " << *command_id);
FieldRendererId field_renderer_id(test::MakeFieldRendererId());
FieldGlobalId field_global_id{
LocalFrameToken(main_rfh()->GetFrameToken().value()),
field_renderer_id};
autofill_context_menu_manager()->set_params_for_testing(
CreateContextMenuParams(absl::nullopt, field_renderer_id));
EXPECT_CALL(*driver(), RendererShouldFillFieldWithValue(
field_global_id, map_value.fill_value));
autofill_context_menu_manager()->ExecuteCommand(command_id);
}
}
// Tests that the Autofill's ContentAutofillDriver is called to record metrics
// when the context menu is triggered on a field.
TEST_F(AutofillContextMenuManagerTest, RecordContextMenuIsShownOnField) {
FormRendererId form_renderer_id(test::MakeFormRendererId());
FieldRendererId field_renderer_id(test::MakeFieldRendererId());
autofill_context_menu_manager()->set_params_for_testing(
CreateContextMenuParams(form_renderer_id, field_renderer_id));
FormGlobalId form_global_id{
LocalFrameToken(main_rfh()->GetFrameToken().value()), form_renderer_id};
FieldGlobalId field_global_id{
LocalFrameToken(main_rfh()->GetFrameToken().value()), field_renderer_id};
EXPECT_CALL(*driver(),
OnContextMenuShownInField(form_global_id, field_global_id));
autofill_context_menu_manager()->AppendItems();
}
} // namespace autofill