OLD | NEW |
| (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 """WebsiteTest testing class.""" | |
6 | |
7 import logging | |
8 import sys | |
9 import time | |
10 | |
11 sys.path.insert(0, '../../../../third_party/webdriver/pylib/') | |
12 | |
13 from selenium.webdriver.common.action_chains import ActionChains | |
14 from selenium.webdriver.common.keys import Keys | |
15 | |
16 import environment | |
17 | |
18 | |
19 def _IsOneSubstringOfAnother(s1, s2): | |
20 """Checks if one of the string arguements is substring of the other. | |
21 | |
22 Args: | |
23 s1: The first string. | |
24 s2: The second string. | |
25 Returns: | |
26 | |
27 True if one of the string arguements is substring of the other. | |
28 False otherwise. | |
29 """ | |
30 return s1 in s2 or s2 in s1 | |
31 | |
32 | |
33 class WebsiteTest: | |
34 """Handles a tested WebsiteTest.""" | |
35 | |
36 class Mode: | |
37 """Test mode.""" | |
38 # Password and username are expected to be autofilled. | |
39 AUTOFILLED = 1 | |
40 # Password and username are not expected to be autofilled. | |
41 NOT_AUTOFILLED = 2 | |
42 | |
43 def __init__(self): | |
44 pass | |
45 | |
46 def __init__(self, name, username_not_auto=False): | |
47 """Creates a new WebsiteTest. | |
48 | |
49 Args: | |
50 name: The website name. | |
51 username_not_auto: Username inputs in some websites (like wikipedia) are | |
52 sometimes filled with some messages and thus, the usernames are not | |
53 automatically autofilled. This flag handles that and disables us from | |
54 checking if the state of the DOM is the same as the username of | |
55 website. | |
56 """ | |
57 # Name of the website | |
58 self.name = name | |
59 # Username of the website. | |
60 self.username = None | |
61 # Password of the website. | |
62 self.password = None | |
63 # Username is not automatically filled. | |
64 self.username_not_auto = username_not_auto | |
65 # Autofilling mode. | |
66 self.mode = self.Mode.NOT_AUTOFILLED | |
67 # The |remaining_time_to_wait| limits the total time in seconds spent in | |
68 # potentially infinite loops. | |
69 self.remaining_time_to_wait = 200 | |
70 # The testing Environment. | |
71 self.environment = None | |
72 # The webdriver. | |
73 self.driver = None | |
74 # Whether or not the test was run. | |
75 self.was_run = False | |
76 | |
77 # Mouse/Keyboard actions. | |
78 | |
79 def Click(self, selector): | |
80 """Clicks on an element. | |
81 | |
82 Args: | |
83 selector: The element CSS selector. | |
84 """ | |
85 logging.info("action: Click %s" % selector) | |
86 self.WaitUntilDisplayed(selector) | |
87 element = self.driver.find_element_by_css_selector(selector) | |
88 element.click() | |
89 | |
90 def ClickIfClickable(self, selector): | |
91 """Clicks on an element if it's clickable: If it doesn't exist in the DOM, | |
92 it's covered by another element or it's out viewing area, nothing is | |
93 done and False is returned. Otherwise, even if the element is 100% | |
94 transparent, the element is going to receive a click and a True is | |
95 returned. | |
96 | |
97 Args: | |
98 selector: The element CSS selector. | |
99 | |
100 Returns: | |
101 True if the click happens. | |
102 False otherwise. | |
103 """ | |
104 logging.info("action: ClickIfVisible %s" % selector) | |
105 self.WaitUntilDisplayed(selector) | |
106 try: | |
107 element = self.driver.find_element_by_css_selector(selector) | |
108 element.click() | |
109 return True | |
110 except Exception: | |
111 return False | |
112 | |
113 def GoTo(self, url): | |
114 """Navigates the main frame to the |url|. | |
115 | |
116 Args: | |
117 url: The URL. | |
118 """ | |
119 logging.info("action: GoTo %s" % self.name) | |
120 if self.environment.first_go_to: | |
121 self.environment.OpenTabAndGoToInternals(url) | |
122 self.environment.first_go_to = False | |
123 else: | |
124 self.driver.get(url) | |
125 | |
126 def HoverOver(self, selector): | |
127 """Hovers over an element. | |
128 | |
129 Args: | |
130 selector: The element CSS selector. | |
131 """ | |
132 logging.info("action: Hover %s" % selector) | |
133 self.WaitUntilDisplayed(selector) | |
134 element = self.driver.find_element_by_css_selector(selector) | |
135 hover = ActionChains(self.driver).move_to_element(element) | |
136 hover.perform() | |
137 | |
138 def SendEnterTo(self, selector): | |
139 """Sends an enter key to an element. | |
140 | |
141 Args: | |
142 selector: The element CSS selector. | |
143 """ | |
144 logging.info("action: SendEnterTo %s" % selector) | |
145 body = self.driver.find_element_by_tag_name("body") | |
146 body.send_keys(Keys.ENTER) | |
147 | |
148 # Waiting/Displaying actions. | |
149 | |
150 def IsDisplayed(self, selector): | |
151 """Returns False if an element doesn't exist in the DOM or is 100% | |
152 transparent. Otherwise, returns True even if it's covered by another | |
153 element or it's out viewing area. | |
154 | |
155 Args: | |
156 selector: The element CSS selector. | |
157 """ | |
158 logging.info("action: IsDisplayed %s" % selector) | |
159 try: | |
160 element = self.driver.find_element_by_css_selector(selector) | |
161 return element.is_displayed() | |
162 except Exception: | |
163 return False | |
164 | |
165 def Wait(self, duration): | |
166 """Wait for a duration in seconds. This needs to be used in potentially | |
167 infinite loops, to limit their running time. | |
168 | |
169 Args: | |
170 duration: The time to wait in seconds. | |
171 """ | |
172 logging.info("action: Wait %s" % duration) | |
173 time.sleep(duration) | |
174 self.remaining_time_to_wait -= 1 | |
175 if self.remaining_time_to_wait < 0: | |
176 raise Exception("Tests took more time than expected for the following " | |
177 "website : %s \n" % self.name) | |
178 | |
179 def WaitUntilDisplayed(self, selector, timeout=10): | |
180 """Waits until an element is displayed. | |
181 | |
182 Args: | |
183 selector: The element CSS selector. | |
184 timeout: The maximum waiting time in seconds before failing. | |
185 """ | |
186 if not self.IsDisplayed(selector): | |
187 self.Wait(1) | |
188 timeout = timeout - 1 | |
189 if (timeout <= 0): | |
190 raise Exception("Error: Element %s not shown before timeout is " | |
191 "finished for the following website: %s" | |
192 % (selector, self.name)) | |
193 else: | |
194 self.WaitUntilDisplayed(selector, timeout) | |
195 | |
196 # Form actions. | |
197 | |
198 def FillPasswordInto(self, selector): | |
199 """If the testing mode is the Autofilled mode, compares the website | |
200 password to the DOM state. | |
201 If the testing mode is the NotAutofilled mode, checks that the DOM state | |
202 is empty. | |
203 Then, fills the input with the Website password. | |
204 | |
205 Args: | |
206 selector: The password input CSS selector. | |
207 | |
208 Raises: | |
209 Exception: An exception is raised if the DOM value of the password is | |
210 different than the one we expected. | |
211 """ | |
212 logging.info("action: FillPasswordInto %s" % selector) | |
213 self.WaitUntilDisplayed(selector) | |
214 password_element = self.driver.find_element_by_css_selector(selector) | |
215 # Chrome protects the password inputs and doesn't fill them until | |
216 # the user interacts with the page. To be sure that such thing has | |
217 # happened we perform |Keys.CONTROL| keypress. | |
218 action_chains = ActionChains(self.driver) | |
219 action_chains.key_down(Keys.CONTROL).key_up(Keys.CONTROL).perform() | |
220 if self.mode == self.Mode.AUTOFILLED: | |
221 autofilled_password = password_element.get_attribute("value") | |
222 if autofilled_password != self.password: | |
223 raise Exception("Error: autofilled password is different from the one " | |
224 "we just saved for the following website : %s p1: %s " | |
225 "p2:%s \n" % (self.name, | |
226 password_element.get_attribute("value"), | |
227 self.password)) | |
228 | |
229 elif self.mode == self.Mode.NOT_AUTOFILLED: | |
230 autofilled_password = password_element.get_attribute("value") | |
231 if autofilled_password: | |
232 raise Exception("Error: password is autofilled when it shouldn't be " | |
233 "for the following website : %s \n" | |
234 % self.name) | |
235 | |
236 password_element.send_keys(self.password) | |
237 | |
238 def FillUsernameInto(self, selector): | |
239 """If the testing mode is the Autofilled mode, compares the website | |
240 username to the input value. Then, fills the input with the website | |
241 username. | |
242 | |
243 Args: | |
244 selector: The username input CSS selector. | |
245 | |
246 Raises: | |
247 Exception: An exception is raised if the DOM value of the username is | |
248 different that the one we expected. | |
249 """ | |
250 logging.info("action: FillUsernameInto %s" % selector) | |
251 self.WaitUntilDisplayed(selector) | |
252 username_element = self.driver.find_element_by_css_selector(selector) | |
253 | |
254 if (self.mode == self.Mode.AUTOFILLED and not self.username_not_auto): | |
255 if not (username_element.get_attribute("value") == self.username): | |
256 raise Exception("Error: autofilled username is different form the one " | |
257 "we just saved for the following website : %s \n" % | |
258 self.name) | |
259 else: | |
260 username_element.clear() | |
261 username_element.send_keys(self.username) | |
262 | |
263 def Submit(self, selector): | |
264 """Finds an element using CSS Selector and calls its submit() handler. | |
265 | |
266 Args: | |
267 selector: The input CSS selector. | |
268 """ | |
269 logging.info("action: Submit %s" % selector) | |
270 self.WaitUntilDisplayed(selector) | |
271 element = self.driver.find_element_by_css_selector(selector) | |
272 element.submit() | |
273 | |
274 # Login/Logout Methods | |
275 | |
276 def Login(self): | |
277 """Login Method. Has to be overloaded by the WebsiteTest test.""" | |
278 raise NotImplementedError("Login is not implemented.") | |
279 | |
280 def LoginWhenAutofilled(self): | |
281 """Logs in and checks that the password is autofilled.""" | |
282 self.mode = self.Mode.AUTOFILLED | |
283 self.Login() | |
284 | |
285 def LoginWhenNotAutofilled(self): | |
286 """Logs in and checks that the password is not autofilled.""" | |
287 self.mode = self.Mode.NOT_AUTOFILLED | |
288 self.Login() | |
289 | |
290 def Logout(self): | |
291 """Logout Method.""" | |
292 | |
293 # Tests | |
294 | |
295 def WrongLoginTest(self): | |
296 """Does the wrong login test: Tries to login with a wrong password and | |
297 checks that the password is not saved. | |
298 | |
299 Raises: | |
300 Exception: An exception is raised if the test fails: If there is a | |
301 problem when performing the login (ex: the login button is not | |
302 available ...), if the state of the username and password fields is | |
303 not like we expected or if the password is saved. | |
304 """ | |
305 logging.info("\nWrong Login Test for %s \n" % self.name) | |
306 try: | |
307 correct_password = self.password | |
308 self.password = self.password + "1" | |
309 self.LoginWhenNotAutofilled() | |
310 self.password = correct_password | |
311 self.Wait(2) | |
312 self.environment.SwitchToInternals() | |
313 self.environment.CheckForNewMessage( | |
314 environment.MESSAGE_SAVE, | |
315 False, | |
316 "Error: password manager thinks that a login with wrong password was " | |
317 "successful for the following website : %s \n" % self.name) | |
318 finally: | |
319 self.environment.SwitchFromInternals() | |
320 | |
321 def SuccessfulLoginTest(self): | |
322 """Does the successful login when the password is not expected to be | |
323 autofilled test: Checks that the password is not autofilled, tries to login | |
324 with a right password and checks if the password is saved. Then logs out. | |
325 | |
326 Raises: | |
327 Exception: An exception is raised if the test fails: If there is a | |
328 problem when performing the login and the logout (ex: the login | |
329 button is not available ...), if the state of the username and | |
330 password fields is not like we expected or if the password is not | |
331 saved. | |
332 """ | |
333 logging.info("\nSuccessful Login Test for %s \n" % self.name) | |
334 try: | |
335 self.LoginWhenNotAutofilled() | |
336 self.Wait(2) | |
337 self.environment.SwitchToInternals() | |
338 self.environment.CheckForNewMessage( | |
339 environment.MESSAGE_SAVE, | |
340 True, | |
341 "Error: password manager hasn't detected a successful login for the " | |
342 "following website : %s \n" | |
343 % self.name) | |
344 finally: | |
345 self.environment.SwitchFromInternals() | |
346 self.Logout() | |
347 | |
348 def SuccessfulLoginWithAutofilledPasswordTest(self): | |
349 """Does the successful login when the password is expected to be autofilled | |
350 test: Checks that the password is autofilled, tries to login with the | |
351 autofilled password and checks if the password is saved. Then logs out. | |
352 | |
353 Raises: | |
354 Exception: An exception is raised if the test fails: If there is a | |
355 problem when performing the login and the logout (ex: the login | |
356 button is not available ...), if the state of the username and | |
357 password fields is not like we expected or if the password is not | |
358 saved. | |
359 """ | |
360 logging.info("\nSuccessful Login With Autofilled Password" | |
361 " Test %s \n" % self.name) | |
362 try: | |
363 self.LoginWhenAutofilled() | |
364 self.Wait(2) | |
365 self.environment.SwitchToInternals() | |
366 self.environment.CheckForNewMessage( | |
367 environment.MESSAGE_SAVE, | |
368 True, | |
369 "Error: password manager hasn't detected a successful login for the " | |
370 "following website : %s \n" | |
371 % self.name) | |
372 finally: | |
373 self.environment.SwitchFromInternals() | |
374 self.Logout() | |
375 | |
376 def PromptTest(self): | |
377 """Does the prompt test: Tries to login with a wrong password and | |
378 checks that the prompt is not shown. Then tries to login with a right | |
379 password and checks that the prompt is not shown. | |
380 | |
381 Raises: | |
382 Exception: An exception is raised if the test fails: If there is a | |
383 problem when performing the login (ex: the login button is not | |
384 available ...), if the state of the username and password fields is | |
385 not like we expected or if the prompt is not shown for the right | |
386 password or is shown for a wrong one. | |
387 """ | |
388 logging.info("\nPrompt Test for %s \n" % self.name) | |
389 try: | |
390 correct_password = self.password | |
391 self.password = self.password + "1" | |
392 self.LoginWhenNotAutofilled() | |
393 self.password = correct_password | |
394 self.Wait(2) | |
395 self.environment.SwitchToInternals() | |
396 self.environment.CheckForNewMessage( | |
397 environment.MESSAGE_ASK, | |
398 False, | |
399 "Error: password manager thinks that a login with wrong password was " | |
400 "successful for the following website : %s \n" % self.name) | |
401 self.environment.SwitchFromInternals() | |
402 | |
403 self.LoginWhenNotAutofilled() | |
404 self.Wait(2) | |
405 self.environment.SwitchToInternals() | |
406 self.environment.CheckForNewMessage( | |
407 environment.MESSAGE_ASK, | |
408 True, | |
409 "Error: password manager hasn't detected a successful login for the " | |
410 "following website : %s \n" % self.name) | |
411 finally: | |
412 self.environment.SwitchFromInternals() | |
OLD | NEW |