Chromium Code Reviews| Index: chrome/test/pyautolib/chromoting.py |
| =================================================================== |
| --- chrome/test/pyautolib/chromoting.py (revision 150103) |
| +++ chrome/test/pyautolib/chromoting.py (working copy) |
| @@ -2,9 +2,16 @@ |
| # 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. |
| @@ -21,30 +28,52 @@ |
| """ |
| def _ExecuteJavascript(self, command, tab_index, windex): |
| - """Helper that returns immediately after running a Javascript command.""" |
| - return self.ExecuteJavascript( |
| - '%s; window.domAutomationController.send("done");' % 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' |
|
Nirnimesh
2012/08/14 22:09:30
why is this necessary?
yihongg
2012/08/15 17:48:38
As why we have sleep() in the following. When Exec
|
| + 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. |
| """ |
| - return self.WaitUntil(lambda: self.GetDOMValue( |
| - '(%s) ? "1" : ""' % condition, tab_index, windex)) |
| + 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): |
| - self.assertTrue(self._ExecuteJavascript(command, tab_index, windex), |
| - 'Javascript command did not return anything.') |
| + """ 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): |
| - self.assertTrue(self._ExecuteJavascript(command, tab_index, windex), |
| - 'Javascript command did not return anything.') |
| + """ 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) |
| @@ -56,24 +85,44 @@ |
| """ |
| return os.path.join(self.BrowserPath(), 'remoting', 'remoting.webapp') |
| - def Authenticate(self, email=None, password=None, otp=None, |
| - tab_index=1, windex=0): |
| - """Logs a user in for Chromoting and accepts permissions for the app. |
| + def _GetHelperRunner(self): |
| + """Returns the python binary name that runs chromoting_helper.py.""" |
| + if sys.platform.startswith('win'): |
| + return 'python' |
| + else: |
| + return 'suid-python' |
| - 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. |
| + def _GetHelper(self): |
| + """Get chromoting_helper.py.""" |
| + return os.path.join('chrome', 'test', 'pyautolib', 'chromoting_helper.py') |
| - Raises: |
| - AssertionError if the authentication flow changes or |
| - the credentials are incorrect. |
| - """ |
| + 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), |
| @@ -95,10 +144,10 @@ |
| '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) |
| + 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( |
| @@ -112,12 +161,14 @@ |
| '{ 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='Authentication failed. The username, password, or otp is invalid.') |
| + msg='Did not go to permission page.') |
| self._ExecuteJavascript( |
| 'document.getElementById("submit_approve_access").click();', |
| tab_index, windex) |
| @@ -135,6 +186,37 @@ |
| 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. |
| @@ -148,40 +230,305 @@ |
| 'document.getElementById("access-code-display").innerText', |
| tab_index, windex) |
| - def Connect(self, access_code, wait_for_frame, tab_index=1, windex=0): |
| - """Connects to a Chromoting host and starts the session. |
| + 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') |
| - Returns: |
| - True on success; False otherwise. |
| - """ |
| - if not self._ExecuteAndWaitForMode( |
| - 'document.getElementById("access-code-entry").value = "%s";' |
| - 'remoting.connectIt2Me();' % access_code, |
| - 'IN_SESSION', tab_index, windex): |
| - return False |
| + 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']) |
| - if wait_for_frame and not self._WaitForJavascriptCondition( |
| - 'remoting.clientSession && remoting.clientSession.hasReceivedFrame()', |
| - tab_index, windex): |
| - return False |
| - return True |
| + 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') |
| - def CancelShare(self, tab_index=1, windex=0): |
| - """Stops sharing the desktop on the host side. |
| + if pin_exercise: |
| + # Cancels the pin prompt |
| + self._ExecuteJavascript( |
| + 'document.getElementById("daemon-pin-cancel").click();', |
| + tab_index, windex) |
| - Returns: |
| - True on success; False otherwise. |
| - """ |
| - return self._ExecuteAndWaitForMode( |
| - 'remoting.cancelShare();', |
| - 'HOST_SHARE_FINISHED', 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 session on the client side. |
| + """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') |
| - Returns: |
| - True on success; False otherwise. |
| - """ |
| - return self._ExecuteAndWaitForMode( |
| - 'remoting.disconnect();', |
| - 'CLIENT_SESSION_FINISHED_IT2ME', tab_index, windex) |
| + 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') |