Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(134)

Unified Diff: components/test/data/password_manager/website.py

Issue 273523004: Password Manager testing automation (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: new arch Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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()

Powered by Google App Engine
This is Rietveld 408576698