| 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()
|
|
|