Index: components/test/data/password_manager/website.py |
diff --git a/components/test/data/password_manager/website.py b/components/test/data/password_manager/website.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..6b40eafb5c2b5c645d83ac4c5572666ce245ef83 |
--- /dev/null |
+++ b/components/test/data/password_manager/website.py |
@@ -0,0 +1,461 @@ |
+"""Website testing class.""" |
+ |
+ |
+import time |
+ |
+from selenium.common.exceptions import ElementNotVisibleException |
+from selenium.common.exceptions import NoSuchElementException |
+from selenium.common.exceptions import StaleElementReferenceException |
+from selenium.webdriver.common.action_chains import ActionChains |
+from selenium.webdriver.common.keys import Keys |
+ |
+ |
+def _IsOneStringPrefixOfAnother(s1, s2): |
+ l = min(len(s1), len(s2)) |
+ return s1[:l] == s2[:l] |
+ |
+ |
+def _IsOneSubstringOfAnother(s1, s2): |
+ return s1 in s2 or s2 in s1 |
+ |
+ |
+class Website: |
+ """Handles a tested Website.""" |
+ |
+ class Mode: |
+ """Test mode.""" |
+ # Password and username are expected to be autofilled. |
+ Autofilled = 1 |
+ # Password and username are not expected to be autofilled. |
+ NotAutofilled = 2 |
+ |
+ def __init__(self): |
+ pass |
+ |
+ def __init__( |
+ self, name, url, username=None, password=None, |
+ username_not_auto=False): |
+ """Creates a new Website. |
+ |
+ Args: |
+ name: The Website name. |
+ url: The Website URL. |
+ username: The Website username. If it's None, the username is going to be |
+ replaced by the value in the usernames and passwords file. |
+ password: The Website password. If it's None, the password is going to be |
+ replaced by the value in the usernames and passwords file. |
+ 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 |
+ # URL of the Website |
+ self.url = url |
+ # Username of the Website. |
+ self.username = username |
+ # Password of the Website. |
+ self.password = password |
+ # TODO: Remove this as soon as the password internals page is ready. |
+ self.internals_folder = ("file:///usr/local/google/home/rchtara/chrome" |
+ "/python/passwordinternals/internalmock/") |
+ self.internals_file = "" |
+ # Username is not automatically filled. |
+ self.username_not_auto = username_not_auto |
+ # Autofilling mode. |
+ self.mode = self.Mode.NotAutofilled |
+ # Waiting duration before stopping the test. |
+ self.max_duration = 200 |
+ |
+ # Mouse/Keyboard actions. |
+ |
+ def Click(self, selector): |
+ """Clicks on an element. |
+ |
+ Args: |
+ selector: The element CSS selector. |
+ """ |
+ print "action: Click %s" % selector |
+ element = self.driver.find_element_by_css_selector(selector) |
+ element.click() |
+ |
+ def ClickIfAvailable(self, selector): |
+ """Clicks on an element, if it's available. |
+ |
+ Args: |
+ selector: The element CSS selector. |
+ """ |
+ print "action: ClickIfAvailable %s" % selector |
+ try: |
+ element = self.driver.find_element_by_css_selector(selector) |
+ element.click() |
+ except NoSuchElementException: |
+ return False |
+ except StaleElementReferenceException: |
+ return False |
+ |
+ def Enter(self, selector): |
+ """Sends an enter key to an element. |
+ |
+ Args: |
+ selector: The element CSS selector. |
+ """ |
+ print "action: Enter %s" % selector |
+ body = self.driver.find_element_by_tag_name("body") |
+ body.send_keys(Keys.ENTER) |
+ |
+ def GoTo(self, url): |
+ """Goes to a URL. |
+ |
+ Args: |
+ url: The URL. |
+ """ |
+ print "action: GoTo %s" % self.url |
+ self.driver.get(url) |
+ |
+ def Hover(self, selector): |
+ """Hovers on an element, if it's available. |
+ |
+ Args: |
+ selector: The element CSS selector. |
+ """ |
+ print "action: Hover %s" % selector |
+ element = self.driver.find_element_by_css_selector(selector) |
+ hover = ActionChains(self.driver).move_to_element(element) |
+ hover.perform() |
+ |
+ # Waiting/Displaying actions. |
+ |
+ def IsDisplayed(self, selector): |
+ """Checks if an element is displayed. |
+ |
+ Args: |
+ selector: The element CSS selector. |
+ """ |
+ print "action: IsDisplayed %s" % selector |
+ try: |
+ element = self.driver.find_element_by_css_selector(selector) |
+ return element.is_displayed() |
+ except NoSuchElementException: |
+ return False |
+ except StaleElementReferenceException: |
+ return False |
+ |
+ def Wait(self, duration): |
+ """Wait for a duration. |
+ |
+ Args: |
+ duration: The element. |
+ """ |
+ print "action: Wait %s" % duration |
+ time.sleep(duration) |
+ self.max_duration -= 1 |
+ if self.max_duration < 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. |
+ """ |
+ if not self.IsDisplayed(selector): |
+ time.sleep(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) |
+ self.max_duration -= 1 |
+ if self.max_duration < 0: |
+ raise Exception("Tests took more time than expected for the " |
+ "following website : %s \n" % self.name) |
+ |
+ # Form actions. |
+ |
+ def FillPassword(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 that the one we expected. |
+ """ |
+ print "action: FillPassword %s" % selector |
+ password_element = self.driver.find_element_by_css_selector(selector) |
+ if self.mode == self.Mode.Autofilled: |
+ # Chrome protects the password inputs and doesn't fill them until |
+ # the user interacts with the page. To guarantee that, we just |
+ # send a key to the password input. Clicking on the password input was |
+ # tried too, but because the password is sometimes hidden, this didn't |
+ # worked out. |
+ password_element.send_keys("a") |
+ ps = password_element.get_attribute("value")[:-1] |
+ password_element.clear() |
+ password_element.send_keys(ps) |
+ if password_element.get_attribute("value") != 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.NotAutofilled: |
+ # Chrome protects the password inputs and doesn't fill them until |
+ # the user interacts with the page. To guarantee that, we just |
+ # send a key to the password input. Clicking on the password input was |
+ # tried too, but because the password is sometimes hidden, this didn't |
+ # worked out. |
+ password_element.send_keys("a") |
+ ps = password_element.get_attribute("value")[1:] |
+ password_element.clear() |
+ password_element.send_keys(ps) |
+ if ps: |
+ raise Exception("Error: password is autofilled when it shouldn't be " |
+ "for the following website : %s \n" |
+ % self.name) |
+ |
+ # Chrome protects the password inputs and doesn't fill them until |
+ # the user interacts with the page. To guarantee that, we just |
+ # send a key to the password. Clicking on the password input was tried |
+ # too, but because the password is sometime hidden, this didn't worked |
+ # out. |
+ password_element.send_keys("a") |
+ password_element.clear() |
+ password_element.send_keys(self.password) |
+ |
+ def FillUsername(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. |
+ """ |
+ print "action: FillUsername %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 OptionalFillUsername(self, selector): |
+ """Fills the input with the website username, if the input exists. |
+ |
+ Args: |
+ selector: The username input CSS selector. |
+ """ |
+ print "action: OptionalFillUsername %s" % selector |
+ username_element = self.driver.find_element_by_css_selector(selector) |
+ try: |
+ username_element.clear() |
+ username_element.send_keys(self.username) |
+ except ElementNotVisibleException: |
+ pass |
+ |
+ def Submit(self, selector): |
+ """Submits an input. |
+ |
+ Args: |
+ selector: The input CSS selector. |
+ """ |
+ print "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 Website 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.NotAutofilled |
+ self.Login() |
+ |
+ def Logout(self): |
+ """Logout Method. Has to be overloaded by the Website test.""" |
+ raise NotImplementedError("Logout is not implemented.") |
+ |
+ # TestsTools |
+ |
+ def RemoveAllPasswords(self, urls): |
+ """Removes all the saved passwords for the current Website. |
+ |
+ Args: |
+ urls: All the available URLs in the saved passwords list. |
+ """ |
+ if (self.url != ""): |
+ i = 0 |
+ for current_url in urls: |
+ if _IsOneSubstringOfAnother(current_url, self.url): |
+ self.driver.execute_script( |
+ "document.querySelectorAll('#saved-passwords-list " |
+ ".row-delete-button')[%d].click()" % i) |
+ time.sleep(1) # Wait until command is executed. |
+ else: |
+ i = i + 1 |
+ |
+ # TODO(rchtara) Replace this when password internals is ready. |
+ def SwitchToInternals(self): |
+ """Switches from the Website window to internals tab.""" |
+ self.driver.switch_to_window(self.test_environment.internals_window) |
+ self.driver.get(self.internals_folder + self.internals_file) |
+ |
+ # TODO(rchtara) Replace this when password internals is ready. |
+ def SwitchFromInternals(self): |
+ """Switches from internals tab to the Website window.""" |
+ self.driver.switch_to_window(self.test_environment.websitewindow) |
+ |
+ # TODO(rchtara) Replace this when password internals is ready. |
+ def UrlExists(self, element_id, msg): |
+ elments = self.driver.find_element_by_id(element_id) |
+ urls = elments.find_elements_by_class_name("url") |
+ match = False |
+ for u in urls: |
+ if (_IsOneSubstringOfAnother(u.text, self.url) or |
+ _IsOneSubstringOfAnother(u.text, self.name)): |
+ match = True |
+ break |
+ if not match: |
+ raise Exception(msg) |
+ |
+ # TODO(rchtara) Replace this when password internals is ready. |
+ def UrlNotExists(self, element_id, msg): |
+ |
+ elments = self.driver.find_element_by_id(element_id) |
+ urls = elments.find_elements_by_class_name("url") |
+ match = False |
+ for u in urls: |
+ if _IsOneSubstringOfAnother(u.text, self.url): |
+ match = True |
+ break |
+ if match: |
+ raise Exception(msg) |
+ |
+ # Tests |
+ |
+ def WrongLoginTest(self): |
+ """Does the wrong login test: Tries to login with a wrong password and |
+ checks that the prompt is not shown. |
+ |
+ Raises: |
+ Exception: An exception is raised if the tests fail. |
+ """ |
+ print "\nWrong Login Test for %s \n" % self.name |
+ correct_password = self.password |
+ self.password = self.password + "1" |
+ self.LoginWhenNotAutofilled() |
+ self.password = correct_password |
+ self.internals_file = "wrongpass.html" |
+ self.SwitchToInternals() |
+ self.UrlExists("unsuccessfullogins", ("Error: password manager thinks that" |
+ " a login with wrong password was " |
+ "successful for the following " |
+ "website : %s \n" % self.name)) |
+ self.UrlNotExists( |
+ "showedprompts", |
+ "Error: prompt is shown for a wrong password for the following website" |
+ " : %s \n" % self.name) |
+ self.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 that the prompt is shown. Then logs |
+ out. |
+ |
+ Raises: |
+ Exception: An exception is raised if the tests fail. |
+ """ |
+ print "\nSuccessful Login Test for %s \n" % self.name |
+ self.LoginWhenNotAutofilled() |
+ time.sleep(2) |
+ self.internals_file = "rightpass.html" |
+ self.SwitchToInternals() |
+ self.UrlExists( |
+ "successfullogins", |
+ "Error: password manager hasn't detected a successful login for the " |
+ "following website : %s \n" |
+ % self.name) |
+ self.UrlExists( |
+ "showedprompts", |
+ "Error: prompt is not shown for a right password for the following " |
+ "website : %s \n" % self.name) |
+ self.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 a right |
+ password and checks if the that the prompt is shown. Then logs out. |
+ |
+ Raises: |
+ Exception: An exception is raised if the tests fail. |
+ """ |
+ print "\nSuccessful Login With Autofilled Password Test %s \n" % self.name |
+ self.LoginWhenAutofilled() |
+ time.sleep(2) |
+ self.internals_file = "rightpass.html" |
+ self.SwitchToInternals() |
+ self.UrlExists( |
+ "successfullogins", |
+ "Error: password manager hasn't detected a successful login for the " |
+ "following website : %s \n" |
+ % self.name) |
+ self.UrlExists( |
+ "showedprompts", |
+ "Error: prompt is not shown for a right password for the following " |
+ "website : %s \n" % self.name) |
+ self.SwitchFromInternals() |
+ self.Logout() |
+ |
+ def SuccessfulLoginAfterDeletionTest(self): |
+ """Does the successful login after the deletion of the password test: Checks |
+ that the password is not autofilled, tries to login with a right password |
+ and checks if the that the prompt is shown. Then logs out. |
+ |
+ Raises: |
+ Exception: An exception is raised if the tests fail. |
+ """ |
+ print "\nSuccessful Login After Deletion Test for %s \n" % self.name |
+ self.LoginWhenNotAutofilled() |
+ self.internals_file = "rightpass.html" |
+ self.SwitchToInternals() |
+ self.UrlExists( |
+ "successfullogins", "Error: password manager hasn't detected a " |
+ "successful login for the following website : %s " |
+ "\n" |
+ % self.name) |
+ self.UrlExists( |
+ "showedprompts", |
+ "Error: prompt is not shown for a right password for the following " |
+ "website : %s \n" % self.name) |
+ self.SwitchFromInternals() |
+ self.Logout() |