1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/password_manager/core/browser/form_parsing/password_field_prediction.h"
6
7 #include <vector>
8
9 #include "base/stl_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "base/test/scoped_feature_list.h"
12 #include "components/autofill/core/browser/form_structure.h"
13 #include "components/autofill/core/common/form_data.h"
14 #include "components/autofill/core/common/renderer_id.h"
15 #include "components/password_manager/core/common/password_manager_features.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18
19 using autofill::ACCOUNT_CREATION_PASSWORD;
20 using autofill::AutofillField;
21 using autofill::CONFIRMATION_PASSWORD;
22 using autofill::CREDIT_CARD_VERIFICATION_CODE;
23 using autofill::EMAIL_ADDRESS;
24 using autofill::FormData;
25 using autofill::FormFieldData;
26 using autofill::FormStructure;
27 using autofill::NEW_PASSWORD;
28 using autofill::NO_SERVER_DATA;
29 using autofill::PASSWORD;
30 using autofill::ServerFieldType;
31 using autofill::SINGLE_USERNAME;
32 using autofill::UNKNOWN_TYPE;
33 using autofill::USERNAME;
34 using autofill::USERNAME_AND_EMAIL_ADDRESS;
35 using base::ASCIIToUTF16;
36
37 using FieldPrediction = autofill::AutofillQueryResponse::FormSuggestion::
38 FieldSuggestion::FieldPrediction;
39
40 namespace password_manager {
41
42 namespace {
43
44 // The boolean parameter determines the feature state of
45 // `kSecondaryServerFieldPredictions`.
46 class FormPredictionsTest : public ::testing::TestWithParam<bool> {
47 public:
FormPredictionsTest()48 FormPredictionsTest() {
49 feature_list_.InitWithFeatureState(
50 features::kSecondaryServerFieldPredictions,
51 AreSecondaryPredictionsEnabled());
52 }
53
AreSecondaryPredictionsEnabled() const54 bool AreSecondaryPredictionsEnabled() const { return GetParam(); }
55
56 private:
57 base::test::ScopedFeatureList feature_list_;
58 };
59
TEST_P(FormPredictionsTest,ConvertToFormPredictions)60 TEST_P(FormPredictionsTest, ConvertToFormPredictions) {
61 struct TestField {
62 std::string name;
63 std::string form_control_type;
64 ServerFieldType input_type;
65 ServerFieldType expected_type;
66 bool may_use_prefilled_placeholder;
67 std::vector<ServerFieldType> additional_types;
68 } test_fields[] = {
69 {"full_name", "text", UNKNOWN_TYPE, UNKNOWN_TYPE, false},
70 // Password Manager is interested only in credential related types.
71 {"Email", "email", EMAIL_ADDRESS, EMAIL_ADDRESS, false},
72 {"username", "text", USERNAME, USERNAME, true},
73 {"Password", "password", PASSWORD, PASSWORD, false},
74 {"confirm_password", "password", CONFIRMATION_PASSWORD,
75 CONFIRMATION_PASSWORD, true},
76 // username in |additional_types| takes precedence if the feature is
77 // enabled.
78 {"email",
79 "text",
80 EMAIL_ADDRESS,
81 AreSecondaryPredictionsEnabled() ? USERNAME : EMAIL_ADDRESS,
82 false,
83 {USERNAME}},
84 // cvc in |additional_types| takes precedence if the feature is enabled.
85 {"cvc",
86 "password",
87 PASSWORD,
88 AreSecondaryPredictionsEnabled() ? CREDIT_CARD_VERIFICATION_CODE
89 : PASSWORD,
90 false,
91 {CREDIT_CARD_VERIFICATION_CODE}},
92 // non-password, non-cvc types in |additional_types| are ignored.
93 {"email", "text", UNKNOWN_TYPE, UNKNOWN_TYPE, false, {EMAIL_ADDRESS}},
94 };
95
96 FormData form_data;
97 for (size_t i = 0; i < base::size(test_fields); ++i) {
98 FormFieldData field;
99 field.unique_renderer_id = autofill::FieldRendererId(i + 1000);
100 field.name = ASCIIToUTF16(test_fields[i].name);
101 field.form_control_type = test_fields[i].form_control_type;
102 form_data.fields.push_back(field);
103 }
104
105 FormStructure form_structure(form_data);
106 // Set server predictions and create expected votes.
107 for (size_t i = 0; i < base::size(test_fields); ++i) {
108 AutofillField* field = form_structure.field(i);
109 field->set_server_type(test_fields[i].input_type);
110
111 std::vector<FieldPrediction> predictions(1);
112
113 for (ServerFieldType type : test_fields[i].additional_types) {
114 FieldPrediction additional_prediction;
115 additional_prediction.set_type(type);
116 predictions.push_back(additional_prediction);
117 }
118 field->set_server_predictions(predictions);
119 field->set_may_use_prefilled_placeholder(
120 test_fields[i].may_use_prefilled_placeholder);
121 }
122
123 constexpr int driver_id = 1000;
124 FormPredictions actual_predictions =
125 ConvertToFormPredictions(driver_id, form_structure);
126
127 // Check whether actual predictions are equal to expected ones.
128 EXPECT_EQ(driver_id, actual_predictions.driver_id);
129 EXPECT_EQ(form_structure.form_signature(), actual_predictions.form_signature);
130 EXPECT_EQ(base::size(test_fields), actual_predictions.fields.size());
131
132 for (size_t i = 0; i < base::size(test_fields); ++i) {
133 const PasswordFieldPrediction& actual_prediction =
134 actual_predictions.fields[i];
135 EXPECT_EQ(test_fields[i].expected_type, actual_prediction.type);
136 EXPECT_EQ(test_fields[i].may_use_prefilled_placeholder,
137 actual_prediction.may_use_prefilled_placeholder);
138 EXPECT_EQ(form_structure.field(i)->GetFieldSignature(),
139 actual_prediction.signature);
140 }
141 }
142
TEST(FormPredictionsTest,ConvertToFormPredictions_SynthesiseConfirmation)143 TEST(FormPredictionsTest, ConvertToFormPredictions_SynthesiseConfirmation) {
144 struct TestField {
145 std::string name;
146 std::string form_control_type;
147 ServerFieldType input_type;
148 ServerFieldType expected_type;
149 };
150 const std::vector<TestField> kTestForms[] = {
151 {
152 {"username", "text", USERNAME, USERNAME},
153 {"new password", "password", ACCOUNT_CREATION_PASSWORD,
154 ACCOUNT_CREATION_PASSWORD},
155 // Same name and type means same signature. As a second new-password
156 // field with this signature, the next field should be re-classified
157 // to confirmation password.
158 {"new password", "password", ACCOUNT_CREATION_PASSWORD,
159 CONFIRMATION_PASSWORD},
160 },
161 {
162 {"username", "text", USERNAME, USERNAME},
163 {"new password duplicate", "password", ACCOUNT_CREATION_PASSWORD,
164 ACCOUNT_CREATION_PASSWORD},
165 // An explicit confirmation password above should override the
166 // 2-new-passwords heuristic.
167 {"new password duplicate", "password", ACCOUNT_CREATION_PASSWORD,
168 ACCOUNT_CREATION_PASSWORD},
169 {"confirm_password", "password", CONFIRMATION_PASSWORD,
170 CONFIRMATION_PASSWORD},
171 },
172 };
173
174 for (const std::vector<TestField>& test_form : kTestForms) {
175 FormData form_data;
176 for (size_t i = 0; i < test_form.size(); ++i) {
177 FormFieldData field;
178 field.unique_renderer_id = autofill::FieldRendererId(i + 1000);
179 field.name = ASCIIToUTF16(test_form[i].name);
180 field.form_control_type = test_form[i].form_control_type;
181 form_data.fields.push_back(field);
182 }
183
184 FormStructure form_structure(form_data);
185 // Set server predictions and create expected votes.
186 for (size_t i = 0; i < test_form.size(); ++i) {
187 AutofillField* field = form_structure.field(i);
188 field->set_server_type(test_form[i].input_type);
189 }
190
191 FormPredictions actual_predictions =
192 ConvertToFormPredictions(0 /*driver_id*/, form_structure);
193
194 for (size_t i = 0; i < form_data.fields.size(); ++i) {
195 SCOPED_TRACE(testing::Message()
196 << "field description: name=" << test_form[i].name
197 << ", form control type=" << test_form[i].form_control_type
198 << ", input type=" << test_form[i].input_type
199 << ", expected type=" << test_form[i].expected_type
200 << ", synthesised FormFieldData=" << form_data.fields[i]);
201 EXPECT_EQ(test_form[i].expected_type, actual_predictions.fields[i].type);
202 }
203 }
204 }
205
TEST(FormPredictionsTest,DeriveFromServerFieldType)206 TEST(FormPredictionsTest, DeriveFromServerFieldType) {
207 struct TestCase {
208 const char* name;
209 // Input.
210 ServerFieldType server_type;
211 CredentialFieldType expected_result;
212 } test_cases[] = {
213 {"No prediction", NO_SERVER_DATA, CredentialFieldType::kNone},
214 {"Irrelevant type", EMAIL_ADDRESS, CredentialFieldType::kNone},
215 {"Username", USERNAME, CredentialFieldType::kUsername},
216 {"Username/Email", USERNAME_AND_EMAIL_ADDRESS,
217 CredentialFieldType::kUsername},
218 {"Single Username", SINGLE_USERNAME,
219 CredentialFieldType::kSingleUsername},
220 {"Password", PASSWORD, CredentialFieldType::kCurrentPassword},
221 {"New password", NEW_PASSWORD, CredentialFieldType::kNewPassword},
222 {"Account creation password", ACCOUNT_CREATION_PASSWORD,
223 CredentialFieldType::kNewPassword},
224 {"Confirmation password", CONFIRMATION_PASSWORD,
225 CredentialFieldType::kConfirmationPassword},
226 };
227
228 for (const TestCase& test_case : test_cases) {
229 SCOPED_TRACE(test_case.name);
230 EXPECT_EQ(test_case.expected_result,
231 DeriveFromServerFieldType(test_case.server_type));
232 }
233 }
234 INSTANTIATE_TEST_SUITE_P(All, FormPredictionsTest, testing::Bool());
235 } // namespace
236
237 } // namespace password_manager
238