Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 """WebsiteTest testing class.""" | |
| 2 | |
| 3 import logging | |
| 4 import time | |
| 5 | |
| 6 from selenium.common.exceptions import NoSuchElementException | |
| 7 from selenium.common.exceptions import StaleElementReferenceException | |
| 8 from selenium.webdriver.common.action_chains import ActionChains | |
| 9 from selenium.webdriver.common.keys import Keys | |
| 10 | |
| 11 | |
| 12 def _IsOneSubstringOfAnother(s1, s2): | |
| 13 """Checks if one of the string arguements is substring of the other. | |
| 14 | |
| 15 Args: | |
| 16 s1: The first string. | |
| 17 s2: The second string. | |
| 18 Returns: | |
| 19 | |
| 20 True if one of the string arguements is substring of the other. | |
| 21 False otherwise. | |
| 22 """ | |
| 23 return s1 in s2 or s2 in s1 | |
| 24 | |
| 25 | |
| 26 class WebsiteTest: | |
| 27 """Handles a tested WebsiteTest.""" | |
| 28 | |
| 29 class Mode: | |
| 30 """Test mode.""" | |
| 31 # Password and username are expected to be autofilled. | |
| 32 AUTOFILLED = 1 | |
| 33 # Password and username are not expected to be autofilled. | |
| 34 NOT_AUTOFILLED = 2 | |
| 35 | |
| 36 def __init__(self): | |
| 37 pass | |
| 38 | |
| 39 def __init__( | |
| 40 self, name, username=None, password=None, | |
|
vabr (Chromium)
2014/05/20 14:47:25
Please remove username and password, you don't use
rchtara
2014/05/22 08:44:38
There not used, but they are useful: if someone wh
| |
| 41 username_not_auto=False): | |
| 42 """Creates a new WebsiteTest. | |
| 43 | |
| 44 Args: | |
| 45 name: The website name. | |
| 46 username: The website username. If it's None, the username is going to be | |
| 47 replaced with the value in the usernames and passwords file. | |
| 48 password: The website password. If it's None, the password is going to be | |
| 49 replaced with the value in the usernames and passwords file. | |
| 50 username_not_auto: Username inputs in some websites (like wikipedia) are | |
| 51 sometimes filled with some messages and thus, the usernames are not | |
| 52 automatically autofilled. This flag handles that and disables us from | |
| 53 checking if the state of the DOM is the same as the username of | |
| 54 website. | |
| 55 """ | |
| 56 # Name of the website | |
| 57 self.name = name | |
| 58 # Username of the website. | |
| 59 self.username = username | |
| 60 # Password of the website. | |
| 61 self.password = password | |
| 62 # Username is not automatically filled. | |
| 63 self.username_not_auto = username_not_auto | |
| 64 # Autofilling mode. | |
| 65 self.mode = self.Mode.NOT_AUTOFILLED | |
| 66 # The |remaining_time_to_wait| limits the total time spent in waiting, but | |
|
vabr (Chromium)
2014/05/20 14:47:25
nit: "spent in waiting, but ... running." -> "spen
rchtara
2014/05/22 08:44:38
Done.
| |
| 67 # does not include the time spent running. | |
| 68 self.remaining_time_to_wait = 200 | |
| 69 # The testing Environment. | |
| 70 self.environment = None | |
| 71 # The webdriver. | |
| 72 self.driver = None | |
| 73 | |
| 74 # Mouse/Keyboard actions. | |
| 75 | |
| 76 def Click(self, selector): | |
| 77 """Clicks on an element. | |
| 78 | |
| 79 Args: | |
| 80 selector: The element CSS selector. | |
| 81 """ | |
| 82 logging.info("action: Click %s" % selector) | |
| 83 element = self.driver.find_element_by_css_selector(selector) | |
| 84 element.click() | |
| 85 | |
| 86 def ClickIfVisible(self, selector): | |
| 87 """Clicks on an element, if it's visible. | |
|
vabr (Chromium)
2014/05/20 14:47:25
I'm not sure I understand what's meant by "visible
rchtara
2014/05/22 08:44:38
Done.
| |
| 88 | |
| 89 Args: | |
| 90 selector: The element CSS selector. | |
| 91 | |
| 92 Returns: | |
| 93 True if the element is visible. | |
| 94 False otherwise. | |
| 95 """ | |
| 96 logging.info("action: ClickIfVisible %s" % selector) | |
| 97 try: | |
| 98 element = self.driver.find_element_by_css_selector(selector) | |
| 99 element.click() | |
| 100 return True | |
| 101 except NoSuchElementException: | |
| 102 return False | |
| 103 except StaleElementReferenceException: | |
| 104 return False | |
| 105 | |
| 106 def GoTo(self, url): | |
| 107 """Navigates the main frame to the |url|. | |
| 108 | |
| 109 Args: | |
| 110 url: The URL. | |
| 111 """ | |
| 112 logging.info("action: GoTo %s" % self.name) | |
| 113 if self.environment.first_go_to: | |
| 114 self.environment.OpenTabAndGoToInternals(url) | |
| 115 self.environment.first_go_to = False | |
| 116 else: | |
| 117 self.driver.get(url) | |
| 118 | |
| 119 def HoverOver(self, selector): | |
| 120 """Hovers over an element. | |
| 121 | |
| 122 Args: | |
| 123 selector: The element CSS selector. | |
| 124 """ | |
| 125 logging.info("action: Hover %s" % selector) | |
| 126 element = self.driver.find_element_by_css_selector(selector) | |
| 127 hover = ActionChains(self.driver).move_to_element(element) | |
| 128 hover.perform() | |
| 129 | |
| 130 def SendEnterTo(self, selector): | |
| 131 """Sends an enter key to an element. | |
| 132 | |
| 133 Args: | |
| 134 selector: The element CSS selector. | |
| 135 """ | |
| 136 logging.info("action: SendEnterTo %s" % selector) | |
| 137 body = self.driver.find_element_by_tag_name("body") | |
| 138 body.send_keys(Keys.ENTER) | |
| 139 | |
| 140 # Waiting/Displaying actions. | |
| 141 | |
| 142 def IsDisplayed(self, selector): | |
| 143 """Checks if an element is displayed. | |
| 144 | |
| 145 Args: | |
| 146 selector: The element CSS selector. | |
| 147 | |
| 148 Returns: | |
| 149 True if the element is visible. | |
|
vabr (Chromium)
2014/05/20 14:47:25
Please be consistent with any changes you make abo
rchtara
2014/05/22 08:44:38
Done.
| |
| 150 False otherwise. | |
| 151 """ | |
| 152 logging.info("action: IsDisplayed %s" % selector) | |
| 153 try: | |
| 154 element = self.driver.find_element_by_css_selector(selector) | |
| 155 return element.is_displayed() | |
| 156 except NoSuchElementException: | |
| 157 return False | |
| 158 except StaleElementReferenceException: | |
| 159 return False | |
| 160 | |
| 161 def Wait(self, duration): | |
| 162 """Wait for a duration in seconds. | |
|
vabr (Chromium)
2014/05/20 14:47:25
Please comment on the intended use-case: this need
rchtara
2014/05/22 08:44:38
Done.
| |
| 163 | |
| 164 Args: | |
| 165 duration: The time to wait in seconds. | |
| 166 """ | |
| 167 logging.info("action: Wait %s" % duration) | |
| 168 time.sleep(duration) | |
| 169 self.remaining_time_to_wait -= 1 | |
| 170 if self.remaining_time_to_wait < 0: | |
| 171 raise Exception("Tests took more time than expected for the following " | |
| 172 "website : %s \n" % self.name) | |
| 173 | |
| 174 def WaitUntilDisplayed(self, selector, timeout=10): | |
| 175 """Waits until an element is displayed. | |
| 176 | |
| 177 Args: | |
| 178 selector: The element CSS selector. | |
| 179 timeout: The maximum waiting time in seconds before failing. | |
| 180 """ | |
| 181 if not self.IsDisplayed(selector): | |
| 182 self.Wait(1) | |
| 183 timeout = timeout - 1 | |
| 184 if (timeout <= 0): | |
| 185 raise Exception("Error: Element %s not shown before timeout is " | |
| 186 "finished for the following website: %s" | |
| 187 % (selector, self.name)) | |
| 188 else: | |
| 189 self.WaitUntilDisplayed(selector, timeout) | |
| 190 | |
| 191 # Form actions. | |
| 192 | |
| 193 def FillPasswordInto(self, selector): | |
| 194 """If the testing mode is the Autofilled mode, compares the website | |
| 195 password to the DOM state. | |
| 196 If the testing mode is the NotAutofilled mode, checks that the DOM state | |
| 197 is empty. | |
| 198 Then, fills the input with the Website password. | |
| 199 | |
| 200 Args: | |
| 201 selector: The password input CSS selector. | |
| 202 | |
| 203 Raises: | |
| 204 Exception: An exception is raised if the DOM value of the password is | |
| 205 different than the one we expected. | |
| 206 """ | |
| 207 logging.info("action: FillPasswordInto %s" % selector) | |
| 208 password_element = self.driver.find_element_by_css_selector(selector) | |
| 209 if self.mode == self.Mode.AUTOFILLED: | |
| 210 autofilled_password = password_element.get_attribute("value") | |
| 211 if autofilled_password != self.password: | |
| 212 raise Exception("Error: autofilled password is different from the one " | |
| 213 "we just saved for the following website : %s p1: %s " | |
| 214 "p2:%s \n" % (self.name, | |
| 215 password_element.get_attribute("value"), | |
| 216 self.password)) | |
| 217 | |
| 218 elif self.mode == self.Mode.NOT_AUTOFILLED: | |
| 219 autofilled_password = password_element.get_attribute("value") | |
| 220 password_element.send_keys(autofilled_password) | |
|
vabr (Chromium)
2014/05/20 14:47:25
Unless we raise an exception in the next step, |au
rchtara
2014/05/22 08:44:38
Done.
| |
| 221 if autofilled_password: | |
| 222 raise Exception("Error: password is autofilled when it shouldn't be " | |
| 223 "for the following website : %s \n" | |
| 224 % self.name) | |
| 225 | |
| 226 password_element.send_keys(self.password) | |
| 227 | |
| 228 def FillUsernameInto(self, selector): | |
| 229 """If the testing mode is the Autofilled mode, compares the website | |
| 230 username to the input value. Then, fills the input with the website | |
| 231 username. | |
| 232 | |
| 233 Args: | |
| 234 selector: The username input CSS selector. | |
| 235 | |
| 236 Raises: | |
| 237 Exception: An exception is raised if the DOM value of the username is | |
| 238 different that the one we expected. | |
| 239 """ | |
| 240 logging.info("action: FillUsernameInto %s" % selector) | |
| 241 username_element = self.driver.find_element_by_css_selector(selector) | |
| 242 | |
| 243 if (self.mode == self.Mode.AUTOFILLED and not self.username_not_auto): | |
| 244 if not (username_element.get_attribute("value") == self.username): | |
| 245 raise Exception("Error: autofilled username is different form the one " | |
| 246 "we just saved for the following website : %s \n" % | |
| 247 self.name) | |
| 248 # Chrome protects the password inputs and doesn't fill them until | |
|
vabr (Chromium)
2014/05/20 14:47:25
This assumes that FillUsernameInto is always calle
rchtara
2014/05/22 08:44:38
Thanks :)
| |
| 249 # the user interacts with the page. If we fill the username, we don't | |
| 250 # have this problem anymore. Otherwise, we just click on the username. | |
| 251 username_element.click() | |
| 252 else: | |
| 253 username_element.clear() | |
| 254 username_element.send_keys(self.username) | |
| 255 | |
| 256 def Submit(self, selector): | |
| 257 """Finds an element using CSS Selector and calls its submit() handler. | |
| 258 | |
| 259 Args: | |
| 260 selector: The input CSS selector. | |
| 261 """ | |
| 262 logging.info("action: Submit %s" % selector) | |
| 263 element = self.driver.find_element_by_css_selector(selector) | |
| 264 element.submit() | |
| 265 | |
| 266 # Login/Logout Methods | |
| 267 | |
| 268 def Login(self): | |
| 269 """Login Method. Has to be overloaded by the WebsiteTest test.""" | |
| 270 raise NotImplementedError("Login is not implemented.") | |
| 271 | |
| 272 def LoginWhenAutofilled(self): | |
| 273 """Logs in and checks that the password is autofilled.""" | |
| 274 self.mode = self.Mode.AUTOFILLED | |
| 275 self.Login() | |
| 276 | |
| 277 def LoginWhenNotAutofilled(self): | |
| 278 """Logs in and checks that the password is not autofilled.""" | |
| 279 self.mode = self.Mode.NOT_AUTOFILLED | |
| 280 self.Login() | |
| 281 | |
| 282 def Logout(self): | |
| 283 """Logout Method. Has to be overloaded by the Website test.""" | |
| 284 raise NotImplementedError("Logout is not implemented.") | |
| 285 | |
| 286 # Tests | |
| 287 | |
| 288 def WrongLoginTest(self): | |
| 289 """Does the wrong login test: Tries to login with a wrong password and | |
| 290 checks that the prompt is not shown. | |
| 291 | |
| 292 Raises: | |
| 293 Exception: An exception is raised if the test fail. | |
|
vabr (Chromium)
2014/05/20 14:47:25
nit: fails
vabr (Chromium)
2014/05/20 14:47:25
Actually, "test fails" is confusing in this case:
rchtara
2014/05/22 08:44:38
Done.
rchtara
2014/05/22 08:44:38
Done.
| |
| 294 """ | |
| 295 logging.info("\nWrong Login Test for %s \n" % self.name) | |
| 296 correct_password = self.password | |
| 297 self.password = self.password + "1" | |
| 298 self.LoginWhenNotAutofilled() | |
| 299 self.password = correct_password | |
| 300 self.Wait(2) | |
| 301 self.environment.SwitchToInternals() | |
| 302 self.environment.CheckPrompt( | |
| 303 "Message: Decision: SAVE the password", | |
| 304 False, | |
| 305 "Error: password manager thinks that a login with wrong password was " | |
| 306 "successful for the following website : %s \n" % self.name) | |
| 307 self.environment.SwitchFromInternals() | |
| 308 | |
| 309 def SuccessfulLoginTest(self): | |
| 310 """Does the successful login when the password is not expected to be | |
| 311 autofilled test: Checks that the password is not autofilled, tries to login | |
| 312 with a right password and checks if the that the prompt is shown. Then logs | |
| 313 out. | |
| 314 | |
| 315 Raises: | |
| 316 Exception: An exception is raised if the test fail. | |
| 317 """ | |
| 318 logging.info("\nSuccessful Login Test for %s \n" % self.name) | |
| 319 self.LoginWhenNotAutofilled() | |
| 320 self.Wait(2) | |
| 321 self.environment.SwitchToInternals() | |
| 322 self.environment.CheckPrompt( | |
| 323 "Message: Decision: SAVE the password", | |
| 324 True, | |
| 325 "Error: password manager hasn't detected a successful login for the " | |
| 326 "following website : %s \n" | |
| 327 % self.name) | |
| 328 self.environment.SwitchFromInternals() | |
| 329 self.Logout() | |
| 330 | |
| 331 def SuccessfulLoginWithAutofilledPasswordTest(self): | |
| 332 """Does the successful login when the password is expected to be autofilled | |
| 333 test: Checks that the password is autofilled, tries to login with a right | |
| 334 password and checks if the that the prompt is shown. Then logs out. | |
| 335 | |
| 336 Raises: | |
| 337 Exception: An exception is raised if the test fail. | |
| 338 """ | |
| 339 logging.info("\nSuccessful Login With Autofilled Password" | |
| 340 " Test %s \n" % self.name) | |
| 341 self.LoginWhenAutofilled() | |
| 342 self.Wait(2) | |
| 343 self.environment.SwitchToInternals() | |
| 344 self.environment.CheckPrompt( | |
| 345 "Message: Decision: SAVE the password", | |
| 346 True, | |
| 347 "Error: password manager hasn't detected a successful login for the " | |
| 348 "following website : %s \n" | |
| 349 % self.name) | |
| 350 self.environment.SwitchFromInternals() | |
| 351 self.Logout() | |
| 352 | |
| 353 def PromptTest(self): | |
| 354 """Does the wrong login test: Tries to login with a wrong password and | |
|
vabr (Chromium)
2014/05/20 14:47:25
Did you forget to change the doc string?
rchtara
2014/05/22 08:44:38
Done.
| |
| 355 checks that the prompt is not shown. | |
| 356 | |
| 357 Raises: | |
| 358 Exception: An exception is raised if the test fail. | |
| 359 """ | |
| 360 logging.info("\nWrong Login Test for %s \n" % self.name) | |
|
vabr (Chromium)
2014/05/20 14:47:25
Did you forget to change the log message?
rchtara
2014/05/22 08:44:38
Done.
| |
| 361 correct_password = self.password | |
| 362 self.password = self.password + "1" | |
| 363 self.LoginWhenNotAutofilled() | |
| 364 self.password = correct_password | |
| 365 self.Wait(2) | |
| 366 self.environment.SwitchToInternals() | |
| 367 self.environment.CheckPrompt( | |
| 368 "Message: Decision: ASK the user", | |
| 369 False, | |
| 370 "Error: password manager thinks that a login with wrong password was " | |
| 371 "successful for the following website : %s \n" % self.name) | |
| 372 self.environment.SwitchFromInternals() | |
| 373 | |
| 374 self.LoginWhenNotAutofilled() | |
| 375 self.Wait(2) | |
| 376 self.environment.SwitchToInternals() | |
| 377 self.environment.CheckPrompt( | |
| 378 "Message: Decision: ASK the user", | |
| 379 True, | |
| 380 "Error: password manager thinks that a login with wrong password was " | |
| 381 "successful for the following website : %s \n" % self.name) | |
| 382 self.environment.SwitchFromInternals() | |
| OLD | NEW |