• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2# Copyright 2013 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""End to end tests for ChromeDriver."""
7
8import base64
9import json
10import math
11import optparse
12import os
13import socket
14import subprocess
15import sys
16import tempfile
17import threading
18import time
19import unittest
20import urllib2
21
22_THIS_DIR = os.path.abspath(os.path.dirname(__file__))
23sys.path.insert(1, os.path.join(_THIS_DIR, os.pardir))
24sys.path.insert(1, os.path.join(_THIS_DIR, os.pardir, 'client'))
25sys.path.insert(1, os.path.join(_THIS_DIR, os.pardir, 'server'))
26
27import chrome_paths
28import chromedriver
29import unittest_util
30import util
31import server
32from webelement import WebElement
33import webserver
34
35_TEST_DATA_DIR = os.path.join(chrome_paths.GetTestData(), 'chromedriver')
36
37if util.IsLinux():
38  sys.path.insert(0, os.path.join(chrome_paths.GetSrc(), 'build', 'android'))
39  from pylib import android_commands
40  from pylib import constants
41  from pylib import forwarder
42  from pylib import valgrind_tools
43  from pylib.device import device_utils
44
45
46_NEGATIVE_FILTER = [
47    # https://code.google.com/p/chromedriver/issues/detail?id=213
48    'ChromeDriverTest.testClickElementInSubFrame',
49    # This test is flaky since it uses setTimeout.
50    # Re-enable once crbug.com/177511 is fixed and we can remove setTimeout.
51    'ChromeDriverTest.testAlert',
52]
53
54_VERSION_SPECIFIC_FILTER = {}
55_VERSION_SPECIFIC_FILTER['HEAD'] = [
56    # https://code.google.com/p/chromedriver/issues/detail?id=815
57    'ChromeDriverTest.testShouldHandleNewWindowLoadingProperly',
58]
59
60_OS_SPECIFIC_FILTER = {}
61_OS_SPECIFIC_FILTER['win'] = [
62    # https://code.google.com/p/chromedriver/issues/detail?id=214
63    'ChromeDriverTest.testCloseWindow',
64    # https://code.google.com/p/chromedriver/issues/detail?id=299
65    'ChromeLogPathCapabilityTest.testChromeLogPath',
66]
67_OS_SPECIFIC_FILTER['linux'] = [
68    # Xvfb doesn't support maximization.
69    'ChromeDriverTest.testWindowMaximize',
70    # https://code.google.com/p/chromedriver/issues/detail?id=302
71    'ChromeDriverTest.testWindowPosition',
72    'ChromeDriverTest.testWindowSize',
73]
74_OS_SPECIFIC_FILTER['mac'] = [
75    # https://code.google.com/p/chromedriver/issues/detail?id=304
76    'ChromeDriverTest.testGoBackAndGoForward',
77]
78
79_DESKTOP_NEGATIVE_FILTER = [
80    # Desktop doesn't support touch (without --touch-events).
81    'ChromeDriverTest.testSingleTapElement',
82    'ChromeDriverTest.testTouchDownUpElement',
83    'ChromeDriverTest.testTouchFlickElement',
84    'ChromeDriverTest.testTouchMovedElement',
85    'ChromeDriverAndroidTest.*',
86]
87
88
89def _GetDesktopNegativeFilter(version_name):
90  filter = _NEGATIVE_FILTER + _DESKTOP_NEGATIVE_FILTER
91  os = util.GetPlatformName()
92  if os in _OS_SPECIFIC_FILTER:
93    filter += _OS_SPECIFIC_FILTER[os]
94  if version_name in _VERSION_SPECIFIC_FILTER:
95    filter += _VERSION_SPECIFIC_FILTER[version_name]
96  return filter
97
98_ANDROID_NEGATIVE_FILTER = {}
99_ANDROID_NEGATIVE_FILTER['chrome'] = (
100    _NEGATIVE_FILTER + [
101        # TODO(chrisgao): fix hang of tab crash test on android.
102        'ChromeDriverTest.testTabCrash',
103        # Android doesn't support switches and extensions.
104        'ChromeSwitchesCapabilityTest.*',
105        'ChromeExtensionsCapabilityTest.*',
106        'MobileEmulationCapabilityTest.*',
107        # https://crbug.com/274650
108        'ChromeDriverTest.testCloseWindow',
109        # https://code.google.com/p/chromedriver/issues/detail?id=270
110        'ChromeDriverTest.testPopups',
111        # https://code.google.com/p/chromedriver/issues/detail?id=298
112        'ChromeDriverTest.testWindowPosition',
113        'ChromeDriverTest.testWindowSize',
114        'ChromeDriverTest.testWindowMaximize',
115        'ChromeLogPathCapabilityTest.testChromeLogPath',
116        'RemoteBrowserTest.*',
117        # Don't enable perf testing on Android yet.
118        'PerfTest.testSessionStartTime',
119        'PerfTest.testSessionStopTime',
120        'PerfTest.testColdExecuteScript',
121        # https://code.google.com/p/chromedriver/issues/detail?id=459
122        'ChromeDriverTest.testShouldHandleNewWindowLoadingProperly',
123    ]
124)
125_ANDROID_NEGATIVE_FILTER['chrome_stable'] = (
126    _ANDROID_NEGATIVE_FILTER['chrome'])
127_ANDROID_NEGATIVE_FILTER['chrome_beta'] = (
128    _ANDROID_NEGATIVE_FILTER['chrome'])
129_ANDROID_NEGATIVE_FILTER['chrome_shell'] = (
130    _ANDROID_NEGATIVE_FILTER['chrome'] + [
131        # ChromeShell doesn't support multiple tabs.
132        'ChromeDriverTest.testGetWindowHandles',
133        'ChromeDriverTest.testSwitchToWindow',
134        'ChromeDriverTest.testShouldHandleNewWindowLoadingProperly',
135    ]
136)
137_ANDROID_NEGATIVE_FILTER['chromedriver_webview_shell'] = (
138    _ANDROID_NEGATIVE_FILTER['chrome_shell'])
139
140
141class ChromeDriverBaseTest(unittest.TestCase):
142  """Base class for testing chromedriver functionalities."""
143
144  def __init__(self, *args, **kwargs):
145    super(ChromeDriverBaseTest, self).__init__(*args, **kwargs)
146    self._drivers = []
147
148  def tearDown(self):
149    for driver in self._drivers:
150      try:
151        driver.Quit()
152      except:
153        pass
154
155  def CreateDriver(self, server_url=None, **kwargs):
156    if server_url is None:
157      server_url = _CHROMEDRIVER_SERVER_URL
158
159    android_package = None
160    android_activity = None
161    android_process = None
162    if _ANDROID_PACKAGE_KEY:
163      android_package = constants.PACKAGE_INFO[_ANDROID_PACKAGE_KEY].package
164      if _ANDROID_PACKAGE_KEY == 'chromedriver_webview_shell':
165        android_activity = constants.PACKAGE_INFO[_ANDROID_PACKAGE_KEY].activity
166        android_process = '%s:main' % android_package
167
168    driver = chromedriver.ChromeDriver(server_url,
169                                       chrome_binary=_CHROME_BINARY,
170                                       android_package=android_package,
171                                       android_activity=android_activity,
172                                       android_process=android_process,
173                                       **kwargs)
174    self._drivers += [driver]
175    return driver
176
177
178class ChromeDriverTest(ChromeDriverBaseTest):
179  """End to end tests for ChromeDriver."""
180
181  @staticmethod
182  def GlobalSetUp():
183    ChromeDriverTest._http_server = webserver.WebServer(
184        chrome_paths.GetTestData())
185    ChromeDriverTest._sync_server = webserver.SyncWebServer()
186    if _ANDROID_PACKAGE_KEY:
187      ChromeDriverTest._device = device_utils.DeviceUtils(
188          android_commands.GetAttachedDevices()[0])
189      http_host_port = ChromeDriverTest._http_server._server.server_port
190      sync_host_port = ChromeDriverTest._sync_server._server.server_port
191      forwarder.Forwarder.Map(
192          [(http_host_port, http_host_port), (sync_host_port, sync_host_port)],
193          ChromeDriverTest._device)
194
195  @staticmethod
196  def GlobalTearDown():
197    if _ANDROID_PACKAGE_KEY:
198      forwarder.Forwarder.UnmapAllDevicePorts(ChromeDriverTest._device)
199    ChromeDriverTest._http_server.Shutdown()
200
201  @staticmethod
202  def GetHttpUrlForFile(file_path):
203    return ChromeDriverTest._http_server.GetUrl() + file_path
204
205  def setUp(self):
206    self._driver = self.CreateDriver()
207
208  def testStartStop(self):
209    pass
210
211  def testLoadUrl(self):
212    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/empty.html'))
213
214  def testGetCurrentWindowHandle(self):
215    self._driver.GetCurrentWindowHandle()
216
217  def _WaitForNewWindow(self, old_handles):
218    """Wait for at least one new window to show up in 20 seconds.
219
220    Args:
221      old_handles: Handles to all old windows before the new window is added.
222
223    Returns:
224      Handle to a new window. None if timeout.
225    """
226    timeout = time.time() + 20
227    while time.time() < timeout:
228      new_handles = self._driver.GetWindowHandles()
229      if len(new_handles) > len(old_handles):
230        for index, old_handle in enumerate(old_handles):
231          self.assertEquals(old_handle, new_handles[index])
232        return new_handles[len(old_handles)]
233      time.sleep(0.01)
234    return None
235
236  def testCloseWindow(self):
237    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/page_test.html'))
238    old_handles = self._driver.GetWindowHandles()
239    self._driver.FindElement('id', 'link').Click()
240    new_window_handle = self._WaitForNewWindow(old_handles)
241    self.assertNotEqual(None, new_window_handle)
242    self._driver.SwitchToWindow(new_window_handle)
243    self.assertEquals(new_window_handle, self._driver.GetCurrentWindowHandle())
244    self.assertRaises(chromedriver.NoSuchElement,
245                      self._driver.FindElement, 'id', 'link')
246    self._driver.CloseWindow()
247    self.assertRaises(chromedriver.NoSuchWindow,
248                      self._driver.GetCurrentWindowHandle)
249    new_handles = self._driver.GetWindowHandles()
250    for old_handle in old_handles:
251      self.assertTrue(old_handle in new_handles)
252    for handle in new_handles:
253      self._driver.SwitchToWindow(handle)
254      self.assertEquals(handle, self._driver.GetCurrentWindowHandle())
255      self._driver.CloseWindow()
256
257  def testGetWindowHandles(self):
258    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/page_test.html'))
259    old_handles = self._driver.GetWindowHandles()
260    self._driver.FindElement('id', 'link').Click()
261    self.assertNotEqual(None, self._WaitForNewWindow(old_handles))
262
263  def testSwitchToWindow(self):
264    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/page_test.html'))
265    self.assertEquals(
266        1, self._driver.ExecuteScript('window.name = "oldWindow"; return 1;'))
267    window1_handle = self._driver.GetCurrentWindowHandle()
268    old_handles = self._driver.GetWindowHandles()
269    self._driver.FindElement('id', 'link').Click()
270    new_window_handle = self._WaitForNewWindow(old_handles)
271    self.assertNotEqual(None, new_window_handle)
272    self._driver.SwitchToWindow(new_window_handle)
273    self.assertEquals(new_window_handle, self._driver.GetCurrentWindowHandle())
274    self.assertRaises(chromedriver.NoSuchElement,
275                      self._driver.FindElement, 'id', 'link')
276    self._driver.SwitchToWindow('oldWindow')
277    self.assertEquals(window1_handle, self._driver.GetCurrentWindowHandle())
278
279  def testEvaluateScript(self):
280    self.assertEquals(1, self._driver.ExecuteScript('return 1'))
281    self.assertEquals(None, self._driver.ExecuteScript(''))
282
283  def testEvaluateScriptWithArgs(self):
284    script = ('document.body.innerHTML = "<div>b</div><div>c</div>";'
285              'return {stuff: document.querySelectorAll("div")};')
286    stuff = self._driver.ExecuteScript(script)['stuff']
287    script = 'return arguments[0].innerHTML + arguments[1].innerHTML'
288    self.assertEquals(
289        'bc', self._driver.ExecuteScript(script, stuff[0], stuff[1]))
290
291  def testEvaluateInvalidScript(self):
292    self.assertRaises(chromedriver.ChromeDriverException,
293                      self._driver.ExecuteScript, '{{{')
294
295  def testExecuteAsyncScript(self):
296    self._driver.SetTimeout('script', 3000)
297    self.assertRaises(
298        chromedriver.ScriptTimeout,
299        self._driver.ExecuteAsyncScript,
300        'var callback = arguments[0];'
301        'setTimeout(function(){callback(1);}, 10000);')
302    self.assertEquals(
303        2,
304        self._driver.ExecuteAsyncScript(
305            'var callback = arguments[0];'
306            'setTimeout(function(){callback(2);}, 300);'))
307
308  def testSwitchToFrame(self):
309    self._driver.ExecuteScript(
310        'var frame = document.createElement("iframe");'
311        'frame.id="id";'
312        'frame.name="name";'
313        'document.body.appendChild(frame);')
314    self.assertTrue(self._driver.ExecuteScript('return window.top == window'))
315    self._driver.SwitchToFrame('id')
316    self.assertTrue(self._driver.ExecuteScript('return window.top != window'))
317    self._driver.SwitchToMainFrame()
318    self.assertTrue(self._driver.ExecuteScript('return window.top == window'))
319    self._driver.SwitchToFrame('name')
320    self.assertTrue(self._driver.ExecuteScript('return window.top != window'))
321    self._driver.SwitchToMainFrame()
322    self.assertTrue(self._driver.ExecuteScript('return window.top == window'))
323    self._driver.SwitchToFrameByIndex(0)
324    self.assertTrue(self._driver.ExecuteScript('return window.top != window'))
325    self._driver.SwitchToMainFrame()
326    self.assertTrue(self._driver.ExecuteScript('return window.top == window'))
327    self._driver.SwitchToFrame(self._driver.FindElement('tag name', 'iframe'))
328    self.assertTrue(self._driver.ExecuteScript('return window.top != window'))
329
330  def testSwitchToParentFrame(self):
331    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/nested.html'))
332    self.assertTrue('One' in self._driver.GetPageSource())
333    self._driver.SwitchToFrameByIndex(0)
334    self.assertTrue('Two' in self._driver.GetPageSource())
335    self._driver.SwitchToFrameByIndex(0)
336    self.assertTrue('Three' in self._driver.GetPageSource())
337    self._driver.SwitchToParentFrame()
338    self.assertTrue('Two' in self._driver.GetPageSource())
339    self._driver.SwitchToParentFrame()
340    self.assertTrue('One' in self._driver.GetPageSource())
341
342  def testExecuteInRemovedFrame(self):
343    self._driver.ExecuteScript(
344        'var frame = document.createElement("iframe");'
345        'frame.id="id";'
346        'frame.name="name";'
347        'document.body.appendChild(frame);'
348        'window.addEventListener("message",'
349        '    function(event) { document.body.removeChild(frame); });')
350    self.assertTrue(self._driver.ExecuteScript('return window.top == window'))
351    self._driver.SwitchToFrame('id')
352    self.assertTrue(self._driver.ExecuteScript('return window.top != window'))
353    self._driver.ExecuteScript('parent.postMessage("remove", "*");')
354    self.assertTrue(self._driver.ExecuteScript('return window.top == window'))
355
356  def testGetTitle(self):
357    script = 'document.title = "title"; return 1;'
358    self.assertEquals(1, self._driver.ExecuteScript(script))
359    self.assertEquals('title', self._driver.GetTitle())
360
361  def testGetPageSource(self):
362    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/page_test.html'))
363    self.assertTrue('Link to empty.html' in self._driver.GetPageSource())
364
365  def testFindElement(self):
366    self._driver.ExecuteScript(
367        'document.body.innerHTML = "<div>a</div><div>b</div>";')
368    self.assertTrue(
369        isinstance(self._driver.FindElement('tag name', 'div'), WebElement))
370
371  def testFindElements(self):
372    self._driver.ExecuteScript(
373        'document.body.innerHTML = "<div>a</div><div>b</div>";')
374    divs = self._driver.FindElements('tag name', 'div')
375    self.assertTrue(isinstance(divs, list))
376    self.assertEquals(2, len(divs))
377    for div in divs:
378      self.assertTrue(isinstance(div, WebElement))
379
380  def testFindChildElement(self):
381    self._driver.ExecuteScript(
382        'document.body.innerHTML = "<div><br><br></div><div><a></a></div>";')
383    element = self._driver.FindElement('tag name', 'div')
384    self.assertTrue(
385        isinstance(element.FindElement('tag name', 'br'), WebElement))
386
387  def testFindChildElements(self):
388    self._driver.ExecuteScript(
389        'document.body.innerHTML = "<div><br><br></div><div><br></div>";')
390    element = self._driver.FindElement('tag name', 'div')
391    brs = element.FindElements('tag name', 'br')
392    self.assertTrue(isinstance(brs, list))
393    self.assertEquals(2, len(brs))
394    for br in brs:
395      self.assertTrue(isinstance(br, WebElement))
396
397  def testHoverOverElement(self):
398    div = self._driver.ExecuteScript(
399        'document.body.innerHTML = "<div>old</div>";'
400        'var div = document.getElementsByTagName("div")[0];'
401        'div.addEventListener("mouseover", function() {'
402        '  document.body.appendChild(document.createElement("br"));'
403        '});'
404        'return div;')
405    div.HoverOver()
406    self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
407
408  def testClickElement(self):
409    div = self._driver.ExecuteScript(
410        'document.body.innerHTML = "<div>old</div>";'
411        'var div = document.getElementsByTagName("div")[0];'
412        'div.addEventListener("click", function() {'
413        '  div.innerHTML="new<br>";'
414        '});'
415        'return div;')
416    div.Click()
417    self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
418
419  def testSingleTapElement(self):
420    div = self._driver.ExecuteScript(
421        'document.body.innerHTML = "<div>old</div>";'
422        'var div = document.getElementsByTagName("div")[0];'
423        'div.addEventListener("touchend", function() {'
424        '  div.innerHTML="new<br>";'
425        '});'
426        'return div;')
427    div.SingleTap()
428    self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
429
430  def testTouchDownUpElement(self):
431    div = self._driver.ExecuteScript(
432        'document.body.innerHTML = "<div>old</div>";'
433        'var div = document.getElementsByTagName("div")[0];'
434        'div.addEventListener("touchend", function() {'
435        '  div.innerHTML="new<br>";'
436        '});'
437        'return div;')
438    loc = div.GetLocation()
439    self._driver.TouchDown(loc['x'], loc['y'])
440    self._driver.TouchUp(loc['x'], loc['y'])
441    self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
442
443  def testTouchFlickElement(self):
444    dx = 3
445    dy = 4
446    speed = 5
447    flickTouchEventsPerSecond = 30
448    moveEvents = int(
449        math.sqrt(dx * dx + dy * dy) * flickTouchEventsPerSecond / speed)
450    div = self._driver.ExecuteScript(
451        'document.body.innerHTML = "<div>old</div>";'
452        'var div = document.getElementsByTagName("div")[0];'
453        'div.addEventListener("touchstart", function() {'
454        '  div.innerHTML = "preMove0";'
455        '});'
456        'div.addEventListener("touchmove", function() {'
457        '  res = div.innerHTML.match(/preMove(\d+)/);'
458        '  if (res != null) {'
459        '    div.innerHTML = "preMove" + (parseInt(res[1], 10) + 1);'
460        '  }'
461        '});'
462        'div.addEventListener("touchend", function() {'
463        '  if (div.innerHTML == "preMove' + str(moveEvents) + '") {'
464        '    div.innerHTML = "new<br>";'
465        '  }'
466        '});'
467        'return div;')
468    self._driver.TouchFlick(div, dx, dy, speed)
469    self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
470
471  def testTouchMovedElement(self):
472    div = self._driver.ExecuteScript(
473        'document.body.innerHTML = "<div>old</div>";'
474        'var div = document.getElementsByTagName("div")[0];'
475        'div.addEventListener("touchmove", function() {'
476        '  div.innerHTML="new<br>";'
477        '});'
478        'return div;')
479    loc = div.GetLocation()
480    self._driver.TouchDown(loc['x'], loc['y'])
481    self._driver.TouchMove(loc['x'] + 1, loc['y'] + 1)
482    self._driver.TouchUp(loc['x'] + 1, loc['y'] + 1)
483    self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
484
485  def testClickElementInSubFrame(self):
486    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/frame_test.html'))
487    frame = self._driver.FindElement('tag name', 'iframe')
488    self._driver.SwitchToFrame(frame)
489    # Test clicking element in the sub frame.
490    self.testClickElement()
491
492  def testClearElement(self):
493    text = self._driver.ExecuteScript(
494        'document.body.innerHTML = \'<input type="text" value="abc">\';'
495        'var input = document.getElementsByTagName("input")[0];'
496        'input.addEventListener("change", function() {'
497        '  document.body.appendChild(document.createElement("br"));'
498        '});'
499        'return input;')
500    text.Clear()
501    self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
502
503  def testSendKeysToElement(self):
504    text = self._driver.ExecuteScript(
505        'document.body.innerHTML = \'<input type="text">\';'
506        'var input = document.getElementsByTagName("input")[0];'
507        'input.addEventListener("change", function() {'
508        '  document.body.appendChild(document.createElement("br"));'
509        '});'
510        'return input;')
511    text.SendKeys('0123456789+-*/ Hi')
512    text.SendKeys(', there!')
513    value = self._driver.ExecuteScript('return arguments[0].value;', text)
514    self.assertEquals('0123456789+-*/ Hi, there!', value)
515
516  def testGetCurrentUrl(self):
517    self.assertEquals('data:,', self._driver.GetCurrentUrl())
518
519  def testGoBackAndGoForward(self):
520    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/empty.html'))
521    self._driver.GoBack()
522    self._driver.GoForward()
523
524  def testRefresh(self):
525    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/empty.html'))
526    self._driver.Refresh()
527
528  def testMouseMoveTo(self):
529    div = self._driver.ExecuteScript(
530        'document.body.innerHTML = "<div>old</div>";'
531        'var div = document.getElementsByTagName("div")[0];'
532        'div.style["width"] = "100px";'
533        'div.style["height"] = "100px";'
534        'div.addEventListener("mouseover", function() {'
535        '  var div = document.getElementsByTagName("div")[0];'
536        '  div.innerHTML="new<br>";'
537        '});'
538        'return div;')
539    self._driver.MouseMoveTo(div, 10, 10)
540    self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
541
542  def testMouseClick(self):
543    div = self._driver.ExecuteScript(
544        'document.body.innerHTML = "<div>old</div>";'
545        'var div = document.getElementsByTagName("div")[0];'
546        'div.style["width"] = "100px";'
547        'div.style["height"] = "100px";'
548        'div.addEventListener("click", function() {'
549        '  var div = document.getElementsByTagName("div")[0];'
550        '  div.innerHTML="new<br>";'
551        '});'
552        'return div;')
553    self._driver.MouseMoveTo(div)
554    self._driver.MouseClick()
555    self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
556
557  def testMouseButtonDownAndUp(self):
558    self._driver.ExecuteScript(
559        'document.body.innerHTML = "<div>old</div>";'
560        'var div = document.getElementsByTagName("div")[0];'
561        'div.style["width"] = "100px";'
562        'div.style["height"] = "100px";'
563        'div.addEventListener("mousedown", function() {'
564        '  var div = document.getElementsByTagName("div")[0];'
565        '  div.innerHTML="new1<br>";'
566        '});'
567        'div.addEventListener("mouseup", function() {'
568        '  var div = document.getElementsByTagName("div")[0];'
569        '  div.innerHTML="new2<a></a>";'
570        '});')
571    self._driver.MouseMoveTo(None, 50, 50)
572    self._driver.MouseButtonDown()
573    self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
574    self._driver.MouseButtonUp()
575    self.assertEquals(1, len(self._driver.FindElements('tag name', 'a')))
576
577  def testMouseDoubleClick(self):
578    div = self._driver.ExecuteScript(
579        'document.body.innerHTML = "<div>old</div>";'
580        'var div = document.getElementsByTagName("div")[0];'
581        'div.style["width"] = "100px";'
582        'div.style["height"] = "100px";'
583        'div.addEventListener("dblclick", function() {'
584        '  var div = document.getElementsByTagName("div")[0];'
585        '  div.innerHTML="new<br>";'
586        '});'
587        'return div;')
588    self._driver.MouseMoveTo(div, 1, 1)
589    self._driver.MouseDoubleClick()
590    self.assertEquals(1, len(self._driver.FindElements('tag name', 'br')))
591
592  def testAlert(self):
593    self.assertFalse(self._driver.IsAlertOpen())
594    self._driver.ExecuteScript(
595        'window.setTimeout('
596        '    function() { window.confirmed = confirm(\'HI\'); },'
597        '    0);')
598    self.assertTrue(self._driver.IsAlertOpen())
599    self.assertEquals('HI', self._driver.GetAlertMessage())
600    self._driver.HandleAlert(False)
601    self.assertFalse(self._driver.IsAlertOpen())
602    self.assertEquals(False,
603                      self._driver.ExecuteScript('return window.confirmed'))
604
605  def testShouldHandleNewWindowLoadingProperly(self):
606    """Tests that ChromeDriver determines loading correctly for new windows."""
607    self._http_server.SetDataForPath(
608        '/newwindow',
609        """
610        <html>
611        <body>
612        <a href='%s' target='_blank'>new window/tab</a>
613        </body>
614        </html>""" % self._sync_server.GetUrl())
615    self._driver.Load(self._http_server.GetUrl() + '/newwindow')
616    old_windows = self._driver.GetWindowHandles()
617    self._driver.FindElement('tagName', 'a').Click()
618    new_window = self._WaitForNewWindow(old_windows)
619    self.assertNotEqual(None, new_window)
620
621    self.assertFalse(self._driver.IsLoading())
622    self._driver.SwitchToWindow(new_window)
623    self.assertTrue(self._driver.IsLoading())
624    self._sync_server.RespondWithContent('<html>new window</html>')
625    self._driver.ExecuteScript('return 1')  # Shouldn't hang.
626
627  def testPopups(self):
628    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/empty.html'))
629    old_handles = self._driver.GetWindowHandles()
630    self._driver.ExecuteScript('window.open("about:blank")')
631    new_window_handle = self._WaitForNewWindow(old_handles)
632    self.assertNotEqual(None, new_window_handle)
633
634  def testNoSuchFrame(self):
635    self.assertRaises(chromedriver.NoSuchFrame,
636                      self._driver.SwitchToFrame, 'nosuchframe')
637    self.assertRaises(chromedriver.NoSuchFrame,
638                      self._driver.SwitchToFrame,
639                      self._driver.FindElement('tagName', 'body'))
640
641  def testWindowPosition(self):
642    position = self._driver.GetWindowPosition()
643    self._driver.SetWindowPosition(position[0], position[1])
644    self.assertEquals(position, self._driver.GetWindowPosition())
645
646    # Resize so the window isn't moved offscreen.
647    # See https://code.google.com/p/chromedriver/issues/detail?id=297.
648    self._driver.SetWindowSize(300, 300)
649
650    self._driver.SetWindowPosition(100, 200)
651    self.assertEquals([100, 200], self._driver.GetWindowPosition())
652
653  def testWindowSize(self):
654    size = self._driver.GetWindowSize()
655    self._driver.SetWindowSize(size[0], size[1])
656    self.assertEquals(size, self._driver.GetWindowSize())
657
658    self._driver.SetWindowSize(600, 400)
659    self.assertEquals([600, 400], self._driver.GetWindowSize())
660
661  def testWindowMaximize(self):
662    self._driver.SetWindowPosition(100, 200)
663    self._driver.SetWindowSize(600, 400)
664    self._driver.MaximizeWindow()
665
666    self.assertNotEqual([100, 200], self._driver.GetWindowPosition())
667    self.assertNotEqual([600, 400], self._driver.GetWindowSize())
668    # Set size first so that the window isn't moved offscreen.
669    # See https://code.google.com/p/chromedriver/issues/detail?id=297.
670    self._driver.SetWindowSize(600, 400)
671    self._driver.SetWindowPosition(100, 200)
672    self.assertEquals([100, 200], self._driver.GetWindowPosition())
673    self.assertEquals([600, 400], self._driver.GetWindowSize())
674
675  def testConsoleLogSources(self):
676    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/console_log.html'))
677    logs = self._driver.GetLog('browser')
678    self.assertEquals(len(logs), 2)
679    self.assertEquals(logs[0]['source'], 'network')
680    self.assertEquals(logs[1]['source'], 'javascript')
681
682  def testAutoReporting(self):
683    self.assertFalse(self._driver.IsAutoReporting())
684    self._driver.SetAutoReporting(True)
685    self.assertTrue(self._driver.IsAutoReporting())
686    url = self.GetHttpUrlForFile('/chromedriver/console_log.html')
687    self.assertRaisesRegexp(chromedriver.UnknownError,
688                            '.*(404|Failed to load resource).*',
689                            self._driver.Load,
690                            url)
691
692  def testContextMenuEventFired(self):
693    self._driver.Load(self.GetHttpUrlForFile('/chromedriver/context_menu.html'))
694    self._driver.MouseMoveTo(self._driver.FindElement('tagName', 'div'))
695    self._driver.MouseClick(2)
696    self.assertTrue(self._driver.ExecuteScript('return success'))
697
698  def testHasFocusOnStartup(self):
699    # Some pages (about:blank) cause Chrome to put the focus in URL bar.
700    # This breaks tests depending on focus.
701    self.assertTrue(self._driver.ExecuteScript('return document.hasFocus()'))
702
703  def testTabCrash(self):
704    # If a tab is crashed, the session will be deleted.
705    # When 31 is released, will reload the tab instead.
706    # https://code.google.com/p/chromedriver/issues/detail?id=547
707    self.assertRaises(chromedriver.UnknownError,
708                      self._driver.Load, 'chrome://crash')
709    self.assertRaises(chromedriver.NoSuchSession,
710                      self._driver.GetCurrentUrl)
711
712  def testDoesntHangOnDebugger(self):
713    self._driver.ExecuteScript('debugger;')
714
715  def testMobileEmulationDisabledByDefault(self):
716    self.assertFalse(self._driver.capabilities['mobileEmulationEnabled'])
717
718
719class ChromeDriverAndroidTest(ChromeDriverBaseTest):
720  """End to end tests for Android-specific tests."""
721
722  def testLatestAndroidAppInstalled(self):
723    if ('stable' not in _ANDROID_PACKAGE_KEY and
724        'beta' not in _ANDROID_PACKAGE_KEY):
725      return
726
727    self._driver = self.CreateDriver()
728
729    try:
730      omaha_list = json.loads(
731          urllib2.urlopen('http://omahaproxy.appspot.com/all.json').read())
732      for l in omaha_list:
733        if l['os'] != 'android':
734          continue
735        for v in l['versions']:
736          if (('stable' in v['channel'] and 'stable' in _ANDROID_PACKAGE_KEY) or
737              ('beta' in v['channel'] and 'beta' in _ANDROID_PACKAGE_KEY)):
738            self.assertEquals(v['version'],
739                              self._driver.capabilities['version'])
740            return
741      raise RuntimeError('Malformed omaha JSON')
742    except urllib2.URLError as e:
743      print 'Unable to fetch current version info from omahaproxy (%s)' % e
744
745  def testDeviceManagement(self):
746    self._drivers = [self.CreateDriver() for x in
747                     android_commands.GetAttachedDevices()]
748    self.assertRaises(chromedriver.UnknownError, self.CreateDriver)
749    self._drivers[0].Quit()
750    self._drivers[0] = self.CreateDriver()
751
752
753class ChromeSwitchesCapabilityTest(ChromeDriverBaseTest):
754  """Tests that chromedriver properly processes chromeOptions.args capabilities.
755
756  Makes sure the switches are passed to Chrome.
757  """
758
759  def testSwitchWithoutArgument(self):
760    """Tests that switch --dom-automation can be passed to Chrome.
761
762    Unless --dom-automation is specified, window.domAutomationController
763    is undefined.
764    """
765    driver = self.CreateDriver(chrome_switches=['dom-automation'])
766    self.assertNotEqual(
767        None,
768        driver.ExecuteScript('return window.domAutomationController'))
769
770
771class ChromeExtensionsCapabilityTest(ChromeDriverBaseTest):
772  """Tests that chromedriver properly processes chromeOptions.extensions."""
773
774  def _PackExtension(self, ext_path):
775    return base64.b64encode(open(ext_path, 'rb').read())
776
777  def testExtensionsInstall(self):
778    """Checks that chromedriver can take the extensions in crx format."""
779    crx_1 = os.path.join(_TEST_DATA_DIR, 'ext_test_1.crx')
780    crx_2 = os.path.join(_TEST_DATA_DIR, 'ext_test_2.crx')
781    self.CreateDriver(chrome_extensions=[self._PackExtension(crx_1),
782                                         self._PackExtension(crx_2)])
783
784  def testExtensionsInstallZip(self):
785    """Checks that chromedriver can take the extensions in zip format."""
786    zip_1 = os.path.join(_TEST_DATA_DIR, 'ext_test_1.zip')
787    self.CreateDriver(chrome_extensions=[self._PackExtension(zip_1)])
788
789  def testWaitsForExtensionToLoad(self):
790    did_load_event = threading.Event()
791    server = webserver.SyncWebServer()
792    def RunServer():
793      time.sleep(5)
794      server.RespondWithContent('<html>iframe</html>')
795      did_load_event.set()
796
797    thread = threading.Thread(target=RunServer)
798    thread.daemon = True
799    thread.start()
800    crx = os.path.join(_TEST_DATA_DIR, 'ext_slow_loader.crx')
801    driver = self.CreateDriver(
802        chrome_switches=['user-agent=' + server.GetUrl()],
803        chrome_extensions=[self._PackExtension(crx)])
804    self.assertTrue(did_load_event.is_set())
805
806
807class ChromeLogPathCapabilityTest(ChromeDriverBaseTest):
808  """Tests that chromedriver properly processes chromeOptions.logPath."""
809
810  LOG_MESSAGE = 'Welcome to ChromeLogPathCapabilityTest!'
811
812  def testChromeLogPath(self):
813    """Checks that user can specify the path of the chrome log.
814
815    Verifies that a log message is written into the specified log file.
816    """
817    tmp_log_path = tempfile.NamedTemporaryFile()
818    driver = self.CreateDriver(chrome_log_path=tmp_log_path.name)
819    driver.ExecuteScript('console.info("%s")' % self.LOG_MESSAGE)
820    driver.Quit()
821    self.assertTrue(self.LOG_MESSAGE in open(tmp_log_path.name).read())
822
823
824class MobileEmulationCapabilityTest(ChromeDriverBaseTest):
825  """Tests that ChromeDriver processes chromeOptions.mobileEmulation.
826
827  Makes sure the device metrics are overridden in DevTools and user agent is
828  overridden in Chrome.
829  """
830
831  @staticmethod
832  def GlobalSetUp():
833    def respondWithUserAgentString(request):
834      return request.GetHeader('User-Agent')
835
836    MobileEmulationCapabilityTest._http_server = webserver.WebServer(
837        chrome_paths.GetTestData())
838    MobileEmulationCapabilityTest._http_server.SetCallbackForPath(
839        '/userAgent', respondWithUserAgentString)
840
841  @staticmethod
842  def GlobalTearDown():
843    MobileEmulationCapabilityTest._http_server.Shutdown()
844
845  def testDeviceMetrics(self):
846    driver = self.CreateDriver(
847        mobile_emulation = {
848            'deviceMetrics': {'width': 360, 'height': 640, 'pixelRatio': 3}})
849    self.assertTrue(driver.capabilities['mobileEmulationEnabled'])
850    self.assertEqual(360, driver.ExecuteScript('return window.innerWidth'))
851    self.assertEqual(640, driver.ExecuteScript('return window.innerHeight'))
852
853  def testUserAgent(self):
854    driver = self.CreateDriver(
855        mobile_emulation = {'userAgent': 'Agent Smith'})
856    driver.Load(self._http_server.GetUrl() + '/userAgent')
857    body_tag = driver.FindElement('tag name', 'body')
858    self.assertEqual("Agent Smith", body_tag.GetText())
859
860  def testDeviceName(self):
861    driver = self.CreateDriver(
862        mobile_emulation = {'deviceName': 'Google Nexus 5'})
863    driver.Load(self._http_server.GetUrl() + '/userAgent')
864    self.assertEqual(360, driver.ExecuteScript('return window.innerWidth'))
865    self.assertEqual(640, driver.ExecuteScript('return window.innerHeight'))
866    body_tag = driver.FindElement('tag name', 'body')
867    self.assertEqual(
868        'Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) AppleW'
869        'ebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/53'
870        '5.19',
871        body_tag.GetText())
872
873
874class ChromeDriverLogTest(unittest.TestCase):
875  """Tests that chromedriver produces the expected log file."""
876
877  UNEXPECTED_CHROMEOPTION_CAP = 'unexpected_chromeoption_capability'
878  LOG_MESSAGE = 'unrecognized chrome option: %s' % UNEXPECTED_CHROMEOPTION_CAP
879
880  def testChromeDriverLog(self):
881    _, tmp_log_path = tempfile.mkstemp(prefix='chromedriver_log_')
882    chromedriver_server = server.Server(
883        _CHROMEDRIVER_BINARY, log_path=tmp_log_path)
884    try:
885      driver = chromedriver.ChromeDriver(
886          chromedriver_server.GetUrl(), chrome_binary=_CHROME_BINARY,
887          experimental_options={ self.UNEXPECTED_CHROMEOPTION_CAP : 1 })
888      driver.Quit()
889    except chromedriver.ChromeDriverException, e:
890      self.assertTrue(self.LOG_MESSAGE in e.message)
891    finally:
892      chromedriver_server.Kill()
893    with open(tmp_log_path, 'r') as f:
894      self.assertTrue(self.LOG_MESSAGE in f.read())
895
896
897class SessionHandlingTest(ChromeDriverBaseTest):
898  """Tests for session operations."""
899  def testQuitASessionMoreThanOnce(self):
900    driver = self.CreateDriver()
901    driver.Quit()
902    driver.Quit()
903
904
905class RemoteBrowserTest(ChromeDriverBaseTest):
906  """Tests for ChromeDriver remote browser capability."""
907  def setUp(self):
908    self.assertTrue(_CHROME_BINARY is not None,
909                    'must supply a chrome binary arg')
910
911  def testConnectToRemoteBrowser(self):
912    port = self.FindFreePort()
913    temp_dir = util.MakeTempDir()
914    process = subprocess.Popen([_CHROME_BINARY,
915                                '--remote-debugging-port=%d' % port,
916                                '--user-data-dir=%s' % temp_dir])
917    if process is None:
918      raise RuntimeError('Chrome could not be started with debugging port')
919    try:
920      driver = self.CreateDriver(debugger_address='127.0.0.1:%d' % port)
921      driver.ExecuteScript('console.info("%s")' % 'connecting at %d!' % port)
922      driver.Quit()
923    finally:
924      process.terminate()
925
926  def FindFreePort(self):
927    for port in range(10000, 10100):
928      try:
929        socket.create_connection(('127.0.0.1', port), 0.2).close()
930      except socket.error:
931        return port
932    raise RuntimeError('Cannot find open port')
933
934class PerfTest(ChromeDriverBaseTest):
935  """Tests for ChromeDriver perf."""
936  def setUp(self):
937    self.assertTrue(_REFERENCE_CHROMEDRIVER is not None,
938                    'must supply a reference-chromedriver arg')
939
940  def _RunDriverPerfTest(self, name, test_func):
941    """Runs a perf test comparing a reference and new ChromeDriver server.
942
943    Args:
944      name: The name of the perf test.
945      test_func: Called with the server url to perform the test action. Must
946                 return the time elapsed.
947    """
948    class Results(object):
949      ref = []
950      new = []
951
952    ref_server = server.Server(_REFERENCE_CHROMEDRIVER)
953    results = Results()
954    result_url_pairs = zip([results.new, results.ref],
955                           [_CHROMEDRIVER_SERVER_URL, ref_server.GetUrl()])
956    for iteration in range(30):
957      for result, url in result_url_pairs:
958        result += [test_func(url)]
959      # Reverse the order for the next run.
960      result_url_pairs = result_url_pairs[::-1]
961
962    def PrintResult(build, result):
963      mean = sum(result) / len(result)
964      avg_dev = sum([abs(sample - mean) for sample in result]) / len(result)
965      print 'perf result', build, name, mean, avg_dev, result
966      util.AddBuildStepText('%s %s: %.3f+-%.3f' % (
967          build, name, mean, avg_dev))
968
969    # Discard first result, which may be off due to cold start.
970    PrintResult('new', results.new[1:])
971    PrintResult('ref', results.ref[1:])
972
973  def testSessionStartTime(self):
974    def Run(url):
975      start = time.time()
976      driver = self.CreateDriver(url)
977      end = time.time()
978      driver.Quit()
979      return end - start
980    self._RunDriverPerfTest('session start', Run)
981
982  def testSessionStopTime(self):
983    def Run(url):
984      driver = self.CreateDriver(url)
985      start = time.time()
986      driver.Quit()
987      end = time.time()
988      return end - start
989    self._RunDriverPerfTest('session stop', Run)
990
991  def testColdExecuteScript(self):
992    def Run(url):
993      driver = self.CreateDriver(url)
994      start = time.time()
995      driver.ExecuteScript('return 1')
996      end = time.time()
997      driver.Quit()
998      return end - start
999    self._RunDriverPerfTest('cold exe js', Run)
1000
1001if __name__ == '__main__':
1002  parser = optparse.OptionParser()
1003  parser.add_option(
1004      '', '--chromedriver',
1005      help='Path to chromedriver server (REQUIRED!)')
1006  parser.add_option(
1007      '', '--log-path',
1008      help='Output verbose server logs to this file')
1009  parser.add_option(
1010      '', '--reference-chromedriver',
1011      help='Path to the reference chromedriver server')
1012  parser.add_option(
1013      '', '--chrome', help='Path to a build of the chrome binary')
1014  parser.add_option(
1015      '', '--chrome-version', default='HEAD',
1016      help='Version of chrome. Default is \'HEAD\'.')
1017  parser.add_option(
1018      '', '--filter', type='string', default='*',
1019      help=('Filter for specifying what tests to run, "*" will run all. E.g., '
1020            '*testStartStop'))
1021  parser.add_option(
1022      '', '--android-package',
1023      help=('Android package key. Possible values: ' +
1024            str(_ANDROID_NEGATIVE_FILTER.keys())))
1025  options, args = parser.parse_args()
1026
1027  options.chromedriver = util.GetAbsolutePathOfUserPath(options.chromedriver)
1028  if not options.chromedriver or not os.path.exists(options.chromedriver):
1029    parser.error('chromedriver is required or the given path is invalid.' +
1030                 'Please run "%s --help" for help' % __file__)
1031
1032  global _CHROMEDRIVER_BINARY
1033  _CHROMEDRIVER_BINARY = options.chromedriver
1034
1035  if (options.android_package and
1036      options.android_package not in _ANDROID_NEGATIVE_FILTER):
1037    parser.error('Invalid --android-package')
1038
1039  chromedriver_server = server.Server(_CHROMEDRIVER_BINARY, options.log_path)
1040  global _CHROMEDRIVER_SERVER_URL
1041  _CHROMEDRIVER_SERVER_URL = chromedriver_server.GetUrl()
1042
1043  global _REFERENCE_CHROMEDRIVER
1044  _REFERENCE_CHROMEDRIVER = util.GetAbsolutePathOfUserPath(
1045      options.reference_chromedriver)
1046
1047  global _CHROME_BINARY
1048  if options.chrome:
1049    _CHROME_BINARY = util.GetAbsolutePathOfUserPath(options.chrome)
1050  else:
1051    _CHROME_BINARY = None
1052
1053  global _ANDROID_PACKAGE_KEY
1054  _ANDROID_PACKAGE_KEY = options.android_package
1055
1056  if options.filter == '*':
1057    if _ANDROID_PACKAGE_KEY:
1058      negative_filter = _ANDROID_NEGATIVE_FILTER[_ANDROID_PACKAGE_KEY]
1059    else:
1060      negative_filter = _GetDesktopNegativeFilter(options.chrome_version)
1061    options.filter = '*-' + ':__main__.'.join([''] + negative_filter)
1062
1063  all_tests_suite = unittest.defaultTestLoader.loadTestsFromModule(
1064      sys.modules[__name__])
1065  tests = unittest_util.FilterTestSuite(all_tests_suite, options.filter)
1066  ChromeDriverTest.GlobalSetUp()
1067  MobileEmulationCapabilityTest.GlobalSetUp()
1068  result = unittest.TextTestRunner(stream=sys.stdout, verbosity=2).run(tests)
1069  ChromeDriverTest.GlobalTearDown()
1070  MobileEmulationCapabilityTest.GlobalTearDown()
1071  sys.exit(len(result.failures) + len(result.errors))
1072