| Index: chrome/test/pyautolib/chromotinglib.py
|
| ===================================================================
|
| --- chrome/test/pyautolib/chromotinglib.py (revision 0)
|
| +++ chrome/test/pyautolib/chromotinglib.py (revision 0)
|
| @@ -0,0 +1,534 @@
|
| +# Copyright (c) 2012 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.
|
| +
|
| +"""Includes different methods to drive chromoting UI."""
|
| +
|
| +import os
|
| +import subprocess
|
| +import sys
|
| +import time
|
| +
|
| +from pyauto_errors import JSONInterfaceError
|
| +
|
| +
|
| +class ChromotingMixIn(object):
|
| + """MixIn for PyUITest that adds Chromoting-specific methods.
|
| +
|
| + Prepend it as a base class of a test to enable Chromoting functionality.
|
| + This is a separate class from PyUITest to avoid namespace collisions.
|
| +
|
| + Example usage:
|
| + class ChromotingExample(chromoting.ChromotingMixIn, pyauto.PyUITest):
|
| + def testShare(self):
|
| + app = self.InstallApp(self.GetWebappPath())
|
| + self.LaunchApp(app)
|
| + self.Authenticate()
|
| + self.assertTrue(self.Share())
|
| + """
|
| +
|
| + def _ExecuteJavascript(self, command, tab_index, windex):
|
| + """Helper that returns immediately after running a Javascript command.
|
| + """
|
| + try:
|
| + self.ExecuteJavascript(
|
| + '%s; window.domAutomationController.send("done");' % command,
|
| + tab_index, windex)
|
| + return True
|
| + except JSONInterfaceError:
|
| + print '_ExecuteJavascript threw JSONInterfaceError'
|
| + return False
|
| +
|
| + def _WaitForJavascriptCondition(self, condition, tab_index, windex):
|
| + """Waits until the Javascript condition is true.
|
| +
|
| + This is different from a naive self.WaitUntil(lambda: self.GetDOMValue())
|
| + because it uses Javascript to check the condition instead of Python.
|
| +
|
| + Returns: True if condition is satisfied or otherwise False.
|
| + """
|
| + try:
|
| + return self.WaitUntil(lambda: self.GetDOMValue(
|
| + '(%s) ? "1" : ""' % condition, tab_index, windex))
|
| + except JSONInterfaceError:
|
| + print '_WaitForJavascriptCondition threw JSONInterfaceError'
|
| + return False
|
| +
|
| + def _ExecuteAndWaitForMode(self, command, mode, tab_index, windex):
|
| + """ Executes JavaScript and wait for remoting app mode equal to
|
| + the given mode.
|
| +
|
| + Returns: True if condition is satisfied or otherwise False.
|
| + """
|
| + if not self._ExecuteJavascript(command, tab_index, windex):
|
| + return False
|
| + return self._WaitForJavascriptCondition(
|
| + 'remoting.currentMode == remoting.AppMode.%s' % mode,
|
| + tab_index, windex)
|
| +
|
| + def _ExecuteAndWaitForMajorMode(self, command, mode, tab_index, windex):
|
| + """ Executes JavaScript and wait for remoting app major mode equal to
|
| + the given mode.
|
| +
|
| + Returns: True if condition is satisfied or otherwise False.
|
| + """
|
| + if not self._ExecuteJavascript(command, tab_index, windex):
|
| + return False
|
| + return self._WaitForJavascriptCondition(
|
| + 'remoting.getMajorMode() == remoting.AppMode.%s' % mode,
|
| + tab_index, windex)
|
| +
|
| + def GetWebappPath(self):
|
| + """Returns the path to the webapp.
|
| +
|
| + Expects the webapp to be in the same place as the pyautolib binaries.
|
| + """
|
| + return os.path.join(self.BrowserPath(), 'remoting', 'remoting.webapp')
|
| +
|
| + def _GetHelperRunner(self):
|
| + """Returns the python binary name that runs chromoting_helper.py."""
|
| + if sys.platform.startswith('win'):
|
| + return 'python'
|
| + else:
|
| + return 'suid-python'
|
| +
|
| + def _GetHelper(self):
|
| + """Get chromoting_helper.py."""
|
| + return os.path.join('chrome', 'test', 'pyautolib', 'chromoting_helper.py')
|
| +
|
| + def InstallHostDaemon(self):
|
| + """Installs the host daemon."""
|
| + subprocess.call([self._GetHelperRunner(), self._GetHelper(),
|
| + 'install', self.BrowserPath()])
|
| +
|
| + def UninstallHostDaemon(self):
|
| + """Uninstalls the host daemon."""
|
| + subprocess.call([self._GetHelperRunner(), self._GetHelper(),
|
| + 'uninstall', self.BrowserPath()])
|
| +
|
| + def ContinueAuth(self, tab_index=1, windex=0):
|
| + """Starts authentication."""
|
| + self.assertTrue(
|
| + self._WaitForJavascriptCondition('window.remoting && remoting.oauth2',
|
| + tab_index, windex),
|
| + msg='Timed out while waiting for remoting app to finish loading.')
|
| + self._ExecuteJavascript('remoting.oauth2.doAuthRedirect();',
|
| + tab_index, windex)
|
| +
|
| + def SignIn(self, email=None, password=None, otp=None,
|
| + tab_index=1, windex=0):
|
| + """Logs a user in.
|
| +
|
| + PyAuto tests start with a clean profile, so Chromoting tests should call
|
| + this for every run after launching the app. If email or password is
|
| + omitted, the user can type it into the browser window manually.
|
| + """
|
| + self.assertTrue(
|
| + self._WaitForJavascriptCondition('document.getElementById("signIn")',
|
| + tab_index, windex),
|
| + msg='Unable to redirect for authentication.')
|
| +
|
| + if email:
|
| + self._ExecuteJavascript('document.getElementById("Email").value = "%s";'
|
| + 'document.getElementById("Passwd").focus();'
|
| + % email, tab_index, windex)
|
| +
|
| + if password:
|
| + self._ExecuteJavascript('document.getElementById("Passwd").value = "%s";'
|
| + 'document.getElementById("signIn").click();'
|
| + % password, tab_index, windex)
|
| +
|
| + if otp:
|
| + self.assertTrue(
|
| + self._WaitForJavascriptCondition(
|
| + 'document.getElementById("smsVerifyPin")',
|
| + tab_index, windex),
|
| + msg='Invalid username or password.')
|
| + self._ExecuteJavascript(
|
| + 'document.getElementById("smsUserPin").value = "%s";'
|
| + 'document.getElementById("smsVerifyPin").click();' % otp,
|
| + tab_index, windex)
|
| +
|
| + # If the account adder screen appears, then skip it.
|
| + self.assertTrue(
|
| + self._WaitForJavascriptCondition(
|
| + 'document.getElementById("skip") || '
|
| + 'document.getElementById("submit_approve_access")',
|
| + tab_index, windex),
|
| + msg='No "skip adding account" or "approve access" link.')
|
| + self._ExecuteJavascript(
|
| + 'if (document.getElementById("skip")) '
|
| + '{ document.getElementById("skip").click(); }',
|
| + tab_index, windex)
|
| +
|
| + def AllowAccess(self, tab_index=1, windex=0):
|
| + """Allows access to chromoting webapp."""
|
| + # Approve access.
|
| + self.assertTrue(
|
| + self._WaitForJavascriptCondition(
|
| + 'document.getElementById("submit_approve_access")',
|
| + tab_index, windex),
|
| + msg='Did not go to permission page.')
|
| + self._ExecuteJavascript(
|
| + 'document.getElementById("submit_approve_access").click();',
|
| + tab_index, windex)
|
| +
|
| + # Wait for some things to be ready.
|
| + self.assertTrue(
|
| + self._WaitForJavascriptCondition(
|
| + 'window.remoting && remoting.oauth2 && ' \
|
| + 'remoting.oauth2.isAuthenticated()',
|
| + tab_index, windex),
|
| + msg='OAuth2 authentication failed.')
|
| + self.assertTrue(
|
| + self._WaitForJavascriptCondition(
|
| + 'window.localStorage.getItem("remoting-email")',
|
| + tab_index, windex),
|
| + msg='Chromoting app did not reload after authentication.')
|
| +
|
| + def DenyAccess(self, tab_index=1, windex=0):
|
| + """Deny and then allow access to chromoting webapp."""
|
| + self.assertTrue(
|
| + self._WaitForJavascriptCondition(
|
| + 'document.getElementById("submit_deny_access")',
|
| + tab_index, windex),
|
| + msg='Did not go to permission page.')
|
| + self._ExecuteJavascript(
|
| + 'document.getElementById("submit_deny_access").click();',
|
| + tab_index, windex)
|
| +
|
| + def SignOut(self, tab_index=1, windex=0):
|
| + """Signs out from chromoting and signs back in."""
|
| + self._ExecuteAndWaitForMode(
|
| + 'document.getElementById("sign-out").click();',
|
| + 'UNAUTHENTICATED', tab_index, windex)
|
| +
|
| + def Authenticate(self, tab_index=1, windex=0):
|
| + """Finishes authentication flow for user."""
|
| + self.ContinueAuth(tab_index, windex)
|
| + account = self.GetPrivateInfo()['test_chromoting_account']
|
| + self.host.SignIn(account['username'], account['password'], None,
|
| + tab_index, windex)
|
| + self.host.AllowAccess(tab_index, windex)
|
| +
|
| + def StartMe2Me(self, tab_index=1, windex=0):
|
| + """Starts Me2Me. """
|
| + self._ExecuteJavascript(
|
| + 'document.getElementById("get-started-me2me").click();',
|
| + tab_index, windex)
|
| +
|
| + def Share(self, tab_index=1, windex=0):
|
| + """Generates an access code and waits for incoming connections.
|
| +
|
| + Returns:
|
| + The access code on success; None otherwise.
|
| + """
|
| + self._ExecuteAndWaitForMode(
|
| + 'remoting.tryShare();',
|
| + 'HOST_WAITING_FOR_CONNECTION', tab_index, windex)
|
| + return self.GetDOMValue(
|
| + 'document.getElementById("access-code-display").innerText',
|
| + tab_index, windex)
|
| +
|
| + def CancelShare(self, tab_index=1, windex=0):
|
| + """Stops sharing the desktop on the host side."""
|
| + self.assertTrue(
|
| + self._ExecuteAndWaitForMode(
|
| + 'remoting.cancelShare();',
|
| + 'HOST_SHARE_FINISHED', tab_index, windex),
|
| + msg='Stopping sharing from the host side failed')
|
| +
|
| + def EnableConnectionsInstalled(self, pin_exercise=False,
|
| + tab_index=1, windex=0):
|
| + """Enables the remote connections on the host side."""
|
| + subprocess.call([self._GetHelperRunner(), self._GetHelper(), 'enable'])
|
| +
|
| + self.assertTrue(
|
| + self._ExecuteAndWaitForMode(
|
| + 'document.getElementById("start-daemon").click();',
|
| + 'HOST_SETUP_ASK_PIN', tab_index, windex),
|
| + msg='Cannot start host setup')
|
| + self.assertTrue(
|
| + self._WaitForJavascriptCondition(
|
| + 'document.getElementById("ask-pin-form").hidden == false',
|
| + tab_index, windex),
|
| + msg='No ask pin dialog')
|
| +
|
| + if pin_exercise:
|
| + # Cancels the pin prompt
|
| + self._ExecuteJavascript(
|
| + 'document.getElementById("daemon-pin-cancel").click();',
|
| + tab_index, windex)
|
| +
|
| + # Enables again
|
| + self.assertTrue(
|
| + self._ExecuteAndWaitForMode(
|
| + 'document.getElementById("start-daemon").click();',
|
| + 'HOST_SETUP_ASK_PIN', tab_index, windex),
|
| + msg='Cannot start host setup')
|
| +
|
| + # Click ok without typing in pins
|
| + self._ExecuteJavascript(
|
| + 'document.getElementById("daemon-pin-ok").click();',
|
| + tab_index, windex)
|
| + self.assertTrue(
|
| + self._WaitForJavascriptCondition(
|
| + 'document.getElementById("daemon-pin-error-message")',
|
| + tab_index, windex),
|
| + msg='No pin error message')
|
| +
|
| + # Mis-matching pins
|
| + self._ExecuteJavascript(
|
| + 'document.getElementById("daemon-pin-entry").value = "111111";',
|
| + tab_index, windex)
|
| + self._ExecuteJavascript(
|
| + 'document.getElementById("daemon-pin-confirm").value = "123456";',
|
| + tab_index, windex)
|
| + self.assertTrue(
|
| + self._WaitForJavascriptCondition(
|
| + 'document.getElementById("daemon-pin-error-message")',
|
| + tab_index, windex),
|
| + msg='No pin error message')
|
| +
|
| + # Types in correct pins
|
| + self._ExecuteJavascript(
|
| + 'document.getElementById("daemon-pin-entry").value = "111111";',
|
| + tab_index, windex)
|
| + self._ExecuteJavascript(
|
| + 'document.getElementById("daemon-pin-confirm").value = "111111";',
|
| + tab_index, windex)
|
| + self.assertTrue(
|
| + self._ExecuteAndWaitForMode(
|
| + 'document.getElementById("daemon-pin-ok").click();',
|
| + 'HOST_SETUP_PROCESSING', tab_index, windex),
|
| + msg='Host setup was not started')
|
| +
|
| + # Handles preference panes
|
| + self.assertTrue(
|
| + self._WaitForJavascriptCondition(
|
| + 'remoting.currentMode == remoting.AppMode.HOST_SETUP_DONE',
|
| + tab_index, windex),
|
| + msg='Host setup was not done')
|
| +
|
| + # Dismisses the host config done dialog
|
| + self.assertTrue(
|
| + self._WaitForJavascriptCondition(
|
| + 'document.getElementById("host-setup-dialog")'
|
| + '.childNodes[5].hidden == false',
|
| + tab_index, windex),
|
| + msg='No host setup done dialog')
|
| + self.assertTrue(
|
| + self._ExecuteAndWaitForMode(
|
| + 'document.getElementById("host-config-done-dismiss").click();',
|
| + 'HOME', tab_index, windex),
|
| + msg='Failed to dismiss host setup confirmation dialog')
|
| +
|
| + def EnableConnectionsUninstalledAndCancel(self, tab_index=1, windex=0):
|
| + """Enables remote connections while host is not installed yet."""
|
| + self.assertTrue(
|
| + self._ExecuteAndWaitForMode(
|
| + 'document.getElementById("start-daemon").click();',
|
| + 'HOST_SETUP_INSTALL', tab_index, windex),
|
| + msg='Cannot start host install')
|
| + self.assertTrue(
|
| + self._ExecuteAndWaitForMode(
|
| + 'document.getElementById("host-config-install-dismiss").click();',
|
| + 'HOME', tab_index, windex),
|
| + msg='Failed to dismiss host install dialog')
|
| +
|
| + def DisableConnections(self, tab_index=1, windex=0):
|
| + """Disables the remote connections on the host side."""
|
| + subprocess.call([self._GetHelperRunner(), self._GetHelper(), 'disable'])
|
| +
|
| + self._ExecuteJavascript(
|
| + 'document.getElementById("stop-daemon").click();',
|
| + tab_index, windex)
|
| + self.assertTrue(
|
| + self._ExecuteAndWaitForMode(
|
| + 'document.getElementById("host-config-done-dismiss").click();',
|
| + 'HOME', tab_index, windex),
|
| + msg='Failed to dismiss host setup confirmation dialog')
|
| +
|
| + def Connect(self, access_code, tab_index=1, windex=0):
|
| + """Connects to a Chromoting host and starts the session."""
|
| + self.assertTrue(
|
| + self._ExecuteAndWaitForMode(
|
| + 'document.getElementById("access-code-entry").value = "%s";'
|
| + 'remoting.connectIt2Me();' % access_code,
|
| + 'IN_SESSION', tab_index, windex),
|
| + msg='Cannot connect it2me session')
|
| +
|
| + def ChangePin(self, pin='222222', tab_index=1, windex=0):
|
| + """Changes pin for enabled host."""
|
| + subprocess.call([self._GetHelperRunner(), self._GetHelper(), 'changepin'])
|
| +
|
| + self.assertTrue(
|
| + self._ExecuteAndWaitForMode(
|
| + 'document.getElementById("change-daemon-pin").click();',
|
| + 'HOST_SETUP_ASK_PIN', tab_index, windex),
|
| + msg='Cannot change daemon pin')
|
| + self.assertTrue(
|
| + self._WaitForJavascriptCondition(
|
| + 'document.getElementById("ask-pin-form").hidden == false',
|
| + tab_index, windex),
|
| + msg='No ask pin dialog')
|
| +
|
| + self._ExecuteJavascript(
|
| + 'document.getElementById("daemon-pin-entry").value = "' + pin + '";',
|
| + tab_index, windex)
|
| + self._ExecuteJavascript(
|
| + 'document.getElementById("daemon-pin-confirm").value = "' +
|
| + pin + '";', tab_index, windex)
|
| + self.assertTrue(
|
| + self._ExecuteAndWaitForMode(
|
| + 'document.getElementById("daemon-pin-ok").click();',
|
| + 'HOST_SETUP_PROCESSING', tab_index, windex),
|
| + msg='Host setup was not started')
|
| +
|
| + # Handles preference panes
|
| + self.assertTrue(
|
| + self._WaitForJavascriptCondition(
|
| + 'remoting.currentMode == remoting.AppMode.HOST_SETUP_DONE',
|
| + tab_index, windex),
|
| + msg='Host setup was not done')
|
| +
|
| + # Dismisses the host config done dialog
|
| + self.assertTrue(
|
| + self._WaitForJavascriptCondition(
|
| + 'document.getElementById("host-setup-dialog")'
|
| + '.childNodes[5].hidden == false',
|
| + tab_index, windex),
|
| + msg='No host setup done dialog')
|
| + self.assertTrue(
|
| + self._ExecuteAndWaitForMode(
|
| + 'document.getElementById("host-config-done-dismiss").click();',
|
| + 'HOME', tab_index, windex),
|
| + msg='Failed to dismiss host setup confirmation dialog')
|
| +
|
| + def ChangeName(self, new_name='Changed', tab_index=1, windex=0):
|
| + """Changes the host name."""
|
| + self._ExecuteJavascript(
|
| + 'document.getElementById("this-host-rename").click();',
|
| + tab_index, windex)
|
| + self._ExecuteJavascript(
|
| + 'document.getElementById("this-host-name").childNodes[0].value = "' +
|
| + new_name + '";', tab_index, windex)
|
| + self._ExecuteJavascript(
|
| + 'document.getElementById("this-host-rename").click();',
|
| + tab_index, windex)
|
| +
|
| + def ConnectMe2Me(self, pin='111111', mode='IN_SESSION',
|
| + tab_index=1, windex=0):
|
| + """Connects to a Chromoting host and starts the session."""
|
| +
|
| + # There is delay from the enabling remote connections to the host
|
| + # showing up in the host list. We need to reload the web app to get
|
| + # the host to show up. We will repeat this a few times to make sure
|
| + # eventually host appears.
|
| + for _ in range(1, 3):
|
| + self._ExecuteJavascript(
|
| + 'window.location.reload();',
|
| + tab_index, windex)
|
| +
|
| + # pyauto _GetResultFromJSONRequest throws JSONInterfaceError after
|
| + # ~60 seconds if ExecuteJavascript is called right after reload.
|
| + # Waiting 2s here can avoid this. So instead of getting the error and
|
| + # wait ~60s, we wait 2s here. If the error still happens, the following
|
| + # retry will handle that.
|
| + time.sleep(2)
|
| +
|
| + # If this-host-connect is still not enabled, let's retry 3 times here.
|
| + this_host_connect_enabled = False
|
| + for _ in range(1, 3):
|
| + this_host_connect_enabled = self._WaitForJavascriptCondition(
|
| + 'document.getElementById("this-host-connect")'
|
| + '.getAttribute("data-daemon-state") == "enabled"',
|
| + tab_index, windex)
|
| + if this_host_connect_enabled:
|
| + break
|
| + if this_host_connect_enabled:
|
| + break;
|
| +
|
| + # Clicking this-host-connect does work right after this-host-connect
|
| + # is enabled. Need to retry.
|
| + for _ in range(1, 3):
|
| + self._ExecuteJavascript(
|
| + 'document.getElementById("this-host-connect").click();',
|
| + tab_index, windex)
|
| +
|
| + # pyauto _GetResultFromJSONRequest throws JSONInterfaceError after
|
| + # a long time out if WaitUntil is called right after click.
|
| + # Waiting 2s here can avoid this.
|
| + time.sleep(2)
|
| +
|
| + # If cannot detect that pin-form appears, try 3 times.
|
| + pin_form_exposed = False
|
| + for _ in range(1, 3):
|
| + pin_form_exposed = self._WaitForJavascriptCondition(
|
| + 'document.getElementById("client-dialog")'
|
| + '.childNodes[9].hidden == false',
|
| + tab_index, windex)
|
| + if pin_form_exposed:
|
| + break
|
| + if pin_form_exposed:
|
| + break
|
| +
|
| + self._ExecuteJavascript(
|
| + 'document.getElementById("pin-entry").value = "' + pin + '";',
|
| + tab_index, windex)
|
| + self.assertTrue(
|
| + self._ExecuteAndWaitForMode(
|
| + 'document.getElementById("pin-form").childNodes[5].click();',
|
| + mode, tab_index, windex),
|
| + msg='Session was not started')
|
| +
|
| + def Disconnect(self, tab_index=1, windex=0):
|
| + """Disconnects from the Chromoting it2me session on the client side."""
|
| + self.assertTrue(
|
| + self._ExecuteAndWaitForMode(
|
| + 'remoting.disconnect();',
|
| + 'CLIENT_SESSION_FINISHED_IT2ME', tab_index, windex),
|
| + msg='Disconnecting it2me session from the client side failed')
|
| +
|
| + def DisconnectMe2Me(self, confirmation=True, tab_index=1, windex=0):
|
| + """Disconnects from the Chromoting me2me session on the client side."""
|
| + self.assertTrue(
|
| + self._ExecuteAndWaitForMode(
|
| + 'remoting.disconnect();',
|
| + 'CLIENT_SESSION_FINISHED_ME2ME', tab_index, windex),
|
| + msg='Disconnecting me2me session from the client side failed')
|
| +
|
| + if confirmation:
|
| + self.assertTrue(
|
| + self._ExecuteAndWaitForMode(
|
| + 'document.getElementById("client-finished-me2me-button")'
|
| + '.click();', 'HOME', tab_index, windex),
|
| + msg='Failed to dismiss session finished dialog')
|
| +
|
| + def ReconnectMe2Me(self, pin='111111', tab_index=1, windex=0):
|
| + """Reconnects the me2me session."""
|
| + self._ExecuteJavascript(
|
| + 'document.getElementById("client-reconnect-button").click();',
|
| + tab_index, windex)
|
| +
|
| + # pyauto _GetResultFromJSONRequest throws JSONInterfaceError after
|
| + # a long time out if WaitUntil is called right after click.
|
| + time.sleep(2)
|
| +
|
| + # If cannot detect that pin-form appears, try 3 times.
|
| + for _ in range(1, 3):
|
| + pin_form_exposed = self._WaitForJavascriptCondition(
|
| + 'document.getElementById("client-dialog")'
|
| + '.childNodes[9].hidden == false',
|
| + tab_index, windex)
|
| + if pin_form_exposed:
|
| + break
|
| +
|
| + self._ExecuteJavascript(
|
| + 'document.getElementById("pin-entry").value = "' + pin + '";',
|
| + tab_index, windex)
|
| + self.assertTrue(
|
| + self._ExecuteAndWaitForMode(
|
| + 'document.getElementById("pin-form").childNodes[5].click();',
|
| + 'IN_SESSION', tab_index, windex),
|
| + msg='Session was not started')
|
|
|