blob: e84eb673cb85ef209fbfeeedcfb372b4a693abba [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/base_paths.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/content/renderer/autofill_renderer_test.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/form_field_data.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/web/web_input_element.h"
#include "third_party/blink/public/web/web_view.h"
using ::testing::_;
using ::testing::AllOf;
using ::testing::AtLeast;
using ::testing::ElementsAre;
using ::testing::Field;
using ::testing::IsEmpty;
using ::testing::Property;
using ::testing::ResultOf;
using ::testing::SizeIs;
using ::testing::Truly;
namespace autofill {
namespace {
auto FormField(
const std::string& name,
const std::string& value = "",
FormControlType form_control_type = FormControlType::kInputText) {
return AllOf(
Property("name", &FormFieldData::name, base::UTF8ToUTF16(name)),
Property("value", &FormFieldData::value, base::UTF8ToUTF16(value)),
Property("form_control_type", &FormFieldData::form_control_type,
form_control_type));
}
auto FormWithFields(auto matcher) {
return ElementsAre(Property(&FormData::fields, matcher));
}
auto Nth(int index, auto matcher) {
return ResultOf(
base::StringPrintf("array[%d]", index),
[index](auto& container) { return container[index]; }, matcher);
}
using AutofillRendererTest = test::AutofillRendererTest;
TEST_F(AutofillRendererTest, SendForms) {
EXPECT_CALL(
autofill_driver(),
FormsSeen(FormWithFields(ElementsAre(
FormField("firstname"), FormField("middlename"),
FormField("lastname"),
FormField("state", "?", FormControlType::kSelectOne))),
IsEmpty()));
LoadHTML(R"(<form method='POST'>
<input type='text' id='firstname'/>
<input type='text' id='middlename'/>
<input type='text' id='lastname' autoComplete='off'/>
<input type='hidden' id='email'/>
<select id='state'/>
<option>?</option>
<option>California</option>
<option>Texas</option>
</select>
</form>)");
WaitForFormsSeen();
// Dynamically create a new form. A new message should be sent for it, but
// not for the previous form.
EXPECT_CALL(autofill_driver(),
FormsSeen(FormWithFields(ElementsAre(
FormField("second_firstname", "Bob"),
FormField("second_lastname", "Hope"),
FormField("second_email", "bobhope@example.com"))),
IsEmpty()));
ExecuteJavaScriptForTests(
R"(var newForm=document.createElement('form');
newForm.id='new_testform';
newForm.action='http://google.com';
newForm.method='post';
var newFirstname=document.createElement('input');
newFirstname.setAttribute('type', 'text');
newFirstname.setAttribute('id', 'second_firstname');
newFirstname.value = 'Bob';
var newLastname=document.createElement('input');
newLastname.setAttribute('type', 'text');
newLastname.setAttribute('id', 'second_lastname');
newLastname.value = 'Hope';
var newEmail=document.createElement('input');
newEmail.setAttribute('type', 'text');
newEmail.setAttribute('id', 'second_email');
newEmail.value = 'bobhope@example.com';
newForm.appendChild(newFirstname);
newForm.appendChild(newLastname);
newForm.appendChild(newEmail);
document.body.appendChild(newForm);)");
WaitForFormsSeen();
}
// Regression test for [ http://crbug.com/346010 ].
// Shouldn't crash.
TEST_F(AutofillRendererTest, DontCrashWhileAssociatingForms) {
EXPECT_CALL(autofill_driver(), FormsSeen(_, _)).Times(0);
LoadHTML(R"(<form id='form'>
<foo id='foo'>
<script id='script'>
document.documentElement.appendChild(foo);
newDoc = document.implementation.createDocument(
'http://www.w3.org/1999/xhtml', 'html');
foo.insertBefore(form, script);
newDoc.adoptNode(foo);
</script>)");
WaitForFormsSeen();
}
TEST_F(AutofillRendererTest, DynamicallyAddedUnownedFormElements) {
base::FilePath test_data_dir;
CHECK(base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &test_data_dir));
base::FilePath test_path = test_data_dir.AppendASCII(
"chrome/test/data/autofill/autofill_noform_dynamic.html");
std::string html_data;
ASSERT_TRUE(base::ReadFileToString(test_path, &html_data));
EXPECT_CALL(autofill_driver(),
FormsSeen(FormWithFields(SizeIs(7)), IsEmpty()));
LoadHTML(html_data.c_str());
WaitForFormsSeen();
EXPECT_CALL(autofill_driver(),
FormsSeen(FormWithFields(AllOf(
SizeIs(9), Nth(7, FormField("EMAIL_ADDRESS")),
Nth(8, FormField("PHONE_HOME_WHOLE_NUMBER")))),
IsEmpty()));
ExecuteJavaScriptForTests("AddFields()");
WaitForFormsSeen();
}
TEST_F(AutofillRendererTest, IgnoreNonUserGestureTextFieldChanges) {
EXPECT_CALL(autofill_driver(), TextFieldValueChanged).Times(0);
LoadHTML(R"(<form method='post'>
<input type='text' id='full_name'/>
</form>)");
WaitForFormsSeen();
blink::WebInputElement full_name = GetMainFrame()
->GetDocument()
.GetElementById("full_name")
.To<blink::WebInputElement>();
while (!full_name.Focused())
GetMainFrame()->View()->AdvanceFocus(false);
full_name.SetValue("Alice", true);
GetMainFrame()->AutofillClient()->TextFieldValueChanged(full_name);
EXPECT_CALL(autofill_driver(), TextFieldValueChanged);
SimulateUserInputChangeForElement(full_name, "Alice");
}
} // namespace
} // namespace autofill