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

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

Issue 273523004: Password Manager testing automation (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: 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/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..fd678221fe9eb27cfd61dd0d3df7496e023ec5a4
--- /dev/null
+++ b/components/test/data/password_manager/websitetest.py
@@ -0,0 +1,400 @@
+"""WebsiteTest testing class."""
+
+import logging
+import time
+
+from selenium.webdriver.common.action_chains import ActionChains
+from selenium.webdriver.common.keys import Keys
+
+
+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(
+ "Message: Decision: SAVE the password",
+ 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(
+ "Message: Decision: SAVE the password",
+ 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(
+ "Message: Decision: SAVE the password",
+ 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(
+ "Message: Decision: ASK the user",
+ 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(
+ "Message: Decision: ASK the user",
+ True,
+ "Error: password manager thinks that a login with wrong password was "
+ "successful for the following website : %s \n" % self.name)
+ self.environment.SwitchFromInternals()

Powered by Google App Engine
This is Rietveld 408576698