Home | History | Annotate | Download | only in gtalk
      1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 """Base GTalk tests.
      6 
      7 This module contains a set of common utilities for querying
      8 and manipulating the Google Talk Chrome Extension.
      9 """
     10 
     11 import logging
     12 import re
     13 import os
     14 
     15 import pyauto_gtalk
     16 import pyauto
     17 import pyauto_errors
     18 
     19 
     20 class GTalkBaseTest(pyauto.PyUITest):
     21   """Base test class for testing GTalk."""
     22 
     23   _injected_js = None
     24 
     25   def Prompt(self, text):
     26     """Pause execution with debug output.
     27 
     28     Args:
     29       text: The debug output.
     30     """
     31     text = str(text)
     32     raw_input('--------------------> ' + text)
     33 
     34   def InstallGTalkExtension(self, gtalk_version):
     35     """Download and install the GTalk extension."""
     36     extension_path = os.path.abspath(
     37         os.path.join(self.DataDir(), 'extensions', 'gtalk',
     38                      gtalk_version + '.crx'))
     39     self.assertTrue(
     40         os.path.exists(extension_path),
     41         msg='Failed to find GTalk extension: ' + extension_path)
     42 
     43     extension = self.GetGTalkExtensionInfo()
     44     if extension:
     45       logging.info('Extension already installed. Skipping install...\n')
     46       return
     47 
     48     self.InstallExtension(extension_path, False)
     49     extension = self.GetGTalkExtensionInfo()
     50     self.assertTrue(extension, msg='Failed to install GTalk extension.')
     51     self.assertTrue(extension['is_enabled'], msg='GTalk extension is disabled.')
     52 
     53   def UninstallGTalkExtension(self):
     54     """Uninstall the GTalk extension (if present)"""
     55     extension = self.GetGTalkExtensionInfo()
     56     if extension:
     57       self.UninstallExtensionById(extension['id'])
     58 
     59   def GetGTalkExtensionInfo(self):
     60     """Get the data object about the GTalk extension."""
     61     extensions = [x for x in self.GetExtensionsInfo()
     62         if x['name'] == 'Chat for Google']
     63     return extensions[0] if len(extensions) == 1 else None
     64 
     65   def RunInMole(self, js, mole_index=0):
     66     """Execute javascript in a chat mole.
     67 
     68     Args:
     69       js: The javascript to run.
     70       mole_index: The index of the mole in which to run the JS.
     71 
     72     Returns:
     73       The resulting value from executing the javascript.
     74     """
     75     return self._RunInRenderView(self.GetMoleInfo(mole_index), js,
     76         '//iframe[1]')
     77 
     78   def RunInAllMoles(self, js):
     79     """Execute javascript in all chat moles.
     80 
     81     Args:
     82       js: The javascript to run.
     83     """
     84     moles = self.GetMolesInfo()
     85     for mole in moles:
     86       self._RunInRenderView(mole, js, '//iframe[1]')
     87 
     88   def RunInRoster(self, js):
     89     """Execute javascript in the chat roster.
     90 
     91     Args:
     92       js: The javascript to run.
     93 
     94     Returns:
     95       The resulting value from executing the javascript.
     96     """
     97     return self._RunInRenderView(self.GetViewerInfo(), js,
     98         '//iframe[1]\n//iframe[1]')
     99 
    100   def RunInLoginPage(self, js, xpath=''):
    101     """Execute javascript in the gaia login popup.
    102 
    103     Args:
    104       js: The javascript to run.
    105       xpath: The xpath to the frame in which to execute the javascript.
    106 
    107     Returns:
    108       The resulting value from executing the javascript.
    109     """
    110     return self._RunInTab(self.GetLoginPageInfo(), js, xpath)
    111 
    112   def RunInViewer(self, js, xpath=''):
    113     """Execute javascript in the GTalk viewer window.
    114 
    115     Args:
    116       js: The javascript to run.
    117       xpath: The xpath to the frame in which to execute the javascript.
    118 
    119     Returns:
    120       The resulting value from executing the javascript.
    121     """
    122     return self._RunInRenderView(self.GetViewerInfo(), js, xpath)
    123 
    124   def RunInBackground(self, js, xpath=''):
    125     """Execute javascript in the GTalk viewer window.
    126 
    127     Args:
    128       js: The javascript to run.
    129       xpath: The xpath to the frame in which to execute the javascript.
    130 
    131     Returns:
    132       The resulting value from executing the javascript.
    133     """
    134     background_view = self.GetBackgroundInfo()
    135     return self._RunInRenderView(background_view['view'], js, xpath)
    136 
    137   def GetMoleInfo(self, mole_index=0):
    138     """Get the data object about a given chat mole.
    139 
    140     Args:
    141       mole_index: The index of the mole to retrieve.
    142 
    143     Returns:
    144       Data object describing mole.
    145     """
    146     extension = self.GetGTalkExtensionInfo()
    147     return self._GetExtensionViewInfo(
    148         'chrome-extension://%s/panel.html' % extension['id'],
    149         mole_index)
    150 
    151   def GetMolesInfo(self):
    152     """Get the data objects for all of the chat moles.
    153 
    154     Returns:
    155       Set of data objects describing moles.
    156     """
    157     extension = self.GetGTalkExtensionInfo()
    158     return self._GetMatchingExtensionViews(
    159         'chrome-extension://%s/panel.html' % extension['id'])
    160 
    161   def GetViewerInfo(self):
    162     """Get the data object about the GTalk viewer dialog."""
    163     extension = self.GetGTalkExtensionInfo()
    164     return self._GetExtensionViewInfo(
    165         'chrome-extension://%s/viewer.html' % extension['id'])
    166 
    167   def GetLoginPageInfo(self):
    168     """Get the data object about the gaia login popup."""
    169     return self._GetTabInfo('https://accounts.google.com/ServiceLogin?')
    170 
    171   def GetBackgroundInfo(self):
    172     """Get the data object about the GTalk background page."""
    173     extension_views = self.GetBrowserInfo()['extension_views']
    174     for extension_view in extension_views:
    175       if 'Google Talk' in extension_view['name'] and \
    176           'EXTENSION_BACKGROUND_PAGE' == extension_view['view_type']:
    177         return extension_view
    178     return None
    179 
    180   def WaitUntilResult(self, result, func, msg):
    181     """Loop func until a condition matches is satified.
    182 
    183     Args:
    184       result: Value of func() at which to stop.
    185       func: Function to run at each iteration.
    186       msg: Error to print upon timing out.
    187     """
    188     assert callable(func)
    189     self.assertTrue(self.WaitUntil(
    190         lambda: func(), expect_retval=result), msg=msg)
    191 
    192   def WaitUntilCondition(self, func, matches, msg):
    193     """Loop func until condition matches is satified.
    194 
    195     Args:
    196       func: Function to run at each iteration.
    197       matches: Funtion to evalute output and determine whether to stop.
    198       msg: Error to print upon timing out.
    199     """
    200     assert callable(func)
    201     assert callable(matches)
    202     self.assertTrue(self.WaitUntil(
    203         lambda: matches(func())), msg=msg)
    204 
    205   def _WrapJs(self, statement):
    206     """Wrap the javascript to be executed.
    207 
    208     Args:
    209       statement: The piece of javascript to wrap.
    210 
    211     Returns:
    212       The wrapped javascript.
    213     """
    214     return """
    215             window.domAutomationController.send(
    216             (function(){
    217             %s
    218             try{return %s}
    219             catch(e){return "JS_ERROR: " + e}})())
    220            """ % (self._GetInjectedJs(), statement)
    221 
    222   def _RunInTab(self, tab, js, xpath=''):
    223     """Execute javascript in a given tab.
    224 
    225     Args:
    226       tab: The data object for the Chrome window tab returned by
    227           _GetTabInfo.
    228       js: The javascript to run.
    229       xpath: The xpath to the frame in which to execute the javascript.
    230 
    231     Returns:
    232       The resulting value from executing the javascript.
    233     """
    234     if not tab:
    235       logging.debug('Tab not found: %s' % tab)
    236       return False
    237     logging.info('Run in tab: %s' % js)
    238 
    239     value = self.ExecuteJavascript(
    240         self._WrapJs(js),
    241         tab_index = tab['index'],
    242         windex = tab['windex'],
    243         frame_xpath = xpath)
    244     self._LogRun(js, value)
    245     return value
    246 
    247   def _RunInRenderView(self, view, js, xpath=''):
    248     """Execute javascript in a given render view.
    249 
    250     Args:
    251       view: The data object for the Chrome render view returned by
    252           _GetExtensionViewInfo.
    253       js: The javascript to run.
    254       xpath: The xpath to the frame in which to execute the javascript.
    255 
    256     Returns:
    257       The resulting value from executing the javascript.
    258     """
    259     if not view:
    260       logging.debug('View not found: %s' % view)
    261       return False
    262     logging.info('Run in view: %s' % js)
    263 
    264     value = self.ExecuteJavascriptInRenderView(
    265         self._WrapJs(js),
    266         view,
    267         frame_xpath = xpath)
    268     self._LogRun(js, value)
    269     return value
    270 
    271   def _LogRun(self, js, value):
    272     """Log a particular run.
    273 
    274     Args:
    275       js: The javascript statement executed.
    276       value: The return value for the execution.
    277     """
    278     # works around UnicodeEncodeError: 'ascii' codec can't encode...
    279     out = value
    280     if not isinstance(value, basestring):
    281       out = str(value)
    282     out = re.sub('\s', ';', out[:300])
    283     logging.info(js + ' ===> ' + out.encode('utf-8'))
    284 
    285   def _GetTabInfo(self, url_query, index=0):
    286     """Get the data object for a given tab.
    287 
    288     Args:
    289       url_query: The substring of the URL to search for.
    290       index: The index within the list of matches to return.
    291 
    292     Returns:
    293       The data object for the tab.
    294     """
    295     windows = self.GetBrowserInfo()['windows']
    296     i = 0
    297     for win in windows:
    298       for tab in win['tabs']:
    299         if tab['url'] and url_query in tab['url']:
    300           # Store reference to windex used in _RunInTab.
    301           tab['windex'] = win['index']
    302           if i == index:
    303             return tab
    304           i = i + 1
    305     return None
    306 
    307   def _GetExtensionViewInfo(self, url_query, index=0):
    308     """Get the data object for a given extension view.
    309 
    310     Args:
    311       url_query: The substring of the URL to search for.
    312       index: The index within the list of matches to return.
    313 
    314     Returns:
    315       The data object for the tab.
    316     """
    317 
    318     candidate_views = self._GetMatchingExtensionViews(url_query)
    319     if len(candidate_views) > index:
    320       return candidate_views[index]
    321     return None
    322 
    323   def _GetMatchingExtensionViews(self, url_query):
    324     """Gets the data objects for the extension views matching the url_query.
    325 
    326     Args:
    327       url_query: The substring of the URL to search for.
    328 
    329     Returns:
    330       An array of matching data objects.
    331     """
    332     extension_views = self.GetBrowserInfo()['extension_views']
    333     candidate_views = list()
    334     for extension_view in extension_views:
    335       if extension_view['url'] and url_query in extension_view['url']:
    336         candidate_views.append(extension_view['view'])
    337 
    338     # No guarantee on view order, so sort the views to get the correct one for
    339     # a given index.
    340     candidate_views.sort()
    341     return candidate_views
    342 
    343   def _GetInjectedJs(self):
    344     """Get the javascript to inject in the execution environment."""
    345     if self._injected_js is None:
    346       self._injected_js = open(
    347           os.path.join(os.path.dirname(__file__), 'jsutils.js')).read()
    348     return self._injected_js
    349