OLD | NEW |
---|---|
1 # Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """The testing Environment class.""" | 5 """The testing Environment class.""" |
6 | 6 |
7 import logging | |
8 import shutil | 7 import shutil |
9 import sys | |
10 import time | 8 import time |
11 import traceback | |
12 from xml.etree import ElementTree | 9 from xml.etree import ElementTree |
13 from xml.sax.saxutils import escape | |
14 | |
15 sys.path.insert(0, '../../../../third_party/webdriver/pylib/') | |
16 | 10 |
17 from selenium import webdriver | 11 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 | 12 from selenium.webdriver.chrome.options import Options |
21 | 13 |
22 | 14 |
23 # Message strings to look for in chrome://password-manager-internals | 15 # Message strings to look for in chrome://password-manager-internals |
24 MESSAGE_ASK = "Message: Decision: ASK the user" | 16 MESSAGE_ASK = "Message: Decision: ASK the user" |
25 MESSAGE_SAVE = "Message: Decision: SAVE the password" | 17 MESSAGE_SAVE = "Message: Decision: SAVE the password" |
26 | 18 |
27 | 19 |
28 class TestResult: | 20 class TestResult: |
29 """Stores the information related to a test result. """ | 21 """Stores the information related to a test result. """ |
30 def __init__(self, name, test_type, successful, message): | 22 def __init__(self, name, test_type, successful, message): |
31 """Creates a new TestResult. | 23 """Creates a new TestResult. |
32 | 24 |
33 Args: | 25 Args: |
34 name: The tested website name. | 26 name: The tested website name. |
35 test_type: The test type. | 27 test_type: The test type. |
36 successful: Whether or not the test was successful. | 28 successful: Whether or not the test was successful. |
37 message: The error message of the test. | 29 message: The error message of the test. |
38 """ | 30 """ |
39 self.name = name | 31 self.name = name |
40 self.test_type = test_type | 32 self.test_type = test_type |
41 self.successful = successful | 33 self.successful = successful |
42 self.message = message | 34 self.message = message |
43 | 35 |
44 | 36 |
45 class Environment: | 37 class Environment: |
46 """Sets up the testing Environment. """ | 38 """Sets up the testing Environment. """ |
47 | 39 |
48 def __init__(self, chrome_path, chromedriver_path, profile_path, | 40 def __init__(self, chrome_path, chromedriver_path, profile_path, |
49 passwords_path, enable_automatic_password_saving, | 41 passwords_path, enable_automatic_password_saving): |
50 numeric_level=None, log_to_console=False, log_file=""): | |
51 """Creates a new testing Environment. | 42 """Creates a new testing Environment. |
52 | 43 |
53 Args: | 44 Args: |
54 chrome_path: The chrome binary file. | 45 chrome_path: The chrome binary file. |
55 chromedriver_path: The chromedriver binary file. | 46 chromedriver_path: The chromedriver binary file. |
56 profile_path: The chrome testing profile folder. | 47 profile_path: The chrome testing profile folder. |
57 passwords_path: The usernames and passwords file. | 48 passwords_path: The usernames and passwords file. |
58 enable_automatic_password_saving: If True, the passwords are going to be | 49 enable_automatic_password_saving: If True, the passwords are going to be |
59 saved without showing the prompt. | 50 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 | 51 |
65 Raises: | 52 Raises: |
66 Exception: An exception is raised if |profile_path| folder could not be | 53 Exception: An exception is raised if |profile_path| folder could not be |
67 removed. | 54 removed. |
68 """ | 55 """ |
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 | 56 |
86 # Cleaning the chrome testing profile folder. | 57 # Cleaning the chrome testing profile folder. |
87 try: | 58 shutil.rmtree(profile_path, ignore_errors=True) |
engedy
2015/03/26 16:18:55
nit: Given that this masks both kinds of errors (i
vabr (Chromium)
2015/03/26 18:01:50
Done.
| |
88 shutil.rmtree(profile_path) | 59 options = Options() |
89 except Exception, e: | 60 self.enable_automatic_password_saving = enable_automatic_password_saving |
90 pass | 61 if enable_automatic_password_saving: |
91 # If |chrome_path| is not defined, this means that we are in the dashboard | 62 options.add_argument("enable-automatic-password-saving") |
engedy
2015/03/26 16:18:55
Could you please mention in the CL description tha
vabr (Chromium)
2015/03/26 18:01:50
This was not so much removed, as simplified. In th
engedy
2015/03/26 18:04:54
Acknowledged.
| |
92 # website, and we just need to get the list of all websites. In this case, | 63 # Chrome path. |
93 # we don't need to initilize the webdriver. | 64 options.binary_location = chrome_path |
94 if chrome_path: | 65 # Chrome testing profile path. |
95 options = Options() | 66 options.add_argument("user-data-dir=%s" % profile_path) |
96 self.enable_automatic_password_saving = enable_automatic_password_saving | |
97 if enable_automatic_password_saving: | |
98 options.add_argument("enable-automatic-password-saving") | |
99 # Chrome path. | |
100 options.binary_location = chrome_path | |
101 # Chrome testing profile path. | |
102 options.add_argument("user-data-dir=%s" % profile_path) | |
103 | 67 |
104 # The webdriver. It's possible to choose the port the service is going to | 68 # The webdriver. It's possible to choose the port the service is going to |
105 # run on. If it's left to 0, a free port will be found. | 69 # run on. If it's left to 0, a free port will be found. |
106 self.driver = webdriver.Chrome(chromedriver_path, 0, options) | 70 self.driver = webdriver.Chrome(chromedriver_path, 0, options) |
107 # The password internals window. | 71 # The password internals window. |
108 self.internals_window = self.driver.current_window_handle | 72 self.internals_window = self.driver.current_window_handle |
109 if passwords_path: | 73 if passwords_path: |
110 # An xml tree filled with logins and passwords. | 74 # An xml tree filled with logins and passwords. |
111 self.passwords_tree = ElementTree.parse(passwords_path).getroot() | 75 self.passwords_tree = ElementTree.parse(passwords_path).getroot() |
112 else: | 76 else: |
113 raise Exception("Error: |passwords_path| needs to be provided if" | 77 raise Exception("Error: |passwords_path| needs to be provided if" |
114 "|chrome_path| is provided, otherwise the tests could not be run") | 78 "|chrome_path| is provided, otherwise the tests could not be run") |
115 # Password internals page. | 79 # Password internals page. |
116 self.internals_page = "chrome://password-manager-internals/" | 80 self.internals_page = "chrome://password-manager-internals/" |
117 # The Website window. | 81 # The Website window. |
118 self.website_window = None | 82 self.website_window = None |
119 # The WebsiteTests list. | 83 # The WebsiteTests list. |
120 self.websitetests = [] | 84 self.websitetests = [] |
121 # Map messages to the number of their appearance in the log. | 85 # Map messages to the number of their appearance in the log. |
122 self.message_count = dict() | 86 self.message_count = { MESSAGE_ASK: 0, MESSAGE_SAVE: 0 } |
123 self.message_count[MESSAGE_ASK] = 0 | |
124 self.message_count[MESSAGE_SAVE] = 0 | |
125 # The tests needs two tabs to work. A new tab is opened with the first | 87 # The tests needs two tabs to work. A new tab is opened with the first |
126 # GoTo. This is why we store here whether or not it's the first time to | 88 # GoTo. This is why we store here whether or not it's the first time to |
127 # execute GoTo. | 89 # execute GoTo. |
128 self.first_go_to = True | 90 self.first_go_to = True |
129 # List of all tests results. | 91 # List of all tests results. |
130 self.tests_results = [] | 92 self.tests_results = [] |
131 | 93 |
132 def AddWebsiteTest(self, websitetest): | 94 def AddWebsiteTest(self, websitetest): |
133 """Adds a WebsiteTest to the testing Environment. | 95 """Adds a WebsiteTest to the testing Environment. |
134 | 96 |
97 TODO(vabr): Currently, this is only called at most once for each | |
98 Environment instance. That is because to run all tests efficiently in | |
99 parallel, each test gets its own process spawned (outside of Python). | |
100 That makes sense, but then we should flatten the hierarchy of calls | |
101 and consider making the 1:1 relation of environment to tests more | |
102 explicit. | |
103 | |
135 Args: | 104 Args: |
136 websitetest: The WebsiteTest instance to be added. | 105 websitetest: The WebsiteTest instance to be added. |
137 """ | 106 """ |
138 websitetest.environment = self | 107 websitetest.environment = self |
139 if hasattr(self, "driver"): | 108 websitetest.driver = self.driver |
engedy
2015/03/26 16:18:55
nit: Please add a todo to make this a property.
vabr (Chromium)
2015/03/26 18:01:49
Done.
| |
140 websitetest.driver = self.driver | 109 if not websitetest.username: |
141 if hasattr(self, "passwords_tree") and self.passwords_tree is not None: | 110 username_tag = ( |
142 if not websitetest.username: | 111 self.passwords_tree.find( |
143 username_tag = ( | 112 ".//*[@name='%s']/username" % websitetest.name)) |
144 self.passwords_tree.find( | 113 websitetest.username = username_tag.text |
145 ".//*[@name='%s']/username" % websitetest.name)) | 114 if not websitetest.password: |
146 if username_tag.text: | 115 password_tag = ( |
147 websitetest.username = username_tag.text | 116 self.passwords_tree.find( |
148 if not websitetest.password: | 117 ".//*[@name='%s']/password" % websitetest.name)) |
149 password_tag = ( | 118 websitetest.password = password_tag.text |
150 self.passwords_tree.find( | |
151 ".//*[@name='%s']/password" % websitetest.name)) | |
152 if password_tag.text: | |
153 websitetest.password = password_tag.text | |
154 self.websitetests.append(websitetest) | 119 self.websitetests.append(websitetest) |
155 | 120 |
156 def ClearCache(self, clear_passwords): | 121 def ClearCache(self, clear_passwords): |
157 """Clear the browser cookies. If |clear_passwords| is true, clear all the | 122 """Clear the browser cookies. If |clear_passwords| is true, clear all the |
158 saved passwords too. | 123 saved passwords too. |
159 | 124 |
160 Args: | 125 Args: |
161 clear_passwords : Clear all the passwords if the bool value is true. | 126 clear_passwords : Clear all the passwords if the bool value is true. |
162 """ | 127 """ |
163 logging.info("\nClearCache\n") | |
164 self.driver.get("chrome://settings/clearBrowserData") | 128 self.driver.get("chrome://settings/clearBrowserData") |
165 self.driver.switch_to_frame("settings") | 129 self.driver.switch_to_frame("settings") |
166 script = ( | 130 script = ( |
167 "if (!document.querySelector('#delete-cookies-checkbox').checked)" | 131 "if (!document.querySelector('#delete-cookies-checkbox').checked)" |
168 " document.querySelector('#delete-cookies-checkbox').click();" | 132 " document.querySelector('#delete-cookies-checkbox').click();" |
169 ) | 133 ) |
170 negation = "" | 134 negation = "" |
171 if clear_passwords: | 135 if clear_passwords: |
172 negation = "!" | 136 negation = "!" |
173 script += ( | 137 script += ( |
174 "if (%sdocument.querySelector('#delete-passwords-checkbox').checked)" | 138 "if (%sdocument.querySelector('#delete-passwords-checkbox').checked)" |
175 " document.querySelector('#delete-passwords-checkbox').click();" | 139 " document.querySelector('#delete-passwords-checkbox').click();" |
176 % negation) | 140 % negation) |
177 script += "document.querySelector('#clear-browser-data-commit').click();" | 141 script += "document.querySelector('#clear-browser-data-commit').click();" |
178 self.driver.execute_script(script) | 142 self.driver.execute_script(script) |
179 time.sleep(2) | 143 time.sleep(2) |
180 # Every time we do something to the cache let's enable password saving. | 144 # Every time we do something to the cache let's enable password saving. |
181 # TODO(melandory): We should check why it's off in a first place. | 145 # TODO(melandory): We should check why it's off in a first place. |
182 # TODO(melandory): Investigate, maybe there is no need to enable it that | 146 # TODO(melandory): Investigate, maybe there is no need to enable it that |
183 # often. | 147 # often. |
184 self.EnablePasswordsSaving() | 148 self.EnablePasswordsSaving() |
185 | 149 |
186 def EnablePasswordsSaving(self): | 150 def EnablePasswordsSaving(self): |
187 logging.info("\nEnablePasswordSaving\n") | |
188 self.driver.get("chrome://settings") | 151 self.driver.get("chrome://settings") |
189 self.driver.switch_to_frame("settings") | 152 self.driver.switch_to_frame("settings") |
190 script = "document.getElementById('advanced-settings-expander').click();" | 153 script = "document.getElementById('advanced-settings-expander').click();" |
191 self.driver.execute_script(script) | 154 self.driver.execute_script(script) |
192 time.sleep(2) | 155 time.sleep(2) |
193 script = ( | 156 script = ( |
194 "if (!document.querySelector('#password-manager-enabled').checked)" | 157 "if (!document.querySelector('#password-manager-enabled').checked)" |
195 "{ document.querySelector('#password-manager-enabled').click();}") | 158 "{ document.querySelector('#password-manager-enabled').click();}") |
196 self.driver.execute_script(script) | 159 self.driver.execute_script(script) |
197 time.sleep(2) | 160 time.sleep(2) |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
287 Exception: An exception is raised in case the result does not match the | 250 Exception: An exception is raised in case the result does not match the |
288 expectation | 251 expectation |
289 """ | 252 """ |
290 if (self._DidMessageAppearUntilTimeout(log_message, timeout) != | 253 if (self._DidMessageAppearUntilTimeout(log_message, timeout) != |
291 message_should_show_up): | 254 message_should_show_up): |
292 raise Exception(error_message) | 255 raise Exception(error_message) |
293 | 256 |
294 def AllTests(self, prompt_test): | 257 def AllTests(self, prompt_test): |
295 """Runs the tests on all the WebsiteTests. | 258 """Runs the tests on all the WebsiteTests. |
296 | 259 |
260 TODO(vabr): Currently, "all tests" always means one. | |
261 | |
297 Args: | 262 Args: |
298 prompt_test: If True, tests caring about showing the save-password | 263 prompt_test: If True, tests caring about showing the save-password |
299 prompt are going to be run, otherwise tests which don't care about | 264 prompt are going to be run, otherwise tests which don't care about |
300 the prompt are going to be run. | 265 the prompt are going to be run. |
301 | 266 |
302 Raises: | 267 Raises: |
303 Exception: An exception is raised if the tests fail. | 268 Exception: An exception is raised if the tests fail. |
304 """ | 269 """ |
305 if prompt_test: | 270 if prompt_test: |
306 self.PromptTestList(self.websitetests) | 271 self.PromptTestList(self.websitetests) |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
380 except Exception as e: | 345 except Exception as e: |
381 successful = False | 346 successful = False |
382 error = e.message | 347 error = e.message |
383 self.tests_results.append(TestResult(websitetest.name, "prompt", | 348 self.tests_results.append(TestResult(websitetest.name, "prompt", |
384 successful, error)) | 349 successful, error)) |
385 | 350 |
386 def Quit(self): | 351 def Quit(self): |
387 """Closes the tests.""" | 352 """Closes the tests.""" |
388 # Close the webdriver. | 353 # Close the webdriver. |
389 self.driver.quit() | 354 self.driver.quit() |
OLD | NEW |