Home | History | Annotate | Download | only in actions
      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 import mock
      5 import unittest
      6 
      7 from telemetry.core import exceptions
      8 from telemetry import decorators
      9 from telemetry.internal.actions import action_runner as action_runner_module
     10 from telemetry.internal.actions import page_action
     11 from telemetry.testing import tab_test_case
     12 from telemetry.timeline import chrome_trace_category_filter
     13 from telemetry.timeline import model
     14 from telemetry.timeline import tracing_config
     15 from telemetry.web_perf import timeline_interaction_record as tir_module
     16 
     17 import py_utils
     18 
     19 
     20 class ActionRunnerInteractionTest(tab_test_case.TabTestCase):
     21 
     22   def GetInteractionRecords(self, trace_data):
     23     timeline_model = model.TimelineModel(trace_data)
     24     renderer_thread = timeline_model.GetRendererThreadFromTabId(self._tab.id)
     25     return [
     26         tir_module.TimelineInteractionRecord.FromAsyncEvent(e)
     27         for e in renderer_thread.async_slices
     28         if tir_module.IsTimelineInteractionRecord(e.name)
     29         ]
     30 
     31   def VerifyIssuingInteractionRecords(self, **interaction_kwargs):
     32     action_runner = action_runner_module.ActionRunner(self._tab,
     33                                                       skip_waits=True)
     34     self.Navigate('interaction_enabled_page.html')
     35     action_runner.Wait(1)
     36     config = tracing_config.TracingConfig()
     37     config.chrome_trace_config.SetLowOverheadFilter()
     38     config.enable_chrome_trace = True
     39     self._browser.platform.tracing_controller.StartTracing(config)
     40     with action_runner.CreateInteraction('InteractionName',
     41                                                  **interaction_kwargs):
     42       pass
     43     trace_data = self._browser.platform.tracing_controller.StopTracing()
     44 
     45     records = self.GetInteractionRecords(trace_data)
     46     self.assertEqual(
     47         1, len(records),
     48         'Failed to issue the interaction record on the tracing timeline.'
     49         ' Trace data:\n%s' % repr(trace_data._raw_data))
     50     self.assertEqual('InteractionName', records[0].label)
     51     for attribute_name in interaction_kwargs:
     52       self.assertTrue(getattr(records[0], attribute_name))
     53 
     54   # Test disabled for android: crbug.com/437057
     55   # Test disabled for linux: crbug.com/513874
     56   @decorators.Disabled('android', 'chromeos', 'linux')
     57   def testIssuingMultipleMeasurementInteractionRecords(self):
     58     self.VerifyIssuingInteractionRecords(repeatable=True)
     59 
     60 
     61 class ActionRunnerMeasureMemoryTest(tab_test_case.TabTestCase):
     62   def setUp(self):
     63     super(ActionRunnerMeasureMemoryTest, self).setUp()
     64     self.action_runner = action_runner_module.ActionRunner(self._tab,
     65                                                            skip_waits=True)
     66     self.Navigate('blank.html')
     67 
     68   def testWithoutTracing(self):
     69     with mock.patch.object(self._tab.browser, 'DumpMemory') as mock_method:
     70       self.assertIsNone(self.action_runner.MeasureMemory())
     71       self.assertFalse(mock_method.called)  # No-op with no tracing.
     72 
     73   def _testWithTracing(self, deterministic_mode=False):
     74     trace_memory = chrome_trace_category_filter.ChromeTraceCategoryFilter(
     75         filter_string='-*,blink.console,disabled-by-default-memory-infra')
     76     config = tracing_config.TracingConfig()
     77     config.enable_chrome_trace = True
     78     config.chrome_trace_config.SetCategoryFilter(trace_memory)
     79     self._browser.platform.tracing_controller.StartTracing(config)
     80     try:
     81       dump_id = self.action_runner.MeasureMemory(deterministic_mode)
     82     finally:
     83       trace_data = self._browser.platform.tracing_controller.StopTracing()
     84 
     85     # If successful, i.e. we haven't balied out due to an exception, check
     86     # that we can find our dump in the trace.
     87     self.assertIsNotNone(dump_id)
     88     timeline_model = model.TimelineModel(trace_data)
     89     dump_ids = (d.dump_id for d in timeline_model.IterGlobalMemoryDumps())
     90     self.assertIn(dump_id, dump_ids)
     91 
     92   # TODO(perezju): Enable when reference browser is >= M53
     93   # https://github.com/catapult-project/catapult/issues/2610
     94   @decorators.Disabled('reference')
     95   def testDeterministicMode(self):
     96     self._testWithTracing(deterministic_mode=True)
     97 
     98   # TODO(perezju): Enable when reference browser is >= M53
     99   # https://github.com/catapult-project/catapult/issues/2610
    100   @decorators.Disabled('reference')
    101   def testRealisticMode(self):
    102     with mock.patch.object(
    103         self.action_runner, 'ForceGarbageCollection') as mock_method:
    104       self._testWithTracing(deterministic_mode=False)
    105       self.assertFalse(mock_method.called)  # No forced GC in "realistic" mode.
    106 
    107   def testWithFailedDump(self):
    108     with mock.patch.object(self._tab.browser, 'DumpMemory') as mock_method:
    109       mock_method.return_value = False  # Dump fails!
    110       with self.assertRaises(exceptions.Error):
    111         self._testWithTracing()
    112 
    113 
    114 class ActionRunnerTest(tab_test_case.TabTestCase):
    115   def testExecuteJavaScript(self):
    116     action_runner = action_runner_module.ActionRunner(self._tab,
    117                                                       skip_waits=True)
    118     self.Navigate('blank.html')
    119     action_runner.ExecuteJavaScript('var testing = 42;')
    120     self.assertEqual(42, self._tab.EvaluateJavaScript('testing'))
    121 
    122   def testWaitForNavigate(self):
    123     self.Navigate('page_with_link.html')
    124     action_runner = action_runner_module.ActionRunner(self._tab,
    125                                                       skip_waits=True)
    126     action_runner.ClickElement('#clickme')
    127     action_runner.WaitForNavigate()
    128 
    129     self.assertTrue(self._tab.EvaluateJavaScript(
    130         'document.readyState == "interactive" || '
    131         'document.readyState == "complete"'))
    132     self.assertEqual(
    133         self._tab.EvaluateJavaScript('document.location.pathname;'),
    134         '/blank.html')
    135 
    136   def testNavigateBack(self):
    137     action_runner = action_runner_module.ActionRunner(self._tab,
    138                                                       skip_waits=True)
    139     self.Navigate('page_with_link.html')
    140     action_runner.WaitForJavaScriptCondition(
    141         'document.location.pathname === "/page_with_link.html"')
    142 
    143     # Test that after 3 navigations & 3 back navs, we have to be back at the
    144     # initial page
    145     self.Navigate('page_with_swipeables.html')
    146     action_runner.WaitForJavaScriptCondition(
    147         'document.location.pathname === "/page_with_swipeables.html"')
    148 
    149     self.Navigate('blank.html')
    150     action_runner.WaitForJavaScriptCondition(
    151         'document.location.pathname === "/blank.html"')
    152 
    153     self.Navigate('page_with_swipeables.html')
    154     action_runner.WaitForJavaScriptCondition(
    155         'document.location.pathname === "/page_with_swipeables.html"')
    156 
    157     action_runner.NavigateBack()
    158     action_runner.WaitForJavaScriptCondition(
    159         'document.location.pathname === "/blank.html"')
    160 
    161     action_runner.NavigateBack()
    162     action_runner.WaitForJavaScriptCondition(
    163         'document.location.pathname === "/page_with_swipeables.html"')
    164 
    165     action_runner.NavigateBack()
    166     action_runner.WaitForJavaScriptCondition(
    167         'document.location.pathname === "/page_with_link.html"')
    168 
    169   def testWait(self):
    170     action_runner = action_runner_module.ActionRunner(self._tab)
    171     self.Navigate('blank.html')
    172 
    173     action_runner.ExecuteJavaScript(
    174         'window.setTimeout(function() { window.testing = 101; }, 50);')
    175     action_runner.Wait(0.1)
    176     self.assertEqual(101, self._tab.EvaluateJavaScript('window.testing'))
    177 
    178     action_runner.ExecuteJavaScript(
    179         'window.setTimeout(function() { window.testing = 102; }, 100);')
    180     action_runner.Wait(0.2)
    181     self.assertEqual(102, self._tab.EvaluateJavaScript('window.testing'))
    182 
    183   def testWaitForJavaScriptCondition(self):
    184     action_runner = action_runner_module.ActionRunner(self._tab,
    185                                                       skip_waits=True)
    186     self.Navigate('blank.html')
    187 
    188     action_runner.ExecuteJavaScript('window.testing = 219;')
    189     action_runner.WaitForJavaScriptCondition(
    190         'window.testing == 219', timeout=0.1)
    191     action_runner.ExecuteJavaScript(
    192         'window.setTimeout(function() { window.testing = 220; }, 50);')
    193     action_runner.WaitForJavaScriptCondition(
    194         'window.testing == 220', timeout=0.1)
    195     self.assertEqual(220, self._tab.EvaluateJavaScript('window.testing'))
    196 
    197   def testWaitForJavaScriptCondition_returnsValue(self):
    198     action_runner = action_runner_module.ActionRunner(self._tab,
    199                                                       skip_waits=True)
    200     self.Navigate('blank.html')
    201 
    202     action_runner.ExecuteJavaScript('window.testing = 0;')
    203     action_runner.WaitForJavaScriptCondition(
    204         'window.testing == 0', timeout=0.1)
    205     action_runner.ExecuteJavaScript(
    206         'window.setTimeout(function() { window.testing = 42; }, 50);')
    207     self.assertEqual(
    208         42,
    209         action_runner.WaitForJavaScriptCondition('window.testing', timeout=10))
    210 
    211   def testWaitForElement(self):
    212     action_runner = action_runner_module.ActionRunner(self._tab,
    213                                                       skip_waits=True)
    214     self.Navigate('blank.html')
    215 
    216     action_runner.ExecuteJavaScript(
    217         '(function() {'
    218         '  var el = document.createElement("div");'
    219         '  el.id = "test1";'
    220         '  el.textContent = "foo";'
    221         '  document.body.appendChild(el);'
    222         '})()')
    223     action_runner.WaitForElement('#test1', timeout_in_seconds=0.1)
    224     action_runner.WaitForElement(text='foo', timeout_in_seconds=0.1)
    225     action_runner.WaitForElement(
    226         element_function='document.getElementById("test1")')
    227     action_runner.ExecuteJavaScript(
    228         'window.setTimeout(function() {'
    229         '  var el = document.createElement("div");'
    230         '  el.id = "test2";'
    231         '  document.body.appendChild(el);'
    232         '}, 50)')
    233     action_runner.WaitForElement('#test2', timeout_in_seconds=0.1)
    234     action_runner.ExecuteJavaScript(
    235         'window.setTimeout(function() {'
    236         '  document.getElementById("test2").textContent = "bar";'
    237         '}, 50)')
    238     action_runner.WaitForElement(text='bar', timeout_in_seconds=0.1)
    239     action_runner.ExecuteJavaScript(
    240         'window.setTimeout(function() {'
    241         '  var el = document.createElement("div");'
    242         '  el.id = "test3";'
    243         '  document.body.appendChild(el);'
    244         '}, 50)')
    245     action_runner.WaitForElement(
    246         element_function='document.getElementById("test3")')
    247 
    248   def testWaitForElementWithWrongText(self):
    249     action_runner = action_runner_module.ActionRunner(self._tab,
    250                                                       skip_waits=True)
    251     self.Navigate('blank.html')
    252 
    253     action_runner.ExecuteJavaScript(
    254         '(function() {'
    255         '  var el = document.createElement("div");'
    256         '  el.id = "test1";'
    257         '  el.textContent = "foo";'
    258         '  document.body.appendChild(el);'
    259         '})()')
    260     action_runner.WaitForElement('#test1', timeout_in_seconds=0.2)
    261     def WaitForElement():
    262       action_runner.WaitForElement(text='oo', timeout_in_seconds=0.2)
    263     self.assertRaises(py_utils.TimeoutException, WaitForElement)
    264 
    265   def testClickElement(self):
    266     self.Navigate('page_with_clickables.html')
    267     action_runner = action_runner_module.ActionRunner(self._tab,
    268                                                       skip_waits=True)
    269 
    270     action_runner.ExecuteJavaScript('valueSettableByTest = 1;')
    271     action_runner.ClickElement('#test')
    272     self.assertEqual(1, action_runner.EvaluateJavaScript('valueToTest'))
    273 
    274     action_runner.ExecuteJavaScript('valueSettableByTest = 2;')
    275     action_runner.ClickElement(text='Click/tap me')
    276     self.assertEqual(2, action_runner.EvaluateJavaScript('valueToTest'))
    277 
    278     action_runner.ExecuteJavaScript('valueSettableByTest = 3;')
    279     action_runner.ClickElement(
    280         element_function='document.body.firstElementChild;')
    281     self.assertEqual(3, action_runner.EvaluateJavaScript('valueToTest'))
    282 
    283     def WillFail():
    284       action_runner.ClickElement('#notfound')
    285     self.assertRaises(exceptions.EvaluateException, WillFail)
    286 
    287   @decorators.Disabled('android', 'debug',  # crbug.com/437068
    288                        'chromeos',          # crbug.com/483212
    289                        'win')               # catapult/issues/2282
    290   def testTapElement(self):
    291     self.Navigate('page_with_clickables.html')
    292     action_runner = action_runner_module.ActionRunner(self._tab,
    293                                                       skip_waits=True)
    294 
    295     action_runner.ExecuteJavaScript('valueSettableByTest = 1;')
    296     action_runner.TapElement('#test')
    297     self.assertEqual(1, action_runner.EvaluateJavaScript('valueToTest'))
    298 
    299     action_runner.ExecuteJavaScript('valueSettableByTest = 2;')
    300     action_runner.TapElement(text='Click/tap me')
    301     self.assertEqual(2, action_runner.EvaluateJavaScript('valueToTest'))
    302 
    303     action_runner.ExecuteJavaScript('valueSettableByTest = 3;')
    304     action_runner.TapElement(
    305         element_function='document.body.firstElementChild')
    306     self.assertEqual(3, action_runner.EvaluateJavaScript('valueToTest'))
    307 
    308     def WillFail():
    309       action_runner.TapElement('#notfound')
    310     self.assertRaises(exceptions.EvaluateException, WillFail)
    311 
    312   # https://github.com/catapult-project/catapult/issues/3099
    313   @decorators.Disabled('android')
    314   def testScrollToElement(self):
    315     self.Navigate('page_with_swipeables.html')
    316     action_runner = action_runner_module.ActionRunner(self._tab,
    317                                                       skip_waits=True)
    318 
    319     off_screen_element = 'document.querySelectorAll("#off-screen")[0]'
    320     top_bottom_element = 'document.querySelector("#top-bottom")'
    321 
    322     def viewport_comparator(element):
    323       return action_runner.EvaluateJavaScript('''
    324           (function(elem) {
    325             var rect = elem.getBoundingClientRect();
    326 
    327             if (rect.bottom < 0) {
    328               // The bottom of the element is above the viewport.
    329               return -1;
    330             }
    331             if (rect.top - window.innerHeight > 0) {
    332               // rect.top provides the pixel offset of the element from the
    333               // top of the page. Because that exceeds the viewport's height,
    334               // we know that the element is below the viewport.
    335               return 1;
    336             }
    337             return 0;
    338           })({{ @element }});
    339           ''', element=element)
    340 
    341 
    342     self.assertEqual(viewport_comparator(off_screen_element), 1)
    343     action_runner.ScrollPageToElement(selector='#off-screen',
    344                                       speed_in_pixels_per_second=5000)
    345     self.assertEqual(viewport_comparator(off_screen_element), 0)
    346 
    347     self.assertEqual(viewport_comparator(top_bottom_element), -1)
    348     action_runner.ScrollPageToElement(selector='#top-bottom',
    349                                       container_selector='body',
    350                                       speed_in_pixels_per_second=5000)
    351     self.assertEqual(viewport_comparator(top_bottom_element), 0)
    352 
    353   @decorators.Disabled('android',   # crbug.com/437065.
    354                        'chromeos')  # crbug.com/483212.
    355   def testScroll(self):
    356     if not page_action.IsGestureSourceTypeSupported(
    357         self._tab, 'touch'):
    358       return
    359 
    360     self.Navigate('page_with_swipeables.html')
    361     action_runner = action_runner_module.ActionRunner(self._tab,
    362                                                       skip_waits=True)
    363 
    364     action_runner.ScrollElement(
    365         selector='#left-right', direction='right', left_start_ratio=0.9)
    366     self.assertTrue(action_runner.EvaluateJavaScript(
    367         'document.querySelector("#left-right").scrollLeft') > 75)
    368     action_runner.ScrollElement(
    369         selector='#top-bottom', direction='down', top_start_ratio=0.9)
    370     self.assertTrue(action_runner.EvaluateJavaScript(
    371         'document.querySelector("#top-bottom").scrollTop') > 75)
    372 
    373     action_runner.ScrollPage(direction='right', left_start_ratio=0.9,
    374                              distance=100)
    375     self.assertTrue(action_runner.EvaluateJavaScript(
    376         '(document.scrollingElement || document.body).scrollLeft') > 75)
    377 
    378   @decorators.Disabled('android',   # crbug.com/437065.
    379                        'chromeos')  # crbug.com/483212.
    380   def testSwipe(self):
    381     if not page_action.IsGestureSourceTypeSupported(
    382         self._tab, 'touch'):
    383       return
    384 
    385     self.Navigate('page_with_swipeables.html')
    386     action_runner = action_runner_module.ActionRunner(self._tab,
    387                                                       skip_waits=True)
    388 
    389     action_runner.SwipeElement(
    390         selector='#left-right', direction='left', left_start_ratio=0.9)
    391     self.assertTrue(action_runner.EvaluateJavaScript(
    392         'document.querySelector("#left-right").scrollLeft') > 75)
    393     action_runner.SwipeElement(
    394         selector='#top-bottom', direction='up', top_start_ratio=0.9)
    395     self.assertTrue(action_runner.EvaluateJavaScript(
    396         'document.querySelector("#top-bottom").scrollTop') > 75)
    397 
    398     action_runner.SwipePage(direction='left', left_start_ratio=0.9)
    399     self.assertTrue(action_runner.EvaluateJavaScript(
    400         '(document.scrollingElement || document.body).scrollLeft') > 75)
    401 
    402   def testWaitForNetworkQuiescenceSmoke(self):
    403     self.Navigate('blank.html')
    404     action_runner = action_runner_module.ActionRunner(self._tab)
    405     action_runner.WaitForNetworkQuiescence()
    406     self.assertEqual(
    407         self._tab.EvaluateJavaScript('document.location.pathname;'),
    408         '/blank.html')
    409 
    410   def testEnterText(self):
    411     self.Navigate('blank.html')
    412     self._tab.ExecuteJavaScript(
    413         '(function() {'
    414         '  var elem = document.createElement("textarea");'
    415         '  document.body.appendChild(elem);'
    416         '  elem.focus();'
    417         '})();')
    418 
    419     action_runner = action_runner_module.ActionRunner(self._tab,
    420                                                       skip_waits=True)
    421     action_runner.EnterText('That is boring')  # That is boring|.
    422     action_runner.PressKey('Home')  # |That is boring.
    423     action_runner.PressKey('ArrowRight', repeat_count=2)  # Th|at is boring.
    424     action_runner.PressKey('Delete', repeat_count=2)  # Th| is boring.
    425     action_runner.EnterText('is')  # This| is boring.
    426     action_runner.PressKey('End')  # This is boring|.
    427     action_runner.PressKey('ArrowLeft', repeat_count=3)  # This is bor|ing.
    428     action_runner.PressKey('Backspace', repeat_count=3)  # This is |ing.
    429     action_runner.EnterText('interest')  # This is interest|ing.
    430 
    431     # Check that the contents of the textarea is correct. It might take a second
    432     # until all keystrokes have been handled by the browser (crbug.com/630017).
    433     self._tab.WaitForJavaScriptCondition(
    434         'document.querySelector("textarea").value === "This is interesting"',
    435         timeout=1)
    436 
    437 
    438 class InteractionTest(unittest.TestCase):
    439 
    440   def setUp(self):
    441     self.mock_action_runner = mock.Mock(action_runner_module.ActionRunner)
    442 
    443     def expected_js_call(method):
    444       return mock.call.ExecuteJavaScript(
    445           '%s({{ marker }});' % method, marker='Interaction.ABC')
    446 
    447     self.expected_calls = [
    448         expected_js_call('console.time'),
    449         expected_js_call('console.timeEnd')]
    450 
    451   def testIssuingInteractionRecordCommand(self):
    452     with action_runner_module.Interaction(
    453         self.mock_action_runner, label='ABC', flags=[]):
    454       pass
    455     self.assertEqual(self.expected_calls, self.mock_action_runner.mock_calls)
    456 
    457   def testExceptionRaisedInWithInteraction(self):
    458     class FooException(Exception):
    459       pass
    460     # Test that the Foo exception raised in the with block is propagated to the
    461     # caller.
    462     with self.assertRaises(FooException):
    463       with action_runner_module.Interaction(
    464           self.mock_action_runner, label='ABC', flags=[]):
    465         raise FooException()
    466 
    467     # Test that the end console.timeEnd(...) isn't called because exception was
    468     # raised.
    469     self.assertEqual(
    470         self.expected_calls[:1], self.mock_action_runner.mock_calls)
    471