| // Copyright 2013 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/metrics/autofill_metrics.h" |
| |
| #include <stddef.h> |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/base64.h" |
| #include "base/check.h" |
| #include "base/containers/fixed_flat_map.h" |
| #include "base/feature_list.h" |
| #include "base/ios/ios_util.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/metrics/statistics_recorder.h" |
| #include "base/strings/strcat.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/metrics/user_action_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/task_environment.h" |
| #include "base/time/time.h" |
| #include "base/types/cxx23_to_underlying.h" |
| #include "build/build_config.h" |
| #include "components/autofill/core/browser/autofill_data_util.h" |
| #include "components/autofill/core/browser/autofill_external_delegate.h" |
| #include "components/autofill/core/browser/autofill_form_test_utils.h" |
| #include "components/autofill/core/browser/autofill_suggestion_generator.h" |
| #include "components/autofill/core/browser/autofill_test_utils.h" |
| #include "components/autofill/core/browser/browser_autofill_manager.h" |
| #include "components/autofill/core/browser/crowdsourcing/autofill_crowdsourcing_encoding.h" |
| #include "components/autofill/core/browser/data_model/credit_card.h" |
| #include "components/autofill/core/browser/field_types.h" |
| #include "components/autofill/core/browser/form_structure.h" |
| #include "components/autofill/core/browser/form_structure_test_api.h" |
| #include "components/autofill/core/browser/metrics/autofill_metrics_test_base.h" |
| #include "components/autofill/core/browser/metrics/autofill_metrics_utils.h" |
| #include "components/autofill/core/browser/metrics/form_events/address_form_event_logger.h" |
| #include "components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.h" |
| #include "components/autofill/core/browser/metrics/form_events/form_events.h" |
| #include "components/autofill/core/browser/metrics/payments/credit_card_save_metrics.h" |
| #include "components/autofill/core/browser/metrics/ukm_metrics_test_utils.h" |
| #include "components/autofill/core/browser/payments/credit_card_access_manager.h" |
| #include "components/autofill/core/browser/payments/test_credit_card_save_manager.h" |
| #include "components/autofill/core/browser/payments_data_manager.h" |
| #include "components/autofill/core/browser/personal_data_manager.h" |
| #include "components/autofill/core/browser/test_autofill_client.h" |
| #include "components/autofill/core/browser/test_autofill_driver.h" |
| #include "components/autofill/core/browser/test_browser_autofill_manager.h" |
| #include "components/autofill/core/browser/test_form_data_importer.h" |
| #include "components/autofill/core/browser/test_personal_data_manager.h" |
| #include "components/autofill/core/browser/ui/suggestion.h" |
| #include "components/autofill/core/browser/ui/suggestion_type.h" |
| #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" |
| #include "components/autofill/core/common/autofill_clock.h" |
| #include "components/autofill/core/common/autofill_features.h" |
| #include "components/autofill/core/common/autofill_payments_features.h" |
| #include "components/autofill/core/common/dense_set.h" |
| #include "components/autofill/core/common/form_data.h" |
| #include "components/autofill/core/common/form_field_data.h" |
| #include "components/autofill/core/common/form_interactions_flow.h" |
| #include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h" |
| #include "components/autofill/core/common/signatures.h" |
| #include "components/autofill/core/common/unique_ids.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/sync/test/test_sync_service.h" |
| #include "components/translate/core/common/language_detection_details.h" |
| #include "components/ukm/test_ukm_recorder.h" |
| #include "components/webdata/common/web_data_results.h" |
| #include "services/metrics/public/cpp/ukm_builders.h" |
| #include "services/metrics/public/cpp/ukm_source.h" |
| #include "services/network/public/cpp/shared_url_loader_factory.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "url/gurl.h" |
| #include "url/url_canon.h" |
| |
| #if !BUILDFLAG(IS_IOS) |
| #include "components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h" |
| #endif |
| |
| using ::autofill::test::AddFieldPredictionToForm; |
| using ::autofill::test::CreateTestFormField; |
| using ::base::ASCIIToUTF16; |
| using ::base::Bucket; |
| using ::base::BucketsAre; |
| using ::base::BucketsInclude; |
| using ::base::TimeTicks; |
| using ::testing::ElementsAre; |
| using ::testing::HasSubstr; |
| using ::testing::Matcher; |
| using ::testing::NiceMock; |
| using ::testing::UnorderedPointwise; |
| |
| namespace autofill { |
| |
| // This is defined in the autofill_metrics.cc implementation file. |
| int GetFieldTypeGroupPredictionQualityMetric( |
| FieldType field_type, |
| AutofillMetrics::FieldTypeQualityMetric metric); |
| |
| } // namespace autofill |
| |
| namespace autofill::autofill_metrics { |
| |
| using mojom::SubmissionSource; |
| using PaymentsSigninState = AutofillMetrics::PaymentsSigninState; |
| using AutofillStatus = AutofillMetrics::AutofillStatus; |
| |
| namespace { |
| |
| using UkmCardUploadDecisionType = ukm::builders::Autofill_CardUploadDecision; |
| using UkmDeveloperEngagementType = ukm::builders::Autofill_DeveloperEngagement; |
| using UkmInteractedWithFormType = ukm::builders::Autofill_InteractedWithForm; |
| using UkmSuggestionsShownType = ukm::builders::Autofill_SuggestionsShown; |
| using UkmSuggestionFilledType = ukm::builders::Autofill_SuggestionFilled; |
| using UkmTextFieldDidChangeType = ukm::builders::Autofill_TextFieldDidChange; |
| using UkmLogHiddenRepresentationalFieldSkipDecisionType = |
| ukm::builders::Autofill_HiddenRepresentationalFieldSkipDecision; |
| using UkmLogRepeatedServerTypePredictionRationalized = |
| ukm::builders::Autofill_RepeatedServerTypePredictionRationalized; |
| using UkmFieldTypeValidationType = ukm::builders::Autofill_FieldTypeValidation; |
| using UkmFieldFillStatusType = ukm::builders::Autofill_FieldFillStatus; |
| using UkmFormEventType = ukm::builders::Autofill_FormEvent; |
| using UkmEditedAutofilledFieldAtSubmission = |
| ukm::builders::Autofill_EditedAutofilledFieldAtSubmission; |
| using UkmAutofillKeyMetricsType = ukm::builders::Autofill_KeyMetrics; |
| using UkmFieldInfoType = ukm::builders::Autofill2_FieldInfo; |
| using UkmFieldInfoAfterSubmissionType = |
| ukm::builders::Autofill2_FieldInfoAfterSubmission; |
| using UkmFormSummaryType = ukm::builders::Autofill2_FormSummary; |
| using ExpectedUkmMetricsRecord = std::vector<ExpectedUkmMetricsPair>; |
| using ExpectedUkmMetrics = std::vector<ExpectedUkmMetricsRecord>; |
| |
| FormSignature Collapse(FormSignature sig) { |
| return FormSignature(sig.value() % 1021); |
| } |
| |
| FieldSignature Collapse(FieldSignature sig) { |
| return FieldSignature(sig.value() % 1021); |
| } |
| |
| void CreateSimpleForm(const GURL& origin, FormData& form) { |
| form.host_frame = test::MakeLocalFrameToken(); |
| form.renderer_id = test::MakeFormRendererId(); |
| form.name = u"TestForm"; |
| form.url = GURL("http://example.com/form.html"); |
| form.action = GURL("http://example.com/submit.html"); |
| form.main_frame_origin = url::Origin::Create(origin); |
| } |
| |
| std::string SerializeAndEncode(const AutofillQueryResponse& response) { |
| std::string unencoded_response_string; |
| if (!response.SerializeToString(&unencoded_response_string)) { |
| LOG(ERROR) << "Cannot serialize the response proto"; |
| return ""; |
| } |
| return base::Base64Encode(unencoded_response_string); |
| } |
| |
| } // namespace |
| |
| class AutofillMetricsTest : public AutofillMetricsBaseTest, |
| public testing::Test { |
| public: |
| using AutofillMetricsBaseTest::AutofillMetricsBaseTest; |
| ~AutofillMetricsTest() override = default; |
| |
| void SetUp() override { SetUpHelper(); } |
| |
| void TearDown() override { TearDownHelper(); } |
| }; |
| |
| // Test parameter indicates if the metrics are being logged for a form in an |
| // iframe or the main frame. True means the form is in the main frame. |
| class AutofillMetricsIFrameTest : public testing::WithParamInterface<bool>, |
| public AutofillMetricsTest { |
| public: |
| AutofillMetricsIFrameTest() |
| : AutofillMetricsTest( |
| /*is_in_any_main_frame=*/GetParam()), |
| credit_card_form_events_frame_histogram_( |
| std::string("Autofill.FormEvents.CreditCard.") + |
| (is_in_any_main_frame_ ? "IsInMainFrame" : "IsInIFrame")) {} |
| |
| CreditCard GetVirtualCreditCard(const std::string& guid) { |
| CreditCard copy = |
| *personal_data().payments_data_manager().GetCreditCardByGUID(guid); |
| copy.set_record_type(CreditCard::RecordType::kVirtualCard); |
| return copy; |
| } |
| |
| protected: |
| const std::string credit_card_form_events_frame_histogram_; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P(AutofillMetricsTest, |
| AutofillMetricsIFrameTest, |
| testing::Bool()); |
| |
| TEST_F(AutofillMetricsTest, PerfectFilling_Addresses_CreditCards) { |
| FormData address_form = test::GetFormData( |
| {.fields = {{.role = NAME_FULL, |
| .value = u"Elvis Aaron Presley", |
| .is_autofilled = true}, |
| {.role = ADDRESS_HOME_CITY, .value = u"Munich"}}}); |
| FormData payments_form = test::GetFormData( |
| {.fields = {{.role = CREDIT_CARD_NAME_FULL, |
| .value = u"Elvis Aaron Presley", |
| .is_autofilled = true}, |
| {.role = CREDIT_CARD_NUMBER, .value = u"01230123012399"}}}); |
| payments_form.fields.back().set_is_user_edited(true); |
| autofill_manager().AddSeenForm(address_form, {NAME_FULL, ADDRESS_HOME_LINE1}); |
| autofill_manager().AddSeenForm(payments_form, |
| {CREDIT_CARD_NAME_FULL, CREDIT_CARD_NUMBER}); |
| |
| base::HistogramTester histogram_tester; |
| SubmitForm(address_form); |
| histogram_tester.ExpectUniqueSample("Autofill.PerfectFilling.Addresses", 1, |
| 1); |
| histogram_tester.ExpectTotalCount("Autofill.PerfectFilling.CreditCards", 0); |
| |
| SubmitForm(payments_form); |
| histogram_tester.ExpectUniqueSample("Autofill.PerfectFilling.Addresses", 1, |
| 1); |
| histogram_tester.ExpectUniqueSample("Autofill.PerfectFilling.CreditCards", 0, |
| 1); |
| } |
| |
| // Test the emission of collisions between NUMERIC_QUANTITY and server |
| // predictions as well as the potential false positives. |
| TEST_F(AutofillMetricsTest, NumericQuantityCollision) { |
| // Those metrics are only collected when the numeric quantities are not |
| // getting precedence over server predictions. |
| base::test::ScopedFeatureList numeric_quantity_feature_list; |
| numeric_quantity_feature_list.InitAndDisableFeature( |
| features::kAutofillGivePrecedenceToNumericQuantities); |
| |
| // Set up our form data. |
| test::FormDescription form_description = { |
| .description_for_logging = "NumericQuantityCollision", |
| .fields = {{.server_type = NO_SERVER_DATA, |
| .heuristic_type = NUMERIC_QUANTITY, |
| .is_autofilled = false}, |
| // We add a second field to make sure the metrics are only |
| // recorded for the field with the numeric quantity prediction. |
| {.server_type = ADDRESS_HOME_LINE1, |
| .heuristic_type = ADDRESS_HOME_LINE1, |
| .is_autofilled = false}}}; |
| |
| // Helper to submit the `form` and test the expectations. `collision` |
| // indicates that there was a collision between the NUMERIC_QUANTITY |
| // prediction and a server prediction. |
| // If `autofill_used` and a `collision` exists, the histogram to |
| // track `false_positive` is checked. |
| auto SubmitAndTest = [this](const FormData& form, bool collision, |
| bool autofill_used, bool false_positive) { |
| base::HistogramTester histogram_tester; |
| SubmitForm(form); |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.NumericQuantityCollidesWithServerPrediction", collision, 1); |
| if (collision && autofill_used) { |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.AcceptedFilledFieldWithNumericQuantityHeuristicPrediction", |
| false_positive, 1); |
| } |
| }; |
| |
| { |
| SCOPED_TRACE( |
| "No collision case - The numeric quantity does not collide with a " |
| "server prediction."); |
| FormData form = GetAndAddSeenForm(form_description); |
| SubmitAndTest(form, /*collision=*/false, /*autofill_used=*/false, |
| /*false_positive=*/false); |
| } |
| { |
| SCOPED_TRACE("Collision, but nothing is filled."); |
| // Add a server prediction to create a collision. |
| form_description.fields[0].server_type = NAME_FIRST; |
| FormData form = GetAndAddSeenForm(form_description); |
| SubmitAndTest(form, /*collision=*/true, /*autofill_used=*/false, |
| /*false_positive=*/false); |
| } |
| { |
| SCOPED_TRACE("Collision, the field is autofilled."); |
| form_description.fields[0].is_autofilled = true; |
| FormData form = GetAndAddSeenForm(form_description); |
| SubmitAndTest(form, /*collision=*/true, /*autofill_used=*/true, |
| /*false_positive=*/true); |
| } |
| { |
| SCOPED_TRACE( |
| "Collision, the field is autofilled and subsequently changed."); |
| FormData form = GetAndAddSeenForm(form_description); |
| SimulateUserChangedTextField(form, form.fields[0]); |
| SubmitAndTest(form, /*collision=*/true, /*autofill_used=*/true, |
| /*false_positive=*/false); |
| } |
| } |
| |
| // Test that we log the skip decisions for hidden/representational fields |
| // correctly. |
| TEST_F(AutofillMetricsTest, LogHiddenRepresentationalFieldSkipDecision) { |
| RecreateProfile(); |
| |
| FormData form = CreateForm({ |
| CreateTestFormField("Name", "name", "", |
| FormControlType::kInputText), // no decision |
| CreateTestFormField("Street", "street", "", |
| FormControlType::kInputText), // skips |
| CreateTestFormField("City", "city", "", |
| FormControlType::kInputText), // skips |
| CreateTestFormField("State", "state", "", |
| FormControlType::kSelectOne), // doesn't skip |
| CreateTestFormField("Country", "country", "", |
| FormControlType::kSelectOne) // doesn't skip |
| }); |
| |
| form.fields[1].set_is_focusable(false); |
| form.fields[2].set_role(FormFieldData::RoleAttribute::kPresentation); |
| form.fields[3].set_is_focusable(false); |
| form.fields[4].set_role(FormFieldData::RoleAttribute::kPresentation); |
| |
| std::vector<FieldType> field_types = {NAME_FULL, ADDRESS_HOME_LINE1, |
| ADDRESS_HOME_CITY, ADDRESS_HOME_STATE, |
| ADDRESS_HOME_COUNTRY}; |
| |
| std::vector<FieldSignature> field_signature; |
| for (auto it = form.fields.begin() + 1; it != form.fields.end(); ++it) |
| field_signature.push_back(Collapse(CalculateFieldSignatureForField(*it))); |
| |
| FormSignature form_signature = Collapse(CalculateFormSignature(form)); |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| // Simulate filling form. |
| { |
| base::UserActionTester user_action_tester; |
| FillTestProfile(form); |
| } |
| |
| VerifyUkm( |
| &test_ukm_recorder(), form, |
| UkmLogHiddenRepresentationalFieldSkipDecisionType::kEntryName, |
| {{{UkmLogHiddenRepresentationalFieldSkipDecisionType::kFormSignatureName, |
| form_signature.value()}, |
| {UkmLogHiddenRepresentationalFieldSkipDecisionType::kFieldSignatureName, |
| field_signature[2].value()}, |
| {UkmLogHiddenRepresentationalFieldSkipDecisionType::kFieldTypeGroupName, |
| static_cast<int64_t>(FieldTypeGroup::kAddress)}, |
| {UkmLogHiddenRepresentationalFieldSkipDecisionType:: |
| kFieldOverallTypeName, |
| ADDRESS_HOME_STATE}, |
| {UkmLogHiddenRepresentationalFieldSkipDecisionType::kHeuristicTypeName, |
| ADDRESS_HOME_STATE}, |
| {UkmLogHiddenRepresentationalFieldSkipDecisionType::kServerTypeName, |
| ADDRESS_HOME_STATE}, |
| {UkmLogHiddenRepresentationalFieldSkipDecisionType::kHtmlFieldTypeName, |
| HtmlFieldType::kUnspecified}, |
| {UkmLogHiddenRepresentationalFieldSkipDecisionType::kHtmlFieldModeName, |
| HtmlFieldMode::kNone}, |
| {UkmLogHiddenRepresentationalFieldSkipDecisionType::kIsSkippedName, |
| false}}, |
| {{UkmLogHiddenRepresentationalFieldSkipDecisionType::kFormSignatureName, |
| form_signature.value()}, |
| {UkmLogHiddenRepresentationalFieldSkipDecisionType::kFieldSignatureName, |
| field_signature[3].value()}, |
| {UkmLogHiddenRepresentationalFieldSkipDecisionType::kFieldTypeGroupName, |
| static_cast<int64_t>(FieldTypeGroup::kAddress)}, |
| {UkmLogHiddenRepresentationalFieldSkipDecisionType:: |
| kFieldOverallTypeName, |
| ADDRESS_HOME_COUNTRY}, |
| {UkmLogHiddenRepresentationalFieldSkipDecisionType::kHeuristicTypeName, |
| ADDRESS_HOME_COUNTRY}, |
| {UkmLogHiddenRepresentationalFieldSkipDecisionType::kServerTypeName, |
| ADDRESS_HOME_COUNTRY}, |
| {UkmLogHiddenRepresentationalFieldSkipDecisionType::kHtmlFieldTypeName, |
| HtmlFieldType::kUnspecified}, |
| {UkmLogHiddenRepresentationalFieldSkipDecisionType::kHtmlFieldModeName, |
| HtmlFieldMode::kNone}, |
| {UkmLogHiddenRepresentationalFieldSkipDecisionType::kIsSkippedName, |
| false}}}); |
| } |
| |
| // Test that we log the address line fields whose server types are rationalized |
| TEST_F(AutofillMetricsTest, LogRepeatedAddressTypeRationalized) { |
| FormData form = CreateEmptyForm(); |
| |
| FieldSignature field_signature[2]; |
| |
| FormFieldData field; |
| field.set_form_control_type(FormControlType::kInputText); |
| |
| field.set_label(u"fullname"); |
| field.set_name(u"fullname"); |
| form.fields.push_back(field); |
| |
| field.set_label(u"Street 1"); |
| field.set_name(u"street1"); |
| form.fields.push_back(field); |
| field_signature[0] = Collapse(CalculateFieldSignatureForField(field)); |
| |
| field.set_label(u"Street 2"); |
| field.set_name(u"street2"); |
| form.fields.push_back(field); |
| field_signature[1] = Collapse(CalculateFieldSignatureForField(field)); |
| |
| FormSignature form_signature = Collapse(CalculateFormSignature(form)); |
| |
| FormStructure form_structure(form); |
| |
| std::vector<FieldType> field_types; |
| for (size_t i = 0; i < form_structure.field_count(); ++i) |
| field_types.push_back(UNKNOWN_TYPE); |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| AutofillQueryResponse response; |
| auto* form_suggestion = response.add_form_suggestions(); |
| AddFieldPredictionToForm(form.fields[0], NAME_FULL, form_suggestion); |
| AddFieldPredictionToForm(form.fields[1], ADDRESS_HOME_STREET_ADDRESS, |
| form_suggestion); |
| AddFieldPredictionToForm(form.fields[2], ADDRESS_HOME_STREET_ADDRESS, |
| form_suggestion); |
| |
| std::string response_string = SerializeAndEncode(response); |
| ParseServerPredictionsQueryResponse( |
| response_string, {&form_structure}, |
| test::GetEncodedSignatures({&form_structure}), |
| autofill_manager().form_interactions_ukm_logger(), nullptr); |
| |
| ASSERT_EQ(test_ukm_recorder() |
| .GetEntriesByName( |
| UkmLogRepeatedServerTypePredictionRationalized::kEntryName) |
| .size(), |
| (size_t)2); |
| |
| VerifyUkm( |
| &test_ukm_recorder(), form, |
| UkmLogRepeatedServerTypePredictionRationalized::kEntryName, |
| {{{UkmLogRepeatedServerTypePredictionRationalized::kFormSignatureName, |
| form_signature.value()}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kFieldSignatureName, |
| field_signature[0].value()}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kFieldTypeGroupName, |
| static_cast<int64_t>(FieldTypeGroup::kAddress)}, |
| {UkmLogRepeatedServerTypePredictionRationalized:: |
| kFieldOldOverallTypeName, |
| ADDRESS_HOME_STREET_ADDRESS}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kHeuristicTypeName, |
| UNKNOWN_TYPE}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kHtmlFieldTypeName, |
| HtmlFieldType::kUnspecified}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kHtmlFieldModeName, |
| HtmlFieldMode::kNone}, |
| {UkmLogRepeatedServerTypePredictionRationalized:: |
| kFieldNewOverallTypeName, |
| ADDRESS_HOME_LINE1}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kServerTypeName, |
| ADDRESS_HOME_STREET_ADDRESS}}, |
| {{UkmLogRepeatedServerTypePredictionRationalized::kFormSignatureName, |
| form_signature.value()}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kFieldSignatureName, |
| field_signature[1].value()}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kFieldTypeGroupName, |
| static_cast<int64_t>(FieldTypeGroup::kAddress)}, |
| {UkmLogRepeatedServerTypePredictionRationalized:: |
| kFieldOldOverallTypeName, |
| ADDRESS_HOME_STREET_ADDRESS}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kHeuristicTypeName, |
| UNKNOWN_TYPE}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kHtmlFieldTypeName, |
| HtmlFieldType::kUnspecified}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kHtmlFieldModeName, |
| HtmlFieldMode::kNone}, |
| {UkmLogRepeatedServerTypePredictionRationalized:: |
| kFieldNewOverallTypeName, |
| ADDRESS_HOME_LINE2}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kServerTypeName, |
| ADDRESS_HOME_STREET_ADDRESS}}}); |
| } |
| |
| // Test that we log the state/country fields whose server types are rationalized |
| TEST_F(AutofillMetricsTest, LogRepeatedStateCountryTypeRationalized) { |
| FormData form = CreateEmptyForm(); |
| |
| FieldSignature field_signature[3]; |
| |
| FormFieldData field; |
| field.set_form_control_type(FormControlType::kInputText); |
| |
| field.set_label(u"Country"); |
| field.set_name(u"country"); |
| form.fields.push_back(field); |
| field_signature[0] = Collapse(CalculateFieldSignatureForField(field)); |
| |
| field.set_label(u"fullname"); |
| field.set_name(u"fullname"); |
| form.fields.push_back(field); |
| |
| field.set_label(u"State"); |
| field.set_name(u"state"); |
| form.fields.push_back(field); |
| field_signature[2] = Collapse(CalculateFieldSignatureForField(field)); |
| |
| field.set_label(u"State"); |
| field.set_name(u"state"); |
| field.set_is_focusable(false); |
| field.set_form_control_type(FormControlType::kSelectOne); |
| form.fields.push_back(field); |
| // Regardless of the order of appearance, hidden fields are rationalized |
| // before their corresponding visible one. |
| field_signature[1] = Collapse(CalculateFieldSignatureForField(field)); |
| |
| FormSignature form_signature = Collapse(CalculateFormSignature(form)); |
| |
| FormStructure form_structure(form); |
| |
| std::vector<FieldType> field_types; |
| for (size_t i = 0; i < form_structure.field_count(); ++i) |
| field_types.push_back(UNKNOWN_TYPE); |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| AutofillQueryResponse response; |
| auto* form_suggestion = response.add_form_suggestions(); |
| AddFieldPredictionToForm(form.fields[0], ADDRESS_HOME_COUNTRY, |
| form_suggestion); |
| AddFieldPredictionToForm(form.fields[1], NAME_FULL, form_suggestion); |
| AddFieldPredictionToForm(form.fields[2], ADDRESS_HOME_COUNTRY, |
| form_suggestion); |
| AddFieldPredictionToForm(form.fields[3], ADDRESS_HOME_COUNTRY, |
| form_suggestion); |
| |
| std::string response_string = SerializeAndEncode(response); |
| ParseServerPredictionsQueryResponse( |
| response_string, {&form_structure}, |
| test::GetEncodedSignatures({&form_structure}), |
| autofill_manager().form_interactions_ukm_logger(), nullptr); |
| |
| ASSERT_EQ(test_ukm_recorder() |
| .GetEntriesByName( |
| UkmLogRepeatedServerTypePredictionRationalized::kEntryName) |
| .size(), |
| (size_t)3); |
| |
| VerifyUkm( |
| &test_ukm_recorder(), form, |
| UkmLogRepeatedServerTypePredictionRationalized::kEntryName, |
| {{{UkmLogRepeatedServerTypePredictionRationalized::kFormSignatureName, |
| *form_signature}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kFieldSignatureName, |
| field_signature[0].value()}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kFieldTypeGroupName, |
| static_cast<int64_t>(FieldTypeGroup::kAddress)}, |
| {UkmLogRepeatedServerTypePredictionRationalized:: |
| kFieldOldOverallTypeName, |
| ADDRESS_HOME_COUNTRY}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kHeuristicTypeName, |
| UNKNOWN_TYPE}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kHtmlFieldTypeName, |
| HtmlFieldType::kUnspecified}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kHtmlFieldModeName, |
| HtmlFieldMode::kNone}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kServerTypeName, |
| ADDRESS_HOME_COUNTRY}, |
| {UkmLogRepeatedServerTypePredictionRationalized:: |
| kFieldNewOverallTypeName, |
| ADDRESS_HOME_COUNTRY}}, |
| {{UkmLogRepeatedServerTypePredictionRationalized::kFormSignatureName, |
| *form_signature}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kFieldSignatureName, |
| field_signature[1].value()}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kFieldTypeGroupName, |
| static_cast<int64_t>(FieldTypeGroup::kAddress)}, |
| {UkmLogRepeatedServerTypePredictionRationalized:: |
| kFieldOldOverallTypeName, |
| ADDRESS_HOME_COUNTRY}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kHeuristicTypeName, |
| UNKNOWN_TYPE}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kHtmlFieldTypeName, |
| HtmlFieldType::kUnspecified}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kHtmlFieldModeName, |
| HtmlFieldMode::kNone}, |
| {UkmLogRepeatedServerTypePredictionRationalized:: |
| kFieldNewOverallTypeName, |
| ADDRESS_HOME_STATE}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kServerTypeName, |
| ADDRESS_HOME_COUNTRY}}, |
| {{UkmLogRepeatedServerTypePredictionRationalized::kFormSignatureName, |
| *form_signature}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kFieldSignatureName, |
| field_signature[2].value()}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kFieldTypeGroupName, |
| static_cast<int64_t>(FieldTypeGroup::kAddress)}, |
| {UkmLogRepeatedServerTypePredictionRationalized:: |
| kFieldOldOverallTypeName, |
| ADDRESS_HOME_COUNTRY}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kHeuristicTypeName, |
| UNKNOWN_TYPE}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kHtmlFieldTypeName, |
| HtmlFieldType::kUnspecified}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kHtmlFieldModeName, |
| HtmlFieldMode::kNone}, |
| {UkmLogRepeatedServerTypePredictionRationalized:: |
| kFieldNewOverallTypeName, |
| ADDRESS_HOME_STATE}, |
| {UkmLogRepeatedServerTypePredictionRationalized::kServerTypeName, |
| ADDRESS_HOME_COUNTRY}}}); |
| } |
| |
| // Ensures that metrics that measure timing some important Autofill functions |
| // actually are recorded and retrieved. |
| TEST_F(AutofillMetricsTest, TimingMetrics) { |
| base::HistogramTester histogram_tester; |
| FormData form = CreateForm( |
| {CreateTestFormField("Autofilled", "autofilled", "Elvis Aaron Presley", |
| FormControlType::kInputText), |
| CreateTestFormField("Autofill Failed", "autofillfailed", |
| "buddy@gmail.com", FormControlType::kInputText), |
| CreateTestFormField("Phone", "phone", "2345678901", |
| FormControlType::kInputTelephone)}); |
| form.fields[0].set_is_autofilled(true); |
| form.fields[1].set_is_autofilled(false); |
| form.fields[2].set_is_autofilled(false); |
| |
| SeeForm(form); |
| |
| // Because these metrics are related to timing, it is not possible to know in |
| // advance which bucket the sample will fall into, so we just need to make |
| // sure we have valid samples. |
| EXPECT_FALSE( |
| histogram_tester.GetAllSamples("Autofill.Timing.DetermineHeuristicTypes") |
| .empty()); |
| EXPECT_FALSE(histogram_tester.GetAllSamples("Autofill.Timing.ParseFormsAsync") |
| .empty()); |
| EXPECT_FALSE( |
| histogram_tester |
| .GetAllSamples("Autofill.Timing.ParseFormsAsync.RunHeuristics") |
| .empty()); |
| EXPECT_FALSE(histogram_tester |
| .GetAllSamples("Autofill.Timing.ParseFormsAsync.UpdateCache") |
| .empty()); |
| } |
| |
| // Test that we behave sanely when the cached form differs from the submitted |
| // one. |
| TEST_F(AutofillMetricsTest, SaneMetricsWithCacheMismatch) { |
| FormData form = CreateForm( |
| {CreateTestFormField("Both match", "match", "Elvis Aaron Presley", |
| FormControlType::kInputText), |
| CreateTestFormField("Both mismatch", "mismatch", "buddy@gmail.com", |
| FormControlType::kInputText), |
| CreateTestFormField("Only heuristics match", "mixed", "Memphis", |
| FormControlType::kInputText), |
| CreateTestFormField("Unknown", "unknown", "garbage", |
| FormControlType::kInputText)}); |
| form.fields.front().set_is_autofilled(true); |
| |
| std::vector<FieldType> heuristic_types = {NAME_FULL, PHONE_HOME_NUMBER, |
| ADDRESS_HOME_CITY, UNKNOWN_TYPE}; |
| std::vector<FieldType> server_types = {NAME_FULL, PHONE_HOME_NUMBER, |
| PHONE_HOME_NUMBER, UNKNOWN_TYPE}; |
| |
| autofill_manager().AddSeenForm(form, heuristic_types, server_types); |
| |
| // Add a field and re-arrange the remaining form fields before submitting. |
| std::vector<FormFieldData> cached_fields = form.fields; |
| form.fields = {CreateTestFormField("New field", "new field", "Tennessee", |
| FormControlType::kInputText), |
| cached_fields[2], cached_fields[1], cached_fields[3], |
| cached_fields[0]}; |
| |
| base::HistogramTester histogram_tester; |
| SubmitForm(form); |
| |
| for (const std::string source : {"Heuristic", "Server", "Overall"}) { |
| std::string aggregate_histogram = |
| "Autofill.FieldPredictionQuality.Aggregate." + source; |
| std::string by_field_type_histogram = |
| "Autofill.FieldPredictionQuality.ByFieldType." + source; |
| |
| // Unknown: |
| histogram_tester.ExpectBucketCount( |
| aggregate_histogram, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN, 1); |
| histogram_tester.ExpectBucketCount( |
| by_field_type_histogram, |
| GetFieldTypeGroupPredictionQualityMetric( |
| ADDRESS_HOME_STATE, AutofillMetrics::FALSE_NEGATIVE_UNKNOWN), |
| 1); |
| // Match: |
| histogram_tester.ExpectBucketCount(aggregate_histogram, |
| AutofillMetrics::TRUE_POSITIVE, |
| source == "Heuristic" ? 2 : 1); |
| histogram_tester.ExpectBucketCount( |
| by_field_type_histogram, |
| GetFieldTypeGroupPredictionQualityMetric( |
| NAME_FULL, AutofillMetrics::TRUE_POSITIVE), |
| 1); |
| // Mismatch: |
| histogram_tester.ExpectBucketCount(aggregate_histogram, |
| AutofillMetrics::FALSE_NEGATIVE_MISMATCH, |
| source == "Heuristic" ? 1 : 2); |
| histogram_tester.ExpectBucketCount( |
| by_field_type_histogram, |
| GetFieldTypeGroupPredictionQualityMetric( |
| EMAIL_ADDRESS, AutofillMetrics::FALSE_NEGATIVE_MISMATCH), |
| 1); |
| // Source dependent: |
| histogram_tester.ExpectBucketCount( |
| by_field_type_histogram, |
| GetFieldTypeGroupPredictionQualityMetric( |
| ADDRESS_HOME_CITY, source == "Heuristic" |
| ? AutofillMetrics::TRUE_POSITIVE |
| : AutofillMetrics::FALSE_NEGATIVE_MISMATCH), |
| 1); |
| } |
| } |
| |
| // Verify that when submitting an autofillable form, the stored profile metric |
| // is logged. |
| TEST_F(AutofillMetricsTest, StoredProfileCountAutofillableFormSubmission) { |
| // Three fields is enough to make it an autofillable form. |
| FormData form = CreateForm( |
| {CreateTestFormField("Name", "name", "", FormControlType::kInputText), |
| CreateTestFormField("Email", "email", "", FormControlType::kInputText), |
| CreateTestFormField("Phone", "phone", "", FormControlType::kInputText)}); |
| |
| base::HistogramTester histogram_tester; |
| SeeForm(form); |
| SubmitForm(form); |
| |
| // An autofillable form was submitted, and the number of stored profiles is |
| // logged. |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.StoredProfileCountAtAutofillableFormSubmission", 2, 1); |
| } |
| |
| // Verify that when submitting a non-autofillable form, the stored profile |
| // metric is not logged. |
| TEST_F(AutofillMetricsTest, StoredProfileCountNonAutofillableFormSubmission) { |
| // Two fields is not enough to make it an autofillable form. |
| FormData form = CreateForm( |
| {CreateTestFormField("Name", "name", "", FormControlType::kInputText), |
| CreateTestFormField("Email", "email", "", FormControlType::kInputText)}); |
| |
| base::HistogramTester histogram_tester; |
| SeeForm(form); |
| SubmitForm(form); |
| |
| // A non-autofillable form was submitted, and number of stored profiles is NOT |
| // logged. |
| histogram_tester.ExpectTotalCount( |
| "Autofill.StoredProfileCountAtAutofillableFormSubmission", 0); |
| } |
| |
| // Verify that when submitting an autofillable form, the proper type of |
| // the edited fields is correctly logged to UKM. |
| TEST_F(AutofillMetricsTest, TypeOfEditedAutofilledFieldsUkmLogging) { |
| FormData form = CreateForm( |
| {CreateTestFormField("Autofilled", "autofilled", "Elvis Aaron Presley", |
| FormControlType::kInputText), |
| CreateTestFormField("Autofill Failed", "autofillfailed", |
| "buddy@gmail.com", FormControlType::kInputText), |
| CreateTestFormField("Phone", "phone", "2345678901", |
| FormControlType::kInputTelephone)}); |
| form.fields[0].set_is_autofilled(true); |
| form.fields[1].set_is_autofilled(true); |
| form.fields[2].set_is_autofilled(true); |
| |
| std::vector<FieldType> heuristic_types = {NAME_FULL, EMAIL_ADDRESS, |
| PHONE_HOME_CITY_AND_NUMBER}; |
| |
| std::vector<FieldType> server_types = {NAME_FULL, EMAIL_ADDRESS, |
| PHONE_HOME_CITY_AND_NUMBER}; |
| |
| autofill_manager().AddSeenForm(form, heuristic_types, server_types); |
| |
| // Verify that there are no counts before form submission. |
| |
| EXPECT_EQ(0U, test_ukm_recorder().entries_count()); |
| |
| base::HistogramTester histogram_tester; |
| // Simulate text input in the first and second fields. |
| SimulateUserChangedTextField(form, form.fields[0]); |
| |
| SubmitForm(form); |
| ExpectedUkmMetricsRecord name_field_ukm_record{ |
| {UkmEditedAutofilledFieldAtSubmission::kFieldSignatureName, |
| Collapse(CalculateFieldSignatureForField(form.fields[0])).value()}, |
| {UkmEditedAutofilledFieldAtSubmission::kFormSignatureName, |
| Collapse(CalculateFormSignature(form)).value()}, |
| {UkmEditedAutofilledFieldAtSubmission::kOverallTypeName, |
| static_cast<int64_t>(NAME_FULL)}}; |
| |
| VerifyUkm(&test_ukm_recorder(), form, |
| UkmEditedAutofilledFieldAtSubmission::kEntryName, |
| {name_field_ukm_record}); |
| } |
| |
| // Tests the logging of type-specific field-wise correctness. |
| TEST_F(AutofillMetricsTest, EditedAutofilledFieldAtSubmission) { |
| test::FormDescription form_description = { |
| .description_for_logging = "NumberOfAutofilledFields", |
| .fields = {{.role = NAME_FULL, |
| .value = u"Elvis Aaron Presley", |
| .is_autofilled = true}, |
| {.role = EMAIL_ADDRESS, |
| .value = u"buddy@gmail.com", |
| .is_autofilled = true}, |
| {.role = PHONE_HOME_CITY_AND_NUMBER, .is_autofilled = true}}, |
| .renderer_id = test::MakeFormRendererId(), |
| .main_frame_origin = |
| url::Origin::Create(autofill_client_->form_origin())}; |
| |
| FormData form = GetAndAddSeenForm(form_description); |
| |
| base::HistogramTester histogram_tester; |
| // Simulate text input in the first and second fields. |
| SimulateUserChangedTextField(form, form.fields[0]); |
| SimulateUserChangedTextField(form, form.fields[1]); |
| |
| SubmitForm(form); |
| |
| // The |NAME_FULL| field was edited (bucket 112). |
| histogram_tester.ExpectBucketCount( |
| "Autofill.EditedAutofilledFieldAtSubmission2.ByFieldType", 112, 1); |
| |
| // The |EMAIL_ADDRESS| field was edited (bucket 144). |
| histogram_tester.ExpectBucketCount( |
| "Autofill.EditedAutofilledFieldAtSubmission2.ByFieldType", 144, 1); |
| |
| // The |PHONE_HOME_CITY_AND_NUMBER| field was not edited (bucket 209). |
| histogram_tester.ExpectBucketCount( |
| "Autofill.EditedAutofilledFieldAtSubmission2.ByFieldType", 209, 1); |
| |
| // The aggregated histogram should have two counts on edited fields. |
| histogram_tester.ExpectBucketCount( |
| "Autofill.EditedAutofilledFieldAtSubmission2.Aggregate", 0, 2); |
| |
| // The aggregated histogram should have one count on accepted fields. |
| histogram_tester.ExpectBucketCount( |
| "Autofill.EditedAutofilledFieldAtSubmission2.Aggregate", 1, 1); |
| |
| // The autocomplete!=off histogram should have one count on accepted fields. |
| histogram_tester.ExpectBucketCount( |
| "Autofill.Autocomplete.NotOff.EditedAutofilledFieldAtSubmission2." |
| "Address", |
| 1, 1); |
| |
| // The autocomplete!=off histogram should have no count on accepted fields. |
| histogram_tester.ExpectTotalCount( |
| "Autofill.Autocomplete.Off.EditedAutofilledFieldAtSubmission2.Address", |
| 0); |
| } |
| |
| // Verify that we correctly log metrics regarding developer engagement. |
| TEST_F(AutofillMetricsTest, DeveloperEngagement) { |
| FormData form = CreateForm( |
| {CreateTestFormField("Name", "name", "", FormControlType::kInputText), |
| CreateTestFormField("Email", "email", "", FormControlType::kInputText)}); |
| |
| // Ensure no metrics are logged when small form support is disabled (min |
| // number of fields enforced). |
| { |
| base::HistogramTester histogram_tester; |
| SeeForm(form); |
| autofill_manager().Reset(); |
| histogram_tester.ExpectTotalCount("Autofill.DeveloperEngagement", 0); |
| } |
| |
| // Add another field to the form, so that it becomes fillable. |
| form.fields.push_back( |
| CreateTestFormField("Phone", "phone", "", FormControlType::kInputText)); |
| |
| // Expect the "form parsed without hints" metric to be logged. |
| { |
| base::HistogramTester histogram_tester; |
| SeeForm(form); |
| autofill_manager().Reset(); |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.DeveloperEngagement", |
| AutofillMetrics::FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS, 1); |
| } |
| |
| // Add some fields with an author-specified field type to the form. |
| // We need to add at least three fields, because a form must have at least |
| // three fillable fields to be considered to be autofillable; and if at least |
| // one field specifies an explicit type hint, we don't apply any of our usual |
| // local heuristics to detect field types in the rest of the form. |
| form.fields.push_back(CreateTestFormField( |
| "", "", "", FormControlType::kInputText, "given-name")); |
| form.fields.push_back( |
| CreateTestFormField("", "", "", FormControlType::kInputText, "email")); |
| form.fields.push_back(CreateTestFormField( |
| "", "", "", FormControlType::kInputText, "address-line1")); |
| |
| // Expect the "form parsed with field type hints" metric to be logged. |
| { |
| base::HistogramTester histogram_tester; |
| SeeForm(form); |
| autofill_manager().Reset(); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.DeveloperEngagement", |
| AutofillMetrics::FILLABLE_FORM_PARSED_WITH_TYPE_HINTS, 1); |
| } |
| } |
| |
| // Verify that we correctly log UKM for form parsed without type hints regarding |
| // developer engagement. |
| TEST_F(AutofillMetricsTest, |
| UkmDeveloperEngagement_LogFillableFormParsedWithoutTypeHints) { |
| FormData form = CreateForm( |
| {CreateTestFormField("Name", "name", "", FormControlType::kInputText), |
| CreateTestFormField("Email", "email", "", FormControlType::kInputText)}); |
| |
| // Ensure no entries are logged when loading a non-fillable form. |
| { |
| SeeForm(form); |
| autofill_manager().Reset(); |
| |
| EXPECT_EQ(0ul, test_ukm_recorder().entries_count()); |
| } |
| |
| // Add another field to the form, so that it becomes fillable. |
| form.fields.push_back( |
| CreateTestFormField("Phone", "phone", "", FormControlType::kInputText)); |
| |
| // Expect the "form parsed without field type hints" metric and the |
| // "form loaded" form interaction event to be logged. |
| { |
| SeeForm(form); |
| autofill_manager().Reset(); |
| |
| VerifyDeveloperEngagementUkm( |
| &test_ukm_recorder(), form, /*is_for_credit_card=*/false, |
| {FormType::kAddressForm}, |
| {AutofillMetrics::FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS}); |
| } |
| } |
| |
| // Verify that we correctly log UKM for form parsed with type hints regarding |
| // developer engagement. |
| TEST_F(AutofillMetricsTest, |
| UkmDeveloperEngagement_LogFillableFormParsedWithTypeHints) { |
| // The latter three fields have an author-specified field type to the form. |
| // We need to add at least three fields, because a form must have at least |
| // three fillable fields to be considered to be autofillable; and if at least |
| // one field specifies an explicit type hint, we don't apply any of our usual |
| // local heuristics to detect field types in the rest of the form. |
| FormData form = CreateForm( |
| {CreateTestFormField("Name", "name", "", FormControlType::kInputText), |
| CreateTestFormField("Email", "email", "", FormControlType::kInputText), |
| CreateTestFormField("Phone", "phone", "", FormControlType::kInputText), |
| CreateTestFormField("", "", "", FormControlType::kInputText, |
| "given-name"), |
| CreateTestFormField("", "", "", FormControlType::kInputText, "email"), |
| CreateTestFormField("", "", "", FormControlType::kInputText, |
| "address-line1")}); |
| |
| // Expect the "form parsed without field type hints" metric and the |
| // "form loaded" form interaction event to be logged. |
| { |
| SeeForm(form); |
| autofill_manager().Reset(); |
| |
| VerifyDeveloperEngagementUkm( |
| &test_ukm_recorder(), form, /*is_for_credit_card=*/false, |
| {FormType::kAddressForm}, |
| {AutofillMetrics::FILLABLE_FORM_PARSED_WITH_TYPE_HINTS}); |
| } |
| } |
| |
| TEST_F(AutofillMetricsTest, LogStoredCreditCardMetrics) { |
| // Helper timestamps for setting up the test data. |
| base::Time now = AutofillClock::Now(); |
| base::Time one_month_ago = now - base::Days(30); |
| base::Time::Exploded one_month_ago_exploded; |
| one_month_ago.LocalExplode(&one_month_ago_exploded); |
| |
| std::vector<std::unique_ptr<CreditCard>> local_cards; |
| std::vector<std::unique_ptr<CreditCard>> server_cards; |
| local_cards.reserve(2); |
| server_cards.reserve(10); |
| |
| // Create in-use and in-disuse cards of each record type: 1 of each for local, |
| // 2 of each for masked, and 3 of each for unmasked. |
| const std::vector<CreditCard::RecordType> record_types{ |
| CreditCard::RecordType::kLocalCard, |
| CreditCard::RecordType::kMaskedServerCard, |
| CreditCard::RecordType::kFullServerCard}; |
| int num_cards_of_type = 0; |
| for (auto record_type : record_types) { |
| num_cards_of_type += 1; |
| for (int i = 0; i < num_cards_of_type; ++i) { |
| // Create a card that's still in active use. |
| CreditCard card_in_use = test::GetRandomCreditCard(record_type); |
| card_in_use.set_use_date(now - base::Days(30)); |
| card_in_use.set_use_count(10); |
| |
| // Create a card that's not in active use. |
| CreditCard card_in_disuse = test::GetRandomCreditCard(record_type); |
| card_in_disuse.SetExpirationYear(one_month_ago_exploded.year); |
| card_in_disuse.SetExpirationMonth(one_month_ago_exploded.month); |
| card_in_disuse.set_use_date(now - base::Days(200)); |
| card_in_disuse.set_use_count(10); |
| |
| // Add the cards to the personal data manager in the appropriate way. |
| auto& repo = (record_type == CreditCard::RecordType::kLocalCard) |
| ? local_cards |
| : server_cards; |
| repo.push_back(std::make_unique<CreditCard>(std::move(card_in_use))); |
| repo.push_back(std::make_unique<CreditCard>(std::move(card_in_disuse))); |
| } |
| } |
| |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::LogStoredCreditCardMetrics( |
| local_cards, server_cards, /*server_card_count_with_card_art_image=*/2, |
| base::Days(180)); |
| |
| // Validate the basic count metrics. |
| histogram_tester.ExpectTotalCount("Autofill.StoredCreditCardCount", 1); |
| histogram_tester.ExpectTotalCount("Autofill.StoredCreditCardCount.Local", 1); |
| histogram_tester.ExpectTotalCount("Autofill.StoredCreditCardCount.Server", 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.StoredCreditCardCount.Server.Masked", 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.StoredCreditCardCount.Server.Unmasked", 1); |
| histogram_tester.ExpectBucketCount("Autofill.StoredCreditCardCount", 12, 1); |
| histogram_tester.ExpectBucketCount("Autofill.StoredCreditCardCount.Local", 2, |
| 1); |
| histogram_tester.ExpectBucketCount("Autofill.StoredCreditCardCount.Server", |
| 10, 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.StoredCreditCardCount.Server.Masked", 4, 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.StoredCreditCardCount.Server.Unmasked", 6, 1); |
| |
| // Validate the disused count metrics. |
| histogram_tester.ExpectTotalCount("Autofill.StoredCreditCardDisusedCount", 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.StoredCreditCardDisusedCount.Local", 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.StoredCreditCardDisusedCount.Server", 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.StoredCreditCardDisusedCount.Server.Masked", 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.StoredCreditCardDisusedCount.Server.Unmasked", 1); |
| histogram_tester.ExpectBucketCount("Autofill.StoredCreditCardDisusedCount", 6, |
| 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.StoredCreditCardDisusedCount.Local", 1, 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.StoredCreditCardDisusedCount.Server", 5, 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.StoredCreditCardDisusedCount.Server.Masked", 2, 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.StoredCreditCardDisusedCount.Server.Unmasked", 3, 1); |
| |
| // Validate the days-since-last-use metrics. |
| histogram_tester.ExpectTotalCount( |
| "Autofill.DaysSinceLastUse.StoredCreditCard", 12); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.DaysSinceLastUse.StoredCreditCard.Local", 2); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.DaysSinceLastUse.StoredCreditCard.Server", 10); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.DaysSinceLastUse.StoredCreditCard.Server.Masked", 4); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.DaysSinceLastUse.StoredCreditCard.Server.Unmasked", 6); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.DaysSinceLastUse.StoredCreditCard", 30, 6); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.DaysSinceLastUse.StoredCreditCard", 200, 6); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.DaysSinceLastUse.StoredCreditCard.Local", 30, 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.DaysSinceLastUse.StoredCreditCard.Local", 200, 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.DaysSinceLastUse.StoredCreditCard.Server", 30, 5); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.DaysSinceLastUse.StoredCreditCard.Server", 200, 5); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.DaysSinceLastUse.StoredCreditCard.Server.Masked", 30, 2); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.DaysSinceLastUse.StoredCreditCard.Server.Masked", 200, 2); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.DaysSinceLastUse.StoredCreditCard.Server.Unmasked", 30, 3); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.DaysSinceLastUse.StoredCreditCard.Server.Unmasked", 200, 3); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.StoredCreditCardCount.Server.WithCardArtImage", 2, 1); |
| } |
| |
| TEST_F(AutofillMetricsTest, LogStoredCreditCardWithNicknameMetrics) { |
| std::vector<std::unique_ptr<CreditCard>> local_cards; |
| std::vector<std::unique_ptr<CreditCard>> server_cards; |
| local_cards.reserve(2); |
| server_cards.reserve(4); |
| |
| // Create cards with and without nickname of each record type: 1 of each for |
| // local, 2 of each for masked. |
| const std::vector<CreditCard::RecordType> record_types{ |
| CreditCard::RecordType::kLocalCard, |
| CreditCard::RecordType::kMaskedServerCard}; |
| int num_cards_of_type = 0; |
| for (auto record_type : record_types) { |
| num_cards_of_type += 1; |
| for (int i = 0; i < num_cards_of_type; ++i) { |
| // Create a card with a nickname. |
| CreditCard card_with_nickname = test::GetRandomCreditCard(record_type); |
| card_with_nickname.SetNickname(u"Valid nickname"); |
| |
| // Create a card that doesn't have a nickname. |
| CreditCard card_without_nickname = test::GetRandomCreditCard(record_type); |
| // Set nickname to empty. |
| card_without_nickname.SetNickname(u""); |
| |
| // Add the cards to the personal data manager in the appropriate way. |
| auto& repo = (record_type == CreditCard::RecordType::kLocalCard) |
| ? local_cards |
| : server_cards; |
| repo.push_back( |
| std::make_unique<CreditCard>(std::move(card_with_nickname))); |
| repo.push_back( |
| std::make_unique<CreditCard>(std::move(card_without_nickname))); |
| } |
| } |
| |
| // Log the stored credit card metrics for the cards configured above. |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::LogStoredCreditCardMetrics( |
| local_cards, server_cards, /*server_card_count_with_card_art_image=*/0, |
| base::Days(180)); |
| |
| // Validate the count metrics. |
| histogram_tester.ExpectTotalCount("Autofill.StoredCreditCardCount", 1); |
| histogram_tester.ExpectTotalCount("Autofill.StoredCreditCardCount.Local", 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.StoredCreditCardCount.Local.WithNickname", 1); |
| histogram_tester.ExpectTotalCount("Autofill.StoredCreditCardCount.Server", 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.StoredCreditCardCount.Server.Masked", 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.StoredCreditCardCount.Server.Masked.WithNickname", 1); |
| histogram_tester.ExpectBucketCount("Autofill.StoredCreditCardCount", 6, 1); |
| histogram_tester.ExpectBucketCount("Autofill.StoredCreditCardCount.Local", 2, |
| 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.StoredCreditCardCount.Local.WithNickname", 1, 1); |
| histogram_tester.ExpectBucketCount("Autofill.StoredCreditCardCount.Server", 4, |
| 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.StoredCreditCardCount.Server.Masked", 4, 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.StoredCreditCardCount.Server.Masked.WithNickname", 2, 1); |
| } |
| |
| // Tests that local cards with invalid card numbers are correctly logged. |
| TEST_F(AutofillMetricsTest, LogStoredCreditCardWithInvalidCardNumberMetrics) { |
| // Only local cards can have invalid card numbers. |
| CreditCard card_with_valid_card_number = |
| test::GetRandomCreditCard(CreditCard::RecordType::kLocalCard); |
| card_with_valid_card_number.SetNumber(u"4444333322221111"); |
| CreditCard card_with_invalid_card_number = |
| test::GetRandomCreditCard(CreditCard::RecordType::kLocalCard); |
| card_with_invalid_card_number.SetNumber(u"4444333322221115"); |
| CreditCard card_with_non_digit_card_number = |
| test::GetRandomCreditCard(CreditCard::RecordType::kLocalCard); |
| card_with_non_digit_card_number.SetNumber(u"invalid_number"); |
| |
| std::vector<std::unique_ptr<CreditCard>> local_cards; |
| local_cards.push_back( |
| std::make_unique<CreditCard>(card_with_valid_card_number)); |
| local_cards.push_back( |
| std::make_unique<CreditCard>(card_with_non_digit_card_number)); |
| local_cards.push_back( |
| std::make_unique<CreditCard>(card_with_invalid_card_number)); |
| std::vector<std::unique_ptr<CreditCard>> server_cards; |
| |
| // Log the stored credit card metrics for the cards configured above. |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::LogStoredCreditCardMetrics( |
| local_cards, server_cards, /*server_card_count_with_card_art_image=*/0, |
| base::Days(180)); |
| |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.StoredCreditCardCount.Local.WithInvalidCardNumber", 2, 1); |
| } |
| |
| // Test that the credit card checkout flow user actions are correctly logged. |
| TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) { |
| // Disable mandatory reauth as it is not part of this test and will |
| // interfere with the card retrieval flow. |
| personal_data() |
| .payments_data_manager() |
| .SetPaymentMethodsMandatoryReauthEnabled(false); |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/false, |
| /*include_full_server_credit_card=*/false, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| |
| FormData form = |
| CreateForm({CreateTestFormField("Name on card", "cc-name", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Credit card", "cardnum", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Expiration date", "expdate", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = {CREDIT_CARD_NAME_FULL, |
| CREDIT_CARD_NUMBER, |
| CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| // Simulate an Autofill query on a credit card field. |
| { |
| base::UserActionTester user_action_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.front()); |
| EXPECT_EQ(1, user_action_tester.GetActionCount( |
| "Autofill_PolledCreditCardSuggestions")); |
| } |
| |
| // Simulate showing a credit card suggestion polled from "Name on card" field. |
| { |
| base::UserActionTester user_action_tester; |
| DidShowAutofillSuggestions(form, /*field_index=*/0, |
| SuggestionType::kCreditCardEntry); |
| EXPECT_EQ(1, user_action_tester.GetActionCount( |
| "Autofill_ShowedCreditCardSuggestions")); |
| } |
| |
| // Simulate showing a credit card suggestion polled from "Credit card number" |
| // field. |
| { |
| base::UserActionTester user_action_tester; |
| DidShowAutofillSuggestions(form, /*field_index=*/1, |
| SuggestionType::kCreditCardEntry); |
| EXPECT_EQ(1, user_action_tester.GetActionCount( |
| "Autofill_ShowedCreditCardSuggestions")); |
| } |
| |
| // Simulate selecting a credit card suggestions. |
| { |
| base::UserActionTester user_action_tester; |
| external_delegate().OnQuery( |
| form, form.fields.front(), gfx::RectF(), |
| AutofillSuggestionTriggerSource::kFormControlElementClicked); |
| |
| external_delegate().DidAcceptSuggestion( |
| test::CreateAutofillSuggestion(SuggestionType::kCreditCardEntry, |
| u"Test", |
| Suggestion::Guid(kTestLocalCardId)), |
| AutofillPopupDelegate::SuggestionPosition{.row = 0}); |
| |
| EXPECT_EQ(1, |
| user_action_tester.GetActionCount("Autofill_SelectedSuggestion")); |
| } |
| |
| // Simulate showing a credit card suggestion polled from "Credit card number" |
| // field along with a "Clear form" footer suggestion. |
| { |
| base::UserActionTester user_action_tester; |
| DidShowAutofillSuggestions(form, /*field_index=*/1, |
| SuggestionType::kCreditCardEntry); |
| EXPECT_EQ(1, user_action_tester.GetActionCount( |
| "Autofill_ShowedCreditCardSuggestions")); |
| } |
| |
| #if !BUILDFLAG(IS_IOS) |
| // Simulate selecting an "Undo autofill" suggestion. |
| { |
| base::UserActionTester user_action_tester; |
| external_delegate().OnQuery( |
| form, form.fields.front(), gfx::RectF(), |
| AutofillSuggestionTriggerSource::kFormControlElementClicked); |
| |
| external_delegate().DidAcceptSuggestion( |
| Suggestion(SuggestionType::kClearForm), |
| AutofillPopupDelegate::SuggestionPosition{.row = 0}); |
| |
| EXPECT_EQ( |
| 1, user_action_tester.GetActionCount("Autofill_UndoPaymentsAutofill")); |
| } |
| #endif |
| |
| // Simulate showing a credit card suggestion polled from "Credit card number" |
| // field, this time to submit the form. |
| { |
| base::UserActionTester user_action_tester; |
| DidShowAutofillSuggestions(form, /*field_index=*/1, |
| SuggestionType::kCreditCardEntry); |
| EXPECT_EQ(1, user_action_tester.GetActionCount( |
| "Autofill_ShowedCreditCardSuggestions")); |
| } |
| |
| // Simulate selecting a credit card suggestions. |
| { |
| base::UserActionTester user_action_tester; |
| external_delegate().OnQuery( |
| form, form.fields.front(), gfx::RectF(), |
| AutofillSuggestionTriggerSource::kFormControlElementClicked); |
| |
| external_delegate().DidAcceptSuggestion( |
| test::CreateAutofillSuggestion(SuggestionType::kCreditCardEntry, |
| u"Test", |
| Suggestion::Guid(kTestLocalCardId)), |
| AutofillPopupDelegate::SuggestionPosition{.row = 0}); |
| |
| EXPECT_EQ(1, |
| user_action_tester.GetActionCount("Autofill_SelectedSuggestion")); |
| } |
| |
| // Simulate filling a credit card suggestion. |
| { |
| base::UserActionTester user_action_tester; |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.front(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestLocalCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| EXPECT_EQ(1, user_action_tester.GetActionCount( |
| "Autofill_FilledCreditCardSuggestion")); |
| } |
| |
| // Simulate submitting the credit card form. |
| { |
| base::UserActionTester user_action_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| SubmitForm(form); |
| EXPECT_EQ(1, |
| user_action_tester.GetActionCount("Autofill_OnWillSubmitForm")); |
| } |
| |
| // Expect one record for a click on the cardholder name field and one record |
| // for each of the 3 clicks on the card number field. |
| ExpectedUkmMetricsRecord name_field_record{ |
| {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}, |
| {UkmTextFieldDidChangeType::kHeuristicTypeName, CREDIT_CARD_NAME_FULL}, |
| {UkmTextFieldDidChangeType::kHtmlFieldTypeName, |
| HtmlFieldType::kUnspecified}, |
| {UkmTextFieldDidChangeType::kServerTypeName, CREDIT_CARD_NAME_FULL}, |
| {UkmSuggestionsShownType::kFieldSignatureName, |
| Collapse(CalculateFieldSignatureForField(form.fields[0])).value()}, |
| {UkmSuggestionsShownType::kFormSignatureName, |
| Collapse(CalculateFormSignature(form)).value()}}; |
| ExpectedUkmMetricsRecord number_field_record{ |
| {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}, |
| {UkmTextFieldDidChangeType::kHeuristicTypeName, CREDIT_CARD_NUMBER}, |
| {UkmTextFieldDidChangeType::kHtmlFieldTypeName, |
| HtmlFieldType::kUnspecified}, |
| {UkmTextFieldDidChangeType::kServerTypeName, CREDIT_CARD_NUMBER}, |
| {UkmSuggestionsShownType::kFieldSignatureName, |
| Collapse(CalculateFieldSignatureForField(form.fields[1])).value()}, |
| {UkmSuggestionsShownType::kFormSignatureName, |
| Collapse(CalculateFormSignature(form)).value()}}; |
| VerifyUkm(&test_ukm_recorder(), form, UkmSuggestionsShownType::kEntryName, |
| {name_field_record, number_field_record, number_field_record, |
| number_field_record}); |
| |
| // Expect 3 |FORM_EVENT_LOCAL_SUGGESTION_FILLED| events. First, from |
| // call to |external_delegate().DidAcceptSuggestion|. Second and third, from |
| // ExpectedUkmMetrics |autofill_manager().AuthenticateThenFillCreditCardForm|. |
| ExpectedUkmMetricsRecord from_did_accept_suggestion{ |
| {UkmSuggestionFilledType::kRecordTypeName, |
| base::to_underlying(CreditCard::RecordType::kLocalCard)}, |
| {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}, |
| {UkmSuggestionFilledType::kIsForCreditCardName, true}, |
| {UkmSuggestionFilledType::kFieldSignatureName, |
| Collapse(CalculateFieldSignatureForField(form.fields.front())).value()}, |
| {UkmSuggestionFilledType::kFormSignatureName, |
| Collapse(CalculateFormSignature(form)).value()}}; |
| ExpectedUkmMetricsRecord from_fill_or_preview_form{ |
| {UkmSuggestionFilledType::kRecordTypeName, |
| base::to_underlying(CreditCard::RecordType::kLocalCard)}, |
| {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}, |
| {UkmSuggestionFilledType::kIsForCreditCardName, true}, |
| {UkmSuggestionFilledType::kFieldSignatureName, |
| Collapse(CalculateFieldSignatureForField(form.fields.front())).value()}, |
| {UkmSuggestionFilledType::kFormSignatureName, |
| Collapse(CalculateFormSignature(form)).value()}}; |
| VerifyUkm(&test_ukm_recorder(), form, UkmSuggestionFilledType::kEntryName, |
| {from_did_accept_suggestion, from_fill_or_preview_form, |
| from_fill_or_preview_form}); |
| } |
| |
| // Test that the profile checkout flow user actions are correctly logged. |
| TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) { |
| RecreateProfile(); |
| |
| FormData form = CreateForm( |
| {CreateTestFormField("State", "state", "", FormControlType::kInputText), |
| CreateTestFormField("City", "city", "", FormControlType::kInputText), |
| CreateTestFormField("Street", "street", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = {ADDRESS_HOME_STATE, ADDRESS_HOME_CITY, |
| ADDRESS_HOME_STREET_ADDRESS}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| // Simulate an Autofill query on a profile field. |
| { |
| base::UserActionTester user_action_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| EXPECT_EQ(1, user_action_tester.GetActionCount( |
| "Autofill_PolledProfileSuggestions")); |
| } |
| |
| // Simulate showing a profile suggestion polled from "State" field. |
| { |
| base::UserActionTester user_action_tester; |
| DidShowAutofillSuggestions(form); |
| EXPECT_EQ(1, user_action_tester.GetActionCount( |
| "Autofill_ShowedProfileSuggestions")); |
| } |
| |
| // Simulate showing a profile suggestion polled from "City" field. |
| { |
| base::UserActionTester user_action_tester; |
| DidShowAutofillSuggestions(form, /*field_index=*/1); |
| EXPECT_EQ(1, user_action_tester.GetActionCount( |
| "Autofill_ShowedProfileSuggestions")); |
| } |
| |
| // Simulate selecting a profile suggestions. |
| { |
| base::UserActionTester user_action_tester; |
| external_delegate().OnQuery( |
| form, form.fields.front(), gfx::RectF(), |
| AutofillSuggestionTriggerSource::kFormControlElementClicked); |
| |
| external_delegate().DidAcceptSuggestion( |
| test::CreateAutofillSuggestion(SuggestionType::kCreditCardEntry, |
| u"Test", |
| Suggestion::Guid(kTestProfileId)), |
| AutofillPopupDelegate::SuggestionPosition{.row = 0}); |
| |
| EXPECT_EQ(1, |
| user_action_tester.GetActionCount("Autofill_SelectedSuggestion")); |
| } |
| |
| // Simulate filling a profile suggestion. |
| { |
| base::UserActionTester user_action_tester; |
| FillTestProfile(form); |
| EXPECT_EQ(1, user_action_tester.GetActionCount( |
| "Autofill_FilledProfileSuggestion")); |
| } |
| |
| // Simulate submitting the profile form. |
| { |
| base::UserActionTester user_action_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| SubmitForm(form); |
| EXPECT_EQ(1, |
| user_action_tester.GetActionCount("Autofill_OnWillSubmitForm")); |
| } |
| |
| VerifyUkm( |
| &test_ukm_recorder(), form, UkmSuggestionsShownType::kEntryName, |
| {{{UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}, |
| {UkmTextFieldDidChangeType::kHeuristicTypeName, ADDRESS_HOME_STATE}, |
| {UkmTextFieldDidChangeType::kHtmlFieldTypeName, |
| HtmlFieldType::kUnspecified}, |
| {UkmTextFieldDidChangeType::kServerTypeName, ADDRESS_HOME_STATE}, |
| {UkmSuggestionsShownType::kFieldSignatureName, |
| Collapse(CalculateFieldSignatureForField(form.fields[0])).value()}, |
| {UkmSuggestionsShownType::kFormSignatureName, |
| Collapse(CalculateFormSignature(form)).value()}}, |
| {{UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}, |
| {UkmTextFieldDidChangeType::kHeuristicTypeName, ADDRESS_HOME_CITY}, |
| {UkmTextFieldDidChangeType::kHtmlFieldTypeName, |
| HtmlFieldType::kUnspecified}, |
| {UkmTextFieldDidChangeType::kServerTypeName, ADDRESS_HOME_CITY}, |
| {UkmSuggestionsShownType::kFieldSignatureName, |
| Collapse(CalculateFieldSignatureForField(form.fields[1])).value()}, |
| {UkmSuggestionsShownType::kFormSignatureName, |
| Collapse(CalculateFormSignature(form)).value()}}}); |
| // Expect 2 |FORM_EVENT_LOCAL_SUGGESTION_FILLED| events. First, from |
| // call to |external_delegate().DidAcceptSuggestion|. Second, from call to |
| // |autofill_manager().FillOrPreviewProfileForm|. |
| VerifyUkm(&test_ukm_recorder(), form, UkmSuggestionFilledType::kEntryName, |
| {{{UkmSuggestionFilledType::kIsForCreditCardName, false}, |
| {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}, |
| {UkmSuggestionFilledType::kFieldSignatureName, |
| Collapse(CalculateFieldSignatureForField(form.fields.front())) |
| .value()}, |
| {UkmSuggestionFilledType::kFormSignatureName, |
| Collapse(CalculateFormSignature(form)).value()}}, |
| {{UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}, |
| {UkmSuggestionFilledType::kIsForCreditCardName, false}, |
| {UkmSuggestionsShownType::kFieldSignatureName, |
| Collapse(CalculateFieldSignatureForField(form.fields.front())) |
| .value()}, |
| {UkmSuggestionsShownType::kFormSignatureName, |
| Collapse(CalculateFormSignature(form)).value()}}}); |
| } |
| |
| // Tests that the Autofill_PolledCreditCardSuggestions user action is only |
| // logged once if the field is queried repeatedly. |
| TEST_F(AutofillMetricsTest, PolledCreditCardSuggestions_DebounceLogs) { |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/false, |
| /*include_full_server_credit_card=*/false, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| |
| FormData form = |
| CreateForm({CreateTestFormField("Name on card", "cc-name", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Credit card", "cardnum", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Expiration date", "expdate", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = {CREDIT_CARD_NAME_FULL, |
| CREDIT_CARD_NUMBER, |
| CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| // Simulate an Autofill query on a credit card field. A poll should be logged. |
| base::UserActionTester user_action_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| EXPECT_EQ(1, user_action_tester.GetActionCount( |
| "Autofill_PolledCreditCardSuggestions")); |
| |
| // Simulate a second query on the same field. There should still only be one |
| // logged poll. |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| EXPECT_EQ(1, user_action_tester.GetActionCount( |
| "Autofill_PolledCreditCardSuggestions")); |
| |
| // Simulate a query to another field. There should be a second poll logged. |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[1]); |
| EXPECT_EQ(2, user_action_tester.GetActionCount( |
| "Autofill_PolledCreditCardSuggestions")); |
| |
| // Simulate a query back to the initial field. There should be a third poll |
| // logged. |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| EXPECT_EQ(3, user_action_tester.GetActionCount( |
| "Autofill_PolledCreditCardSuggestions")); |
| } |
| |
| // Tests that the Autofill.QueriedCreditCardFormIsSecure histogram is logged |
| // properly. |
| TEST_F(AutofillMetricsTest, QueriedCreditCardFormIsSecure) { |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/false, |
| /*include_full_server_credit_card=*/false, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| |
| FormData form = |
| CreateForm({CreateTestFormField("Month", "card_month", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Year", "card_year", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Credit card", "cardnum", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = { |
| CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_2_DIGIT_YEAR, CREDIT_CARD_NUMBER}; |
| |
| { |
| // Simulate having seen this insecure form on page load. |
| form.host_frame = test::MakeLocalFrameToken(); |
| form.renderer_id = test::MakeFormRendererId(); |
| form.url = GURL("http://example.com/form.html"); |
| form.action = GURL("http://example.com/submit.html"); |
| // In order to test that the QueriedCreditCardFormIsSecure is logged as |
| // false, we need to set the main frame origin, otherwise this fill is |
| // skipped due to the form being detected as mixed content. |
| GURL client_form_origin = autofill_client_->form_origin(); |
| GURL::Replacements replacements; |
| replacements.SetSchemeStr(url::kHttpScheme); |
| autofill_client_->set_form_origin( |
| client_form_origin.ReplaceComponents(replacements)); |
| form.main_frame_origin = |
| url::Origin::Create(autofill_client_->form_origin()); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| // Simulate an Autofill query on a credit card field (HTTP, non-secure |
| // form). |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[1]); |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.QueriedCreditCardFormIsSecure", false, 1); |
| // Reset the main frame origin to secure for other tests |
| autofill_client_->set_form_origin(client_form_origin); |
| } |
| |
| { |
| autofill_manager().Reset(); |
| form.host_frame = test::MakeLocalFrameToken(); |
| form.renderer_id = test::MakeFormRendererId(); |
| form.url = GURL("https://example.com/form.html"); |
| form.action = GURL("https://example.com/submit.html"); |
| form.main_frame_origin = |
| url::Origin::Create(autofill_client_->form_origin()); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| // Simulate an Autofill query on a credit card field (HTTPS form). |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[1]); |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.QueriedCreditCardFormIsSecure", true, 1); |
| } |
| } |
| |
| // Tests that the Autofill_PolledProfileSuggestions user action is only logged |
| // once if the field is queried repeatedly. |
| TEST_F(AutofillMetricsTest, PolledProfileSuggestions_DebounceLogs) { |
| RecreateProfile(); |
| |
| FormData form = CreateForm( |
| {CreateTestFormField("State", "state", "", FormControlType::kInputText), |
| CreateTestFormField("City", "city", "", FormControlType::kInputText), |
| CreateTestFormField("Street", "street", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = {ADDRESS_HOME_STATE, ADDRESS_HOME_CITY, |
| ADDRESS_HOME_STREET_ADDRESS}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| // Simulate an Autofill query on a profile field. A poll should be logged. |
| base::UserActionTester user_action_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| EXPECT_EQ(1, user_action_tester.GetActionCount( |
| "Autofill_PolledProfileSuggestions")); |
| |
| // Simulate a second query on the same field. There should still only be poll |
| // logged. |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| EXPECT_EQ(1, user_action_tester.GetActionCount( |
| "Autofill_PolledProfileSuggestions")); |
| |
| // Simulate a query to another field. There should be a second poll logged. |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[1]); |
| EXPECT_EQ(2, user_action_tester.GetActionCount( |
| "Autofill_PolledProfileSuggestions")); |
| |
| // Simulate a query back to the initial field. There should be a third poll |
| // logged. |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| EXPECT_EQ(3, user_action_tester.GetActionCount( |
| "Autofill_PolledProfileSuggestions")); |
| } |
| |
| // Test that we log parsed form event for credit card forms. |
| TEST_P(AutofillMetricsIFrameTest, CreditCardParsedFormEvents) { |
| FormData form = |
| CreateForm({CreateTestFormField("Card Number", "card_number", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Expiration", "cc_exp", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Verification", "verification", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = {CREDIT_CARD_NAME_FULL, |
| CREDIT_CARD_EXP_MONTH, |
| CREDIT_CARD_VERIFICATION_CODE}; |
| |
| base::HistogramTester histogram_tester; |
| SeeForm(form); |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.FormEvents.CreditCard.WithNoData", FORM_EVENT_DID_PARSE_FORM, |
| 1); |
| } |
| |
| // Test that we log interacted form event for credit cards related. |
| TEST_P(AutofillMetricsIFrameTest, CreditCardInteractedFormEvents) { |
| FormData form = |
| CreateForm({CreateTestFormField("Month", "card_month", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Year", "card_year", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Credit card", "cardnum", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = { |
| CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_2_DIGIT_YEAR, CREDIT_CARD_NUMBER}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulate activating the autofill popup for the credit card field. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| histogram_tester.ExpectUniqueSample("Autofill.FormEvents.CreditCard", |
| FORM_EVENT_INTERACTED_ONCE, 1); |
| histogram_tester.ExpectUniqueSample( |
| credit_card_form_events_frame_histogram_, FORM_EVENT_INTERACTED_ONCE, |
| 1); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulate activating the autofill popup for the credit card field twice. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| histogram_tester.ExpectUniqueSample("Autofill.FormEvents.CreditCard", |
| FORM_EVENT_INTERACTED_ONCE, 1); |
| histogram_tester.ExpectUniqueSample( |
| credit_card_form_events_frame_histogram_, FORM_EVENT_INTERACTED_ONCE, |
| 1); |
| } |
| } |
| |
| // Test that we log suggestion shown form events for credit cards. |
| TEST_P(AutofillMetricsIFrameTest, CreditCardShownFormEvents) { |
| FormData form = |
| CreateForm({CreateTestFormField("Month", "card_month", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Year", "card_year", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Credit card", "cardnum", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = { |
| CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_2_DIGIT_YEAR, CREDIT_CARD_NUMBER}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating new popup being shown. |
| base::HistogramTester histogram_tester; |
| DidShowAutofillSuggestions(form, /*field_index=*/0, |
| SuggestionType::kCreditCardEntry); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude(Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 1), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1))); |
| EXPECT_THAT(histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude(Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 1), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1))); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating two popups in the same page load. |
| base::HistogramTester histogram_tester; |
| DidShowAutofillSuggestions(form, /*field_index=*/0, |
| SuggestionType::kCreditCardEntry); |
| DidShowAutofillSuggestions(form, /*field_index=*/0, |
| SuggestionType::kCreditCardEntry); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude(Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 2), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1))); |
| EXPECT_THAT(histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude(Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 2), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1))); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating same popup being refreshed. |
| // Suggestions not related to credit cards/addresses should not affect the |
| // histograms. |
| base::HistogramTester histogram_tester; |
| DidShowAutofillSuggestions(form, /*field_index=*/0, |
| SuggestionType::kAutocompleteEntry); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsAre(Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 0), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 0))); |
| EXPECT_THAT(histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsAre(Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 0), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 0))); |
| } |
| } |
| |
| // Test that we log specific suggestion shown form events for virtual credit |
| // cards. |
| TEST_P(AutofillMetricsIFrameTest, VirtualCreditCardShownFormEvents) { |
| FormData form = CreateForm( |
| {CreateTestFormField("Month", "card_month", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Year", "card_year", "", |
| FormControlType::kInputText), |
| CreateTestFormField("CVC", "cvc", "", FormControlType::kInputText), |
| CreateTestFormField("Credit card", "cardnum", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = { |
| CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_2_DIGIT_YEAR, |
| CREDIT_CARD_VERIFICATION_CODE, CREDIT_CARD_NUMBER}; |
| |
| // Creating cards, including a virtual card. |
| RecreateCreditCards(/*include_local_credit_card=*/false, |
| /*include_masked_server_credit_card=*/true, |
| /*include_full_server_credit_card=*/false, |
| /*masked_card_is_enrolled_for_virtual_card*/ true); |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulate new popup being shown. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); |
| DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, |
| SuggestionType::kCreditCardEntry); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 1), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_WITH_VIRTUAL_CARD, 1), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_WITH_VIRTUAL_CARD_ONCE, 1))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 1), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_WITH_VIRTUAL_CARD, 1), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_WITH_VIRTUAL_CARD_ONCE, 1))); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating two popups in the same page load. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); |
| DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, |
| SuggestionType::kCreditCardEntry); |
| DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, |
| SuggestionType::kCreditCardEntry); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 2), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_WITH_VIRTUAL_CARD, 2), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_WITH_VIRTUAL_CARD_ONCE, 1))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 2), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_WITH_VIRTUAL_CARD, 2), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_WITH_VIRTUAL_CARD_ONCE, 1))); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating same popup being refreshed. |
| // Suggestions not related to credit cards/addresses should not affect the |
| // histograms. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); |
| DidShowAutofillSuggestions(form, |
| /*field_index=*/form.fields.size() - 1, |
| SuggestionType::kAutocompleteEntry); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 0), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 0), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_WITH_VIRTUAL_CARD, 0), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_WITH_VIRTUAL_CARD_ONCE, 0))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 0), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 0), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_WITH_VIRTUAL_CARD, 0), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_WITH_VIRTUAL_CARD_ONCE, 0))); |
| } |
| |
| // Recreate cards, this time *without* a virtual card. |
| RecreateCreditCards(/*include_local_credit_card=*/false, |
| /*include_masked_server_credit_card=*/true, |
| /*include_full_server_credit_card=*/false, |
| /*masked_card_is_enrolled_for_virtual_card*/ false); |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating two popups in the same page load. Suggestions shown should be |
| // logged, but suggestions shown with virtual card should not. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); |
| DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, |
| SuggestionType::kCreditCardEntry); |
| DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, |
| SuggestionType::kCreditCardEntry); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 2), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_WITH_VIRTUAL_CARD, 0), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_WITH_VIRTUAL_CARD_ONCE, 0))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 2), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_WITH_VIRTUAL_CARD, 0), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_WITH_VIRTUAL_CARD_ONCE, 0))); |
| } |
| } |
| |
| // Test that we log selected form event for credit cards. |
| TEST_P(AutofillMetricsIFrameTest, CreditCardSelectedFormEvents) { |
| // Creating all kinds of cards. |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/true, |
| /*include_full_server_credit_card=*/true, |
| /*masked_card_is_enrolled_for_virtual_card=*/true); |
| FormData form = |
| CreateForm({CreateTestFormField("Month", "card_month", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Year", "card_year", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Credit card", "cardnum", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = { |
| CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_2_DIGIT_YEAR, CREDIT_CARD_NUMBER}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating selecting a masked server card suggestion. |
| base::HistogramTester histogram_tester; |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields[2], |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestMaskedCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1))); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating selecting a masked server card multiple times. |
| base::HistogramTester histogram_tester; |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields[2], |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestMaskedCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields[2], |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestMaskedCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 2), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 2), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1))); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating selecting a virtual server suggestion by selecting the |
| // option based on the enrolled masked card. |
| base::HistogramTester histogram_tester; |
| CreditCard virtual_card = GetVirtualCreditCard(kTestMaskedCardId); |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields[2], virtual_card, |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| OnCreditCardFetchingSuccessful(u"6011000990139424", |
| /*is_virtual_card=*/true); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_VIRTUAL_CARD_SUGGESTION_SELECTED, 1), |
| Bucket(FORM_EVENT_VIRTUAL_CARD_SUGGESTION_SELECTED_ONCE, 1))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_VIRTUAL_CARD_SUGGESTION_SELECTED, 1), |
| Bucket(FORM_EVENT_VIRTUAL_CARD_SUGGESTION_SELECTED_ONCE, 1))); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating selecting a virtual card multiple times. |
| base::HistogramTester histogram_tester; |
| CreditCard virtual_card = GetVirtualCreditCard(kTestMaskedCardId); |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields[2], virtual_card, |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| OnCreditCardFetchingSuccessful(u"6011000990139424", |
| /*is_virtual_card=*/true); |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields[2], virtual_card, |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| OnCreditCardFetchingSuccessful(u"6011000990139424", |
| /*is_virtual_card=*/true); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_VIRTUAL_CARD_SUGGESTION_SELECTED, 2), |
| Bucket(FORM_EVENT_VIRTUAL_CARD_SUGGESTION_SELECTED_ONCE, 1))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_VIRTUAL_CARD_SUGGESTION_SELECTED, 2), |
| Bucket(FORM_EVENT_VIRTUAL_CARD_SUGGESTION_SELECTED_ONCE, 1))); |
| } |
| } |
| |
| // Test 1 of 2 for crbug/1513307, to ensure legacy deprecated metrics are not |
| // logged if card number is not filled and an unmask request is not sent, but |
| // that the bugfix's new metric buckets are indeed logged. |
| TEST_P(AutofillMetricsIFrameTest, |
| CreditCardSelectedLegacyFormEvents_NotLoggedIfNoCardNumberFieldExists) { |
| // We only care about masked server card for this test, so only create that. |
| RecreateCreditCards(/*include_local_credit_card=*/false, |
| /*include_masked_server_credit_card=*/true, |
| /*include_full_server_credit_card=*/false, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| |
| // Create a form *without* a card number, to avoid triggering an unmask |
| // request to Payments. The deprecated metrics should not be logged. |
| FormData form = |
| CreateForm({CreateTestFormField("Name on card", "cc-name", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Month", "card_month", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Year", "card_year", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = {CREDIT_CARD_NAME_FULL, |
| CREDIT_CARD_EXP_MONTH, |
| CREDIT_CARD_EXP_2_DIGIT_YEAR}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulate selecting a masked server card suggestion. |
| base::HistogramTester histogram_tester; |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields[0], |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestMaskedCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1), |
| Bucket(DEPRECATED_FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, |
| 0), |
| Bucket( |
| DEPRECATED_FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, |
| 0))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1), |
| Bucket(DEPRECATED_FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, |
| 0), |
| Bucket( |
| DEPRECATED_FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, |
| 0))); |
| |
| // Simulate accepting the suggestion again to test ONCE vs. non-ONCE |
| // metrics. |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields[0], |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestMaskedCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 2), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1), |
| Bucket(DEPRECATED_FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, |
| 0), |
| Bucket( |
| DEPRECATED_FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, |
| 0))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 2), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1), |
| Bucket(DEPRECATED_FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, |
| 0), |
| Bucket( |
| DEPRECATED_FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, |
| 0))); |
| } |
| } |
| |
| // Test 2 of 2 for crbug/1513307, to ensure legacy deprecated metrics are logged |
| // if card number is filled and an unmask request is sent, along with the |
| // bugfix's new metric buckets. |
| TEST_P(AutofillMetricsIFrameTest, |
| CreditCardSelectedLegacyFormEvents_LoggedIfCardNumberFieldExists) { |
| // We only care about masked server card for this test, so only create that. |
| RecreateCreditCards(/*include_local_credit_card=*/false, |
| /*include_masked_server_credit_card=*/true, |
| /*include_full_server_credit_card=*/false, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| |
| // Second, create a form *with* a card number, and ensure the deprecated |
| // metrics are also logged. |
| FormData form = |
| CreateForm({CreateTestFormField("Name on card", "cc-name", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Month", "card_month", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Year", "card_year", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Credit card", "cardnum", "", |
| FormControlType::kInputText)}); |
| std::vector<FieldType> field_types = { |
| CREDIT_CARD_NAME_FULL, CREDIT_CARD_EXP_MONTH, |
| CREDIT_CARD_EXP_2_DIGIT_YEAR, CREDIT_CARD_NUMBER}; |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulate selecting a masked server card suggestion. |
| base::HistogramTester histogram_tester; |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields[0], |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestMaskedCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1), |
| Bucket(DEPRECATED_FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, |
| 1), |
| Bucket( |
| DEPRECATED_FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, |
| 1))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1), |
| Bucket(DEPRECATED_FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, |
| 1), |
| Bucket( |
| DEPRECATED_FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, |
| 1))); |
| |
| // Simulate accepting the suggestion again to test ONCE vs. non-ONCE |
| // metrics. |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields[0], |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestMaskedCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 2), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1), |
| Bucket(DEPRECATED_FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, |
| 2), |
| Bucket( |
| DEPRECATED_FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, |
| 1))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 2), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1), |
| Bucket(DEPRECATED_FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, |
| 2), |
| Bucket( |
| DEPRECATED_FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, |
| 1))); |
| } |
| } |
| |
| // Test that we log filled form events for credit cards. |
| TEST_P(AutofillMetricsIFrameTest, CreditCardFilledFormEvents) { |
| // Disable mandatory reauth as it is not part of this test and will |
| // interfere with the card retrieval flow. |
| personal_data() |
| .payments_data_manager() |
| .SetPaymentMethodsMandatoryReauthEnabled(false); |
| // Creating all kinds of cards. |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/true, |
| /*include_full_server_credit_card=*/true, |
| /*masked_card_is_enrolled_for_virtual_card=*/true); |
| FormData form = |
| CreateForm({CreateTestFormField("Month", "card_month", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Year", "card_year", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Credit card", "cardnum", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = { |
| CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_2_DIGIT_YEAR, CREDIT_CARD_NUMBER}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating filling a local card suggestion. |
| base::HistogramTester histogram_tester; |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.front(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestLocalCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude(Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED, 1), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE, 1))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude(Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED, 1), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE, 1))); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating filling a virtual card suggestion by selecting the option |
| // based on the enrolled masked card. |
| base::HistogramTester histogram_tester; |
| CreditCard virtual_card = GetVirtualCreditCard(kTestMaskedCardId); |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.front(), virtual_card, |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| OnCreditCardFetchingSuccessful(u"6011000990139424", |
| /*is_virtual_card=*/true); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_VIRTUAL_CARD_SUGGESTION_FILLED, 1), |
| Bucket(FORM_EVENT_VIRTUAL_CARD_SUGGESTION_FILLED_ONCE, 1))); |
| EXPECT_THAT(histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_VIRTUAL_CARD_SUGGESTION_FILLED, 1), |
| Bucket(FORM_EVENT_VIRTUAL_CARD_SUGGESTION_FILLED_ONCE, 1))); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating filling a masked card server suggestion. |
| base::HistogramTester histogram_tester; |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.back(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestMaskedCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| OnCreditCardFetchingSuccessful(u"6011000990139424"); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE, 1))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE, 1))); |
| } |
| |
| // Recreating cards as the previous test should have upgraded the masked |
| // card to a full card. |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/true, |
| /*include_full_server_credit_card=*/true, |
| /*masked_card_is_enrolled_for_virtual_card=*/true); |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating filling a full card server suggestion. |
| base::HistogramTester histogram_tester; |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.front(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestFullServerCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude(Bucket(FORM_EVENT_SERVER_SUGGESTION_FILLED, 1), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_FILLED_ONCE, 1))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude(Bucket(FORM_EVENT_SERVER_SUGGESTION_FILLED, 1), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_FILLED_ONCE, 1))); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating filling multiple times. |
| base::HistogramTester histogram_tester; |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.front(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestLocalCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.front(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestLocalCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude(Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED, 2), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE, 1))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude(Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED, 2), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE, 1))); |
| } |
| } |
| |
| // Test to log when an unique local card is autofilled, when other duplicated |
| // server and local cards exist. |
| TEST_P( |
| AutofillMetricsIFrameTest, |
| CreditCardFilledFormEventsUsingUniqueLocalCardWhenOtherDuplicateServerCardsPresent) { |
| // Clearing all the existing cards and creating a local credit card. |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/false, |
| /*include_full_server_credit_card*/ false, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| CreateLocalAndDuplicateServerCreditCard(); |
| std::string local_guid = kTestLocalCardId; |
| |
| // Set up our form data. |
| FormData form = test::GetFormData( |
| {.description_for_logging = "PaymentProfileImportRequirements", |
| .fields = {{.role = CREDIT_CARD_EXP_MONTH, .value = u""}, |
| {.role = CREDIT_CARD_EXP_2_DIGIT_YEAR, .value = u""}, |
| {.role = CREDIT_CARD_NUMBER, .value = u""}}}); |
| std::vector<FieldType> field_types = { |
| CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_2_DIGIT_YEAR, CREDIT_CARD_NUMBER}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| // Simulate filling a unique local card suggestion. |
| base::HistogramTester histogram_tester; |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.front(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID(local_guid), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED, 1), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE, 1), |
| Bucket( |
| FORM_EVENT_LOCAL_SUGGESTION_FILLED_FOR_AN_EXISTING_SERVER_CARD_ONCE, |
| 0))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples(credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED, 1), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE, 1), |
| Bucket( |
| FORM_EVENT_LOCAL_SUGGESTION_FILLED_FOR_AN_EXISTING_SERVER_CARD_ONCE, |
| 0))); |
| } |
| |
| // Test to log when a server card is autofilled and a local card with the same |
| // number exists. |
| TEST_P(AutofillMetricsIFrameTest, |
| CreditCardFilledFormEvents_UsingServerCard_WithLocalDuplicate) { |
| RecreateCreditCards(/*include_local_credit_card=*/false, |
| /*include_masked_server_credit_card=*/true, |
| /*include_full_server_credit_card=*/false, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| CreateLocalAndDuplicateServerCreditCard(); |
| std::string local_guid = kTestDuplicateMaskedCardId; |
| // Set up our form data. |
| FormData form = test::GetFormData( |
| {.description_for_logging = "PaymentProfileImportRequirements", |
| .fields = {{.role = CREDIT_CARD_EXP_MONTH, .value = u""}, |
| {.role = CREDIT_CARD_EXP_2_DIGIT_YEAR, .value = u""}, |
| {.role = CREDIT_CARD_NUMBER, .value = u""}}}); |
| std::vector<FieldType> field_types = { |
| CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_2_DIGIT_YEAR, CREDIT_CARD_NUMBER}; |
| |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| // Simulate filling a server card suggestion with a duplicate local card. |
| base::HistogramTester histogram_tester; |
| // Server card with a duplicate local card present at index 0. |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.front(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID(local_guid), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); |
| DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1); |
| OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kSuccess, |
| "5454545454545454"); |
| SubmitForm(form); |
| |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE, 1), |
| Bucket( |
| FORM_EVENT_SERVER_CARD_SUGGESTION_SELECTED_FOR_AN_EXISTING_LOCAL_CARD_ONCE, |
| 1), |
| Bucket(FORM_EVENT_SERVER_CARD_FILLED_FOR_AN_EXISTING_LOCAL_CARD_ONCE, |
| 1), |
| Bucket( |
| FORM_EVENT_SERVER_CARD_SUBMITTED_FOR_AN_EXISTING_LOCAL_CARD_ONCE, |
| 1))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples(credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE, 1), |
| Bucket( |
| FORM_EVENT_SERVER_CARD_SUGGESTION_SELECTED_FOR_AN_EXISTING_LOCAL_CARD_ONCE, |
| 1), |
| Bucket(FORM_EVENT_SERVER_CARD_FILLED_FOR_AN_EXISTING_LOCAL_CARD_ONCE, |
| 1), |
| Bucket( |
| FORM_EVENT_SERVER_CARD_SUBMITTED_FOR_AN_EXISTING_LOCAL_CARD_ONCE, |
| 1))); |
| } |
| |
| // Test to log when a unique server card is autofilled and a different server |
| // card suggestion has the same number as a local card. That is, for local card |
| // A and server card B with the same number, this fills unrelated server card C. |
| TEST_P(AutofillMetricsIFrameTest, |
| CreditCardFilledFormEvents_UsingServerCard_WithoutLocalDuplicate) { |
| RecreateCreditCards(/*include_local_credit_card=*/false, |
| /*include_masked_server_credit_card=*/true, |
| /*include_full_server_credit_card*/ false, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| CreateLocalAndDuplicateServerCreditCard(); |
| std::string local_guid = kTestMaskedCardId; |
| // Set up our form data. |
| FormData form = test::GetFormData( |
| {.description_for_logging = "PaymentProfileImportRequirements", |
| .fields = {{.role = CREDIT_CARD_EXP_MONTH, .value = u""}, |
| {.role = CREDIT_CARD_EXP_2_DIGIT_YEAR, .value = u""}, |
| {.role = CREDIT_CARD_NUMBER, .value = u""}}}); |
| std::vector<FieldType> field_types = { |
| CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_2_DIGIT_YEAR, CREDIT_CARD_NUMBER}; |
| |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| // Simulate filling a server card suggestion with a duplicate local card. |
| base::HistogramTester histogram_tester; |
| // Server card with a duplicate local card present at index 0. |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.front(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID(local_guid), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); |
| DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1); |
| OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kSuccess, |
| "6011000990139424"); |
| SubmitForm(form); |
| |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE, 1), |
| Bucket( |
| FORM_EVENT_SERVER_CARD_SUGGESTION_SELECTED_FOR_AN_EXISTING_LOCAL_CARD_ONCE, |
| 0), |
| Bucket(FORM_EVENT_SERVER_CARD_FILLED_FOR_AN_EXISTING_LOCAL_CARD_ONCE, |
| 0), |
| Bucket( |
| FORM_EVENT_SERVER_CARD_SUBMITTED_FOR_AN_EXISTING_LOCAL_CARD_ONCE, |
| 0))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples(credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE, 1), |
| Bucket( |
| FORM_EVENT_SERVER_CARD_SUGGESTION_SELECTED_FOR_AN_EXISTING_LOCAL_CARD_ONCE, |
| 0), |
| Bucket(FORM_EVENT_SERVER_CARD_FILLED_FOR_AN_EXISTING_LOCAL_CARD_ONCE, |
| 0), |
| Bucket( |
| FORM_EVENT_SERVER_CARD_SUBMITTED_FOR_AN_EXISTING_LOCAL_CARD_ONCE, |
| 0))); |
| } |
| |
| // Test that we log submitted form events for credit cards. |
| TEST_F(AutofillMetricsTest, CreditCardGetRealPanDuration_ServerCard) { |
| // Creating masked card |
| RecreateCreditCards(/*include_local_credit_card=*/false, |
| /*include_masked_server_credit_card=*/true, |
| /*include_full_server_credit_card=*/false, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| FormData form = |
| CreateForm({CreateTestFormField("Month", "card_month", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Year", "card_year", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Credit card", "cardnum", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = { |
| CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_2_DIGIT_YEAR, CREDIT_CARD_NUMBER}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating filling a masked card server suggestion. |
| base::HistogramTester histogram_tester; |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.back(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestMaskedCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kSuccess, |
| "6011000990139424"); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.UnmaskPrompt.GetRealPanDuration", 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.UnmaskPrompt.GetRealPanDuration.ServerCard.Success", 1); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| // Creating masked card |
| RecreateCreditCards(/*include_local_credit_card=*/false, |
| /*include_masked_server_credit_card=*/true, |
| /*include_full_server_credit_card=*/false, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| |
| { |
| // Simulating filling a masked card server suggestion. |
| base::HistogramTester histogram_tester; |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.back(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestMaskedCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kPermanentFailure, |
| std::string()); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.UnmaskPrompt.GetRealPanDuration", 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.UnmaskPrompt.GetRealPanDuration.ServerCard.Failure", 1); |
| } |
| } |
| |
| // Test that a malformed or non-HTTP_OK response doesn't cause problems, per |
| // crbug/1267105. |
| TEST_F(AutofillMetricsTest, CreditCardGetRealPanDuration_BadServerResponse) { |
| // Creating masked card. |
| RecreateCreditCards(/*include_local_credit_card=*/false, |
| /*include_masked_server_credit_card=*/true, |
| /*include_full_server_credit_card=*/false, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| // Set up our form data. |
| FormData form = test::CreateTestCreditCardFormData( |
| /*is_https=*/true, |
| /*use_month_type=*/true, |
| /*split_names=*/false); |
| std::vector<FieldType> field_types{CREDIT_CARD_NAME_FULL, CREDIT_CARD_NUMBER, |
| CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR, |
| CREDIT_CARD_VERIFICATION_CODE}; |
| ASSERT_EQ(form.fields.size(), field_types.size()); |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating filling a masked card server suggestion. |
| base::HistogramTester histogram_tester; |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.back(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestMaskedCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| OnDidGetRealPanWithNonHttpOkResponse(); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.UnmaskPrompt.GetRealPanDuration", 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.UnmaskPrompt.GetRealPanDuration.UnknownCard.Failure", 1); |
| } |
| } |
| |
| TEST_F(AutofillMetricsTest, CreditCardGetRealPanResult_ServerCard) { |
| { |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::LogRealPanResult( |
| AutofillClient::PaymentsRpcResult::kTryAgainFailure, |
| AutofillClient::PaymentsRpcCardType::kServerCard); |
| |
| histogram_tester.ExpectBucketCount( |
| "Autofill.UnmaskPrompt.GetRealPanResult", |
| AutofillMetrics::PAYMENTS_RESULT_TRY_AGAIN_FAILURE, 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.UnmaskPrompt.GetRealPanResult.ServerCard", |
| AutofillMetrics::PAYMENTS_RESULT_TRY_AGAIN_FAILURE, 1); |
| } |
| |
| { |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::LogRealPanResult( |
| AutofillClient::PaymentsRpcResult::kPermanentFailure, |
| AutofillClient::PaymentsRpcCardType::kServerCard); |
| |
| histogram_tester.ExpectBucketCount( |
| "Autofill.UnmaskPrompt.GetRealPanResult", |
| AutofillMetrics::PAYMENTS_RESULT_PERMANENT_FAILURE, 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.UnmaskPrompt.GetRealPanResult.ServerCard", |
| AutofillMetrics::PAYMENTS_RESULT_PERMANENT_FAILURE, 1); |
| } |
| |
| { |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::LogRealPanResult( |
| AutofillClient::PaymentsRpcResult::kSuccess, |
| AutofillClient::PaymentsRpcCardType::kServerCard); |
| |
| histogram_tester.ExpectBucketCount("Autofill.UnmaskPrompt.GetRealPanResult", |
| AutofillMetrics::PAYMENTS_RESULT_SUCCESS, |
| 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.UnmaskPrompt.GetRealPanResult.ServerCard", |
| AutofillMetrics::PAYMENTS_RESULT_SUCCESS, 1); |
| } |
| } |
| |
| TEST_F(AutofillMetricsTest, CreditCardGetRealPanResult_VirtualCard) { |
| base::HistogramTester histogram_tester; |
| { |
| AutofillMetrics::LogRealPanResult( |
| AutofillClient::PaymentsRpcResult::kTryAgainFailure, |
| AutofillClient::PaymentsRpcCardType::kVirtualCard); |
| |
| histogram_tester.ExpectBucketCount( |
| "Autofill.UnmaskPrompt.GetRealPanResult", |
| AutofillMetrics::PAYMENTS_RESULT_TRY_AGAIN_FAILURE, 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.UnmaskPrompt.GetRealPanResult.VirtualCard", |
| AutofillMetrics::PAYMENTS_RESULT_TRY_AGAIN_FAILURE, 1); |
| } |
| |
| { |
| AutofillMetrics::LogRealPanResult( |
| AutofillClient::PaymentsRpcResult::kVcnRetrievalPermanentFailure, |
| AutofillClient::PaymentsRpcCardType::kVirtualCard); |
| |
| histogram_tester.ExpectBucketCount( |
| "Autofill.UnmaskPrompt.GetRealPanResult", |
| AutofillMetrics::PAYMENTS_RESULT_VCN_RETRIEVAL_PERMANENT_FAILURE, 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.UnmaskPrompt.GetRealPanResult.VirtualCard", |
| AutofillMetrics::PAYMENTS_RESULT_VCN_RETRIEVAL_PERMANENT_FAILURE, 1); |
| } |
| |
| { |
| AutofillMetrics::LogRealPanResult( |
| AutofillClient::PaymentsRpcResult::kSuccess, |
| AutofillClient::PaymentsRpcCardType::kVirtualCard); |
| |
| histogram_tester.ExpectBucketCount("Autofill.UnmaskPrompt.GetRealPanResult", |
| AutofillMetrics::PAYMENTS_RESULT_SUCCESS, |
| 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.UnmaskPrompt.GetRealPanResult.VirtualCard", |
| AutofillMetrics::PAYMENTS_RESULT_SUCCESS, 1); |
| } |
| } |
| |
| TEST_F(AutofillMetricsTest, |
| CreditCardSubmittedWithoutSelectingSuggestionsNoCard) { |
| // Create a local card for testing, card number is 4111111111111111. |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/false, |
| /*include_full_server_credit_card=*/false, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| |
| FormData form = |
| CreateForm({CreateTestFormField("Month", "card_month", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Year", "card_year", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Credit card", "cardnum", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = { |
| CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_2_DIGIT_YEAR, CREDIT_CARD_NUMBER}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| // Simulating submission with suggestion shown, but not selected. |
| base::HistogramTester histogram_tester; |
| DidShowAutofillSuggestions(form, /*field_index=*/0, |
| SuggestionType::kCreditCardEntry); |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| SubmitForm(form); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.FormEvents.CreditCard", |
| FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_NO_CARD, 1); |
| } |
| |
| TEST_P(AutofillMetricsIFrameTest, |
| CreditCardSubmittedWithoutSelectingSuggestionsWrongSizeCard) { |
| // Create a local card for testing, card number is 4111111111111111. |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/false, |
| /*include_full_server_credit_card=*/false, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| |
| FormData form = |
| CreateForm({CreateTestFormField("Month", "card_month", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Year", "card_year", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Credit card", "cardnum", "411111111", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = { |
| CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_2_DIGIT_YEAR, CREDIT_CARD_NUMBER}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| // Simulating submission with suggestion shown, but not selected. |
| base::HistogramTester histogram_tester; |
| DidShowAutofillSuggestions(form, /*field_index=*/0, |
| SuggestionType::kCreditCardEntry); |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| SubmitForm(form); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.FormEvents.CreditCard", |
| FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_WRONG_SIZE_CARD, 1); |
| histogram_tester.ExpectBucketCount( |
| credit_card_form_events_frame_histogram_, |
| FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_WRONG_SIZE_CARD, 1); |
| } |
| |
| TEST_P(AutofillMetricsIFrameTest, |
| CreditCardSubmittedWithoutSelectingSuggestionsFailLuhnCheckCard) { |
| // Create a local card for testing, card number is 4111111111111111. |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/false, |
| /*include_full_server_credit_card=*/false, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| |
| FormData form = CreateForm( |
| {CreateTestFormField("Month", "card_month", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Year", "card_year", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Credit card", "cardnum", "4444444444444444", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = { |
| CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_2_DIGIT_YEAR, CREDIT_CARD_NUMBER}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| // Simulating submission with suggestion shown, but not selected. |
| base::HistogramTester histogram_tester; |
| DidShowAutofillSuggestions(form, /*field_index=*/0, |
| SuggestionType::kCreditCardEntry); |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| SubmitForm(form); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.FormEvents.CreditCard", |
| FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_FAIL_LUHN_CHECK_CARD, 1); |
| histogram_tester.ExpectBucketCount( |
| credit_card_form_events_frame_histogram_, |
| FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_FAIL_LUHN_CHECK_CARD, 1); |
| } |
| |
| TEST_P(AutofillMetricsIFrameTest, |
| CreditCardSubmittedWithoutSelectingSuggestionsUnknownCard) { |
| // Create a local card for testing, card number is 4111111111111111. |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/false, |
| /*include_full_server_credit_card=*/false, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| |
| FormData form = CreateForm( |
| {CreateTestFormField("Month", "card_month", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Year", "card_year", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Credit card", "cardnum", "5105105105105100", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = { |
| CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_2_DIGIT_YEAR, CREDIT_CARD_NUMBER}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| // Simulating submission with suggestion shown, but not selected. |
| base::HistogramTester histogram_tester; |
| DidShowAutofillSuggestions(form, /*field_index=*/0, |
| SuggestionType::kCreditCardEntry); |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| SubmitForm(form); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.FormEvents.CreditCard", |
| FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_UNKNOWN_CARD, 1); |
| histogram_tester.ExpectBucketCount( |
| credit_card_form_events_frame_histogram_, |
| FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_UNKNOWN_CARD, 1); |
| } |
| |
| TEST_P(AutofillMetricsIFrameTest, |
| CreditCardSubmittedWithoutSelectingSuggestionsKnownCard) { |
| // Create a local card for testing, card number is 4111111111111111. |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/false, |
| /*include_full_server_credit_card=*/false, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| |
| FormData form = CreateForm( |
| {CreateTestFormField("Month", "card_month", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Year", "card_year", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Credit card", "cardnum", "4111111111111111", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = { |
| CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_2_DIGIT_YEAR, CREDIT_CARD_NUMBER}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| // Simulating submission with suggestion shown, but not selected. |
| base::HistogramTester histogram_tester; |
| DidShowAutofillSuggestions(form, /*field_index=*/0, |
| SuggestionType::kCreditCardEntry); |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| SubmitForm(form); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.FormEvents.CreditCard", |
| FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_KNOWN_CARD, 1); |
| histogram_tester.ExpectBucketCount( |
| credit_card_form_events_frame_histogram_, |
| FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_KNOWN_CARD, 1); |
| } |
| |
| TEST_P(AutofillMetricsIFrameTest, |
| ShouldNotLogSubmitWithoutSelectingSuggestionsIfSuggestionFilled) { |
| // Create a local card for testing, card number is 4111111111111111. |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/false, |
| /*include_full_server_credit_card=*/false, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| |
| FormData form = CreateForm( |
| {CreateTestFormField("Month", "card_month", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Year", "card_year", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Credit card", "cardnum", "4111111111111111", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = { |
| CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_2_DIGIT_YEAR, CREDIT_CARD_NUMBER}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| // Simulating submission with suggestion shown and selected. |
| base::HistogramTester histogram_tester; |
| DidShowAutofillSuggestions(form, /*field_index=*/0, |
| SuggestionType::kCreditCardEntry); |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.back(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestLocalCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_KNOWN_CARD, 0), |
| Bucket(FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_UNKNOWN_CARD, |
| 0), |
| Bucket(FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_NO_CARD, 0))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples(credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_KNOWN_CARD, 0), |
| Bucket(FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_KNOWN_CARD, 0), |
| Bucket(FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_KNOWN_CARD, |
| 0))); |
| } |
| |
| TEST_F(AutofillMetricsTest, ShouldNotLogFormEventNoCardForAddressForm) { |
| RecreateProfile(); |
| FormData form = CreateForm( |
| {CreateTestFormField("State", "state", "", FormControlType::kInputText), |
| CreateTestFormField("City", "city", "", FormControlType::kInputText), |
| CreateTestFormField("Street", "street", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = {ADDRESS_HOME_STATE, ADDRESS_HOME_CITY, |
| ADDRESS_HOME_STREET_ADDRESS}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| // Simulating submission with no filled data. |
| base::HistogramTester histogram_tester; |
| DidShowAutofillSuggestions(form); |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| SubmitForm(form); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.FormEvents.Address", |
| FORM_EVENT_SUBMIT_WITHOUT_SELECTING_SUGGESTIONS_NO_CARD, 0); |
| } |
| |
| // Test that we log submitted form events for credit cards. |
| TEST_P(AutofillMetricsIFrameTest, CreditCardSubmittedFormEvents) { |
| // Creating all kinds of cards. |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/true, |
| /*include_full_server_credit_card=*/true, |
| /*masked_card_is_enrolled_for_virtual_card=*/true); |
| FormData form = |
| CreateForm({CreateTestFormField("Month", "card_month", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Year", "card_year", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Credit card", "cardnum", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = { |
| CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_2_DIGIT_YEAR, CREDIT_CARD_NUMBER}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with no filled data. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); |
| SubmitForm(form); |
| |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude(Bucket(FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude(Bucket(FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1))); |
| } |
| |
| // Reset the autofill manager state and purge UKM logs. |
| PurgeUKM(); |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with suggestion shown. |
| base::HistogramTester histogram_tester; |
| DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, |
| SuggestionType::kCreditCardEntry); |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 1), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1))); |
| EXPECT_THAT(histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 1), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1))); |
| |
| VerifyUkm( |
| &test_ukm_recorder(), form, UkmSuggestionsShownType::kEntryName, |
| {{{UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}, |
| {UkmTextFieldDidChangeType::kHeuristicTypeName, CREDIT_CARD_NUMBER}, |
| {UkmTextFieldDidChangeType::kHtmlFieldTypeName, |
| HtmlFieldType::kUnspecified}, |
| {UkmTextFieldDidChangeType::kServerTypeName, CREDIT_CARD_NUMBER}, |
| {UkmSuggestionsShownType::kFieldSignatureName, |
| Collapse(CalculateFieldSignatureForField(form.fields[2])).value()}, |
| {UkmSuggestionsShownType::kFormSignatureName, |
| Collapse(CalculateFormSignature(form)).value()}}}); |
| } |
| |
| // Reset the autofill manager state and purge UKM logs. |
| PurgeUKM(); |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with suggestion shown. Form is submitted and |
| // autofill manager is reset before UploadFormDataAsyncCallback is |
| // triggered. |
| base::HistogramTester histogram_tester; |
| DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, |
| SuggestionType::kCreditCardEntry); |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); |
| SubmitForm(form); |
| // Trigger UploadFormDataAsyncCallback. |
| autofill_manager().Reset(); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 1), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1))); |
| EXPECT_THAT(histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 1), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1))); |
| |
| VerifyUkm( |
| &test_ukm_recorder(), form, UkmSuggestionsShownType::kEntryName, |
| {{{UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}, |
| {UkmTextFieldDidChangeType::kHeuristicTypeName, CREDIT_CARD_NUMBER}, |
| {UkmTextFieldDidChangeType::kHtmlFieldTypeName, |
| HtmlFieldType::kUnspecified}, |
| {UkmTextFieldDidChangeType::kServerTypeName, CREDIT_CARD_NUMBER}, |
| {UkmSuggestionsShownType::kFieldSignatureName, |
| Collapse(CalculateFieldSignatureForField(form.fields[2])).value()}, |
| {UkmSuggestionsShownType::kFormSignatureName, |
| Collapse(CalculateFormSignature(form)).value()}}}); |
| } |
| |
| // Reset the autofill manager state and purge UKM logs. |
| PurgeUKM(); |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with filled local data. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.front(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestLocalCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude(Bucket(FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 1))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude(Bucket(FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 1))); |
| |
| VerifyUkm(&test_ukm_recorder(), form, UkmSuggestionFilledType::kEntryName, |
| {{{UkmSuggestionFilledType::kRecordTypeName, |
| base::to_underlying(CreditCard::RecordType::kLocalCard)}, |
| {UkmSuggestionFilledType::kIsForCreditCardName, true}, |
| {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}, |
| {UkmSuggestionFilledType::kFieldSignatureName, |
| Collapse(CalculateFieldSignatureForField(form.fields.front())) |
| .value()}, |
| {UkmSuggestionFilledType::kFormSignatureName, |
| Collapse(CalculateFormSignature(form)).value()}}}); |
| } |
| |
| // Reset the autofill manager state and purge UKM logs. |
| PurgeUKM(); |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with filled virtual card data by selecting the |
| // option based on the enrolled masked card. |
| base::HistogramTester histogram_tester; |
| CreditCard virtual_card = GetVirtualCreditCard(kTestMaskedCardId); |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.front(), virtual_card, |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| OnCreditCardFetchingSuccessful(u"6011000990139424", |
| /*is_virtual_card=*/true); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_VIRTUAL_CARD_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_VIRTUAL_CARD_SUGGESTION_SUBMITTED_ONCE, 1))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_VIRTUAL_CARD_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_VIRTUAL_CARD_SUGGESTION_SUBMITTED_ONCE, 1))); |
| |
| VerifyUkm(&test_ukm_recorder(), form, UkmSuggestionFilledType::kEntryName, |
| {{{UkmSuggestionFilledType::kRecordTypeName, |
| base::to_underlying(CreditCard::RecordType::kVirtualCard)}, |
| {UkmSuggestionFilledType::kIsForCreditCardName, true}, |
| {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}, |
| {UkmSuggestionFilledType::kFieldSignatureName, |
| Collapse(CalculateFieldSignatureForField(form.fields.front())) |
| .value()}, |
| {UkmSuggestionFilledType::kFormSignatureName, |
| Collapse(CalculateFormSignature(form)).value()}}}); |
| } |
| |
| // Reset the autofill manager state and purge UKM logs. |
| PurgeUKM(); |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with filled server data. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.front(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestFullServerCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| SubmitForm(form); |
| |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude(Bucket(FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 1))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude(Bucket(FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 1))); |
| |
| VerifyUkm(&test_ukm_recorder(), form, UkmSuggestionFilledType::kEntryName, |
| {{{UkmSuggestionFilledType::kRecordTypeName, |
| base::to_underlying(CreditCard::RecordType::kFullServerCard)}, |
| {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}, |
| {UkmSuggestionFilledType::kIsForCreditCardName, true}, |
| {UkmSuggestionFilledType::kFieldSignatureName, |
| Collapse(CalculateFieldSignatureForField(form.fields.front())) |
| .value()}, |
| {UkmSuggestionFilledType::kFormSignatureName, |
| Collapse(CalculateFormSignature(form)).value()}}}); |
| } |
| |
| // Reset the autofill manager state and purge UKM logs. |
| PurgeUKM(); |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with a masked card server suggestion. |
| base::HistogramTester histogram_tester; |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.back(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestMaskedCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| OnCreditCardFetchingSuccessful(u"6011000990139424"); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE, 1))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE, 1))); |
| |
| VerifyUkm( |
| &test_ukm_recorder(), form, UkmSuggestionFilledType::kEntryName, |
| {{{UkmSuggestionFilledType::kRecordTypeName, |
| base::to_underlying(CreditCard::RecordType::kMaskedServerCard)}, |
| {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}, |
| {UkmSuggestionFilledType::kIsForCreditCardName, true}, |
| {UkmSuggestionFilledType::kFieldSignatureName, |
| Collapse(CalculateFieldSignatureForField(form.fields.back())) |
| .value()}, |
| {UkmSuggestionFilledType::kFormSignatureName, |
| Collapse(CalculateFormSignature(form)).value()}}}); |
| } |
| |
| // Reset the autofill manager state and purge UKM logs. |
| PurgeUKM(); |
| |
| // Recreating cards as the previous test should have upgraded the masked |
| // card to a full card. |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/true, |
| /*include_full_server_credit_card=*/true, |
| /*masked_card_is_enrolled_for_virtual_card=*/true); |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating multiple submissions. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); |
| SubmitForm(form); |
| SubmitForm(form); |
| |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, |
| 0), |
| Bucket(FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, |
| 0))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, |
| 0), |
| Bucket(FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, |
| 0))); |
| } |
| |
| // Reset the autofill manager state and purge UKM logs. |
| PurgeUKM(); |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with suggestion shown but without previous |
| // interaction. |
| base::HistogramTester histogram_tester; |
| DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, |
| 0))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, |
| 0))); |
| |
| VerifyUkm( |
| &test_ukm_recorder(), form, UkmSuggestionsShownType::kEntryName, |
| {{{UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}, |
| {UkmTextFieldDidChangeType::kHeuristicTypeName, CREDIT_CARD_NUMBER}, |
| {UkmTextFieldDidChangeType::kHtmlFieldTypeName, |
| HtmlFieldType::kUnspecified}, |
| {UkmTextFieldDidChangeType::kServerTypeName, CREDIT_CARD_NUMBER}, |
| {UkmSuggestionsShownType::kFieldSignatureName, |
| Collapse(CalculateFieldSignatureForField(form.fields[2])).value()}, |
| {UkmSuggestionsShownType::kFormSignatureName, |
| Collapse(CalculateFormSignature(form)).value()}}}); |
| } |
| } |
| |
| // Test that we log "will submit" and "submitted" form events for credit |
| // cards. |
| TEST_P(AutofillMetricsIFrameTest, CreditCardWillSubmitFormEvents) { |
| // Creating all kinds of cards. |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/true, |
| /*include_full_server_credit_card=*/true, |
| /*masked_card_is_enrolled_for_virtual_card=*/true); |
| FormData form = |
| CreateForm({CreateTestFormField("Month", "card_month", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Year", "card_year", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Credit card", "cardnum", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = { |
| CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_2_DIGIT_YEAR, CREDIT_CARD_NUMBER}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with no filled data. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude(Bucket(FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude(Bucket(FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1))); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with suggestion shown. |
| base::HistogramTester histogram_tester; |
| DidShowAutofillSuggestions(form, /*field_index=*/0, |
| SuggestionType::kCreditCardEntry); |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude(Bucket(FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 1))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude(Bucket(FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 1))); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with filled local data. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.front(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestLocalCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude(Bucket(FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 1))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude(Bucket(FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 1))); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with filled virtual card data by selecting the |
| // option based on the enrolled masked card. |
| base::HistogramTester histogram_tester; |
| CreditCard virtual_card = GetVirtualCreditCard(kTestMaskedCardId); |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.front(), virtual_card, |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| OnCreditCardFetchingSuccessful(u"6011000990139424", |
| /*is_virtual_card=*/true); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_VIRTUAL_CARD_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_VIRTUAL_CARD_SUGGESTION_SUBMITTED_ONCE, 1))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_VIRTUAL_CARD_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_VIRTUAL_CARD_SUGGESTION_SUBMITTED_ONCE, 1))); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with filled server data. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| // Full server card. |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.front(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestFullServerCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude(Bucket(FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 1))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude(Bucket(FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 1))); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with a masked card server suggestion. |
| base::HistogramTester histogram_tester; |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.back(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestMaskedCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| OnCreditCardFetchingSuccessful(u"6011000990139424"); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE, 1))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE, 1))); |
| } |
| |
| // Recreating cards as the previous test should have upgraded the masked |
| // card to a full card. |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/true, |
| /*include_full_server_credit_card=*/true, |
| /*masked_card_is_enrolled_for_virtual_card=*/true); |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating multiple submissions. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| SubmitForm(form); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, |
| 0), |
| Bucket(FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, |
| 0))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, |
| 0), |
| Bucket(FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, |
| 0))); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with suggestion shown but without previous |
| // interaction. |
| base::HistogramTester histogram_tester; |
| DidShowAutofillSuggestions(form); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, |
| 0))); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| credit_card_form_events_frame_histogram_), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, |
| 0))); |
| } |
| } |
| |
| // Test that we log form events for masked server card with offers. |
| TEST_F(AutofillMetricsTest, LogServerOfferFormEvents) { |
| FormData form = |
| CreateForm({CreateTestFormField("Month", "card_month", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Year", "card_year", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Credit card", "cardnum", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = { |
| CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_2_DIGIT_YEAR, CREDIT_CARD_NUMBER}; |
| |
| // Creating all kinds of cards. None of them have offers. |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/true, |
| /*include_full_server_credit_card=*/true, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| |
| const std::string kMaskedServerCardIds[] = { |
| "12340000-0000-0000-0000-000000000001", |
| "12340000-0000-0000-0000-000000000002", |
| "12340000-0000-0000-0000-000000000003"}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating activating the autofill popup for the credit card field, new |
| // popup being shown and filling a local card suggestion. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); |
| DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, |
| SuggestionType::kCreditCardEntry); |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.front(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestLocalCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude(Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 1), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED, 1))); |
| // Check that the offer sub-histogram was not recorded. |
| // ExpectBucketCount() can't be used here because it expects the histogram |
| // to exist. |
| EXPECT_EQ(0, histogram_tester.GetTotalCountsForPrefix( |
| "Autofill.FormEvents.CreditCard") |
| ["Autofill.FormEvents.CreditCard.WithOffer"]); |
| |
| // Ensure offers were not shown. |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.Offer.SuggestedCardsHaveOffer", |
| /*suggestions with offers=*/0, 1); |
| |
| // Since no offers were shown, we should not track offer selection or |
| // submission. |
| EXPECT_EQ(0, histogram_tester.GetTotalCountsForPrefix( |
| "Autofill.Offer")["Autofill.Offer.SelectedCardHasOffer"]); |
| EXPECT_EQ(0, histogram_tester.GetTotalCountsForPrefix( |
| "Autofill.Offer")["Autofill.Offer.SubmittedCardHasOffer"]); |
| } |
| |
| // Add another masked server card, this time with a linked offer. |
| AddMaskedServerCreditCardWithOffer(kMaskedServerCardIds[0], "$4", |
| autofill_client_->form_origin(), |
| /*id=*/0x4fff); |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // A masked server card with linked offers. |
| // Simulating activating the autofill popup for the credit card field, new |
| // popup being shown, selecting a masked card server suggestion and |
| // submitting the form. Verify that all related form events are correctly |
| // logged to offer sub-histogram. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); |
| DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, |
| SuggestionType::kCreditCardEntry); |
| // Select the masked server card with the linked offer. |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.back(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kMaskedServerCardIds[0]), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kSuccess, |
| "6011000990139424"); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| "Autofill.FormEvents.CreditCard.WithOffer"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 1), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, |
| 1))); |
| |
| // Ensure we count the correct number of offers shown. |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.Offer.SuggestedCardsHaveOffer", |
| /*suggestions with offers=*/1, 1); |
| |
| // Should track card was selected and form was submitted with that card. |
| histogram_tester.ExpectUniqueSample("Autofill.Offer.SelectedCardHasOffer", |
| /*sample=*/true, 1); |
| histogram_tester.ExpectUniqueSample("Autofill.Offer.SubmittedCardHasOffer", |
| /*sample=*/true, 1); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // A masked server card with linked offers. |
| // Simulating activating the autofill popup for the credit card field, new |
| // popup being shown, selecting a masked card server suggestion and |
| // submitting the form. Verify that all related form events are correctly |
| // logged to offer sub-histogram. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); |
| DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, |
| SuggestionType::kCreditCardEntry); |
| // Select another card, and still log to offer |
| // sub-histogram because user has another masked server card with offer. |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.back(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestMaskedCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kSuccess, |
| "6011000990139424"); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| "Autofill.FormEvents.CreditCard.WithOffer"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 1), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, |
| 1))); |
| |
| // Ensure we count the correct number of offers shown. |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.Offer.SuggestedCardsHaveOffer", |
| /*suggestions with offers=*/1, 1); |
| |
| // Should track card was not selected. |
| histogram_tester.ExpectUniqueSample("Autofill.Offer.SelectedCardHasOffer", |
| /*sample=*/false, 1); |
| histogram_tester.ExpectUniqueSample("Autofill.Offer.SubmittedCardHasOffer", |
| /*sample=*/false, 1); |
| } |
| |
| // Recreate cards and add card that is linked to an expired offer. |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/true, |
| /*include_full_server_credit_card=*/true, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| AddMaskedServerCreditCardWithOffer(kMaskedServerCardIds[1], "$4", |
| autofill_client_->form_origin(), |
| /*id=*/0x3fff, /*offer_expired=*/true); |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating activating the autofill popup for the credit card field, |
| // new popup being shown and filling a local card suggestion. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); |
| DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, |
| SuggestionType::kCreditCardEntry); |
| // Select the card with linked offer, though metrics should not record it |
| // since the offer is expired. |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.back(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kMaskedServerCardIds[1]), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kSuccess, |
| "6011000990139424"); |
| SubmitForm(form); |
| // Histograms without ".WithOffer" should be recorded. |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.CreditCard"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 1), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, |
| 1))); |
| |
| // Check that the offer sub-histogram was not recorded. |
| // ExpectBucketCount() can't be used here because it expects the |
| // histogram to exist. |
| EXPECT_EQ(0, histogram_tester.GetTotalCountsForPrefix( |
| "Autofill.FormEvents.CreditCard") |
| ["Autofill.FormEvents.CreditCard.WithOffer"]); |
| |
| // Ensure offers were not shown. |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.Offer.SuggestedCardsHaveOffer", |
| /*suggestions with offers=*/0, 1); |
| |
| // Since no offers were shown, we should not track offer selection or |
| // submission. |
| EXPECT_EQ(0, histogram_tester.GetTotalCountsForPrefix( |
| "Autofill.Offer")["Autofill.Offer.SelectedCardHasOffer"]); |
| EXPECT_EQ(0, histogram_tester.GetTotalCountsForPrefix( |
| "Autofill.Offer")["Autofill.Offer.SubmittedCardHasOffer"]); |
| } |
| |
| // Recreate cards and add card that is linked to an offer. |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/true, |
| /*include_full_server_credit_card=*/true, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| AddMaskedServerCreditCardWithOffer(kMaskedServerCardIds[2], "$5", |
| autofill_client_->form_origin(), |
| /*id=*/0x5fff); |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // A masked server card with linked offers. |
| // Simulating activating the autofill popup for the credit card field, new |
| // popup being shown, selecting a masked card server suggestion, showing the |
| // suggestions again, and then submitting the form with previously filled |
| // card. Verify that all related form events are correctly logged to offer |
| // sub-histogram. Making suggestions reappear tests confirmation of a fix |
| // for crbug/1198751. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); |
| DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, |
| SuggestionType::kCreditCardEntry); |
| // Select the masked server card with the linked offer. |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.back(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kMaskedServerCardIds[2]), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kSuccess, |
| "6011000990139424"); |
| |
| // Simulate user showing suggestions but then submitting form with |
| // previously filled card info. |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); |
| DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, |
| SuggestionType::kCreditCardEntry); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| "Autofill.FormEvents.CreditCard.WithOffer"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 2), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, |
| 1))); |
| |
| // Ensure we count the correct number of offers shown. |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.Offer.SuggestedCardsHaveOffer", |
| /*suggestions with offers=*/1, 1); |
| |
| // Should track card was selected and form was submitted with that card. |
| histogram_tester.ExpectBucketCount("Autofill.Offer.SelectedCardHasOffer", |
| /*sample=*/true, 1); |
| histogram_tester.ExpectUniqueSample("Autofill.Offer.SubmittedCardHasOffer", |
| /*sample=*/true, 1); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // A masked server card with linked offers. |
| // Simulating activating the autofill popup for the credit card field, new |
| // popup being shown, selecting a masked card server suggestion, but then |
| // failing the CVC check and submitting the form anyways. Verify that all |
| // related form events are correctly logged to offer sub-histogram. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); |
| DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, |
| SuggestionType::kCreditCardEntry); |
| // Select the masked server card with the linked offer, but fail the CVC |
| // check. |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.back(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kMaskedServerCardIds[2]), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kPermanentFailure, |
| std::string()); |
| |
| // Submitting the form without the filled suggestion. |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| "Autofill.FormEvents.CreditCard.WithOffer"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 1), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 0), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE, 0), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, |
| 0))); |
| |
| // Ensure we count the correct number of offers shown. |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.Offer.SuggestedCardsHaveOffer", |
| /*suggestions with offers=*/1, 1); |
| |
| // Should track card was selected once, but not submitted. |
| histogram_tester.ExpectUniqueSample("Autofill.Offer.SelectedCardHasOffer", |
| /*sample=*/true, 1); |
| histogram_tester.ExpectBucketCount("Autofill.Offer.SubmittedCardHasOffer", |
| /*sample=*/true, 0); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // A masked server card with linked offers. |
| // Simulating activating the autofill popup for the credit card field, new |
| // popup being shown, selecting a masked card server suggestion, but then |
| // selecting a local card instead. Verify that all related form events are |
| // correctly logged to offer sub-histogram. |
| base::HistogramTester histogram_tester; |
| |
| // Show suggestions and select the card with offer. |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); |
| DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, |
| SuggestionType::kCreditCardEntry); |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.back(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kMaskedServerCardIds[2]), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| OnDidGetRealPan(AutofillClient::PaymentsRpcResult::kSuccess, |
| "6011000990139424"); |
| |
| // Show suggestions again, and select a local card instead. |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); |
| DidShowAutofillSuggestions(form, /*field_index=*/form.fields.size() - 1, |
| SuggestionType::kCreditCardEntry); |
| autofill_manager().AuthenticateThenFillCreditCardForm( |
| form, form.fields.back(), |
| *personal_data().payments_data_manager().GetCreditCardByGUID( |
| kTestLocalCardId), |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples( |
| "Autofill.FormEvents.CreditCard.WithOffer"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 2), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE, 1), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, |
| 1))); |
| |
| // Ensure we count the correct number of offers shown. |
| histogram_tester.ExpectBucketCount("Autofill.Offer.SuggestedCardsHaveOffer", |
| /*suggestions with offers=*/1, 1); |
| |
| // Should track card was only selected once. |
| histogram_tester.ExpectBucketCount("Autofill.Offer.SelectedCardHasOffer", |
| /*sample=*/true, 1); |
| histogram_tester.ExpectBucketCount("Autofill.Offer.SelectedCardHasOffer", |
| /*sample=*/false, 1); |
| histogram_tester.ExpectUniqueSample("Autofill.Offer.SubmittedCardHasOffer", |
| /*sample=*/false, 1); |
| } |
| } |
| |
| // Test that we log parsed form events for address and cards in the same form. |
| TEST_F(AutofillMetricsTest, MixedParsedFormEvents) { |
| FormData form = CreateForm( |
| {CreateTestFormField("State", "state", "", FormControlType::kInputText), |
| CreateTestFormField("City", "city", "", FormControlType::kInputText), |
| CreateTestFormField("Street", "street", "", FormControlType::kInputText), |
| CreateTestFormField("Card Number", "card_number", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Expiration", "cc_exp", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Verification", "verification", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = { |
| ADDRESS_HOME_STATE, ADDRESS_HOME_CITY, |
| ADDRESS_HOME_STREET_ADDRESS, CREDIT_CARD_NAME_FULL, |
| CREDIT_CARD_EXP_MONTH, CREDIT_CARD_VERIFICATION_CODE}; |
| |
| base::HistogramTester histogram_tester; |
| SeeForm(form); |
| histogram_tester.ExpectUniqueSample("Autofill.FormEvents.Address", |
| FORM_EVENT_DID_PARSE_FORM, 1); |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.FormEvents.CreditCard.WithNoData", FORM_EVENT_DID_PARSE_FORM, |
| 1); |
| } |
| |
| // Test that we log parsed form events for address. |
| TEST_F(AutofillMetricsTest, AddressParsedFormEvents) { |
| FormData form = CreateForm( |
| {CreateTestFormField("State", "state", "", FormControlType::kInputText), |
| CreateTestFormField("City", "city", "", FormControlType::kInputText), |
| CreateTestFormField("Street", "street", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = {ADDRESS_HOME_STATE, ADDRESS_HOME_CITY, |
| ADDRESS_HOME_STREET_ADDRESS}; |
| |
| base::HistogramTester histogram_tester; |
| SeeForm(form); |
| histogram_tester.ExpectUniqueSample("Autofill.FormEvents.Address", |
| FORM_EVENT_DID_PARSE_FORM, 1); |
| |
| // Check if FormEvent UKM is logged properly |
| auto entries = |
| test_ukm_recorder().GetEntriesByName(UkmFormEventType::kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| VerifyUkm( |
| &test_ukm_recorder(), form, UkmFormEventType::kEntryName, |
| {{{UkmFormEventType::kAutofillFormEventName, FORM_EVENT_DID_PARSE_FORM}, |
| {UkmFormEventType::kFormTypesName, |
| AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, |
| {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}}); |
| } |
| |
| // Test that we log interacted form events for address. |
| TEST_F(AutofillMetricsTest, AddressInteractedFormEvents) { |
| FormData form = CreateForm( |
| {CreateTestFormField("State", "state", "", FormControlType::kInputText), |
| CreateTestFormField("City", "city", "", FormControlType::kInputText), |
| CreateTestFormField("Street", "street", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = {ADDRESS_HOME_STATE, ADDRESS_HOME_CITY, |
| ADDRESS_HOME_STREET_ADDRESS}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulate activating the autofill popup for the street field. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[2]); |
| histogram_tester.ExpectUniqueSample("Autofill.FormEvents.Address", |
| FORM_EVENT_INTERACTED_ONCE, 1); |
| |
| // Check if FormEvent UKM is logged properly |
| auto entries = |
| test_ukm_recorder().GetEntriesByName(UkmFormEventType::kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| VerifyUkm( |
| &test_ukm_recorder(), form, UkmFormEventType::kEntryName, |
| {{{UkmFormEventType::kAutofillFormEventName, |
| FORM_EVENT_INTERACTED_ONCE}, |
| {UkmFormEventType::kFormTypesName, |
| AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, |
| {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}}); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| PurgeUKM(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulate activating the autofill popup for the street field twice. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[2]); |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[2]); |
| histogram_tester.ExpectUniqueSample("Autofill.FormEvents.Address", |
| FORM_EVENT_INTERACTED_ONCE, 1); |
| // Check if FormEvent UKM is logged properly |
| auto entries = |
| test_ukm_recorder().GetEntriesByName(UkmFormEventType::kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| VerifyUkm( |
| &test_ukm_recorder(), form, UkmFormEventType::kEntryName, |
| {{{UkmFormEventType::kAutofillFormEventName, |
| FORM_EVENT_INTERACTED_ONCE}, |
| {UkmFormEventType::kFormTypesName, |
| AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, |
| {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}}); |
| } |
| } |
| |
| // Test that we log suggestion shown form events for address. |
| TEST_F(AutofillMetricsTest, AddressShownFormEvents) { |
| RecreateProfile(); |
| FormData form = CreateForm( |
| {CreateTestFormField("State", "state", "", FormControlType::kInputText), |
| CreateTestFormField("City", "city", "", FormControlType::kInputText), |
| CreateTestFormField("Street", "street", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = {ADDRESS_HOME_STATE, ADDRESS_HOME_CITY, |
| ADDRESS_HOME_STREET_ADDRESS}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating new popup being shown. |
| base::HistogramTester histogram_tester; |
| DidShowAutofillSuggestions(form); |
| EXPECT_THAT(histogram_tester.GetAllSamples("Autofill.FormEvents.Address"), |
| BucketsInclude(Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 1), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1))); |
| // Check if FormEvent UKM is logged properly |
| auto entries = |
| test_ukm_recorder().GetEntriesByName(UkmFormEventType::kEntryName); |
| EXPECT_EQ(2u, entries.size()); |
| VerifyUkm( |
| &test_ukm_recorder(), form, UkmFormEventType::kEntryName, |
| {{{UkmFormEventType::kAutofillFormEventName, |
| FORM_EVENT_SUGGESTIONS_SHOWN}, |
| {UkmFormEventType::kFormTypesName, |
| AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, |
| {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}, |
| {{UkmFormEventType::kAutofillFormEventName, |
| FORM_EVENT_SUGGESTIONS_SHOWN_ONCE}, |
| {UkmFormEventType::kFormTypesName, |
| AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, |
| {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}}); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| PurgeUKM(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating two popups in the same page load. |
| base::HistogramTester histogram_tester; |
| DidShowAutofillSuggestions(form); |
| DidShowAutofillSuggestions(form); |
| EXPECT_THAT(histogram_tester.GetAllSamples("Autofill.FormEvents.Address"), |
| BucketsInclude(Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 2), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 1))); |
| // Check if FormEvent UKM is logged properly |
| auto entries = |
| test_ukm_recorder().GetEntriesByName(UkmFormEventType::kEntryName); |
| EXPECT_EQ(3u, entries.size()); |
| VerifyUkm( |
| &test_ukm_recorder(), form, UkmFormEventType::kEntryName, |
| {{{UkmFormEventType::kAutofillFormEventName, |
| FORM_EVENT_SUGGESTIONS_SHOWN}, |
| {UkmFormEventType::kFormTypesName, |
| AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, |
| {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}, |
| {{UkmFormEventType::kAutofillFormEventName, |
| FORM_EVENT_SUGGESTIONS_SHOWN_ONCE}, |
| {UkmFormEventType::kFormTypesName, |
| AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, |
| {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}, |
| {{UkmFormEventType::kAutofillFormEventName, |
| FORM_EVENT_SUGGESTIONS_SHOWN}, |
| {UkmFormEventType::kFormTypesName, |
| AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, |
| {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}}); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| PurgeUKM(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating same popup being refreshed. |
| // Suggestions not related to credit cards/addresses should not affect the |
| // histograms. |
| base::HistogramTester histogram_tester; |
| DidShowAutofillSuggestions(form, /*field_index=*/0, |
| SuggestionType::kAutocompleteEntry); |
| EXPECT_THAT(histogram_tester.GetAllSamples("Autofill.FormEvents.Address"), |
| BucketsInclude(Bucket(FORM_EVENT_SUGGESTIONS_SHOWN, 0), |
| Bucket(FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 0))); |
| // Check if FormEvent UKM is logged properly |
| auto entries = |
| test_ukm_recorder().GetEntriesByName(UkmFormEventType::kEntryName); |
| EXPECT_EQ(0u, entries.size()); |
| } |
| } |
| |
| // Test that we log filled form events for address. |
| TEST_F(AutofillMetricsTest, AddressFilledFormEvents) { |
| RecreateProfile(); |
| FormData form = CreateForm( |
| {CreateTestFormField("State", "state", "", FormControlType::kInputText), |
| CreateTestFormField("City", "city", "", FormControlType::kInputText), |
| CreateTestFormField("Street", "street", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = {ADDRESS_HOME_STATE, ADDRESS_HOME_CITY, |
| ADDRESS_HOME_STREET_ADDRESS}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating selecting/filling a local profile suggestion. |
| base::HistogramTester histogram_tester; |
| FillTestProfile(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.Address"), |
| BucketsInclude(Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED, 1), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE, 1))); |
| // Check if FormEvent UKM is logged properly |
| auto entries = |
| test_ukm_recorder().GetEntriesByName(UkmFormEventType::kEntryName); |
| EXPECT_EQ(2u, entries.size()); |
| VerifyUkm( |
| &test_ukm_recorder(), form, UkmFormEventType::kEntryName, |
| {{{UkmFormEventType::kAutofillFormEventName, |
| FORM_EVENT_LOCAL_SUGGESTION_FILLED}, |
| {UkmFormEventType::kFormTypesName, |
| AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, |
| {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}, |
| {{UkmFormEventType::kAutofillFormEventName, |
| FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE}, |
| {UkmFormEventType::kFormTypesName, |
| AutofillMetrics::FormTypesToBitVector({FormType::kAddressForm})}, |
| {UkmSuggestionFilledType::kMillisecondsSinceFormParsedName, 0}}}); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| PurgeUKM(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating selecting/filling a local profile suggestion more than once. |
| base::HistogramTester histogram_tester; |
| FillTestProfile(form); |
| FillTestProfile(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.Address"), |
| BucketsInclude(Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED, 2), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE, 1))); |
| } |
| } |
| |
| // Test that we log submitted form events for address. |
| TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { |
| RecreateProfile(); |
| FormData form = CreateForm( |
| {CreateTestFormField("State", "state", "", FormControlType::kInputText), |
| CreateTestFormField("City", "city", "", FormControlType::kInputText), |
| CreateTestFormField("Street", "street", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = {ADDRESS_HOME_STATE, ADDRESS_HOME_CITY, |
| ADDRESS_HOME_STREET_ADDRESS}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with no filled data. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.Address"), |
| BucketsInclude(Bucket(FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1))); |
| } |
| |
| // Reset the autofill manager state and purge UKM logs. |
| PurgeUKM(); |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with no filled data. Form is submitted and |
| // autofill manager is reset before UploadFormDataAsyncCallback is |
| // triggered. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| SubmitForm(form); |
| // Trigger UploadFormDataAsyncCallback. |
| autofill_manager().Reset(); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.Address"), |
| BucketsInclude(Bucket(FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1))); |
| } |
| |
| // Reset the autofill manager state and purge UKM logs. |
| PurgeUKM(); |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with suggestion shown. |
| base::HistogramTester histogram_tester; |
| DidShowAutofillSuggestions(form); |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.Address"), |
| BucketsInclude(Bucket(FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 1))); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| PurgeUKM(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with filled local data. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| FillTestProfile(form); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.Address"), |
| BucketsInclude(Bucket(FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 1))); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating multiple submissions. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| SubmitForm(form); |
| SubmitForm(form); |
| |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.Address"), |
| BucketsInclude(Bucket(FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0))); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| PurgeUKM(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with suggestion show but without previous |
| // interaction. |
| base::HistogramTester histogram_tester; |
| DidShowAutofillSuggestions(form); |
| SubmitForm(form); |
| |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.Address"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, |
| 0), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, |
| 0))); |
| |
| // Check if FormEvent UKM is logged properly |
| auto entries = |
| test_ukm_recorder().GetEntriesByName(UkmFormEventType::kEntryName); |
| EXPECT_EQ(2u, entries.size()); |
| } |
| } |
| |
| // Test that we log "will submit" and "submitted" form events for address. |
| TEST_F(AutofillMetricsTest, AddressWillSubmitFormEvents) { |
| RecreateProfile(); |
| FormData form = CreateForm( |
| {CreateTestFormField("State", "state", "", FormControlType::kInputText), |
| CreateTestFormField("City", "city", "", FormControlType::kInputText), |
| CreateTestFormField("Street", "street", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = {ADDRESS_HOME_STATE, ADDRESS_HOME_CITY, |
| ADDRESS_HOME_STREET_ADDRESS}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with no filled data. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.Address"), |
| BucketsInclude(Bucket(FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1))); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| PurgeUKM(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with suggestion shown. |
| base::HistogramTester histogram_tester; |
| DidShowAutofillSuggestions(form); |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.Address"), |
| BucketsInclude(Bucket(FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 1))); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| PurgeUKM(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with filled local data. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| FillTestProfile(form); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.Address"), |
| BucketsInclude(Bucket(FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 1))); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| PurgeUKM(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating multiple submissions. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| SubmitForm(form); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.Address"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, |
| 0), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, |
| 0))); |
| // Check if FormEvent UKM is logged properly |
| auto entries = |
| test_ukm_recorder().GetEntriesByName(UkmFormEventType::kEntryName); |
| EXPECT_EQ(3u, entries.size()); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| PurgeUKM(); |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with suggestion shown but without previous |
| // interaction. |
| base::HistogramTester histogram_tester; |
| DidShowAutofillSuggestions(form); |
| SubmitForm(form); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.FormEvents.Address"), |
| BucketsInclude( |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_WILL_SUBMIT_ONCE, 0), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, |
| 0), |
| Bucket(FORM_EVENT_SUGGESTION_SHOWN_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 0), |
| Bucket(FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE, |
| 0))); |
| // Check if FormEvent UKM is logged properly |
| auto entries = |
| test_ukm_recorder().GetEntriesByName(UkmFormEventType::kEntryName); |
| EXPECT_EQ(2u, entries.size()); |
| } |
| } |
| |
| // Test that we log the phone field. |
| TEST_F(AutofillMetricsTest, RecordStandalonePhoneField) { |
| FormData form = CreateForm({CreateTestFormField( |
| "Phone", "phone", "", FormControlType::kInputTelephone)}); |
| |
| std::vector<FieldType> field_types = {PHONE_HOME_NUMBER}; |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| histogram_tester.ExpectBucketCount("Autofill.FormEvents.Address.PhoneOnly", |
| FORM_EVENT_INTERACTED_ONCE, 1); |
| } |
| |
| // Test that we log interacted form event for credit cards only once. |
| TEST_F(AutofillMetricsTest, CreditCardFormEventsAreSegmented) { |
| FormData form = |
| CreateForm({CreateTestFormField("Month", "card_month", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Year", "card_year", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Credit card", "cardnum", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = { |
| CREDIT_CARD_EXP_MONTH, CREDIT_CARD_EXP_2_DIGIT_YEAR, CREDIT_CARD_NUMBER}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| RecreateCreditCards(/*include_local_credit_card=*/false, |
| /*include_masked_server_credit_card=*/false, |
| /*include_full_server_credit_card=*/false, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| |
| { |
| // Simulate activating the autofill popup for the credit card field. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.FormEvents.CreditCard.WithNoData", FORM_EVENT_INTERACTED_ONCE, |
| 1); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| PurgeUKM(); |
| autofill_manager().AddSeenForm(form, field_types); |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/false, |
| /*include_full_server_credit_card=*/false, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| |
| { |
| // Simulate activating the autofill popup for the credit card field. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.FormEvents.CreditCard.WithOnlyLocalData", |
| FORM_EVENT_INTERACTED_ONCE, 1); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| PurgeUKM(); |
| autofill_manager().AddSeenForm(form, field_types); |
| RecreateCreditCards(/*include_local_credit_card=*/false, |
| /*include_masked_server_credit_card=*/true, |
| /*include_full_server_credit_card=*/false, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| |
| { |
| // Simulate activating the autofill popup for the credit card field. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.FormEvents.CreditCard.WithOnlyServerData", |
| FORM_EVENT_INTERACTED_ONCE, 1); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| PurgeUKM(); |
| autofill_manager().AddSeenForm(form, field_types); |
| RecreateCreditCards(/*include_local_credit_card=*/false, |
| /*include_masked_server_credit_card=*/false, |
| /*include_full_server_credit_card=*/true, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| |
| { |
| // Simulate activating the autofill popup for the credit card field. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.FormEvents.CreditCard.WithOnlyServerData", |
| FORM_EVENT_INTERACTED_ONCE, 1); |
| } |
| |
| // Reset the autofill manager state. |
| autofill_manager().Reset(); |
| PurgeUKM(); |
| autofill_manager().AddSeenForm(form, field_types); |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/false, |
| /*include_full_server_credit_card=*/true, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| |
| { |
| // Simulate activating the autofill popup for the credit card field. |
| base::HistogramTester histogram_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields[0]); |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.FormEvents.CreditCard.WithBothServerAndLocalData", |
| FORM_EVENT_INTERACTED_ONCE, 1); |
| } |
| } |
| |
| // Test that we log that Profile Autofill is enabled when filling a form. |
| TEST_F(AutofillMetricsTest, AutofillProfileIsEnabledAtPageLoad) { |
| base::HistogramTester histogram_tester; |
| autofill_manager().SetAutofillProfileEnabled(*autofill_client_, true); |
| autofill_manager().OnFormsSeen(/*updated_forms=*/{}, |
| /*removed_forms=*/{}); |
| histogram_tester.ExpectUniqueSample("Autofill.Address.IsEnabled.PageLoad", |
| true, 1); |
| } |
| |
| // Test that we log that Profile Autofill is disabled when filling a form. |
| TEST_F(AutofillMetricsTest, AutofillProfileIsDisabledAtPageLoad) { |
| base::HistogramTester histogram_tester; |
| autofill_manager().SetAutofillProfileEnabled(*autofill_client_, false); |
| autofill_manager().OnFormsSeen(/*updated_forms=*/{}, |
| /*removed_forms=*/{}); |
| histogram_tester.ExpectUniqueSample("Autofill.Address.IsEnabled.PageLoad", |
| false, 1); |
| } |
| |
| // Test that we log that CreditCard Autofill is enabled when filling a form. |
| TEST_F(AutofillMetricsTest, AutofillCreditCardIsEnabledAtPageLoad) { |
| base::HistogramTester histogram_tester; |
| autofill_manager().SetAutofillPaymentMethodsEnabled(*autofill_client_, true); |
| autofill_manager().OnFormsSeen(/*updated_forms=*/{}, |
| /*removed_forms=*/{}); |
| histogram_tester.ExpectUniqueSample("Autofill.CreditCard.IsEnabled.PageLoad", |
| true, 1); |
| } |
| |
| // Test that we log that CreditCard Autofill is disabled when filling a form. |
| TEST_F(AutofillMetricsTest, AutofillCreditCardIsDisabledAtPageLoad) { |
| base::HistogramTester histogram_tester; |
| autofill_manager().SetAutofillPaymentMethodsEnabled(*autofill_client_, false); |
| autofill_manager().OnFormsSeen(/*updated_forms=*/{}, |
| /*removed_forms=*/{}); |
| histogram_tester.ExpectUniqueSample("Autofill.CreditCard.IsEnabled.PageLoad", |
| false, 1); |
| } |
| |
| // Test that we log the days since last use of a credit card when it is used. |
| TEST_F(AutofillMetricsTest, DaysSinceLastUse_CreditCard) { |
| base::HistogramTester histogram_tester; |
| CreditCard credit_card; |
| credit_card.set_use_date(AutofillClock::Now() - base::Days(21)); |
| credit_card.RecordAndLogUse(); |
| histogram_tester.ExpectBucketCount("Autofill.DaysSinceLastUse.CreditCard", 21, |
| 1); |
| } |
| |
| // Test that we log the days since last use of a profile when it is used. |
| TEST_F(AutofillMetricsTest, DaysSinceLastUse_Profile) { |
| base::HistogramTester histogram_tester; |
| AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode); |
| profile.set_use_date(AutofillClock::Now() - base::Days(13)); |
| profile.RecordAndLogUse(); |
| histogram_tester.ExpectBucketCount("Autofill.DaysSinceLastUse.Profile", 13, |
| 1); |
| } |
| |
| // Test that we log the verification status of name tokens. |
| TEST_F(AutofillMetricsTest, LogVerificationStatusesOfNameTokens) { |
| base::HistogramTester histogram_tester; |
| AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode); |
| profile.SetRawInfoWithVerificationStatus(NAME_FULL, u"First Last", |
| VerificationStatus::kObserved); |
| profile.SetRawInfoWithVerificationStatus(NAME_FIRST, u"First", |
| VerificationStatus::kParsed); |
| profile.SetRawInfoWithVerificationStatus(NAME_LAST, u"Last", |
| VerificationStatus::kParsed); |
| profile.SetRawInfoWithVerificationStatus(NAME_LAST_SECOND, u"Last", |
| VerificationStatus::kParsed); |
| |
| AutofillMetrics::LogVerificationStatusOfNameTokensOnProfileUsage(profile); |
| |
| std::string base_histogram = |
| "Autofill.NameTokenVerificationStatusAtProfileUsage."; |
| |
| histogram_tester.ExpectUniqueSample(base_histogram + "Full", |
| VerificationStatus::kObserved, 1); |
| histogram_tester.ExpectUniqueSample(base_histogram + "First", |
| VerificationStatus::kParsed, 1); |
| histogram_tester.ExpectUniqueSample(base_histogram + "Last", |
| VerificationStatus::kParsed, 1); |
| histogram_tester.ExpectUniqueSample(base_histogram + "SecondLast", |
| VerificationStatus::kParsed, 1); |
| |
| histogram_tester.ExpectTotalCount(base_histogram + "Middle", 0); |
| histogram_tester.ExpectTotalCount(base_histogram + "FirstLast", 0); |
| |
| histogram_tester.ExpectTotalCount(base_histogram + "Any", 4); |
| histogram_tester.ExpectBucketCount(base_histogram + "Any", |
| VerificationStatus::kObserved, 1); |
| histogram_tester.ExpectBucketCount(base_histogram + "Any", |
| VerificationStatus::kParsed, 3); |
| } |
| |
| // Test that we log the verification status of address tokens.. |
| TEST_F(AutofillMetricsTest, LogVerificationStatusesOfAddressTokens) { |
| base::HistogramTester histogram_tester; |
| AutofillProfile profile(i18n_model_definition::kLegacyHierarchyCountryCode); |
| profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_STREET_ADDRESS, |
| u"123 StreetName", |
| VerificationStatus::kFormatted); |
| profile.SetRawInfoWithVerificationStatus(ADDRESS_HOME_HOUSE_NUMBER, u"123", |
| VerificationStatus::kObserved); |
| profile.SetRawInfoWithVerificationStatus( |
| ADDRESS_HOME_STREET_NAME, u"StreetName", VerificationStatus::kObserved); |
| |
| AutofillMetrics::LogVerificationStatusOfAddressTokensOnProfileUsage(profile); |
| |
| std::string base_histogram = |
| "Autofill.AddressTokenVerificationStatusAtProfileUsage."; |
| |
| histogram_tester.ExpectUniqueSample(base_histogram + "StreetAddress", |
| VerificationStatus::kFormatted, 1); |
| histogram_tester.ExpectUniqueSample(base_histogram + "StreetName", |
| VerificationStatus::kObserved, 1); |
| histogram_tester.ExpectUniqueSample(base_histogram + "HouseNumber", |
| VerificationStatus::kObserved, 1); |
| |
| histogram_tester.ExpectTotalCount(base_histogram + "FloorNumber", 0); |
| histogram_tester.ExpectTotalCount(base_histogram + "ApartmentNumber", 0); |
| histogram_tester.ExpectTotalCount(base_histogram + "Premise", 0); |
| histogram_tester.ExpectTotalCount(base_histogram + "SubPremise", 0); |
| |
| histogram_tester.ExpectTotalCount(base_histogram + "Any", 3); |
| histogram_tester.ExpectBucketCount(base_histogram + "Any", |
| VerificationStatus::kFormatted, 1); |
| histogram_tester.ExpectBucketCount(base_histogram + "Any", |
| VerificationStatus::kObserved, 2); |
| } |
| |
| // Verify that we correctly log metrics tracking the duration of form fill. |
| TEST_F(AutofillMetricsTest, FormFillDuration) { |
| FormData empty_form = CreateForm( |
| {CreateTestFormField("Name", "name", "", FormControlType::kInputText), |
| CreateTestFormField("Email", "email", "", FormControlType::kInputText), |
| CreateTestFormField("Phone", "phone", "", FormControlType::kInputText)}); |
| |
| FormData filled_form = empty_form; |
| filled_form.fields[0].set_value(u"Elvis Aaron Presley"); |
| filled_form.fields[1].set_value(u"theking@gmail.com"); |
| filled_form.fields[2].set_value(u"12345678901"); |
| |
| // Fill additional form. |
| FormData second_form = empty_form; |
| second_form.host_frame = test::MakeLocalFrameToken(); |
| second_form.renderer_id = test::MakeFormRendererId(); |
| second_form.fields.push_back(CreateTestFormField( |
| "Second Phone", "second_phone", "", FormControlType::kInputText)); |
| |
| // Fill the field values for form submission. |
| second_form.fields[0].set_value(u"Elvis Aaron Presley"); |
| second_form.fields[1].set_value(u"theking@gmail.com"); |
| second_form.fields[2].set_value(u"12345678901"); |
| second_form.fields[3].set_value(u"51512345678"); |
| |
| // Expect only form load metrics to be logged if the form is submitted without |
| // user interaction. |
| { |
| SCOPED_TRACE("Test 1 - no interaction, fields are prefilled"); |
| base::HistogramTester histogram_tester; |
| SeeForm(empty_form); |
| task_environment_.FastForwardBy(base::Microseconds(17)); |
| SubmitForm(filled_form); |
| |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromLoad.WithAutofill", 0); |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.FillDuration.FromLoad.WithoutAutofill", 16, 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromInteraction.WithAutofill", 0); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromInteraction.WithoutAutofill", 0); |
| |
| autofill_manager().Reset(); |
| } |
| |
| // Expect metric to be logged if the user manually edited a form field. |
| { |
| SCOPED_TRACE("Test 2 - all fields are filled by the user"); |
| base::HistogramTester histogram_tester; |
| SeeForm(empty_form); |
| base::TimeTicks parse_time = autofill_manager() |
| .form_structures() |
| .begin() |
| ->second->form_parsed_timestamp(); |
| |
| FormData user_filled_form = filled_form; |
| SimulateUserChangedTextField(user_filled_form, |
| user_filled_form.fields.front(), |
| parse_time + base::Microseconds(3)); |
| task_environment_.FastForwardBy(base::Microseconds(17)); |
| SubmitForm(filled_form); |
| |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromLoad.WithAutofill", 0); |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.FillDuration.FromLoad.WithoutAutofill", 16, 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromInteraction.WithAutofill", 0); |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.FillDuration.FromInteraction.WithoutAutofill", 14, 1); |
| |
| // We expected an upload to be triggered when the manager is reset. |
| autofill_manager().Reset(); |
| } |
| |
| // Expect metric to be logged if the user autofilled the form. |
| { |
| SCOPED_TRACE("Test 3 - all fields are autofilled"); |
| base::HistogramTester histogram_tester; |
| SeeForm(empty_form); |
| base::TimeTicks parse_time = autofill_manager() |
| .form_structures() |
| .begin() |
| ->second->form_parsed_timestamp(); |
| |
| FormData autofilled_form = test::AsAutofilled(filled_form); |
| FillAutofillFormData(autofilled_form, parse_time + base::Microseconds(5)); |
| task_environment_.FastForwardBy(base::Microseconds(17)); |
| SubmitForm(autofilled_form); |
| |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.FillDuration.FromLoad.WithAutofill", 16, 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromLoad.WithoutAutofill", 0); |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.FillDuration.FromInteraction.WithAutofill", 12, 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromInteraction.WithoutAutofill", 0); |
| |
| // We expected an upload to be triggered when the manager is reset. |
| autofill_manager().Reset(); |
| } |
| |
| // Expect metric to be logged if the user both manually filled some fields |
| // and autofilled others. Messages can arrive out of order, so make sure they |
| // take precedence appropriately. |
| { |
| SCOPED_TRACE( |
| "Test 4 - mixed case: some fields are autofilled, some fields are " |
| "edited."); |
| base::HistogramTester histogram_tester; |
| |
| SeeForm(empty_form); |
| base::TimeTicks parse_time = autofill_manager() |
| .form_structures() |
| .begin() |
| ->second->form_parsed_timestamp(); |
| |
| FormData mixed_filled_form = test::AsAutofilled(filled_form); |
| FillAutofillFormData(mixed_filled_form, parse_time + base::Microseconds(5)); |
| SimulateUserChangedTextField(mixed_filled_form, |
| mixed_filled_form.fields.front(), |
| parse_time + base::Microseconds(3)); |
| |
| task_environment_.FastForwardBy(base::Microseconds(17)); |
| SubmitForm(mixed_filled_form); |
| |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.FillDuration.FromLoad.WithAutofill", 16, 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromLoad.WithoutAutofill", 0); |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.FillDuration.FromInteraction.WithAutofill", 14, 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromInteraction.WithoutAutofill", 0); |
| |
| // We expected an upload to be triggered when the manager is reset. |
| autofill_manager().Reset(); |
| } |
| |
| // Make sure that loading another form doesn't affect metrics from the first |
| // form. |
| { |
| SCOPED_TRACE("Test 5 - load a second form before submitting the first"); |
| base::HistogramTester histogram_tester; |
| SeeForm(empty_form); |
| base::TimeTicks parse_time = autofill_manager() |
| .form_structures() |
| .begin() |
| ->second->form_parsed_timestamp(); |
| |
| SeeForm(test::WithoutValues(second_form)); |
| |
| FormData mixed_filled_form = test::AsAutofilled(filled_form); |
| FillAutofillFormData(mixed_filled_form, parse_time + base::Microseconds(5)); |
| SimulateUserChangedTextField(mixed_filled_form, |
| mixed_filled_form.fields.front(), |
| parse_time + base::Microseconds(3)); |
| |
| task_environment_.FastForwardBy(base::Microseconds(17)); |
| SubmitForm(mixed_filled_form); |
| |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.FillDuration.FromLoad.WithAutofill", 16, 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromLoad.WithoutAutofill", 0); |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.FillDuration.FromInteraction.WithAutofill", 14, 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromInteraction.WithoutAutofill", 0); |
| |
| // We expected an upload to be triggered when the manager is reset. |
| autofill_manager().Reset(); |
| } |
| |
| // Make sure that submitting a form that was loaded later will report the |
| // later loading time. |
| { |
| SCOPED_TRACE("Test 6 - submit the second seen form first"); |
| base::HistogramTester histogram_tester; |
| SeeForm(test::WithoutValues(empty_form)); |
| SeeForm(test::WithoutValues(second_form)); |
| base::TimeTicks parse_time{}; |
| for (const auto& kv : autofill_manager().form_structures()) { |
| if (kv.second->form_parsed_timestamp() > parse_time) |
| parse_time = kv.second->form_parsed_timestamp(); |
| } |
| |
| task_environment_.FastForwardBy(base::Microseconds(17)); |
| SubmitForm(second_form); |
| |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromLoad.WithAutofill", 0); |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.FillDuration.FromLoad.WithoutAutofill", 12, 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromInteraction.WithAutofill", 0); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromInteraction.WithoutAutofill", 0); |
| |
| autofill_manager().Reset(); |
| } |
| } |
| |
| TEST_F(AutofillMetricsTest, FormFillDurationFromInteraction_CreditCardForm) { |
| // Should log time duration with autofill for credit card form. |
| { |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::LogFormFillDurationFromInteraction( |
| {FormType::kCreditCardForm}, /*used_autofill=*/true, |
| base::Milliseconds(2000)); |
| histogram_tester.ExpectTimeBucketCount( |
| "Autofill.FillDuration.FromInteraction.WithAutofill.CreditCard", |
| base::Milliseconds(2000), 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromInteraction.WithoutAutofill.CreditCard", 0); |
| } |
| |
| // Should log time duration without autofill for credit card form. |
| { |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::LogFormFillDurationFromInteraction( |
| {FormType::kCreditCardForm}, /*used_autofill=*/false, |
| base::Milliseconds(2000)); |
| histogram_tester.ExpectTimeBucketCount( |
| "Autofill.FillDuration.FromInteraction.WithoutAutofill.CreditCard", |
| base::Milliseconds(2000), 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromInteraction.WithAutofill.CreditCard", 0); |
| } |
| |
| // Should not log time duration for credit card form if credit card form is |
| // not detected. |
| { |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::LogFormFillDurationFromInteraction( |
| {FormType::kUnknownFormType}, /*used_autofill=*/false, |
| base::Milliseconds(2000)); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromInteraction.WithAutofill.CreditCard", 0); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromInteraction.WithoutAutofill.CreditCard", 0); |
| } |
| } |
| |
| TEST_F(AutofillMetricsTest, FormFillDurationFromInteraction_AddressForm) { |
| // Should log time duration with autofill for address form. |
| { |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::LogFormFillDurationFromInteraction( |
| {FormType::kAddressForm}, /*used_autofill=*/true, |
| base::Milliseconds(2000)); |
| histogram_tester.ExpectTimeBucketCount( |
| "Autofill.FillDuration.FromInteraction.WithAutofill.Address", |
| base::Milliseconds(2000), 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromInteraction.WithoutAutofill.Address", 0); |
| } |
| |
| // Should log time duration without autofill for address form. |
| { |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::LogFormFillDurationFromInteraction( |
| {FormType::kAddressForm}, /*used_autofill=*/false, |
| base::Milliseconds(2000)); |
| histogram_tester.ExpectTimeBucketCount( |
| "Autofill.FillDuration.FromInteraction.WithoutAutofill.Address", |
| base::Milliseconds(2000), 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromInteraction.WithAutofill.Address", 0); |
| } |
| |
| // Should not log time duration for address form if address form is not |
| // detected. |
| { |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::LogFormFillDurationFromInteraction( |
| {FormType::kUnknownFormType}, /*used_autofill=*/false, |
| base::Milliseconds(2000)); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromInteraction.WithAutofill.Address", 0); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromInteraction.WithoutAutofill.Address", 0); |
| } |
| } |
| |
| TEST_F(AutofillMetricsTest, FormFillDurationFromInteraction_PasswordForm) { |
| // Should log time duration with autofill for password form. |
| { |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::LogFormFillDurationFromInteraction( |
| {FormType::kPasswordForm}, /*used_autofill=*/true, |
| base::Milliseconds(2000)); |
| histogram_tester.ExpectTimeBucketCount( |
| "Autofill.FillDuration.FromInteraction.WithAutofill.Password", |
| base::Milliseconds(2000), 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromInteraction.WithoutAutofill.Password", 0); |
| } |
| |
| // Should log time duration without autofill for password form. |
| { |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::LogFormFillDurationFromInteraction( |
| {FormType::kPasswordForm}, /*used_autofill=*/false, |
| base::Milliseconds(2000)); |
| histogram_tester.ExpectTimeBucketCount( |
| "Autofill.FillDuration.FromInteraction.WithoutAutofill.Password", |
| base::Milliseconds(2000), 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromInteraction.WithAutofill.Password", 0); |
| } |
| |
| // Should not log time duration for password form if password form is not |
| // detected. |
| { |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::LogFormFillDurationFromInteraction( |
| {FormType::kUnknownFormType}, /*used_autofill=*/false, |
| base::Milliseconds(2000)); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromInteraction.WithAutofill.Password", 0); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromInteraction.WithoutAutofill.Password", 0); |
| } |
| } |
| |
| TEST_F(AutofillMetricsTest, FormFillDurationFromInteraction_UnknownForm) { |
| // Should log time duration with autofill for unknown form. |
| { |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::LogFormFillDurationFromInteraction( |
| {FormType::kUnknownFormType}, /*used_autofill=*/true, |
| base::Milliseconds(2000)); |
| histogram_tester.ExpectTimeBucketCount( |
| "Autofill.FillDuration.FromInteraction.WithAutofill.Unknown", |
| base::Milliseconds(2000), 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromInteraction.WithoutAutofill.Unknown", 0); |
| } |
| |
| // Should log time duration without autofill for unknown form. |
| { |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::LogFormFillDurationFromInteraction( |
| {FormType::kUnknownFormType}, /*used_autofill=*/false, |
| base::Milliseconds(2000)); |
| histogram_tester.ExpectTimeBucketCount( |
| "Autofill.FillDuration.FromInteraction.WithoutAutofill.Unknown", |
| base::Milliseconds(2000), 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromInteraction.WithAutofill.Unknown", 0); |
| } |
| |
| // Should not log time duration for unknown form if unknown form is not |
| // detected. |
| { |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::LogFormFillDurationFromInteraction( |
| {FormType::kAddressForm}, /*used_autofill=*/false, |
| base::Milliseconds(2000)); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromInteraction.WithAutofill.Unknown", 0); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.FillDuration.FromInteraction.WithoutAutofill.Unknown", 0); |
| } |
| } |
| |
| TEST_F(AutofillMetricsTest, FormFillDurationFromInteraction_MultipleForms) { |
| // Should log time duration with autofill for all forms. |
| { |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::LogFormFillDurationFromInteraction( |
| {FormType::kCreditCardForm, FormType::kAddressForm, |
| FormType::kPasswordForm, FormType::kUnknownFormType}, |
| /*used_autofill=*/true, base::Milliseconds(2000)); |
| histogram_tester.ExpectTimeBucketCount( |
| "Autofill.FillDuration.FromInteraction.WithAutofill.CreditCard", |
| base::Milliseconds(2000), 1); |
| histogram_tester.ExpectTimeBucketCount( |
| "Autofill.FillDuration.FromInteraction.WithAutofill.Address", |
| base::Milliseconds(2000), 1); |
| histogram_tester.ExpectTimeBucketCount( |
| "Autofill.FillDuration.FromInteraction.WithAutofill.Password", |
| base::Milliseconds(2000), 1); |
| histogram_tester.ExpectTimeBucketCount( |
| "Autofill.FillDuration.FromInteraction.WithAutofill.Unknown", |
| base::Milliseconds(2000), 1); |
| } |
| |
| // Should log time duration without autofill for all forms. |
| { |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::LogFormFillDurationFromInteraction( |
| {FormType::kCreditCardForm, FormType::kAddressForm, |
| FormType::kPasswordForm, FormType::kUnknownFormType}, |
| /*used_autofill=*/false, base::Milliseconds(2000)); |
| histogram_tester.ExpectTimeBucketCount( |
| "Autofill.FillDuration.FromInteraction.WithoutAutofill.CreditCard", |
| base::Milliseconds(2000), 1); |
| histogram_tester.ExpectTimeBucketCount( |
| "Autofill.FillDuration.FromInteraction.WithoutAutofill.Address", |
| base::Milliseconds(2000), 1); |
| histogram_tester.ExpectTimeBucketCount( |
| "Autofill.FillDuration.FromInteraction.WithoutAutofill.Password", |
| base::Milliseconds(2000), 1); |
| histogram_tester.ExpectTimeBucketCount( |
| "Autofill.FillDuration.FromInteraction.WithoutAutofill.Unknown", |
| base::Milliseconds(2000), 1); |
| } |
| } |
| |
| // Test class that shares setup code for testing ParseQueryResponse. |
| class AutofillMetricsParseQueryResponseTest : public testing::Test { |
| public: |
| void SetUp() override { |
| FormData form; |
| form.host_frame = test::MakeLocalFrameToken(); |
| form.renderer_id = test::MakeFormRendererId(); |
| form.url = GURL("http://foo.com"); |
| form.main_frame_origin = url::Origin::Create(GURL("http://foo_root.com")); |
| FormFieldData field; |
| field.set_form_control_type(FormControlType::kInputText); |
| |
| field.set_label(u"fullname"); |
| field.set_name(u"fullname"); |
| form.fields.push_back(field); |
| |
| field.set_label(u"address"); |
| field.set_name(u"address"); |
| form.fields.push_back(field); |
| |
| // Checkable fields should be ignored in parsing. |
| FormFieldData checkable_field; |
| checkable_field.set_label(u"radio_button"); |
| checkable_field.set_form_control_type(FormControlType::kInputRadio); |
| checkable_field.set_check_status( |
| FormFieldData::CheckStatus::kCheckableButUnchecked); |
| form.fields.push_back(checkable_field); |
| |
| owned_forms_.push_back(std::make_unique<FormStructure>(form)); |
| forms_.push_back(owned_forms_.back().get()); |
| |
| field.set_label(u"email"); |
| field.set_name(u"email"); |
| form.fields.push_back(field); |
| |
| field.set_label(u"password"); |
| field.set_name(u"password"); |
| field.set_form_control_type(FormControlType::kInputPassword); |
| form.fields.push_back(field); |
| |
| owned_forms_.push_back(std::make_unique<FormStructure>(form)); |
| forms_.push_back(owned_forms_.back().get()); |
| } |
| |
| protected: |
| test::AutofillUnitTestEnvironment autofill_test_environment_; |
| std::vector<std::unique_ptr<FormStructure>> owned_forms_; |
| std::vector<raw_ptr<FormStructure, VectorExperimental>> forms_; |
| }; |
| |
| TEST_F(AutofillMetricsParseQueryResponseTest, ServerHasData) { |
| AutofillQueryResponse response; |
| auto* form_suggestion = response.add_form_suggestions(); |
| AddFieldPredictionToForm(*forms_[0]->field(0), NAME_FULL, form_suggestion); |
| AddFieldPredictionToForm(*forms_[0]->field(1), ADDRESS_HOME_LINE1, |
| form_suggestion); |
| form_suggestion = response.add_form_suggestions(); |
| AddFieldPredictionToForm(*forms_[1]->field(0), EMAIL_ADDRESS, |
| form_suggestion); |
| AddFieldPredictionToForm(*forms_[1]->field(1), NO_SERVER_DATA, |
| form_suggestion); |
| |
| std::string response_string = SerializeAndEncode(response); |
| base::HistogramTester histogram_tester; |
| ParseServerPredictionsQueryResponse(response_string, forms_, |
| test::GetEncodedSignatures(forms_), |
| nullptr, nullptr); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.ServerResponseHasDataForForm"), |
| ElementsAre(Bucket(true, 2))); |
| } |
| |
| // If the server returns NO_SERVER_DATA for one of the forms, expect proper |
| // logging. |
| TEST_F(AutofillMetricsParseQueryResponseTest, OneFormNoServerData) { |
| AutofillQueryResponse response; |
| auto* form_suggestion = response.add_form_suggestions(); |
| AddFieldPredictionToForm(*forms_[0]->field(0), NO_SERVER_DATA, |
| form_suggestion); |
| AddFieldPredictionToForm(*forms_[0]->field(1), NO_SERVER_DATA, |
| form_suggestion); |
| form_suggestion = response.add_form_suggestions(); |
| AddFieldPredictionToForm(*forms_[1]->field(0), EMAIL_ADDRESS, |
| form_suggestion); |
| AddFieldPredictionToForm(*forms_[1]->field(1), NO_SERVER_DATA, |
| form_suggestion); |
| std::string response_string = SerializeAndEncode(response); |
| base::HistogramTester histogram_tester; |
| ParseServerPredictionsQueryResponse(response_string, forms_, |
| test::GetEncodedSignatures(forms_), |
| nullptr, nullptr); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.ServerResponseHasDataForForm"), |
| ElementsAre(Bucket(false, 1), Bucket(true, 1))); |
| } |
| |
| // If the server returns NO_SERVER_DATA for both of the forms, expect proper |
| // logging. |
| TEST_F(AutofillMetricsParseQueryResponseTest, AllFormsNoServerData) { |
| AutofillQueryResponse response; |
| for (int form_idx = 0; form_idx < 2; ++form_idx) { |
| auto* form_suggestion = response.add_form_suggestions(); |
| for (int field_idx = 0; field_idx < 2; ++field_idx) { |
| AddFieldPredictionToForm(*forms_[form_idx]->field(field_idx), |
| NO_SERVER_DATA, form_suggestion); |
| } |
| } |
| |
| std::string response_string = SerializeAndEncode(response); |
| base::HistogramTester histogram_tester; |
| ParseServerPredictionsQueryResponse(response_string, forms_, |
| test::GetEncodedSignatures(forms_), |
| nullptr, nullptr); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.ServerResponseHasDataForForm"), |
| ElementsAre(Bucket(false, 2))); |
| } |
| |
| // If the server returns NO_SERVER_DATA for only some of the fields, expect the |
| // UMA metric to say there is data. |
| TEST_F(AutofillMetricsParseQueryResponseTest, PartialNoServerData) { |
| AutofillQueryResponse response; |
| auto* form_suggestion = response.add_form_suggestions(); |
| AddFieldPredictionToForm(*forms_[0]->field(0), NO_SERVER_DATA, |
| form_suggestion); |
| AddFieldPredictionToForm(*forms_[0]->field(1), PHONE_HOME_NUMBER, |
| form_suggestion); |
| form_suggestion = response.add_form_suggestions(); |
| AddFieldPredictionToForm(*forms_[1]->field(0), NO_SERVER_DATA, |
| form_suggestion); |
| AddFieldPredictionToForm(*forms_[1]->field(1), PHONE_HOME_CITY_CODE, |
| form_suggestion); |
| |
| std::string response_string = SerializeAndEncode(response); |
| base::HistogramTester histogram_tester; |
| ParseServerPredictionsQueryResponse(response_string, forms_, |
| test::GetEncodedSignatures(forms_), |
| nullptr, nullptr); |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.ServerResponseHasDataForForm"), |
| ElementsAre(Bucket(true, 2))); |
| } |
| |
| // Tests that credit card form submissions are logged specially when the form is |
| // on a non-secure page. |
| TEST_F(AutofillMetricsTest, NonSecureCreditCardForm) { |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/false, |
| /*include_full_server_credit_card=*/false, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| |
| FormData form = |
| CreateForm({CreateTestFormField("Name on card", "cc-name", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Credit card", "cardnum", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Month", "cardmonth", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Expiration date", "expdate", "", |
| FormControlType::kInputText)}); |
| std::vector<FieldType> field_types = { |
| CREDIT_CARD_NAME_FULL, CREDIT_CARD_NUMBER, CREDIT_CARD_EXP_MONTH, |
| CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}; |
| |
| // Non-https origin. |
| GURL frame_origin("http://example_root.com/form.html"); |
| form.main_frame_origin = url::Origin::Create(frame_origin); |
| autofill_client_->set_form_origin(frame_origin); |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| // Simulate an Autofill query on a credit card field. |
| { |
| base::UserActionTester user_action_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.front()); |
| EXPECT_EQ(1, user_action_tester.GetActionCount( |
| "Autofill_PolledCreditCardSuggestions")); |
| } |
| |
| // Simulate submitting the credit card form. |
| { |
| base::HistogramTester histograms; |
| SubmitForm(form); |
| histograms.ExpectBucketCount("Autofill.FormEvents.CreditCard", |
| FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1); |
| histograms.ExpectBucketCount( |
| "Autofill.FormEvents.CreditCard.WithOnlyLocalData", |
| FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1); |
| } |
| } |
| |
| // Tests that credit card form submissions are *not* logged specially when the |
| // form is *not* on a non-secure page. |
| TEST_F(AutofillMetricsTest, |
| NonSecureCreditCardFormMetricsNotRecordedOnSecurePage) { |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/false, |
| /*include_full_server_credit_card=*/false, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| |
| FormData form = |
| CreateForm({CreateTestFormField("Name on card", "cc-name", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Credit card", "cardnum", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Expiration date", "expdate", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = {CREDIT_CARD_NAME_FULL, |
| CREDIT_CARD_NUMBER, |
| CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| // Simulate an Autofill query on a credit card field. |
| { |
| base::UserActionTester user_action_tester; |
| autofill_manager().OnAskForValuesToFillTest(form, form.fields.back()); |
| EXPECT_EQ(1, user_action_tester.GetActionCount( |
| "Autofill_PolledCreditCardSuggestions")); |
| } |
| |
| // Simulate submitting the credit card form. |
| { |
| base::HistogramTester histograms; |
| SubmitForm(form); |
| histograms.ExpectBucketCount("Autofill.FormEvents.CreditCard", |
| FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1); |
| histograms.ExpectBucketCount("Autofill.FormEvents.CreditCard", |
| FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1); |
| } |
| } |
| |
| // Tests that logging CardUploadDecision UKM works as expected. |
| TEST_F(AutofillMetricsTest, RecordCardUploadDecisionMetric) { |
| GURL url("https://www.google.com"); |
| int upload_decision = 1; |
| autofill_client_->set_form_origin(url); |
| |
| autofill_metrics::LogCardUploadDecisionsUkm( |
| &test_ukm_recorder(), autofill_client_->GetUkmSourceId(), url, |
| upload_decision); |
| auto entries = test_ukm_recorder().GetEntriesByName( |
| UkmCardUploadDecisionType::kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| for (const ukm::mojom::UkmEntry* const entry : entries) { |
| test_ukm_recorder().ExpectEntrySourceHasUrl(entry, url); |
| EXPECT_EQ(1u, entry->metrics.size()); |
| test_ukm_recorder().ExpectEntryMetric( |
| entry, UkmCardUploadDecisionType::kUploadDecisionName, upload_decision); |
| } |
| } |
| |
| // Tests that logging DeveloperEngagement UKM works as expected. |
| TEST_F(AutofillMetricsTest, RecordDeveloperEngagementMetric) { |
| GURL url("https://www.google.com"); |
| int form_structure_metric = 1; |
| FormSignature form_signature(100); |
| autofill_client_->set_form_origin(url); |
| |
| AutofillMetrics::LogDeveloperEngagementUkm( |
| &test_ukm_recorder(), autofill_client_->GetUkmSourceId(), url, true, |
| {FormType::kCreditCardForm}, form_structure_metric, form_signature); |
| auto entries = test_ukm_recorder().GetEntriesByName( |
| UkmDeveloperEngagementType::kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| for (const ukm::mojom::UkmEntry* const entry : entries) { |
| test_ukm_recorder().ExpectEntrySourceHasUrl(entry, url); |
| EXPECT_EQ(4u, entry->metrics.size()); |
| test_ukm_recorder().ExpectEntryMetric( |
| entry, UkmDeveloperEngagementType::kDeveloperEngagementName, |
| form_structure_metric); |
| test_ukm_recorder().ExpectEntryMetric( |
| entry, UkmDeveloperEngagementType::kIsForCreditCardName, true); |
| test_ukm_recorder().ExpectEntryMetric( |
| entry, UkmDeveloperEngagementType::kFormTypesName, |
| AutofillMetrics::FormTypesToBitVector({FormType::kCreditCardForm})); |
| test_ukm_recorder().ExpectEntryMetric( |
| entry, UkmDeveloperEngagementType::kFormSignatureName, |
| form_signature.value()); |
| } |
| } |
| |
| // Tests that no UKM is logged when the URL is not valid. |
| TEST_F(AutofillMetricsTest, RecordCardUploadDecisionMetric_InvalidUrl) { |
| GURL url(""); |
| test_ukm_recorder().Purge(); |
| autofill_metrics::LogCardUploadDecisionsUkm(&test_ukm_recorder(), -1, url, 1); |
| EXPECT_EQ(0ul, test_ukm_recorder().sources_count()); |
| EXPECT_EQ(0ul, test_ukm_recorder().entries_count()); |
| } |
| |
| // Tests that no UKM is logged when the ukm service is null. |
| TEST_F(AutofillMetricsTest, RecordCardUploadDecisionMetric_NoUkmService) { |
| GURL url("https://www.google.com"); |
| test_ukm_recorder().Purge(); |
| autofill_metrics::LogCardUploadDecisionsUkm(nullptr, -1, url, 1); |
| EXPECT_EQ(0ul, test_ukm_recorder().sources_count()); |
| EXPECT_EQ(0ul, test_ukm_recorder().entries_count()); |
| } |
| |
| // Test the ukm recorded when Suggestion is shown. |
| TEST_F(AutofillMetricsTest, AutofillSuggestionsShownTest) { |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/false, |
| /*include_full_server_credit_card=*/false, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| FormData form = |
| CreateForm({CreateTestFormField("Name on card", "cc-name", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Credit card", "cardnum", "", |
| FormControlType::kInputText), |
| CreateTestFormField("Month", "card_month", "", |
| FormControlType::kInputText)}); |
| |
| FormFieldData field; |
| std::vector<FieldType> field_types = { |
| CREDIT_CARD_NAME_FULL, CREDIT_CARD_NUMBER, CREDIT_CARD_EXP_MONTH}; |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| // Simulate and Autofill query on credit card name field. |
| DidShowAutofillSuggestions(form); |
| VerifyUkm( |
| &test_ukm_recorder(), form, UkmSuggestionsShownType::kEntryName, |
| {{{UkmSuggestionsShownType::kMillisecondsSinceFormParsedName, 0}, |
| {UkmSuggestionsShownType::kHeuristicTypeName, CREDIT_CARD_NAME_FULL}, |
| {UkmSuggestionsShownType::kHtmlFieldTypeName, |
| HtmlFieldType::kUnspecified}, |
| {UkmSuggestionsShownType::kServerTypeName, CREDIT_CARD_NAME_FULL}, |
| {UkmSuggestionsShownType::kFieldSignatureName, |
| Collapse(CalculateFieldSignatureForField(form.fields[0])).value()}, |
| {UkmSuggestionsShownType::kFormSignatureName, |
| Collapse(CalculateFormSignature(form)).value()}}}); |
| } |
| |
| TEST_F(AutofillMetricsTest, DynamicFormMetrics) { |
| FormData form = CreateForm( |
| {CreateTestFormField("State", "state", "", FormControlType::kInputText), |
| CreateTestFormField("City", "city", "", FormControlType::kInputText), |
| CreateTestFormField("Street", "street", "", |
| FormControlType::kInputText)}); |
| |
| std::vector<FieldType> field_types = {ADDRESS_HOME_STATE, ADDRESS_HOME_CITY, |
| ADDRESS_HOME_STREET_ADDRESS}; |
| |
| // Simulate seeing. |
| base::HistogramTester histogram_tester; |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| // Simulate checking whether to fill a dynamic form before the form was filled |
| // initially. |
| test_api(autofill_manager()) |
| .form_filler() |
| .ShouldTriggerRefill(FormStructure(form), |
| RefillTriggerReason::kFormChanged); |
| histogram_tester.ExpectTotalCount("Autofill.FormEvents.Address", 0); |
| |
| // Simulate filling the form. |
| FillTestProfile(form); |
| |
| // Dynamically change the form. |
| form.fields.pop_back(); |
| |
| // Simulate checking whether to fill a dynamic form after the form was filled |
| // initially. |
| test_api(autofill_manager()) |
| .form_filler() |
| .ShouldTriggerRefill(FormStructure(form), |
| RefillTriggerReason::kFormChanged); |
| EXPECT_THAT(histogram_tester.GetAllSamples("Autofill.FormEvents.Address"), |
| BucketsInclude(Bucket(FORM_EVENT_DID_DYNAMIC_REFILL, 0))); |
| |
| // Trigger a refill, the refill metric should be updated. |
| autofill_manager().OnFormsSeen({form}, /*removed_forms=*/{}); |
| EXPECT_THAT(histogram_tester.GetAllSamples("Autofill.FormEvents.Address"), |
| BucketsInclude(Bucket(FORM_EVENT_DID_DYNAMIC_REFILL, 1))); |
| } |
| |
| // Verify that we don't log Autofill.WebOTP.OneTimeCode.FromAutocomplete if the |
| // frame has no form. |
| TEST_F(AutofillMetricsTest, FrameHasNoForm) { |
| base::HistogramTester histogram_tester; |
| autofill_driver_.reset(); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.WebOTP.OneTimeCode.FromAutocomplete", 0); |
| } |
| |
| // Verify that we correctly log metrics if a frame has |
| // autocomplete="one-time-code". |
| TEST_F(AutofillMetricsTest, FrameHasAutocompleteOneTimeCode) { |
| FormData form = CreateForm( |
| {CreateTestFormField("", "", "", FormControlType::kInputPassword, |
| "one-time-code"), |
| CreateTestFormField("", "", "", FormControlType::kInputPassword)}); |
| |
| base::HistogramTester histogram_tester; |
| SeeForm(form); |
| autofill_driver_.reset(); |
| // Verifies that autocomplete="one-time-code" in a form is correctly recorded. |
| histogram_tester.ExpectBucketCount( |
| "Autofill.WebOTP.OneTimeCode.FromAutocomplete", |
| /* has_one_time_code */ 1, |
| /* sample count */ 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.WebOTP.OneTimeCode.FromAutocomplete", 1); |
| } |
| |
| // Verify that we correctly log metrics if a frame does not have |
| // autocomplete="one-time-code". |
| TEST_F(AutofillMetricsTest, FrameDoesNotHaveAutocompleteOneTimeCode) { |
| FormData form = CreateForm( |
| {CreateTestFormField("", "", "", FormControlType::kInputPassword)}); |
| |
| base::HistogramTester histogram_tester; |
| SeeForm(form); |
| autofill_driver_.reset(); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.WebOTP.OneTimeCode.FromAutocomplete", |
| /* has_one_time_code */ 0, |
| /* sample count */ 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.WebOTP.OneTimeCode.FromAutocomplete", 1); |
| } |
| |
| // Verify that we correctly log metrics when a phone number field does not have |
| // autocomplete attribute but there are at least 3 fields in the form. |
| TEST_F(AutofillMetricsTest, FrameHasPhoneNumberFieldWithoutAutocomplete) { |
| // At least 3 fields are necessary for FormStructure to compute proper field |
| // types if autocomplete attribute value is not available. |
| FormData form = |
| CreateForm({CreateTestFormField("Phone", "phone", "", |
| FormControlType::kInputTelephone), |
| CreateTestFormField("Last Name", "lastname", "", |
| FormControlType::kInputText), |
| CreateTestFormField("First Name", "firstname", "", |
| FormControlType::kInputText)}); |
| |
| base::HistogramTester histogram_tester; |
| SeeForm(form); |
| autofill_driver_.reset(); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.WebOTP.PhoneNumberCollection.ParseResult", |
| /* has_phone_number_field */ 1, |
| /* sample count */ 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.WebOTP.PhoneNumberCollection.ParseResult", 1); |
| } |
| |
| // Verify that we correctly log metrics when a phone number field does not have |
| // autocomplete attribute and there are less than 3 fields in the form. |
| TEST_F(AutofillMetricsTest, FrameHasSinglePhoneNumberFieldWithoutAutocomplete) { |
| // At least 3 fields are necessary for FormStructure to compute proper field |
| // types if autocomplete attribute value is not available. |
| FormData form = CreateForm({CreateTestFormField( |
| "Phone", "phone", "", FormControlType::kInputTelephone)}); |
| |
| base::HistogramTester histogram_tester; |
| SeeForm(form); |
| autofill_driver_.reset(); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.WebOTP.PhoneNumberCollection.ParseResult", |
| /* has_phone_number_field */ 0, |
| /* sample count */ 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.WebOTP.PhoneNumberCollection.ParseResult", 1); |
| } |
| |
| // Verify that we correctly log metrics when a phone number field has |
| // autocomplete attribute. |
| TEST_F(AutofillMetricsTest, FrameHasPhoneNumberFieldWithAutocomplete) { |
| FormData form; // Form with phone number. |
| CreateSimpleForm(autofill_client_->form_origin(), form); |
| form.fields = { |
| CreateTestFormField("", "", "", FormControlType::kInputText, "phone")}; |
| |
| base::HistogramTester histogram_tester; |
| SeeForm(form); |
| autofill_driver_.reset(); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.WebOTP.PhoneNumberCollection.ParseResult", |
| /* has_phone_number_field */ 1, |
| /* sample count */ 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.WebOTP.PhoneNumberCollection.ParseResult", 1); |
| } |
| |
| // Verify that we correctly log metrics when a form does not have phone number |
| // field. |
| TEST_F(AutofillMetricsTest, FrameDoesNotHavePhoneNumberField) { |
| FormData form = CreateForm( |
| {CreateTestFormField("", "", "", FormControlType::kInputPassword)}); |
| |
| base::HistogramTester histogram_tester; |
| SeeForm(form); |
| autofill_driver_.reset(); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.WebOTP.PhoneNumberCollection.ParseResult", |
| /* has_phone_number_field */ 0, |
| /* sample count */ 1); |
| histogram_tester.ExpectTotalCount( |
| "Autofill.WebOTP.PhoneNumberCollection.ParseResult", 1); |
| } |
| |
| // ContentAutofillDriver is not visible to TestAutofillDriver on iOS. |
| // In addition, WebOTP will not ship on iOS. |
| #if !BUILDFLAG(IS_IOS) |
| |
| struct WebOTPPhoneCollectionMetricsTestCase { |
| std::vector<const char*> autocomplete_field; |
| PhoneCollectionMetricState phone_collection_metric_state; |
| bool report_autofill_web_otp_metrics = false; |
| }; |
| |
| class WebOTPPhoneCollectionMetricsTest |
| : public AutofillMetricsTest, |
| public ::testing::WithParamInterface< |
| WebOTPPhoneCollectionMetricsTestCase> {}; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| WebOTPPhoneCollectionMetricsTest, |
| WebOTPPhoneCollectionMetricsTest, |
| testing::Values( |
| // Verify that we correctly log PhoneCollectionMetricState::kNone. |
| WebOTPPhoneCollectionMetricsTestCase{{"password"}, |
| PhoneCollectionMetricState::kNone}, |
| // Verify that we correctly log PhoneCollectionMetricState::kOTC. |
| WebOTPPhoneCollectionMetricsTestCase{{"one-time-code"}, |
| PhoneCollectionMetricState::kOTC}, |
| // Verify that we correctly log PhoneCollectionMetricState::kWebOTP. |
| WebOTPPhoneCollectionMetricsTestCase{ |
| {}, |
| PhoneCollectionMetricState::kWebOTP, |
| true}, |
| // Verify that we correctly log |
| // PhoneCollectionMetricState::kWebOTPPlusOTC. |
| WebOTPPhoneCollectionMetricsTestCase{ |
| {"one-time-code"}, |
| PhoneCollectionMetricState::kWebOTPPlusOTC, |
| true}, |
| // Verify that we correctly log PhoneCollectionMetricState::kPhone. |
| WebOTPPhoneCollectionMetricsTestCase{ |
| {"tel"}, |
| PhoneCollectionMetricState::kPhone}, |
| // Verify that we correctly log |
| // PhoneCollectionMetricState::kPhonePlusOTC. |
| WebOTPPhoneCollectionMetricsTestCase{ |
| {"tel", "one-time-code"}, |
| PhoneCollectionMetricState::kPhonePlusOTC}, |
| // Verify that we correctly log |
| // PhoneCollectionMetricState::kPhonePlusWebOTP. |
| WebOTPPhoneCollectionMetricsTestCase{ |
| {"tel"}, |
| PhoneCollectionMetricState::kPhonePlusWebOTP, |
| true}, |
| // Verify that we correctly log |
| // PhoneCollectionMetricState::kPhonePlusWebOTPPlusOTC. |
| WebOTPPhoneCollectionMetricsTestCase{ |
| {"tel", "one-time-code"}, |
| PhoneCollectionMetricState::kPhonePlusWebOTPPlusOTC, |
| true})); |
| |
| TEST_P(WebOTPPhoneCollectionMetricsTest, |
| TestWebOTPPhoneCollectionMetricsState) { |
| auto test_case = GetParam(); |
| |
| if (!test_case.autocomplete_field.empty()) { |
| FormData form; |
| CreateSimpleForm(autofill_client_->form_origin(), form); |
| for (const char* autocomplete : test_case.autocomplete_field) { |
| form.fields.push_back(CreateTestFormField( |
| "", "", "", FormControlType::kInputText, autocomplete)); |
| } |
| |
| SeeForm(form); |
| } |
| |
| base::HistogramTester histogram_tester; |
| autofill_manager().ReportAutofillWebOTPMetrics( |
| test_case.report_autofill_web_otp_metrics); |
| |
| EXPECT_THAT( |
| histogram_tester.GetAllSamples("Autofill.WebOTP.PhonePlusWebOTPPlusOTC"), |
| BucketsAre(Bucket(test_case.phone_collection_metric_state, 1))); |
| } |
| |
| // Verify that proper PhoneCollectionMetricsState is logged to UKM. |
| TEST_F(AutofillMetricsTest, WebOTPPhoneCollectionMetricsStateLoggedToUKM) { |
| auto entries = test_ukm_recorder().GetEntriesByName( |
| ukm::builders::WebOTPImpact::kEntryName); |
| ASSERT_TRUE(entries.empty()); |
| |
| FormData form; |
| CreateSimpleForm(autofill_client_->form_origin(), form); |
| // Document collects phone number |
| form.fields.push_back( |
| CreateTestFormField("", "", "", FormControlType::kInputTelephone, "tel")); |
| // Document uses OntTimeCode |
| form.fields.push_back(CreateTestFormField( |
| "", "", "", FormControlType::kInputText, "one-time-code")); |
| |
| base::HistogramTester histogram_tester; |
| SeeForm(form); |
| autofill_manager().ReportAutofillWebOTPMetrics(true); |
| |
| entries = test_ukm_recorder().GetEntriesByName( |
| ukm::builders::WebOTPImpact::kEntryName); |
| ASSERT_EQ(1u, entries.size()); |
| |
| const int64_t* metric = |
| test_ukm_recorder().GetEntryMetric(entries[0], "PhoneCollection"); |
| EXPECT_EQ(*metric, static_cast<int>( |
| PhoneCollectionMetricState::kPhonePlusWebOTPPlusOTC)); |
| } |
| |
| TEST_F(AutofillMetricsTest, AutocompleteOneTimeCodeFormFilledDuration) { |
| FormData form = CreateForm({CreateTestFormField( |
| "", "", "", FormControlType::kInputPassword, "one-time-code")}); |
| form.fields[0].set_value(u"123456"); |
| |
| { |
| base::HistogramTester histogram_tester; |
| SeeForm(form); |
| task_environment_.FastForwardBy(base::Microseconds(17)); |
| SubmitForm(form); |
| |
| histogram_tester.ExpectTotalCount( |
| "Autofill.WebOTP.OneTimeCode.FillDuration.FromLoad", 1); |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.WebOTP.OneTimeCode.FillDuration.FromLoad", 16, 1); |
| autofill_manager().Reset(); |
| } |
| |
| { |
| base::HistogramTester histogram_tester; |
| SeeForm(form); |
| base::TimeTicks parse_time = autofill_manager() |
| .form_structures() |
| .begin() |
| ->second->form_parsed_timestamp(); |
| FillAutofillFormData(form, parse_time + base::Microseconds(5)); |
| SimulateUserChangedTextField(form, form.fields.front(), |
| parse_time + base::Microseconds(3)); |
| task_environment_.FastForwardBy(base::Microseconds(17)); |
| SubmitForm(form); |
| |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.WebOTP.OneTimeCode.FillDuration.FromInteraction", 14, 1); |
| autofill_manager().Reset(); |
| } |
| } |
| |
| #endif // !BUILDFLAG(IS_IOS) |
| |
| TEST_F(AutofillMetricsTest, OnAutocompleteSuggestionsShown) { |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::OnAutocompleteSuggestionsShown(); |
| histogram_tester.ExpectBucketCount( |
| "Autocomplete.Events2", AutofillMetrics::AUTOCOMPLETE_SUGGESTIONS_SHOWN, |
| /*expected_count=*/1); |
| } |
| |
| // Verify that we correctly log the IsEnabled metrics with the appropriate sync |
| // state. |
| TEST_F(AutofillMetricsTest, LogIsAutofillEnabledAtPageLoad_BySyncState) { |
| { |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::LogIsAutofillEnabledAtPageLoad( |
| /*enabled=*/true, PaymentsSigninState::kSignedIn); |
| histogram_tester.ExpectBucketCount("Autofill.IsEnabled.PageLoad.SignedIn", |
| true, 1); |
| // Make sure the metric without the sync state is still recorded. |
| histogram_tester.ExpectBucketCount("Autofill.IsEnabled.PageLoad", true, 1); |
| } |
| { |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::LogIsAutofillEnabledAtPageLoad( |
| /*enabled=*/false, PaymentsSigninState::kSignedOut); |
| histogram_tester.ExpectBucketCount("Autofill.IsEnabled.PageLoad.SignedOut", |
| false, 1); |
| // Make sure the metric without the sync state is still recorded. |
| histogram_tester.ExpectBucketCount("Autofill.IsEnabled.PageLoad", false, 1); |
| } |
| } |
| |
| TEST_F(AutofillMetricsTest, LogServerCardLinkClicked) { |
| { |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::LogServerCardLinkClicked(PaymentsSigninState::kSignedIn); |
| histogram_tester.ExpectTotalCount("Autofill.ServerCardLinkClicked", 1); |
| histogram_tester.ExpectBucketCount("Autofill.ServerCardLinkClicked", |
| PaymentsSigninState::kSignedIn, 1); |
| } |
| { |
| base::HistogramTester histogram_tester; |
| AutofillMetrics::LogServerCardLinkClicked(PaymentsSigninState::kSignedOut); |
| histogram_tester.ExpectTotalCount("Autofill.ServerCardLinkClicked", 1); |
| histogram_tester.ExpectBucketCount("Autofill.ServerCardLinkClicked", |
| PaymentsSigninState::kSignedOut, 1); |
| } |
| } |
| |
| TEST_F(AutofillMetricsTest, GetFieldTypeUserEditStatusMetric) { |
| // The id of ADDRESS_HOME_COUNTRY is 36 = 0b10'0100. |
| FieldType server_type = ADDRESS_HOME_COUNTRY; |
| // The id of AUTOFILL_FIELD_WAS_NOT_EDITED is 1. |
| AutofillMetrics::AutofilledFieldUserEditingStatusMetric metric = |
| AutofillMetrics::AutofilledFieldUserEditingStatusMetric:: |
| AUTOFILLED_FIELD_WAS_NOT_EDITED; |
| |
| int expected_result = 0b10'0100'0001; |
| int actual_result = GetFieldTypeUserEditStatusMetric(server_type, metric); |
| EXPECT_EQ(expected_result, actual_result); |
| } |
| |
| // Validate that correct page language values are taken from |
| // |AutofillClient| and logged upon form submission. |
| TEST_F(AutofillMetricsTest, PageLanguageMetricsExpectedCase) { |
| FormData form; |
| CreateSimpleForm(autofill_client_->form_origin(), form); |
| |
| // Set up language state. |
| translate::LanguageDetectionDetails language_detection_details; |
| language_detection_details.adopted_language = "ub"; |
| autofill_manager().OnLanguageDetermined(language_detection_details); |
| autofill_client_->GetLanguageState()->SetSourceLanguage("ub"); |
| autofill_client_->GetLanguageState()->SetCurrentLanguage("ub"); |
| int language_code = 'u' * 256 + 'b'; |
| |
| base::HistogramTester histogram_tester; |
| SubmitForm(form); |
| |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.ParsedFieldTypesUsingTranslatedPageLanguage", language_code, 1); |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.ParsedFieldTypesWasPageTranslated", false, 1); |
| } |
| |
| // Validate that invalid language codes (with disallowed symbols in this case) |
| // get logged as invalid. |
| TEST_F(AutofillMetricsTest, PageLanguageMetricsInvalidLanguage) { |
| FormData form; |
| CreateSimpleForm(autofill_client_->form_origin(), form); |
| |
| // Set up language state. |
| translate::LanguageDetectionDetails language_detection_details; |
| language_detection_details.adopted_language = "en"; |
| autofill_manager().OnLanguageDetermined(language_detection_details); |
| autofill_client_->GetLanguageState()->SetSourceLanguage("en"); |
| autofill_client_->GetLanguageState()->SetCurrentLanguage("other"); |
| |
| base::HistogramTester histogram_tester; |
| SubmitForm(form); |
| |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.ParsedFieldTypesUsingTranslatedPageLanguage", 0, 1); |
| histogram_tester.ExpectUniqueSample( |
| "Autofill.ParsedFieldTypesWasPageTranslated", true, 1); |
| } |
| |
| // Base class for cross-frame filling metrics, in particular for |
| // Autofill.CreditCard.SeamlessFills.*. |
| class AutofillMetricsCrossFrameFormTest : public AutofillMetricsTest { |
| public: |
| struct CreditCardAndCvc { |
| CreditCard credit_card; |
| std::u16string cvc; |
| }; |
| |
| AutofillMetricsCrossFrameFormTest() = default; |
| ~AutofillMetricsCrossFrameFormTest() override = default; |
| |
| void SetUp() override { |
| AutofillMetricsTest::SetUp(); |
| |
| RecreateCreditCards(/*include_local_credit_card=*/true, |
| /*include_masked_server_credit_card=*/false, |
| /*include_full_server_credit_card=*/false, |
| /*masked_card_is_enrolled_for_virtual_card=*/false); |
| |
| credit_card_with_cvc_ = { |
| .credit_card = *autofill_client_->GetPersonalDataManager() |
| ->payments_data_manager() |
| .GetCreditCardsToSuggest() |
| .front(), |
| .cvc = u"123"}; |
| |
| url::Origin main_origin = |
| url::Origin::Create(GURL("https://example.test/")); |
| url::Origin other_origin = url::Origin::Create(GURL("https://other.test/")); |
| form_ = test::GetFormData( |
| {.description_for_logging = "CrossFrameFillingMetrics", |
| .fields = |
| { |
| {.label = u"Cardholder name", |
| .name = u"card_name", |
| .is_autofilled = false}, |
| {.label = u"CCNumber", |
| .name = u"ccnumber", |
| .is_autofilled = false, |
| .origin = other_origin}, |
| {.label = u"ExpDate", |
| .name = u"expdate", |
| .is_autofilled = false}, |
| {.is_visible = false, |
| .label = u"CVC", |
| .name = u"cvc", |
| .is_autofilled = false, |
| .origin = other_origin}, |
| }, |
| .renderer_id = test::MakeFormRendererId(), |
| .main_frame_origin = main_origin}); |
| |
| ASSERT_EQ(form_.main_frame_origin, form_.fields[0].origin()); |
| ASSERT_EQ(form_.main_frame_origin, form_.fields[2].origin()); |
| ASSERT_NE(form_.main_frame_origin, form_.fields[1].origin()); |
| ASSERT_NE(form_.main_frame_origin, form_.fields[3].origin()); |
| ASSERT_EQ(form_.fields[1].origin(), form_.fields[3].origin()); |
| |
| // Mock a simplified security model which allows to filter (only) fields |
| // from the same origin. |
| autofill_driver_->SetFieldTypeMapFilter(base::BindRepeating( |
| [](AutofillMetricsCrossFrameFormTest* self, |
| const url::Origin& triggered_origin, FieldGlobalId field, |
| FieldType) { |
| return triggered_origin == self->GetFieldById(field).origin(); |
| }, |
| this)); |
| } |
| |
| CreditCardAndCvc& fill_data() { return credit_card_with_cvc_; } |
| |
| // Any call to FillForm() should be followed by a SetFormValues() call to |
| // mimic its effect on |form_|. |
| void FillForm(const FormFieldData& triggering_field) { |
| autofill_manager().FillOrPreviewCreditCardForm( |
| mojom::ActionPersistence::kFill, form_, triggering_field, |
| fill_data().credit_card, fill_data().cvc, |
| {.trigger_source = AutofillTriggerSource::kPopup}); |
| } |
| |
| // Sets the field values of |form_| according to the parameters. |
| // |
| // Since this test suite doesn't use mocks, we can't intercept the autofilled |
| // form. Therefore, after each manual fill or autofill, we shall call |
| // SetFormValues() |
| void SetFormValues(const FieldTypeSet& fill_field_types, |
| bool is_autofilled, |
| bool is_user_typed) { |
| auto type_to_index = base::MakeFixedFlatMap<FieldType, size_t>( |
| {{CREDIT_CARD_NAME_FULL, 0}, |
| {CREDIT_CARD_NUMBER, 1}, |
| {CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, 2}, |
| {CREDIT_CARD_VERIFICATION_CODE, 3}}); |
| |
| for (FieldType fill_type : fill_field_types) { |
| auto index_it = type_to_index.find(fill_type); |
| ASSERT_NE(index_it, type_to_index.end()); |
| FormFieldData& field = form_.fields[index_it->second]; |
| field.set_value(fill_type != CREDIT_CARD_VERIFICATION_CODE |
| ? fill_data().credit_card.GetRawInfo(fill_type) |
| : fill_data().cvc); |
| field.set_is_autofilled(is_autofilled); |
| field.set_properties_mask((field.properties_mask() & ~kUserTyped) | |
| (is_user_typed ? kUserTyped : 0)); |
| } |
| } |
| |
| FormFieldData& GetFieldById(FieldGlobalId field) { |
| auto it = |
| base::ranges::find(form_.fields, field, &FormFieldData::global_id); |
| CHECK(it != form_.fields.end()); |
| return *it; |
| } |
| |
| FormData form_; |
| CreditCardAndCvc credit_card_with_cvc_; |
| }; |
| |
| // This fixture adds utilities for the seamlessness metric names. |
| // |
| // These metric names get very long, and with >16 variants the tests become |
| // unreadable otherwise. |
| class AutofillMetricsSeamlessnessTest |
| : public AutofillMetricsCrossFrameFormTest { |
| public: |
| struct MetricName { |
| enum class Fill { kFills, kFillable }; |
| enum class Time { kBefore, kAfter, kSubmission }; |
| enum class Visibility { kAll, kVisible }; |
| enum class Variant { kQualitative, kBitmask }; |
| |
| Fill fill; |
| Time time; |
| Visibility visibility; |
| Variant variant; |
| |
| std::string str() const { |
| return base::StringPrintf( |
| "Autofill.CreditCard.Seamless%s.%s%s%s", |
| fill == Fill::kFills ? "Fills" : "Fillable", |
| time == Time::kSubmission ? "AtSubmissionTime" |
| : time == Time::kBefore ? "AtFillTimeBeforeSecurityPolicy" |
| : "AtFillTimeAfterSecurityPolicy", |
| visibility == Visibility::kAll ? "" : ".Visible", |
| variant == Variant::kQualitative ? "" : ".Bitmask"); |
| } |
| }; |
| |
| static constexpr auto kFills = MetricName::Fill::kFills; |
| static constexpr auto kFillable = MetricName::Fill::kFillable; |
| static constexpr auto kBefore = MetricName::Time::kBefore; |
| static constexpr auto kAfter = MetricName::Time::kAfter; |
| static constexpr auto kSubmission = MetricName::Time::kSubmission; |
| static constexpr auto kAll = MetricName::Visibility::kAll; |
| static constexpr auto kVisible = MetricName::Visibility::kVisible; |
| static constexpr auto kQualitative = MetricName::Variant::kQualitative; |
| static constexpr auto kBitmask = MetricName::Variant::kBitmask; |
| |
| protected: |
| AutofillMetricsSeamlessnessTest() { |
| base::FieldTrialParams feature_parameters{ |
| {features::kAutofillLogUKMEventsWithSamplingOnSessionRate.name, "100"}, |
| }; |
| scoped_features_.InitWithFeaturesAndParameters( |
| /*enabled_features=*/{{features:: |
| kAutofillLogUKMEventsWithSamplingOnSession, |
| feature_parameters}, |
| {features::kAutofillParsingPatternProvider, {}}}, |
| /*disabled_features=*/{}); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_features_; |
| }; |
| |
| // Tests that Autofill.CreditCard.SeamlessFills.* is not emitted for manual |
| // fills. |
| TEST_F(AutofillMetricsSeamlessnessTest, |
| DoNotLogCreditCardSeamlessFillsMetricIfNotAutofilled) { |
| using UkmBuilder = ukm::builders::Autofill_CreditCardFill; |
| base::HistogramTester histogram_tester; |
| SeeForm(form_); |
| |
| // Fake manual fill. |
| SetFormValues( |
| {CREDIT_CARD_NAME_FULL, CREDIT_CARD_NUMBER, |
| CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, CREDIT_CARD_VERIFICATION_CODE}, |
| /*is_autofilled=*/false, /*is_user_typed=*/true); |
| |
| // Fakes an Autofill. |
| // This fills nothing because all fields have been manually filled. |
| FillForm(FormFieldData()); |
| SubmitForm(form_); |
| ResetDriverToCommitMetrics(); |
| |
| for (auto fill : {kFills, kFillable}) { |
| for (auto time : {kBefore, kAfter, kSubmission}) { |
| for (auto visibility : {kAll, kVisible}) { |
| for (auto variant : {kQualitative, kBitmask}) { |
| histogram_tester.ExpectTotalCount( |
| MetricName{fill, time, visibility, variant}.str(), 0); |
| } |
| } |
| } |
| } |
| |
| VerifyUkm(&test_ukm_recorder(), form_, UkmBuilder::kEntryName, {}); |
| } |
| |
| // Tests that Autofill.CreditCard.SeamlessFills.* are emitted. |
| TEST_F(AutofillMetricsSeamlessnessTest, |
| LogCreditCardSeamlessFillsMetricIfAutofilledWithoutCvc) { |
| using Metric = AutofillMetrics::CreditCardSeamlessness::Metric; |
| using UkmBuilder = ukm::builders::Autofill_CreditCardFill; |
| |
| // `Metric` as raw integer for UKM. |
| constexpr auto kFullFill = static_cast<uint64_t>(Metric::kFullFill); |
| constexpr auto kOptionalCvcMissing = |
| static_cast<uint64_t>(Metric::kOptionalCvcMissing); |
| constexpr auto kPartialFill = static_cast<uint64_t>(Metric::kPartialFill); |
| // Bits of the bitmask. |
| constexpr uint8_t kName = true << 3; |
| constexpr uint8_t kNumber = true << 2; |
| constexpr uint8_t kExp = true << 1; |
| constexpr uint8_t kCvc = true << 0; |
| // The shared-autofill metric. |
| enum SharedAutofillMetric : uint64_t { |
| kSharedAutofillIsIrrelevant = 0, |
| kSharedAutofillWouldHelp = 1, |
| kSharedAutofillDidHelp = 2, |
| }; |
| |
| base::HistogramTester histogram_tester; |
| auto SamplesOf = [&histogram_tester](MetricName metric) { |
| return histogram_tester.GetAllSamples(metric.str()); |
| }; |
| |
| SeeForm(form_); |
| |
| fill_data().cvc = u""; |
| |
| // Fakes an Autofill with the following behavior: |
| // - before security and assuming a complete profile: kFullFill; |
| // - before security and without a CVC: kOptionalCvcMissing; |
| // - after security and assuming a complete profile: kPartialFill; |
| // - after security and without a CVC: kPartialFill; |
| // because due to the security policy, only NAME and EXP_DATE are filled. |
| // The CVC field is invisible. |
| FillForm(form_.fields[0]); |
| SetFormValues({CREDIT_CARD_NAME_FULL, CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}, |
| /*is_autofilled=*/true, /*is_user_typed=*/false); |
| |
| // Fakes an Autofill with the following behavior: |
| // - before security and assuming a complete profile: kFullFill; |
| // - before security and without a CVC: kPartialFill; |
| // - after security and assuming a complete profile: kPartialFill; |
| // - after security and without a CVC: kPartialFill; |
| // because the due to the security policy, only NUMBER and CVC could be |
| // filled. |
| // The CVC field is invisible. |
| FillForm(form_.fields[1]); |
| SetFormValues({CREDIT_CARD_NUMBER}, |
| /*is_autofilled=*/true, /*is_user_typed=*/false); |
| |
| SubmitForm(form_); |
| ResetDriverToCommitMetrics(); |
| |
| // Bitmask metrics. |
| EXPECT_THAT(SamplesOf({kFillable, kBefore, kAll, kBitmask}), |
| BucketsAre(Bucket(kName | kNumber | kExp | kCvc, 2))); |
| EXPECT_THAT(SamplesOf({kFillable, kAfter, kAll, kBitmask}), |
| BucketsAre(Bucket(kName | kExp, 1), Bucket(kNumber | kCvc, 1))); |
| EXPECT_THAT( |
| SamplesOf({kFills, kBefore, kAll, kBitmask}), |
| BucketsAre(Bucket(kName | kNumber | kExp, 1), Bucket(kNumber, 1))); |
| EXPECT_THAT(SamplesOf({kFills, kAfter, kAll, kBitmask}), |
| BucketsAre(Bucket(kName | kExp, 1), Bucket(kNumber, 1))); |
| EXPECT_THAT(SamplesOf({kFills, kSubmission, kAll, kBitmask}), |
| BucketsAre(Bucket(kName | kNumber | kExp, 1))); |
| // Bitmask metrics restricted to visible fields. |
| EXPECT_THAT(SamplesOf({kFillable, kBefore, kVisible, kBitmask}), |
| BucketsAre(Bucket(kName | kNumber | kExp, 2))); |
| EXPECT_THAT(SamplesOf({kFillable, kAfter, kVisible, kBitmask}), |
| BucketsAre(Bucket(kName | kExp, 1), Bucket(kNumber, 1))); |
| EXPECT_THAT( |
| SamplesOf({kFills, kBefore, kVisible, kBitmask}), |
| BucketsAre(Bucket(kName | kNumber | kExp, 1), Bucket(kNumber, 1))); |
| EXPECT_THAT(SamplesOf({kFills, kAfter, kVisible, kBitmask}), |
| BucketsAre(Bucket(kName | kExp, 1), Bucket(kNumber, 1))); |
| |
| // Qualitative metrics. |
| EXPECT_THAT(SamplesOf({kFillable, kBefore, kAll, kQualitative}), |
| BucketsAre(Bucket(Metric::kFullFill, 2))); |
| EXPECT_THAT(SamplesOf({kFillable, kAfter, kAll, kQualitative}), |
| BucketsAre(Bucket(Metric::kPartialFill, 2))); |
| EXPECT_THAT(SamplesOf({kFills, kBefore, kAll, kQualitative}), |
| BucketsAre(Bucket(Metric::kOptionalCvcMissing, 1), |
| Bucket(Metric::kPartialFill, 1))); |
| EXPECT_THAT(SamplesOf({kFills, kAfter, kAll, kQualitative}), |
| BucketsAre(Bucket(Metric::kPartialFill, 2))); |
| EXPECT_THAT(SamplesOf({kFills, kSubmission, kAll, kQualitative}), |
| BucketsAre(Bucket(Metric::kOptionalCvcMissing, 1))); |
| // Qualitative metrics restricted to visible fields. |
| EXPECT_THAT(SamplesOf({kFillable, kBefore, kVisible, kQualitative}), |
| BucketsAre(Bucket(Metric::kOptionalCvcMissing, 2))); |
| EXPECT_THAT(SamplesOf({kFillable, kAfter, kVisible, kQualitative}), |
| BucketsAre(Bucket(Metric::kPartialFill, 2))); |
| EXPECT_THAT(SamplesOf({kFills, kBefore, kVisible, kQualitative}), |
| BucketsAre(Bucket(Metric::kOptionalCvcMissing, 1), |
| Bucket(Metric::kPartialFill, 1))); |
| EXPECT_THAT(SamplesOf({kFills, kAfter, kVisible, kQualitative}), |
| BucketsAre(Bucket(Metric::kPartialFill, 2))); |
| |
| VerifyUkm( |
| &test_ukm_recorder(), form_, UkmBuilder::kEntryName, |
| {{ |
| {UkmBuilder::kFillable_BeforeSecurity_QualitativeName, kFullFill}, |
| {UkmBuilder::kFillable_AfterSecurity_QualitativeName, kPartialFill}, |
| {UkmBuilder::kFilled_BeforeSecurity_QualitativeName, |
| kOptionalCvcMissing}, |
| {UkmBuilder::kFilled_AfterSecurity_QualitativeName, kPartialFill}, |
| |
| {UkmBuilder::kFillable_BeforeSecurity_BitmaskName, |
| kName | kNumber | kExp | kCvc}, |
| {UkmBuilder::kFillable_AfterSecurity_BitmaskName, kName | kExp}, |
| {UkmBuilder::kFilled_BeforeSecurity_BitmaskName, |
| kName | kNumber | kExp}, |
| {UkmBuilder::kFilled_AfterSecurity_BitmaskName, kName | kExp}, |
| |
| {UkmBuilder::kFillable_BeforeSecurity_Visible_QualitativeName, |
| kOptionalCvcMissing}, |
| {UkmBuilder::kFillable_AfterSecurity_Visible_QualitativeName, |
| kPartialFill}, |
| {UkmBuilder::kFilled_BeforeSecurity_Visible_QualitativeName, |
| kOptionalCvcMissing}, |
| {UkmBuilder::kFilled_AfterSecurity_Visible_QualitativeName, |
| kPartialFill}, |
| |
| {UkmBuilder::kFillable_BeforeSecurity_Visible_BitmaskName, |
| kName | kNumber | kExp}, |
| {UkmBuilder::kFillable_AfterSecurity_Visible_BitmaskName, |
| kName | kExp}, |
| {UkmBuilder::kFilled_BeforeSecurity_Visible_BitmaskName, |
| kName | kNumber | kExp}, |
| {UkmBuilder::kFilled_AfterSecurity_Visible_BitmaskName, |
| kName | kExp}, |
| |
| {UkmBuilder::kSharedAutofillName, kSharedAutofillWouldHelp}, |
| |
| {UkmBuilder::kFormSignatureName, |
| *Collapse(CalculateFormSignature(form_))}, |
| }, |
| { |
| {UkmBuilder::kFillable_BeforeSecurity_QualitativeName, kFullFill}, |
| {UkmBuilder::kFillable_AfterSecurity_QualitativeName, kPartialFill}, |
| {UkmBuilder::kFilled_BeforeSecurity_QualitativeName, kPartialFill}, |
| {UkmBuilder::kFilled_AfterSecurity_QualitativeName, kPartialFill}, |
| |
| {UkmBuilder::kFillable_BeforeSecurity_BitmaskName, |
| kName | kNumber | kExp | kCvc}, |
| {UkmBuilder::kFillable_AfterSecurity_BitmaskName, kNumber | kCvc}, |
| {UkmBuilder::kFilled_BeforeSecurity_BitmaskName, kNumber}, |
| {UkmBuilder::kFilled_AfterSecurity_BitmaskName, kNumber}, |
| |
| {UkmBuilder::kFillable_BeforeSecurity_Visible_QualitativeName, |
| kOptionalCvcMissing}, |
| {UkmBuilder::kFillable_AfterSecurity_Visible_QualitativeName, |
| kPartialFill}, |
| {UkmBuilder::kFilled_BeforeSecurity_Visible_QualitativeName, |
| kPartialFill}, |
| {UkmBuilder::kFilled_AfterSecurity_Visible_QualitativeName, |
| kPartialFill}, |
| |
| {UkmBuilder::kFillable_BeforeSecurity_Visible_BitmaskName, |
| kName | kNumber | kExp}, |
| {UkmBuilder::kFillable_AfterSecurity_Visible_BitmaskName, kNumber}, |
| {UkmBuilder::kFilled_BeforeSecurity_Visible_BitmaskName, kNumber}, |
| {UkmBuilder::kFilled_AfterSecurity_Visible_BitmaskName, kNumber}, |
| |
| {UkmBuilder::kSharedAutofillName, kSharedAutofillIsIrrelevant}, |
| |
| {UkmBuilder::kFormSignatureName, |
| *Collapse(CalculateFormSignature(form_))}, |
| }}); |
| } |
| |
| // Test if we have correctly recorded the filling status of fields in an unsafe |
| // iframe. |
| TEST_F(AutofillMetricsSeamlessnessTest, CreditCardFormRecordOnIFrames) { |
| // Create a form with the credit card number and CVC code fields in an |
| // iframe with a different origin. |
| SeeForm(form_); |
| |
| // Triggering autofill from the credit card name field cannot fill the credit |
| // card number and CVC code fields, which are in an unsafe iframe. |
| FillForm(form_.fields[0]); |
| SetFormValues({CREDIT_CARD_NAME_FULL, CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR}, |
| /*is_autofilled=*/true, /*is_user_typed=*/false); |
| |
| // Triggering autofill from the credit card number field can fill all the |
| // credit card fields with values. |
| FillForm(form_.fields[1]); |
| SetFormValues({CREDIT_CARD_NUMBER, CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, |
| CREDIT_CARD_VERIFICATION_CODE}, |
| /*is_autofilled=*/true, /*is_user_typed=*/false); |
| |
| // Record Autofill2.FieldInfo UKM event at autofill manager reset. |
| SubmitForm(form_); |
| ResetDriverToCommitMetrics(); |
| |
| std::vector<FieldType> field_types = { |
| CREDIT_CARD_NAME_FULL, CREDIT_CARD_NUMBER, |
| CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR, CREDIT_CARD_VERIFICATION_CODE}; |
| |
| // Verify FieldInfo UKM event for every field. |
| auto field_entries = |
| test_ukm_recorder().GetEntriesByName(UkmFieldInfoType::kEntryName); |
| ASSERT_EQ(4u, field_entries.size()); |
| for (size_t i = 0; i < field_entries.size(); ++i) { |
| SCOPED_TRACE(testing::Message() << i); |
| using UFIT = UkmFieldInfoType; |
| const auto* const entry = field_entries[i].get(); |
| DenseSet<FieldFillingSkipReason> skipped_status_vector; |
| if (i == 0 || i == 2) { |
| skipped_status_vector = { |
| FieldFillingSkipReason::kNotSkipped, |
| FieldFillingSkipReason::kAutofilledFieldsNotRefill}; |
| } else { |
| skipped_status_vector = {FieldFillingSkipReason::kNotSkipped}; |
| } |
| DenseSet<AutofillStatus> autofill_status_vector; |
| int field_log_events_count = 0; |
| if (i == 0 || i == 2) { |
| autofill_status_vector = { |
| AutofillStatus::kIsFocusable, |
| AutofillStatus::kWasAutofillTriggered, |
| AutofillStatus::kWasAutofilledBeforeSecurityPolicy, |
| AutofillStatus::kWasRefill, |
| AutofillStatus::kHadValueBeforeFilling, |
| AutofillStatus::kHadTypedOrFilledValueAtSubmission, |
| AutofillStatus::kWasAutofilledAfterSecurityPolicy}; |
| field_log_events_count = i == 0 ? 3 : 2; |
| } else { |
| autofill_status_vector = { |
| AutofillStatus::kIsFocusable, |
| AutofillStatus::kWasAutofillTriggered, |
| AutofillStatus::kWasAutofilledBeforeSecurityPolicy, |
| AutofillStatus::kWasRefill, |
| AutofillStatus::kHadTypedOrFilledValueAtSubmission, |
| AutofillStatus::kFillingPreventedByIframeSecurityPolicy, |
| AutofillStatus::kWasAutofilledAfterSecurityPolicy}; |
| field_log_events_count = i == 1 ? 3 : 2; |
| } |
| std::map<std::string, int64_t> expected = { |
| {UFIT::kFormSessionIdentifierName, |
| AutofillMetrics::FormGlobalIdToHash64Bit(form_.global_id())}, |
| {UFIT::kFieldSessionIdentifierName, |
| AutofillMetrics::FieldGlobalIdToHash64Bit( |
| form_.fields[i].global_id())}, |
| {UFIT::kFieldSignatureName, |
| Collapse(CalculateFieldSignatureForField(form_.fields[i])).value()}, |
| {UFIT::kAutofillSkippedStatusName, skipped_status_vector.data()[0]}, |
| {UFIT::kFormControlType2Name, |
| base::to_underlying(FormControlType::kInputText)}, |
| {UFIT::kAutocompleteStateName, |
| base::to_underlying(AutofillMetrics::AutocompleteState::kNone)}, |
| {UFIT::kAutofillStatusVectorName, autofill_status_vector.data()[0]}, |
| {UFIT::kOverallTypeName, field_types[i]}, |
| {UFIT::kSectionIdName, 1}, |
| {UFIT::kTypeChangedByRationalizationName, false}, |
| {UFIT::kRankInFieldSignatureGroupName, 1}, |
| {UFIT::kHeuristicTypeName, field_types[i]}, |
| #if BUILDFLAG(USE_INTERNAL_AUTOFILL_PATTERNS) |
| {UFIT::kHeuristicTypeLegacyName, UNKNOWN_TYPE}, |
| {UFIT::kHeuristicTypeDefaultName, field_types[i]}, |
| {UFIT::kHeuristicTypeExperimentalName, field_types[i]}, |
| {UFIT::kHeuristicTypeNextGenName, UNKNOWN_TYPE}, |
| {UFIT::kFieldLogEventCountName, field_log_events_count + 3}, |
| #else |
| {UFIT::kHeuristicTypeLegacyName, field_types[i]}, |
| {UFIT::kHeuristicTypeDefaultName, UNKNOWN_TYPE}, |
| {UFIT::kHeuristicTypeExperimentalName, UNKNOWN_TYPE}, |
| {UFIT::kHeuristicTypeNextGenName, UNKNOWN_TYPE}, |
| {UFIT::kFieldLogEventCountName, field_log_events_count + 2}, |
| #endif |
| }; |
| EXPECT_EQ(expected.size(), entry->metrics.size()); |
| for (const auto& [metric, value] : expected) { |
| test_ukm_recorder().ExpectEntryMetric(entry, metric, value); |
| } |
| } |
| } |
| |
| // Test the field log events at the form submission. |
| class AutofillMetricsFromLogEventsTest : public AutofillMetricsTest { |
| protected: |
| AutofillMetricsFromLogEventsTest() { |
| base::FieldTrialParams feature_parameters{ |
| {features::kAutofillLogUKMEventsWithSamplingOnSessionRate.name, "100"}, |
| }; |
| scoped_features_.InitWithFeaturesAndParameters( |
| /*enabled_features=*/{{features:: |
| kAutofillLogUKMEventsWithSamplingOnSession, |
| feature_parameters}, |
| {features::kAutofillParsingPatternProvider, {}}}, |
| /*disabled_features=*/{}); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_features_; |
| }; |
| |
| // Test if we record FieldInfo UKM event correctly after we click the field and |
| // show autofill suggestions. |
| TEST_F(AutofillMetricsFromLogEventsTest, TestShowSuggestionAutofillStatus) { |
| RecreateProfile(); |
| FormData form = test::GetFormData({.fields = { |
| {.label = u"State", .name = u"state"}, |
| {.label = u"Street"}, |
| {.label = u"Number"}, |
| }}); |
| |
| std::vector<FieldType> field_types = {ADDRESS_HOME_STATE, NO_SERVER_DATA, |
| NO_SERVER_DATA}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Show autofill suggestions. |
| autofill_manager().OnAskForValuesToFillTest( |
| form, form.fields[0], gfx::RectF(), |
| AutofillSuggestionTriggerSource::kFormControlElementClicked); |
| |
| task_environment_.FastForwardBy(base::Milliseconds(9)); |
| base::HistogramTester histogram_tester; |
| SubmitForm(form); |
| |
| // Record Autofill2.FieldInfo UKM event at autofill manager reset. |
| autofill_manager().Reset(); |
| |
| // Verify FieldInfo UKM event for every field. |
| auto field_entries = |
| test_ukm_recorder().GetEntriesByName(UkmFieldInfoType::kEntryName); |
| ASSERT_EQ(1u, field_entries.size()); |
| for (size_t i = 0; i < field_entries.size(); ++i) { |
| SCOPED_TRACE(testing::Message() << i); |
| using UFIT = UkmFieldInfoType; |
| const auto* const entry = field_entries[i].get(); |
| |
| DenseSet<AutofillStatus> autofill_status_vector = { |
| AutofillStatus::kIsFocusable, AutofillStatus::kWasFocused, |
| AutofillStatus::kSuggestionWasAvailable, |
| AutofillStatus::kSuggestionWasShown}; |
| std::map<std::string, int64_t> expected = { |
| {UFIT::kFormSessionIdentifierName, |
| AutofillMetrics::FormGlobalIdToHash64Bit(form.global_id())}, |
| {UFIT::kFieldSessionIdentifierName, |
| AutofillMetrics::FieldGlobalIdToHash64Bit( |
| form.fields[i].global_id())}, |
| {UFIT::kFieldSignatureName, |
| Collapse(CalculateFieldSignatureForField(form.fields[i])).value()}, |
| {UFIT::kFormControlType2Name, |
| base::to_underlying(FormControlType::kInputText)}, |
| {UFIT::kAutocompleteStateName, |
| base::to_underlying(AutofillMetrics::AutocompleteState::kNone)}, |
| {UFIT::kAutofillStatusVectorName, autofill_status_vector.data()[0]}, |
| {UFIT::kFieldLogEventCountName, 1}, |
| }; |
| |
| EXPECT_EQ(expected.size(), entry->metrics.size()); |
| for (const auto& [metric, value] : expected) { |
| test_ukm_recorder().ExpectEntryMetric(entry, metric, value); |
| } |
| } |
| } |
| } |
| |
| // Test if we record FieldInfo UKM metrics correctly after we fill and submit an |
| // address form. |
| TEST_F(AutofillMetricsFromLogEventsTest, AddressSubmittedFormLogEvents) { |
| RecreateProfile(); |
| FormData form = test::GetFormData({.fields = { |
| {.label = u"State", .name = u"state"}, |
| {.label = u"Street"}, |
| {.label = u"Number"}, |
| }}); |
| |
| std::vector<FieldType> field_types = { |
| ADDRESS_HOME_STATE, ADDRESS_HOME_STREET_ADDRESS, NO_SERVER_DATA}; |
| |
| autofill_manager().AddSeenForm(form, field_types); |
| |
| { |
| // Simulating submission with filled local data. The third field cannot be |
| // autofilled because its type cannot be predicted. |
| autofill_manager().OnAskForValuesToFillTest( |
| form, form.fields[0], gfx::RectF(), |
| AutofillSuggestionTriggerSource::kFormControlElementClicked); |
| FillTestProfile(form); |
| |
| base::TimeTicks parse_time = autofill_manager() |
| .form_structures() |
| .begin() |
| ->second->form_parsed_timestamp(); |
| // Simulate text input in the first fields. |
| SimulateUserChangedTextFieldTo(form, form.fields[0], u"United States", |
| parse_time + base::Milliseconds(3)); |
| task_environment_.FastForwardBy(base::Milliseconds(9)); |
| base::HistogramTester histogram_tester; |
| SubmitForm(form); |
| |
| // Record Autofill2.FieldInfo UKM event at autofill manager reset. |
| autofill_manager().Reset(); |
| |
| // Verify FieldInfo UKM event for every field. |
| auto field_entries = |
| test_ukm_recorder().GetEntriesByName(UkmFieldInfoType::kEntryName); |
| ASSERT_EQ(3u, field_entries.size()); |
| for (size_t i = 0; i < field_entries.size(); ++i) { |
| SCOPED_TRACE(testing::Message() << i); |
| using UFIT = UkmFieldInfoType; |
| const auto* const entry = field_entries[i].get(); |
| |
| FieldFillingSkipReason status = |
| i == 2 ? FieldFillingSkipReason::kNoFillableGroup |
| : FieldFillingSkipReason::kNotSkipped; |
| DenseSet<AutofillStatus> autofill_status_vector; |
| int field_log_events_count = 0; |
| if (i == 0) { |
| autofill_status_vector = { |
| AutofillStatus::kIsFocusable, |
| AutofillStatus::kWasFocused, |
| AutofillStatus::kWasAutofillTriggered, |
| AutofillStatus::kWasAutofilledBeforeSecurityPolicy, |
| AutofillStatus::kSuggestionWasAvailable, |
| AutofillStatus::kSuggestionWasShown, |
| AutofillStatus::kSuggestionWasAccepted, |
| AutofillStatus::kUserTypedIntoField, |
| AutofillStatus::kFilledValueWasModified, |
| AutofillStatus::kHadTypedOrFilledValueAtSubmission, |
| AutofillStatus::kWasAutofilledAfterSecurityPolicy}; |
| field_log_events_count = 4; |
| } else if (i == 1) { |
| autofill_status_vector = { |
| AutofillStatus::kIsFocusable, AutofillStatus::kWasAutofillTriggered, |
| AutofillStatus::kWasAutofilledBeforeSecurityPolicy, |
| AutofillStatus::kHadTypedOrFilledValueAtSubmission, |
| AutofillStatus::kWasAutofilledAfterSecurityPolicy}; |
| field_log_events_count = 1; |
| } else if (i == 2) { |
| autofill_status_vector = {AutofillStatus::kIsFocusable, |
| AutofillStatus::kWasAutofillTriggered}; |
| field_log_events_count = 1; |
| } |
| std::map<std::string, int64_t> expected = { |
| {UFIT::kFormSessionIdentifierName, |
| AutofillMetrics::FormGlobalIdToHash64Bit(form.global_id())}, |
| {UFIT::kFieldSessionIdentifierName, |
| AutofillMetrics::FieldGlobalIdToHash64Bit( |
| form.fields[i].global_id())}, |
| {UFIT::kFieldSignatureName, |
| Collapse(CalculateFieldSignatureForField(form.fields[i])).value()}, |
| {UFIT::kAutofillSkippedStatusName, |
| DenseSet<FieldFillingSkipReason>{status}.data()[0]}, |
| {UFIT::kFormControlType2Name, |
| base::to_underlying(FormControlType::kInputText)}, |
| {UFIT::kAutocompleteStateName, |
| base::to_underlying(AutofillMetrics::AutocompleteState::kNone)}, |
| {UFIT::kAutofillStatusVectorName, autofill_status_vector.data()[0]}, |
| {UFIT::kFieldLogEventCountName, field_log_events_count}, |
| }; |
| EXPECT_EQ(expected.size(), entry->metrics.size()); |
| for (const auto& [metric, value] : expected) { |
| test_ukm_recorder().ExpectEntryMetric(entry, metric, value); |
| } |
| } |
| |
| // Verify FieldInfoAfterSubmission UKM event for each field in the form. |
| auto submission_entries = test_ukm_recorder().GetEntriesByName( |
| UkmFieldInfoAfterSubmissionType::kEntryName); |
| // Form submission and user interaction trigger uploading votes twice. |
| ASSERT_EQ(6u, submission_entries.size()); |
| for (size_t i = 0; i < submission_entries.size(); ++i) { |
| SCOPED_TRACE(testing::Message() << i); |
| using UFIAST = UkmFieldInfoAfterSubmissionType; |
| const auto* const entry = submission_entries[i].get(); |
| FieldType submitted_type1 = |
| i % 3 == 0 ? ADDRESS_HOME_COUNTRY : EMPTY_TYPE; |
| |
| // TODO(crbug.com/40225658): Check that the second vote submission (with |
| // SubmissionSource::NONE) is always identical with the first one (it's |
| // possible that only the SubmissionSource::NONE exists). If we always |
| // get the same values, we should modify |
| // BrowserAutofillManager::OnFormSubmittedImpl to only send one vote |
| // submission. |
| SubmissionSource submission_source = |
| i < 3 ? SubmissionSource::FORM_SUBMISSION : SubmissionSource::NONE; |
| std::map<std::string, int64_t> expected = { |
| {UFIAST::kFormSessionIdentifierName, |
| AutofillMetrics::FormGlobalIdToHash64Bit(form.global_id())}, |
| {UFIAST::kFieldSessionIdentifierName, |
| AutofillMetrics::FieldGlobalIdToHash64Bit( |
| form.fields[i % 3].global_id())}, |
| {UFIAST::kSubmittedType1Name, submitted_type1}, |
| {UFIAST::kSubmissionSourceName, static_cast<int>(submission_source)}, |
| {UFIAST::kMillisecondsFromFormParsedUntilSubmissionName, 9}, |
| }; |
| EXPECT_EQ(expected.size(), entry->metrics.size()); |
| for (const auto& [metric, value] : expected) { |
| test_ukm_recorder().ExpectEntryMetric(entry, metric, value); |
| } |
| } |
| |
| // Verify FormSummary UKM event for the form. |
| auto form_entries = |
| test_ukm_recorder().GetEntriesByName(UkmFormSummaryType::kEntryName); |
| ASSERT_EQ(1u, form_entries.size()); |
| using UFST = UkmFormSummaryType; |
| const auto* const entry = form_entries[0].get(); |
| AutofillMetrics::FormEventSet form_events = { |
| FORM_EVENT_INTERACTED_ONCE, FORM_EVENT_LOCAL_SUGGESTION_FILLED, |
| FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE, |
| FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, |
| FORM_EVENT_LOCAL_SUGGESTION_WILL_SUBMIT_ONCE}; |
| std::map<std::string, int64_t> expected = { |
| {UFST::kFormSessionIdentifierName, |
| AutofillMetrics::FormGlobalIdToHash64Bit(form.global_id())}, |
| {UFST::kFormSignatureName, |
| Collapse(CalculateFormSignature(form)).value()}, |
| {UFST::kAutofillFormEventsName, form_events.data()[0]}, |
| {UFST::kAutofillFormEvents2Name, form_events.data()[1]}, |
| {UFST::kSampleRateName, 1}, |
| {UFST::kWasSubmittedName, true}, |
| {UFST::kMillisecondsFromFirstInteratctionUntilSubmissionName, 6}, |
| {UFST::kMillisecondsFromFormParsedUntilSubmissionName, 9}, |
| }; |
| EXPECT_EQ(expected.size(), entry->metrics.size()); |
| for (const auto& [metric, value] : expected) { |
| test_ukm_recorder().ExpectEntryMetric(entry, metric, value); |
| } |
| |
| // Verify LogEvent count UMA events of each type. |
| histogram_tester.ExpectBucketCount( |
| "Autofill.LogEvent.AskForValuesToFillEvent", 1, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.TriggerFillEvent", 1, |
| 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.FillEvent", 3, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.TypingEvent", 1, 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.LogEvent.HeuristicPredictionEvent", 0, 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.LogEvent.AutocompleteAttributeEvent", 0, 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.LogEvent.ServerPredictionEvent", 0, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.RationalizationEvent", |
| 0, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.All", 6, 1); |
| } |
| } |
| |
| // Test if we have recorded UKM metrics correctly about field types after |
| // parsing the form by the local heuristic prediction. |
| TEST_F(AutofillMetricsFromLogEventsTest, AutofillFieldInfoMetricsFieldType) { |
| FormData form = test::GetFormData( |
| {.fields = { |
| // Heuristic value will match with Autocomplete attribute. |
| {.label = u"Last Name", |
| .name = u"lastname", |
| .autocomplete_attribute = "family-name"}, |
| // Heuristic value will NOT match with Autocomplete attribute. |
| {.label = u"First Name", |
| .name = u"firstname", |
| .autocomplete_attribute = "additional-name"}, |
| // No autocomplete attribute. |
| {.label = u"Address", |
| .name = u"address", |
| .autocomplete_attribute = "off"}, |
| // Heuristic value will be unknown. |
| {.label = u"Garbage label", |
| .name = u"garbage", |
| .autocomplete_attribute = "postal-code"}, |
| {.label = u"Email", |
| .name = u"email", |
| .autocomplete_attribute = "garbage"}, |
| {.label = u"Password", |
| .name = u"password", |
| .autocomplete_attribute = "new-password"}, |
| }}); |
| |
| auto form_structure = std::make_unique<FormStructure>(form); |
| FormStructure* form_structure_ptr = form_structure.get(); |
| form_structure->DetermineHeuristicTypes(GeoIpCountryCode(""), nullptr, |
| nullptr); |
| ASSERT_TRUE( |
| test_api(autofill_manager()) |
| .mutable_form_structures() |
| ->emplace(form_structure_ptr->global_id(), std::move(form_structure)) |
| .second); |
| |
| AutofillQueryResponse response; |
| auto* form_suggestion = response.add_form_suggestions(); |
| // The server type of each field predicted from autofill crowdsourced server. |
| std::vector<FieldType> server_types{ |
| // Server response will match with autocomplete. |
| NAME_LAST, |
| // Server response will NOT match with autocomplete. |
| NAME_FIRST, |
| // No autocomplete, server predicts a type from majority voting. |
| NAME_MIDDLE, |
| // Server response will have no data. |
| NO_SERVER_DATA, EMAIL_ADDRESS, NO_SERVER_DATA}; |
| // Set suggestions from server for the form. |
| for (size_t i = 0; i < server_types.size(); ++i) { |
| AddFieldPredictionToForm(form.fields[i], server_types[i], form_suggestion); |
| } |
| |
| std::string response_string = SerializeAndEncode(response); |
| test_api(autofill_manager()) |
| .OnLoadedServerPredictions( |
| response_string, test::GetEncodedSignatures(*form_structure_ptr)); |
| |
| task_environment_.FastForwardBy(base::Milliseconds(17)); |
| base::HistogramTester histogram_tester; |
| SubmitForm(form); |
| // Record Autofill2.FieldInfo UKM event at autofill manager reset. |
| autofill_manager().Reset(); |
| |
| auto entries = |
| test_ukm_recorder().GetEntriesByName(UkmFieldInfoType::kEntryName); |
| ASSERT_EQ(6u, entries.size()); |
| // The heuristic type of each field. The local heuristic prediction does not |
| // predict the type for the fourth field. |
| std::vector<FieldType> heuristic_types{NAME_LAST, NAME_FIRST, |
| ADDRESS_HOME_LINE1, UNKNOWN_TYPE, |
| EMAIL_ADDRESS, UNKNOWN_TYPE}; |
| // Field types as per the autocomplete attribute in the input. |
| std::vector<HtmlFieldType> html_field_types{ |
| HtmlFieldType::kFamilyName, HtmlFieldType::kAdditionalName, |
| HtmlFieldType::kUnrecognized, HtmlFieldType::kPostalCode, |
| HtmlFieldType::kUnrecognized, HtmlFieldType::kUnrecognized}; |
| std::vector<FieldType> overall_types{NAME_LAST, NAME_MIDDLE, |
| NAME_MIDDLE, ADDRESS_HOME_ZIP, |
| EMAIL_ADDRESS, UNKNOWN_TYPE}; |
| std::vector<AutofillMetrics::AutocompleteState> autocomplete_states{ |
| AutofillMetrics::AutocompleteState::kValid, |
| AutofillMetrics::AutocompleteState::kValid, |
| AutofillMetrics::AutocompleteState::kOff, |
| AutofillMetrics::AutocompleteState::kValid, |
| AutofillMetrics::AutocompleteState::kGarbage, |
| AutofillMetrics::AutocompleteState::kPassword}; |
| int field_log_events_count = 0; |
| // Verify FieldInfo UKM event for every field. |
| for (size_t i = 0; i < entries.size(); ++i) { |
| SCOPED_TRACE(testing::Message() << i); |
| using UFIT = UkmFieldInfoType; |
| const auto* const entry = entries[i].get(); |
| FieldPrediction::Source prediction_source = |
| server_types[i] != NO_SERVER_DATA |
| ? FieldPrediction::SOURCE_AUTOFILL_DEFAULT |
| : FieldPrediction::SOURCE_UNSPECIFIED; |
| DenseSet<AutofillStatus> autofill_status_vector = { |
| AutofillStatus::kIsFocusable}; |
| field_log_events_count = 2; |
| std::map<std::string, int64_t> expected = { |
| {UFIT::kFormSessionIdentifierName, |
| AutofillMetrics::FormGlobalIdToHash64Bit(form.global_id())}, |
| {UFIT::kFieldSessionIdentifierName, |
| AutofillMetrics::FieldGlobalIdToHash64Bit(form.fields[i].global_id())}, |
| {UFIT::kFieldSignatureName, |
| Collapse(CalculateFieldSignatureForField(form.fields[i])).value()}, |
| {UFIT::kServerType1Name, server_types[i]}, |
| {UFIT::kServerPredictionSource1Name, prediction_source}, |
| {UFIT::kServerType2Name, /*SERVER_RESPONSE_PENDING*/ 161}, |
| {UFIT::kServerPredictionSource2Name, |
| FieldPrediction::SOURCE_UNSPECIFIED}, |
| {UFIT::kServerTypeIsOverrideName, false}, |
| {UFIT::kOverallTypeName, overall_types[i]}, |
| {UFIT::kSectionIdName, 1}, |
| {UFIT::kTypeChangedByRationalizationName, false}, |
| {UFIT::kRankInFieldSignatureGroupName, 1}, |
| {UFIT::kFormControlType2Name, |
| base::to_underlying(FormControlType::kInputText)}, |
| {UFIT::kAutocompleteStateName, |
| base::to_underlying(autocomplete_states[i])}, |
| {UFIT::kAutofillStatusVectorName, autofill_status_vector.data()[0]}, |
| }; |
| if (heuristic_types[i] != UNKNOWN_TYPE) { |
| expected.merge(std::map<std::string, int64_t>({ |
| {UFIT::kHeuristicTypeName, heuristic_types[i]}, |
| #if BUILDFLAG(USE_INTERNAL_AUTOFILL_PATTERNS) |
| {UFIT::kHeuristicTypeLegacyName, UNKNOWN_TYPE}, |
| {UFIT::kHeuristicTypeDefaultName, heuristic_types[i]}, |
| {UFIT::kHeuristicTypeExperimentalName, heuristic_types[i]}, |
| {UFIT::kHeuristicTypeNextGenName, UNKNOWN_TYPE}, |
| })); |
| field_log_events_count += 3; |
| #else |
| {UFIT::kHeuristicTypeLegacyName, heuristic_types[i]}, |
| {UFIT::kHeuristicTypeDefaultName, UNKNOWN_TYPE}, |
| {UFIT::kHeuristicTypeExperimentalName, UNKNOWN_TYPE}, |
| {UFIT::kHeuristicTypeNextGenName, UNKNOWN_TYPE}, |
| })); |
| field_log_events_count += 2; |
| #endif |
| } else { |
| ++field_log_events_count; |
| } |
| if (autocomplete_states[i] != AutofillMetrics::AutocompleteState::kOff) { |
| expected.merge(std::map<std::string, int64_t>({ |
| {UFIT::kHtmlFieldTypeName, base::to_underlying(html_field_types[i])}, |
| {UFIT::kHtmlFieldModeName, base::to_underlying(HtmlFieldMode::kNone)}, |
| })); |
| ++field_log_events_count; |
| } |
| expected.merge(std::map<std::string, int64_t>({ |
| {UFIT::kFieldLogEventCountName, field_log_events_count}, |
| })); |
| EXPECT_EQ(expected.size(), entry->metrics.size()); |
| for (const auto& [metric, value] : expected) { |
| test_ukm_recorder().ExpectEntryMetric(entry, metric, value); |
| } |
| } |
| |
| // Verify FieldInfoAfterSubmission UKM event for each field in the form. |
| auto submission_entries = test_ukm_recorder().GetEntriesByName( |
| UkmFieldInfoAfterSubmissionType::kEntryName); |
| // Form submission triggers uploading votes once. |
| ASSERT_EQ(6u, submission_entries.size()); |
| for (size_t i = 0; i < submission_entries.size(); ++i) { |
| SCOPED_TRACE(testing::Message() << i); |
| using UFIAST = UkmFieldInfoAfterSubmissionType; |
| const auto* const entry = submission_entries[i].get(); |
| std::map<std::string, int64_t> expected = { |
| {UFIAST::kFormSessionIdentifierName, |
| AutofillMetrics::FormGlobalIdToHash64Bit(form.global_id())}, |
| {UFIAST::kFieldSessionIdentifierName, |
| AutofillMetrics::FieldGlobalIdToHash64Bit(form.fields[i].global_id())}, |
| {UFIAST::kSubmittedType1Name, EMPTY_TYPE}, |
| {UFIAST::kSubmissionSourceName, |
| static_cast<int>(SubmissionSource::FORM_SUBMISSION)}, |
| {UFIAST::kMillisecondsFromFormParsedUntilSubmissionName, 10}, |
| }; |
| EXPECT_EQ(expected.size(), entry->metrics.size()); |
| for (const auto& [metric, value] : expected) { |
| if (metric == UFIAST::kMillisecondsFromFormParsedUntilSubmissionName) { |
| test_ukm_recorder().ExpectEntryMetric(entry, metric, value); |
| } |
| } |
| } |
| |
| // Verify FormSummary UKM event for the form. |
| auto form_entries = |
| test_ukm_recorder().GetEntriesByName(UkmFormSummaryType::kEntryName); |
| ASSERT_EQ(1u, form_entries.size()); |
| using UFST = UkmFormSummaryType; |
| const auto* const entry = form_entries[0].get(); |
| AutofillMetrics::FormEventSet form_events = {}; |
| std::map<std::string, int64_t> expected = { |
| {UFST::kFormSessionIdentifierName, |
| AutofillMetrics::FormGlobalIdToHash64Bit(form.global_id())}, |
| {UFST::kFormSignatureName, |
| Collapse(CalculateFormSignature(form)).value()}, |
| {UFST::kAutofillFormEventsName, form_events.data()[0]}, |
| {UFST::kAutofillFormEvents2Name, form_events.data()[1]}, |
| {UFST::kSampleRateName, 1}, |
| {UFST::kWasSubmittedName, true}, |
| {UFST::kMillisecondsFromFormParsedUntilSubmissionName, 10}, |
| }; |
| EXPECT_EQ(expected.size(), entry->metrics.size()); |
| for (const auto& [metric, value] : expected) { |
| test_ukm_recorder().ExpectEntryMetric(entry, metric, value); |
| } |
| |
| // Verify LogEvent count UMA events of each type. |
| histogram_tester.ExpectBucketCount( |
| "Autofill.LogEvent.AskForValuesToFillEvent", 0, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.TriggerFillEvent", 0, |
| 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.FillEvent", 0, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.TypingEvent", 0, 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.LogEvent.AutocompleteAttributeEvent", 5, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.ServerPredictionEvent", |
| 6, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.RationalizationEvent", |
| 12, 1); |
| #if BUILDFLAG(USE_INTERNAL_AUTOFILL_PATTERNS) |
| histogram_tester.ExpectBucketCount( |
| "Autofill.LogEvent.HeuristicPredictionEvent", 8, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.All", 29, 1); |
| #else |
| histogram_tester.ExpectBucketCount( |
| "Autofill.LogEvent.HeuristicPredictionEvent", 4, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.All", 27, 1); |
| #endif |
| } |
| |
| // Test if we have recorded FieldInfo UKM metrics correctly after typing in |
| // fields without autofilling first. |
| TEST_F(AutofillMetricsFromLogEventsTest, |
| AutofillFieldInfoMetricsEditedFieldWithoutFill) { |
| test::FormDescription form_description = { |
| .fields = {{.role = NAME_FULL}, |
| {.role = EMAIL_ADDRESS}, |
| {.role = PHONE_HOME_CITY_AND_NUMBER}}}; |
| |
| FormData form = GetAndAddSeenForm(form_description); |
| |
| base::TimeTicks parse_time = autofill_manager() |
| .form_structures() |
| .begin() |
| ->second->form_parsed_timestamp(); |
| // Simulate text input in the first and second fields. |
| SimulateUserChangedTextFieldTo(form, form.fields[0], u"Elvis Aaron Presley", |
| parse_time + base::Milliseconds(3)); |
| SimulateUserChangedTextFieldTo(form, form.fields[1], u"buddy@gmail.com", |
| parse_time + base::Milliseconds(3)); |
| task_environment_.FastForwardBy(base::Milliseconds(9)); |
| base::HistogramTester histogram_tester; |
| SubmitForm(form); |
| |
| // Record Autofill2.FieldInfo UKM event at autofill manager reset. |
| autofill_manager().Reset(); |
| |
| // Verify FieldInfo UKM event for every field. |
| auto entries = |
| test_ukm_recorder().GetEntriesByName(UkmFieldInfoType::kEntryName); |
| ASSERT_EQ(2u, entries.size()); |
| for (size_t i = 0; i < entries.size(); ++i) { |
| SCOPED_TRACE(testing::Message() << i); |
| DenseSet<AutofillStatus> autofill_status_vector = { |
| AutofillStatus::kIsFocusable, AutofillStatus::kUserTypedIntoField, |
| AutofillStatus::kHadTypedOrFilledValueAtSubmission}; |
| using UFIT = UkmFieldInfoType; |
| const auto* const entry = entries[i].get(); |
| std::map<std::string, int64_t> expected = { |
| {UFIT::kFormSessionIdentifierName, |
| AutofillMetrics::FormGlobalIdToHash64Bit(form.global_id())}, |
| {UFIT::kFieldSessionIdentifierName, |
| AutofillMetrics::FieldGlobalIdToHash64Bit(form.fields[i].global_id())}, |
| {UFIT::kFieldSignatureName, |
| Collapse(CalculateFieldSignatureForField(form.fields[i])).value()}, |
| {UFIT::kFormControlType2Name, |
| base::to_underlying(FormControlType::kInputText)}, |
| {UFIT::kAutocompleteStateName, |
| base::to_underlying(AutofillMetrics::AutocompleteState::kNone)}, |
| {UFIT::kAutofillStatusVectorName, autofill_status_vector.data()[0]}, |
| {UFIT::kFieldLogEventCountName, 1}, |
| }; |
| |
| EXPECT_EQ(expected.size(), entry->metrics.size()); |
| for (const auto& [metric, value] : expected) { |
| test_ukm_recorder().ExpectEntryMetric(entry, metric, value); |
| } |
| } |
| |
| // Verify FieldInfoAfterSubmission UKM event for each field in the form. |
| std::vector<FieldType> submitted_types{NAME_FULL, EMAIL_ADDRESS, EMPTY_TYPE}; |
| auto submission_entries = test_ukm_recorder().GetEntriesByName( |
| UkmFieldInfoAfterSubmissionType::kEntryName); |
| // Form submission and user interaction trigger uploading votes twice. |
| ASSERT_EQ(6u, submission_entries.size()); |
| for (size_t i = 0; i < submission_entries.size(); ++i) { |
| SCOPED_TRACE(testing::Message() << i); |
| using UFIAST = UkmFieldInfoAfterSubmissionType; |
| const auto* const entry = submission_entries[i].get(); |
| SubmissionSource submission_source = |
| i < 3 ? SubmissionSource::FORM_SUBMISSION : SubmissionSource::NONE; |
| std::map<std::string, int64_t> expected = { |
| {UFIAST::kFormSessionIdentifierName, |
| AutofillMetrics::FormGlobalIdToHash64Bit(form.global_id())}, |
| {UFIAST::kFieldSessionIdentifierName, |
| AutofillMetrics::FieldGlobalIdToHash64Bit( |
| form.fields[i % 3].global_id())}, |
| {UFIAST::kSubmittedType1Name, submitted_types[i % 3]}, |
| {UFIAST::kSubmissionSourceName, static_cast<int>(submission_source)}, |
| {UFIAST::kMillisecondsFromFormParsedUntilSubmissionName, 9}, |
| }; |
| EXPECT_EQ(expected.size(), entry->metrics.size()); |
| for (const auto& [metric, value] : expected) { |
| if (metric == UFIAST::kMillisecondsFromFormParsedUntilSubmissionName) { |
| test_ukm_recorder().ExpectEntryMetric(entry, metric, value); |
| } |
| } |
| } |
| |
| // Verify FormSummary UKM event for the form. |
| auto form_entries = |
| test_ukm_recorder().GetEntriesByName(UkmFormSummaryType::kEntryName); |
| ASSERT_EQ(1u, form_entries.size()); |
| using UFST = UkmFormSummaryType; |
| const auto* const entry = form_entries[0].get(); |
| AutofillMetrics::FormEventSet form_events = {}; |
| std::map<std::string, int64_t> expected = { |
| {UFST::kFormSessionIdentifierName, |
| AutofillMetrics::FormGlobalIdToHash64Bit(form.global_id())}, |
| {UFST::kFormSignatureName, |
| Collapse(CalculateFormSignature(form)).value()}, |
| {UFST::kAutofillFormEventsName, form_events.data()[0]}, |
| {UFST::kAutofillFormEvents2Name, form_events.data()[1]}, |
| {UFST::kSampleRateName, 1}, |
| {UFST::kWasSubmittedName, true}, |
| {UFST::kMillisecondsFromFirstInteratctionUntilSubmissionName, 6}, |
| {UFST::kMillisecondsFromFormParsedUntilSubmissionName, 9}, |
| }; |
| EXPECT_EQ(expected.size(), entry->metrics.size()); |
| for (const auto& [metric, value] : expected) { |
| test_ukm_recorder().ExpectEntryMetric(entry, metric, value); |
| } |
| |
| // Verify LogEvent count UMA events of each type. |
| histogram_tester.ExpectBucketCount( |
| "Autofill.LogEvent.AskForValuesToFillEvent", 0, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.TriggerFillEvent", 0, |
| 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.FillEvent", 0, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.TypingEvent", 2, 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.LogEvent.HeuristicPredictionEvent", 0, 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.LogEvent.AutocompleteAttributeEvent", 0, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.ServerPredictionEvent", |
| 0, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.RationalizationEvent", |
| 0, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.All", 2, 1); |
| } |
| |
| // Test that we do not record FieldInfo/FormSummary UKM metrics for forms |
| // whose action is a search URL. We do this to reduce the number of useless UKM |
| // events. |
| TEST_F(AutofillMetricsFromLogEventsTest, |
| AutofillFieldInfoMetricsNotRecordOnSearchURLForm) { |
| FormData form = CreateForm( |
| {CreateTestFormField("input", "", "", FormControlType::kInputText)}); |
| // Form whose action is a search URL should not be parsed. |
| form.action = GURL("http://google.com/search?q=hello"); |
| |
| SeeForm(form); |
| SubmitForm(form); |
| |
| autofill_manager().Reset(); |
| |
| // This form is not parsed in |AutofillManager::OnFormsSeen|. |
| auto entries = |
| test_ukm_recorder().GetEntriesByName(UkmFieldInfoType::kEntryName); |
| EXPECT_EQ(0u, entries.size()); |
| auto form_entries = |
| test_ukm_recorder().GetEntriesByName(UkmFormSummaryType::kEntryName); |
| EXPECT_EQ(0u, form_entries.size()); |
| } |
| |
| // Test that we do not record FieldInfo/FormSummary UKM metrics for forms |
| // that have one search box. We do this to reduce the number of useless UKM |
| // events. |
| TEST_F(AutofillMetricsFromLogEventsTest, |
| AutofillFieldInfoMetricsNotRecordOnSearchBox) { |
| FormData form = CreateForm({CreateTestFormField( |
| "Search", "Search", "", FormControlType::kInputText)}); |
| // The form only has one field which has a placeholder of 'Search'. |
| form.fields[0].set_placeholder(u"Search"); |
| |
| SeeForm(form); |
| SubmitForm(form); |
| |
| autofill_manager().Reset(); |
| |
| // The form that only has a search box is not recorded into any UKM events. |
| auto entries = |
| test_ukm_recorder().GetEntriesByName(UkmFieldInfoType::kEntryName); |
| EXPECT_EQ(0u, entries.size()); |
| auto form_entries = |
| test_ukm_recorder().GetEntriesByName(UkmFormSummaryType::kEntryName); |
| EXPECT_EQ(0u, form_entries.size()); |
| } |
| |
| // Tests that the forms with only <input type="checkbox"> fields are not |
| // recorded in FieldInfo metrics. We do this to reduce bandwidth. |
| TEST_F(AutofillMetricsFromLogEventsTest, |
| AutofillFieldInfoMetricsNotRecordOnAllCheckBox) { |
| // Two checkable checkboxes. |
| FormData form = test::GetFormData( |
| {.fields = { |
| {.label = u"Option 1", |
| .name = u"Option 1", |
| .form_control_type = FormControlType::kInputCheckbox}, |
| {.label = u"Option 2", |
| .name = u"Option 2", |
| .form_control_type = FormControlType::kInputCheckbox}, |
| }}); |
| |
| SeeForm(form); |
| SubmitForm(form); |
| autofill_manager().Reset(); |
| |
| // The form with two checkboxes is not recorded into any UKM events. |
| auto entries = |
| test_ukm_recorder().GetEntriesByName(UkmFieldInfoType::kEntryName); |
| EXPECT_EQ(0u, entries.size()); |
| auto form_entries = |
| test_ukm_recorder().GetEntriesByName(UkmFormSummaryType::kEntryName); |
| EXPECT_EQ(0u, form_entries.size()); |
| } |
| |
| // Tests that the forms with <input type="checkbox"> fields and a text field |
| // which does not get a type from heuristics or the server are not recorded in |
| // UkmFieldInfo metrics. |
| TEST_F( |
| AutofillMetricsFromLogEventsTest, |
| AutofillFieldInfoMetricsNotRecordOnCheckBoxWithTextFieldWithUnknownType) { |
| FormData form = test::GetFormData( |
| {.fields = { |
| // Start with a username field. |
| {.label = u"username", .name = u"username"}, |
| // Two checkable radio buttons. |
| {.label = u"female", |
| .name = u"female", |
| .form_control_type = FormControlType::kInputRadio}, |
| {.label = u"male", |
| .name = u"male", |
| .form_control_type = FormControlType::kInputRadio}, |
| // One checkable checkbox. |
| {.label = u"save", |
| .name = u"save", |
| .form_control_type = FormControlType::kInputCheckbox}, |
| }}); |
| |
| SeeForm(form); |
| SubmitForm(form); |
| autofill_manager().Reset(); |
| |
| // This form only has one non-checkable field, so the local heuristics are |
| // not executed. |
| auto entries = |
| test_ukm_recorder().GetEntriesByName(UkmFieldInfoType::kEntryName); |
| EXPECT_EQ(0u, entries.size()); |
| auto form_entries = |
| test_ukm_recorder().GetEntriesByName(UkmFormSummaryType::kEntryName); |
| EXPECT_EQ(0u, form_entries.size()); |
| } |
| |
| // Tests that the forms with <input type="checkbox"> fields and two text field |
| // which have predicted types are recorded in FieldInfo metrics. |
| TEST_F(AutofillMetricsFromLogEventsTest, |
| AutofillFieldInfoMetricsRecordOnCheckBoxWithTextField) { |
| FormData form = test::GetFormData( |
| {.fields = { |
| // Start with two input text fields. |
| {.label = u"First Name", .name = u"firstname"}, |
| {.label = u"Last Name", .name = u"lastname"}, |
| // Two checkable radio buttons. |
| {.label = u"female", |
| .name = u"female", |
| .form_control_type = FormControlType::kInputRadio}, |
| {.label = u"male", |
| .name = u"male", |
| .form_control_type = FormControlType::kInputRadio}, |
| }}); |
| |
| // The two text fields have predicted types. |
| std::vector<FieldType> field_types = {NAME_FIRST, NAME_LAST, UNKNOWN_TYPE, |
| UNKNOWN_TYPE}; |
| autofill_manager().AddSeenForm(form, field_types); |
| SeeForm(form); |
| task_environment_.FastForwardBy(base::Milliseconds(9)); |
| base::HistogramTester histogram_tester; |
| SubmitForm(form); |
| autofill_manager().Reset(); |
| |
| auto entries = |
| test_ukm_recorder().GetEntriesByName(UkmFieldInfoType::kEntryName); |
| ASSERT_EQ(4u, entries.size()); |
| std::vector<FormControlType> form_control_types = { |
| FormControlType::kInputText, FormControlType::kInputText, |
| FormControlType::kInputRadio, FormControlType::kInputRadio}; |
| for (size_t i = 0; i < entries.size(); ++i) { |
| SCOPED_TRACE(testing::Message() << i); |
| DenseSet<AutofillStatus> autofill_status_vector = { |
| AutofillStatus::kIsFocusable}; |
| using UFIT = UkmFieldInfoType; |
| const auto* const entry = entries[i].get(); |
| std::map<std::string, int64_t> expected = { |
| {UFIT::kFormSessionIdentifierName, |
| AutofillMetrics::FormGlobalIdToHash64Bit(form.global_id())}, |
| {UFIT::kFieldSessionIdentifierName, |
| AutofillMetrics::FieldGlobalIdToHash64Bit(form.fields[i].global_id())}, |
| {UFIT::kFieldSignatureName, |
| Collapse(CalculateFieldSignatureForField(form.fields[i])).value()}, |
| {UFIT::kOverallTypeName, field_types[i]}, |
| {UFIT::kSectionIdName, 1}, |
| {UFIT::kTypeChangedByRationalizationName, false}, |
| {UFIT::kFormControlType2Name, |
| base::to_underlying(form_control_types[i])}, |
| {UFIT::kAutocompleteStateName, |
| base::to_underlying(AutofillMetrics::AutocompleteState::kNone)}, |
| {UFIT::kAutofillStatusVectorName, autofill_status_vector.data()[0]}, |
| {UFIT::kFieldLogEventCountName, 1}, |
| }; |
| |
| EXPECT_EQ(expected.size(), entry->metrics.size()); |
| for (const auto& [metric, value] : expected) { |
| test_ukm_recorder().ExpectEntryMetric(entry, metric, value); |
| } |
| } |
| |
| // Verify FormSummary UKM event for the form. |
| auto form_entries = |
| test_ukm_recorder().GetEntriesByName(UkmFormSummaryType::kEntryName); |
| ASSERT_EQ(1u, form_entries.size()); |
| using UFST = UkmFormSummaryType; |
| const auto* const form_entry = form_entries[0].get(); |
| AutofillMetrics::FormEventSet form_events = {FORM_EVENT_DID_PARSE_FORM}; |
| std::map<std::string, int64_t> expected = { |
| {UFST::kFormSessionIdentifierName, |
| AutofillMetrics::FormGlobalIdToHash64Bit(form.global_id())}, |
| {UFST::kFormSignatureName, |
| Collapse(CalculateFormSignature(form)).value()}, |
| {UFST::kAutofillFormEventsName, form_events.data()[0]}, |
| {UFST::kAutofillFormEvents2Name, form_events.data()[1]}, |
| {UFST::kSampleRateName, 1}, |
| {UFST::kWasSubmittedName, true}, |
| {UFST::kMillisecondsFromFormParsedUntilSubmissionName, 9}, |
| }; |
| EXPECT_EQ(expected.size(), form_entry->metrics.size()); |
| for (const auto& [metric, value] : expected) { |
| test_ukm_recorder().ExpectEntryMetric(form_entry, metric, value); |
| } |
| |
| // Verify LogEvent count UMA events of each type. |
| histogram_tester.ExpectBucketCount( |
| "Autofill.LogEvent.AskForValuesToFillEvent", 0, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.TriggerFillEvent", 0, |
| 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.FillEvent", 0, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.TypingEvent", 0, 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.LogEvent.HeuristicPredictionEvent", 0, 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.LogEvent.AutocompleteAttributeEvent", 0, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.ServerPredictionEvent", |
| 0, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.RationalizationEvent", |
| 4, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.All", 4, 1); |
| } |
| |
| // Tests that the forms with <selectlist> field are recorded in UkmFieldInfo |
| // metrics. |
| TEST_F(AutofillMetricsFromLogEventsTest, |
| AutofillFieldInfoMetricsRecordOnSelectListField) { |
| FormData form = test::GetFormData( |
| {.fields = { |
| // Start with two input text fields. |
| {.label = u"First Name", .name = u"firstname"}, |
| {.label = u"Last Name", .name = u"lastname"}, |
| // A Selectlist. |
| {.label = u"Country", |
| .name = u"country", |
| .form_control_type = FormControlType::kSelectList}, |
| }}); |
| |
| std::vector<FieldType> field_types = {NAME_FIRST, NAME_LAST, |
| ADDRESS_HOME_COUNTRY}; |
| autofill_manager().AddSeenForm(form, field_types); |
| SeeForm(form); |
| task_environment_.FastForwardBy(base::Milliseconds(9)); |
| base::HistogramTester histogram_tester; |
| SubmitForm(form); |
| autofill_manager().Reset(); |
| |
| auto entries = |
| test_ukm_recorder().GetEntriesByName(UkmFieldInfoType::kEntryName); |
| ASSERT_EQ(3u, entries.size()); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries[0], UkmFieldInfoType::kFormControlType2Name, |
| base::to_underlying(FormControlType::kInputText)); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries[1], UkmFieldInfoType::kFormControlType2Name, |
| base::to_underlying(FormControlType::kInputText)); |
| test_ukm_recorder().ExpectEntryMetric( |
| entries[2], UkmFieldInfoType::kFormControlType2Name, |
| base::to_underlying(FormControlType::kSelectList)); |
| } |
| |
| // Tests that the field which is in a different frame than its form is recorded |
| // as AutofillStatus::kIsInSubFrame. |
| TEST_F(AutofillMetricsFromLogEventsTest, |
| AutofillFieldInfoMetricsRecordOnDifferentFrames) { |
| // The form has three input text fields, the second field is in a sub frame. |
| FormData form = test::GetFormData( |
| {.fields = |
| { |
| {.label = u"First Name", .name = u"firstname"}, |
| {.host_frame = test::MakeFormGlobalId().frame_token, |
| .label = u"Last Name", |
| .name = u"lastname"}, |
| {.label = u"Email", .name = u"email"}, |
| }, |
| .host_frame = test::MakeFormGlobalId().frame_token}); |
| |
| std::vector<FieldType> field_types = {NAME_FIRST, NAME_LAST, EMAIL_ADDRESS}; |
| autofill_manager().AddSeenForm(form, field_types); |
| SeeForm(form); |
| task_environment_.FastForwardBy(base::Milliseconds(9)); |
| base::HistogramTester histogram_tester; |
| SubmitForm(form); |
| autofill_manager().Reset(); |
| |
| // Verify FieldInfo UKM event for each field. |
| auto entries = |
| test_ukm_recorder().GetEntriesByName(UkmFieldInfoType::kEntryName); |
| ASSERT_EQ(3u, entries.size()); |
| std::vector<FormControlType> form_control_types = { |
| FormControlType::kInputText, FormControlType::kInputText, |
| FormControlType::kInputText}; |
| for (size_t i = 0; i < entries.size(); ++i) { |
| SCOPED_TRACE(testing::Message() << i); |
| |
| DenseSet<AutofillStatus> autofill_status_vector; |
| if (i == 1) { |
| autofill_status_vector = {AutofillStatus::kIsFocusable, |
| AutofillStatus::kIsInSubFrame}; |
| } else { |
| autofill_status_vector = {AutofillStatus::kIsFocusable}; |
| } |
| using UFIT = UkmFieldInfoType; |
| const auto* const entry = entries[i].get(); |
| std::map<std::string, int64_t> expected = { |
| {UFIT::kFormSessionIdentifierName, |
| AutofillMetrics::FormGlobalIdToHash64Bit(form.global_id())}, |
| {UFIT::kFieldSessionIdentifierName, |
| AutofillMetrics::FieldGlobalIdToHash64Bit(form.fields[i].global_id())}, |
| {UFIT::kFieldSignatureName, |
| Collapse(CalculateFieldSignatureForField(form.fields[i])).value()}, |
| {UFIT::kOverallTypeName, field_types[i]}, |
| {UFIT::kSectionIdName, 1}, |
| {UFIT::kTypeChangedByRationalizationName, false}, |
| {UFIT::kFormControlType2Name, |
| base::to_underlying(form_control_types[i])}, |
| {UFIT::kAutocompleteStateName, |
| base::to_underlying(AutofillMetrics::AutocompleteState::kNone)}, |
| {UFIT::kAutofillStatusVectorName, autofill_status_vector.data()[0]}, |
| {UFIT::kHeuristicTypeName, field_types[i]}, |
| #if BUILDFLAG(USE_INTERNAL_AUTOFILL_PATTERNS) |
| {UFIT::kHeuristicTypeLegacyName, UNKNOWN_TYPE}, |
| {UFIT::kHeuristicTypeDefaultName, field_types[i]}, |
| {UFIT::kHeuristicTypeExperimentalName, field_types[i]}, |
| {UFIT::kHeuristicTypeNextGenName, UNKNOWN_TYPE}, |
| {UFIT::kFieldLogEventCountName, 3}, |
| #else |
| {UFIT::kHeuristicTypeLegacyName, field_types[i]}, |
| {UFIT::kHeuristicTypeDefaultName, UNKNOWN_TYPE}, |
| {UFIT::kHeuristicTypeExperimentalName, UNKNOWN_TYPE}, |
| {UFIT::kHeuristicTypeNextGenName, UNKNOWN_TYPE}, |
| {UFIT::kFieldLogEventCountName, 2}, |
| #endif |
| {UFIT::kRankInFieldSignatureGroupName, 1}, |
| }; |
| |
| EXPECT_EQ(expected.size(), entry->metrics.size()); |
| for (const auto& [metric, value] : expected) { |
| test_ukm_recorder().ExpectEntryMetric(entry, metric, value); |
| } |
| } |
| |
| // Verify FormSummary UKM event for the form. |
| auto form_entries = |
| test_ukm_recorder().GetEntriesByName(UkmFormSummaryType::kEntryName); |
| ASSERT_EQ(1u, form_entries.size()); |
| using UFST = UkmFormSummaryType; |
| const auto* const form_entry = form_entries[0].get(); |
| AutofillMetrics::FormEventSet form_events = {FORM_EVENT_DID_PARSE_FORM}; |
| std::map<std::string, int64_t> expected = { |
| {UFST::kFormSessionIdentifierName, |
| AutofillMetrics::FormGlobalIdToHash64Bit(form.global_id())}, |
| {UFST::kFormSignatureName, |
| Collapse(CalculateFormSignature(form)).value()}, |
| {UFST::kAutofillFormEventsName, form_events.data()[0]}, |
| {UFST::kAutofillFormEvents2Name, form_events.data()[1]}, |
| {UFST::kSampleRateName, 1}, |
| {UFST::kWasSubmittedName, true}, |
| {UFST::kMillisecondsFromFormParsedUntilSubmissionName, 9}, |
| }; |
| EXPECT_EQ(expected.size(), form_entry->metrics.size()); |
| for (const auto& [metric, value] : expected) { |
| test_ukm_recorder().ExpectEntryMetric(form_entry, metric, value); |
| } |
| |
| // Verify LogEvent count UMA events of each type. |
| histogram_tester.ExpectBucketCount( |
| "Autofill.LogEvent.AskForValuesToFillEvent", 0, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.TriggerFillEvent", 0, |
| 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.FillEvent", 0, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.TypingEvent", 0, 1); |
| histogram_tester.ExpectBucketCount( |
| "Autofill.LogEvent.AutocompleteAttributeEvent", 0, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.ServerPredictionEvent", |
| 0, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.RationalizationEvent", |
| 3, 1); |
| #if BUILDFLAG(USE_INTERNAL_AUTOFILL_PATTERNS) |
| histogram_tester.ExpectBucketCount( |
| "Autofill.LogEvent.HeuristicPredictionEvent", 6, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.All", 8, 1); |
| #else |
| histogram_tester.ExpectBucketCount( |
| "Autofill.LogEvent.HeuristicPredictionEvent", 3, 1); |
| histogram_tester.ExpectBucketCount("Autofill.LogEvent.All", 6, 1); |
| #endif |
| } |
| |
| } // namespace autofill::autofill_metrics |