Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 """The testing Environment class.""" | |
| 2 | |
| 3 import logging | |
| 4 import shutil | |
| 5 import time | |
| 6 | |
| 7 from selenium import webdriver | |
| 8 from selenium.common.exceptions import NoSuchElementException | |
| 9 from selenium.common.exceptions import WebDriverException | |
| 10 from selenium.webdriver.chrome.options import Options | |
| 11 from xml.etree import ElementTree | |
| 12 | |
| 13 | |
| 14 class Environment: | |
| 15 """Sets up the testing Environment. """ | |
| 16 | |
| 17 def __init__(self, chrome_path, chromedriver_path, profile_path, | |
| 18 passwords_path, enable_automatic_password_saving, | |
| 19 numeric_level=None, log_to_console=False, log_file=""): | |
| 20 """Creates a new testing Environment. | |
| 21 | |
| 22 Args: | |
| 23 chrome_path: The chrome binary file. | |
| 24 chromedriver_path: The chromedriver binary file. | |
| 25 profile_path: The chrome testing profile folder. | |
| 26 passwords_path: The usernames and passwords file. | |
| 27 enable_automatic_password_saving: If True, the passwords are going to be | |
| 28 saved without showing the prompt. | |
| 29 numeric_level: The log verbosity. | |
| 30 log_to_console: If True, the debug logs will be shown on the console. | |
| 31 log_file: The file where to store the log. If it's empty, the log will | |
| 32 not be stored. | |
| 33 | |
| 34 Raises: | |
| 35 Exception: An exception is raised if |profile_path| folder could not be | |
| 36 removed. | |
| 37 """ | |
| 38 # Setting up the login. | |
| 39 if numeric_level is not None: | |
| 40 if log_file: | |
| 41 # Set up logging to file. | |
| 42 logging.basicConfig(level=numeric_level, | |
| 43 filename=log_file, | |
| 44 filemode='w') | |
| 45 | |
| 46 if log_to_console: | |
| 47 console = logging.StreamHandler() | |
| 48 console.setLevel(numeric_level) | |
| 49 # Add the handler to the root logger. | |
| 50 logging.getLogger('').addHandler(console) | |
| 51 | |
| 52 elif log_to_console: | |
| 53 logging.basicConfig(level=numeric_level) | |
| 54 | |
| 55 # Cleaning the chrome testing profile folder. | |
| 56 try: | |
| 57 shutil.rmtree(profile_path) | |
| 58 except Exception, e: | |
| 59 # The tests execution can continue, but this make them less stable. | |
| 60 logging.error("Error: Could not wipe the chrome profile directory (%s). \ | |
| 61 This affects the stability of the tests. Continuing to run tests." | |
| 62 % e) | |
| 63 options = Options() | |
| 64 if enable_automatic_password_saving: | |
| 65 options.add_argument("enable-automatic-password-saving") | |
| 66 # Chrome path. | |
| 67 options.binary_location = chrome_path | |
| 68 # Chrome testing profile path. | |
| 69 options.add_argument("user-data-dir=%s" % profile_path) | |
| 70 | |
| 71 # The webdriver. It's possible to choose the port the service is going to | |
| 72 # run on. If it's left to 0, a free port will be found. | |
| 73 self.driver = webdriver.Chrome(chromedriver_path, 0, options) | |
| 74 # The password internals window. | |
| 75 self.internals_window = self.driver.current_window_handle | |
| 76 # Password internals page. | |
| 77 self.internals_page = "chrome://password-manager-internals/" | |
| 78 # The Website window. | |
| 79 self.website_window = None | |
| 80 # The WebsiteTests list. | |
| 81 self.websitetests = [] | |
| 82 # An xml tree filled with logins and passwords. | |
| 83 self.passwords_tree = None | |
| 84 if passwords_path: | |
|
vabr (Chromium)
2014/05/22 13:39:24
Once the path to the passwords is no longer option
rchtara
2014/05/22 15:20:56
Done.
| |
| 85 self.passwords_tree = ElementTree.parse(passwords_path).getroot() | |
| 86 # The enabled WebsiteTests list. | |
| 87 self.working_tests = [] | |
| 88 # The number of the save operations done through all the tests. | |
| 89 self.save_count = 0 | |
|
vabr (Chromium)
2014/05/22 13:39:24
I see a potential problem: |save_count| is used to
rchtara
2014/05/22 15:20:56
Done.
| |
| 90 # The tests needs two tabs to work. A new tab is opened with the first | |
| 91 # GoTo. This is why we store here whether or not it's the first time to | |
| 92 # execute GoTo. | |
| 93 self.first_go_to = True | |
| 94 | |
| 95 def AddWebsiteTest(self, websitetest, disabled=False): | |
| 96 """Adds a WebsiteTest to the testing Environment. | |
| 97 | |
| 98 Args: | |
| 99 websitetest: The WebsiteTest instance to be added. | |
| 100 disabled: Whether test is disabled. | |
| 101 """ | |
| 102 websitetest.environment = self | |
| 103 websitetest.driver = self.driver | |
| 104 if self.passwords_tree is not None: | |
| 105 if not websitetest.username: | |
| 106 username_tag = ( | |
| 107 self.passwords_tree.find( | |
| 108 ".//*[@name='%s']/username" % websitetest.name)) | |
| 109 if username_tag.text: | |
| 110 websitetest.username = username_tag.text | |
| 111 if not websitetest.password: | |
| 112 password_tag = ( | |
| 113 self.passwords_tree.find( | |
| 114 ".//*[@name='%s']/password" % websitetest.name)) | |
| 115 if password_tag.text: | |
| 116 websitetest.password = password_tag.text | |
| 117 self.websitetests.append(websitetest) | |
| 118 if not disabled: | |
| 119 self.working_tests.append(websitetest.name) | |
| 120 | |
| 121 def RemoveAllPasswords(self): | |
| 122 """Removes all the stored passwords.""" | |
| 123 logging.info("\nRemoveAllPasswords\n") | |
| 124 self.driver.get("chrome://settings/passwords") | |
| 125 self.driver.switch_to_frame("settings") | |
| 126 while True: | |
| 127 try: | |
| 128 self.driver.execute_script("document.querySelector('" | |
| 129 "#saved-passwords-list .row-delete-button').click()") | |
| 130 time.sleep(1) | |
| 131 except NoSuchElementException: | |
| 132 break | |
| 133 except WebDriverException: | |
| 134 break | |
| 135 | |
| 136 def OpenTabAndGoToInternals(self, url): | |
| 137 """If there is no |self.website_window|, opens a new tab and navigates to | |
| 138 |url| in the new tab. Navigates to the passwords internals page in the | |
| 139 first tab. Raises an exception otherwise. | |
| 140 | |
| 141 Args: | |
| 142 url: Url to go to in the new tab. | |
| 143 | |
| 144 Raises: | |
| 145 Exception: An exception is raised if |self.website_window| already | |
| 146 exists. | |
| 147 """ | |
| 148 if self.website_window: | |
| 149 raise Exception("Error: The window was already opened.") | |
| 150 | |
| 151 self.driver.get("chrome://newtab") | |
| 152 # There is no straightforward way to open a new tab with chromedriver. | |
| 153 # One work-around is to go to a website, insert a link that is going | |
| 154 # to be opened in a new tab, click on it. | |
| 155 a = self.driver.execute_script( | |
| 156 "var a = document.createElement('a');" | |
| 157 "a.target = '_blank';" | |
| 158 "a.href = arguments[0];" | |
| 159 "a.innerHTML = '.';" | |
| 160 "document.body.appendChild(a);" | |
| 161 "return a;", | |
| 162 url) | |
| 163 | |
| 164 a.click() | |
| 165 time.sleep(1) | |
| 166 | |
| 167 self.website_window = self.driver.window_handles[-1] | |
| 168 self.driver.get(self.internals_page) | |
| 169 self.driver.switch_to_window(self.website_window) | |
| 170 | |
| 171 def SwitchToInternals(self): | |
| 172 """Switches from the Website window to internals tab.""" | |
| 173 self.driver.switch_to_window(self.internals_window) | |
| 174 | |
| 175 def SwitchFromInternals(self): | |
| 176 """Switches from internals tab to the Website window.""" | |
| 177 self.driver.switch_to_window(self.website_window) | |
| 178 | |
| 179 def _DidPromptAppearUntilTimeout(self, log_message, timeout): | |
|
vabr (Chromium)
2014/05/22 13:39:24
Looking at the code again, this actually does not
rchtara
2014/05/22 15:20:56
no problem :)
Done.
| |
| 180 """Checks whether the save password prompt is shown. | |
| 181 | |
| 182 Args: | |
| 183 log_message: Log message to look for in the password internals. | |
| 184 timeout: There is some delay between the login and the password | |
| 185 internals update. The method checks periodically during the first | |
| 186 |timeout| seconds if the internals page reports the prompt being | |
| 187 shown. If the prompt is not reported shown within the first | |
| 188 |timeout| seconds, it is considered not shown at all. | |
| 189 | |
| 190 Returns: | |
| 191 True if the save password prompt is shown. | |
| 192 False otherwise. | |
| 193 """ | |
| 194 log = self.driver.find_element_by_css_selector("#log-entries") | |
| 195 count = log.text.count(log_message) | |
| 196 if count > self.save_count: | |
| 197 self.save_count = count | |
| 198 return True | |
| 199 elif timeout > 0: | |
| 200 time.sleep(1) | |
| 201 return self._WaitUntilPromptAppear(log_message, timeout - 1) | |
| 202 else: | |
| 203 return False | |
| 204 | |
| 205 def CheckPrompt(self, log_message, prompt_should_show_up, | |
|
vabr (Chromium)
2014/05/22 13:39:24
Also here: I suggest renaming to CheckForNewMessag
rchtara
2014/05/22 15:20:56
Done.
| |
| 206 error_message, timeout=3): | |
| 207 """Detects whether the save password prompt is shown. | |
| 208 | |
| 209 Args: | |
| 210 log_message: Log message to look for in the password internals. | |
| 211 prompt_should_show_up: Whether or not the prompt is expected to be shown. | |
| 212 error_message: Error message for the exception. | |
| 213 timeout: There is some delay between the login and the password | |
| 214 internals update. The method checks periodically during the first | |
| 215 |timeout| seconds if the internals page reports the prompt being | |
| 216 shown. If the prompt is not reported shown within the first | |
| 217 |timeout| seconds, it is considered not shown at all. | |
| 218 | |
| 219 Raises: | |
| 220 Exception: An exception is raised in case the result does not match the | |
| 221 expectation | |
| 222 """ | |
| 223 if (self._WaitUntilPromptAppear(log_message, timeout) != | |
|
vabr (Chromium)
2014/05/22 13:39:24
Outdated method name.
rchtara
2014/05/22 15:20:56
Done.
| |
| 224 prompt_should_show_up): | |
| 225 raise Exception(error_message) | |
| 226 | |
| 227 def AllTests(self, prompt_test): | |
| 228 """Runs the tests on all the WebsiteTests. | |
| 229 | |
| 230 Args: | |
| 231 prompt_test: If True, tests caring about showing the save-password | |
| 232 prompt are going to be run, otherwise tests which don't care about | |
| 233 the prompt are going to be executed. | |
|
vabr (Chromium)
2014/05/22 13:39:24
nit: Be consistent: either use "run" or "executed"
rchtara
2014/05/22 15:20:56
Done.
| |
| 234 | |
| 235 Raises: | |
| 236 Exception: An exception is raised if the tests fail. | |
| 237 """ | |
| 238 if prompt_test: | |
| 239 self.PromptTestList(self.websitetests) | |
| 240 else: | |
| 241 self.TestList(self.websitetests) | |
| 242 | |
| 243 def WorkingTests(self, prompt_test): | |
| 244 """Runs the tests on all the enabled WebsiteTests. | |
| 245 | |
| 246 Args: | |
| 247 prompt_test: If True, tests caring about showing the save-password | |
| 248 prompt are going to be run, otherwise tests which don't care about | |
| 249 the prompt are going to be executed. | |
| 250 | |
| 251 Raises: | |
| 252 Exception: An exception is raised if the tests fail. | |
| 253 """ | |
| 254 self.Test(self.working_tests, prompt_test) | |
| 255 | |
| 256 def Test(self, tests, prompt_test): | |
| 257 """Runs the tests on websites named in |tests|. | |
| 258 | |
| 259 Args: | |
| 260 tests: A list of the names of the WebsiteTests that are going to be | |
| 261 tested. | |
| 262 prompt_test: If True, tests caring about showing the save-password | |
| 263 prompt are going to be run, otherwise tests which don't care about | |
| 264 the prompt are going to be executed. | |
| 265 | |
| 266 Raises: | |
| 267 Exception: An exception is raised if the tests fail. | |
| 268 """ | |
| 269 websitetests = [] | |
| 270 for websitetest in self.websitetests: | |
| 271 if websitetest.name in tests: | |
| 272 websitetests.append(websitetest) | |
| 273 | |
| 274 if prompt_test: | |
| 275 self.PromptTestList(websitetests) | |
| 276 else: | |
| 277 self.TestList(websitetests) | |
| 278 | |
| 279 def TestList(self, websitetests): | |
| 280 """Runs the tests on the websites in |websitetests|. | |
| 281 | |
| 282 Args: | |
| 283 websitetests: A list of WebsiteTests that are going to be tested. | |
| 284 | |
| 285 Raises: | |
| 286 Exception: An exception is raised if the tests fail. | |
| 287 """ | |
| 288 self.RemoveAllPasswords() | |
| 289 | |
| 290 for websitetest in websitetests: | |
| 291 websitetest.WrongLoginTest() | |
| 292 websitetest.SuccessfulLoginTest() | |
| 293 websitetest.SuccessfulLoginWithAutofilledPasswordTest() | |
| 294 | |
| 295 self.RemoveAllPasswords() | |
| 296 for websitetest in websitetests: | |
| 297 websitetest.SuccessfulLoginTest() | |
| 298 | |
| 299 def PromptTestList(self, websitetests): | |
| 300 """Runs the prompt tests on the websites in |websitetests|. | |
| 301 | |
| 302 Args: | |
| 303 websitetests: A list of WebsiteTests that are going to be tested. | |
| 304 | |
| 305 Raises: | |
| 306 Exception: An exception is raised if the tests fail. | |
| 307 """ | |
| 308 self.RemoveAllPasswords() | |
| 309 | |
| 310 for websitetest in websitetests: | |
| 311 websitetest.PromptTest() | |
| 312 | |
| 313 def Quit(self): | |
| 314 """Closes the tests.""" | |
| 315 # Close the webdriver. | |
| 316 self.driver.quit() | |
| OLD | NEW |