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