# Copyright 2014 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 mock import unittest from telemetry.core import exceptions from telemetry import decorators from telemetry.internal.actions import action_runner as action_runner_module from telemetry.internal.actions import page_action from telemetry.testing import tab_test_case from telemetry.timeline import chrome_trace_category_filter from telemetry.timeline import model from telemetry.timeline import tracing_config from telemetry.web_perf import timeline_interaction_record as tir_module class ActionRunnerInteractionTest(tab_test_case.TabTestCase): def GetInteractionRecords(self, trace_data): timeline_model = model.TimelineModel(trace_data) renderer_thread = timeline_model.GetRendererThreadFromTabId(self._tab.id) return [ tir_module.TimelineInteractionRecord.FromAsyncEvent(e) for e in renderer_thread.async_slices if tir_module.IsTimelineInteractionRecord(e.name) ] def VerifyIssuingInteractionRecords(self, **interaction_kwargs): action_runner = action_runner_module.ActionRunner(self._tab, skip_waits=True) self.Navigate('interaction_enabled_page.html') action_runner.Wait(1) config = tracing_config.TracingConfig() config.chrome_trace_config.SetLowOverheadFilter() config.enable_chrome_trace = True self._browser.platform.tracing_controller.StartTracing(config) with action_runner.CreateInteraction('InteractionName', **interaction_kwargs): pass trace_data = self._browser.platform.tracing_controller.StopTracing() records = self.GetInteractionRecords(trace_data) self.assertEqual( 1, len(records), 'Failed to issue the interaction record on the tracing timeline.' ' Trace data:\n%s' % repr(trace_data._raw_data)) self.assertEqual('InteractionName', records[0].label) for attribute_name in interaction_kwargs: self.assertTrue(getattr(records[0], attribute_name)) # Test disabled for android: crbug.com/437057 # Test disabled for linux: crbug.com/513874 @decorators.Disabled('android', 'chromeos', 'linux') def testIssuingMultipleMeasurementInteractionRecords(self): self.VerifyIssuingInteractionRecords(repeatable=True) class ActionRunnerMeasureMemoryTest(tab_test_case.TabTestCase): def setUp(self): super(ActionRunnerMeasureMemoryTest, self).setUp() self.action_runner = action_runner_module.ActionRunner(self._tab, skip_waits=True) self.Navigate('blank.html') def testWithoutTracing(self): with mock.patch.object(self._tab.browser, 'DumpMemory') as mock_method: self.assertIsNone(self.action_runner.MeasureMemory()) self.assertFalse(mock_method.called) # No-op with no tracing. def _testWithTracing(self, deterministic_mode=False): trace_memory = chrome_trace_category_filter.ChromeTraceCategoryFilter( filter_string='-*,blink.console,disabled-by-default-memory-infra') config = tracing_config.TracingConfig() config.enable_chrome_trace = True config.chrome_trace_config.SetCategoryFilter(trace_memory) self._browser.platform.tracing_controller.StartTracing(config) try: dump_id = self.action_runner.MeasureMemory(deterministic_mode) finally: trace_data = self._browser.platform.tracing_controller.StopTracing() # If successful, i.e. we haven't balied out due to an exception, check # that we can find our dump in the trace. self.assertIsNotNone(dump_id) timeline_model = model.TimelineModel(trace_data) dump_ids = (d.dump_id for d in timeline_model.IterGlobalMemoryDumps()) self.assertIn(dump_id, dump_ids) # TODO(perezju): Enable when reference browser is >= M53 # https://github.com/catapult-project/catapult/issues/2610 @decorators.Disabled('reference') def testDeterministicMode(self): self._testWithTracing(deterministic_mode=True) # TODO(perezju): Enable when reference browser is >= M53 # https://github.com/catapult-project/catapult/issues/2610 @decorators.Disabled('reference') def testRealisticMode(self): with mock.patch.object( self.action_runner, 'ForceGarbageCollection') as mock_method: self._testWithTracing(deterministic_mode=False) self.assertFalse(mock_method.called) # No forced GC in "realistic" mode. def testWithFailedDump(self): with mock.patch.object(self._tab.browser, 'DumpMemory') as mock_method: mock_method.return_value = False # Dump fails! with self.assertRaises(AssertionError): self._testWithTracing() class ActionRunnerTest(tab_test_case.TabTestCase): def testExecuteJavaScript(self): action_runner = action_runner_module.ActionRunner(self._tab, skip_waits=True) self.Navigate('blank.html') action_runner.ExecuteJavaScript('var testing = 42;') self.assertEqual(42, self._tab.EvaluateJavaScript('testing')) def testWaitForNavigate(self): self.Navigate('page_with_link.html') action_runner = action_runner_module.ActionRunner(self._tab, skip_waits=True) action_runner.ClickElement('#clickme') action_runner.WaitForNavigate() self.assertTrue(self._tab.EvaluateJavaScript( 'document.readyState == "interactive" || ' 'document.readyState == "complete"')) self.assertEqual( self._tab.EvaluateJavaScript('document.location.pathname;'), '/blank.html') def testWait(self): action_runner = action_runner_module.ActionRunner(self._tab) self.Navigate('blank.html') action_runner.ExecuteJavaScript( 'window.setTimeout(function() { window.testing = 101; }, 50);') action_runner.Wait(0.1) self.assertEqual(101, self._tab.EvaluateJavaScript('window.testing')) action_runner.ExecuteJavaScript( 'window.setTimeout(function() { window.testing = 102; }, 100);') action_runner.Wait(0.2) self.assertEqual(102, self._tab.EvaluateJavaScript('window.testing')) def testWaitForJavaScriptCondition(self): action_runner = action_runner_module.ActionRunner(self._tab, skip_waits=True) self.Navigate('blank.html') action_runner.ExecuteJavaScript('window.testing = 219;') action_runner.WaitForJavaScriptCondition( 'window.testing == 219', timeout_in_seconds=0.1) action_runner.ExecuteJavaScript( 'window.setTimeout(function() { window.testing = 220; }, 50);') action_runner.WaitForJavaScriptCondition( 'window.testing == 220', timeout_in_seconds=0.1) self.assertEqual(220, self._tab.EvaluateJavaScript('window.testing')) def testWaitForElement(self): action_runner = action_runner_module.ActionRunner(self._tab, skip_waits=True) self.Navigate('blank.html') action_runner.ExecuteJavaScript( '(function() {' ' var el = document.createElement("div");' ' el.id = "test1";' ' el.textContent = "foo";' ' document.body.appendChild(el);' '})()') action_runner.WaitForElement('#test1', timeout_in_seconds=0.1) action_runner.WaitForElement(text='foo', timeout_in_seconds=0.1) action_runner.WaitForElement( element_function='document.getElementById("test1")') action_runner.ExecuteJavaScript( 'window.setTimeout(function() {' ' var el = document.createElement("div");' ' el.id = "test2";' ' document.body.appendChild(el);' '}, 50)') action_runner.WaitForElement('#test2', timeout_in_seconds=0.1) action_runner.ExecuteJavaScript( 'window.setTimeout(function() {' ' document.getElementById("test2").textContent = "bar";' '}, 50)') action_runner.WaitForElement(text='bar', timeout_in_seconds=0.1) action_runner.ExecuteJavaScript( 'window.setTimeout(function() {' ' var el = document.createElement("div");' ' el.id = "test3";' ' document.body.appendChild(el);' '}, 50)') action_runner.WaitForElement( element_function='document.getElementById("test3")') def testWaitForElementWithWrongText(self): action_runner = action_runner_module.ActionRunner(self._tab, skip_waits=True) self.Navigate('blank.html') action_runner.ExecuteJavaScript( '(function() {' ' var el = document.createElement("div");' ' el.id = "test1";' ' el.textContent = "foo";' ' document.body.appendChild(el);' '})()') action_runner.WaitForElement('#test1', timeout_in_seconds=0.2) def WaitForElement(): action_runner.WaitForElement(text='oo', timeout_in_seconds=0.2) self.assertRaises(exceptions.TimeoutException, WaitForElement) def testClickElement(self): self.Navigate('page_with_clickables.html') action_runner = action_runner_module.ActionRunner(self._tab, skip_waits=True) action_runner.ExecuteJavaScript('valueSettableByTest = 1;') action_runner.ClickElement('#test') self.assertEqual(1, action_runner.EvaluateJavaScript('valueToTest')) action_runner.ExecuteJavaScript('valueSettableByTest = 2;') action_runner.ClickElement(text='Click/tap me') self.assertEqual(2, action_runner.EvaluateJavaScript('valueToTest')) action_runner.ExecuteJavaScript('valueSettableByTest = 3;') action_runner.ClickElement( element_function='document.body.firstElementChild;') self.assertEqual(3, action_runner.EvaluateJavaScript('valueToTest')) def WillFail(): action_runner.ClickElement('#notfound') self.assertRaises(exceptions.EvaluateException, WillFail) @decorators.Disabled('android', 'debug', # crbug.com/437068 'chromeos', # crbug.com/483212 'win') # catapult/issues/2282 def testTapElement(self): self.Navigate('page_with_clickables.html') action_runner = action_runner_module.ActionRunner(self._tab, skip_waits=True) action_runner.ExecuteJavaScript('valueSettableByTest = 1;') action_runner.TapElement('#test') self.assertEqual(1, action_runner.EvaluateJavaScript('valueToTest')) action_runner.ExecuteJavaScript('valueSettableByTest = 2;') action_runner.TapElement(text='Click/tap me') self.assertEqual(2, action_runner.EvaluateJavaScript('valueToTest')) action_runner.ExecuteJavaScript('valueSettableByTest = 3;') action_runner.TapElement( element_function='document.body.firstElementChild') self.assertEqual(3, action_runner.EvaluateJavaScript('valueToTest')) def WillFail(): action_runner.TapElement('#notfound') self.assertRaises(exceptions.EvaluateException, WillFail) @decorators.Disabled('android', # crbug.com/437065. 'chromeos') # crbug.com/483212. def testScroll(self): if not page_action.IsGestureSourceTypeSupported( self._tab, 'touch'): return self.Navigate('page_with_swipeables.html') action_runner = action_runner_module.ActionRunner(self._tab, skip_waits=True) action_runner.ScrollElement( selector='#left-right', direction='right', left_start_ratio=0.9) self.assertTrue(action_runner.EvaluateJavaScript( 'document.querySelector("#left-right").scrollLeft') > 75) action_runner.ScrollElement( selector='#top-bottom', direction='down', top_start_ratio=0.9) self.assertTrue(action_runner.EvaluateJavaScript( 'document.querySelector("#top-bottom").scrollTop') > 75) action_runner.ScrollPage(direction='right', left_start_ratio=0.9, distance=100) self.assertTrue(action_runner.EvaluateJavaScript( '(document.scrollingElement || document.body).scrollLeft') > 75) @decorators.Disabled('android', # crbug.com/437065. 'chromeos') # crbug.com/483212. def testSwipe(self): if not page_action.IsGestureSourceTypeSupported( self._tab, 'touch'): return self.Navigate('page_with_swipeables.html') action_runner = action_runner_module.ActionRunner(self._tab, skip_waits=True) action_runner.SwipeElement( selector='#left-right', direction='left', left_start_ratio=0.9) self.assertTrue(action_runner.EvaluateJavaScript( 'document.querySelector("#left-right").scrollLeft') > 75) action_runner.SwipeElement( selector='#top-bottom', direction='up', top_start_ratio=0.9) self.assertTrue(action_runner.EvaluateJavaScript( 'document.querySelector("#top-bottom").scrollTop') > 75) action_runner.SwipePage(direction='left', left_start_ratio=0.9) self.assertTrue(action_runner.EvaluateJavaScript( '(document.scrollingElement || document.body).scrollLeft') > 75) def testEnterText(self): self.Navigate('blank.html') self._tab.ExecuteJavaScript( '(function() {' ' var elem = document.createElement("textarea");' ' document.body.appendChild(elem);' ' elem.focus();' '})();') action_runner = action_runner_module.ActionRunner(self._tab, skip_waits=True) action_runner.EnterText('That is boring') # That is boring|. action_runner.PressKey('Home') # |That is boring. action_runner.PressKey('ArrowRight', repeat_count=2) # Th|at is boring. action_runner.PressKey('Delete', repeat_count=2) # Th| is boring. action_runner.EnterText('is') # This| is boring. action_runner.PressKey('End') # This is boring|. action_runner.PressKey('ArrowLeft', repeat_count=3) # This is bor|ing. action_runner.PressKey('Backspace', repeat_count=3) # This is |ing. action_runner.EnterText('interest') # This is interest|ing. # Check that the contents of the textarea is correct. It might take a second # until all keystrokes have been handled by the browser (crbug.com/630017). self._tab.WaitForJavaScriptExpression( 'document.querySelector("textarea").value === "This is interesting"', timeout=1) class InteractionTest(unittest.TestCase): def setUp(self): self.mock_action_runner = mock.Mock(action_runner_module.ActionRunner) def testIssuingInteractionRecordCommand(self): with action_runner_module.Interaction( self.mock_action_runner, label='ABC', flags=[]): pass expected_calls = [ mock.call.ExecuteJavaScript('console.time("Interaction.ABC");'), mock.call.ExecuteJavaScript('console.timeEnd("Interaction.ABC");')] self.assertEqual(expected_calls, self.mock_action_runner.mock_calls) def testExceptionRaisedInWithInteraction(self): class FooException(Exception): pass # Test that the Foo exception raised in the with block is propagated to the # caller. with self.assertRaises(FooException): with action_runner_module.Interaction( self.mock_action_runner, label='ABC', flags=[]): raise FooException() # Test that the end console.timeEnd(...) isn't called because exception was # raised. expected_calls = [ mock.call.ExecuteJavaScript('console.time("Interaction.ABC");')] self.assertEqual(expected_calls, self.mock_action_runner.mock_calls)