Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 """Website testing class.""" | |
| 2 | |
| 3 | |
| 4 import time | |
| 5 | |
| 6 from selenium.common.exceptions import ElementNotVisibleException | |
| 7 from selenium.common.exceptions import NoSuchElementException | |
| 8 from selenium.common.exceptions import StaleElementReferenceException | |
| 9 from selenium.webdriver.common.action_chains import ActionChains | |
| 10 from selenium.webdriver.common.keys import Keys | |
| 11 | |
| 12 | |
| 13 def _IsOneSubstringOfAnother(s1, s2): | |
|
vabr (Chromium)
2014/05/16 09:36:00
Please add a doc string, explaining what this func
rchtara
2014/05/20 08:24:47
Done.
| |
| 14 return s1 in s2 or s2 in s1 | |
| 15 | |
| 16 | |
| 17 class Website: | |
| 18 """Handles a tested Website.""" | |
| 19 | |
| 20 class Mode: | |
| 21 """Test mode.""" | |
| 22 # Password and username are expected to be autofilled. | |
| 23 Autofilled = 1 | |
|
vabr (Chromium)
2014/05/16 09:36:00
Please use all caps for consistency with the C++ e
rchtara
2014/05/20 08:24:47
Done.
| |
| 24 # Password and username are not expected to be autofilled. | |
| 25 NotAutofilled = 2 | |
| 26 | |
| 27 def __init__(self): | |
| 28 pass | |
| 29 | |
| 30 def __init__( | |
| 31 self, name, url, username=None, password=None, | |
|
vabr (Chromium)
2014/05/16 09:36:00
Please remove |url|. You don't seem to use it, and
vabr (Chromium)
2014/05/16 09:36:00
Are you actually using the username and password a
rchtara
2014/05/20 08:24:47
Yes, I use them to sign in
rchtara
2014/05/20 08:24:47
Done.
vabr (Chromium)
2014/05/20 14:47:25
But you don't seem to use the __init__ arguments u
| |
| 32 username_not_auto=False): | |
| 33 """Creates a new Website. | |
| 34 | |
| 35 Args: | |
| 36 name: The Website name. | |
| 37 url: The Website URL. | |
| 38 username: The Website username. If it's None, the username is going to be | |
| 39 replaced by the value in the usernames and passwords file. | |
| 40 password: The Website password. If it's None, the password is going to be | |
| 41 replaced by the value in the usernames and passwords file. | |
| 42 username_not_auto: Username inputs in some Websites (like wikipedia) are | |
| 43 sometimes filled with some messages and thus, the usernames are not | |
| 44 automatically autofilled. This flag handles that and disables us from | |
| 45 checking if the state of the DOM is the same as the username of | |
| 46 Website. | |
| 47 """ | |
| 48 # Name of the Website | |
| 49 self.name = name | |
| 50 # URL of the Website | |
| 51 self.url = url | |
| 52 # Username of the Website. | |
| 53 self.username = username | |
| 54 # Password of the Website. | |
| 55 self.password = password | |
| 56 # Username is not automatically filled. | |
| 57 self.username_not_auto = username_not_auto | |
|
vabr (Chromium)
2014/05/16 09:36:00
For pages like Wikipedia, where the username is no
rchtara
2014/05/20 08:24:47
username_not_auto affects only the username, but t
vabr (Chromium)
2014/05/20 14:47:25
You are correct, thanks for explanation.
rchtara
2014/05/22 08:44:38
you re welcome :)
| |
| 58 # Autofilling mode. | |
| 59 self.mode = self.Mode.NotAutofilled | |
| 60 # Waiting duration before stopping the test. | |
|
vabr (Chromium)
2014/05/16 09:36:00
Note: max_duration limits the total time spent in
rchtara
2014/05/20 08:24:47
Done.
| |
| 61 self.max_duration = 200 | |
| 62 # The testing Environment. | |
| 63 self.environment = None | |
| 64 # The webdriver. | |
| 65 self.driver = None | |
| 66 | |
| 67 # Mouse/Keyboard actions. | |
| 68 | |
| 69 def Click(self, selector): | |
| 70 """Clicks on an element. | |
| 71 | |
| 72 Args: | |
| 73 selector: The element CSS selector. | |
| 74 """ | |
| 75 self.environment.Log("action: Click %s" % selector) | |
| 76 element = self.driver.find_element_by_css_selector(selector) | |
| 77 element.click() | |
| 78 | |
| 79 def ClickIfVisible(self, selector): | |
| 80 """Clicks on an element, if it's available. | |
|
vabr (Chromium)
2014/05/16 09:36:00
So, is this about visibility, or availability?
If
rchtara
2014/05/20 08:24:47
Done.
| |
| 81 | |
| 82 Args: | |
| 83 selector: The element CSS selector. | |
| 84 """ | |
| 85 self.environment.Log("action: ClickIfVisible %s" % selector) | |
| 86 try: | |
| 87 element = self.driver.find_element_by_css_selector(selector) | |
| 88 element.click() | |
| 89 except NoSuchElementException: | |
| 90 return False | |
|
vabr (Chromium)
2014/05/16 09:36:00
The method returns False if the element is not cli
rchtara
2014/05/20 08:24:47
Done.
| |
| 91 except StaleElementReferenceException: | |
| 92 return False | |
| 93 | |
| 94 def GoTo(self, url): | |
| 95 """Navigates the main frame to a url. | |
|
vabr (Chromium)
2014/05/16 09:36:00
nit: "a url" -> "|url|"
(It does not navigate to j
rchtara
2014/05/20 08:24:47
Done.
| |
| 96 | |
| 97 Args: | |
| 98 url: The URL. | |
| 99 """ | |
| 100 self.environment.Log("action: GoTo %s" % self.url) | |
| 101 self.driver.get(url) | |
| 102 | |
| 103 def HoverOver(self, selector): | |
| 104 """Hovers over an element. | |
| 105 | |
| 106 Args: | |
| 107 selector: The element CSS selector. | |
| 108 """ | |
| 109 self.environment.Log("action: Hover %s" % selector) | |
| 110 element = self.driver.find_element_by_css_selector(selector) | |
| 111 hover = ActionChains(self.driver).move_to_element(element) | |
| 112 hover.perform() | |
| 113 | |
| 114 def SendEnterTo(self, selector): | |
| 115 """Sends an enter key to an element. | |
| 116 | |
| 117 Args: | |
| 118 selector: The element CSS selector. | |
| 119 """ | |
| 120 self.environment.Log("action: SendEnterTo %s" % selector) | |
| 121 body = self.driver.find_element_by_tag_name("body") | |
| 122 body.send_keys(Keys.ENTER) | |
| 123 | |
| 124 # Waiting/Displaying actions. | |
| 125 | |
| 126 def IsDisplayed(self, selector): | |
| 127 """Checks if an element is displayed. | |
| 128 | |
| 129 Args: | |
| 130 selector: The element CSS selector. | |
| 131 """ | |
| 132 self.environment.Log("action: IsDisplayed %s" % selector) | |
| 133 try: | |
| 134 element = self.driver.find_element_by_css_selector(selector) | |
| 135 return element.is_displayed() | |
| 136 except NoSuchElementException: | |
| 137 return False | |
| 138 except StaleElementReferenceException: | |
| 139 return False | |
| 140 | |
| 141 def Wait(self, duration): | |
| 142 """Wait for a duration. | |
|
vabr (Chromium)
2014/05/16 09:36:00
Please specify time units.
rchtara
2014/05/20 08:24:47
Done.
| |
| 143 | |
| 144 Args: | |
| 145 duration: The element. | |
| 146 """ | |
| 147 self.environment.Log("action: Wait %s" % duration) | |
| 148 time.sleep(duration) | |
| 149 self.max_duration -= 1 | |
| 150 if self.max_duration < 0: | |
| 151 raise Exception("Tests took more time than expected for the following " | |
| 152 "website : %s \n" % self.name) | |
| 153 | |
| 154 def WaitUntilDisplayed(self, selector, timeout=10): | |
| 155 """Waits until an element is displayed. | |
| 156 | |
| 157 Args: | |
| 158 selector: The element CSS selector. | |
| 159 """ | |
| 160 if not self.IsDisplayed(selector): | |
| 161 time.sleep(1) | |
|
vabr (Chromium)
2014/05/16 09:36:00
Please call Wait() instead of manually decrementin
rchtara
2014/05/20 08:24:47
Done.
| |
| 162 timeout = timeout - 1 | |
| 163 if (timeout <= 0): | |
| 164 raise Exception("Error: Element %s not shown before timeout is " | |
| 165 "finished for the following website: %s" | |
| 166 % (selector, self.name)) | |
| 167 else: | |
| 168 self.WaitUntilDisplayed(selector, timeout) | |
| 169 self.max_duration -= 1 | |
| 170 if self.max_duration < 0: | |
| 171 raise Exception("Tests took more time than expected for the " | |
| 172 "following website : %s \n" % self.name) | |
| 173 | |
| 174 # Form actions. | |
| 175 | |
| 176 def FillPasswordInto(self, selector): | |
| 177 """If the testing mode is the Autofilled mode, compares the Website | |
| 178 password to the DOM state. | |
| 179 If the testing mode is the NotAutofilled mode, checks that the DOM state | |
| 180 is empty. | |
| 181 Then, fills the input with the Website password. | |
| 182 | |
| 183 Args: | |
| 184 selector: The password input CSS selector. | |
| 185 | |
| 186 Raises: | |
| 187 Exception: An exception is raised if the DOM value of the password is | |
| 188 different that the one we expected. | |
| 189 """ | |
| 190 self.environment.Log("action: FillPasswordInto %s" % selector) | |
| 191 password_element = self.driver.find_element_by_css_selector(selector) | |
| 192 if self.mode == self.Mode.Autofilled: | |
| 193 # Chrome protects the password inputs and doesn't fill them until | |
| 194 # the user interacts with the page. To guarantee that, we just | |
| 195 # send a key to the password input. Clicking on the password input was | |
| 196 # tried too, but because the password is sometimes hidden, this didn't | |
|
vabr (Chromium)
2014/05/16 09:36:00
I'm not sure I understand the issue here -- if the
rchtara
2014/05/20 08:24:47
In www.163.com, we the driver fills the username w
| |
| 197 # worked out. | |
|
vabr (Chromium)
2014/05/16 09:36:00
nit: worked -> work
rchtara
2014/05/20 08:24:47
Done.
| |
| 198 password_element.send_keys("a") | |
| 199 ps = password_element.get_attribute("value")[:-1] | |
|
vabr (Chromium)
2014/05/16 09:36:00
Pleas do not use cryptic abbreviations as names of
rchtara
2014/05/20 08:24:47
Done.
| |
| 200 password_element.clear() | |
| 201 password_element.send_keys(ps) | |
| 202 if password_element.get_attribute("value") != self.password: | |
| 203 raise Exception("Error: autofilled password is different from the one " | |
| 204 "we just saved for the following website : %s p1: %s " | |
| 205 "p2:%s \n" % (self.name, | |
| 206 password_element.get_attribute("value"), | |
|
vabr (Chromium)
2014/05/16 09:36:00
nit: indenting is off
rchtara
2014/05/20 08:24:47
Done.
| |
| 207 self.password)) | |
| 208 elif self.mode == self.Mode.NotAutofilled: | |
| 209 # Chrome protects the password inputs and doesn't fill them until | |
| 210 # the user interacts with the page. To guarantee that, we just | |
| 211 # send a key to the password input. Clicking on the password input was | |
| 212 # tried too, but because the password is sometimes hidden, this didn't | |
| 213 # worked out. | |
| 214 password_element.send_keys("a") | |
| 215 ps = password_element.get_attribute("value")[1:] | |
|
vabr (Chromium)
2014/05/16 09:36:00
Again, please rename |ps| appropriately.
rchtara
2014/05/20 08:24:47
Done.
| |
| 216 password_element.clear() | |
| 217 password_element.send_keys(ps) | |
|
vabr (Chromium)
2014/05/16 09:36:00
Why do you send |ps| to password_element here, and
rchtara
2014/05/20 08:24:47
I' m going to replace all this by a click on the u
| |
| 218 if ps: | |
| 219 raise Exception("Error: password is autofilled when it shouldn't be " | |
| 220 "for the following website : %s \n" | |
| 221 % self.name) | |
| 222 | |
| 223 # Chrome protects the password inputs and doesn't fill them until | |
| 224 # the user interacts with the page. To guarantee that, we just | |
| 225 # send a key to the password. Clicking on the password input was tried | |
| 226 # too, but because the password is sometime hidden, this didn't worked | |
| 227 # out. | |
| 228 password_element.send_keys("a") | |
| 229 password_element.clear() | |
| 230 password_element.send_keys(self.password) | |
| 231 | |
| 232 def FillUsernameInto(self, selector): | |
|
vabr (Chromium)
2014/05/16 09:36:00
In the concrete Websites, FillUsernameInto and Fil
rchtara
2014/05/20 08:24:47
I think it's more flexible to keep it as it's now.
vabr (Chromium)
2014/05/20 14:47:25
Fair enough, let's keep them separate.
rchtara
2014/05/22 08:44:38
Done.
| |
| 233 """If the testing mode is the Autofilled mode, compares the Website username | |
| 234 to the input value. | |
| 235 Then, fills the input with the Website username. | |
| 236 | |
| 237 Args: | |
| 238 selector: The username input CSS selector. | |
| 239 | |
| 240 Raises: | |
| 241 Exception: An exception is raised if the DOM value of the username is | |
| 242 different that the one we expected. | |
| 243 """ | |
| 244 self.environment.Log("action: FillUsernameInto %s" % selector) | |
| 245 username_element = self.driver.find_element_by_css_selector(selector) | |
| 246 | |
| 247 if (self.mode == self.Mode.Autofilled and not self.username_not_auto): | |
| 248 if not (username_element.get_attribute("value") == self.username): | |
| 249 raise Exception("Error: autofilled username is different form the one " | |
| 250 "we just saved for the following website : %s \n" % | |
| 251 self.name) | |
| 252 | |
| 253 else: | |
| 254 username_element.clear() | |
|
vabr (Chromium)
2014/05/16 09:36:00
Should you check that either self.username_not_aut
rchtara
2014/05/20 08:24:47
I don't think it's important it important to do so
vabr (Chromium)
2014/05/20 14:47:25
Ah right, that's a good point, I forgot about the
rchtara
2014/05/22 08:44:38
You re welcome
| |
| 255 username_element.send_keys(self.username) | |
| 256 | |
| 257 def FillUsernameIfVisible(self, selector): | |
| 258 """Fills the input with the website username, if the input exists. | |
|
vabr (Chromium)
2014/05/16 09:36:00
Please comment on ignoring self.mode here.
rchtara
2014/05/20 08:24:47
I removed the method, not needed
| |
| 259 | |
| 260 Args: | |
| 261 selector: The username input CSS selector. | |
| 262 """ | |
| 263 self.environment.Log("action: FillUsernameIfVisible %s" % selector) | |
| 264 username_element = self.driver.find_element_by_css_selector(selector) | |
| 265 try: | |
| 266 username_element.clear() | |
| 267 username_element.send_keys(self.username) | |
| 268 except ElementNotVisibleException: | |
| 269 pass | |
| 270 | |
| 271 def Submit(self, selector): | |
| 272 """Finds an element using CSS Selector and calls its submit() handler. | |
| 273 | |
| 274 Args: | |
| 275 selector: The input CSS selector. | |
| 276 """ | |
| 277 self.environment.Log("action: Submit %s" % selector) | |
| 278 element = self.driver.find_element_by_css_selector(selector) | |
| 279 element.submit() | |
| 280 | |
| 281 # Login/Logout Methods | |
| 282 | |
| 283 def Login(self): | |
| 284 """Login Method. Has to be overloaded by the Website test.""" | |
| 285 raise NotImplementedError("Login is not implemented.") | |
| 286 | |
| 287 def LoginWhenAutofilled(self): | |
| 288 """Logs in and checks that the password is autofilled.""" | |
| 289 self.mode = self.Mode.Autofilled | |
| 290 self.Login() | |
| 291 | |
| 292 def LoginWhenNotAutofilled(self): | |
| 293 """Logs in and checks that the password is not autofilled.""" | |
| 294 self.mode = self.Mode.NotAutofilled | |
| 295 self.Login() | |
| 296 | |
| 297 def Logout(self): | |
| 298 """Logout Method. Has to be overloaded by the Website test.""" | |
| 299 raise NotImplementedError("Logout is not implemented.") | |
| 300 | |
| 301 # TestsTools | |
| 302 | |
| 303 def RemoveAllPasswords(self, urls): | |
|
rchtara
2014/05/20 08:24:47
Function removed
| |
| 304 """Removes all the saved passwords for the current Website. | |
| 305 | |
| 306 Args: | |
| 307 urls: All the available URLs in the saved passwords list. | |
| 308 """ | |
| 309 if (self.url != ""): | |
|
vabr (Chromium)
2014/05/16 09:36:00
nit: Just use
if self.url:
rchtara
2014/05/20 08:24:47
Done.
| |
| 310 i = 0 | |
| 311 for current_url in urls: | |
| 312 if _IsOneSubstringOfAnother(current_url, self.url): | |
| 313 self.driver.execute_script( | |
|
vabr (Chromium)
2014/05/16 09:36:00
Please comment on what the script does.
In particu
rchtara
2014/05/20 08:24:47
Done.
| |
| 314 "document.querySelectorAll('#saved-passwords-list " | |
| 315 ".row-delete-button')[%d].click()" % i) | |
|
vabr (Chromium)
2014/05/16 09:36:00
Actually, I don't think you use |i| correctly here
rchtara
2014/05/20 08:24:47
we get a fresh copy of |urls|, after each deletion
vabr (Chromium)
2014/05/20 14:47:25
Ah, that makes sense, thanks for your explanation.
| |
| 316 time.sleep(1) # Wait until command is executed. | |
|
vabr (Chromium)
2014/05/16 09:36:00
Please use Wait().
rchtara
2014/05/20 08:24:47
Done.
| |
| 317 else: | |
| 318 i = i + 1 | |
| 319 | |
| 320 | |
| 321 # Tests | |
| 322 | |
| 323 def WrongLoginTest(self): | |
| 324 """Does the wrong login test: Tries to login with a wrong password and | |
| 325 checks that the prompt is not shown. | |
| 326 | |
| 327 Raises: | |
| 328 Exception: An exception is raised if the tests fail. | |
| 329 """ | |
| 330 self.environment.Log("\nWrong Login Test for %s \n" % self.name) | |
| 331 correct_password = self.password | |
| 332 self.password = self.password + "1" | |
| 333 self.LoginWhenNotAutofilled() | |
| 334 self.password = correct_password | |
|
vabr (Chromium)
2014/05/16 09:36:00
Why do you not Wait() here, but you do wait at the
rchtara
2014/05/20 08:24:47
Done.
| |
| 335 self.environment.SwitchToInternals() | |
| 336 self.environment.CheckPromptIsNotShown( | |
| 337 False, | |
| 338 "Error: password manager thinks that a login with wrong password was " | |
| 339 "successful for the following website : %s \n" % self.name) | |
| 340 self.environment.SwitchFromInternals() | |
| 341 | |
| 342 def SuccessfulLoginTest(self): | |
| 343 """Does the successful login when the password is not expected to be | |
| 344 autofilled test: Checks that the password is not autofilled, tries to login | |
| 345 with a right password and checks if the that the prompt is shown. Then logs | |
| 346 out. | |
| 347 | |
| 348 Raises: | |
| 349 Exception: An exception is raised if the tests fail. | |
| 350 """ | |
| 351 self.environment.Log("\nSuccessful Login Test for %s \n" % self.name) | |
| 352 self.LoginWhenNotAutofilled() | |
| 353 time.sleep(2) | |
|
vabr (Chromium)
2014/05/16 09:36:00
Please use Wait().
rchtara
2014/05/20 08:24:47
Done.
| |
| 354 self.environment.SwitchToInternals() | |
| 355 self.environment.CheckPromptIsNotShown(True, | |
| 356 "Error: password manager hasn't detected a successful login for the " | |
| 357 "following website : %s \n" | |
| 358 % self.name) | |
| 359 self.environment.SwitchFromInternals() | |
| 360 self.Logout() | |
| 361 | |
| 362 def SuccessfulLoginWithAutofilledPasswordTest(self): | |
| 363 """Does the successful login when the password is expected to be autofilled | |
| 364 test: Checks that the password is autofilled, tries to login with a right | |
| 365 password and checks if the that the prompt is shown. Then logs out. | |
| 366 | |
| 367 Raises: | |
| 368 Exception: An exception is raised if the tests fail. | |
| 369 """ | |
| 370 self.environment.Log("\nSuccessful Login With Autofilled Password" | |
| 371 " Test %s \n" % self.name) | |
| 372 self.LoginWhenAutofilled() | |
| 373 time.sleep(2) | |
|
vabr (Chromium)
2014/05/16 09:36:00
Please use Wait().
rchtara
2014/05/20 08:24:47
Done.
| |
| 374 self.environment.SwitchToInternals() | |
| 375 self.environment.CheckPromptIsNotShown(True, | |
| 376 "Error: password manager hasn't detected a successful login for the " | |
| 377 "following website : %s \n" | |
| 378 % self.name) | |
| 379 self.environment.SwitchFromInternals() | |
| 380 self.Logout() | |
| 381 | |
| 382 def SuccessfulLoginAfterDeletionTest(self): | |
|
vabr (Chromium)
2014/05/16 09:36:00
This does exactly the same thing as SuccessfulLogi
rchtara
2014/05/20 08:24:47
Done.
| |
| 383 """Does the successful login after the deletion of the password test: Checks | |
| 384 that the password is not autofilled, tries to login with a right password | |
| 385 and checks if the that the prompt is shown. Then logs out. | |
| 386 | |
| 387 Raises: | |
| 388 Exception: An exception is raised if the tests fail. | |
| 389 """ | |
| 390 self.environment.Log("\nSuccessful Login After Deletion Test" | |
| 391 " for %s \n" % self.name) | |
| 392 self.LoginWhenNotAutofilled() | |
| 393 self.environment.SwitchToInternals() | |
| 394 self.environment.CheckPromptIsNotShown( | |
| 395 True, | |
| 396 "Error: password manager hasn't detected a successful login for the " | |
| 397 "following website : %s \n" % self.name) | |
| 398 self.environment.SwitchFromInternals() | |
| 399 self.Logout() | |
| OLD | NEW |