diff options
Diffstat (limited to 'tools/telemetry')
-rw-r--r-- | tools/telemetry/telemetry/page/actions/action_runner.py | 30 | ||||
-rw-r--r-- | tools/telemetry/telemetry/page/actions/drag.js | 70 | ||||
-rw-r--r-- | tools/telemetry/telemetry/page/actions/drag.py | 104 | ||||
-rw-r--r-- | tools/telemetry/telemetry/page/actions/drag_unittest.py | 61 | ||||
-rw-r--r-- | tools/telemetry/unittest_data/draggable.html | 52 |
5 files changed, 317 insertions, 0 deletions
diff --git a/tools/telemetry/telemetry/page/actions/action_runner.py b/tools/telemetry/telemetry/page/actions/action_runner.py index f615f98..8b1f285 100644 --- a/tools/telemetry/telemetry/page/actions/action_runner.py +++ b/tools/telemetry/telemetry/page/actions/action_runner.py @@ -6,6 +6,7 @@ import time import logging import urlparse +from telemetry.page.actions.drag import DragAction from telemetry.page.actions.javascript_click import ClickElementAction from telemetry.page.actions.loop import LoopAction from telemetry.page.actions.mouse_click import MouseClickAction @@ -239,6 +240,35 @@ class ActionRunner(object): self._RunAction(ClickElementAction( selector=selector, text=text, element_function=element_function)) + def DragPage(self, left_start_ratio, top_start_ratio, left_end_ratio, + top_end_ratio, speed_in_pixels_per_second=800, use_touch=False): + """Perform a drag gesture on the page. + + You should specify a start and an end point in ratios of page width and + height (see drag.js for full implementation). + + Args: + left_start_ratio: The horizontal starting coordinate of the + gesture, as a ratio of the visible bounding rectangle for + document.body. + top_start_ratio: The vertical starting coordinate of the + gesture, as a ratio of the visible bounding rectangle for + document.body. + left_end_ratio: The horizontal ending coordinate of the + gesture, as a ratio of the visible bounding rectangle for + document.body. + top_end_ratio: The vertical ending coordinate of the + gesture, as a ratio of the visible bounding rectangle for + document.body. + speed_in_pixels_per_second: The speed of the gesture (in pixels/s). + use_touch: Whether dragging should be done with touch input. + """ + self._RunAction(DragAction( + left_start_ratio=left_start_ratio, top_start_ratio=top_start_ratio, + left_end_ratio=left_end_ratio, top_end_ratio=top_end_ratio, + speed_in_pixels_per_second=speed_in_pixels_per_second, + use_touch=use_touch)) + def PinchPage(self, left_anchor_ratio=0.5, top_anchor_ratio=0.5, scale_factor=None, speed_in_pixels_per_second=800): """Perform the pinch gesture on the page. diff --git a/tools/telemetry/telemetry/page/actions/drag.js b/tools/telemetry/telemetry/page/actions/drag.js new file mode 100644 index 0000000..cee51ae --- /dev/null +++ b/tools/telemetry/telemetry/page/actions/drag.js @@ -0,0 +1,70 @@ +// Copyright 2015 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. + +// This file provides the DragAction object, which performs drag on a page +// using given start and end positions: +// 1. var action = new __DragAction(callback) +// 2. action.start(drag_options) +'use strict'; + +(function() { + function DragGestureOptions(opt_options) { + this.element_ = opt_options.element; + this.left_start_ratio_ = opt_options.left_start_ratio; + this.top_start_ratio_ = opt_options.top_start_ratio; + this.left_end_ratio_ = opt_options.left_end_ratio; + this.top_end_ratio_ = opt_options.top_end_ratio; + this.speed_ = opt_options.speed; + this.gesture_source_type_ = opt_options.gesture_source_type; + } + + function supportedByBrowser() { + return !!(window.chrome && + chrome.gpuBenchmarking && + chrome.gpuBenchmarking.smoothDrag); + } + + // This class performs drag action using given start and end positions, + // by a single drag gesture. + function DragAction(opt_callback) { + this.beginMeasuringHook = function() {} + this.endMeasuringHook = function() {} + + this.callback_ = opt_callback; + } + + DragAction.prototype.start = function(opt_options) { + this.options_ = new DragGestureOptions(opt_options); + requestAnimationFrame(this.startGesture_.bind(this)); + }; + + DragAction.prototype.startGesture_ = function() { + this.beginMeasuringHook(); + + var rect = __GestureCommon_GetBoundingVisibleRect(this.options_.element_); + var start_left = + rect.left + (rect.width * this.options_.left_start_ratio_); + var start_top = + rect.top + (rect.height * this.options_.top_start_ratio_); + var end_left = + rect.left + (rect.width * this.options_.left_end_ratio_); + var end_top = + rect.top + (rect.height * this.options_.top_end_ratio_); + chrome.gpuBenchmarking.smoothDrag( + start_left, start_top, end_left, end_top, + this.onGestureComplete_.bind(this), this.options_.gesture_source_type_, + this.options_.speed_); + }; + + DragAction.prototype.onGestureComplete_ = function() { + this.endMeasuringHook(); + + // We're done. + if (this.callback_) + this.callback_(); + }; + + window.__DragAction = DragAction; + window.__DragAction_SupportedByBrowser = supportedByBrowser; +})(); diff --git a/tools/telemetry/telemetry/page/actions/drag.py b/tools/telemetry/telemetry/page/actions/drag.py new file mode 100644 index 0000000..5d46e86 --- /dev/null +++ b/tools/telemetry/telemetry/page/actions/drag.py @@ -0,0 +1,104 @@ +# Copyright 2015 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. + +"""A Telemetry page_action that performs the "drag" action on pages. + +Action parameters are: +- selector: If no selector is defined then the action attempts to drag the + document element on the page. +- element_function: CSS selector used to evaluate callback when test completes +- text: The element with exact text is selected. +- left_start_ratio: ratio of start point's left coordinate to the element + width. +- top_start_ratio: ratio of start point's top coordinate to the element height. +- left_end_ratio: ratio of end point's left coordinate to the element width. +- left_end_ratio: ratio of end point's top coordinate to the element height. +- speed_in_pixels_per_second: speed of the drag gesture in pixels per second. +- use_touch: boolean value to specify if gesture should use touch input or not. +""" + +import os + +from telemetry.page.actions import page_action + + +class DragAction(page_action.PageAction): + + def __init__(self, selector=None, text=None, element_function=None, + left_start_ratio=None, top_start_ratio=None, left_end_ratio=None, + top_end_ratio=None, speed_in_pixels_per_second=800, + use_touch=False): + super(DragAction, self).__init__() + self._selector = selector + self._text = text + self._element_function = element_function + self._left_start_ratio = left_start_ratio + self._top_start_ratio = top_start_ratio + self._left_end_ratio = left_end_ratio + self._top_end_ratio = top_end_ratio + self._speed = speed_in_pixels_per_second + self._use_touch = use_touch + + def WillRunAction(self, tab): + for js_file in ['gesture_common.js', 'drag.js']: + with open(os.path.join(os.path.dirname(__file__), js_file)) as f: + js = f.read() + tab.ExecuteJavaScript(js) + + # Fail if browser doesn't support synthetic drag gestures. + if not tab.EvaluateJavaScript('window.__DragAction_SupportedByBrowser()'): + raise page_action.PageActionNotSupported( + 'Synthetic drag not supported for this browser') + + # Fail if this action requires touch and we can't send touch events. + if self._use_touch: + if not page_action.IsGestureSourceTypeSupported(tab, 'touch'): + raise page_action.PageActionNotSupported( + 'Touch drag not supported for this browser') + + if (page_action.GetGestureSourceTypeFromOptions(tab) == + 'chrome.gpuBenchmarking.MOUSE_INPUT'): + raise page_action.PageActionNotSupported( + 'Drag requires touch on this page but mouse input was requested') + + done_callback = 'function() { window.__dragActionDone = true; }' + tab.ExecuteJavaScript(''' + window.__dragActionDone = false; + window.__dragAction = new __DragAction(%s);''' + % done_callback) + + def RunAction(self, tab): + if (self._selector is None and self._text is None and + self._element_function is None): + self._element_function = 'document.body' + + gesture_source_type = 'chrome.gpuBenchmarking.TOUCH_INPUT' + if (page_action.IsGestureSourceTypeSupported(tab, 'mouse') and + not self._use_touch): + gesture_source_type = 'chrome.gpuBenchmarking.MOUSE_INPUT' + + code = ''' + function(element, info) { + if (!element) { + throw Error('Cannot find element: ' + info); + } + window.__dragAction.start({ + element: element, + left_start_ratio: %s, + top_start_ratio: %s, + left_end_ratio: %s, + top_end_ratio: %s, + speed: %s, + gesture_source_type: %s + }); + }''' % (self._left_start_ratio, + self._top_start_ratio, + self._left_end_ratio, + self._top_end_ratio, + self._speed, + gesture_source_type) + page_action.EvaluateCallbackWithElement( + tab, code, selector=self._selector, text=self._text, + element_function=self._element_function) + tab.WaitForJavaScriptExpression('window.__dragActionDone', 60) diff --git a/tools/telemetry/telemetry/page/actions/drag_unittest.py b/tools/telemetry/telemetry/page/actions/drag_unittest.py new file mode 100644 index 0000000..0fabe60 --- /dev/null +++ b/tools/telemetry/telemetry/page/actions/drag_unittest.py @@ -0,0 +1,61 @@ +# Copyright 2015 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. +import math +import os + +from telemetry.page.actions import drag +from telemetry.unittest_util import tab_test_case + + +class DragActionTest(tab_test_case.TabTestCase): + def CheckWithinRange(self, value, expected, error_ratio): + error_range = abs(expected * error_ratio) + return abs(value - expected) <= error_range + + def testDragAction(self): + self.Navigate('draggable.html') + + with open(os.path.join(os.path.dirname(__file__), + 'gesture_common.js')) as f: + js = f.read() + self._tab.ExecuteJavaScript(js) + + div_width = self._tab.EvaluateJavaScript( + '__GestureCommon_GetBoundingVisibleRect(document.body).width') + div_height = self._tab.EvaluateJavaScript( + '__GestureCommon_GetBoundingVisibleRect(document.body).height') + + i = drag.DragAction(left_start_ratio=0.5, top_start_ratio=0.5, + left_end_ratio=0.25, top_end_ratio=0.25) + i.WillRunAction(self._tab) + self._tab.ExecuteJavaScript(''' + window.__dragAction.beginMeasuringHook = function() { + window.__didBeginMeasuring = true; + }; + window.__dragAction.endMeasuringHook = function() { + window.__didEndMeasuring = true; + };''') + i.RunAction(self._tab) + + self.assertTrue(self._tab.EvaluateJavaScript('window.__didBeginMeasuring')) + self.assertTrue(self._tab.EvaluateJavaScript('window.__didEndMeasuring')) + + div_position_x = self._tab.EvaluateJavaScript( + 'document.getElementById("drag_div").offsetLeft') + div_position_y = self._tab.EvaluateJavaScript( + 'document.getElementById("drag_div").offsetTop') + + # 0.25 is the ratio of displacement to the initial size. + expected_x = math.floor(div_width * -0.25) + expected_y = math.floor(div_height * -0.25) + error_ratio = 0.1 + + self.assertTrue( + self.CheckWithinRange(div_position_x, expected_x, error_ratio), + msg="Moved element's left coordinate: %d, expected: %d" % + (div_position_x, expected_x)) + self.assertTrue( + self.CheckWithinRange(div_position_y, expected_y, error_ratio), + msg="Moved element's top coordinate: %d, expected: %d" % + (div_position_y, expected_y)) diff --git a/tools/telemetry/unittest_data/draggable.html b/tools/telemetry/unittest_data/draggable.html new file mode 100644 index 0000000..8af37ba --- /dev/null +++ b/tools/telemetry/unittest_data/draggable.html @@ -0,0 +1,52 @@ +<html> + <head> + <style> + body { margin: 0; } + #drag_div { + width: 100%; + height: 100%; + background: blue; + position: relative; + } + </style> + <script> + offsetX = 0; + offsetY = 0; + + function drag(event) { + d = document.getElementById('drag_div'); + offsetX = event.clientX - d.offsetLeft; + offsetY = event.clientY - d.offsetTop; + } + + function drop(event) { + d = document.getElementById('drag_div'); + d.style.left = event.clientX - offsetX; + d.style.top = event.clientY - offsetY; + } + + function touchStart(event) { + d = document.getElementById('drag_div'); + offsetX = event.touches[0].clientX - d.offsetLeft; + offsetY = event.touches[0].clientY - d.offsetTop; + } + + function touchEnd(event) { + d = document.getElementById('drag_div'); + d.style.left = event.changedTouches[0].clientX - offsetX; + d.style.top = event.changedTouches[0].clientY - offsetY; + } + + </script> + </head> + + <body id ="body"> + <div id="drag_div"> </div> + </body> + <script> + document.getElementById('body').addEventListener('mouseup', drop); + document.getElementById('body').addEventListener('touchend', touchEnd); + document.getElementById('drag_div').addEventListener('mousedown', drag); + document.getElementById('drag_div').addEventListener('touchstart', touchStart); + </script> +</html> |