• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
7This module contains a set of common utilities for querying
8and manipulating the Google Talk Chrome Extension.
9"""
10
11import logging
12import re
13import os
14
15import pyauto_gtalk
16import pyauto
17import pyauto_errors
18
19
20class 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