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 | |
7 It holds the WebsiteTest instances, provides them with credentials, | |
8 provides clean browser environment, runs the tests, and gathers the | |
9 results. | |
10 """ | |
6 | 11 |
7 import os | 12 import os |
8 import shutil | 13 import shutil |
9 import time | 14 import time |
10 from xml.etree import ElementTree | 15 from xml.etree import ElementTree |
11 | 16 |
12 from selenium import webdriver | 17 from selenium import webdriver |
13 from selenium.webdriver.chrome.options import Options | 18 from selenium.webdriver.chrome.options import Options |
14 | 19 |
15 | 20 |
16 # Message strings to look for in chrome://password-manager-internals | 21 # Message strings to look for in chrome://password-manager-internals. |
17 MESSAGE_ASK = "Message: Decision: ASK the user" | 22 MESSAGE_ASK = "Message: Decision: ASK the user" |
18 MESSAGE_SAVE = "Message: Decision: SAVE the password" | 23 MESSAGE_SAVE = "Message: Decision: SAVE the password" |
19 | 24 |
20 | 25 INTERNALS_PAGE_URL = "chrome://password-manager-internals/" |
21 class TestResult: | |
22 """Stores the information related to a test result. """ | |
23 def __init__(self, name, test_type, successful, message): | |
24 """Creates a new TestResult. | |
25 | |
26 Args: | |
27 name: The tested website name. | |
28 test_type: The test type. | |
29 successful: Whether or not the test was successful. | |
30 message: The error message of the test. | |
31 """ | |
32 self.name = name | |
33 self.test_type = test_type | |
34 self.successful = successful | |
35 self.message = message | |
36 | |
37 | 26 |
38 class Environment: | 27 class Environment: |
39 """Sets up the testing Environment. """ | 28 """Sets up the testing Environment. """ |
40 | 29 |
41 def __init__(self, chrome_path, chromedriver_path, profile_path, | 30 def __init__(self, chrome_path, chromedriver_path, profile_path, |
42 passwords_path, enable_automatic_password_saving): | 31 passwords_path, enable_automatic_password_saving): |
43 """Creates a new testing Environment. | 32 """Creates a new testing Environment, starts Chromedriver. |
44 | 33 |
45 Args: | 34 Args: |
46 chrome_path: The chrome binary file. | 35 chrome_path: The chrome binary file. |
47 chromedriver_path: The chromedriver binary file. | 36 chromedriver_path: The chromedriver binary file. |
48 profile_path: The chrome testing profile folder. | 37 profile_path: The chrome testing profile folder. |
49 passwords_path: The usernames and passwords file. | 38 passwords_path: The usernames and passwords file. |
50 enable_automatic_password_saving: If True, the passwords are going to be | 39 enable_automatic_password_saving: If True, the passwords are going to be |
51 saved without showing the prompt. | 40 saved without showing the prompt. |
52 | 41 |
53 Raises: | 42 Raises: |
54 Exception: An exception is raised if |profile_path| folder could not be | 43 Exception: An exception is raised if |profile_path| folder could not be |
55 removed. | 44 removed. |
56 """ | 45 """ |
57 | 46 |
58 # Cleaning the chrome testing profile folder. | 47 # Cleaning the chrome testing profile folder. |
59 if os.path.exists(profile_path): | 48 if os.path.exists(profile_path): |
60 shutil.rmtree(profile_path) | 49 shutil.rmtree(profile_path) |
50 | |
61 options = Options() | 51 options = Options() |
62 self.enable_automatic_password_saving = enable_automatic_password_saving | |
63 if enable_automatic_password_saving: | 52 if enable_automatic_password_saving: |
64 options.add_argument("enable-automatic-password-saving") | 53 options.add_argument("enable-automatic-password-saving") |
65 # Chrome path. | 54 # TODO(vabr): show_prompt is used in WebsiteTest for asserting that |
55 # Chrome set-up corresponds to the test type. Remove that knowledge | |
56 # about Environment from the WebsiteTest. | |
57 self.show_prompt = not enable_automatic_password_saving | |
66 options.binary_location = chrome_path | 58 options.binary_location = chrome_path |
67 # Chrome testing profile path. | |
68 options.add_argument("user-data-dir=%s" % profile_path) | 59 options.add_argument("user-data-dir=%s" % profile_path) |
69 | 60 |
70 # The webdriver. It's possible to choose the port the service is going to | 61 # The webdriver. It's possible to choose the port the service is going to |
71 # run on. If it's left to 0, a free port will be found. | 62 # run on. If it's left to 0, a free port will be found. |
72 self.driver = webdriver.Chrome(chromedriver_path, 0, options) | 63 self.driver = webdriver.Chrome(chromedriver_path, 0, options) |
73 # The password internals window. | 64 |
65 # Password internals page tab/window handle. | |
74 self.internals_window = self.driver.current_window_handle | 66 self.internals_window = self.driver.current_window_handle |
75 if passwords_path: | 67 |
76 # An xml tree filled with logins and passwords. | 68 # An xml tree filled with logins and passwords. |
77 self.passwords_tree = ElementTree.parse(passwords_path).getroot() | 69 self.passwords_tree = ElementTree.parse(passwords_path).getroot() |
msramek
2015/03/27 11:37:06
This can throw IOError or ParseError. Why don't we
vabr (Chromium)
2015/03/27 12:14:43
This is intentional. The tests cannot be run, and
| |
78 else: | 70 |
79 raise Exception("Error: |passwords_path| needs to be provided if" | 71 self.website_window = self._OpenNewTab() |
80 "|chrome_path| is provided, otherwise the tests could not be run") | 72 |
81 # Password internals page. | |
82 self.internals_page = "chrome://password-manager-internals/" | |
83 # The Website window. | |
84 self.website_window = None | |
85 # The WebsiteTests list. | |
86 self.websitetests = [] | 73 self.websitetests = [] |
74 | |
87 # Map messages to the number of their appearance in the log. | 75 # Map messages to the number of their appearance in the log. |
88 self.message_count = { MESSAGE_ASK: 0, MESSAGE_SAVE: 0 } | 76 self.message_count = { MESSAGE_ASK: 0, MESSAGE_SAVE: 0 } |
89 # The tests needs two tabs to work. A new tab is opened with the first | 77 |
90 # GoTo. This is why we store here whether or not it's the first time to | 78 # A list of (test_name, test_type, test_success, failure_log). |
91 # execute GoTo. | |
92 self.first_go_to = True | |
93 # List of all tests results. | |
94 self.tests_results = [] | 79 self.tests_results = [] |
95 | 80 |
96 def AddWebsiteTest(self, websitetest): | 81 def AddWebsiteTest(self, websitetest): |
97 """Adds a WebsiteTest to the testing Environment. | 82 """Adds a WebsiteTest to the testing Environment. |
98 | 83 |
99 TODO(vabr): Currently, this is only called at most once for each | 84 TODO(vabr): Currently, this is only called at most once for each |
100 Environment instance. That is because to run all tests efficiently in | 85 Environment instance. That is because to run all tests efficiently in |
101 parallel, each test gets its own process spawned (outside of Python). | 86 parallel, each test gets its own process spawned (outside of Python). |
102 That makes sense, but then we should flatten the hierarchy of calls | 87 That makes sense, but then we should flatten the hierarchy of calls |
103 and consider making the 1:1 relation of environment to tests more | 88 and consider making the 1:1 relation of environment to tests more |
104 explicit. | 89 explicit. |
105 | 90 |
106 Args: | 91 Args: |
107 websitetest: The WebsiteTest instance to be added. | 92 websitetest: The WebsiteTest instance to be added. |
108 """ | 93 """ |
109 websitetest.environment = self | 94 websitetest.environment = self |
110 # TODO(vabr): Make driver a property of WebsiteTest. | 95 # TODO(vabr): Make driver a property of WebsiteTest. |
111 websitetest.driver = self.driver | 96 websitetest.driver = self.driver |
112 if not websitetest.username: | 97 if not websitetest.username: |
113 username_tag = ( | 98 username_tag = (self.passwords_tree.find( |
114 self.passwords_tree.find( | 99 ".//*[@name='%s']/username" % websitetest.name)) |
115 ".//*[@name='%s']/username" % websitetest.name)) | |
116 websitetest.username = username_tag.text | 100 websitetest.username = username_tag.text |
117 if not websitetest.password: | 101 if not websitetest.password: |
118 password_tag = ( | 102 password_tag = (self.passwords_tree.find( |
119 self.passwords_tree.find( | 103 ".//*[@name='%s']/password" % websitetest.name)) |
120 ".//*[@name='%s']/password" % websitetest.name)) | |
121 websitetest.password = password_tag.text | 104 websitetest.password = password_tag.text |
122 self.websitetests.append(websitetest) | 105 self.websitetests.append(websitetest) |
123 | 106 |
124 def ClearCache(self, clear_passwords): | 107 def _ClearBrowserDataInit(self): |
125 """Clear the browser cookies. If |clear_passwords| is true, clear all the | 108 """Opens and resets the chrome://settings/clearBrowserData dialog. |
126 saved passwords too. | 109 |
110 It unchecks all checkboxes, and sets the time range to the "beginning of | |
111 time". | |
112 """ | |
113 | |
114 self.driver.get("chrome://settings/clearBrowserData") | |
115 self.driver.switch_to_frame("settings") | |
116 | |
117 time_range_selector = "#clear-browser-data-time-period" | |
118 # TODO(vabr): Wait until time_range_selector is displayed instead. | |
119 time.sleep(2) | |
120 set_time_range = ( | |
121 "var range = document.querySelector('{0}');".format( | |
122 time_range_selector) + | |
123 "range.value = 4" # 4 == the beginning of time | |
124 ) | |
125 self.driver.execute_script(set_time_range) | |
126 | |
127 all_cboxes_selector = ( | |
128 "#clear-data-checkboxes > * > * >[type=\"checkbox\"]") | |
msramek
2015/03/27 11:37:07
Nit: Why not just
#clear-data-checkboxes [type="c
vabr (Chromium)
2015/03/27 12:14:43
Agreed and done.
(Learning CSS selectors on the fl
| |
129 uncheck_all = ( | |
130 "var checkboxes = document.querySelectorAll('{0}');".format( | |
131 all_cboxes_selector ) + | |
132 "for (var i = 0; i < checkboxes.length; ++i) {" | |
133 " if (checkboxes[i].checked)" | |
msramek
2015/03/27 11:37:06
Nit:
checkboxes[i].checked = false
vabr (Chromium)
2015/03/27 12:14:43
Done.
| |
134 " checkboxes[i].click();" | |
135 "}" | |
136 ) | |
137 self.driver.execute_script(uncheck_all) | |
138 | |
139 def _ClearDataForCheckbox(self, selector): | |
140 """Causes the data associated with |selector| to be cleared. | |
141 | |
142 Opens chrome://settings/clearBrowserData, unchecks all checkboxes, then | |
143 checks the one described by |selector|, then clears the corresponding | |
144 browsing data for the full time range. | |
127 | 145 |
128 Args: | 146 Args: |
129 clear_passwords : Clear all the passwords if the bool value is true. | 147 selector: describes the checkbox through which to delete the data. |
130 """ | 148 """ |
131 self.driver.get("chrome://settings/clearBrowserData") | 149 |
132 self.driver.switch_to_frame("settings") | 150 self._ClearBrowserDataInit() |
133 script = ( | 151 check_cookies_and_submit = ( |
134 "if (!document.querySelector('#delete-cookies-checkbox').checked)" | 152 "var cbox = document.querySelector('{0}');".format(selector) + |
135 " document.querySelector('#delete-cookies-checkbox').click();" | 153 "if (!cbox.checked)" |
msramek
2015/03/27 11:37:07
Nit:
document.querySelector().checked = true
vabr (Chromium)
2015/03/27 12:14:43
Done.
| |
154 " cbox.click();" | |
155 "document.querySelector('#clear-browser-data-commit').click();" | |
136 ) | 156 ) |
137 negation = "" | 157 self.driver.execute_script(check_cookies_and_submit) |
138 if clear_passwords: | 158 |
139 negation = "!" | 159 def _EnablePasswordSaving(self): |
140 script += ( | 160 """Make sure that password manager is enabled.""" |
141 "if (%sdocument.querySelector('#delete-passwords-checkbox').checked)" | 161 |
142 " document.querySelector('#delete-passwords-checkbox').click();" | |
143 % negation) | |
144 script += "document.querySelector('#clear-browser-data-commit').click();" | |
145 self.driver.execute_script(script) | |
146 time.sleep(2) | |
147 # Every time we do something to the cache let's enable password saving. | |
148 # TODO(melandory): We should check why it's off in a first place. | 162 # TODO(melandory): We should check why it's off in a first place. |
149 # TODO(melandory): Investigate, maybe there is no need to enable it that | 163 # TODO(melandory): Investigate, maybe there is no need to enable it that |
150 # often. | 164 # often. |
151 self.EnablePasswordsSaving() | |
152 | |
153 def EnablePasswordsSaving(self): | |
154 self.driver.get("chrome://settings") | 165 self.driver.get("chrome://settings") |
155 self.driver.switch_to_frame("settings") | 166 self.driver.switch_to_frame("settings") |
156 script = "document.getElementById('advanced-settings-expander').click();" | 167 script = "document.getElementById('advanced-settings-expander').click();" |
157 self.driver.execute_script(script) | 168 self.driver.execute_script(script) |
169 # TODO(vabr): Wait until element is displayed instead. | |
158 time.sleep(2) | 170 time.sleep(2) |
159 script = ( | 171 script = ( |
160 "if (!document.querySelector('#password-manager-enabled').checked)" | 172 "if (!document.querySelector('#password-manager-enabled').checked)" |
msramek
2015/03/27 11:37:06
Nit: As above.
vabr (Chromium)
2015/03/27 12:14:43
Done.
| |
161 "{ document.querySelector('#password-manager-enabled').click();}") | 173 "{ document.querySelector('#password-manager-enabled').click();}") |
162 self.driver.execute_script(script) | 174 self.driver.execute_script(script) |
163 time.sleep(2) | 175 time.sleep(2) |
164 | 176 |
165 def OpenTabAndGoToInternals(self, url): | 177 def _OpenNewTab(self): |
166 """If there is no |self.website_window|, opens a new tab and navigates to | 178 """Open a new tab, and loads the internals page in the old tab. |
167 |url| in the new tab. Navigates to the passwords internals page in the | |
168 first tab. Raises an exception otherwise. | |
169 | 179 |
170 Args: | 180 Returns: |
171 url: Url to go to in the new tab. | 181 A handle to the new tab. |
182 """ | |
172 | 183 |
173 Raises: | 184 number_old_tabs = len(self.driver.window_handles) |
174 Exception: An exception is raised if |self.website_window| already | |
175 exists. | |
176 """ | |
177 if self.website_window: | |
178 raise Exception("Error: The window was already opened.") | |
179 | |
180 self.driver.get("chrome://newtab") | |
181 # There is no straightforward way to open a new tab with chromedriver. | 185 # There is no straightforward way to open a new tab with chromedriver. |
182 # One work-around is to go to a website, insert a link that is going | 186 # One work-around is to go to a website, insert a link that is going |
183 # to be opened in a new tab, click on it. | 187 # to be opened in a new tab, and click on it. |
188 self.driver.get("about:blank") | |
184 a = self.driver.execute_script( | 189 a = self.driver.execute_script( |
185 "var a = document.createElement('a');" | 190 "var a = document.createElement('a');" |
186 "a.target = '_blank';" | 191 "a.target = '_blank';" |
187 "a.href = arguments[0];" | 192 "a.href = 'about:blank';" |
188 "a.innerHTML = '.';" | 193 "a.innerHTML = '.';" |
189 "document.body.appendChild(a);" | 194 "document.body.appendChild(a);" |
190 "return a;", | 195 "return a;") |
191 url) | 196 a.click() |
197 while number_old_tabs == len(self.driver.window_handles): | |
198 time.sleep(1) # Wait until the new tab is opened. | |
192 | 199 |
193 a.click() | 200 new_tab = self.driver.window_handles[-1] |
194 time.sleep(1) | 201 self.driver.get(INTERNALS_PAGE_URL) |
202 self.driver.switch_to_window(new_tab) | |
203 return new_tab | |
195 | 204 |
196 self.website_window = self.driver.window_handles[-1] | 205 def _DidStringAppearUntilTimeout(self, strings, timeout): |
197 self.driver.get(self.internals_page) | 206 """Checks whether some of |strings| appeared in the current page. |
198 self.driver.switch_to_window(self.website_window) | |
199 | 207 |
200 def SwitchToInternals(self): | 208 Waits for up to |timeout| seconds until at least one of |strings| is |
201 """Switches from the Website window to internals tab.""" | 209 shown in the current page. Updates self.message_count with the current |
202 self.driver.switch_to_window(self.internals_window) | 210 number of occurrences of the shown string. Assumes that at most |
203 | 211 one of |strings| is newly shown. |
204 def SwitchFromInternals(self): | |
205 """Switches from internals tab to the Website window.""" | |
206 self.driver.switch_to_window(self.website_window) | |
207 | |
208 def _DidMessageAppearUntilTimeout(self, log_message, timeout): | |
209 """Checks whether the save password prompt is shown. | |
210 | 212 |
211 Args: | 213 Args: |
212 log_message: Log message to look for in the password internals. | 214 strings: A list of strings to look for. |
213 timeout: There is some delay between the login and the password | 215 timeout: If any such string does not appear within the first |timeout| |
214 internals update. The method checks periodically during the first | 216 seconds, it is considered a no-show. |
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 | 217 |
219 Returns: | 218 Returns: |
220 True if the save password prompt is shown. | 219 True if one of |strings| is observed until |timeout|, False otherwise. |
221 False otherwise. | |
222 """ | 220 """ |
221 | |
223 log = self.driver.find_element_by_css_selector("#log-entries") | 222 log = self.driver.find_element_by_css_selector("#log-entries") |
224 count = log.text.count(log_message) | 223 while timeout: |
msramek
2015/03/27 11:37:06
Why don't we just sleep for |timeout| and then cou
vabr (Chromium)
2015/03/27 12:14:43
No, but in the case when they appear, they mostly
| |
224 for string in strings: | |
225 count = log.text.count(string) | |
226 if count > self.message_count[string]: | |
227 self.message_count[string] = count | |
228 return True | |
229 time.sleep(1) | |
230 timeout -= 1 | |
231 return False | |
225 | 232 |
226 if count > self.message_count[log_message]: | 233 def CheckForNewString(self, strings, string_should_show_up, error): |
227 self.message_count[log_message] = count | 234 """Checks that |strings| show up on the internals page as it should. |
228 return True | |
229 elif timeout > 0: | |
230 time.sleep(1) | |
231 return self._DidMessageAppearUntilTimeout(log_message, timeout - 1) | |
232 else: | |
233 return False | |
234 | 235 |
235 def CheckForNewMessage(self, log_message, message_should_show_up, | 236 Switches to the internals page and looks for a new instances of |strings| |
236 error_message, timeout=15): | 237 being shown up there. It checks that |string_should_show_up| is true if |
237 """Detects whether the save password prompt is shown. | 238 and only if at leas one string from |strings| shows up, and throws an |
239 Exception if that check fails. | |
238 | 240 |
239 Args: | 241 Args: |
240 log_message: Log message to look for in the password internals. The | 242 strings: A list of strings to look for in the internals page. |
241 only valid values are the constants MESSAGE_* defined at the | 243 string_should_show_up: Whether or not at least one string from |strings| |
242 beginning of this file. | 244 is expected to be shown. |
243 message_should_show_up: Whether or not the message is expected to be | 245 error: Error message for the exception. |
244 shown. | |
245 error_message: Error message for the exception. | |
246 timeout: There is some delay between the login and the password | |
247 internals update. The method checks periodically during the first | |
248 |timeout| seconds if the internals page reports the prompt being | |
249 shown. If the prompt is not reported shown within the first | |
250 |timeout| seconds, it is considered not shown at all. | |
251 | 246 |
252 Raises: | 247 Raises: |
253 Exception: An exception is raised in case the result does not match the | 248 Exception: (See above.) |
254 expectation | |
255 """ | 249 """ |
256 if (self._DidMessageAppearUntilTimeout(log_message, timeout) != | |
257 message_should_show_up): | |
258 raise Exception(error_message) | |
259 | 250 |
260 def AllTests(self, prompt_test): | 251 self.driver.switch_to_window(self.internals_window) |
261 """Runs the tests on all the WebsiteTests. | 252 try: |
253 if (self._DidStringAppearUntilTimeout(strings, 15) != | |
254 string_should_show_up): | |
255 raise Exception(error) | |
256 finally: | |
257 self.driver.switch_to_window(self.website_window) | |
262 | 258 |
263 TODO(vabr): Currently, "all tests" always means one. | 259 def DeleteCookies(self): |
260 """Deletes cookies via the settings page.""" | |
261 | |
262 self._ClearDataForCheckbox("#delete-cookies-checkbox") | |
263 | |
264 def RunTestsOnSites(self, test_type): | |
265 """Runs the specified test on the known websites. | |
266 | |
267 Also saves the test results in the environment. Note that test types | |
268 differ in their requirements on whether the save password prompt | |
269 should be displayed. Make sure that such requirements are consistent | |
270 with the enable_automatic_password_saving argument passed to |self| | |
271 on construction. | |
264 | 272 |
265 Args: | 273 Args: |
266 prompt_test: If True, tests caring about showing the save-password | 274 test_type: A test identifier understood by WebsiteTest.run_test(). |
267 prompt are going to be run, otherwise tests which don't care about | 275 """ |
268 the prompt are going to be run. | |
269 | 276 |
270 Raises: | 277 self.DeleteCookies() |
271 Exception: An exception is raised if the tests fail. | 278 self._ClearDataForCheckbox("#delete-passwords-checkbox") |
272 """ | 279 self._EnablePasswordSaving() |
273 if prompt_test: | |
274 self.PromptTestList(self.websitetests) | |
275 else: | |
276 self.TestList(self.websitetests) | |
277 | 280 |
278 def Test(self, tests, prompt_test): | |
279 """Runs the tests on websites named in |tests|. | |
280 | |
281 Args: | |
282 tests: A list of the names of the WebsiteTests that are going to be | |
283 tested. | |
284 prompt_test: If True, tests caring about showing the save-password | |
285 prompt are going to be run, otherwise tests which don't care about | |
286 the prompt are going to be executed. | |
287 | |
288 Raises: | |
289 Exception: An exception is raised if the tests fail. | |
290 """ | |
291 websitetests = [] | |
292 for websitetest in self.websitetests: | 281 for websitetest in self.websitetests: |
293 if websitetest.name in tests: | |
294 websitetests.append(websitetest) | |
295 | |
296 if prompt_test: | |
297 self.PromptTestList(websitetests) | |
298 else: | |
299 self.TestList(websitetests) | |
300 | |
301 def TestList(self, websitetests): | |
302 """Runs the tests on the websites in |websitetests|. | |
303 | |
304 Args: | |
305 websitetests: A list of WebsiteTests that are going to be tested. | |
306 | |
307 Raises: | |
308 Exception: An exception is raised if the tests fail. | |
309 """ | |
310 self.ClearCache(True) | |
311 | |
312 for websitetest in websitetests: | |
313 successful = True | 282 successful = True |
314 error = "" | 283 error = "" |
315 try: | 284 try: |
316 websitetest.was_run = True | 285 websitetest.RunTest(test_type) |
317 websitetest.WrongLoginTest() | |
318 websitetest.SuccessfulLoginTest() | |
319 self.ClearCache(False) | |
320 websitetest.SuccessfulLoginWithAutofilledPasswordTest() | |
321 self.ClearCache(True) | |
322 websitetest.SuccessfulLoginTest() | |
323 self.ClearCache(True) | |
324 except Exception as e: | 286 except Exception as e: |
325 successful = False | 287 successful = False |
326 error = e.message | 288 error = e.message |
327 self.tests_results.append(TestResult(websitetest.name, "normal", | 289 self.tests_results.append( |
328 successful, error)) | 290 (websitetest.name, test_type, successful, error)) |
329 | |
330 | |
331 def PromptTestList(self, websitetests): | |
332 """Runs the prompt tests on the websites in |websitetests|. | |
333 | |
334 Args: | |
335 websitetests: A list of WebsiteTests that are going to be tested. | |
336 | |
337 Raises: | |
338 Exception: An exception is raised if the tests fail. | |
339 """ | |
340 self.ClearCache(True) | |
341 | |
342 for websitetest in websitetests: | |
343 successful = True | |
344 error = "" | |
345 try: | |
346 websitetest.was_run = True | |
347 websitetest.PromptTest() | |
348 except Exception as e: | |
349 successful = False | |
350 error = e.message | |
351 self.tests_results.append(TestResult(websitetest.name, "prompt", | |
352 successful, error)) | |
353 | 291 |
354 def Quit(self): | 292 def Quit(self): |
355 """Closes the tests.""" | 293 """Shuts down the driver.""" |
356 # Close the webdriver. | 294 |
357 self.driver.quit() | 295 self.driver.quit() |
OLD | NEW |