1# -*- coding: utf-8 -*-
2# Copyright 2014 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Automated tests for many websites"""
7
8import argparse
9import logging
10
11from environment import Environment
12from websitetest import WebsiteTest
13
14
15class TypeOfTestedWebsites:
16  """An enum to specify which groups of tests to run."""
17  # Runs only the disabled tests.
18  DISABLED_TESTS = 0
19  # Runs only the enabled tests.
20  ENABLED_TESTS = 1
21  # Runs all the tests.
22  ALL_TESTS = 2
23  # Runs a specified list of tests.
24  LIST_OF_TESTS = 3
25
26  def __init__(self):
27    pass
28
29
30class Facebook(WebsiteTest):
31
32  def Login(self):
33    self.GoTo("https://www.facebook.com")
34    self.FillUsernameInto("[name='email']")
35    self.FillPasswordInto("[name='pass']")
36    self.Submit("[name='pass']")
37
38
39class Google(WebsiteTest):
40
41  def Login(self):
42    self.GoTo("https://accounts.google.com/ServiceLogin?sacu=1&continue=")
43    self.FillUsernameInto("#Email")
44    self.FillPasswordInto("#Passwd")
45    self.Submit("#Passwd")
46
47
48class Linkedin(WebsiteTest):
49
50  def Login(self):
51    self.GoTo("https://www.linkedin.com")
52    self.FillUsernameInto("#session_key-login")
53    self.FillPasswordInto("#session_password-login")
54    self.Submit("#session_password-login")
55
56
57class Mailru(WebsiteTest):
58
59  def Login(self):
60    self.GoTo("https://mail.ru")
61    self.FillUsernameInto("#mailbox__login")
62    self.FillPasswordInto("#mailbox__password")
63    self.Submit("#mailbox__password")
64
65
66class Nytimes(WebsiteTest):
67
68  def Login(self):
69    self.GoTo("https://myaccount.nytimes.com/auth/login")
70    self.FillUsernameInto("#userid")
71    self.FillPasswordInto("#password")
72    self.Submit("#password")
73
74
75class Pinterest(WebsiteTest):
76
77  def Login(self):
78    self.GoTo("https://www.pinterest.com/login/")
79    self.FillUsernameInto("[name='username_or_email']")
80    self.FillPasswordInto("[name='password']")
81    self.Submit("[name='password']")
82
83
84class Reddit(WebsiteTest):
85
86  def Login(self):
87    self.GoTo("http://www.reddit.com")
88    self.Click(".user .login-required")
89    self.FillUsernameInto("#user_login")
90    self.FillPasswordInto("#passwd_login")
91    self.Wait(2)
92    self.Submit("#passwd_login")
93
94
95class Tumblr(WebsiteTest):
96
97  def Login(self):
98    self.GoTo("https://www.tumblr.com/login")
99    self.FillUsernameInto("#signup_email")
100    self.FillPasswordInto("#signup_password")
101    self.Submit("#signup_password")
102
103
104class Twitter(WebsiteTest):
105
106  def Login(self):
107    self.GoTo("https:///twitter.com")
108    self.FillUsernameInto("#signin-email")
109    self.FillPasswordInto("#signin-password")
110    self.Submit("#signin-password")
111
112
113class Wikipedia(WebsiteTest):
114
115  def Login(self):
116    self.GoTo("https://en.wikipedia.org/w/index.php?title=Special:UserLogin")
117    self.FillUsernameInto("#wpName1")
118    self.FillPasswordInto("#wpPassword1")
119    self.Submit("#wpPassword1")
120
121
122class Yahoo(WebsiteTest):
123
124  def Login(self):
125    self.GoTo("https://login.yahoo.com")
126    self.FillUsernameInto("#username")
127    self.FillPasswordInto("#passwd")
128    self.Submit("#passwd")
129
130
131class Yandex(WebsiteTest):
132
133  def Login(self):
134    self.GoTo("https://mail.yandex.com")
135    self.FillUsernameInto("#b-mail-domik-username11")
136    self.FillPasswordInto("#b-mail-domik-password11")
137    self.Click(".b-mail-button__button")
138
139
140# Disabled tests.
141
142
143# Bug not reproducible without test.
144class Amazon(WebsiteTest):
145
146  def Login(self):
147    self.GoTo(
148        "https://www.amazon.com/ap/signin?openid.assoc_handle=usflex"
149        "&openid.mode=checkid_setup&openid.ns=http%3A%2F%2Fspecs.openid.net"
150        "%2Fauth%2F2.0")
151    self.FillUsernameInto("[name='email']")
152    self.FillPasswordInto("[name='password']")
153    self.Click("#signInSubmit-input")
154
155
156# Password not saved.
157class Ask(WebsiteTest):
158
159  def Login(self):
160    self.GoTo("http://www.ask.com/answers/browse?qsrc=321&q=&o=0&l=dir#")
161    while not self.IsDisplayed("[name='username']"):
162      self.Click("#a16CnbSignInText")
163      self.Wait(1)
164    self.FillUsernameInto("[name='username']")
165    self.FillPasswordInto("[name='password']")
166    self.Click(".signin_show.signin_submit")
167
168
169# Password not saved.
170class Baidu(WebsiteTest):
171
172  def Login(self):
173    self.GoTo("https://passport.baidu.com")
174    self.FillUsernameInto("[name='userName']")
175    self.FillPasswordInto("[name='password']")
176    self.Submit("[name='password']")
177
178
179# http://crbug.com/368690
180class Cnn(WebsiteTest):
181
182  def Login(self):
183    self.GoTo("http://www.cnn.com")
184    self.Wait(5)
185    while not self.IsDisplayed(".cnnOvrlyBtn.cnnBtnLogIn"):
186      self.ClickIfClickable("#hdr-auth .no-border.no-pad-right a")
187      self.Wait(1)
188
189    self.Click(".cnnOvrlyBtn.cnnBtnLogIn")
190    self.FillUsernameInto("#cnnOverlayEmail1l")
191    self.FillPasswordInto("#cnnOverlayPwd")
192    self.Click(".cnnOvrlyBtn.cnnBtnLogIn")
193    self.Click(".cnnOvrlyBtn.cnnBtnLogIn")
194    self.Wait(5)
195
196
197# http://crbug.com/368690
198class Ebay(WebsiteTest):
199
200  def Login(self):
201    self.GoTo("https://signin.ebay.com/")
202    self.FillUsernameInto("[name='userid']")
203    self.FillPasswordInto("[name='pass']")
204    self.Submit("[name='pass']")
205
206
207# Iframe, password saved but not autofileld.
208class Espn(WebsiteTest):
209
210  def Login(self):
211    self.GoTo("http://espn.go.com/")
212    while not self.IsDisplayed("#cboxLoadedContent iframe"):
213      self.Click("#signin .cbOverlay")
214      self.Wait(1)
215    frame = self.driver.find_element_by_css_selector("#cboxLoadedContent "
216                                                     "iframe")
217    self.driver.switch_to_frame(frame)
218    self.FillUsernameInto("#username")
219    self.FillPasswordInto("#password")
220    while self.IsDisplayed("#password"):
221      self.ClickIfClickable("#submitBtn")
222      self.Wait(1)
223
224
225# http://crbug.com/367768
226class Live(WebsiteTest):
227
228  def Login(self):
229    self.GoTo("https://login.live.com")
230    self.FillUsernameInto("[name='login']")
231    self.FillPasswordInto("[name='passwd']")
232    self.Submit("[name='passwd']")
233
234
235# http://crbug.com/368690
236class One63(WebsiteTest):
237
238  def Login(self):
239    self.GoTo("http://www.163.com")
240    self.HoverOver("#js_N_navHighlight")
241    self.FillUsernameInto("#js_loginframe_username")
242    self.FillPasswordInto(".ntes-loginframe-label-ipt[type='password']")
243    self.Click(".ntes-loginframe-btn")
244
245
246# http://crbug.com/368690
247class Vube(WebsiteTest):
248
249  def Login(self):
250    self.GoTo("https://vube.com")
251    self.Click("[vube-login='']")
252    self.FillUsernameInto("[ng-model='login.user']")
253    self.FillPasswordInto("[ng-model='login.pass']")
254    while (self.IsDisplayed("[ng-model='login.pass']")
255           and not self.IsDisplayed(".prompt.alert")):
256      self.ClickIfClickable("[ng-click='login()']")
257      self.Wait(1)
258
259
260def Tests(environment):
261
262
263  # Working tests.
264
265
266  environment.AddWebsiteTest(Facebook("facebook"))
267
268  environment.AddWebsiteTest(Google("google"))
269
270  environment.AddWebsiteTest(Linkedin("linkedin"))
271
272  environment.AddWebsiteTest(Mailru("mailru"))
273
274  environment.AddWebsiteTest(Nytimes("nytimes"))
275
276  environment.AddWebsiteTest(Pinterest("pinterest"))
277
278  environment.AddWebsiteTest(Reddit("reddit", username_not_auto=True))
279
280  environment.AddWebsiteTest(Tumblr("tumblr", username_not_auto=True))
281
282  environment.AddWebsiteTest(Twitter("twitter"))
283
284  environment.AddWebsiteTest(Wikipedia("wikipedia", username_not_auto=True))
285
286  environment.AddWebsiteTest(Yahoo("yahoo", username_not_auto=True))
287
288  environment.AddWebsiteTest(Yandex("yandex"))
289
290  # Disabled tests.
291
292
293  # Bug not reproducible without test.
294  environment.AddWebsiteTest(Amazon("amazon"), disabled=True)
295
296  # Password not saved.
297  environment.AddWebsiteTest(Ask("ask"), disabled=True)
298
299  # Password not saved.
300  environment.AddWebsiteTest(Baidu("baidu"), disabled=True)
301
302  # http://crbug.com/368690
303  environment.AddWebsiteTest(Cnn("cnn"), disabled=True)
304
305  # http://crbug.com/368690
306  environment.AddWebsiteTest(Ebay("ebay"), disabled=True)
307
308  # Iframe, password saved but not autofileld.
309  environment.AddWebsiteTest(Espn("espn"), disabled=True)
310
311  # http://crbug.com/367768
312  environment.AddWebsiteTest(Live("live", username_not_auto=True),
313                             disabled=True)
314
315  # http://crbug.com/368690
316  environment.AddWebsiteTest(One63("163"), disabled=True)
317
318  # http://crbug.com/368690
319  environment.AddWebsiteTest(Vube("vube"), disabled=True)
320
321def saveResults(environment_tests_results, environment_save_path):
322  """Save the test results in an xml file.
323
324  Args:
325    environment_tests_results: A list of the TestResults that are going to be
326        saved.
327    environment_save_path: The file where the results are going to be saved.
328        If it's None, the results are not going to be stored.
329  Raises:
330    Exception: An exception is raised if the file is not found.
331  """
332  if environment_save_path:
333    xml = "<result>"
334    for test_result in environment_tests_results:
335      xml += ("<test name='%s' successful='%s' type='%s'>%s</test>"
336          % (test_result.name, str(test_result.successful),
337          test_result.test_type, test_result.message))
338    xml += "</result>"
339    with open(environment_save_path, "w") as save_file:
340      save_file.write(xml)
341
342def RunTests(chrome_path, chromedriver_path, profile_path,
343             environment_passwords_path, enable_automatic_password_saving,
344             environment_numeric_level, log_to_console, environment_log_file,
345             environment_tested_websites, tests=None):
346
347  """Runs the the tests
348
349  Args:
350    chrome_path: The chrome binary file.
351    chromedriver_path: The chromedriver binary file.
352    profile_path: The chrome testing profile folder.
353    environment_passwords_path: The usernames and passwords file.
354    enable_automatic_password_saving: If True, the passwords are going to be
355        saved without showing the prompt.
356    environment_numeric_level: The log verbosity.
357    log_to_console: If True, the debug logs will be shown on the console.
358    environment_log_file: The file where to store the log. If it's empty, the
359        log is not stored.
360    environment_tested_websites: One of the TypeOfTestedWebsites values,
361        indicating which group of tests to run.
362    tests: Specifies which tests to run. Ignored unless
363       |environment_tested_websites| is equal to LIST_OF_TESTS.
364
365  Returns:
366    The results of tests as list of TestResults.
367  Raises:
368    Exception: An exception is raised if one of the tests fails.
369  """
370
371  environment = Environment(chrome_path, chromedriver_path, profile_path,
372                            environment_passwords_path,
373                            enable_automatic_password_saving,
374                            environment_numeric_level,
375                            log_to_console,
376                            environment_log_file)
377
378  # Test which care about the save-password prompt need the prompt
379  # to be shown. Automatic password saving results in no prompt.
380  run_prompt_tests = not enable_automatic_password_saving
381
382  Tests(environment)
383
384  if environment_tested_websites == TypeOfTestedWebsites.ALL_TESTS:
385    environment.AllTests(run_prompt_tests)
386  elif environment_tested_websites == TypeOfTestedWebsites.DISABLED_TESTS:
387    environment.DisabledTests(run_prompt_tests)
388  elif environment_tested_websites == TypeOfTestedWebsites.LIST_OF_TESTS:
389    environment.Test(tests, run_prompt_tests)
390  elif environment_tested_websites == TypeOfTestedWebsites.ENABLED_TESTS:
391    environment.WorkingTests(run_prompt_tests)
392  else:
393    raise Exception("Error: |environment_tested_websites| has to be one of the"
394        "TypeOfTestedWebsites values")
395
396
397  environment.Quit()
398  return environment.tests_results
399
400# Tests setup.
401if __name__ == "__main__":
402  parser = argparse.ArgumentParser(
403      description="Password Manager automated tests help.")
404
405  parser.add_argument(
406      "--chrome-path", action="store", dest="chrome_path",
407      help="Set the chrome path (required).", nargs=1, required=True)
408  parser.add_argument(
409      "--chromedriver-path", action="store", dest="chromedriver_path",
410      help="Set the chromedriver path (required).", nargs=1, required=True)
411  parser.add_argument(
412      "--profile-path", action="store", dest="profile_path",
413      help="Set the profile path (required). You just need to choose a "
414           "temporary empty folder. If the folder is not empty all its content "
415           "is going to be removed.",
416      nargs=1, required=True)
417
418  parser.add_argument(
419      "--passwords-path", action="store", dest="passwords_path",
420      help="Set the usernames/passwords path (required).", nargs=1,
421      required=True)
422  parser.add_argument("--all", action="store_true", dest="all",
423                      help="Run all tests.")
424  parser.add_argument("--disabled", action="store_true", dest="disabled",
425                      help="Run only disabled tests.")
426  parser.add_argument("--log", action="store", nargs=1, dest="log_level",
427                      help="Set log level.")
428  parser.add_argument("--log-screen", action="store_true", dest="log_screen",
429                      help="Show log on the screen.")
430  parser.add_argument("--log-file", action="store", dest="log_file",
431                      help="Write the log in a file.", nargs=1)
432  parser.add_argument("--save-path", action="store", nargs=1, dest="save_path",
433                      help="Write the results in a file.")
434  parser.add_argument("tests", help="Tests to be run.",  nargs="*")
435
436  args = parser.parse_args()
437
438  passwords_path = args.passwords_path[0]
439
440  if args.all:
441    tested_websites = TypeOfTestedWebsites.ALL_TESTS
442  elif args.disabled:
443    tested_websites = TypeOfTestedWebsites.DISABLED_TESTS
444  elif args.tests:
445    tested_websites = TypeOfTestedWebsites.LIST_OF_TESTS
446  else:
447    tested_websites = TypeOfTestedWebsites.ENABLED_TESTS
448
449  numeric_level = None
450  if args.log_level:
451    numeric_level = getattr(logging, args.log_level[0].upper(), None)
452    if not isinstance(numeric_level, int):
453      raise ValueError("Invalid log level: %s" % args.log_level[0])
454
455  log_file = None
456  if args.log_file:
457    log_file = args.log_file[0]
458
459  save_path = None
460  if args.save_path:
461    save_path = args.save_path[0]
462
463  # Run the test without enable-automatic-password-saving to check whether or
464  # not the prompt is shown in the way we expected.
465  tests_results = RunTests(args.chrome_path[0],
466                           args.chromedriver_path[0],
467                           args.profile_path[0],
468                           passwords_path,
469                           False,
470                           numeric_level,
471                           args.log_screen,
472                           log_file,
473                           tested_websites,
474                           args.tests)
475
476  # Run the test with enable-automatic-password-saving to check whether or not
477  # the passwords is stored in the the way we expected.
478  tests_results += RunTests(args.chrome_path[0],
479                            args.chromedriver_path[0],
480                            args.profile_path[0],
481                            passwords_path,
482                            True,
483                            numeric_level,
484                            args.log_screen,
485                            log_file,
486                            tested_websites,
487                            args.tests)
488
489  saveResults(tests_results, save_path)
490