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 package org.chromium.chrome.browser.autofill;
7 import android.test.suitebuilder.annotation.MediumTest;
8 import android.text.TextUtils;
9 import android.view.View;
10 import android.view.ViewGroup;
12 import org.chromium.base.ThreadUtils;
13 import org.chromium.base.test.util.Feature;
14 import org.chromium.base.test.util.UrlUtils;
15 import org.chromium.chrome.R;
16 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
17 import org.chromium.chrome.shell.ChromeShellTestBase;
18 import org.chromium.content.browser.ContentViewCore;
19 import org.chromium.content.browser.test.util.Criteria;
20 import org.chromium.content.browser.test.util.CriteriaHelper;
21 import org.chromium.content.browser.test.util.DOMUtils;
22 import org.chromium.content.browser.test.util.TestInputMethodManagerWrapper;
23 import org.chromium.content.browser.test.util.TouchCommon;
24 import org.chromium.ui.autofill.AutofillPopup;
26 import java.util.ArrayList;
27 import java.util.List;
28 import java.util.concurrent.ExecutionException;
29 import java.util.concurrent.TimeoutException;
32 * Integration tests for the AutofillPopup.
34 public class AutofillPopupTest extends ChromeShellTestBase {
36 private static final String FIRST_NAME = "John";
37 private static final String LAST_NAME = "Smith";
38 private static final String COMPANY_NAME = "Acme Inc.";
39 private static final String ADDRESS_LINE1 = "1 Main";
40 private static final String ADDRESS_LINE2 = "Apt A";
41 private static final String STREET_ADDRESS_TEXTAREA = ADDRESS_LINE1 + "\n" + ADDRESS_LINE2;
42 private static final String CITY = "San Francisco";
43 private static final String DEPENDENT_LOCALITY = "";
44 private static final String STATE = "CA";
45 private static final String ZIP_CODE = "94102";
46 private static final String SORTING_CODE = "";
47 private static final String COUNTRY = "US";
48 private static final String PHONE_NUMBER = "4158889999";
49 private static final String EMAIL = "john@acme.inc";
50 private static final String LANGUAGE_CODE = "";
51 private static final String ORIGIN = "https://www.example.com";
53 private static final String BASIC_PAGE_DATA = UrlUtils.encodeHtmlDataUri(
54 "<html><head><meta name=\"viewport\"" +
55 "content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0\" /></head>" +
56 "<body><form method=\"POST\">" +
57 "<input type=\"text\" id=\"fn\" autocomplete=\"given-name\" /><br>" +
58 "<input type=\"text\" id=\"ln\" autocomplete=\"family-name\" /><br>" +
59 "<textarea id=\"sa\" autocomplete=\"street-address\"></textarea><br>" +
60 "<input type=\"text\" id=\"a1\" autocomplete=\"address-line1\" /><br>" +
61 "<input type=\"text\" id=\"a2\" autocomplete=\"address-line2\" /><br>" +
62 "<input type=\"text\" id=\"ct\" autocomplete=\"locality\" /><br>" +
63 "<input type=\"text\" id=\"zc\" autocomplete=\"postal-code\" /><br>" +
64 "<input type=\"text\" id=\"em\" autocomplete=\"email\" /><br>" +
65 "<input type=\"text\" id=\"ph\" autocomplete=\"tel\" /><br>" +
66 "<input type=\"text\" id=\"fx\" autocomplete=\"fax\" /><br>" +
67 "<select id=\"co\" autocomplete=\"country\"><br>" +
68 "<option value=\"BR\">Brazil</option>" +
69 "<option value=\"US\">United States</option>" +
71 "<input type=\"submit\" />" +
72 "</form></body></html>");
74 private static final String INITIATING_ELEMENT_FILLED = UrlUtils.encodeHtmlDataUri(
75 "<html><head><meta name=\"viewport\"" +
76 "content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0\" /></head>" +
77 "<body><form method=\"POST\">" +
78 "<input type=\"text\" id=\"fn\" autocomplete=\"given-name\" value=\"J\"><br>" +
79 "<input type=\"text\" id=\"ln\" autocomplete=\"family-name\"><br>" +
80 "<input type=\"text\" id=\"em\" autocomplete=\"email\"><br>" +
81 "<select id=\"co\" autocomplete=\"country\"><br>" +
82 "<option value=\"US\">United States</option>" +
83 "<option value=\"BR\">Brazil</option>" +
85 "<input type=\"submit\" />" +
86 "</form></body></html>");
88 private static final String ANOTHER_ELEMENT_FILLED = UrlUtils.encodeHtmlDataUri(
89 "<html><head><meta name=\"viewport\"" +
90 "content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0\" /></head>" +
91 "<body><form method=\"POST\">" +
92 "<input type=\"text\" id=\"fn\" autocomplete=\"given-name\"><br>" +
93 "<input type=\"text\" id=\"ln\" autocomplete=\"family-name\"><br>" +
94 "<input type=\"text\" id=\"em\" autocomplete=\"email\" value=\"foo@example.com\"><br>" +
95 "<select id=\"co\" autocomplete=\"country\"><br>" +
97 "<option value=\"BR\">Brazil</option>" +
98 "<option value=\"US\">United States</option>" +
100 "<input type=\"submit\" />" +
101 "</form></body></html>");
103 private static final String INVALID_OPTION = UrlUtils.encodeHtmlDataUri(
104 "<html><head><meta name=\"viewport\"" +
105 "content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0\" /></head>" +
106 "<body><form method=\"POST\">" +
107 "<input type=\"text\" id=\"fn\" autocomplete=\"given-name\" value=\"J\"><br>" +
108 "<input type=\"text\" id=\"ln\" autocomplete=\"family-name\"><br>" +
109 "<input type=\"text\" id=\"em\" autocomplete=\"email\"><br>" +
110 "<select id=\"co\" autocomplete=\"country\"><br>" +
111 "<option value=\"GB\">Great Britain</option>" +
112 "<option value=\"BR\">Brazil</option>" +
114 "<input type=\"submit\" />" +
115 "</form></body></html>");
117 private AutofillTestHelper mHelper;
118 private List<AutofillLogger.LogEntry> mAutofillLoggedEntries;
121 public void setUp() throws Exception {
124 mAutofillLoggedEntries = new ArrayList<AutofillLogger.LogEntry>();
125 AutofillLogger.setLogger(
126 new AutofillLogger.Logger() {
128 public void didFillField(AutofillLogger.LogEntry logEntry) {
129 mAutofillLoggedEntries.add(logEntry);
134 private void loadAndFillForm(
135 final String formDataUrl, final String inputText)
136 throws InterruptedException, ExecutionException, TimeoutException {
137 launchChromeShellWithUrl(formDataUrl);
138 assertTrue(waitForActiveShellToBeDoneLoading());
139 mHelper = new AutofillTestHelper();
141 // The TestInputMethodManagerWrapper intercepts showSoftInput so that a keyboard is never
143 final ContentViewCore viewCore = getActivity().getActiveContentViewCore();
144 final ViewGroup view = viewCore.getContainerView();
145 final TestInputMethodManagerWrapper immw =
146 new TestInputMethodManagerWrapper(viewCore);
147 viewCore.getImeAdapterForTest().setInputMethodManagerWrapper(immw);
149 // Add an Autofill profile.
150 AutofillProfile profile = new AutofillProfile(
151 "" /* guid */, ORIGIN, FIRST_NAME + " " + LAST_NAME, COMPANY_NAME,
152 STREET_ADDRESS_TEXTAREA,
153 STATE, CITY, DEPENDENT_LOCALITY,
154 ZIP_CODE, SORTING_CODE, COUNTRY, PHONE_NUMBER, EMAIL,
156 mHelper.setProfile(profile);
157 assertEquals(1, mHelper.getNumberOfProfiles());
159 // Click the input field for the first name.
160 assertTrue(DOMUtils.waitForNonZeroNodeBounds(viewCore, "fn"));
161 DOMUtils.clickNode(this, viewCore, "fn");
163 waitForKeyboardShowRequest(immw, 1);
165 ThreadUtils.runOnUiThreadBlocking(new Runnable() {
168 viewCore.getInputConnectionForTest().setComposingText(
173 waitForAnchorViewAdd(view);
174 View anchorView = view.findViewById(R.id.dropdown_popup_window);
176 assertTrue(anchorView.getTag() instanceof AutofillPopup);
177 final AutofillPopup popup = (AutofillPopup) anchorView.getTag();
179 waitForAutofillPopopShow(popup);
181 TouchCommon touchCommon = new TouchCommon(this);
182 touchCommon.singleClickViewRelative(popup.getListView(), 10, 10);
184 waitForInputFieldFill(viewCore);
188 * Tests that bringing up an Autofill and clicking on the first entry fills out the expected
189 * Autofill information.
192 @Feature({"autofill"})
193 public void testClickAutofillPopupSuggestion()
194 throws InterruptedException, ExecutionException, TimeoutException {
195 loadAndFillForm(BASIC_PAGE_DATA, "J");
196 final ContentViewCore viewCore = getActivity().getActiveContentViewCore();
198 assertEquals("First name did not match",
199 FIRST_NAME, DOMUtils.getNodeValue(viewCore, "fn"));
200 assertEquals("Last name did not match",
201 LAST_NAME, DOMUtils.getNodeValue(viewCore, "ln"));
202 assertEquals("Street address (textarea) did not match",
203 STREET_ADDRESS_TEXTAREA, DOMUtils.getNodeValue(viewCore, "sa"));
204 assertEquals("Address line 1 did not match",
205 ADDRESS_LINE1, DOMUtils.getNodeValue(viewCore, "a1"));
206 assertEquals("Address line 2 did not match",
207 ADDRESS_LINE2, DOMUtils.getNodeValue(viewCore, "a2"));
208 assertEquals("City did not match",
209 CITY, DOMUtils.getNodeValue(viewCore, "ct"));
210 assertEquals("Zip code did not match",
211 ZIP_CODE, DOMUtils.getNodeValue(viewCore, "zc"));
212 assertEquals("Country did not match",
213 COUNTRY, DOMUtils.getNodeValue(viewCore, "co"));
214 assertEquals("Email did not match",
215 EMAIL, DOMUtils.getNodeValue(viewCore, "em"));
216 assertEquals("Phone number did not match",
217 PHONE_NUMBER, DOMUtils.getNodeValue(viewCore, "ph"));
219 final String profileFullName = FIRST_NAME + " " + LAST_NAME;
220 final int loggedEntries = 10;
221 assertEquals("Mismatched number of logged entries",
222 loggedEntries, mAutofillLoggedEntries.size());
223 assertLogged(FIRST_NAME, profileFullName);
224 assertLogged(LAST_NAME, profileFullName);
225 assertLogged(STREET_ADDRESS_TEXTAREA, profileFullName);
226 assertLogged(ADDRESS_LINE1, profileFullName);
227 assertLogged(ADDRESS_LINE2, profileFullName);
228 assertLogged(CITY, profileFullName);
229 assertLogged(ZIP_CODE, profileFullName);
230 assertLogged(COUNTRY, profileFullName);
231 assertLogged(EMAIL, profileFullName);
232 assertLogged(PHONE_NUMBER, profileFullName);
236 * Tests that bringing up an Autofill and clicking on the partially filled first
237 * element will still fill the entire form (including the initiating element itself).
240 @Feature({"autofill"})
241 public void testLoggingInitiatedElementFilled()
242 throws InterruptedException, ExecutionException, TimeoutException {
243 loadAndFillForm(INITIATING_ELEMENT_FILLED, "o");
244 final String profileFullName = FIRST_NAME + " " + LAST_NAME;
245 final int loggedEntries = 4;
246 assertEquals("Mismatched number of logged entries",
247 loggedEntries, mAutofillLoggedEntries.size());
248 assertLogged(FIRST_NAME, profileFullName);
249 assertLogged(LAST_NAME, profileFullName);
250 assertLogged(EMAIL, profileFullName);
251 assertLogged(COUNTRY, profileFullName);
255 * Tests that bringing up an Autofill and clicking on the empty first element
256 * will fill the all other elements except the previously filled email.
259 @Feature({"autofill"})
260 public void testLoggingAnotherElementFilled()
261 throws InterruptedException, ExecutionException, TimeoutException {
262 loadAndFillForm(ANOTHER_ELEMENT_FILLED, "J");
263 final String profileFullName = FIRST_NAME + " " + LAST_NAME;
264 final int loggedEntries = 3;
265 assertEquals("Mismatched number of logged entries",
266 loggedEntries, mAutofillLoggedEntries.size());
267 assertLogged(FIRST_NAME, profileFullName);
268 assertLogged(LAST_NAME, profileFullName);
269 assertLogged(COUNTRY, profileFullName);
270 // Email will not be logged since it already had some data.
274 * Tests that selecting a value not present in <option> will not be filled.
277 @Feature({"autofill"})
278 public void testNotLoggingInvalidOption()
279 throws InterruptedException, ExecutionException, TimeoutException {
280 loadAndFillForm(INVALID_OPTION, "o");
281 final String profileFullName = FIRST_NAME + " " + LAST_NAME;
282 final int loggedEntries = 3;
283 assertEquals("Mismatched number of logged entries",
284 loggedEntries, mAutofillLoggedEntries.size());
285 assertLogged(FIRST_NAME, profileFullName);
286 assertLogged(LAST_NAME, profileFullName);
287 assertLogged(EMAIL, profileFullName);
288 // Country will not be logged since "US" is not a valid <option>.
291 // Wait and assert helper methods -------------------------------------------------------------
293 private void waitForKeyboardShowRequest(final TestInputMethodManagerWrapper immw,
294 final int count) throws InterruptedException {
295 assertTrue("Keyboard was never requested to be shown.",
296 CriteriaHelper.pollForCriteria(new Criteria() {
298 public boolean isSatisfied() {
299 return immw.getShowSoftInputCounter() == count;
304 private void waitForAnchorViewAdd(final ViewGroup view) throws InterruptedException {
305 assertTrue("Autofill Popup anchor view was never added.",
306 CriteriaHelper.pollForCriteria(new Criteria() {
308 public boolean isSatisfied() {
309 return view.findViewById(R.id.dropdown_popup_window) != null;
314 private void waitForAutofillPopopShow(final AutofillPopup popup) throws InterruptedException {
315 assertTrue("Autofill Popup anchor view was never added.",
316 CriteriaHelper.pollForCriteria(new Criteria() {
318 public boolean isSatisfied() {
319 return popup.isShowing();
324 private void waitForInputFieldFill(final ContentViewCore viewCore) throws InterruptedException {
325 assertTrue("First name field was never filled.",
326 CriteriaHelper.pollForCriteria(new Criteria() {
328 public boolean isSatisfied() {
330 return TextUtils.equals(FIRST_NAME,
331 DOMUtils.getNodeValue(viewCore, "fn"));
332 } catch (InterruptedException e) {
334 } catch (TimeoutException e) {
342 private void assertLogged(String autofilledValue, String profileFullName) {
343 for (AutofillLogger.LogEntry entry : mAutofillLoggedEntries) {
344 if (entry.getAutofilledValue().equals(autofilledValue) &&
345 entry.getProfileFullName().equals(profileFullName)) {
349 fail("Logged entry not found [" + autofilledValue + "," + profileFullName + "]");