blob: 1d4cb76a63f00f91b065918152e84aea36adf914 [file]
// Copyright 2020 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/core/browser/test_utils/autofill_form_test_utils.h"
#include <string_view>
#include "base/containers/to_vector.h"
#include "components/autofill/core/browser/country_type.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/form_parsing/determine_regex_types.h"
#include "components/autofill/core/browser/form_qualifiers.h"
#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/browser/test_utils/autofill_test_utils.h"
#include "components/autofill/core/common/autocomplete_parsing_util.h"
namespace autofill::test {
namespace {
using ::testing::ElementsAre;
using ::testing::Message;
constexpr std::string_view kDefaultTestOrigin = "https://example.test/";
} // namespace
Message DescribeFormData(const FormData& form_data) {
Message result;
result << "Form contains " << form_data.fields().size() << " fields:\n";
for (const FormFieldData& field : form_data.fields()) {
result << "type=" << FormControlTypeToString(field.form_control_type())
<< ", name=" << field.name() << ", label=" << field.label() << "\n";
}
return result;
}
// Returns the form field relevant to the `role`.
FormFieldData CreateFieldByRole(FieldType role) {
DCHECK(role != FieldType::MAX_VALID_FIELD_TYPE)
<< "MAX_VALID_FIELD_TYPE is not a valid field type.";
FormFieldData field;
// Set the name and label of the field based on the `role`.
switch (role) {
case FieldType::USERNAME:
field.set_name(u"username");
field.set_label(u"Username");
break;
case FieldType::NAME_FULL:
field.set_name(u"name");
field.set_label(u"Name");
break;
case FieldType::NAME_FIRST:
field.set_name(u"firstName");
field.set_label(u"First name");
break;
case FieldType::NAME_LAST:
field.set_name(u"lastName");
field.set_label(u"Last name");
break;
case FieldType::EMAIL_ADDRESS:
field.set_name(u"email");
field.set_label(u"E-mail address");
break;
case FieldType::ADDRESS_HOME_LINE1:
field.set_name(u"home_line_one");
field.set_label(u"Address line 1");
break;
case FieldType::ADDRESS_HOME_CITY:
field.set_name(u"city");
field.set_label(u"City");
break;
case FieldType::ADDRESS_HOME_STATE:
field.set_name(u"state");
field.set_label(u"State");
break;
case FieldType::ADDRESS_HOME_COUNTRY:
field.set_name(u"country");
field.set_label(u"Country");
break;
case FieldType::ADDRESS_HOME_ZIP:
field.set_name(u"zipCode");
field.set_label(u"ZIP code");
break;
case FieldType::PHONE_HOME_NUMBER:
field.set_name(u"phone");
field.set_label(u"Phone");
break;
case FieldType::COMPANY_NAME:
field.set_name(u"company");
field.set_label(u"Company");
break;
case FieldType::CREDIT_CARD_NUMBER:
field.set_name(u"cardNumber");
field.set_label(u"Credit card number");
break;
case FieldType::PASSWORD:
field.set_name(u"password");
field.set_label(u"Password");
break;
case FieldType::ADDRESS_HOME_ADMIN_LEVEL2:
field.set_name(u"admin_level2");
field.set_label(u"Admin level 2");
break;
case FieldType::ADDRESS_HOME_APT:
field.set_name(u"apartment");
field.set_label(u"Apartment");
break;
case FieldType::ADDRESS_HOME_APT_NUM:
field.set_name(u"apt_num");
field.set_label(u"Apt/Suite");
break;
case FieldType::ADDRESS_HOME_BETWEEN_STREETS:
field.set_name(u"between_streets");
field.set_label(u"Between streets");
break;
case FieldType::ADDRESS_HOME_BETWEEN_STREETS_1:
field.set_name(u"between_streets_1");
field.set_label(u"Between streets 1");
break;
case FieldType::ADDRESS_HOME_BETWEEN_STREETS_2:
field.set_name(u"between_streets_2");
field.set_label(u"Between streets 2");
break;
case FieldType::ADDRESS_HOME_BETWEEN_STREETS_OR_LANDMARK:
field.set_name(u"between_streets_or_landmark");
field.set_label(u"Between streets or landmark");
break;
case FieldType::ADDRESS_HOME_DEPENDENT_LOCALITY:
field.set_name(u"dependent_locality");
field.set_label(u"Dependent locality");
break;
case FieldType::ADDRESS_HOME_HOUSE_NUMBER:
field.set_name(u"house_number");
field.set_label(u"House number");
break;
case FieldType::ADDRESS_HOME_HOUSE_NUMBER_AND_APT:
field.set_name(u"house_number_and_apt");
field.set_label(u"House number and apt");
break;
case FieldType::ADDRESS_HOME_LANDMARK:
field.set_name(u"landmark");
field.set_label(u"Landmark");
break;
case FieldType::ADDRESS_HOME_LINE2:
field.set_name(u"address2");
field.set_label(u"Address line 2");
break;
case FieldType::ADDRESS_HOME_LINE3:
field.set_name(u"address3");
field.set_label(u"Address line 3");
break;
case FieldType::ADDRESS_HOME_OVERFLOW:
field.set_name(u"overflow");
field.set_label(u"Overflow");
break;
case FieldType::ADDRESS_HOME_OVERFLOW_AND_LANDMARK:
field.set_name(u"overflow_and_landmark");
field.set_label(u"Overflow and landmark");
break;
case FieldType::ADDRESS_HOME_STREET_ADDRESS:
field.set_name(u"street_address");
field.set_label(u"Street address");
break;
case FieldType::ADDRESS_HOME_STREET_LOCATION:
field.set_name(u"street_location");
field.set_label(u"Street location");
break;
case FieldType::ADDRESS_HOME_STREET_NAME:
field.set_name(u"street_name");
field.set_label(u"Street name");
break;
case FieldType::ALTERNATIVE_FAMILY_NAME:
field.set_name(u"alt_family_name");
field.set_label(u"Alternative family name");
break;
case FieldType::ALTERNATIVE_FULL_NAME:
field.set_name(u"alt_full_name");
field.set_label(u"Alternative full name");
break;
case FieldType::ALTERNATIVE_GIVEN_NAME:
field.set_name(u"alternative_given_name");
field.set_label(u"Alternative given name");
break;
case FieldType::CREDIT_CARD_EXP_2_DIGIT_YEAR:
field.set_name(u"exp_year_2_digit");
field.set_label(u"Credit card expiration year 2-digit");
break;
case FieldType::CREDIT_CARD_EXP_4_DIGIT_YEAR:
field.set_name(u"cc_exp_year_4_digit");
field.set_label(u"Credit card expiration year 4-digit");
break;
case FieldType::CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR:
field.set_name(u"cc_exp_date_2_digit_year");
field.set_label(u"Credit card expiration date 2-digit year");
break;
case FieldType::CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR:
field.set_name(u"cc_exp_date_4_digit_year");
field.set_label(u"Credit card expiration date 4-digit year");
break;
case FieldType::CREDIT_CARD_EXP_MONTH:
field.set_name(u"cc_exp_month");
field.set_label(u"Credit card expiration month");
break;
case FieldType::CREDIT_CARD_NAME_FIRST:
field.set_name(u"cc_name_first");
field.set_label(u"Credit card first name");
break;
case FieldType::CREDIT_CARD_NAME_FULL:
field.set_name(u"cc_name_full");
field.set_label(u"Credit card full name");
break;
case FieldType::CREDIT_CARD_NAME_LAST:
field.set_name(u"cc_name_last");
field.set_label(u"Credit card last name");
break;
case FieldType::CREDIT_CARD_STANDALONE_VERIFICATION_CODE:
field.set_name(u"cc_csc_standalone");
field.set_label(u"Credit card verification code standalone");
break;
case FieldType::CREDIT_CARD_TYPE:
field.set_name(u"cc_type");
field.set_label(u"Credit card type");
break;
case FieldType::CREDIT_CARD_VERIFICATION_CODE:
field.set_name(u"cc_csc");
field.set_label(u"Credit card verification code");
break;
case FieldType::IBAN_VALUE:
field.set_name(u"iban");
field.set_label(u"IBAN");
break;
case FieldType::EMAIL_OR_LOYALTY_MEMBERSHIP_ID:
field.set_name(u"email_or_loyalty_card_number");
field.set_label(u"email or loyalty card number");
break;
case FieldType::LOYALTY_MEMBERSHIP_ID:
field.set_name(u"loyalty_card_number");
field.set_label(u"loyalty card number");
break;
case FieldType::MERCHANT_PROMO_CODE:
field.set_name(u"promo_code");
field.set_label(u"Promo code");
break;
case FieldType::NAME_HONORIFIC_PREFIX:
field.set_name(u"honorific_prefix");
field.set_label(u"Honorific prefix");
break;
case FieldType::NAME_LAST_FIRST:
field.set_name(u"lname_first");
field.set_label(u"Last name first");
break;
case FieldType::NAME_LAST_SECOND:
field.set_name(u"lname_second");
field.set_label(u"Last name second");
break;
case FieldType::NAME_MIDDLE:
field.set_name(u"mname");
field.set_label(u"Middle name");
break;
case FieldType::NAME_MIDDLE_INITIAL:
field.set_name(u"minitial");
field.set_label(u"Middle initial");
break;
case FieldType::NUMERIC_QUANTITY:
field.set_name(u"numeric_quantity");
field.set_label(u"Numeric quantity");
break;
case FieldType::PHONE_HOME_CITY_CODE:
field.set_name(u"phone_city_code");
field.set_label(u"Phone city code");
break;
case FieldType::PHONE_HOME_COUNTRY_CODE:
field.set_name(u"phone_country_code");
field.set_label(u"Phone country code");
break;
case FieldType::PHONE_HOME_EXTENSION:
field.set_name(u"phone_ext");
field.set_label(u"Phone extension");
break;
case FieldType::PHONE_HOME_NUMBER_PREFIX:
field.set_name(u"phone_prefix");
field.set_label(u"Phone number prefix");
break;
case FieldType::PHONE_HOME_NUMBER_SUFFIX:
field.set_name(u"phone_suffix");
field.set_label(u"Phone number suffix");
break;
case FieldType::PHONE_HOME_WHOLE_NUMBER:
case FieldType::PHONE_HOME_CITY_AND_NUMBER:
case FieldType::PHONE_HOME_CITY_AND_NUMBER_WITHOUT_TRUNK_PREFIX:
field.set_label(u"Phone Number");
field.set_name(u"phone_number");
break;
case FieldType::PRICE:
field.set_name(u"price");
field.set_label(u"Price");
break;
case FieldType::SEARCH_TERM:
field.set_name(u"search");
field.set_label(u"Search");
break;
case FieldType::UNKNOWN_TYPE:
case FieldType::EMPTY_TYPE:
break;
case FieldType::NOT_NEW_PASSWORD:
case FieldType::NO_SERVER_DATA:
case FieldType::PASSPORT_NUMBER:
case FieldType::PASSPORT_ISSUING_COUNTRY:
case FieldType::PASSPORT_EXPIRATION_DATE:
case FieldType::PASSPORT_ISSUE_DATE:
case FieldType::LOYALTY_MEMBERSHIP_PROGRAM:
case FieldType::LOYALTY_MEMBERSHIP_PROVIDER:
case FieldType::VEHICLE_LICENSE_PLATE:
case FieldType::VEHICLE_VIN:
case FieldType::VEHICLE_MAKE:
case FieldType::VEHICLE_MODEL:
case FieldType::DRIVERS_LICENSE_REGION:
case FieldType::DRIVERS_LICENSE_NUMBER:
case FieldType::DRIVERS_LICENSE_EXPIRATION_DATE:
case FieldType::DRIVERS_LICENSE_ISSUE_DATE:
case FieldType::VEHICLE_YEAR:
case FieldType::VEHICLE_PLATE_STATE:
case FieldType::NATIONAL_ID_CARD_NUMBER:
case FieldType::NATIONAL_ID_CARD_EXPIRATION_DATE:
case FieldType::NATIONAL_ID_CARD_ISSUE_DATE:
case FieldType::NATIONAL_ID_CARD_ISSUING_COUNTRY:
case FieldType::KNOWN_TRAVELER_NUMBER:
case FieldType::KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE:
case FieldType::REDRESS_NUMBER:
case FieldType::FLIGHT_RESERVATION_FLIGHT_NUMBER:
case FieldType::FLIGHT_RESERVATION_CONFIRMATION_CODE:
case FieldType::FLIGHT_RESERVATION_TICKET_NUMBER:
case FieldType::FLIGHT_RESERVATION_DEPARTURE_DATE:
case FieldType::NAME_SUFFIX:
case FieldType::MERCHANT_EMAIL_SIGNUP:
case FieldType::ACCOUNT_CREATION_PASSWORD:
case FieldType::ADDRESS_HOME_SORTING_CODE:
case FieldType::NOT_ACCOUNT_CREATION_PASSWORD:
case FieldType::USERNAME_AND_EMAIL_ADDRESS:
case FieldType::NEW_PASSWORD:
case FieldType::PROBABLY_NEW_PASSWORD:
case FieldType::NOT_PASSWORD:
case FieldType::CONFIRMATION_PASSWORD:
case FieldType::AMBIGUOUS_TYPE:
case FieldType::SINGLE_USERNAME_WITH_INTERMEDIATE_VALUES:
case FieldType::ADDRESS_HOME_ZIP_PREFIX:
case FieldType::ADDRESS_HOME_ZIP_SUFFIX:
case FieldType::ADDRESS_HOME_ZIP_AND_CITY:
case FieldType::SINGLE_USERNAME:
case FieldType::NOT_USERNAME:
case FieldType::ADDRESS_HOME_SUBPREMISE:
case FieldType::ADDRESS_HOME_OTHER_SUBUNIT:
case FieldType::NAME_LAST_CONJUNCTION:
case FieldType::ADDRESS_HOME_ADDRESS:
case FieldType::ADDRESS_HOME_ADDRESS_WITH_NAME:
case FieldType::ADDRESS_HOME_FLOOR:
case FieldType::PHONE_HOME_CITY_CODE_WITH_TRUNK_PREFIX:
case FieldType::ONE_TIME_CODE:
case FieldType::DELIVERY_INSTRUCTIONS:
case FieldType::ADDRESS_HOME_STREET_LOCATION_AND_LOCALITY:
case FieldType::ADDRESS_HOME_STREET_LOCATION_AND_LANDMARK:
case FieldType::ADDRESS_HOME_DEPENDENT_LOCALITY_AND_LANDMARK:
case FieldType::SINGLE_USERNAME_FORGOT_PASSWORD:
case FieldType::ADDRESS_HOME_APT_TYPE:
case FieldType::ORDER_ID:
case FieldType::ORDER_DATE:
case FieldType::ORDER_MERCHANT_NAME:
case FieldType::SHIPMENT_TRACKING_NUMBER:
case FieldType::MAX_VALID_FIELD_TYPE:
LOG(ERROR) << "The field created by " << __func__ << "("
<< FieldTypeToStringView(role)
<< ") will not get a name and label assigned";
break;
}
// Provide a warning if the role type is not predictable by the local
// heuristics.
switch (role) {
// The following types are predicted by the local heuristics.
case FieldType::NAME_FULL:
case FieldType::NAME_FIRST:
case FieldType::NAME_LAST:
case FieldType::EMAIL_ADDRESS:
case FieldType::ADDRESS_HOME_LINE1:
case FieldType::ADDRESS_HOME_CITY:
case FieldType::ADDRESS_HOME_STATE:
case FieldType::ADDRESS_HOME_COUNTRY:
case FieldType::ADDRESS_HOME_ZIP:
case FieldType::PHONE_HOME_NUMBER:
case FieldType::COMPANY_NAME:
case FieldType::CREDIT_CARD_NUMBER:
case FieldType::PASSWORD:
case FieldType::ADDRESS_HOME_ADMIN_LEVEL2:
case FieldType::ADDRESS_HOME_APT:
case FieldType::ADDRESS_HOME_APT_NUM:
case FieldType::ADDRESS_HOME_BETWEEN_STREETS:
case FieldType::ADDRESS_HOME_BETWEEN_STREETS_1:
case FieldType::ADDRESS_HOME_BETWEEN_STREETS_2:
case FieldType::ADDRESS_HOME_BETWEEN_STREETS_OR_LANDMARK:
case FieldType::ADDRESS_HOME_DEPENDENT_LOCALITY:
case FieldType::ADDRESS_HOME_HOUSE_NUMBER:
case FieldType::ADDRESS_HOME_HOUSE_NUMBER_AND_APT:
case FieldType::ADDRESS_HOME_LANDMARK:
case FieldType::ADDRESS_HOME_LINE2:
case FieldType::ADDRESS_HOME_LINE3:
case FieldType::ADDRESS_HOME_OVERFLOW:
case FieldType::ADDRESS_HOME_OVERFLOW_AND_LANDMARK:
case FieldType::ADDRESS_HOME_STREET_ADDRESS:
case FieldType::ADDRESS_HOME_STREET_LOCATION:
case FieldType::ADDRESS_HOME_STREET_NAME:
case FieldType::ALTERNATIVE_FAMILY_NAME:
case FieldType::ALTERNATIVE_FULL_NAME:
case FieldType::ALTERNATIVE_GIVEN_NAME:
case FieldType::CREDIT_CARD_EXP_2_DIGIT_YEAR:
case FieldType::CREDIT_CARD_EXP_4_DIGIT_YEAR:
case FieldType::CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR:
case FieldType::CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR:
case FieldType::CREDIT_CARD_EXP_MONTH:
case FieldType::CREDIT_CARD_NAME_FIRST:
case FieldType::CREDIT_CARD_NAME_FULL:
case FieldType::CREDIT_CARD_NAME_LAST:
case FieldType::CREDIT_CARD_STANDALONE_VERIFICATION_CODE:
case FieldType::CREDIT_CARD_TYPE:
case FieldType::CREDIT_CARD_VERIFICATION_CODE:
case FieldType::EMAIL_OR_LOYALTY_MEMBERSHIP_ID:
case FieldType::IBAN_VALUE:
case FieldType::LOYALTY_MEMBERSHIP_ID:
case FieldType::MERCHANT_PROMO_CODE:
case FieldType::NAME_HONORIFIC_PREFIX:
case FieldType::NAME_LAST_FIRST:
case FieldType::NAME_LAST_SECOND:
case FieldType::NAME_MIDDLE:
case FieldType::NAME_MIDDLE_INITIAL:
case FieldType::NUMERIC_QUANTITY:
case FieldType::PHONE_HOME_CITY_AND_NUMBER:
case FieldType::PHONE_HOME_CITY_CODE:
case FieldType::PHONE_HOME_COUNTRY_CODE:
case FieldType::PHONE_HOME_EXTENSION:
case FieldType::PHONE_HOME_NUMBER_PREFIX:
case FieldType::PHONE_HOME_NUMBER_SUFFIX:
case FieldType::PHONE_HOME_WHOLE_NUMBER:
case FieldType::PRICE:
case FieldType::SEARCH_TERM:
case FieldType::UNKNOWN_TYPE:
case FieldType::EMPTY_TYPE:
break;
// The following types cannot be predicted by the local heuristics.
case FieldType::USERNAME:
case FieldType::NOT_NEW_PASSWORD:
case FieldType::SINGLE_USERNAME:
case FieldType::NO_SERVER_DATA:
case FieldType::PASSPORT_NUMBER:
case FieldType::PASSPORT_ISSUING_COUNTRY:
case FieldType::PASSPORT_EXPIRATION_DATE:
case FieldType::PASSPORT_ISSUE_DATE:
case FieldType::LOYALTY_MEMBERSHIP_PROGRAM:
case FieldType::LOYALTY_MEMBERSHIP_PROVIDER:
case FieldType::VEHICLE_LICENSE_PLATE:
case FieldType::VEHICLE_VIN:
case FieldType::VEHICLE_MAKE:
case FieldType::VEHICLE_MODEL:
case FieldType::DRIVERS_LICENSE_REGION:
case FieldType::DRIVERS_LICENSE_NUMBER:
case FieldType::DRIVERS_LICENSE_EXPIRATION_DATE:
case FieldType::DRIVERS_LICENSE_ISSUE_DATE:
case FieldType::VEHICLE_YEAR:
case FieldType::VEHICLE_PLATE_STATE:
case FieldType::NATIONAL_ID_CARD_NUMBER:
case FieldType::NATIONAL_ID_CARD_EXPIRATION_DATE:
case FieldType::NATIONAL_ID_CARD_ISSUE_DATE:
case FieldType::NATIONAL_ID_CARD_ISSUING_COUNTRY:
case FieldType::KNOWN_TRAVELER_NUMBER:
case FieldType::KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE:
case FieldType::REDRESS_NUMBER:
case FieldType::FLIGHT_RESERVATION_FLIGHT_NUMBER:
case FieldType::FLIGHT_RESERVATION_CONFIRMATION_CODE:
case FieldType::FLIGHT_RESERVATION_TICKET_NUMBER:
case FieldType::FLIGHT_RESERVATION_DEPARTURE_DATE:
case FieldType::PHONE_HOME_CITY_AND_NUMBER_WITHOUT_TRUNK_PREFIX:
case FieldType::NAME_SUFFIX:
case FieldType::MERCHANT_EMAIL_SIGNUP:
case FieldType::ACCOUNT_CREATION_PASSWORD:
case FieldType::ADDRESS_HOME_SORTING_CODE:
case FieldType::NOT_ACCOUNT_CREATION_PASSWORD:
case FieldType::USERNAME_AND_EMAIL_ADDRESS:
case FieldType::NEW_PASSWORD:
case FieldType::PROBABLY_NEW_PASSWORD:
case FieldType::NOT_PASSWORD:
case FieldType::CONFIRMATION_PASSWORD:
case FieldType::AMBIGUOUS_TYPE:
case FieldType::SINGLE_USERNAME_WITH_INTERMEDIATE_VALUES:
case FieldType::ADDRESS_HOME_ZIP_PREFIX:
case FieldType::ADDRESS_HOME_ZIP_SUFFIX:
case FieldType::ADDRESS_HOME_ZIP_AND_CITY:
case FieldType::NOT_USERNAME:
case FieldType::ADDRESS_HOME_SUBPREMISE:
case FieldType::ADDRESS_HOME_OTHER_SUBUNIT:
case FieldType::NAME_LAST_CONJUNCTION:
case FieldType::ADDRESS_HOME_ADDRESS:
case FieldType::ADDRESS_HOME_ADDRESS_WITH_NAME:
case FieldType::ADDRESS_HOME_FLOOR:
case FieldType::PHONE_HOME_CITY_CODE_WITH_TRUNK_PREFIX:
case FieldType::ONE_TIME_CODE:
case FieldType::DELIVERY_INSTRUCTIONS:
case FieldType::ADDRESS_HOME_STREET_LOCATION_AND_LOCALITY:
case FieldType::ADDRESS_HOME_STREET_LOCATION_AND_LANDMARK:
case FieldType::ADDRESS_HOME_DEPENDENT_LOCALITY_AND_LANDMARK:
case FieldType::SINGLE_USERNAME_FORGOT_PASSWORD:
case FieldType::ADDRESS_HOME_APT_TYPE:
case FieldType::ORDER_ID:
case FieldType::ORDER_DATE:
case FieldType::ORDER_MERCHANT_NAME:
case FieldType::SHIPMENT_TRACKING_NUMBER:
case FieldType::MAX_VALID_FIELD_TYPE:
LOG(ERROR) << "The field created by " << __func__ << "("
<< FieldTypeToStringView(role)
<< ") will not be assigned to the expected "
"FieldType by the local heuristics!";
break;
}
return field;
}
FormFieldData GetFormFieldData(const FieldDescription& fd) {
FormFieldData ff = CreateFieldByRole(fd.role);
ff.set_form_control_type(fd.form_control_type);
if (ff.form_control_type() == FormControlType::kSelectOne &&
!fd.select_options.empty()) {
ff.set_options(fd.select_options);
}
if (!fd.datalist_options.empty()) {
ff.set_datalist_options(fd.datalist_options);
}
ff.set_renderer_id(fd.renderer_id.value_or(MakeFieldRendererId()));
ff.set_host_form_id(MakeFormRendererId());
ff.set_is_focusable(fd.is_focusable);
ff.set_is_visible(fd.is_visible);
if (!fd.autocomplete_attribute.empty()) {
ff.set_autocomplete_attribute(fd.autocomplete_attribute);
ff.set_parsed_autocomplete(
ParseAutocompleteAttribute(fd.autocomplete_attribute));
}
if (fd.host_frame) {
ff.set_host_frame(*fd.host_frame);
}
if (fd.host_form_signature) {
ff.set_host_form_signature(*fd.host_form_signature);
}
if (fd.label) {
ff.set_label(*fd.label);
}
if (fd.name) {
ff.set_name(*fd.name);
}
if (fd.name_attribute) {
ff.set_name_attribute(*fd.name_attribute);
}
if (fd.id_attribute) {
ff.set_id_attribute(*fd.id_attribute);
}
if (fd.nonce) {
ff.set_nonce(*fd.nonce);
}
if (fd.value) {
ff.set_value(*fd.value);
}
if (fd.placeholder) {
ff.set_placeholder(*fd.placeholder);
}
if (fd.aria_label) {
ff.set_aria_label(*fd.aria_label);
}
if (fd.aria_description) {
ff.set_aria_description(*fd.aria_description);
}
if (fd.max_length) {
ff.set_max_length(*fd.max_length);
} else if (ff.IsSelectElement()) {
ff.set_max_length(0);
}
if (fd.origin) {
ff.set_origin(*fd.origin);
}
ff.set_is_autofilled_according_to_renderer(
fd.is_autofilled_according_to_renderer.value_or(false));
ff.set_should_autocomplete(fd.should_autocomplete);
ff.set_properties_mask(fd.properties_mask);
if (ff.form_control_type() == FormControlType::kInputCheckbox ||
ff.form_control_type() == FormControlType::kInputRadio) {
ff.set_check_status(
fd.checked ? FormFieldData::CheckStatus::kChecked
: FormFieldData::CheckStatus::kCheckableButUnchecked);
}
if (fd.form_control_ax_id) {
ff.set_form_control_ax_id(*fd.form_control_ax_id);
}
CHECK(!fd.checked ||
ff.form_control_type() == FormControlType::kInputCheckbox ||
ff.form_control_type() == FormControlType::kInputRadio)
<< "Only <input type=checkbox> and <input type=radio> are checkable";
return ff;
}
FormData GetFormData(const FormDescription& d) {
FormData f;
f.set_url(GURL(d.url));
f.set_action(GURL(d.action));
f.set_name(d.name);
f.set_host_frame(d.host_frame.value_or(MakeLocalFrameToken()));
f.set_renderer_id(d.renderer_id.value_or(MakeFormRendererId()));
if (d.main_frame_origin) {
f.set_main_frame_origin(*d.main_frame_origin);
} else {
f.set_main_frame_origin(url::Origin::Create(GURL(kDefaultTestOrigin)));
}
f.set_fields(base::ToVector(d.fields, [&f](const FieldDescription& dd) {
FormFieldData ff = GetFormFieldData(dd);
ff.set_host_frame(dd.host_frame.value_or(f.host_frame()));
ff.set_origin(dd.origin.value_or(f.main_frame_origin()));
ff.set_host_form_id(f.renderer_id());
return ff;
}));
return f;
}
FormData GetFormData(const std::vector<FieldType>& field_types) {
FormDescription form_description;
form_description.fields.reserve(field_types.size());
for (FieldType type : field_types) {
form_description.fields.emplace_back(type);
}
return GetFormData(form_description);
}
std::vector<FieldType> GetHeuristicTypes(
const FormDescription& form_description) {
return base::ToVector(form_description.fields, [](const auto& field) {
return field.heuristic_type.value_or(field.role);
});
}
std::vector<FieldType> GetServerTypes(const FormDescription& form_description) {
return base::ToVector(form_description.fields, [](const auto& field) {
return field.server_type.value_or(field.role);
});
}
// static
void FormStructureTest::CheckFormStructureTestData(
const std::vector<FormStructureTestCase>& test_cases) {
for (const FormStructureTestCase& test_case : test_cases) {
const FormData form = GetFormData(test_case.form_attributes);
SCOPED_TRACE(Message("Test description: ")
<< test_case.form_attributes.description_for_logging);
auto form_structure = std::make_unique<FormStructure>(form);
if (test_case.form_flags.determine_heuristic_type) {
const RegexPredictions regex_predictions = DetermineRegexTypes(
GeoIpCountryCode(""), LanguageCode(""), form_structure->ToFormData(),
nullptr, /*ignore_small_forms=*/true);
regex_predictions.ApplyTo(form_structure->fields());
form_structure->RationalizeAndAssignSections(GeoIpCountryCode(""),
LanguageCode(""), nullptr);
}
if (test_case.form_flags.is_autofillable) {
EXPECT_TRUE(IsAutofillable(*form_structure));
}
if (test_case.form_flags.should_be_parsed) {
EXPECT_TRUE(ShouldBeParsed(*form_structure, /*log_manager=*/nullptr));
}
if (test_case.form_flags.should_be_queried) {
EXPECT_TRUE(ShouldBeQueried(*form_structure));
}
if (test_case.form_flags.should_be_uploaded) {
EXPECT_TRUE(ShouldBeUploaded(*form_structure));
}
if (test_case.form_flags.has_author_specified_types) {
EXPECT_TRUE(
std::ranges::any_of(form_structure->fields(), [](const auto& field) {
return field->parsed_autocomplete().has_value();
}));
}
if (test_case.form_flags.is_complete_credit_card_form.has_value()) {
auto [completeness, expected_result] =
*test_case.form_flags.is_complete_credit_card_form;
EXPECT_EQ(form_structure->IsCompleteCreditCardForm(completeness),
expected_result);
}
if (test_case.form_flags.field_count) {
ASSERT_EQ(*test_case.form_flags.field_count,
static_cast<int>(form_structure->field_count()));
}
if (test_case.form_flags.autofill_count) {
ASSERT_EQ(*test_case.form_flags.autofill_count,
static_cast<int>(std::ranges::count_if(
form_structure->fields(), [](const auto& field) {
return field->IsFieldFillable();
})));
}
if (test_case.form_flags.section_count) {
std::set<Section> section_names;
for (const auto& field : *form_structure) {
section_names.insert(field->section());
}
EXPECT_EQ(*test_case.form_flags.section_count,
static_cast<int>(section_names.size()));
}
for (size_t i = 0;
i < test_case.expected_field_types.expected_html_type.size(); ++i) {
EXPECT_EQ(test_case.expected_field_types.expected_html_type[i],
form_structure->field(i)->html_type());
}
for (size_t i = 0;
i < test_case.expected_field_types.expected_heuristic_type.size();
++i) {
EXPECT_EQ(test_case.expected_field_types.expected_heuristic_type[i],
form_structure->field(i)->heuristic_type());
}
for (size_t i = 0;
i < test_case.expected_field_types.expected_overall_type.size(); ++i) {
EXPECT_THAT(
form_structure->field(i)->Type().GetTypes(),
ElementsAre(test_case.expected_field_types.expected_overall_type[i]));
}
}
}
} // namespace autofill::test