Chromium Code Reviews| 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): | |
|
dyu1
2012/08/20 21:23:26
Is this partial test being covered by a browser te
jam
2012/08/20 21:26:37
yes, FormFillLatencyAfterSubmit. I also automated
| |
| 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): | |
|
dyu1
2012/08/20 21:23:26
Same question here as above.
jam
2012/08/20 21:26:37
yes MergeAggregatedDuplicatedProfiles
all the tes
| |
| 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 |