Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / components / autofill / content / renderer / password_form_conversion_utils_browsertest.cc
blobd733c53342eabfe9341dfd55dd426241a77a5b33
1 // Copyright 2013 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.
5 #include "base/strings/string16.h"
6 #include "base/strings/string_util.h"
7 #include "base/strings/stringprintf.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "components/autofill/content/renderer/password_form_conversion_utils.h"
10 #include "components/autofill/core/common/password_form.h"
11 #include "content/public/test/render_view_test.h"
12 #include "testing/gmock/include/gmock/gmock.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14 #include "third_party/WebKit/public/platform/WebVector.h"
15 #include "third_party/WebKit/public/web/WebDocument.h"
16 #include "third_party/WebKit/public/web/WebFormControlElement.h"
17 #include "third_party/WebKit/public/web/WebFormElement.h"
18 #include "third_party/WebKit/public/web/WebInputElement.h"
19 #include "third_party/WebKit/public/web/WebLocalFrame.h"
21 using blink::WebFormControlElement;
22 using blink::WebFormElement;
23 using blink::WebFrame;
24 using blink::WebInputElement;
25 using blink::WebVector;
27 namespace autofill {
28 namespace {
30 const char kTestFormActionURL[] = "http://cnn.com";
32 // A builder to produce HTML code for a password form composed of the desired
33 // number and kinds of username and password fields.
34 class PasswordFormBuilder {
35 public:
36 // Creates a builder to start composing a new form. The form will have the
37 // specified |action| URL.
38 explicit PasswordFormBuilder(const char* action) {
39 base::StringAppendF(
40 &html_, "<FORM name=\"Test\" action=\"%s\" method=\"post\">", action);
43 // Appends a new text-type field at the end of the form, having the specified
44 // |name_and_id|, |value|, and |autocomplete| attributes. The |autocomplete|
45 // argument can take two special values, namely:
46 // 1.) NULL, causing no autocomplete attribute to be added,
47 // 2.) "", causing an empty attribute (i.e. autocomplete="") to be added.
48 void AddUsernameField(const char* name_and_id,
49 const char* value,
50 const char* autocomplete) {
51 std::string autocomplete_attribute(autocomplete ?
52 base::StringPrintf("autocomplete=\"%s\"", autocomplete) : "");
53 base::StringAppendF(
54 &html_,
55 "<INPUT type=\"text\" name=\"%s\" id=\"%s\" value=\"%s\" %s/>",
56 name_and_id, name_and_id, value, autocomplete_attribute.c_str());
59 // Appends a new password-type field at the end of the form, having the
60 // specified |name_and_id|, |value|, and |autocomplete| attributes. Special
61 // values for |autocomplete| are the same as in AddUsernameField.
62 void AddPasswordField(const char* name_and_id,
63 const char* value,
64 const char* autocomplete) {
65 std::string autocomplete_attribute(autocomplete ?
66 base::StringPrintf("autocomplete=\"%s\"", autocomplete): "");
67 base::StringAppendF(
68 &html_,
69 "<INPUT type=\"password\" name=\"%s\" id=\"%s\" value=\"%s\" %s/>",
70 name_and_id, name_and_id, value, autocomplete_attribute.c_str());
73 // Appends a disabled text-type field at the end of the form.
74 void AddDisabledUsernameField() {
75 html_ += "<INPUT type=\"text\" disabled/>";
78 // Appends a disabled password-type field at the end of the form.
79 void AddDisabledPasswordField() {
80 html_ += "<INPUT type=\"password\" disabled/>";
83 // Appends a hidden field at the end of the form.
84 void AddHiddenField() { html_ += "<INPUT type=\"hidden\"/>"; }
86 // Appends a new submit-type field at the end of the form with the specified
87 // |name|. If |activated| is true, the test will emulate as if this button
88 // were used to submit the form.
89 void AddSubmitButton(const char* name, bool activated) {
90 base::StringAppendF(
91 &html_,
92 "<INPUT type=\"submit\" name=\"%s\" value=\"Submit\" %s/>",
93 name, activated ? "set-activated-submit" : "");
96 // Returns the HTML code for the form containing the fields that have been
97 // added so far.
98 std::string ProduceHTML() const {
99 return html_ + "</FORM>";
102 private:
103 std::string html_;
105 DISALLOW_COPY_AND_ASSIGN(PasswordFormBuilder);
108 // RenderViewTest-based tests crash on Android
109 // http://crbug.com/187500
110 #if defined(OS_ANDROID)
111 #define MAYBE_PasswordFormConversionUtilsTest \
112 DISABLED_PasswordFormConversionUtilsTest
113 #else
114 #define MAYBE_PasswordFormConversionUtilsTest PasswordFormConversionUtilsTest
115 #endif // defined(OS_ANDROID)
117 class MAYBE_PasswordFormConversionUtilsTest : public content::RenderViewTest {
118 public:
119 MAYBE_PasswordFormConversionUtilsTest() : content::RenderViewTest() {}
120 ~MAYBE_PasswordFormConversionUtilsTest() override {}
122 protected:
123 // Loads the given |html|, retrieves the sole WebFormElement from it, and then
124 // calls CreatePasswordForm() to convert it into a |password_form|. Note that
125 // ASSERT() can only be used in void functions, this is why |password_form| is
126 // passed in as a pointer to a scoped_ptr.
127 void LoadHTMLAndConvertForm(const std::string& html,
128 scoped_ptr<PasswordForm>* password_form) {
129 LoadHTML(html.c_str());
131 WebFrame* frame = GetMainFrame();
132 ASSERT_NE(static_cast<WebFrame*>(NULL), frame);
134 WebVector<WebFormElement> forms;
135 frame->document().forms(forms);
136 ASSERT_EQ(1U, forms.size());
138 WebVector<WebFormControlElement> control_elements;
139 forms[0].getFormControlElements(control_elements);
140 for (size_t i = 0; i < control_elements.size(); ++i) {
141 WebInputElement* input_element = toWebInputElement(&control_elements[i]);
142 if (input_element->hasAttribute("set-activated-submit"))
143 input_element->setActivatedSubmit(true);
146 *password_form = CreatePasswordForm(forms[0], nullptr, nullptr);
149 private:
150 DISALLOW_COPY_AND_ASSIGN(MAYBE_PasswordFormConversionUtilsTest);
153 } // namespace
155 TEST_F(MAYBE_PasswordFormConversionUtilsTest, BasicFormAttributes) {
156 PasswordFormBuilder builder(kTestFormActionURL);
157 builder.AddUsernameField("username", "johnsmith", NULL);
158 builder.AddSubmitButton("inactive_submit", false);
159 builder.AddSubmitButton("active_submit", true);
160 builder.AddSubmitButton("inactive_submit2", false);
161 builder.AddPasswordField("password", "secret", NULL);
162 std::string html = builder.ProduceHTML();
164 scoped_ptr<PasswordForm> password_form;
165 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form));
166 ASSERT_TRUE(password_form);
168 EXPECT_EQ("data:", password_form->signon_realm);
169 EXPECT_EQ(GURL(kTestFormActionURL), password_form->action);
170 EXPECT_EQ(base::UTF8ToUTF16("active_submit"), password_form->submit_element);
171 EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element);
172 EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value);
173 EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element);
174 EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value);
175 EXPECT_EQ(PasswordForm::SCHEME_HTML, password_form->scheme);
176 EXPECT_FALSE(password_form->ssl_valid);
177 EXPECT_FALSE(password_form->preferred);
178 EXPECT_FALSE(password_form->blacklisted_by_user);
179 EXPECT_EQ(PasswordForm::TYPE_MANUAL, password_form->type);
182 TEST_F(MAYBE_PasswordFormConversionUtilsTest, DisabledFieldsAreIgnored) {
183 PasswordFormBuilder builder(kTestFormActionURL);
184 builder.AddUsernameField("username", "johnsmith", NULL);
185 builder.AddDisabledUsernameField();
186 builder.AddDisabledPasswordField();
187 builder.AddPasswordField("password", "secret", NULL);
188 builder.AddSubmitButton("submit", true);
189 std::string html = builder.ProduceHTML();
191 scoped_ptr<PasswordForm> password_form;
192 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form));
193 ASSERT_TRUE(password_form);
194 EXPECT_EQ(base::UTF8ToUTF16("username"), password_form->username_element);
195 EXPECT_EQ(base::UTF8ToUTF16("johnsmith"), password_form->username_value);
196 EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element);
197 EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value);
200 TEST_F(MAYBE_PasswordFormConversionUtilsTest, IdentifyingUsernameFields) {
201 // Each test case consists of a set of parameters to be plugged into the
202 // PasswordFormBuilder below, plus the corresponding expectations.
203 struct TestCase {
204 const char* autocomplete[3];
205 const char* expected_username_element;
206 const char* expected_username_value;
207 const char* expected_other_possible_usernames;
208 } cases[] = {
209 // When no elements are marked with autocomplete='username', the text-type
210 // input field before the first password element should get selected as
211 // the username, and the rest should be marked as alternatives.
212 {{NULL, NULL, NULL}, "username2", "William", "John+Smith"},
213 // When a sole element is marked with autocomplete='username', it should
214 // be treated as the username for sure, with no other_possible_usernames.
215 {{"username", NULL, NULL}, "username1", "John", ""},
216 {{NULL, "username", NULL}, "username2", "William", ""},
217 {{NULL, NULL, "username"}, "username3", "Smith", ""},
218 // When >=2 elements have the attribute, the first should be selected as
219 // the username, and the rest should go to other_possible_usernames.
220 {{"username", "username", NULL}, "username1", "John", "William"},
221 {{NULL, "username", "username"}, "username2", "William", "Smith"},
222 {{"username", NULL, "username"}, "username1", "John", "Smith"},
223 {{"username", "username", "username"}, "username1", "John",
224 "William+Smith"},
225 // When there is an empty autocomplete attribute (i.e. autocomplete=""),
226 // it should have the same effect as having no attribute whatsoever.
227 {{"", "", ""}, "username2", "William", "John+Smith"},
228 {{"", "", "username"}, "username3", "Smith", ""},
229 {{"username", "", "username"}, "username1", "John", "Smith"},
230 // It should not matter if attribute values are upper or mixed case.
231 {{"USERNAME", NULL, "uSeRNaMe"}, "username1", "John", "Smith"},
232 {{"uSeRNaMe", NULL, "USERNAME"}, "username1", "John", "Smith"}};
234 for (size_t i = 0; i < arraysize(cases); ++i) {
235 for (size_t nonempty_username_fields = 0; nonempty_username_fields < 2;
236 ++nonempty_username_fields) {
237 SCOPED_TRACE(testing::Message()
238 << "Iteration " << i << " "
239 << (nonempty_username_fields ? "nonempty" : "empty"));
241 // Repeat each test once with empty, and once with non-empty usernames.
242 // In the former case, no empty other_possible_usernames should be saved.
243 const char* names[3];
244 if (nonempty_username_fields) {
245 names[0] = "John";
246 names[1] = "William";
247 names[2] = "Smith";
248 } else {
249 names[0] = names[1] = names[2] = "";
252 PasswordFormBuilder builder(kTestFormActionURL);
253 builder.AddUsernameField("username1", names[0], cases[i].autocomplete[0]);
254 builder.AddUsernameField("username2", names[1], cases[i].autocomplete[1]);
255 builder.AddPasswordField("password", "secret", NULL);
256 builder.AddUsernameField("username3", names[2], cases[i].autocomplete[2]);
257 builder.AddPasswordField("password2", "othersecret", NULL);
258 builder.AddSubmitButton("submit", true);
259 std::string html = builder.ProduceHTML();
261 scoped_ptr<PasswordForm> password_form;
262 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form));
263 ASSERT_TRUE(password_form);
265 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_element),
266 password_form->username_element);
268 if (nonempty_username_fields) {
269 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_username_value),
270 password_form->username_value);
271 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_other_possible_usernames),
272 JoinString(password_form->other_possible_usernames, '+'));
273 } else {
274 EXPECT_TRUE(password_form->username_value.empty());
275 EXPECT_TRUE(password_form->other_possible_usernames.empty());
278 // Do a basic sanity check that we are still having a password field.
279 EXPECT_EQ(base::UTF8ToUTF16("password"), password_form->password_element);
280 EXPECT_EQ(base::UTF8ToUTF16("secret"), password_form->password_value);
285 TEST_F(MAYBE_PasswordFormConversionUtilsTest, IdentifyingTwoPasswordFields) {
286 // Each test case consists of a set of parameters to be plugged into the
287 // PasswordFormBuilder below, plus the corresponding expectations.
288 struct TestCase {
289 const char* password_values[2];
290 const char* expected_password_element;
291 const char* expected_password_value;
292 const char* expected_new_password_element;
293 const char* expected_new_password_value;
294 } cases[] = {
295 // Twp non-empty fields with the same value should be treated as a new
296 // password field plus a confirmation field for the new password.
297 {{"alpha", "alpha"}, "", "", "password1", "alpha"},
298 // The same goes if the fields are yet empty: we speculate that we will
299 // identify them as new password fields once they are filled out, and we
300 // want to keep our abstract interpretation of the form less flaky.
301 {{"", ""}, "", "", "password1", ""},
302 // Two different values should be treated as a password change form, one
303 // that also asks for the current password, but only once for the new.
304 {{"alpha", ""}, "password1", "alpha", "password2", ""},
305 {{"", "beta"}, "password1", "", "password2", "beta"},
306 {{"alpha", "beta"}, "password1", "alpha", "password2", "beta"}};
308 for (size_t i = 0; i < arraysize(cases); ++i) {
309 SCOPED_TRACE(testing::Message() << "Iteration " << i);
311 PasswordFormBuilder builder(kTestFormActionURL);
312 builder.AddPasswordField("password1", cases[i].password_values[0], NULL);
313 builder.AddUsernameField("username1", "William", NULL);
314 builder.AddPasswordField("password2", cases[i].password_values[1], NULL);
315 builder.AddUsernameField("username2", "Smith", NULL);
316 builder.AddSubmitButton("submit", true);
317 std::string html = builder.ProduceHTML();
319 scoped_ptr<PasswordForm> password_form;
320 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form));
321 ASSERT_TRUE(password_form);
323 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_element),
324 password_form->password_element);
325 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_value),
326 password_form->password_value);
327 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_element),
328 password_form->new_password_element);
329 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_value),
330 password_form->new_password_value);
332 // Do a basic sanity check that we are still selecting the right username.
333 EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form->username_element);
334 EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value);
335 EXPECT_THAT(password_form->other_possible_usernames,
336 testing::ElementsAre(base::UTF8ToUTF16("Smith")));
340 TEST_F(MAYBE_PasswordFormConversionUtilsTest, IdentifyingThreePasswordFields) {
341 // Each test case consists of a set of parameters to be plugged into the
342 // PasswordFormBuilder below, plus the corresponding expectations.
343 struct TestCase {
344 const char* password_values[3];
345 const char* expected_password_element;
346 const char* expected_password_value;
347 const char* expected_new_password_element;
348 const char* expected_new_password_value;
349 } cases[] = {
350 // Two fields with the same value, and one different: we should treat this
351 // as a password change form with confirmation for the new password. Note
352 // that we only recognize (current + new + new) and (new + new + current)
353 // without autocomplete attributes.
354 {{"alpha", "", ""}, "password1", "alpha", "password2", ""},
355 {{"", "beta", "beta"}, "password1", "", "password2", "beta"},
356 {{"alpha", "beta", "beta"}, "password1", "alpha", "password2", "beta"},
357 // If confirmed password comes first, assume that the third password
358 // field is related to security question, SSN, or credit card and ignore
359 // it.
360 {{"beta", "beta", "alpha"}, "", "", "password1", "beta"},
361 // If the fields are yet empty, we speculate that we will identify them as
362 // (current + new + new) once they are filled out, so we should classify
363 // them the same for now to keep our abstract interpretation less flaky.
364 {{"", "", ""}, "password1", "", "password2", ""}};
365 // Note: In all other cases, we give up and consider the form invalid.
366 // This is tested in InvalidFormDueToConfusingPasswordFields.
368 for (size_t i = 0; i < arraysize(cases); ++i) {
369 SCOPED_TRACE(testing::Message() << "Iteration " << i);
371 PasswordFormBuilder builder(kTestFormActionURL);
372 builder.AddPasswordField("password1", cases[i].password_values[0], NULL);
373 builder.AddUsernameField("username1", "William", NULL);
374 builder.AddPasswordField("password2", cases[i].password_values[1], NULL);
375 builder.AddUsernameField("username2", "Smith", NULL);
376 builder.AddPasswordField("password3", cases[i].password_values[2], NULL);
377 builder.AddSubmitButton("submit", true);
378 std::string html = builder.ProduceHTML();
380 scoped_ptr<PasswordForm> password_form;
381 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form));
382 ASSERT_TRUE(password_form);
384 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_element),
385 password_form->password_element);
386 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_value),
387 password_form->password_value);
388 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_element),
389 password_form->new_password_element);
390 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_value),
391 password_form->new_password_value);
393 // Do a basic sanity check that we are still selecting the right username.
394 EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form->username_element);
395 EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value);
396 EXPECT_THAT(password_form->other_possible_usernames,
397 testing::ElementsAre(base::UTF8ToUTF16("Smith")));
401 TEST_F(MAYBE_PasswordFormConversionUtilsTest,
402 IdentifyingPasswordFieldsWithAutocompleteAttributes) {
403 // Each test case consists of a set of parameters to be plugged into the
404 // PasswordFormBuilder below, plus the corresponding expectations.
405 struct TestCase {
406 const char* autocomplete[3];
407 const char* expected_password_element;
408 const char* expected_password_value;
409 const char* expected_new_password_element;
410 const char* expected_new_password_value;
411 bool expected_new_password_marked_by_site;
412 } cases[] = {
413 // When there are elements marked with autocomplete='current-password',
414 // but no elements with 'new-password', we should treat the first of the
415 // former kind as the current password, and ignore all other password
416 // fields, assuming they are not intentionally not marked. They might be
417 // for other purposes, such as PINs, OTPs, and the like. Actual values in
418 // the password fields should be ignored in all cases below.
419 {{"current-password", NULL, NULL},
420 "password1", "alpha", "", "", false},
421 {{NULL, "current-password", NULL},
422 "password2", "beta", "", "", false},
423 {{NULL, NULL, "current-password"},
424 "password3", "gamma", "", "", false},
425 {{NULL, "current-password", "current-password"},
426 "password2", "beta", "", "", false},
427 {{"current-password", NULL, "current-password"},
428 "password1", "alpha", "", "", false},
429 {{"current-password", "current-password", NULL},
430 "password1", "alpha", "", "", false},
431 {{"current-password", "current-password", "current-password"},
432 "password1", "alpha", "", "", false},
433 // The same goes vice versa for autocomplete='new-password'.
434 {{"new-password", NULL, NULL},
435 "", "", "password1", "alpha", true},
436 {{NULL, "new-password", NULL},
437 "", "", "password2", "beta", true},
438 {{NULL, NULL, "new-password"},
439 "", "", "password3", "gamma", true},
440 {{NULL, "new-password", "new-password"},
441 "", "", "password2", "beta", true},
442 {{"new-password", NULL, "new-password"},
443 "", "", "password1", "alpha", true},
444 {{"new-password", "new-password", NULL},
445 "", "", "password1", "alpha", true},
446 {{"new-password", "new-password", "new-password"},
447 "", "", "password1", "alpha", true},
448 // When there is one element marked with autocomplete='current-password',
449 // and one with 'new-password', just comply, regardless of their order.
450 // Ignore the unmarked password field(s) for the same reason as above.
451 {{"current-password", "new-password", NULL},
452 "password1", "alpha", "password2", "beta", true},
453 {{"current-password", NULL, "new-password"},
454 "password1", "alpha", "password3", "gamma", true},
455 {{NULL, "current-password", "new-password"},
456 "password2", "beta", "password3", "gamma", true},
457 {{"new-password", "current-password", NULL},
458 "password2", "beta", "password1", "alpha", true},
459 {{"new-password", NULL, "current-password"},
460 "password3", "gamma", "password1", "alpha", true},
461 {{NULL, "new-password", "current-password"},
462 "password3", "gamma", "password2", "beta", true},
463 // In case of duplicated elements of either kind, go with the first one of
464 // its kind.
465 {{"current-password", "current-password", "new-password"},
466 "password1", "alpha", "password3", "gamma", true},
467 {{"current-password", "new-password", "current-password"},
468 "password1", "alpha", "password2", "beta", true},
469 {{"new-password", "current-password", "current-password"},
470 "password2", "beta", "password1", "alpha", true},
471 {{"current-password", "new-password", "new-password"},
472 "password1", "alpha", "password2", "beta", true},
473 {{"new-password", "current-password", "new-password"},
474 "password2", "beta", "password1", "alpha", true},
475 {{"new-password", "new-password", "current-password"},
476 "password3", "gamma", "password1", "alpha", true},
477 // When there is an empty autocomplete attribute (i.e. autocomplete=""),
478 // it should have the same effect as having no attribute whatsoever.
479 {{"current-password", "", ""},
480 "password1", "alpha", "", "", false},
481 {{"", "", "new-password"},
482 "", "", "password3", "gamma", true},
483 {{"", "new-password", ""},
484 "", "", "password2", "beta", true},
485 {{"", "current-password", "current-password"},
486 "password2", "beta", "", "", false},
487 {{"new-password", "", "new-password"},
488 "", "", "password1", "alpha", true},
489 {{"new-password", "", "current-password"},
490 "password3", "gamma", "password1", "alpha", true},
491 // It should not matter if attribute values are upper or mixed case.
492 {{NULL, "current-password", NULL},
493 "password2", "beta", "", "", false},
494 {{NULL, "CURRENT-PASSWORD", NULL},
495 "password2", "beta", "", "", false},
496 {{NULL, "new-password", NULL},
497 "", "", "password2", "beta", true},
498 {{NULL, "nEw-PaSsWoRd", NULL},
499 "", "", "password2", "beta", true}};
501 for (size_t i = 0; i < arraysize(cases); ++i) {
502 SCOPED_TRACE(testing::Message() << "Iteration " << i);
504 PasswordFormBuilder builder(kTestFormActionURL);
505 builder.AddPasswordField("pin1", "123456", NULL);
506 builder.AddPasswordField("pin2", "789101", NULL);
507 builder.AddPasswordField("password1", "alpha", cases[i].autocomplete[0]);
508 builder.AddUsernameField("username1", "William", NULL);
509 builder.AddPasswordField("password2", "beta", cases[i].autocomplete[1]);
510 builder.AddUsernameField("username2", "Smith", NULL);
511 builder.AddPasswordField("password3", "gamma", cases[i].autocomplete[2]);
512 builder.AddSubmitButton("submit", true);
513 std::string html = builder.ProduceHTML();
515 scoped_ptr<PasswordForm> password_form;
516 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form));
517 ASSERT_TRUE(password_form);
519 // In the absence of username autocomplete attributes, the username should
520 // be the text input field before the first password element.
521 // No constellation of password autocomplete attributes should change that.
522 EXPECT_EQ(base::UTF8ToUTF16("username1"), password_form->username_element);
523 EXPECT_EQ(base::UTF8ToUTF16("William"), password_form->username_value);
524 EXPECT_THAT(password_form->other_possible_usernames,
525 testing::ElementsAre(base::UTF8ToUTF16("Smith")));
526 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_element),
527 password_form->password_element);
528 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_password_value),
529 password_form->password_value);
530 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_element),
531 password_form->new_password_element);
532 EXPECT_EQ(base::UTF8ToUTF16(cases[i].expected_new_password_value),
533 password_form->new_password_value);
534 EXPECT_EQ(cases[i].expected_new_password_marked_by_site,
535 password_form->new_password_marked_by_site);
539 TEST_F(MAYBE_PasswordFormConversionUtilsTest, InvalidFormDueToBadActionURL) {
540 PasswordFormBuilder builder("invalid_target");
541 builder.AddUsernameField("username", "JohnSmith", NULL);
542 builder.AddSubmitButton("submit", true);
543 builder.AddPasswordField("password", "secret", NULL);
544 std::string html = builder.ProduceHTML();
546 scoped_ptr<PasswordForm> password_form;
547 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form));
548 EXPECT_FALSE(password_form);
551 TEST_F(MAYBE_PasswordFormConversionUtilsTest,
552 InvalidFormDueToNoPasswordFields) {
553 PasswordFormBuilder builder(kTestFormActionURL);
554 builder.AddUsernameField("username1", "John", NULL);
555 builder.AddUsernameField("username2", "Smith", NULL);
556 builder.AddSubmitButton("submit", true);
557 std::string html = builder.ProduceHTML();
559 scoped_ptr<PasswordForm> password_form;
560 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form));
561 EXPECT_FALSE(password_form);
564 TEST_F(MAYBE_PasswordFormConversionUtilsTest,
565 InvalidFormsDueToConfusingPasswordFields) {
566 // Each test case consists of a set of parameters to be plugged into the
567 // PasswordFormBuilder below.
568 const char* cases[][3] = {
569 // No autocomplete attributes to guide us, and we see:
570 // * three password values that are all different,
571 // * three password values that are all the same;
572 // * three password values with the first and last matching.
573 // In any case, we should just give up on this form.
574 {"alpha", "beta", "gamma"},
575 {"alpha", "alpha", "alpha"},
576 {"alpha", "beta", "alpha"}};
578 for (size_t i = 0; i < arraysize(cases); ++i) {
579 SCOPED_TRACE(testing::Message() << "Iteration " << i);
581 PasswordFormBuilder builder(kTestFormActionURL);
582 builder.AddUsernameField("username1", "John", NULL);
583 builder.AddPasswordField("password1", cases[i][0], NULL);
584 builder.AddPasswordField("password2", cases[i][1], NULL);
585 builder.AddPasswordField("password3", cases[i][2], NULL);
586 builder.AddSubmitButton("submit", true);
587 std::string html = builder.ProduceHTML();
589 scoped_ptr<PasswordForm> password_form;
590 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form));
591 EXPECT_FALSE(password_form);
595 TEST_F(MAYBE_PasswordFormConversionUtilsTest,
596 InvalidFormDueToTooManyPasswordFieldsWithoutAutocompleteAttributes) {
597 PasswordFormBuilder builder(kTestFormActionURL);
598 builder.AddUsernameField("username1", "John", NULL);
599 builder.AddPasswordField("password1", "alpha", NULL);
600 builder.AddPasswordField("password2", "alpha", NULL);
601 builder.AddPasswordField("password3", "alpha", NULL);
602 builder.AddPasswordField("password4", "alpha", NULL);
603 builder.AddSubmitButton("submit", true);
604 std::string html = builder.ProduceHTML();
606 scoped_ptr<PasswordForm> password_form;
607 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(html, &password_form));
608 EXPECT_FALSE(password_form);
611 TEST_F(MAYBE_PasswordFormConversionUtilsTest, LayoutClassificationLogin) {
612 PasswordFormBuilder builder(kTestFormActionURL);
613 builder.AddHiddenField();
614 builder.AddUsernameField("username", "", nullptr);
615 builder.AddPasswordField("password", "", nullptr);
616 builder.AddSubmitButton("submit", false);
617 std::string login_html = builder.ProduceHTML();
619 scoped_ptr<PasswordForm> login_form;
620 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(login_html, &login_form));
621 ASSERT_TRUE(login_form);
622 EXPECT_EQ(PasswordForm::Layout::LAYOUT_OTHER, login_form->layout);
625 TEST_F(MAYBE_PasswordFormConversionUtilsTest, LayoutClassificationSignup) {
626 PasswordFormBuilder builder(kTestFormActionURL);
627 builder.AddUsernameField("someotherfield", "", nullptr);
628 builder.AddUsernameField("username", "", nullptr);
629 builder.AddPasswordField("new_password", "", nullptr);
630 builder.AddHiddenField();
631 builder.AddPasswordField("new_password2", "", nullptr);
632 builder.AddSubmitButton("submit", false);
633 std::string signup_html = builder.ProduceHTML();
635 scoped_ptr<PasswordForm> signup_form;
636 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(signup_html, &signup_form));
637 ASSERT_TRUE(signup_form);
638 EXPECT_EQ(PasswordForm::Layout::LAYOUT_OTHER, signup_form->layout);
641 TEST_F(MAYBE_PasswordFormConversionUtilsTest, LayoutClassificationChange) {
642 PasswordFormBuilder builder(kTestFormActionURL);
643 builder.AddUsernameField("username", "", nullptr);
644 builder.AddPasswordField("old_password", "", nullptr);
645 builder.AddHiddenField();
646 builder.AddPasswordField("new_password", "", nullptr);
647 builder.AddPasswordField("new_password2", "", nullptr);
648 builder.AddSubmitButton("submit", false);
649 std::string change_html = builder.ProduceHTML();
651 scoped_ptr<PasswordForm> change_form;
652 ASSERT_NO_FATAL_FAILURE(LoadHTMLAndConvertForm(change_html, &change_form));
653 ASSERT_TRUE(change_form);
654 EXPECT_EQ(PasswordForm::Layout::LAYOUT_OTHER, change_form->layout);
657 TEST_F(MAYBE_PasswordFormConversionUtilsTest,
658 LayoutClassificationLoginPlusSignup_A) {
659 PasswordFormBuilder builder(kTestFormActionURL);
660 builder.AddUsernameField("username", "", nullptr);
661 builder.AddHiddenField();
662 builder.AddPasswordField("password", "", nullptr);
663 builder.AddUsernameField("username2", "", nullptr);
664 builder.AddUsernameField("someotherfield", "", nullptr);
665 builder.AddPasswordField("new_password", "", nullptr);
666 builder.AddPasswordField("new_password2", "", nullptr);
667 builder.AddHiddenField();
668 builder.AddSubmitButton("submit", false);
669 std::string login_plus_signup_html = builder.ProduceHTML();
671 scoped_ptr<PasswordForm> login_plus_signup_form;
672 ASSERT_NO_FATAL_FAILURE(
673 LoadHTMLAndConvertForm(login_plus_signup_html, &login_plus_signup_form));
674 ASSERT_TRUE(login_plus_signup_form);
675 EXPECT_EQ(PasswordForm::Layout::LAYOUT_LOGIN_AND_SIGNUP,
676 login_plus_signup_form->layout);
679 TEST_F(MAYBE_PasswordFormConversionUtilsTest,
680 LayoutClassificationLoginPlusSignup_B) {
681 PasswordFormBuilder builder(kTestFormActionURL);
682 builder.AddUsernameField("username", "", nullptr);
683 builder.AddHiddenField();
684 builder.AddPasswordField("password", "", nullptr);
685 builder.AddUsernameField("username2", "", nullptr);
686 builder.AddUsernameField("someotherfield", "", nullptr);
687 builder.AddPasswordField("new_password", "", nullptr);
688 builder.AddUsernameField("someotherfield2", "", nullptr);
689 builder.AddHiddenField();
690 builder.AddSubmitButton("submit", false);
691 std::string login_plus_signup_html = builder.ProduceHTML();
693 scoped_ptr<PasswordForm> login_plus_signup_form;
694 ASSERT_NO_FATAL_FAILURE(
695 LoadHTMLAndConvertForm(login_plus_signup_html, &login_plus_signup_form));
696 ASSERT_TRUE(login_plus_signup_form);
697 EXPECT_EQ(PasswordForm::Layout::LAYOUT_LOGIN_AND_SIGNUP,
698 login_plus_signup_form->layout);
701 } // namespace autofill