From dab69c8fc34fcd6143e4ab82193b207e38c1207c Mon Sep 17 00:00:00 2001 From: sullivan Date: Tue, 2 Sep 2014 10:08:47 -0700 Subject: [PATCH] Revert of Revert of Add support for codepen to form_based_credentials_background (patchset #9 of https://codereview.chromium.org/485743002/) Reason for revert: I believe the flakiness may have been fixed by https://codereview.chromium.org/520683002 BUG= Review URL: https://codereview.chromium.org/531733004 Cr-Commit-Position: refs/heads/master@{#292950} --- .../perf/page_sets/data/credentials.json.sha1 | 2 +- .../backends/codepen_credentials_backend.py | 41 +++++++++++ .../codepen_credentials_backend_unittest.py | 18 +++++ .../backends/facebook_credentials_backend.py | 10 ++- .../facebook_credentials_backend_unittest.py | 7 +- .../form_based_credentials_backend.py | 73 ++++++++++--------- ...based_credentials_backend_unittest_base.py | 17 +++-- .../backends/google_credentials_backend.py | 8 +- .../google_credentials_backend_unittest.py | 3 +- .../telemetry/core/browser_credentials.py | 6 +- .../core/browser_credentials_unittest.py | 2 +- 11 files changed, 133 insertions(+), 54 deletions(-) create mode 100644 tools/telemetry/telemetry/core/backends/codepen_credentials_backend.py create mode 100644 tools/telemetry/telemetry/core/backends/codepen_credentials_backend_unittest.py diff --git a/tools/perf/page_sets/data/credentials.json.sha1 b/tools/perf/page_sets/data/credentials.json.sha1 index 76f00e0f76dbb..dd0deb54ecfc8 100644 --- a/tools/perf/page_sets/data/credentials.json.sha1 +++ b/tools/perf/page_sets/data/credentials.json.sha1 @@ -1 +1 @@ -11752daf3b27c9ced2d530b5241cab7e5b109dd9 \ No newline at end of file +3a0f9c995a41e058857af7d9c2e265b046ba96e6 \ No newline at end of file diff --git a/tools/telemetry/telemetry/core/backends/codepen_credentials_backend.py b/tools/telemetry/telemetry/core/backends/codepen_credentials_backend.py new file mode 100644 index 0000000000000..a12ae4b0c179c --- /dev/null +++ b/tools/telemetry/telemetry/core/backends/codepen_credentials_backend.py @@ -0,0 +1,41 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +from telemetry.core.backends import form_based_credentials_backend + + +class CodePenCredentialsBackend( + form_based_credentials_backend.FormBasedCredentialsBackend): + + @property + def logged_in_javascript(self): + """Evaluates to true iff already logged in.""" + return 'document.querySelector(".login-area") === null' + + @property + def credentials_type(self): + return 'codepen' + + @property + def url(self): + return 'https://codepen.io/login' + + @property + def login_form_id(self): + return 'login-login-form' + + @property + def login_button_javascript(self): + return """ + LoginSettings.timeOnPageStartTime = 0; + document.getElementById("log-in-button").click(); + """ + + @property + def login_input_id(self): + return 'login-email-field' + + @property + def password_input_id(self): + return 'login-password-field_' diff --git a/tools/telemetry/telemetry/core/backends/codepen_credentials_backend_unittest.py b/tools/telemetry/telemetry/core/backends/codepen_credentials_backend_unittest.py new file mode 100644 index 0000000000000..43ecb5e986d35 --- /dev/null +++ b/tools/telemetry/telemetry/core/backends/codepen_credentials_backend_unittest.py @@ -0,0 +1,18 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +from telemetry.core.backends import form_based_credentials_backend_unittest_base +from telemetry.core.backends import codepen_credentials_backend + + +class TestCodePenCredentialsBackend( + form_based_credentials_backend_unittest_base. + FormBasedCredentialsBackendUnitTestBase): + def setUp(self): + self._credentials_type = 'codepen' + + def testLoginUsingMock(self): + backend = codepen_credentials_backend.CodePenCredentialsBackend() + self._LoginUsingMock(backend, backend.url, backend.login_input_id, + backend.password_input_id, backend.login_form_id, + backend.logged_in_javascript) diff --git a/tools/telemetry/telemetry/core/backends/facebook_credentials_backend.py b/tools/telemetry/telemetry/core/backends/facebook_credentials_backend.py index 2b66f9dc1063f..b391e9f91992b 100644 --- a/tools/telemetry/telemetry/core/backends/facebook_credentials_backend.py +++ b/tools/telemetry/telemetry/core/backends/facebook_credentials_backend.py @@ -7,10 +7,12 @@ class FacebookCredentialsBackend( form_based_credentials_backend.FormBasedCredentialsBackend): - def IsAlreadyLoggedIn(self, tab): - return tab.EvaluateJavaScript( - 'document.getElementById("fbNotificationsList")!== null || ' - 'document.getElementById("m_home_notice")!== null') + + @property + def logged_in_javascript(self): + """Evaluates to true iff already logged in.""" + return ('document.getElementById("fbNotificationsList")!== null || ' + 'document.getElementById("m_home_notice")!== null') @property def credentials_type(self): diff --git a/tools/telemetry/telemetry/core/backends/facebook_credentials_backend_unittest.py b/tools/telemetry/telemetry/core/backends/facebook_credentials_backend_unittest.py index 2f6b22f9a94a9..c182ce7c24b9e 100644 --- a/tools/telemetry/telemetry/core/backends/facebook_credentials_backend_unittest.py +++ b/tools/telemetry/telemetry/core/backends/facebook_credentials_backend_unittest.py @@ -13,6 +13,7 @@ def setUp(self): self._credentials_type = 'facebook' def testLoginUsingMock(self): - self._LoginUsingMock( - facebook_credentials_backend.FacebookCredentialsBackend(), - 'http://www.facebook.com/', 'email', 'pass') + backend = facebook_credentials_backend.FacebookCredentialsBackend() + self._LoginUsingMock(backend, backend.url, backend.login_input_id, + backend.password_input_id, backend.login_form_id, + backend.logged_in_javascript) diff --git a/tools/telemetry/telemetry/core/backends/form_based_credentials_backend.py b/tools/telemetry/telemetry/core/backends/form_based_credentials_backend.py index bb32f32251a34..93c2cc26e5872 100644 --- a/tools/telemetry/telemetry/core/backends/form_based_credentials_backend.py +++ b/tools/telemetry/telemetry/core/backends/form_based_credentials_backend.py @@ -6,32 +6,12 @@ from telemetry.core import util -def _WaitForLoginFormToLoad(backend, login_form_id, tab): - def IsFormLoadedOrAlreadyLoggedIn(): - return (tab.EvaluateJavaScript( - 'document.querySelector("#%s")!== null' % login_form_id) or - backend.IsAlreadyLoggedIn(tab)) - - # Wait until the form is submitted and the page completes loading. - util.WaitFor(IsFormLoadedOrAlreadyLoggedIn, 60) - -def _SubmitFormAndWait(form_id, tab): - tab.ExecuteJavaScript( - 'document.getElementById("%s").submit();' % form_id) - - def FinishedLoading(): - return not tab.EvaluateJavaScript( - 'document.querySelector("#%s") !== null' % form_id) - - # Wait until the form is submitted and the page completes loading. - util.WaitFor(FinishedLoading, 60) - class FormBasedCredentialsBackend(object): def __init__(self): self._logged_in = False def IsAlreadyLoggedIn(self, tab): - raise NotImplementedError() + return tab.EvaluateJavaScript(self.logged_in_javascript) @property def credentials_type(self): @@ -45,6 +25,11 @@ def url(self): def login_form_id(self): raise NotImplementedError() + @property + def login_button_javascript(self): + """Some sites have custom JS to log in.""" + return None + @property def login_input_id(self): raise NotImplementedError() @@ -53,6 +38,11 @@ def login_input_id(self): def password_input_id(self): raise NotImplementedError() + @property + def logged_in_javascript(self): + """Evaluates to true iff already logged in.""" + raise NotImplementedError() + def IsLoggedIn(self): return self._logged_in @@ -62,7 +52,31 @@ def _ResetLoggedInState(self): """ self._logged_in = False - def LoginNeeded(self, tab, config): + def _WaitForLoginState(self, action_runner): + """Waits until it can detect either the login form, or already logged in.""" + condition = '(document.querySelector("#%s") !== null) || (%s)' % ( + self.login_form_id, self.logged_in_javascript) + action_runner.WaitForJavaScriptCondition(condition, 60) + + def _SubmitLoginFormAndWait(self, action_runner, tab, username, password): + """Submits the login form and waits for the navigation.""" + tab.WaitForDocumentReadyStateToBeInteractiveOrBetter() + email_id = 'document.querySelector("#%s #%s").value = "%s"; ' % ( + self.login_form_id, self.login_input_id, username) + password = 'document.querySelector("#%s #%s").value = "%s"; ' % ( + self.login_form_id, self.password_input_id, password) + tab.ExecuteJavaScript(email_id) + tab.ExecuteJavaScript(password) + if self.login_button_javascript: + tab.ExecuteJavaScript(self.login_button_javascript) + else: + tab.ExecuteJavaScript( + 'document.getElementById("%s").submit();' % self.login_form_id) + # Wait for the form element to disappear as confirmation of the navigation. + action_runner.WaitForNavigate() + + + def LoginNeeded(self, tab, action_runner, config): """Logs in to a test account. Raises: @@ -86,23 +100,14 @@ def LoginNeeded(self, tab, config): try: logging.info('Loading %s...', url) tab.Navigate(url) - _WaitForLoginFormToLoad(self, self.login_form_id, tab) + self._WaitForLoginState(action_runner) if self.IsAlreadyLoggedIn(tab): self._logged_in = True return True - tab.WaitForDocumentReadyStateToBeInteractiveOrBetter() - logging.info('Loaded page: %s', url) - - email_id = 'document.querySelector("#%s").%s.value = "%s"; ' % ( - self.login_form_id, self.login_input_id, config['username']) - password = 'document.querySelector("#%s").%s.value = "%s"; ' % ( - self.login_form_id, self.password_input_id, config['password']) - tab.ExecuteJavaScript(email_id) - tab.ExecuteJavaScript(password) - - _SubmitFormAndWait(self.login_form_id, tab) + self._SubmitLoginFormAndWait( + action_runner, tab, config['username'], config['password']) self._logged_in = True return True diff --git a/tools/telemetry/telemetry/core/backends/form_based_credentials_backend_unittest_base.py b/tools/telemetry/telemetry/core/backends/form_based_credentials_backend_unittest_base.py index 3ce0c864aa0cb..ef292d6853bf1 100644 --- a/tools/telemetry/telemetry/core/backends/form_based_credentials_backend_unittest_base.py +++ b/tools/telemetry/telemetry/core/backends/form_based_credentials_backend_unittest_base.py @@ -95,18 +95,23 @@ def testLoginUsingMock(self): raise NotImplementedError() def _LoginUsingMock(self, backend, login_page_url, email_element_id, - password_element_id): # pylint: disable=R0201 + password_element_id, form_element_id, + already_logged_in_js): # pylint: disable=R0201 tab = simple_mock.MockObject() + ar = simple_mock.MockObject() config = {'username': 'blah', 'password': 'blargh'} tab.ExpectCall('Navigate', login_page_url) - tab.ExpectCall('EvaluateJavaScript', _).WillReturn(False) - tab.ExpectCall('EvaluateJavaScript', _).WillReturn(True) - tab.ExpectCall('EvaluateJavaScript', _).WillReturn(False) + tab.ExpectCall('EvaluateJavaScript', already_logged_in_js).WillReturn(False) tab.ExpectCall('WaitForDocumentReadyStateToBeInteractiveOrBetter') + ar.ExpectCall('WaitForJavaScriptCondition', + '(document.querySelector("#%s") !== null) || (%s)' % ( + form_element_id, already_logged_in_js), 60) + ar.ExpectCall('WaitForNavigate') + def VerifyEmail(js): assert email_element_id in js assert 'blah' in js @@ -118,10 +123,10 @@ def VerifyPw(js): tab.ExpectCall('ExecuteJavaScript', _).WhenCalled(VerifyPw) def VerifySubmit(js): - assert '.submit' in js + assert '.submit' in js or '.click' in js tab.ExpectCall('ExecuteJavaScript', _).WhenCalled(VerifySubmit) # Checking for form still up. tab.ExpectCall('EvaluateJavaScript', _).WillReturn(False) - backend.LoginNeeded(tab, config) + backend.LoginNeeded(tab, ar, config) diff --git a/tools/telemetry/telemetry/core/backends/google_credentials_backend.py b/tools/telemetry/telemetry/core/backends/google_credentials_backend.py index 34d2d5c747887..2464f4d32f951 100644 --- a/tools/telemetry/telemetry/core/backends/google_credentials_backend.py +++ b/tools/telemetry/telemetry/core/backends/google_credentials_backend.py @@ -7,9 +7,11 @@ class GoogleCredentialsBackend( form_based_credentials_backend.FormBasedCredentialsBackend): - def IsAlreadyLoggedIn(self, tab): - return tab.EvaluateJavaScript( - 'document.getElementById("gb")!== null') + + @property + def logged_in_javascript(self): + """Evaluates to true iff already logged in.""" + return 'document.getElementById("gb")!== null' @property def credentials_type(self): diff --git a/tools/telemetry/telemetry/core/backends/google_credentials_backend_unittest.py b/tools/telemetry/telemetry/core/backends/google_credentials_backend_unittest.py index 4298edf976bc0..e02ecb24bb888 100644 --- a/tools/telemetry/telemetry/core/backends/google_credentials_backend_unittest.py +++ b/tools/telemetry/telemetry/core/backends/google_credentials_backend_unittest.py @@ -14,4 +14,5 @@ def setUp(self): def testLoginUsingMock(self): backend = google_credentials_backend.GoogleCredentialsBackend() self._LoginUsingMock(backend, backend.url, backend.login_input_id, - backend.password_input_id) + backend.password_input_id, backend.login_form_id, + backend.logged_in_javascript) diff --git a/tools/telemetry/telemetry/core/browser_credentials.py b/tools/telemetry/telemetry/core/browser_credentials.py index d78ced98568ed..b44e47b07a115 100644 --- a/tools/telemetry/telemetry/core/browser_credentials.py +++ b/tools/telemetry/telemetry/core/browser_credentials.py @@ -7,8 +7,10 @@ import os from telemetry.core import util +from telemetry.core.backends import codepen_credentials_backend from telemetry.core.backends import facebook_credentials_backend from telemetry.core.backends import google_credentials_backend +from telemetry.page.actions import action_runner from telemetry.unittest import options_for_unittests @@ -24,6 +26,7 @@ def __init__(self, backends = None): if backends is None: backends = [ + codepen_credentials_backend.CodePenCredentialsBackend(), facebook_credentials_backend.FacebookCredentialsBackend(), google_credentials_backend.GoogleCredentialsBackend()] @@ -55,8 +58,9 @@ def LoginNeeded(self, tab, credentials_type): 'Unrecognized credentials type: %s', credentials_type) if credentials_type not in self._credentials: return False + runner = action_runner.ActionRunner(tab) return self._backends[credentials_type].LoginNeeded( - tab, self._credentials[credentials_type]) + tab, runner, self._credentials[credentials_type]) def LoginNoLongerNeeded(self, tab, credentials_type): assert credentials_type in self._backends diff --git a/tools/telemetry/telemetry/core/browser_credentials_unittest.py b/tools/telemetry/telemetry/core/browser_credentials_unittest.py index 7093dcba47964..fd79ac87972b6 100644 --- a/tools/telemetry/telemetry/core/browser_credentials_unittest.py +++ b/tools/telemetry/telemetry/core/browser_credentials_unittest.py @@ -23,7 +23,7 @@ def __init__(self, credentials_type): self.login_no_longer_needed_called = None self.credentials_type = credentials_type - def LoginNeeded(self, config, tab): + def LoginNeeded(self, config, _, tab): self.login_needed_called = (config, tab) return True