Index: components/test/data/password_manager/websitetest.py |
diff --git a/components/test/data/password_manager/websitetest.py b/components/test/data/password_manager/websitetest.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a0df59308b03734e32a5c7fe75a93c7780ee9e85 |
--- /dev/null |
+++ b/components/test/data/password_manager/websitetest.py |
@@ -0,0 +1,406 @@ |
+# Copyright 2014 The Chromium Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+"""WebsiteTest testing class.""" |
+ |
+import logging |
+import time |
+ |
+from selenium.webdriver.common.action_chains import ActionChains |
+from selenium.webdriver.common.keys import Keys |
+ |
+import environment |
+ |
+ |
+def _IsOneSubstringOfAnother(s1, s2): |
+ """Checks if one of the string arguements is substring of the other. |
+ |
+ Args: |
+ s1: The first string. |
+ s2: The second string. |
+ Returns: |
+ |
+ True if one of the string arguements is substring of the other. |
+ False otherwise. |
+ """ |
+ return s1 in s2 or s2 in s1 |
+ |
+ |
+class WebsiteTest: |
+ """Handles a tested WebsiteTest.""" |
+ |
+ class Mode: |
+ """Test mode.""" |
+ # Password and username are expected to be autofilled. |
+ AUTOFILLED = 1 |
+ # Password and username are not expected to be autofilled. |
+ NOT_AUTOFILLED = 2 |
+ |
+ def __init__(self): |
+ pass |
+ |
+ def __init__(self, name, username_not_auto=False): |
+ """Creates a new WebsiteTest. |
+ |
+ Args: |
+ name: The website name. |
+ username_not_auto: Username inputs in some websites (like wikipedia) are |
+ sometimes filled with some messages and thus, the usernames are not |
+ automatically autofilled. This flag handles that and disables us from |
+ checking if the state of the DOM is the same as the username of |
+ website. |
+ """ |
+ # Name of the website |
+ self.name = name |
+ # Username of the website. |
+ self.username = None |
+ # Password of the website. |
+ self.password = None |
+ # Username is not automatically filled. |
+ self.username_not_auto = username_not_auto |
+ # Autofilling mode. |
+ self.mode = self.Mode.NOT_AUTOFILLED |
+ # The |remaining_time_to_wait| limits the total time in seconds spent in |
+ # potentially infinite loops. |
+ self.remaining_time_to_wait = 200 |
+ # The testing Environment. |
+ self.environment = None |
+ # The webdriver. |
+ self.driver = None |
+ |
+ # Mouse/Keyboard actions. |
+ |
+ def Click(self, selector): |
+ """Clicks on an element. |
+ |
+ Args: |
+ selector: The element CSS selector. |
+ """ |
+ logging.info("action: Click %s" % selector) |
+ element = self.driver.find_element_by_css_selector(selector) |
+ element.click() |
+ |
+ def ClickIfClickable(self, selector): |
+ """Clicks on an element if it's clickable: If it doesn't exist in the DOM, |
+ it's covered by another element or it's out viewing area, nothing is |
+ done and False is returned. Otherwise, even if the element is 100% |
+ transparent, the element is going to receive a click and a True is |
+ returned. |
+ |
+ Args: |
+ selector: The element CSS selector. |
+ |
+ Returns: |
+ True if the click happens. |
+ False otherwise. |
+ """ |
+ logging.info("action: ClickIfVisible %s" % selector) |
+ try: |
+ element = self.driver.find_element_by_css_selector(selector) |
+ element.click() |
+ return True |
+ except Exception: |
+ return False |
+ |
+ def GoTo(self, url): |
+ """Navigates the main frame to the |url|. |
+ |
+ Args: |
+ url: The URL. |
+ """ |
+ logging.info("action: GoTo %s" % self.name) |
+ if self.environment.first_go_to: |
+ self.environment.OpenTabAndGoToInternals(url) |
+ self.environment.first_go_to = False |
+ else: |
+ self.driver.get(url) |
+ |
+ def HoverOver(self, selector): |
+ """Hovers over an element. |
+ |
+ Args: |
+ selector: The element CSS selector. |
+ """ |
+ logging.info("action: Hover %s" % selector) |
+ element = self.driver.find_element_by_css_selector(selector) |
+ hover = ActionChains(self.driver).move_to_element(element) |
+ hover.perform() |
+ |
+ def SendEnterTo(self, selector): |
+ """Sends an enter key to an element. |
+ |
+ Args: |
+ selector: The element CSS selector. |
+ """ |
+ logging.info("action: SendEnterTo %s" % selector) |
+ body = self.driver.find_element_by_tag_name("body") |
+ body.send_keys(Keys.ENTER) |
+ |
+ # Waiting/Displaying actions. |
+ |
+ def IsDisplayed(self, selector): |
+ """Returns False if an element doesn't exist in the DOM or is 100% |
+ transparent. Otherwise, returns True even if it's covered by another |
+ element or it's out viewing area. |
+ |
+ Args: |
+ selector: The element CSS selector. |
+ """ |
+ logging.info("action: IsDisplayed %s" % selector) |
+ try: |
+ element = self.driver.find_element_by_css_selector(selector) |
+ return element.is_displayed() |
+ except Exception: |
+ return False |
+ |
+ def Wait(self, duration): |
+ """Wait for a duration in seconds. This needs to be used in potentially |
+ infinite loops, to limit their running time. |
+ |
+ Args: |
+ duration: The time to wait in seconds. |
+ """ |
+ logging.info("action: Wait %s" % duration) |
+ time.sleep(duration) |
+ self.remaining_time_to_wait -= 1 |
+ if self.remaining_time_to_wait < 0: |
+ raise Exception("Tests took more time than expected for the following " |
+ "website : %s \n" % self.name) |
+ |
+ def WaitUntilDisplayed(self, selector, timeout=10): |
+ """Waits until an element is displayed. |
+ |
+ Args: |
+ selector: The element CSS selector. |
+ timeout: The maximum waiting time in seconds before failing. |
+ """ |
+ if not self.IsDisplayed(selector): |
+ self.Wait(1) |
+ timeout = timeout - 1 |
+ if (timeout <= 0): |
+ raise Exception("Error: Element %s not shown before timeout is " |
+ "finished for the following website: %s" |
+ % (selector, self.name)) |
+ else: |
+ self.WaitUntilDisplayed(selector, timeout) |
+ |
+ # Form actions. |
+ |
+ def FillPasswordInto(self, selector): |
+ """If the testing mode is the Autofilled mode, compares the website |
+ password to the DOM state. |
+ If the testing mode is the NotAutofilled mode, checks that the DOM state |
+ is empty. |
+ Then, fills the input with the Website password. |
+ |
+ Args: |
+ selector: The password input CSS selector. |
+ |
+ Raises: |
+ Exception: An exception is raised if the DOM value of the password is |
+ different than the one we expected. |
+ """ |
+ logging.info("action: FillPasswordInto %s" % selector) |
+ |
+ password_element = self.driver.find_element_by_css_selector(selector) |
+ # Chrome protects the password inputs and doesn't fill them until |
+ # the user interacts with the page. To be sure that such thing has |
+ # happened we click on the password fields or one of its ancestors. |
+ element = password_element |
+ while True: |
+ try: |
+ element.click() |
+ break |
+ except Exception: |
+ try: |
+ element = element.parent |
+ except AttributeError: |
+ raise Exception("Error: unable to find a clickable element to " |
+ "release the password protection for the following website: %s \n" |
+ % (self.name)) |
+ |
+ if self.mode == self.Mode.AUTOFILLED: |
+ autofilled_password = password_element.get_attribute("value") |
+ if autofilled_password != self.password: |
+ raise Exception("Error: autofilled password is different from the one " |
+ "we just saved for the following website : %s p1: %s " |
+ "p2:%s \n" % (self.name, |
+ password_element.get_attribute("value"), |
+ self.password)) |
+ |
+ elif self.mode == self.Mode.NOT_AUTOFILLED: |
+ autofilled_password = password_element.get_attribute("value") |
+ if autofilled_password: |
+ raise Exception("Error: password is autofilled when it shouldn't be " |
+ "for the following website : %s \n" |
+ % self.name) |
+ |
+ password_element.send_keys(self.password) |
+ |
+ def FillUsernameInto(self, selector): |
+ """If the testing mode is the Autofilled mode, compares the website |
+ username to the input value. Then, fills the input with the website |
+ username. |
+ |
+ Args: |
+ selector: The username input CSS selector. |
+ |
+ Raises: |
+ Exception: An exception is raised if the DOM value of the username is |
+ different that the one we expected. |
+ """ |
+ logging.info("action: FillUsernameInto %s" % selector) |
+ username_element = self.driver.find_element_by_css_selector(selector) |
+ |
+ if (self.mode == self.Mode.AUTOFILLED and not self.username_not_auto): |
+ if not (username_element.get_attribute("value") == self.username): |
+ raise Exception("Error: autofilled username is different form the one " |
+ "we just saved for the following website : %s \n" % |
+ self.name) |
+ else: |
+ username_element.clear() |
+ username_element.send_keys(self.username) |
+ |
+ def Submit(self, selector): |
+ """Finds an element using CSS Selector and calls its submit() handler. |
+ |
+ Args: |
+ selector: The input CSS selector. |
+ """ |
+ logging.info("action: Submit %s" % selector) |
+ element = self.driver.find_element_by_css_selector(selector) |
+ element.submit() |
+ |
+ # Login/Logout Methods |
+ |
+ def Login(self): |
+ """Login Method. Has to be overloaded by the WebsiteTest test.""" |
+ raise NotImplementedError("Login is not implemented.") |
+ |
+ def LoginWhenAutofilled(self): |
+ """Logs in and checks that the password is autofilled.""" |
+ self.mode = self.Mode.AUTOFILLED |
+ self.Login() |
+ |
+ def LoginWhenNotAutofilled(self): |
+ """Logs in and checks that the password is not autofilled.""" |
+ self.mode = self.Mode.NOT_AUTOFILLED |
+ self.Login() |
+ |
+ def Logout(self): |
+ """Logout Method. Has to be overloaded by the Website test.""" |
+ raise NotImplementedError("Logout is not implemented.") |
+ |
+ # Tests |
+ |
+ def WrongLoginTest(self): |
+ """Does the wrong login test: Tries to login with a wrong password and |
+ checks that the password is not saved. |
+ |
+ Raises: |
+ Exception: An exception is raised if the test fails: If there is a |
+ problem when performing the login (ex: the login button is not |
+ available ...), if the state of the username and password fields is |
+ not like we expected or if the password is saved. |
+ """ |
+ logging.info("\nWrong Login Test for %s \n" % self.name) |
+ correct_password = self.password |
+ self.password = self.password + "1" |
+ self.LoginWhenNotAutofilled() |
+ self.password = correct_password |
+ self.Wait(2) |
+ self.environment.SwitchToInternals() |
+ self.environment.CheckForNewMessage( |
+ environment.MESSAGE_SAVE, |
+ False, |
+ "Error: password manager thinks that a login with wrong password was " |
+ "successful for the following website : %s \n" % self.name) |
+ self.environment.SwitchFromInternals() |
+ |
+ def SuccessfulLoginTest(self): |
+ """Does the successful login when the password is not expected to be |
+ autofilled test: Checks that the password is not autofilled, tries to login |
+ with a right password and checks if the password is saved. Then logs out. |
+ |
+ Raises: |
+ Exception: An exception is raised if the test fails: If there is a |
+ problem when performing the login and the logout (ex: the login |
+ button is not available ...), if the state of the username and |
+ password fields is not like we expected or if the password is not |
+ saved. |
+ """ |
+ logging.info("\nSuccessful Login Test for %s \n" % self.name) |
+ self.LoginWhenNotAutofilled() |
+ self.Wait(2) |
+ self.environment.SwitchToInternals() |
+ self.environment.CheckForNewMessage( |
+ environment.MESSAGE_SAVE, |
+ True, |
+ "Error: password manager hasn't detected a successful login for the " |
+ "following website : %s \n" |
+ % self.name) |
+ self.environment.SwitchFromInternals() |
+ self.Logout() |
+ |
+ def SuccessfulLoginWithAutofilledPasswordTest(self): |
+ """Does the successful login when the password is expected to be autofilled |
+ test: Checks that the password is autofilled, tries to login with the |
+ autofilled password and checks if the password is saved. Then logs out. |
+ |
+ Raises: |
+ Exception: An exception is raised if the test fails: If there is a |
+ problem when performing the login and the logout (ex: the login |
+ button is not available ...), if the state of the username and |
+ password fields is not like we expected or if the password is not |
+ saved. |
+ """ |
+ logging.info("\nSuccessful Login With Autofilled Password" |
+ " Test %s \n" % self.name) |
+ self.LoginWhenAutofilled() |
+ self.Wait(2) |
+ self.environment.SwitchToInternals() |
+ self.environment.CheckForNewMessage( |
+ environment.MESSAGE_SAVE, |
+ True, |
+ "Error: password manager hasn't detected a successful login for the " |
+ "following website : %s \n" |
+ % self.name) |
+ self.environment.SwitchFromInternals() |
+ self.Logout() |
+ |
+ def PromptTest(self): |
+ """Does the prompt test: Tries to login with a wrong password and |
+ checks that the prompt is not shown. Then tries to login with a right |
+ password and checks that the prompt is not shown. |
+ |
+ Raises: |
+ Exception: An exception is raised if the test fails: If there is a |
+ problem when performing the login (ex: the login button is not |
+ available ...), if the state of the username and password fields is |
+ not like we expected or if the prompt is not shown for the right |
+ password or is shown for a wrong one. |
+ """ |
+ logging.info("\nPrompt Test for %s \n" % self.name) |
+ correct_password = self.password |
+ self.password = self.password + "1" |
+ self.LoginWhenNotAutofilled() |
+ self.password = correct_password |
+ self.Wait(2) |
+ self.environment.SwitchToInternals() |
+ self.environment.CheckForNewMessage( |
+ environment.MESSAGE_ASK, |
+ False, |
+ "Error: password manager thinks that a login with wrong password was " |
+ "successful for the following website : %s \n" % self.name) |
+ self.environment.SwitchFromInternals() |
+ |
+ self.LoginWhenNotAutofilled() |
+ self.Wait(2) |
+ self.environment.SwitchToInternals() |
+ self.environment.CheckForNewMessage( |
+ environment.MESSAGE_ASK, |
+ True, |
+ "Error: password manager thinks that a login with wrong password was " |
+ "successful for the following website : %s \n" % self.name) |
+ self.environment.SwitchFromInternals() |