1 #!/usr/bin/env python 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 4 # found in the LICENSE file. 5 6 import os 7 from urlparse import urlparse 8 9 import pyauto_functional # Must be imported before pyauto 10 import pyauto 11 import test_utils 12 from webdriver_pages import settings 13 14 15 class PasswordTest(pyauto.PyUITest): 16 """Tests that passwords work correctly.""" 17 18 INFOBAR_TYPE = 'password_infobar' 19 URL = 'https://accounts.google.com/ServiceLogin' 20 URL_HTTPS = 'https://accounts.google.com/Login' 21 URL_LOGOUT = 'https://accounts.google.com/Logout' 22 HOSTNAME = 'https://' + urlparse(URL).netloc + '/' 23 USERNAME_ELEM = 'Email' 24 PASSWORD_ELEM = 'Passwd' 25 USERNAME = 'test (at] google.com' 26 PASSWORD = 'test.password' 27 28 def Debug(self): 29 """Test method for experimentation. 30 31 This method will not run automatically. 32 """ 33 while True: 34 raw_input('Interact with the browser and hit <enter> to dump passwords. ') 35 print '*' * 20 36 self.pprint(self.GetSavedPasswords()) 37 38 def setUp(self): 39 pyauto.PyUITest.setUp(self) 40 self.assertFalse(self.GetSavedPasswords()) 41 42 def _AssertWithinOneSecond(self, time1, time2): 43 self.assertTrue(abs(time1 - time2) < 1.0, 44 'Times not within an acceptable range. ' 45 'First was %lf, second was %lf' % (time1, time2)) 46 47 def _ConstructPasswordDictionary(self, username_value, password_value, 48 signon_realm, origin_url, username_element, 49 password_element, action_target, 50 time=1279650942.0, submit_element='submit', 51 blacklist=False): 52 """Construct a password dictionary with all the required fields.""" 53 return {'username_value': username_value, 54 'password_value': password_value, 55 'signon_realm': signon_realm, 56 'time': time, 57 'origin_url': origin_url, 58 'username_element': username_element, 59 'password_element': password_element, 60 'submit_element': submit_element, 61 'action_target': action_target, 62 'blacklist': blacklist} 63 64 def _ClickOnLoginPage(self, window_index, tab_index): 65 # In some cases (such as on Windows) the current page displays an account 66 # name and e-mail, rather than an e-mail and password. Clicking on a 67 # particular DOM element causes the e-mail and password to be displayed. 68 click_js = """ 69 var elements = document.getElementsByClassName("accounts"); 70 if (elements && elements.length > 0) { 71 elements = elements[0].getElementsByTagName("p"); 72 if (elements && elements.length > 0) 73 elements[0].onclick(); 74 } 75 window.domAutomationController.send("done"); 76 """ 77 self.ExecuteJavascript(click_js, tab_index, window_index) 78 79 # Wait until username/password is filled by the Password manager on the 80 # login page. 81 js_template = """ 82 var value = ""; 83 var element = document.getElementById("%s"); 84 if (element) 85 value = element.value; 86 window.domAutomationController.send(value); 87 """ 88 self.assertTrue(self.WaitUntil( 89 lambda: self.ExecuteJavascript(js_template % self.USERNAME_ELEM, 90 tab_index, window_index) != '' and 91 self.ExecuteJavascript(js_template % self.PASSWORD_ELEM, 92 tab_index, window_index) != '')) 93 94 def testSavePassword(self): 95 """Test saving a password and getting saved passwords.""" 96 password1 = self._ConstructPasswordDictionary( 97 'user (at] example.com', 'test.password', 98 'https://www.example.com/', 'https://www.example.com/login', 99 'username', 'password', 'https://www.example.com/login/') 100 self.assertTrue(self.AddSavedPassword(password1)) 101 self.assertEqual(self.GetSavedPasswords(), [password1]) 102 103 def testRemovePasswords(self): 104 """Verify that saved passwords can be removed.""" 105 password1 = self._ConstructPasswordDictionary( 106 'user1 (at] example.com', 'test1.password', 107 'https://www.example.com/', 'https://www.example.com/login', 108 'username1', 'password', 'https://www.example.com/login/') 109 password2 = self._ConstructPasswordDictionary( 110 'user2 (at] example.com', 'test2.password', 111 'https://www.example.com/', 'https://www.example.com/login', 112 'username2', 'password2', 'https://www.example.com/login/') 113 self.AddSavedPassword(password1) 114 self.AddSavedPassword(password2) 115 self.assertEquals(2, len(self.GetSavedPasswords())) 116 self.assertEquals([password1, password2], self.GetSavedPasswords()) 117 self.RemoveSavedPassword(password1) 118 self.assertEquals(1, len(self.GetSavedPasswords())) 119 self.assertEquals([password2], self.GetSavedPasswords()) 120 self.RemoveSavedPassword(password2) 121 # TODO: GetSavedPasswords() doesn't return anything when empty. 122 # http://crbug.com/64603 123 # self.assertFalse(self.GetSavedPasswords()) 124 125 def testDisplayAndSavePasswordInfobar(self): 126 """Verify password infobar displays and able to save password.""" 127 creds = self.GetPrivateInfo()['test_google_account'] 128 username = creds['username'] 129 password = creds['password'] 130 # Disable one-click login infobar for sync. 131 self.SetPrefs(pyauto.kReverseAutologinEnabled, False) 132 test_utils.GoogleAccountsLogin(self, username, password) 133 # Wait until page completes loading. 134 self.WaitUntil( 135 lambda: self.GetDOMValue('document.readyState'), 136 expect_retval='complete') 137 self.PerformActionOnInfobar( 138 'accept', infobar_index=test_utils.WaitForInfobarTypeAndGetIndex( 139 self, self.INFOBAR_TYPE)) 140 self.NavigateToURL(self.URL_LOGOUT) 141 self.NavigateToURL(self.URL_HTTPS) 142 self._ClickOnLoginPage(0, 0) 143 test_utils.VerifyGoogleAccountCredsFilled(self, username, password, 144 tab_index=0, windex=0) 145 146 def testNeverSavePasswords(self): 147 """Verify passwords not saved/deleted when 'never for this site' chosen.""" 148 creds1 = self.GetPrivateInfo()['test_google_account'] 149 # Disable one-click login infobar for sync. 150 self.SetPrefs(pyauto.kReverseAutologinEnabled, False) 151 test_utils.GoogleAccountsLogin( 152 self, creds1['username'], creds1['password']) 153 self.PerformActionOnInfobar( 154 'accept', infobar_index=test_utils.WaitForInfobarTypeAndGetIndex( 155 self, self.INFOBAR_TYPE)) 156 self.assertEquals(1, len(self.GetSavedPasswords())) 157 self.AppendTab(pyauto.GURL(creds1['logout_url'])) 158 creds2 = self.GetPrivateInfo()['test_google_account_2'] 159 test_utils.GoogleAccountsLogin( 160 self, creds2['username'], creds2['password'], tab_index=1) 161 # Selecting 'Never for this site' option on password infobar. 162 self.PerformActionOnInfobar( 163 'cancel', infobar_index=test_utils.WaitForInfobarTypeAndGetIndex( 164 self, self.INFOBAR_TYPE, tab_index=1), tab_index=1) 165 166 # TODO: GetSavedPasswords() doesn't return anything when empty. 167 # http://crbug.com/64603 168 # self.assertFalse(self.GetSavedPasswords()) 169 # TODO: Check the exceptions list 170 171 def testSavedPasswordInTabsAndWindows(self): 172 """Verify saved username/password shows in window and tab.""" 173 creds = self.GetPrivateInfo()['test_google_account'] 174 username = creds['username'] 175 password = creds['password'] 176 # Disable one-click login infobar for sync. 177 self.SetPrefs(pyauto.kReverseAutologinEnabled, False) 178 # Login to Google a/c 179 test_utils.GoogleAccountsLogin(self, username, password) 180 self.PerformActionOnInfobar( 181 'accept', infobar_index=test_utils.WaitForInfobarTypeAndGetIndex( 182 self, self.INFOBAR_TYPE)) 183 self.NavigateToURL(self.URL_LOGOUT) 184 self.NavigateToURL(self.URL) 185 self._ClickOnLoginPage(0, 0) 186 test_utils.VerifyGoogleAccountCredsFilled(self, username, password, 187 tab_index=0, windex=0) 188 self.AppendTab(pyauto.GURL(self.URL)) 189 self._ClickOnLoginPage(0, 1) 190 test_utils.VerifyGoogleAccountCredsFilled(self, username, password, 191 tab_index=1, windex=0) 192 193 def testLoginCredsNotShownInIncognito(self): 194 """Verify login creds are not shown in Incognito mode.""" 195 creds = self.GetPrivateInfo()['test_google_account'] 196 username = creds['username'] 197 password = creds['password'] 198 # Disable one-click login infobar for sync. 199 self.SetPrefs(pyauto.kReverseAutologinEnabled, False) 200 # Login to Google account. 201 test_utils.GoogleAccountsLogin(self, username, password) 202 self.PerformActionOnInfobar( 203 'accept', infobar_index=test_utils.WaitForInfobarTypeAndGetIndex( 204 self, self.INFOBAR_TYPE)) 205 self.NavigateToURL(self.URL_LOGOUT) 206 self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW) 207 self.NavigateToURL(self.URL, 1, 0) 208 email_value = self.GetDOMValue('document.getElementById("Email").value', 209 tab_index=0, windex=1) 210 passwd_value = self.GetDOMValue('document.getElementById("Passwd").value', 211 tab_index=0, windex=1) 212 self.assertEqual(email_value, '', 213 msg='Email creds displayed %s.' % email_value) 214 self.assertEqual(passwd_value, '', msg='Password creds displayed.') 215 216 def testPasswordAutofilledInIncognito(self): 217 """Verify saved password is autofilled in Incognito mode. 218 219 Saved passwords should be autofilled once the username is entered in 220 incognito mode. 221 """ 222 action_target = self.HOSTNAME 223 224 driver = self.NewWebDriver() 225 password_dict = self._ConstructPasswordDictionary( 226 self.USERNAME, self.PASSWORD, self.HOSTNAME, self.URL, 227 self.USERNAME_ELEM, self.PASSWORD_ELEM, action_target) 228 self.AddSavedPassword(password_dict) 229 self.RunCommand(pyauto.IDC_NEW_INCOGNITO_WINDOW) 230 self.NavigateToURL(self.URL, 1, 0) 231 # Switch to window 1. 232 driver.switch_to_window(driver.window_handles[1]) 233 driver.find_element_by_id( 234 self.USERNAME_ELEM).send_keys(self.USERNAME + '\t') 235 incognito_passwd = self.GetDOMValue( 236 'document.getElementById("Passwd").value', tab_index=0, windex=1) 237 self.assertEqual(incognito_passwd, self.PASSWORD, 238 msg='Password creds did not autofill in incognito mode.') 239 240 def testInfoBarDisappearByNavigatingPage(self): 241 """Test password infobar is dismissed when navigating to different page.""" 242 creds = self.GetPrivateInfo()['test_google_account'] 243 # Disable one-click login infobar for sync. 244 self.SetPrefs(pyauto.kReverseAutologinEnabled, False) 245 # Login to Google account. 246 test_utils.GoogleAccountsLogin(self, creds['username'], creds['password']) 247 self.PerformActionOnInfobar( 248 'accept', infobar_index=test_utils.WaitForInfobarTypeAndGetIndex( 249 self, self.INFOBAR_TYPE)) 250 self.NavigateToURL('chrome://version') 251 self.assertTrue(self.WaitForInfobarCount(0)) 252 # To make sure user is navigated to Version page. 253 self.assertTrue(self.WaitUntil(self.GetActiveTabTitle, 254 expect_retval='About Version')) 255 test_utils.AssertInfobarTypeDoesNotAppear(self, self.INFOBAR_TYPE) 256 257 def testInfoBarDisappearByReload(self): 258 """Test that Password infobar disappears by the page reload.""" 259 creds = self.GetPrivateInfo()['test_google_account'] 260 # Disable one-click login infobar for sync. 261 self.SetPrefs(pyauto.kReverseAutologinEnabled, False) 262 # Login to Google a/c 263 test_utils.GoogleAccountsLogin(self, creds['username'], creds['password']) 264 self.PerformActionOnInfobar( 265 'accept', infobar_index=test_utils.WaitForInfobarTypeAndGetIndex( 266 self, self.INFOBAR_TYPE)) 267 self.ReloadTab() 268 test_utils.AssertInfobarTypeDoesNotAppear(self, self.INFOBAR_TYPE) 269 270 def testPasswdInfoNotStoredWhenAutocompleteOff(self): 271 """Verify that password infobar does not appear when autocomplete is off. 272 273 If the password field has autocomplete turned off, then the password infobar 274 should not offer to save the password info. 275 """ 276 password_info = {'Email': self.USERNAME, 277 'Passwd': self.PASSWORD} 278 279 # Disable one-click login infobar for sync. 280 self.SetPrefs(pyauto.kReverseAutologinEnabled, False) 281 url = self.GetHttpURLForDataPath( 282 os.path.join('password', 'password_autocomplete_off_test.html')) 283 self.NavigateToURL(url) 284 for key, value in password_info.iteritems(): 285 script = ('document.getElementById("%s").value = "%s"; ' 286 'window.domAutomationController.send("done");') % (key, value) 287 self.ExecuteJavascript(script, 0, 0) 288 self.assertTrue(self.SubmitForm('loginform')) 289 test_utils.AssertInfobarTypeDoesNotAppear(self, self.INFOBAR_TYPE) 290 291 def _SendCharToPopulateField(self, char, tab_index=0, windex=0): 292 """Simulate a char being typed into a field. 293 294 Args: 295 char: the char value to be typed into the field. 296 tab_index: tab index to work on. Defaults to 0 (first tab). 297 windex: window index to work on. Defaults to 0 (first window). 298 """ 299 CHAR_KEYPRESS = ord((char).upper()) # ASCII char key press. 300 KEY_DOWN_TYPE = 0 # kRawKeyDownType 301 KEY_UP_TYPE = 3 # kKeyUpType 302 303 self.SendWebkitKeyEvent(KEY_DOWN_TYPE, CHAR_KEYPRESS, tab_index, windex) 304 self.SendWebkitCharEvent(char, tab_index, windex) 305 self.SendWebkitKeyEvent(KEY_UP_TYPE, CHAR_KEYPRESS, tab_index, windex) 306 307 def testClearFetchedCredForNewUserName(self): 308 """Verify that the fetched credentials are cleared for a new username. 309 310 This test requires sending key events rather than pasting a new username 311 into the Email field. 312 """ 313 creds = self.GetPrivateInfo()['test_google_account'] 314 username = creds['username'] 315 password = creds['password'] 316 # Disable one-click login infobar for sync. 317 self.SetPrefs(pyauto.kReverseAutologinEnabled, False) 318 # Login to Google a/c 319 test_utils.GoogleAccountsLogin(self, username, password) 320 self.PerformActionOnInfobar( 321 'accept', infobar_index=test_utils.WaitForInfobarTypeAndGetIndex( 322 self, self.INFOBAR_TYPE)) 323 self.NavigateToURL(self.URL_LOGOUT) 324 self.NavigateToURL(self.URL) 325 self._ClickOnLoginPage(0, 0) 326 test_utils.VerifyGoogleAccountCredsFilled(self, username, password, 327 tab_index=0, windex=0) 328 clear_username_field = ( 329 'document.getElementById("Email").value = ""; ' 330 'window.domAutomationController.send("done");') 331 set_focus = ( 332 'document.getElementById("Email").focus(); ' 333 'window.domAutomationController.send("done");') 334 self.ExecuteJavascript(clear_username_field, 0, 0) 335 self.ExecuteJavascript(set_focus, 0, 0) 336 self._SendCharToPopulateField('t', tab_index=0, windex=0) 337 passwd_value = self.GetDOMValue('document.getElementById("Passwd").value') 338 self.assertFalse(passwd_value, 339 msg='Password field not empty for new username.') 340 341 def testPasswordInfobarShowsForBlockedDomain(self): 342 """Verify that password infobar shows when cookies are blocked. 343 344 Password infobar should be shown if cookies are blocked for Google 345 accounts domain. 346 """ 347 creds = self.GetPrivateInfo()['test_google_account'] 348 username = creds['username'] 349 password = creds['password'] 350 # Block cookies for Google accounts domain. 351 self.SetPrefs(pyauto.kContentSettingsPatternPairs, 352 {'https://accounts.google.com/': {'cookies': 2}}) 353 test_utils.GoogleAccountsLogin(self, username, password) 354 test_utils.WaitForInfobarTypeAndGetIndex(self, self.INFOBAR_TYPE) 355 356 357 if __name__ == '__main__': 358 pyauto_functional.Main() 359