OLD | NEW |
---|---|
(Empty) | |
1 """Website testing class.""" | |
2 | |
3 | |
4 import time | |
5 | |
6 from selenium.common.exceptions import ElementNotVisibleException | |
7 from selenium.common.exceptions import NoSuchElementException | |
8 from selenium.common.exceptions import StaleElementReferenceException | |
9 from selenium.webdriver.common.action_chains import ActionChains | |
10 from selenium.webdriver.common.keys import Keys | |
11 | |
12 | |
13 def _IsOneSubstringOfAnother(s1, s2): | |
vabr (Chromium)
2014/05/16 09:36:00
Please add a doc string, explaining what this func
rchtara
2014/05/20 08:24:47
Done.
| |
14 return s1 in s2 or s2 in s1 | |
15 | |
16 | |
17 class Website: | |
18 """Handles a tested Website.""" | |
19 | |
20 class Mode: | |
21 """Test mode.""" | |
22 # Password and username are expected to be autofilled. | |
23 Autofilled = 1 | |
vabr (Chromium)
2014/05/16 09:36:00
Please use all caps for consistency with the C++ e
rchtara
2014/05/20 08:24:47
Done.
| |
24 # Password and username are not expected to be autofilled. | |
25 NotAutofilled = 2 | |
26 | |
27 def __init__(self): | |
28 pass | |
29 | |
30 def __init__( | |
31 self, name, url, username=None, password=None, | |
vabr (Chromium)
2014/05/16 09:36:00
Please remove |url|. You don't seem to use it, and
vabr (Chromium)
2014/05/16 09:36:00
Are you actually using the username and password a
rchtara
2014/05/20 08:24:47
Yes, I use them to sign in
rchtara
2014/05/20 08:24:47
Done.
vabr (Chromium)
2014/05/20 14:47:25
But you don't seem to use the __init__ arguments u
| |
32 username_not_auto=False): | |
33 """Creates a new Website. | |
34 | |
35 Args: | |
36 name: The Website name. | |
37 url: The Website URL. | |
38 username: The Website username. If it's None, the username is going to be | |
39 replaced by the value in the usernames and passwords file. | |
40 password: The Website password. If it's None, the password is going to be | |
41 replaced by the value in the usernames and passwords file. | |
42 username_not_auto: Username inputs in some Websites (like wikipedia) are | |
43 sometimes filled with some messages and thus, the usernames are not | |
44 automatically autofilled. This flag handles that and disables us from | |
45 checking if the state of the DOM is the same as the username of | |
46 Website. | |
47 """ | |
48 # Name of the Website | |
49 self.name = name | |
50 # URL of the Website | |
51 self.url = url | |
52 # Username of the Website. | |
53 self.username = username | |
54 # Password of the Website. | |
55 self.password = password | |
56 # Username is not automatically filled. | |
57 self.username_not_auto = username_not_auto | |
vabr (Chromium)
2014/05/16 09:36:00
For pages like Wikipedia, where the username is no
rchtara
2014/05/20 08:24:47
username_not_auto affects only the username, but t
vabr (Chromium)
2014/05/20 14:47:25
You are correct, thanks for explanation.
rchtara
2014/05/22 08:44:38
you re welcome :)
| |
58 # Autofilling mode. | |
59 self.mode = self.Mode.NotAutofilled | |
60 # Waiting duration before stopping the test. | |
vabr (Chromium)
2014/05/16 09:36:00
Note: max_duration limits the total time spent in
rchtara
2014/05/20 08:24:47
Done.
| |
61 self.max_duration = 200 | |
62 # The testing Environment. | |
63 self.environment = None | |
64 # The webdriver. | |
65 self.driver = None | |
66 | |
67 # Mouse/Keyboard actions. | |
68 | |
69 def Click(self, selector): | |
70 """Clicks on an element. | |
71 | |
72 Args: | |
73 selector: The element CSS selector. | |
74 """ | |
75 self.environment.Log("action: Click %s" % selector) | |
76 element = self.driver.find_element_by_css_selector(selector) | |
77 element.click() | |
78 | |
79 def ClickIfVisible(self, selector): | |
80 """Clicks on an element, if it's available. | |
vabr (Chromium)
2014/05/16 09:36:00
So, is this about visibility, or availability?
If
rchtara
2014/05/20 08:24:47
Done.
| |
81 | |
82 Args: | |
83 selector: The element CSS selector. | |
84 """ | |
85 self.environment.Log("action: ClickIfVisible %s" % selector) | |
86 try: | |
87 element = self.driver.find_element_by_css_selector(selector) | |
88 element.click() | |
89 except NoSuchElementException: | |
90 return False | |
vabr (Chromium)
2014/05/16 09:36:00
The method returns False if the element is not cli
rchtara
2014/05/20 08:24:47
Done.
| |
91 except StaleElementReferenceException: | |
92 return False | |
93 | |
94 def GoTo(self, url): | |
95 """Navigates the main frame to a url. | |
vabr (Chromium)
2014/05/16 09:36:00
nit: "a url" -> "|url|"
(It does not navigate to j
rchtara
2014/05/20 08:24:47
Done.
| |
96 | |
97 Args: | |
98 url: The URL. | |
99 """ | |
100 self.environment.Log("action: GoTo %s" % self.url) | |
101 self.driver.get(url) | |
102 | |
103 def HoverOver(self, selector): | |
104 """Hovers over an element. | |
105 | |
106 Args: | |
107 selector: The element CSS selector. | |
108 """ | |
109 self.environment.Log("action: Hover %s" % selector) | |
110 element = self.driver.find_element_by_css_selector(selector) | |
111 hover = ActionChains(self.driver).move_to_element(element) | |
112 hover.perform() | |
113 | |
114 def SendEnterTo(self, selector): | |
115 """Sends an enter key to an element. | |
116 | |
117 Args: | |
118 selector: The element CSS selector. | |
119 """ | |
120 self.environment.Log("action: SendEnterTo %s" % selector) | |
121 body = self.driver.find_element_by_tag_name("body") | |
122 body.send_keys(Keys.ENTER) | |
123 | |
124 # Waiting/Displaying actions. | |
125 | |
126 def IsDisplayed(self, selector): | |
127 """Checks if an element is displayed. | |
128 | |
129 Args: | |
130 selector: The element CSS selector. | |
131 """ | |
132 self.environment.Log("action: IsDisplayed %s" % selector) | |
133 try: | |
134 element = self.driver.find_element_by_css_selector(selector) | |
135 return element.is_displayed() | |
136 except NoSuchElementException: | |
137 return False | |
138 except StaleElementReferenceException: | |
139 return False | |
140 | |
141 def Wait(self, duration): | |
142 """Wait for a duration. | |
vabr (Chromium)
2014/05/16 09:36:00
Please specify time units.
rchtara
2014/05/20 08:24:47
Done.
| |
143 | |
144 Args: | |
145 duration: The element. | |
146 """ | |
147 self.environment.Log("action: Wait %s" % duration) | |
148 time.sleep(duration) | |
149 self.max_duration -= 1 | |
150 if self.max_duration < 0: | |
151 raise Exception("Tests took more time than expected for the following " | |
152 "website : %s \n" % self.name) | |
153 | |
154 def WaitUntilDisplayed(self, selector, timeout=10): | |
155 """Waits until an element is displayed. | |
156 | |
157 Args: | |
158 selector: The element CSS selector. | |
159 """ | |
160 if not self.IsDisplayed(selector): | |
161 time.sleep(1) | |
vabr (Chromium)
2014/05/16 09:36:00
Please call Wait() instead of manually decrementin
rchtara
2014/05/20 08:24:47
Done.
| |
162 timeout = timeout - 1 | |
163 if (timeout <= 0): | |
164 raise Exception("Error: Element %s not shown before timeout is " | |
165 "finished for the following website: %s" | |
166 % (selector, self.name)) | |
167 else: | |
168 self.WaitUntilDisplayed(selector, timeout) | |
169 self.max_duration -= 1 | |
170 if self.max_duration < 0: | |
171 raise Exception("Tests took more time than expected for the " | |
172 "following website : %s \n" % self.name) | |
173 | |
174 # Form actions. | |
175 | |
176 def FillPasswordInto(self, selector): | |
177 """If the testing mode is the Autofilled mode, compares the Website | |
178 password to the DOM state. | |
179 If the testing mode is the NotAutofilled mode, checks that the DOM state | |
180 is empty. | |
181 Then, fills the input with the Website password. | |
182 | |
183 Args: | |
184 selector: The password input CSS selector. | |
185 | |
186 Raises: | |
187 Exception: An exception is raised if the DOM value of the password is | |
188 different that the one we expected. | |
189 """ | |
190 self.environment.Log("action: FillPasswordInto %s" % selector) | |
191 password_element = self.driver.find_element_by_css_selector(selector) | |
192 if self.mode == self.Mode.Autofilled: | |
193 # Chrome protects the password inputs and doesn't fill them until | |
194 # the user interacts with the page. To guarantee that, we just | |
195 # send a key to the password input. Clicking on the password input was | |
196 # tried too, but because the password is sometimes hidden, this didn't | |
vabr (Chromium)
2014/05/16 09:36:00
I'm not sure I understand the issue here -- if the
rchtara
2014/05/20 08:24:47
In www.163.com, we the driver fills the username w
| |
197 # worked out. | |
vabr (Chromium)
2014/05/16 09:36:00
nit: worked -> work
rchtara
2014/05/20 08:24:47
Done.
| |
198 password_element.send_keys("a") | |
199 ps = password_element.get_attribute("value")[:-1] | |
vabr (Chromium)
2014/05/16 09:36:00
Pleas do not use cryptic abbreviations as names of
rchtara
2014/05/20 08:24:47
Done.
| |
200 password_element.clear() | |
201 password_element.send_keys(ps) | |
202 if password_element.get_attribute("value") != self.password: | |
203 raise Exception("Error: autofilled password is different from the one " | |
204 "we just saved for the following website : %s p1: %s " | |
205 "p2:%s \n" % (self.name, | |
206 password_element.get_attribute("value"), | |
vabr (Chromium)
2014/05/16 09:36:00
nit: indenting is off
rchtara
2014/05/20 08:24:47
Done.
| |
207 self.password)) | |
208 elif self.mode == self.Mode.NotAutofilled: | |
209 # Chrome protects the password inputs and doesn't fill them until | |
210 # the user interacts with the page. To guarantee that, we just | |
211 # send a key to the password input. Clicking on the password input was | |
212 # tried too, but because the password is sometimes hidden, this didn't | |
213 # worked out. | |
214 password_element.send_keys("a") | |
215 ps = password_element.get_attribute("value")[1:] | |
vabr (Chromium)
2014/05/16 09:36:00
Again, please rename |ps| appropriately.
rchtara
2014/05/20 08:24:47
Done.
| |
216 password_element.clear() | |
217 password_element.send_keys(ps) | |
vabr (Chromium)
2014/05/16 09:36:00
Why do you send |ps| to password_element here, and
rchtara
2014/05/20 08:24:47
I' m going to replace all this by a click on the u
| |
218 if ps: | |
219 raise Exception("Error: password is autofilled when it shouldn't be " | |
220 "for the following website : %s \n" | |
221 % self.name) | |
222 | |
223 # Chrome protects the password inputs and doesn't fill them until | |
224 # the user interacts with the page. To guarantee that, we just | |
225 # send a key to the password. Clicking on the password input was tried | |
226 # too, but because the password is sometime hidden, this didn't worked | |
227 # out. | |
228 password_element.send_keys("a") | |
229 password_element.clear() | |
230 password_element.send_keys(self.password) | |
231 | |
232 def FillUsernameInto(self, selector): | |
vabr (Chromium)
2014/05/16 09:36:00
In the concrete Websites, FillUsernameInto and Fil
rchtara
2014/05/20 08:24:47
I think it's more flexible to keep it as it's now.
vabr (Chromium)
2014/05/20 14:47:25
Fair enough, let's keep them separate.
rchtara
2014/05/22 08:44:38
Done.
| |
233 """If the testing mode is the Autofilled mode, compares the Website username | |
234 to the input value. | |
235 Then, fills the input with the Website username. | |
236 | |
237 Args: | |
238 selector: The username input CSS selector. | |
239 | |
240 Raises: | |
241 Exception: An exception is raised if the DOM value of the username is | |
242 different that the one we expected. | |
243 """ | |
244 self.environment.Log("action: FillUsernameInto %s" % selector) | |
245 username_element = self.driver.find_element_by_css_selector(selector) | |
246 | |
247 if (self.mode == self.Mode.Autofilled and not self.username_not_auto): | |
248 if not (username_element.get_attribute("value") == self.username): | |
249 raise Exception("Error: autofilled username is different form the one " | |
250 "we just saved for the following website : %s \n" % | |
251 self.name) | |
252 | |
253 else: | |
254 username_element.clear() | |
vabr (Chromium)
2014/05/16 09:36:00
Should you check that either self.username_not_aut
rchtara
2014/05/20 08:24:47
I don't think it's important it important to do so
vabr (Chromium)
2014/05/20 14:47:25
Ah right, that's a good point, I forgot about the
rchtara
2014/05/22 08:44:38
You re welcome
| |
255 username_element.send_keys(self.username) | |
256 | |
257 def FillUsernameIfVisible(self, selector): | |
258 """Fills the input with the website username, if the input exists. | |
vabr (Chromium)
2014/05/16 09:36:00
Please comment on ignoring self.mode here.
rchtara
2014/05/20 08:24:47
I removed the method, not needed
| |
259 | |
260 Args: | |
261 selector: The username input CSS selector. | |
262 """ | |
263 self.environment.Log("action: FillUsernameIfVisible %s" % selector) | |
264 username_element = self.driver.find_element_by_css_selector(selector) | |
265 try: | |
266 username_element.clear() | |
267 username_element.send_keys(self.username) | |
268 except ElementNotVisibleException: | |
269 pass | |
270 | |
271 def Submit(self, selector): | |
272 """Finds an element using CSS Selector and calls its submit() handler. | |
273 | |
274 Args: | |
275 selector: The input CSS selector. | |
276 """ | |
277 self.environment.Log("action: Submit %s" % selector) | |
278 element = self.driver.find_element_by_css_selector(selector) | |
279 element.submit() | |
280 | |
281 # Login/Logout Methods | |
282 | |
283 def Login(self): | |
284 """Login Method. Has to be overloaded by the Website test.""" | |
285 raise NotImplementedError("Login is not implemented.") | |
286 | |
287 def LoginWhenAutofilled(self): | |
288 """Logs in and checks that the password is autofilled.""" | |
289 self.mode = self.Mode.Autofilled | |
290 self.Login() | |
291 | |
292 def LoginWhenNotAutofilled(self): | |
293 """Logs in and checks that the password is not autofilled.""" | |
294 self.mode = self.Mode.NotAutofilled | |
295 self.Login() | |
296 | |
297 def Logout(self): | |
298 """Logout Method. Has to be overloaded by the Website test.""" | |
299 raise NotImplementedError("Logout is not implemented.") | |
300 | |
301 # TestsTools | |
302 | |
303 def RemoveAllPasswords(self, urls): | |
rchtara
2014/05/20 08:24:47
Function removed
| |
304 """Removes all the saved passwords for the current Website. | |
305 | |
306 Args: | |
307 urls: All the available URLs in the saved passwords list. | |
308 """ | |
309 if (self.url != ""): | |
vabr (Chromium)
2014/05/16 09:36:00
nit: Just use
if self.url:
rchtara
2014/05/20 08:24:47
Done.
| |
310 i = 0 | |
311 for current_url in urls: | |
312 if _IsOneSubstringOfAnother(current_url, self.url): | |
313 self.driver.execute_script( | |
vabr (Chromium)
2014/05/16 09:36:00
Please comment on what the script does.
In particu
rchtara
2014/05/20 08:24:47
Done.
| |
314 "document.querySelectorAll('#saved-passwords-list " | |
315 ".row-delete-button')[%d].click()" % i) | |
vabr (Chromium)
2014/05/16 09:36:00
Actually, I don't think you use |i| correctly here
rchtara
2014/05/20 08:24:47
we get a fresh copy of |urls|, after each deletion
vabr (Chromium)
2014/05/20 14:47:25
Ah, that makes sense, thanks for your explanation.
| |
316 time.sleep(1) # Wait until command is executed. | |
vabr (Chromium)
2014/05/16 09:36:00
Please use Wait().
rchtara
2014/05/20 08:24:47
Done.
| |
317 else: | |
318 i = i + 1 | |
319 | |
320 | |
321 # Tests | |
322 | |
323 def WrongLoginTest(self): | |
324 """Does the wrong login test: Tries to login with a wrong password and | |
325 checks that the prompt is not shown. | |
326 | |
327 Raises: | |
328 Exception: An exception is raised if the tests fail. | |
329 """ | |
330 self.environment.Log("\nWrong Login Test for %s \n" % self.name) | |
331 correct_password = self.password | |
332 self.password = self.password + "1" | |
333 self.LoginWhenNotAutofilled() | |
334 self.password = correct_password | |
vabr (Chromium)
2014/05/16 09:36:00
Why do you not Wait() here, but you do wait at the
rchtara
2014/05/20 08:24:47
Done.
| |
335 self.environment.SwitchToInternals() | |
336 self.environment.CheckPromptIsNotShown( | |
337 False, | |
338 "Error: password manager thinks that a login with wrong password was " | |
339 "successful for the following website : %s \n" % self.name) | |
340 self.environment.SwitchFromInternals() | |
341 | |
342 def SuccessfulLoginTest(self): | |
343 """Does the successful login when the password is not expected to be | |
344 autofilled test: Checks that the password is not autofilled, tries to login | |
345 with a right password and checks if the that the prompt is shown. Then logs | |
346 out. | |
347 | |
348 Raises: | |
349 Exception: An exception is raised if the tests fail. | |
350 """ | |
351 self.environment.Log("\nSuccessful Login Test for %s \n" % self.name) | |
352 self.LoginWhenNotAutofilled() | |
353 time.sleep(2) | |
vabr (Chromium)
2014/05/16 09:36:00
Please use Wait().
rchtara
2014/05/20 08:24:47
Done.
| |
354 self.environment.SwitchToInternals() | |
355 self.environment.CheckPromptIsNotShown(True, | |
356 "Error: password manager hasn't detected a successful login for the " | |
357 "following website : %s \n" | |
358 % self.name) | |
359 self.environment.SwitchFromInternals() | |
360 self.Logout() | |
361 | |
362 def SuccessfulLoginWithAutofilledPasswordTest(self): | |
363 """Does the successful login when the password is expected to be autofilled | |
364 test: Checks that the password is autofilled, tries to login with a right | |
365 password and checks if the that the prompt is shown. Then logs out. | |
366 | |
367 Raises: | |
368 Exception: An exception is raised if the tests fail. | |
369 """ | |
370 self.environment.Log("\nSuccessful Login With Autofilled Password" | |
371 " Test %s \n" % self.name) | |
372 self.LoginWhenAutofilled() | |
373 time.sleep(2) | |
vabr (Chromium)
2014/05/16 09:36:00
Please use Wait().
rchtara
2014/05/20 08:24:47
Done.
| |
374 self.environment.SwitchToInternals() | |
375 self.environment.CheckPromptIsNotShown(True, | |
376 "Error: password manager hasn't detected a successful login for the " | |
377 "following website : %s \n" | |
378 % self.name) | |
379 self.environment.SwitchFromInternals() | |
380 self.Logout() | |
381 | |
382 def SuccessfulLoginAfterDeletionTest(self): | |
vabr (Chromium)
2014/05/16 09:36:00
This does exactly the same thing as SuccessfulLogi
rchtara
2014/05/20 08:24:47
Done.
| |
383 """Does the successful login after the deletion of the password test: Checks | |
384 that the password is not autofilled, tries to login with a right password | |
385 and checks if the that the prompt is shown. Then logs out. | |
386 | |
387 Raises: | |
388 Exception: An exception is raised if the tests fail. | |
389 """ | |
390 self.environment.Log("\nSuccessful Login After Deletion Test" | |
391 " for %s \n" % self.name) | |
392 self.LoginWhenNotAutofilled() | |
393 self.environment.SwitchToInternals() | |
394 self.environment.CheckPromptIsNotShown( | |
395 True, | |
396 "Error: password manager hasn't detected a successful login for the " | |
397 "following website : %s \n" % self.name) | |
398 self.environment.SwitchFromInternals() | |
399 self.Logout() | |
OLD | NEW |