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 options.add_argument("enable-password-manager-internals-ui") | |
67 # Chrome path. | |
68 options.binary_location = chrome_path | |
69 # Chrome testing profile path. | |
70 options.add_argument("user-data-dir=%s" % profile_path) | |
71 | |
72 # The webdriver. It's possible to choose the port the service is going to | |
73 # run on. If it's left to 0, a free port will be found. | |
74 self.driver = webdriver.Chrome(chromedriver_path, 0, options) | |
75 # The password internals window. | |
76 self.internals_window = self.driver.current_window_handle | |
77 # Password internals page. | |
78 self.internals_page = "chrome://password-manager-internals/" | |
79 # The Website window. | |
80 self.website_window = None | |
81 # The WebsiteTests list. | |
82 self.websitetests = [] | |
83 # An xml tree filled with logins and passwords. | |
84 self.passwords_tree = None | |
85 if passwords_path: | |
86 self.passwords_tree = ElementTree.parse(passwords_path).getroot() | |
87 # The enabled WebsiteTests list. | |
88 self.working_tests = [] | |
89 # The number of the save operations done through all the tests. | |
90 self.save_count = 0 | |
91 # The tests needs two tabs to work. A new tab is opened with the first | |
92 # GoTo. This is why we store here whether or not it's the first time to | |
93 # execute GoTo. | |
94 self.first_go_to = True | |
95 | |
96 def AddWebsiteTest(self, websitetest, disabled=False): | |
97 """Adds a WebsiteTest to the testing Environment. | |
98 | |
99 Args: | |
100 websitetest: The WebsiteTest instance to be added. | |
101 disabled: Whether test is disabled. | |
102 """ | |
103 websitetest.environment = self | |
104 websitetest.driver = self.driver | |
105 if self.passwords_tree is not None: | |
106 if not websitetest.username: | |
107 username_tag = ( | |
108 self.passwords_tree.find( | |
109 ".//*[@name='%s']/username" % websitetest.name)) | |
110 if username_tag.text: | |
111 websitetest.username = username_tag.text | |
112 if not websitetest.password: | |
113 password_tag = ( | |
114 self.passwords_tree.find( | |
115 ".//*[@name='%s']/password" % websitetest.name)) | |
116 if password_tag.text: | |
117 websitetest.password = password_tag.text | |
118 self.websitetests.append(websitetest) | |
119 if not disabled: | |
120 self.working_tests.append(websitetest.name) | |
121 | |
122 def RemoveAllPasswords(self): | |
123 """Removes all the stored passwords.""" | |
124 logging.info("\nRemoveAllPasswords\n") | |
125 self.driver.get("chrome://settings/passwords") | |
126 self.driver.switch_to_frame("settings") | |
127 while True: | |
128 try: | |
129 self.driver.execute_script("document.querySelector('" | |
130 "#saved-passwords-list .row-delete-button').click()") | |
131 time.sleep(1) | |
132 except NoSuchElementException: | |
133 break | |
134 except WebDriverException: | |
135 break | |
136 | |
137 def OpenTabAndGoToInternals(self, url): | |
138 """If there is no |self.website_window|, opens a new tab and navigates to | |
139 |url| in the new tab. Navigates to the passwords internals page in the | |
140 first tab. Raises an exception otherwise. | |
141 | |
142 Args: | |
143 url: Url to go to in the new tab. | |
144 | |
145 Raises: | |
146 Exception: An exception is raised if |self.website_window| already | |
147 exists. | |
148 """ | |
149 if self.website_window: | |
150 raise Exception("Error: The window was already opened.") | |
151 else: | |
vabr (Chromium)
2014/05/22 10:07:02
nit: remove "else" (and indent the code below 2 co
rchtara
2014/05/22 12:42:01
Done.
| |
152 self.driver.get("chrome://newtab") | |
153 # There is no straightforward way to open a new tab with chromedriver. | |
154 # One work-around is to go to a website, insert a link that is going | |
155 # to be opened in a new tab, click on it. | |
156 a = self.driver.execute_script( | |
157 "var a = document.createElement('a');" | |
158 "a.target = '_blank';" | |
159 "a.href = arguments[0];" | |
160 "a.innerHTML = '.';" | |
161 "document.body.appendChild(a);" | |
162 "return a;", | |
163 url) | |
164 | |
165 a.click() | |
166 time.sleep(1) | |
167 | |
168 self.website_window = self.driver.window_handles[-1] | |
169 self.driver.get(self.internals_page) | |
170 self.driver.switch_to_window(self.website_window) | |
171 | |
172 def SwitchToInternals(self): | |
173 """Switches from the Website window to internals tab.""" | |
174 self.driver.switch_to_window(self.internals_window) | |
175 | |
176 def SwitchFromInternals(self): | |
177 """Switches from internals tab to the Website window.""" | |
178 self.driver.switch_to_window(self.website_window) | |
179 | |
180 def _WaitUntilPromptAppear(self, log_message, timeout): | |
vabr (Chromium)
2014/05/22 10:07:02
Please add a documentation comment for this method
vabr (Chromium)
2014/05/22 10:07:02
Maybe rename to _DidPromptAppearUntilTimeout
to em
rchtara
2014/05/22 12:42:01
Done.
rchtara
2014/05/22 12:42:01
Done.
| |
181 log = self.driver.find_element_by_css_selector("#log-entries") | |
182 count = log.text.count(log_message) | |
183 if count > self.save_count: | |
184 return True | |
vabr (Chromium)
2014/05/22 10:07:02
What about adding
self.save_count = count
before "
rchtara
2014/05/22 12:42:01
Done.
| |
185 elif timeout > 0: | |
186 time.sleep(1) | |
187 return self._WaitUntilPromptAppear(log_message, timeout - 1) | |
188 else: | |
189 return False | |
190 | |
191 def CheckPrompt(self, log_message, prompt_should_show_up, | |
192 error_message, timeout=3): | |
193 """Detects whether the save password prompt is shown. | |
194 | |
195 Args: | |
196 log_message: Log message to look for in the password internals. | |
197 prompt_should_show_up: Whether or not the prompt is expected to be shown. | |
198 error_message: Error message for the exception. | |
199 timeout: There is some delay between the login and the password | |
200 internals update. The method checks periodically during the first | |
201 |timeout| seconds if the internals page reports the prompt being | |
202 shown. If the prompt is not reported shown within the first | |
203 |timeout| seconds, it is considered not shown at all. | |
204 | |
205 Raises: | |
206 Exception: An exception is raised in case the result does not match the | |
207 expectation | |
208 """ | |
209 if (self._WaitUntilPromptAppear(log_message, timeout) == | |
210 prompt_should_show_up): | |
211 if prompt_should_show_up: | |
212 self.save_count += 1 | |
213 else: | |
214 raise Exception(error_message) | |
215 | |
216 def AllTests(self, prompt_test): | |
217 """Runs the tests on all the WebsiteTests. | |
218 | |
219 Args: | |
220 prompt_test: If True, tests caring about the save-password prompt are | |
vabr (Chromium)
2014/05/22 10:07:02
nit: "caring about the save-password prompt" -> "c
rchtara
2014/05/22 12:42:01
Done.
| |
221 going to be ran, otherwise tests which don't are going to be | |
vabr (Chromium)
2014/05/22 10:07:02
typu: ran -> run
vabr (Chromium)
2014/05/22 10:07:02
nit: "don't" -> "don't care about the prompt"
rchtara
2014/05/22 12:42:01
Done.
rchtara
2014/05/22 12:42:01
Done.
| |
222 executed. | |
223 | |
224 Raises: | |
225 Exception: An exception is raised if the tests fail. | |
226 """ | |
227 if prompt_test: | |
228 self.PromptTestList(self.websitetests) | |
229 else: | |
230 self.TestList(self.websitetests) | |
231 | |
232 def WorkingTests(self, prompt_test): | |
233 """Runs the tests on all the enabled WebsiteTests. | |
234 | |
235 Args: | |
236 prompt_test: If True, tests caring about the save-password prompt are | |
vabr (Chromium)
2014/05/22 10:07:02
Please apply the changes to the explanation of pro
rchtara
2014/05/22 12:42:01
Done.
| |
237 going to be ran, otherwise tests which don't are going to be | |
238 executed. | |
239 | |
240 Raises: | |
241 Exception: An exception is raised if the tests fail. | |
242 """ | |
243 self.Test(self.working_tests, prompt_test) | |
244 | |
245 def Test(self, tests, prompt_test): | |
246 """Runs the tests on websites named in |tests|. | |
247 | |
248 Args: | |
249 tests: A list of the names of the WebsiteTests that are going to be | |
250 tested. | |
251 prompt_test: If True, tests caring about the save-password prompt are | |
252 going to be ran, otherwise tests which don't are going to be | |
253 executed. | |
254 | |
255 Raises: | |
256 Exception: An exception is raised if the tests fail. | |
257 """ | |
258 websitetests = [] | |
259 for websitetest in self.websitetests: | |
260 if websitetest.name in tests: | |
261 websitetests.append(websitetest) | |
262 | |
263 if prompt_test: | |
264 self.PromptTestList(websitetests) | |
265 else: | |
266 self.TestList(websitetests) | |
267 | |
268 def TestList(self, websitetests): | |
269 """Runs the tests on the websites in |websitetests|. | |
270 | |
271 Args: | |
272 websitetests: A list of WebsiteTests that are going to be tested. | |
273 | |
274 Raises: | |
275 Exception: An exception is raised if the tests fail. | |
276 """ | |
277 self.RemoveAllPasswords() | |
278 | |
279 for websitetest in websitetests: | |
280 websitetest.WrongLoginTest() | |
281 websitetest.SuccessfulLoginTest() | |
282 websitetest.SuccessfulLoginWithAutofilledPasswordTest() | |
283 | |
284 self.RemoveAllPasswords() | |
285 for websitetest in websitetests: | |
286 websitetest.SuccessfulLoginTest() | |
287 | |
288 def PromptTestList(self, websitetests): | |
289 """Runs the prompt tests on the websites in |websitetests|. | |
290 | |
291 Args: | |
292 websitetests: A list of WebsiteTests that are going to be tested. | |
293 | |
294 Raises: | |
295 Exception: An exception is raised if the tests fail. | |
296 """ | |
297 self.RemoveAllPasswords() | |
298 | |
299 for websitetest in websitetests: | |
300 websitetest.PromptTest() | |
301 | |
302 def Quit(self): | |
303 """Closes the tests.""" | |
304 # Close the webdriver. | |
305 self.driver.quit() | |
OLD | NEW |