1# Copyright 2014 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 5import time 6 7from telemetry.page.actions.javascript_click import ClickElementAction 8from telemetry.page.actions.navigate import NavigateAction 9from telemetry.page.actions.swipe import SwipeAction 10from telemetry.page.actions.tap import TapAction 11from telemetry.page.actions.wait import WaitForElementAction 12from telemetry.web_perf import timeline_interaction_record as tir_module 13 14 15class ActionRunner(object): 16 17 def __init__(self, tab): 18 self._tab = tab 19 20 # TODO(nednguyen): remove this (or make private) when 21 # crbug.com/361809 is marked fixed 22 def RunAction(self, action): 23 action.WillRunAction(self._tab) 24 action.RunAction(self._tab) 25 26 def BeginInteraction(self, label, is_smooth=False, is_responsive=False): 27 """Marks the beginning of an interaction record. 28 29 An interaction record is a labeled time period containing 30 interaction that developers care about. Each set of metrics 31 specified in flags will be calculated for this time period.. The 32 End() method in the returned object must be called once to mark 33 the end of the timeline. 34 35 Args: 36 label: A label for this particular interaction. This can be any 37 user-defined string, but must not contain '/'. 38 is_smooth: Whether to check for smoothness metrics for this interaction. 39 is_responsive: Whether to check for responsiveness metrics for 40 this interaction. 41 """ 42 flags = [] 43 if is_smooth: 44 flags.append(tir_module.IS_SMOOTH) 45 if is_responsive: 46 flags.append(tir_module.IS_RESPONSIVE) 47 48 interaction = Interaction(self._tab, label, flags) 49 interaction.Begin() 50 return interaction 51 52 def BeginGestureInteraction( 53 self, label, is_smooth=False, is_responsive=False): 54 """Marks the beginning of a gesture-based interaction record. 55 56 This is similar to normal interaction record, but it will 57 auto-narrow the interaction time period to only include the 58 synthetic gesture event output by Chrome. This is typically use to 59 reduce noise in gesture-based analysis (e.g., analysis for a 60 swipe/scroll). 61 62 The interaction record label will be prepended with 'Gesture_'. 63 64 Args: 65 label: A label for this particular interaction. This can be any 66 user-defined string, but must not contain '/'. 67 is_smooth: Whether to check for smoothness metrics for this interaction. 68 is_responsive: Whether to check for responsiveness metrics for 69 this interaction. 70 """ 71 return self.BeginInteraction('Gesture_' + label, is_smooth, is_responsive) 72 73 def NavigateToPage(self, page, timeout_seconds=None): 74 """ Navigate to the given page. 75 76 Args: 77 page: page is an instance of page.Page 78 """ 79 if page.is_file: 80 target_side_url = self._tab.browser.http_server.UrlOf(page.file_path_url) 81 else: 82 target_side_url = page.url 83 attributes = { 84 'url': target_side_url, 85 'script_to_evaluate_on_commit': page.script_to_evaluate_on_commit} 86 if timeout_seconds: 87 attributes['timeout_seconds'] = timeout_seconds 88 self.RunAction(NavigateAction(attributes)) 89 90 def WaitForNavigate(self, timeout_seconds=60): 91 self._tab.WaitForNavigate(timeout_seconds) 92 self._tab.WaitForDocumentReadyStateToBeInteractiveOrBetter() 93 94 def ExecuteJavaScript(self, statement): 95 """Executes a given JavaScript expression. Does not return the result. 96 97 Example: runner.ExecuteJavaScript('var foo = 1;'); 98 99 Args: 100 statement: The statement to execute (provided as string). 101 102 Raises: 103 EvaluationException: The statement failed to execute. 104 """ 105 self._tab.ExecuteJavaScript(statement) 106 107 def EvaluateJavaScript(self, expression): 108 """Returns the evaluation result of the given JavaScript expression. 109 110 The evaluation results must be convertible to JSON. If the result 111 is not needed, use ExecuteJavaScript instead. 112 113 Example: num = runner.EvaluateJavaScript('document.location.href') 114 115 Args: 116 expression: The expression to evaluate (provided as string). 117 118 Raises: 119 EvaluationException: The statement expression failed to execute 120 or the evaluation result can not be JSON-ized. 121 """ 122 return self._tab.EvaluateJavaScript(expression) 123 124 def Wait(self, seconds): 125 """Wait for the number of seconds specified. 126 127 Args: 128 seconds: The number of seconds to wait. 129 """ 130 time.sleep(seconds) 131 132 def WaitForJavaScriptCondition(self, condition, timeout=60): 133 """Wait for a JavaScript condition to become true. 134 135 Example: runner.WaitForJavaScriptCondition('window.foo == 10'); 136 137 Args: 138 condition: The JavaScript condition (as string). 139 timeout: The timeout in seconds (default to 60). 140 """ 141 self._tab.WaitForJavaScriptExpression(condition, timeout) 142 143 def WaitForElement(self, selector=None, text=None, element_function=None, 144 timeout=60): 145 """Wait for an element to appear in the document. 146 147 The element may be selected via selector, text, or element_function. 148 Only one of these arguments must be specified. 149 150 Args: 151 selector: A CSS selector describing the element. 152 text: The element must contains this exact text. 153 element_function: A JavaScript function (as string) that is used 154 to retrieve the element. For example: 155 '(function() { return foo.element; })()'. 156 timeout: The timeout in seconds (default to 60). 157 """ 158 self.RunAction(WaitForElementAction( 159 selector=selector, text=text, element_function=element_function, 160 timeout=timeout)) 161 162 def TapElement(self, selector=None, text=None, element_function=None): 163 """Tap an element. 164 165 The element may be selected via selector, text, or element_function. 166 Only one of these arguments must be specified. 167 168 Args: 169 selector: A CSS selector describing the element. 170 text: The element must contains this exact text. 171 element_function: A JavaScript function (as string) that is used 172 to retrieve the element. For example: 173 '(function() { return foo.element; })()'. 174 """ 175 self.RunAction(TapAction( 176 selector=selector, text=text, element_function=element_function)) 177 178 def ClickElement(self, selector=None, text=None, element_function=None): 179 """Click an element. 180 181 The element may be selected via selector, text, or element_function. 182 Only one of these arguments must be specified. 183 184 Args: 185 selector: A CSS selector describing the element. 186 text: The element must contains this exact text. 187 element_function: A JavaScript function (as string) that is used 188 to retrieve the element. For example: 189 '(function() { return foo.element; })()'. 190 """ 191 self.RunAction(ClickElementAction( 192 selector=selector, text=text, element_function=element_function)) 193 194 def SwipePage(self, left_start_ratio=0.5, top_start_ratio=0.5, 195 direction='left', distance=100, speed=800): 196 """Perform swipe gesture on the page. 197 198 Args: 199 left_start_ratio: The horizontal starting coordinate of the 200 gesture, as a ratio of the visible bounding rectangle for 201 document.body. 202 top_start_ratio: The vertical starting coordinate of the 203 gesture, as a ratio of the visible bounding rectangle for 204 document.body. 205 direction: The direction of swipe, either 'left', 'right', 206 'up', or 'down' 207 distance: The distance to swipe (in pixel). 208 speed: The speed of the gesture. 209 """ 210 self.RunAction(SwipeAction( 211 left_start_ratio=left_start_ratio, top_start_ratio=top_start_ratio, 212 direction=direction, distance=distance, speed=speed)) 213 214 def SwipeElement(self, selector=None, text=None, element_function=None, 215 left_start_ratio=0.5, top_start_ratio=0.5, 216 direction='left', distance=100, speed=800): 217 """Perform swipe gesture on the element. 218 219 Args: 220 selector: A CSS selector describing the element. 221 text: The element must contains this exact text. 222 element_function: A JavaScript function (as string) that is used 223 to retrieve the element. For example: 224 'function() { return foo.element; }'. 225 left_start_ratio: The horizontal starting coordinate of the 226 gesture, as a ratio of the visible bounding rectangle for 227 the element. 228 top_start_ratio: The vertical starting coordinate of the 229 gesture, as a ratio of the visible bounding rectangle for 230 the element. 231 direction: The direction of swipe, either 'left', 'right', 232 'up', or 'down' 233 distance: The distance to swipe (in pixel). 234 speed: The speed of the gesture. 235 """ 236 self.RunAction(SwipeAction( 237 selector=selector, text=text, element_function=element_function, 238 left_start_ratio=left_start_ratio, top_start_ratio=top_start_ratio, 239 direction=direction, distance=distance, speed=speed)) 240 241 def ForceGarbageCollection(self): 242 """Forces JavaScript garbage collection on the page.""" 243 self._tab.CollectGarbage() 244 245 def PauseInteractive(self): 246 """Pause the page execution and wait for terminal interaction. 247 248 This is typically used for debugging. You can use this to pause 249 the page execution and inspect the browser state before 250 continuing. 251 """ 252 raw_input("Interacting... Press Enter to continue.") 253 254 255class Interaction(object): 256 257 def __init__(self, action_runner, label, flags): 258 assert action_runner 259 assert label 260 assert isinstance(flags, list) 261 262 self._action_runner = action_runner 263 self._label = label 264 self._flags = flags 265 self._started = False 266 267 def Begin(self): 268 assert not self._started 269 self._started = True 270 self._action_runner.ExecuteJavaScript('console.time("%s");' % 271 tir_module.TimelineInteractionRecord.GetJavaScriptMarker( 272 self._label, self._flags)) 273 274 def End(self): 275 assert self._started 276 self._started = False 277 self._action_runner.ExecuteJavaScript('console.timeEnd("%s");' % 278 tir_module.TimelineInteractionRecord.GetJavaScriptMarker( 279 self._label, self._flags)) 280