Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(144)

Side by Side Diff: components/test/data/password_manager/website.py

Issue 273523004: Password Manager testing automation (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: new arch Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 _IsOneStringPrefixOfAnother(s1, s2):
14 l = min(len(s1), len(s2))
15 return s1[:l] == s2[:l]
16
17
18 def _IsOneSubstringOfAnother(s1, s2):
19 return s1 in s2 or s2 in s1
20
21
22 class Website:
23 """Handles a tested Website."""
24
25 class Mode:
26 """Test mode."""
27 # Password and username are expected to be autofilled.
28 Autofilled = 1
29 # Password and username are not expected to be autofilled.
30 NotAutofilled = 2
31
32 def __init__(self):
33 pass
34
35 def __init__(
36 self, name, url, username=None, password=None,
37 username_not_auto=False):
38 """Creates a new Website.
39
40 Args:
41 name: The Website name.
42 url: The Website URL.
43 username: The Website username. If it's None, the username is going to be
44 replaced by the value in the usernames and passwords file.
45 password: The Website password. If it's None, the password is going to be
46 replaced by the value in the usernames and passwords file.
47 username_not_auto: Username inputs in some Websites (like wikipedia) are
48 sometimes filled with some messages and thus, the usernames are not
49 automatically autofilled. This flag handles that and disables us from
50 checking if the state of the DOM is the same as the username of
51 Website.
52 """
53 # Name of the Website
54 self.name = name
55 # URL of the Website
56 self.url = url
57 # Username of the Website.
58 self.username = username
59 # Password of the Website.
60 self.password = password
61 # TODO: Remove this as soon as the password internals page is ready.
62 self.internals_folder = ("file:///usr/local/google/home/rchtara/chrome"
63 "/python/passwordinternals/internalmock/")
64 self.internals_file = ""
65 # Username is not automatically filled.
66 self.username_not_auto = username_not_auto
67 # Autofilling mode.
68 self.mode = self.Mode.NotAutofilled
69 # Waiting duration before stopping the test.
70 self.max_duration = 200
71
72 # Mouse/Keyboard actions.
73
74 def Click(self, selector):
75 """Clicks on an element.
76
77 Args:
78 selector: The element CSS selector.
79 """
80 print "action: Click %s" % selector
81 element = self.driver.find_element_by_css_selector(selector)
82 element.click()
83
84 def ClickIfAvailable(self, selector):
85 """Clicks on an element, if it's available.
86
87 Args:
88 selector: The element CSS selector.
89 """
90 print "action: ClickIfAvailable %s" % selector
91 try:
92 element = self.driver.find_element_by_css_selector(selector)
93 element.click()
94 except NoSuchElementException:
95 return False
96 except StaleElementReferenceException:
97 return False
98
99 def Enter(self, selector):
100 """Sends an enter key to an element.
101
102 Args:
103 selector: The element CSS selector.
104 """
105 print "action: Enter %s" % selector
106 body = self.driver.find_element_by_tag_name("body")
107 body.send_keys(Keys.ENTER)
108
109 def GoTo(self, url):
110 """Goes to a URL.
111
112 Args:
113 url: The URL.
114 """
115 print "action: GoTo %s" % self.url
116 self.driver.get(url)
117
118 def Hover(self, selector):
119 """Hovers on an element, if it's available.
120
121 Args:
122 selector: The element CSS selector.
123 """
124 print "action: Hover %s" % selector
125 element = self.driver.find_element_by_css_selector(selector)
126 hover = ActionChains(self.driver).move_to_element(element)
127 hover.perform()
128
129 # Waiting/Displaying actions.
130
131 def IsDisplayed(self, selector):
132 """Checks if an element is displayed.
133
134 Args:
135 selector: The element CSS selector.
136 """
137 print "action: IsDisplayed %s" % selector
138 try:
139 element = self.driver.find_element_by_css_selector(selector)
140 return element.is_displayed()
141 except NoSuchElementException:
142 return False
143 except StaleElementReferenceException:
144 return False
145
146 def Wait(self, duration):
147 """Wait for a duration.
148
149 Args:
150 duration: The element.
151 """
152 print "action: Wait %s" % duration
153 time.sleep(duration)
154 self.max_duration -= 1
155 if self.max_duration < 0:
156 raise Exception("Tests took more time than expected for the following "
157 "website : %s \n" % self.name)
158
159 def WaitUntilDisplayed(self, selector, timeout=10):
160 """Waits until an element is displayed.
161
162 Args:
163 selector: The element CSS selector.
164 """
165 if not self.IsDisplayed(selector):
166 time.sleep(1)
167 timeout = timeout - 1
168 if (timeout <= 0):
169 raise Exception("Error: Element %s not shown before timeout is "
170 "finished for the following website: %s"
171 % (selector, self.name))
172 else:
173 self.WaitUntilDisplayed(selector, timeout)
174 self.max_duration -= 1
175 if self.max_duration < 0:
176 raise Exception("Tests took more time than expected for the "
177 "following website : %s \n" % self.name)
178
179 # Form actions.
180
181 def FillPassword(self, selector):
182 """If the testing mode is the Autofilled mode, compares the Website
183 password to the DOM state.
184 If the testing mode is the NotAutofilled mode, checks that the DOM state
185 is empty.
186 Then, fills the input with the Website password.
187
188 Args:
189 selector: The password input CSS selector.
190
191 Raises:
192 Exception: An exception is raised if the DOM value of the password is
193 different that the one we expected.
194 """
195 print "action: FillPassword %s" % selector
196 password_element = self.driver.find_element_by_css_selector(selector)
197 if self.mode == self.Mode.Autofilled:
198 # Chrome protects the password inputs and doesn't fill them until
199 # the user interacts with the page. To guarantee that, we just
200 # send a key to the password input. Clicking on the password input was
201 # tried too, but because the password is sometimes hidden, this didn't
202 # worked out.
203 password_element.send_keys("a")
204 ps = password_element.get_attribute("value")[:-1]
205 password_element.clear()
206 password_element.send_keys(ps)
207 if password_element.get_attribute("value") != self.password:
208 raise Exception("Error: autofilled password is different from the one "
209 "we just saved for the following website : %s p1: %s "
210 "p2:%s \n" % (self.name,
211 password_element.get_attribute("value"),
212 self.password))
213 elif self.mode == self.Mode.NotAutofilled:
214 # Chrome protects the password inputs and doesn't fill them until
215 # the user interacts with the page. To guarantee that, we just
216 # send a key to the password input. Clicking on the password input was
217 # tried too, but because the password is sometimes hidden, this didn't
218 # worked out.
219 password_element.send_keys("a")
220 ps = password_element.get_attribute("value")[1:]
221 password_element.clear()
222 password_element.send_keys(ps)
223 if ps:
224 raise Exception("Error: password is autofilled when it shouldn't be "
225 "for the following website : %s \n"
226 % self.name)
227
228 # Chrome protects the password inputs and doesn't fill them until
229 # the user interacts with the page. To guarantee that, we just
230 # send a key to the password. Clicking on the password input was tried
231 # too, but because the password is sometime hidden, this didn't worked
232 # out.
233 password_element.send_keys("a")
234 password_element.clear()
235 password_element.send_keys(self.password)
236
237 def FillUsername(self, selector):
238 """If the testing mode is the Autofilled mode, compares the Website username
239 to the input value.
240 Then, fills the input with the Website username.
241
242 Args:
243 selector: The username input CSS selector.
244
245 Raises:
246 Exception: An exception is raised if the DOM value of the username is
247 different that the one we expected.
248 """
249 print "action: FillUsername %s" % selector
250 username_element = self.driver.find_element_by_css_selector(selector)
251
252 if (self.mode == self.Mode.Autofilled and not self.username_not_auto):
253 if not (username_element.get_attribute("value") == self.username):
254 raise Exception("Error: autofilled username is different form the one "
255 "we just saved for the following website : %s \n" %
256 self.name)
257
258 else:
259 username_element.clear()
260 username_element.send_keys(self.username)
261
262 def OptionalFillUsername(self, selector):
263 """Fills the input with the website username, if the input exists.
264
265 Args:
266 selector: The username input CSS selector.
267 """
268 print "action: OptionalFillUsername %s" % selector
269 username_element = self.driver.find_element_by_css_selector(selector)
270 try:
271 username_element.clear()
272 username_element.send_keys(self.username)
273 except ElementNotVisibleException:
274 pass
275
276 def Submit(self, selector):
277 """Submits an input.
278
279 Args:
280 selector: The input CSS selector.
281 """
282 print "action: Submit %s" % selector
283 element = self.driver.find_element_by_css_selector(selector)
284 element.submit()
285
286 # Login/Logout Methods
287
288 def Login(self):
289 """Login Method. Has to be overloaded by the Website test."""
290 raise NotImplementedError("Login is not implemented.")
291
292 def LoginWhenAutofilled(self):
293 """Logs in and checks that the password is autofilled."""
294 self.mode = self.Mode.Autofilled
295 self.Login()
296
297 def LoginWhenNotAutofilled(self):
298 """Logs in and checks that the password is not autofilled."""
299 self.mode = self.Mode.NotAutofilled
300 self.Login()
301
302 def Logout(self):
303 """Logout Method. Has to be overloaded by the Website test."""
304 raise NotImplementedError("Logout is not implemented.")
305
306 # TestsTools
307
308 def RemoveAllPasswords(self, urls):
309 """Removes all the saved passwords for the current Website.
310
311 Args:
312 urls: All the available URLs in the saved passwords list.
313 """
314 if (self.url != ""):
315 i = 0
316 for current_url in urls:
317 if _IsOneSubstringOfAnother(current_url, self.url):
318 self.driver.execute_script(
319 "document.querySelectorAll('#saved-passwords-list "
320 ".row-delete-button')[%d].click()" % i)
321 time.sleep(1) # Wait until command is executed.
322 else:
323 i = i + 1
324
325 # TODO(rchtara) Replace this when password internals is ready.
326 def SwitchToInternals(self):
327 """Switches from the Website window to internals tab."""
328 self.driver.switch_to_window(self.test_environment.internals_window)
329 self.driver.get(self.internals_folder + self.internals_file)
330
331 # TODO(rchtara) Replace this when password internals is ready.
332 def SwitchFromInternals(self):
333 """Switches from internals tab to the Website window."""
334 self.driver.switch_to_window(self.test_environment.websitewindow)
335
336 # TODO(rchtara) Replace this when password internals is ready.
337 def UrlExists(self, element_id, msg):
338 elments = self.driver.find_element_by_id(element_id)
339 urls = elments.find_elements_by_class_name("url")
340 match = False
341 for u in urls:
342 if (_IsOneSubstringOfAnother(u.text, self.url) or
343 _IsOneSubstringOfAnother(u.text, self.name)):
344 match = True
345 break
346 if not match:
347 raise Exception(msg)
348
349 # TODO(rchtara) Replace this when password internals is ready.
350 def UrlNotExists(self, element_id, msg):
351
352 elments = self.driver.find_element_by_id(element_id)
353 urls = elments.find_elements_by_class_name("url")
354 match = False
355 for u in urls:
356 if _IsOneSubstringOfAnother(u.text, self.url):
357 match = True
358 break
359 if match:
360 raise Exception(msg)
361
362 # Tests
363
364 def WrongLoginTest(self):
365 """Does the wrong login test: Tries to login with a wrong password and
366 checks that the prompt is not shown.
367
368 Raises:
369 Exception: An exception is raised if the tests fail.
370 """
371 print "\nWrong Login Test for %s \n" % self.name
372 correct_password = self.password
373 self.password = self.password + "1"
374 self.LoginWhenNotAutofilled()
375 self.password = correct_password
376 self.internals_file = "wrongpass.html"
377 self.SwitchToInternals()
378 self.UrlExists("unsuccessfullogins", ("Error: password manager thinks that"
379 " a login with wrong password was "
380 "successful for the following "
381 "website : %s \n" % self.name))
382 self.UrlNotExists(
383 "showedprompts",
384 "Error: prompt is shown for a wrong password for the following website"
385 " : %s \n" % self.name)
386 self.SwitchFromInternals()
387
388 def SuccessfulLoginTest(self):
389 """Does the successful login when the password is not expected to be
390 autofilled test: Checks that the password is not autofilled, tries to login
391 with a right password and checks if the that the prompt is shown. Then logs
392 out.
393
394 Raises:
395 Exception: An exception is raised if the tests fail.
396 """
397 print "\nSuccessful Login Test for %s \n" % self.name
398 self.LoginWhenNotAutofilled()
399 time.sleep(2)
400 self.internals_file = "rightpass.html"
401 self.SwitchToInternals()
402 self.UrlExists(
403 "successfullogins",
404 "Error: password manager hasn't detected a successful login for the "
405 "following website : %s \n"
406 % self.name)
407 self.UrlExists(
408 "showedprompts",
409 "Error: prompt is not shown for a right password for the following "
410 "website : %s \n" % self.name)
411 self.SwitchFromInternals()
412 self.Logout()
413
414 def SuccessfulLoginWithAutofilledPasswordTest(self):
415 """Does the successful login when the password is expected to be autofilled
416 test: Checks that the password is autofilled, tries to login with a right
417 password and checks if the that the prompt is shown. Then logs out.
418
419 Raises:
420 Exception: An exception is raised if the tests fail.
421 """
422 print "\nSuccessful Login With Autofilled Password Test %s \n" % self.name
423 self.LoginWhenAutofilled()
424 time.sleep(2)
425 self.internals_file = "rightpass.html"
426 self.SwitchToInternals()
427 self.UrlExists(
428 "successfullogins",
429 "Error: password manager hasn't detected a successful login for the "
430 "following website : %s \n"
431 % self.name)
432 self.UrlExists(
433 "showedprompts",
434 "Error: prompt is not shown for a right password for the following "
435 "website : %s \n" % self.name)
436 self.SwitchFromInternals()
437 self.Logout()
438
439 def SuccessfulLoginAfterDeletionTest(self):
440 """Does the successful login after the deletion of the password test: Checks
441 that the password is not autofilled, tries to login with a right password
442 and checks if the that the prompt is shown. Then logs out.
443
444 Raises:
445 Exception: An exception is raised if the tests fail.
446 """
447 print "\nSuccessful Login After Deletion Test for %s \n" % self.name
448 self.LoginWhenNotAutofilled()
449 self.internals_file = "rightpass.html"
450 self.SwitchToInternals()
451 self.UrlExists(
452 "successfullogins", "Error: password manager hasn't detected a "
453 "successful login for the following website : %s "
454 "\n"
455 % self.name)
456 self.UrlExists(
457 "showedprompts",
458 "Error: prompt is not shown for a right password for the following "
459 "website : %s \n" % self.name)
460 self.SwitchFromInternals()
461 self.Logout()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698