OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 import logging | 6 import logging |
7 import os | 7 import os |
8 import pickle | 8 import pickle |
9 import re | 9 import re |
10 import simplejson | 10 import simplejson |
11 | 11 |
12 import autofill_dataset_converter | |
13 import autofill_dataset_generator | |
14 import pyauto_functional # Must be imported before pyauto | 12 import pyauto_functional # Must be imported before pyauto |
15 import pyauto | 13 import pyauto |
16 import test_utils | 14 import test_utils |
17 from selenium.webdriver.common.keys import Keys | 15 from selenium.webdriver.common.keys import Keys |
18 from selenium.webdriver.common.action_chains import ActionChains | 16 from selenium.webdriver.common.action_chains import ActionChains |
19 from webdriver_pages import settings | 17 from webdriver_pages import settings |
20 | 18 |
21 | 19 |
22 class AutofillTest(pyauto.PyUITest): | 20 class AutofillTest(pyauto.PyUITest): |
23 """Tests that autofill works correctly""" | 21 """Tests that autofill UI works correctly. Also contains a manual test for |
| 22 the crowdsourcing server.""" |
24 | 23 |
25 def setUp(self): | 24 def setUp(self): |
26 pyauto.PyUITest.setUp(self) | 25 pyauto.PyUITest.setUp(self) |
27 self._driver = self.NewWebDriver() | 26 self._driver = self.NewWebDriver() |
28 | 27 |
29 def Debug(self): | |
30 """Test method for experimentation. | |
31 | |
32 This method will not run automatically. | |
33 """ | |
34 while True: | |
35 raw_input('Hit <enter> to dump info.. ') | |
36 self.pprint(self.GetAutofillProfile()) | |
37 | |
38 def testFillProfile(self): | |
39 """Test filling profiles and overwriting with new profiles.""" | |
40 profiles = [{'NAME_FIRST': ['Bob',], | |
41 'NAME_LAST': ['Smith',], 'ADDRESS_HOME_ZIP': ['94043',],}, | |
42 {'EMAIL_ADDRESS': ['sue@example.com',], | |
43 'COMPANY_NAME': ['Company X',],}] | |
44 credit_cards = [{'CREDIT_CARD_NUMBER': '6011111111111117', | |
45 'CREDIT_CARD_EXP_MONTH': '12', | |
46 'CREDIT_CARD_EXP_4_DIGIT_YEAR': '2011'}, | |
47 {'CREDIT_CARD_NAME': 'Bob C. Smith'}] | |
48 | |
49 self.FillAutofillProfile(profiles=profiles, credit_cards=credit_cards) | |
50 profile = self.GetAutofillProfile() | |
51 self.assertEqual(profiles, profile['profiles']) | |
52 self.assertEqual(credit_cards, profile['credit_cards']) | |
53 | |
54 profiles = [ {'NAME_FIRST': ['Larry']}] | |
55 self.FillAutofillProfile(profiles=profiles) | |
56 profile = self.GetAutofillProfile() | |
57 self.assertEqual(profiles, profile['profiles']) | |
58 self.assertEqual(credit_cards, profile['credit_cards']) | |
59 | |
60 def testFillProfileMultiValue(self): | |
61 """Test filling a profile with multi-value data.""" | |
62 profile_expected = [{'NAME_FIRST': ['Bob', 'Joe'], | |
63 'NAME_LAST': ['Smith', 'Jones'], | |
64 'ADDRESS_HOME_ZIP': ['94043',],},] | |
65 | |
66 self.FillAutofillProfile(profiles=profile_expected) | |
67 profile_actual = self.GetAutofillProfile() | |
68 self.assertEqual(profile_expected, profile_actual['profiles']) | |
69 | |
70 def testFillProfileCrazyCharacters(self): | |
71 """Test filling profiles with unicode strings and crazy characters.""" | |
72 # Adding autofill profiles. | |
73 file_path = os.path.join(self.DataDir(), 'autofill', 'functional', | |
74 'crazy_autofill.txt') | |
75 profiles = self.EvalDataFrom(file_path) | |
76 self.FillAutofillProfile(profiles=profiles) | |
77 self.assertEqual(profiles, self.GetAutofillProfile()['profiles'], | |
78 msg='Autofill profile data does not match.') | |
79 | |
80 # Adding credit cards. | |
81 file_path = os.path.join(self.DataDir(), 'autofill', 'functional', | |
82 'crazy_creditcards.txt') | |
83 test_data = self.EvalDataFrom(file_path) | |
84 credit_cards_input = test_data['input'] | |
85 self.FillAutofillProfile(credit_cards=credit_cards_input) | |
86 | |
87 self.assertEqual(test_data['expected'], | |
88 self.GetAutofillProfile()['credit_cards'], | |
89 msg='Autofill credit card data does not match.') | |
90 | |
91 def testGetProfilesEmpty(self): | |
92 """Test getting profiles when none have been filled.""" | |
93 profile = self.GetAutofillProfile() | |
94 self.assertEqual([], profile['profiles']) | |
95 self.assertEqual([], profile['credit_cards']) | |
96 | |
97 def testAutofillInvalid(self): | |
98 """Test filling in invalid values for profiles are saved as-is. | |
99 | |
100 Phone information entered into the prefs UI is not validated or rejected | |
101 except for duplicates. | |
102 """ | |
103 # First try profiles with invalid ZIP input. | |
104 without_invalid = {'NAME_FIRST': ['Will',], | |
105 'ADDRESS_HOME_CITY': ['Sunnyvale',], | |
106 'ADDRESS_HOME_STATE': ['CA',], | |
107 'ADDRESS_HOME_ZIP': ['my_zip',], | |
108 'ADDRESS_HOME_COUNTRY': ['United States',]} | |
109 # Add invalid data for phone field. | |
110 with_invalid = without_invalid.copy() | |
111 with_invalid['PHONE_HOME_WHOLE_NUMBER'] = ['Invalid_Phone_Number',] | |
112 self.FillAutofillProfile(profiles=[with_invalid]) | |
113 self.assertNotEqual( | |
114 [without_invalid], self.GetAutofillProfile()['profiles'], | |
115 msg='Phone data entered into prefs UI is validated.') | |
116 | |
117 def testAutofillPrefsStringSavedAsIs(self): | |
118 """Test invalid credit card numbers typed in prefs should be saved as-is.""" | |
119 credit_card = {'CREDIT_CARD_NUMBER': 'Not_0123-5Checked'} | |
120 self.FillAutofillProfile(credit_cards=[credit_card]) | |
121 self.assertEqual([credit_card], | |
122 self.GetAutofillProfile()['credit_cards'], | |
123 msg='Credit card number in prefs not saved as-is.') | |
124 | |
125 def _WaitForWebpageFormReadyToFillIn(self, form_profile, tab_index, windex): | |
126 """Waits until an autofill form on a webpage is ready to be filled in. | |
127 | |
128 A call to NavigateToURL() may return before all form elements on the page | |
129 are ready to be accessed. This function waits until they are ready to be | |
130 filled in. | |
131 | |
132 Args: | |
133 form_profile: A dictionary representing an autofill profile in which the | |
134 keys are strings corresponding to webpage element IDs. | |
135 tab_index: The index of the tab containing the webpage form to check. | |
136 windex: The index of the window containing the webpage form to check. | |
137 """ | |
138 field_check_code = ''.join( | |
139 ['if (!document.getElementById("%s")) ready = "false";' % | |
140 key for key in form_profile.keys()]) | |
141 js = """ | |
142 var ready = 'true'; | |
143 if (!document.getElementById("testform")) | |
144 ready = 'false'; | |
145 %s | |
146 window.domAutomationController.send(ready); | |
147 """ % field_check_code | |
148 self.assertTrue( | |
149 self.WaitUntil(lambda: self.ExecuteJavascript(js, tab_index, windex), | |
150 expect_retval='true'), | |
151 msg='Timeout waiting for webpage form to be ready to be filled in.') | |
152 | |
153 def _FillFormAndSubmit(self, datalist, filename, tab_index=0, windex=0): | |
154 """Navigate to the form, input values into the fields, and submit the form. | |
155 | |
156 If multiple profile dictionaries are specified as input, this function will | |
157 repeatedly navigate to the form, fill it out, and submit it, once for each | |
158 specified profile dictionary. | |
159 | |
160 Args: | |
161 datalist: A list of dictionaries, where each dictionary represents the | |
162 key/value pairs for profiles or credit card values. | |
163 filename: HTML form website file. The file is the basic file name and not | |
164 the path to the file. File is assumed to be located in | |
165 autofill/functional directory of the data folder. | |
166 tab_index: Integer index of the tab to work on; defaults to 0 (first tab). | |
167 windex: Integer index of the browser window to work on; defaults to 0 | |
168 (first window). | |
169 """ | |
170 url = self.GetHttpURLForDataPath('autofill', 'functional', filename) | |
171 for profile in datalist: | |
172 self.NavigateToURL(url) | |
173 self._WaitForWebpageFormReadyToFillIn(profile, tab_index, windex) | |
174 # Fill in and submit the form. | |
175 js = ''.join(['document.getElementById("%s").value = "%s";' % | |
176 (key, value) for key, value in profile.iteritems()]) | |
177 js += 'document.getElementById("testform").submit();' | |
178 self.SubmitAutofillForm(js, tab_index=tab_index, windex=windex) | |
179 | |
180 def _LuhnCreditCardNumberValidator(self, number): | |
181 """Validates whether a number is valid or invalid using the Luhn test. | |
182 | |
183 Validation example: | |
184 1. Example number: 49927398716 | |
185 2. Reverse the digits: 61789372994 | |
186 3. Sum the digits in the odd-numbered position for s1: | |
187 6 + 7 + 9 + 7 + 9 + 4 = 42 | |
188 4. Take the digits in the even-numbered position: 1, 8, 3, 2, 9 | |
189 4.1. Two times each digit in the even-numbered position: 2, 16, 6, 4, 18 | |
190 4.2. For each resulting value that is now 2 digits, add the digits | |
191 together: 2, 7, 6, 4, 9 | |
192 (0 + 2 = 2, 1 + 6 = 7, 0 + 6 = 6, 0 + 4 = 4, 1 + 8 = 9) | |
193 4.3. Sum together the digits for s2: 2 + 7 + 6 + 4 + 9 = 28 | |
194 5. Sum together s1 + s2 and if the sum ends in zero, the number passes the | |
195 Luhn test: 42 + 28 = 70 which is a valid credit card number. | |
196 | |
197 Args: | |
198 number: the credit card number being validated, as a string. | |
199 | |
200 Returns: | |
201 boolean whether the credit card number is valid or not. | |
202 """ | |
203 # Filters out non-digit characters. | |
204 number = re.sub('[^0-9]', '', number) | |
205 reverse = [int(ch) for ch in str(number)][::-1] | |
206 # The divmod of the function splits a number into two digits, ready for | |
207 # summing. | |
208 return ((sum(reverse[0::2]) + sum(sum(divmod(d*2, 10)) | |
209 for d in reverse[1::2])) % 10 == 0) | |
210 | |
211 def testInvalidCreditCardNumberIsNotAggregated(self): | |
212 """Test credit card info with an invalid number is not aggregated. | |
213 | |
214 When filling out a form with an invalid credit card number (one that | |
215 does not pass the Luhn test) the credit card info should not be saved into | |
216 Autofill preferences. | |
217 """ | |
218 invalid_cc_info = {'CREDIT_CARD_NAME': 'Bob Smith', | |
219 'CREDIT_CARD_NUMBER': '4408 0412 3456 7890', | |
220 'CREDIT_CARD_EXP_MONTH': '12', | |
221 'CREDIT_CARD_EXP_4_DIGIT_YEAR': '2014'} | |
222 | |
223 cc_number = invalid_cc_info['CREDIT_CARD_NUMBER'] | |
224 self._FillFormAndSubmit([invalid_cc_info], 'autofill_creditcard_form.html', | |
225 tab_index=0, windex=0) | |
226 self.assertFalse(self._LuhnCreditCardNumberValidator(cc_number), | |
227 msg='This test requires an invalid credit card number.') | |
228 cc_infobar = self.GetBrowserInfo()['windows'][0]['tabs'][0]['infobars'] | |
229 self.assertFalse( | |
230 cc_infobar, msg='Save credit card infobar offered to save CC info.') | |
231 | |
232 def testWhitespacesAndSeparatorCharsStrippedForValidCCNums(self): | |
233 """Test whitespaces and separator chars are stripped for valid CC numbers. | |
234 | |
235 The credit card numbers used in this test pass the Luhn test. | |
236 For reference: http://www.merriampark.com/anatomycc.htm | |
237 """ | |
238 credit_card_info = [{'CREDIT_CARD_NAME': 'Bob Smith', | |
239 'CREDIT_CARD_NUMBER': '4408 0412 3456 7893', | |
240 'CREDIT_CARD_EXP_MONTH': '12', | |
241 'CREDIT_CARD_EXP_4_DIGIT_YEAR': '2014'}, | |
242 {'CREDIT_CARD_NAME': 'Jane Doe', | |
243 'CREDIT_CARD_NUMBER': '4417-1234-5678-9113', | |
244 'CREDIT_CARD_EXP_MONTH': '10', | |
245 'CREDIT_CARD_EXP_4_DIGIT_YEAR': '2013'}] | |
246 | |
247 url = self.GetHttpURLForDataPath( | |
248 'autofill', 'functional', 'autofill_creditcard_form.html') | |
249 for cc_info in credit_card_info: | |
250 self.assertTrue( | |
251 self._LuhnCreditCardNumberValidator(cc_info['CREDIT_CARD_NUMBER']), | |
252 msg='This test requires a valid credit card number.') | |
253 self.NavigateToURL(url) | |
254 self._WaitForWebpageFormReadyToFillIn(cc_info, 0, 0) | |
255 # Fill in and submit the form. | |
256 js = ''.join(['document.getElementById("%s").value = "%s";' % | |
257 (key, value) for key, value in cc_info.iteritems()]) | |
258 js += 'document.getElementById("testform").submit();' | |
259 self.SubmitAutofillForm(js, tab_index=0, windex=0) | |
260 | |
261 # Verify the filled-in credit card number against the aggregated number. | |
262 aggregated_cc_1 = ( | |
263 self.GetAutofillProfile()['credit_cards'][0]['CREDIT_CARD_NUMBER']) | |
264 aggregated_cc_2 = ( | |
265 self.GetAutofillProfile()['credit_cards'][1]['CREDIT_CARD_NUMBER']) | |
266 self.assertFalse((' ' in aggregated_cc_1 or ' ' in aggregated_cc_2 or | |
267 '-' in aggregated_cc_1 or '-' in aggregated_cc_2), | |
268 msg='Whitespaces or separator chars not stripped.') | |
269 | |
270 def testAggregatesMinValidProfile(self): | |
271 """Test that Autofill aggregates a minimum valid profile. | |
272 | |
273 The minimum required address fields must be specified: First Name, | |
274 Last Name, Address Line 1, City, Zip Code, and State. | |
275 """ | |
276 profile = {'NAME_FIRST': 'Bob', | |
277 'NAME_LAST': 'Smith', | |
278 'ADDRESS_HOME_LINE1': '1234 H St.', | |
279 'ADDRESS_HOME_CITY': 'Mountain View', | |
280 'ADDRESS_HOME_STATE': 'CA', | |
281 'ADDRESS_HOME_ZIP': '95110'} | |
282 self._FillFormAndSubmit( | |
283 [profile], 'duplicate_profiles_test.html', tab_index=0, windex=0) | |
284 self.assertTrue(self.GetAutofillProfile()['profiles'], | |
285 msg='Profile with minimum address values not aggregated.') | |
286 | |
287 def testProfilesNotAggregatedWithNoAddress(self): | |
288 """Test Autofill does not aggregate profiles with no address info. | |
289 | |
290 The minimum required address fields must be specified: First Name, | |
291 Last Name, Address Line 1, City, Zip Code, and State. | |
292 """ | |
293 profile = {'NAME_FIRST': 'Bob', | |
294 'NAME_LAST': 'Smith', | |
295 'EMAIL_ADDRESS': 'bsmith@example.com', | |
296 'COMPANY_NAME': 'Company X', | |
297 'ADDRESS_HOME_CITY': 'Mountain View', | |
298 'PHONE_HOME_WHOLE_NUMBER': '650-555-4567',} | |
299 self._FillFormAndSubmit( | |
300 [profile], 'duplicate_profiles_test.html', tab_index=0, windex=0) | |
301 self.assertFalse(self.GetAutofillProfile()['profiles'], | |
302 msg='Profile with no address info was aggregated.') | |
303 | |
304 def testProfilesNotAggregatedWithInvalidEmail(self): | |
305 """Test Autofill does not aggregate profiles with an invalid email.""" | |
306 profile = {'NAME_FIRST': 'Bob', | |
307 'NAME_LAST': 'Smith', | |
308 'EMAIL_ADDRESS': 'garbage', | |
309 'ADDRESS_HOME_LINE1': '1234 H St.', | |
310 'ADDRESS_HOME_CITY': 'San Jose', | |
311 'ADDRESS_HOME_STATE': 'CA', | |
312 'ADDRESS_HOME_ZIP': '95110', | |
313 'COMPANY_NAME': 'Company X', | |
314 'PHONE_HOME_WHOLE_NUMBER': '408-871-4567',} | |
315 self._FillFormAndSubmit( | |
316 [profile], 'duplicate_profiles_test.html', tab_index=0, windex=0) | |
317 self.assertFalse(self.GetAutofillProfile()['profiles'], | |
318 msg='Profile with invalid email was aggregated.') | |
319 | |
320 def testComparePhoneNumbers(self): | |
321 """Test phone fields parse correctly from a given profile. | |
322 | |
323 The high level key presses execute the following: Select the first text | |
324 field, invoke the autofill popup list, select the first profile within the | |
325 list, and commit to the profile to populate the form. | |
326 """ | |
327 profile_path = os.path.join(self.DataDir(), 'autofill', 'functional', | |
328 'phone_pinput_autofill.txt') | |
329 profile_expected_path = os.path.join( | |
330 self.DataDir(), 'autofill', 'functional', | |
331 'phone_pexpected_autofill.txt') | |
332 profiles = self.EvalDataFrom(profile_path) | |
333 profiles_expected = self.EvalDataFrom(profile_expected_path) | |
334 self.FillAutofillProfile(profiles=profiles) | |
335 url = self.GetHttpURLForDataPath( | |
336 'autofill', 'functional', 'form_phones.html') | |
337 for profile_expected in profiles_expected: | |
338 self.NavigateToURL(url) | |
339 self.assertTrue(self.AutofillPopulateForm('NAME_FIRST'), | |
340 msg='Autofill form could not be populated.') | |
341 form_values = {} | |
342 for key, value in profile_expected.iteritems(): | |
343 js_returning_field_value = ( | |
344 'var field_value = document.getElementById("%s").value;' | |
345 'window.domAutomationController.send(field_value);' | |
346 ) % key | |
347 form_values[key] = self.ExecuteJavascript( | |
348 js_returning_field_value, 0, 0) | |
349 self.assertEqual( | |
350 [form_values[key]], value, | |
351 msg=('Original profile not equal to expected profile at key: "%s"\n' | |
352 'Expected: "%s"\nReturned: "%s"' % ( | |
353 key, value, [form_values[key]]))) | |
354 | |
355 def testProfileSavedWithValidCountryPhone(self): | |
356 """Test profile is saved if phone number is valid in selected country. | |
357 | |
358 The data file contains two profiles with valid phone numbers and two | |
359 profiles with invalid phone numbers from their respective country. | |
360 """ | |
361 profiles_list = self.EvalDataFrom( | |
362 os.path.join(self.DataDir(), 'autofill', 'functional', | |
363 'phonechecker.txt')) | |
364 self._FillFormAndSubmit(profiles_list, 'autofill_test_form.html', | |
365 tab_index=0, windex=0) | |
366 num_profiles = len(self.GetAutofillProfile()['profiles']) | |
367 self.assertEqual(2, num_profiles, | |
368 msg='Expected 2 profiles, but got %d.' % num_profiles) | |
369 | |
370 def testCharsStrippedForAggregatedPhoneNumbers(self): | |
371 """Test aggregated phone numbers are standardized (not saved "as-is").""" | |
372 profiles_list = self.EvalDataFrom( | |
373 os.path.join(self.DataDir(), 'autofill', 'functional', | |
374 'phonecharacters.txt')) | |
375 self._FillFormAndSubmit(profiles_list, 'autofill_test_form.html', | |
376 tab_index=0, windex=0) | |
377 us_phone = self.GetAutofillProfile()[ | |
378 'profiles'][0]['PHONE_HOME_WHOLE_NUMBER'] | |
379 de_phone = self.GetAutofillProfile()[ | |
380 'profiles'][1]['PHONE_HOME_WHOLE_NUMBER'] | |
381 self.assertEqual( | |
382 ['+1 408-871-4567',], us_phone, | |
383 msg='Aggregated US phone number %s not standardized.' % us_phone) | |
384 self.assertEqual( | |
385 ['+49 40/808179000',], de_phone, | |
386 msg='Aggregated Germany phone number %s not standardized.' % de_phone) | |
387 | |
388 def testAppendCountryCodeForAggregatedPhones(self): | |
389 """Test Autofill appends country codes to aggregated phone numbers. | |
390 | |
391 The country code is added for the following case: | |
392 The phone number contains the correct national number size and | |
393 is a valid format. | |
394 """ | |
395 profile = {'NAME_FIRST': 'Bob', | |
396 'NAME_LAST': 'Smith', | |
397 'ADDRESS_HOME_LINE1': '1234 H St.', | |
398 'ADDRESS_HOME_CITY': 'San Jose', | |
399 'ADDRESS_HOME_STATE': 'CA', | |
400 'ADDRESS_HOME_ZIP': '95110', | |
401 'ADDRESS_HOME_COUNTRY': 'Germany', | |
402 'PHONE_HOME_WHOLE_NUMBER': '(08) 450 777-777',} | |
403 | |
404 self._FillFormAndSubmit( | |
405 [profile], 'autofill_test_form.html', tab_index=0, windex=0) | |
406 de_phone = self.GetAutofillProfile()[ | |
407 'profiles'][0]['PHONE_HOME_WHOLE_NUMBER'] | |
408 self.assertEqual( | |
409 '+49', de_phone[0][:3], | |
410 msg='Country code missing from phone number %s.' % de_phone) | |
411 | |
412 def testCCInfoNotStoredWhenAutocompleteOff(self): | |
413 """Test CC info not offered to be saved when autocomplete=off for CC field. | |
414 | |
415 If the credit card number field has autocomplete turned off, then the credit | |
416 card infobar should not offer to save the credit card info. The credit card | |
417 number must be a valid Luhn number. | |
418 """ | |
419 credit_card_info = {'CREDIT_CARD_NAME': 'Bob Smith', | |
420 'CREDIT_CARD_NUMBER': '4408041234567893', | |
421 'CREDIT_CARD_EXP_MONTH': '12', | |
422 'CREDIT_CARD_EXP_4_DIGIT_YEAR': '2014'} | |
423 | |
424 self._FillFormAndSubmit( | |
425 [credit_card_info], 'cc_autocomplete_off_test.html', | |
426 tab_index=0, windex=0) | |
427 cc_infobar = self.GetBrowserInfo()['windows'][0]['tabs'][0]['infobars'] | |
428 self.assertFalse(cc_infobar, | |
429 msg='Save credit card infobar offered to save CC info.') | |
430 | |
431 def testNoAutofillForReadOnlyFields(self): | |
432 """Test that Autofill does not fill in read-only fields.""" | |
433 profile = {'NAME_FIRST': ['Bob',], | |
434 'NAME_LAST': ['Smith',], | |
435 'EMAIL_ADDRESS': ['bsmith@gmail.com',], | |
436 'ADDRESS_HOME_LINE1': ['1234 H St.',], | |
437 'ADDRESS_HOME_CITY': ['San Jose',], | |
438 'ADDRESS_HOME_STATE': ['CA',], | |
439 'ADDRESS_HOME_ZIP': ['95110',], | |
440 'COMPANY_NAME': ['Company X',], | |
441 'PHONE_HOME_WHOLE_NUMBER': ['408-871-4567',],} | |
442 | |
443 self.FillAutofillProfile(profiles=[profile]) | |
444 url = self.GetHttpURLForDataPath( | |
445 'autofill', 'functional', 'read_only_field_test.html') | |
446 self.NavigateToURL(url) | |
447 self.assertTrue(self.AutofillPopulateForm('firstname'), | |
448 msg='Autofill form could not be populated.') | |
449 js_return_readonly_field = ( | |
450 'var field_value = document.getElementById("email").value;' | |
451 'window.domAutomationController.send(field_value);') | |
452 readonly_field_value = self.ExecuteJavascript( | |
453 js_return_readonly_field, 0, 0) | |
454 js_return_addrline1_field = ( | |
455 'var field_value = document.getElementById("address").value;' | |
456 'window.domAutomationController.send(field_value);') | |
457 addrline1_field_value = self.ExecuteJavascript( | |
458 js_return_addrline1_field, 0, 0) | |
459 self.assertNotEqual( | |
460 readonly_field_value, profile['EMAIL_ADDRESS'][0], | |
461 'Autofill filled in value "%s" for a read-only field.' | |
462 % readonly_field_value) | |
463 self.assertEqual( | |
464 addrline1_field_value, profile['ADDRESS_HOME_LINE1'][0], | |
465 'Unexpected value "%s" in the Address field.' % addrline1_field_value) | |
466 | |
467 def testFormFillableOnReset(self): | |
468 """Test form is fillable from a profile after form was reset. | |
469 | |
470 Steps: | |
471 1. Fill form using a saved profile. | |
472 2. Reset the form. | |
473 3. Fill form using a saved profile. | |
474 """ | |
475 profile = {'NAME_FIRST': ['Bob',], | |
476 'NAME_LAST': ['Smith',], | |
477 'EMAIL_ADDRESS': ['bsmith@gmail.com',], | |
478 'ADDRESS_HOME_LINE1': ['1234 H St.',], | |
479 'ADDRESS_HOME_CITY': ['San Jose',], | |
480 'PHONE_HOME_WHOLE_NUMBER': ['4088714567',],} | |
481 | |
482 self.FillAutofillProfile(profiles=[profile]) | |
483 url = self.GetHttpURLForDataPath( | |
484 'autofill', 'functional', 'autofill_test_form.html') | |
485 self.NavigateToURL(url) | |
486 # Fill form using an address profile. | |
487 self.assertTrue(self.AutofillPopulateForm('NAME_FIRST'), | |
488 msg='Autofill form could not be populated.') | |
489 # Reset the form. | |
490 self.ExecuteJavascript('document.getElementById("testform").reset();' | |
491 'window.domAutomationController.send("done");', | |
492 0, 0) | |
493 # Fill in the form using an Autofill profile. | |
494 self.assertTrue(self.AutofillPopulateForm('NAME_FIRST'), | |
495 msg='Autofill form could not be populated.') | |
496 # Verify value in fields match value in the profile dictionary. | |
497 form_values = {} | |
498 for key, value in profile.iteritems(): | |
499 js_returning_field_value = ( | |
500 'var field_value = document.getElementById("%s").value;' | |
501 'window.domAutomationController.send(field_value);' | |
502 ) % key | |
503 form_values[key] = self.ExecuteJavascript( | |
504 js_returning_field_value, 0, 0) | |
505 self.assertEqual( | |
506 [form_values[key]], value, | |
507 msg=('Original profile not equal to expected profile at key: "%s"\n' | |
508 'Expected: "%s"\nReturned: "%s"' % ( | |
509 key, value, [form_values[key]]))) | |
510 | |
511 def testDistinguishMiddleInitialWithinName(self): | |
512 """Test Autofill distinguishes a middle initial in a name.""" | |
513 profile = {'NAME_FIRST': ['Bob',], | |
514 'NAME_MIDDLE': ['Leo',], | |
515 'NAME_LAST': ['Smith',], | |
516 'EMAIL_ADDRESS': ['bsmith@gmail.com',], | |
517 'ADDRESS_HOME_LINE1': ['1234 H St.',], | |
518 'ADDRESS_HOME_CITY': ['San Jose',], | |
519 'PHONE_HOME_WHOLE_NUMBER': ['4088714567',],} | |
520 | |
521 middle_initial = profile['NAME_MIDDLE'][0][0] | |
522 self.FillAutofillProfile(profiles=[profile]) | |
523 url = self.GetHttpURLForDataPath( | |
524 'autofill', 'functional', 'autofill_middleinit_form.html') | |
525 self.NavigateToURL(url) | |
526 # Fill form using an address profile. | |
527 self.assertTrue(self.AutofillPopulateForm('NAME_FIRST'), | |
528 msg='Autofill form could not be populated.') | |
529 js_return_middleinit_field = ( | |
530 'var field_value = document.getElementById("NAME_MIDDLE").value;' | |
531 'window.domAutomationController.send(field_value);') | |
532 middleinit_field_value = self.ExecuteJavascript( | |
533 js_return_middleinit_field, 0, 0) | |
534 self.assertEqual(middleinit_field_value, middle_initial, | |
535 msg=('Middle initial "%s" not distinguished from "%s".' % | |
536 (middleinit_field_value, profile['NAME_MIDDLE']))) | |
537 | |
538 def testMultipleEmailFilledByOneUserGesture(self): | |
539 """Test forms with multiple email addresses are filled properly. | |
540 | |
541 Entire form should be filled with one user gesture. | |
542 """ | |
543 profile = {'NAME_FIRST': ['Bob',], | |
544 'NAME_LAST': ['Smith',], | |
545 'EMAIL_ADDRESS': ['bsmith@gmail.com',], | |
546 'PHONE_HOME_WHOLE_NUMBER': ['4088714567',],} | |
547 | |
548 self.FillAutofillProfile(profiles=[profile]) | |
549 url = self.GetHttpURLForDataPath( | |
550 'autofill', 'functional', 'autofill_confirmemail_form.html') | |
551 self.NavigateToURL(url) | |
552 # Fill form using an address profile. | |
553 self.assertTrue(self.AutofillPopulateForm('NAME_FIRST'), | |
554 msg='Autofill form could not be populated.') | |
555 js_return_confirmemail_field = ( | |
556 'var field_value = document.getElementById("EMAIL_CONFIRM").value;' | |
557 'window.domAutomationController.send(field_value);') | |
558 confirmemail_field_value = self.ExecuteJavascript( | |
559 js_return_confirmemail_field, 0, 0) | |
560 self.assertEqual([confirmemail_field_value], profile['EMAIL_ADDRESS'], | |
561 msg=('Confirmation Email address "%s" not equal to Email\n' | |
562 'address "%s".' % ([confirmemail_field_value], | |
563 profile['EMAIL_ADDRESS']))) | |
564 | |
565 def testProfileWithEmailInOtherFieldNotSaved(self): | |
566 """Test profile not aggregated if email found in non-email field.""" | |
567 profile = {'NAME_FIRST': 'Bob', | |
568 'NAME_LAST': 'Smith', | |
569 'ADDRESS_HOME_LINE1': 'bsmith@gmail.com', | |
570 'ADDRESS_HOME_CITY': 'San Jose', | |
571 'ADDRESS_HOME_STATE': 'CA', | |
572 'ADDRESS_HOME_ZIP': '95110', | |
573 'COMPANY_NAME': 'Company X', | |
574 'PHONE_HOME_WHOLE_NUMBER': '408-871-4567',} | |
575 self._FillFormAndSubmit( | |
576 [profile], 'duplicate_profiles_test.html', tab_index=0, windex=0) | |
577 self.assertFalse(self.GetAutofillProfile()['profiles'], | |
578 msg='Profile with email in a non-email field was ' | |
579 'aggregated.') | |
580 | |
581 def FormFillLatencyAfterSubmit(self): | |
582 """Test latency time on form submit with lots of stored Autofill profiles. | |
583 | |
584 This test verifies when a profile is selected from the Autofill dictionary | |
585 that consists of thousands of profiles, the form does not hang after being | |
586 submitted. | |
587 | |
588 The high level key presses execute the following: Select the first text | |
589 field, invoke the autofill popup list, select the first profile within the | |
590 list, and commit to the profile to populate the form. | |
591 | |
592 This test is partially automated. The bulk of the work is done, such as | |
593 generating 1500 plus profiles, inserting those profiles into Autofill, | |
594 selecting a profile from the list. The tester will need to click on the | |
595 submit button and check if the browser hangs. | |
596 """ | |
597 # HTML file needs to be run from a http:// url. | |
598 url = self.GetHttpURLForDataPath( | |
599 'autofill', 'functional', 'latency_after_submit_test.html') | |
600 # Run the generator script to generate the dictionary list needed for the | |
601 # profiles. | |
602 gen = autofill_dataset_generator.DatasetGenerator( | |
603 logging_level=logging.ERROR) | |
604 list_of_dict = gen.GenerateDataset(num_of_dict_to_generate=1501) | |
605 self.FillAutofillProfile(profiles=list_of_dict) | |
606 self.NavigateToURL(url) | |
607 self.assertTrue(self.AutofillPopulateForm('NAME_FIRST'), | |
608 msg='Autofill form could not be populated.') | |
609 # TODO(dyu): add automated form hang or crash verification. | |
610 raw_input( | |
611 'Verify the test manually. Test hang time after submitting the form.') | |
612 | |
613 | |
614 def AutofillCrowdsourcing(self): | 28 def AutofillCrowdsourcing(self): |
615 """Test able to send POST request of web form to Autofill server. | 29 """Test able to send POST request of web form to Autofill server. |
616 | 30 |
617 The Autofill server processes the data offline, so it can take a few days | 31 The Autofill server processes the data offline, so it can take a few days |
618 for the result to be detectable. Manual verification is required. | 32 for the result to be detectable. Manual verification is required. |
619 """ | 33 """ |
620 # HTML file needs to be run from a specific http:// url to be able to verify | 34 # HTML file needs to be run from a specific http:// url to be able to verify |
621 # the results a few days later by visiting the same url. | 35 # the results a few days later by visiting the same url. |
622 url = 'http://www.corp.google.com/~dyu/autofill/crowdsourcing-test.html' | 36 url = 'http://www.corp.google.com/~dyu/autofill/crowdsourcing-test.html' |
623 # Adding crowdsourcing Autofill profile. | |
624 file_path = os.path.join(self.DataDir(), 'autofill', 'functional', | |
625 'crowdsource_autofill.txt') | |
626 profiles = self.EvalDataFrom(file_path) | |
627 self.FillAutofillProfile(profiles=profiles) | |
628 # Autofill server captures 2.5% of the data posted. | 37 # Autofill server captures 2.5% of the data posted. |
629 # Looping 1000 times is a safe minimum to exceed the server's threshold or | 38 # Looping 1000 times is a safe minimum to exceed the server's threshold or |
630 # noise. | 39 # noise. |
631 for i in range(1000): | 40 for i in range(1000): |
632 fname = self.GetAutofillProfile()['profiles'][0]['NAME_FIRST'][0] | 41 fname = 'David' |
633 lname = self.GetAutofillProfile()['profiles'][0]['NAME_LAST'][0] | 42 lname = 'Yu' |
634 email = self.GetAutofillProfile()['profiles'][0]['EMAIL_ADDRESS'][0] | 43 email = 'david.yu@gmail.com' |
635 # Submit form to collect crowdsourcing data for Autofill. | 44 # Submit form to collect crowdsourcing data for Autofill. |
636 self.NavigateToURL(url, 0, 0) | 45 self.NavigateToURL(url, 0, 0) |
637 profile = {'fn': fname, 'ln': lname, 'em': email} | 46 profile = {'fn': fname, 'ln': lname, 'em': email} |
638 self._WaitForWebpageFormReadyToFillIn(profile, 0, 0) | |
639 js = ''.join(['document.getElementById("%s").value = "%s";' % | 47 js = ''.join(['document.getElementById("%s").value = "%s";' % |
640 (key, value) for key, value in profile.iteritems()]) | 48 (key, value) for key, value in profile.iteritems()]) |
641 js += 'document.getElementById("testform").submit();' | 49 js += 'document.getElementById("testform").submit();' |
642 self.SubmitAutofillForm(js, tab_index=0, windex=0) | 50 self.ExecuteJavascript(js) |
643 | |
644 def testSameAddressProfilesAddInPrefsDontMerge(self): | |
645 """Test profiles added through prefs with same address do not merge.""" | |
646 profileA = {'NAME_FIRST': ['John',], | |
647 'NAME_LAST': ['Doe',], | |
648 'ADDRESS_HOME_LINE1': ['123 Cherry St',], | |
649 'ADDRESS_HOME_CITY': ['Mountain View',], | |
650 'ADDRESS_HOME_STATE': ['CA',], | |
651 'ADDRESS_HOME_ZIP': ['94043',], | |
652 'PHONE_HOME_WHOLE_NUMBER': ['650-555-1234',],} | |
653 profileB = {'NAME_FIRST': ['Jane',], | |
654 'NAME_LAST': ['Smith',], | |
655 'ADDRESS_HOME_LINE1': ['123 Cherry St',], | |
656 'ADDRESS_HOME_CITY': ['Mountain View',], | |
657 'ADDRESS_HOME_STATE': ['CA',], | |
658 'ADDRESS_HOME_ZIP': ['94043',], | |
659 'PHONE_HOME_WHOLE_NUMBER': ['650-253-1234',],} | |
660 | |
661 profiles_list = [profileA, profileB] | |
662 self.FillAutofillProfile(profiles=profiles_list) | |
663 self.assertEqual(2, len(self.GetAutofillProfile()['profiles']), | |
664 msg='Profiles in prefs with same address merged.') | |
665 | |
666 def testMergeAggregatedProfilesWithSameAddress(self): | |
667 """Test that profiles merge for aggregated data with same address. | |
668 | |
669 The criterion for when two profiles are expected to be merged is when their | |
670 'Address Line 1' and 'City' data match. When two profiles are merged, any | |
671 remaining address fields are expected to be overwritten. Any non-address | |
672 fields should accumulate multi-valued data. | |
673 """ | |
674 self._AggregateProfilesIntoAutofillPrefs('dataset_2.txt') | |
675 # Expecting 3 profiles out of the original 14 within Autofill preferences | |
676 self.assertEqual(3, len(self.GetAutofillProfile()['profiles']), | |
677 msg='Aggregated profiles did not merge correctly.') | |
678 | |
679 def testProfilesNotMergedWhenNoMinAddressData(self): | |
680 """Test profiles are not merged without mininum address values. | |
681 | |
682 Mininum address values needed during aggregation are: address line 1, city, | |
683 state, and zip code. | |
684 | |
685 Profiles are merged when data for address line 1 and city match. | |
686 """ | |
687 self._AggregateProfilesIntoAutofillPrefs('dataset_no_address.txt') | |
688 self.assertFalse(self.GetAutofillProfile()['profiles'], | |
689 msg='Profile with no min address data was merged.') | |
690 | |
691 def MergeAggregatedDuplicatedProfiles(self): | |
692 """Test Autofill ability to merge duplicate profiles and throw away junk.""" | |
693 num_of_profiles = self._AggregateProfilesIntoAutofillPrefs('dataset.txt') | |
694 # Verify total number of inputted profiles is greater than the final number | |
695 # of profiles after merging. | |
696 self.assertTrue( | |
697 num_of_profiles > len(self.GetAutofillProfile()['profiles'])) | |
698 | |
699 def _AggregateProfilesIntoAutofillPrefs(self, data): | |
700 """Aggregate profiles from forms into Autofill preferences. | |
701 | |
702 Args: | |
703 data: Name of the data set file. | |
704 | |
705 Returns: | |
706 Number of profiles in the dictionary list. | |
707 """ | |
708 # HTML file needs to be run from a http:// url. | |
709 url = self.GetHttpURLForDataPath( | |
710 'autofill', 'functional', 'duplicate_profiles_test.html') | |
711 # Run the parser script to generate the dictionary list needed for the | |
712 # profiles. | |
713 c = autofill_dataset_converter.DatasetConverter( | |
714 os.path.abspath( | |
715 os.path.join(self.DataDir(), 'autofill', 'functional', data)), | |
716 logging_level=logging.INFO) # Set verbosity to INFO, WARNING, ERROR. | |
717 list_of_dict = c.Convert() | |
718 | |
719 for profile in list_of_dict: | |
720 self.NavigateToURL(url) | |
721 self._WaitForWebpageFormReadyToFillIn(profile, 0, 0) | |
722 js = ''.join(['document.getElementById("%s").value = "%s";' % | |
723 (key, value) for key, value in profile.iteritems()]) | |
724 js += 'document.getElementById("testform").submit();' | |
725 self.SubmitAutofillForm(js, tab_index=0, windex=0) | |
726 return len(list_of_dict) | |
727 | 51 |
728 def _SelectOptionXpath(self, value): | 52 def _SelectOptionXpath(self, value): |
729 """Returns an xpath query used to select an item from a dropdown list. | 53 """Returns an xpath query used to select an item from a dropdown list. |
730 Args: | 54 Args: |
731 value: Option selected for the drop-down list field. | 55 value: Option selected for the drop-down list field. |
732 | 56 |
733 Returns: | 57 Returns: |
734 The value of the xpath query. | 58 The value of the xpath query. |
735 """ | 59 """ |
736 return '//option[@value="%s"]' % value | 60 return '//option[@value="%s"]' % value |
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
892 'Field expected to receive focus: "%s"\n' | 216 'Field expected to receive focus: "%s"\n' |
893 'Field that received focus instead: "%s"') | 217 'Field that received focus instead: "%s"') |
894 % (self._GetElementInfo(field), | 218 % (self._GetElementInfo(field), |
895 self._GetElementInfo(field_nextfield_dict[field]), | 219 self._GetElementInfo(field_nextfield_dict[field]), |
896 self._GetElementInfo( | 220 self._GetElementInfo( |
897 self._driver.switch_to_active_element()))) | 221 self._driver.switch_to_active_element()))) |
898 | 222 |
899 | 223 |
900 if __name__ == '__main__': | 224 if __name__ == '__main__': |
901 pyauto_functional.Main() | 225 pyauto_functional.Main() |
OLD | NEW |