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

Side by Side Diff: components/test/data/password_manager/environment.py

Issue 743013004: Move password manager automated tests to its own subdirectory (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 1 month 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 unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """The testing Environment class."""
6
7 import logging
8 import shutil
9 import sys
10 import time
11 import traceback
12 from xml.etree import ElementTree
13 from xml.sax.saxutils import escape
14
15 sys.path.insert(0, '../../../../third_party/webdriver/pylib/')
16
17 from selenium import webdriver
18 from selenium.common.exceptions import NoSuchElementException
19 from selenium.common.exceptions import WebDriverException
20 from selenium.webdriver.chrome.options import Options
21
22
23 # Message strings to look for in chrome://password-manager-internals
24 MESSAGE_ASK = "Message: Decision: ASK the user"
25 MESSAGE_SAVE = "Message: Decision: SAVE the password"
26
27
28 class TestResult:
29 """Stores the information related to a test result. """
30 def __init__(self, name, test_type, successful, message):
31 """Creates a new TestResult.
32
33 Args:
34 name: The tested website name.
35 test_type: The test type.
36 successful: Whether or not the test was successful.
37 message: The error message of the test.
38 """
39 self.name = name
40 self.test_type = test_type
41 self.successful = successful
42 self.message = message
43
44
45 class Environment:
46 """Sets up the testing Environment. """
47
48 def __init__(self, chrome_path, chromedriver_path, profile_path,
49 passwords_path, enable_automatic_password_saving,
50 numeric_level=None, log_to_console=False, log_file=""):
51 """Creates a new testing Environment.
52
53 Args:
54 chrome_path: The chrome binary file.
55 chromedriver_path: The chromedriver binary file.
56 profile_path: The chrome testing profile folder.
57 passwords_path: The usernames and passwords file.
58 enable_automatic_password_saving: If True, the passwords are going to be
59 saved without showing the prompt.
60 numeric_level: The log verbosity.
61 log_to_console: If True, the debug logs will be shown on the console.
62 log_file: The file where to store the log. If it's empty, the log will
63 not be stored.
64
65 Raises:
66 Exception: An exception is raised if |profile_path| folder could not be
67 removed.
68 """
69 # Setting up the login.
70 if numeric_level is not None:
71 if log_file:
72 # Set up logging to file.
73 logging.basicConfig(level=numeric_level,
74 filename=log_file,
75 filemode='w')
76
77 if log_to_console:
78 console = logging.StreamHandler()
79 console.setLevel(numeric_level)
80 # Add the handler to the root logger.
81 logging.getLogger('').addHandler(console)
82
83 elif log_to_console:
84 logging.basicConfig(level=numeric_level)
85
86 # Cleaning the chrome testing profile folder.
87 try:
88 shutil.rmtree(profile_path)
89 except Exception, e:
90 # The tests execution can continue, but this make them less stable.
91 logging.error("Error: Could not wipe the chrome profile directory (%s). \
92 This affects the stability of the tests. Continuing to run tests."
93 % e)
94 # If |chrome_path| is not defined, this means that we are in the dashboard
95 # website, and we just need to get the list of all websites. In this case,
96 # we don't need to initilize the webdriver.
97 if chrome_path:
98 options = Options()
99 self.enable_automatic_password_saving = enable_automatic_password_saving
100 if enable_automatic_password_saving:
101 options.add_argument("enable-automatic-password-saving")
102 # Chrome path.
103 options.binary_location = chrome_path
104 # Chrome testing profile path.
105 options.add_argument("user-data-dir=%s" % profile_path)
106
107 # The webdriver. It's possible to choose the port the service is going to
108 # run on. If it's left to 0, a free port will be found.
109 self.driver = webdriver.Chrome(chromedriver_path, 0, options)
110 # The password internals window.
111 self.internals_window = self.driver.current_window_handle
112 if passwords_path:
113 # An xml tree filled with logins and passwords.
114 self.passwords_tree = ElementTree.parse(passwords_path).getroot()
115 else:
116 raise Exception("Error: |passwords_path| needs to be provided if"
117 "|chrome_path| is provided, otherwise the tests could not be run")
118 # Password internals page.
119 self.internals_page = "chrome://password-manager-internals/"
120 # The Website window.
121 self.website_window = None
122 # The WebsiteTests list.
123 self.websitetests = []
124 # The enabled WebsiteTests list.
125 self.working_tests = []
126 # The disabled WebsiteTests list.
127 self.disabled_tests = []
128 # Map messages to the number of their appearance in the log.
129 self.message_count = dict()
130 self.message_count[MESSAGE_ASK] = 0
131 self.message_count[MESSAGE_SAVE] = 0
132 # The tests needs two tabs to work. A new tab is opened with the first
133 # GoTo. This is why we store here whether or not it's the first time to
134 # execute GoTo.
135 self.first_go_to = True
136 # List of all tests results.
137 self.tests_results = []
138
139 def AddWebsiteTest(self, websitetest, disabled=False):
140 """Adds a WebsiteTest to the testing Environment.
141
142 Args:
143 websitetest: The WebsiteTest instance to be added.
144 disabled: Whether test is disabled.
145 """
146 websitetest.environment = self
147 if hasattr(self, "driver"):
148 websitetest.driver = self.driver
149 if hasattr(self, "passwords_tree") and self.passwords_tree is not None:
150 if not websitetest.username:
151 username_tag = (
152 self.passwords_tree.find(
153 ".//*[@name='%s']/username" % websitetest.name))
154 if username_tag.text:
155 websitetest.username = username_tag.text
156 if not websitetest.password:
157 password_tag = (
158 self.passwords_tree.find(
159 ".//*[@name='%s']/password" % websitetest.name))
160 if password_tag.text:
161 websitetest.password = password_tag.text
162 self.websitetests.append(websitetest)
163 if disabled:
164 self.disabled_tests.append(websitetest.name)
165 else:
166 self.working_tests.append(websitetest.name)
167
168 def ClearCache(self, clear_passwords):
169 """Clear the browser cookies. If |clear_passwords| is true, clear all the
170 saved passwords too.
171
172 Args:
173 clear_passwords : Clear all the passwords if the bool value is true.
174 """
175 logging.info("\nClearCache\n")
176 self.driver.get("chrome://settings/clearBrowserData")
177 self.driver.switch_to_frame("settings")
178 script = (
179 "if (!document.querySelector('#delete-cookies-checkbox').checked)"
180 " document.querySelector('#delete-cookies-checkbox').click();"
181 )
182 negation = ""
183 if clear_passwords:
184 negation = "!"
185 script += (
186 "if (%sdocument.querySelector('#delete-passwords-checkbox').checked)"
187 " document.querySelector('#delete-passwords-checkbox').click();"
188 % negation)
189 script += "document.querySelector('#clear-browser-data-commit').click();"
190 self.driver.execute_script(script)
191 time.sleep(2)
192
193 def OpenTabAndGoToInternals(self, url):
194 """If there is no |self.website_window|, opens a new tab and navigates to
195 |url| in the new tab. Navigates to the passwords internals page in the
196 first tab. Raises an exception otherwise.
197
198 Args:
199 url: Url to go to in the new tab.
200
201 Raises:
202 Exception: An exception is raised if |self.website_window| already
203 exists.
204 """
205 if self.website_window:
206 raise Exception("Error: The window was already opened.")
207
208 self.driver.get("chrome://newtab")
209 # There is no straightforward way to open a new tab with chromedriver.
210 # One work-around is to go to a website, insert a link that is going
211 # to be opened in a new tab, click on it.
212 a = self.driver.execute_script(
213 "var a = document.createElement('a');"
214 "a.target = '_blank';"
215 "a.href = arguments[0];"
216 "a.innerHTML = '.';"
217 "document.body.appendChild(a);"
218 "return a;",
219 url)
220
221 a.click()
222 time.sleep(1)
223
224 self.website_window = self.driver.window_handles[-1]
225 self.driver.get(self.internals_page)
226 self.driver.switch_to_window(self.website_window)
227
228 def SwitchToInternals(self):
229 """Switches from the Website window to internals tab."""
230 self.driver.switch_to_window(self.internals_window)
231
232 def SwitchFromInternals(self):
233 """Switches from internals tab to the Website window."""
234 self.driver.switch_to_window(self.website_window)
235
236 def _DidMessageAppearUntilTimeout(self, log_message, timeout):
237 """Checks whether the save password prompt is shown.
238
239 Args:
240 log_message: Log message to look for in the password internals.
241 timeout: There is some delay between the login and the password
242 internals update. The method checks periodically during the first
243 |timeout| seconds if the internals page reports the prompt being
244 shown. If the prompt is not reported shown within the first
245 |timeout| seconds, it is considered not shown at all.
246
247 Returns:
248 True if the save password prompt is shown.
249 False otherwise.
250 """
251 log = self.driver.find_element_by_css_selector("#log-entries")
252 count = log.text.count(log_message)
253
254 if count > self.message_count[log_message]:
255 self.message_count[log_message] = count
256 return True
257 elif timeout > 0:
258 time.sleep(1)
259 return self._DidMessageAppearUntilTimeout(log_message, timeout - 1)
260 else:
261 return False
262
263 def CheckForNewMessage(self, log_message, message_should_show_up,
264 error_message, timeout=3):
265 """Detects whether the save password prompt is shown.
266
267 Args:
268 log_message: Log message to look for in the password internals. The
269 only valid values are the constants MESSAGE_* defined at the
270 beginning of this file.
271 message_should_show_up: Whether or not the message is expected to be
272 shown.
273 error_message: Error message for the exception.
274 timeout: There is some delay between the login and the password
275 internals update. The method checks periodically during the first
276 |timeout| seconds if the internals page reports the prompt being
277 shown. If the prompt is not reported shown within the first
278 |timeout| seconds, it is considered not shown at all.
279
280 Raises:
281 Exception: An exception is raised in case the result does not match the
282 expectation
283 """
284 if (self._DidMessageAppearUntilTimeout(log_message, timeout) !=
285 message_should_show_up):
286 raise Exception(error_message)
287
288 def AllTests(self, prompt_test):
289 """Runs the tests on all the WebsiteTests.
290
291 Args:
292 prompt_test: If True, tests caring about showing the save-password
293 prompt are going to be run, otherwise tests which don't care about
294 the prompt are going to be run.
295
296 Raises:
297 Exception: An exception is raised if the tests fail.
298 """
299 if prompt_test:
300 self.PromptTestList(self.websitetests)
301 else:
302 self.TestList(self.websitetests)
303
304 def DisabledTests(self, prompt_test):
305 """Runs the tests on all the disabled WebsiteTests.
306
307 Args:
308 prompt_test: If True, tests caring about showing the save-password
309 prompt are going to be run, otherwise tests which don't care about
310 the prompt are going to be executed.
311
312 Raises:
313 Exception: An exception is raised if the tests fail.
314 """
315 self.Test(self.disabled_tests, prompt_test)
316
317 def WorkingTests(self, prompt_test):
318 """Runs the tests on all the enabled WebsiteTests.
319
320 Args:
321 prompt_test: If True, tests caring about showing the save-password
322 prompt are going to be run, otherwise tests which don't care about
323 the prompt are going to be executed.
324
325 Raises:
326 Exception: An exception is raised if the tests fail.
327 """
328 self.Test(self.working_tests, prompt_test)
329
330 def Test(self, tests, prompt_test):
331 """Runs the tests on websites named in |tests|.
332
333 Args:
334 tests: A list of the names of the WebsiteTests that are going to be
335 tested.
336 prompt_test: If True, tests caring about showing the save-password
337 prompt are going to be run, otherwise tests which don't care about
338 the prompt are going to be executed.
339
340 Raises:
341 Exception: An exception is raised if the tests fail.
342 """
343 websitetests = []
344 for websitetest in self.websitetests:
345 if websitetest.name in tests:
346 websitetests.append(websitetest)
347
348 if prompt_test:
349 self.PromptTestList(websitetests)
350 else:
351 self.TestList(websitetests)
352
353 def TestList(self, websitetests):
354 """Runs the tests on the websites in |websitetests|.
355
356 Args:
357 websitetests: A list of WebsiteTests that are going to be tested.
358
359 Raises:
360 Exception: An exception is raised if the tests fail.
361 """
362 self.ClearCache(True)
363
364 for websitetest in websitetests:
365 successful = True
366 error = ""
367 try:
368 websitetest.was_run = True
369 websitetest.WrongLoginTest()
370 websitetest.SuccessfulLoginTest()
371 self.ClearCache(False)
372 websitetest.SuccessfulLoginWithAutofilledPasswordTest()
373 self.ClearCache(True)
374 websitetest.SuccessfulLoginTest()
375 self.ClearCache(True)
376 except Exception:
377 successful = False
378 error = traceback.format_exc()
379 self.tests_results.append(TestResult(websitetest.name, "normal",
380 successful, escape(error)))
381
382
383 def PromptTestList(self, websitetests):
384 """Runs the prompt tests on the websites in |websitetests|.
385
386 Args:
387 websitetests: A list of WebsiteTests that are going to be tested.
388
389 Raises:
390 Exception: An exception is raised if the tests fail.
391 """
392 self.ClearCache(True)
393
394 for websitetest in websitetests:
395 successful = True
396 error = ""
397 try:
398 websitetest.was_run = True
399 websitetest.PromptTest()
400 except Exception:
401 successful = False
402 error = traceback.format_exc()
403 self.tests_results.append(TestResult(websitetest.name, "prompt",
404 successful, escape(error)))
405
406 def Quit(self):
407 """Closes the tests."""
408 # Close the webdriver.
409 self.driver.quit()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698