summaryrefslogtreecommitdiffstats
path: root/testing/marionette/harness/marionette_harness/tests
diff options
context:
space:
mode:
Diffstat (limited to 'testing/marionette/harness/marionette_harness/tests')
-rw-r--r--testing/marionette/harness/marionette_harness/tests/harness_unit/conftest.py100
-rw-r--r--testing/marionette/harness/marionette_harness/tests/harness_unit/test_httpd.py90
-rw-r--r--testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_arguments.py32
-rw-r--r--testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_harness.py108
-rw-r--r--testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_runner.py442
-rw-r--r--testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_test_result.py54
-rw-r--r--testing/marionette/harness/marionette_harness/tests/harness_unit/test_serve.py67
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit-tests.ini11
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/importanotherscript.js1
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/importscript.js1
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/mn-restartless-unsigned.xpibin0 -> 1552 bytes
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/single_finger_functions.py131
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_about_pages.py134
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_accessibility.py210
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_addons.py58
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_anonymous_content.py90
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_browsermobproxy.py34
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_capabilities.py253
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_checkbox.py17
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_checkbox_chrome.py36
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_chrome.py51
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_chrome_async_finish.js6
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_chrome_element_css.py23
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_clearing.py72
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_click.py254
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_click_chrome.py35
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_click_scrolling.py117
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_cookies.py115
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_crash.py155
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_data_driven.py67
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_date_time_value.py29
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_element_retrieval.py483
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_element_state.py162
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_element_state_chrome.py85
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_elementsize.py17
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_elementsize_chrome.py34
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_errors.py77
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_execute_async_script.py156
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_execute_isolate.py37
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_execute_sandboxes.py79
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_execute_script.py402
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_expected.py228
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_expectedfail.py11
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_file_upload.py152
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_findelement_chrome.py82
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_geckoinstance.py25
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_getactiveframe_oop.py93
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_implicit_waits.py26
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_import_script.py138
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_key_actions.py91
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_localization.py56
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_log.py64
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_marionette.py67
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_modal_dialogs.py198
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_mouse_action.py114
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py447
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_pagesource.py33
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_pagesource_chrome.py29
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_position.py19
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_prefs.py167
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_profile_management.py34
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_proxy.py252
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_quit_restart.py173
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_rendered_element.py34
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_report.py29
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_run_js_test.py10
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_screen_orientation.py86
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_screenshot.py428
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_select.py164
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_session.py56
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_set_window_size.py84
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_shadow_dom.py80
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_chrome.js12
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_fail.js16
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_pass.js12
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_sanity.py107
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_timeout.js16
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_single_finger_desktop.py123
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_skip_setup.py35
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_switch_frame.py183
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_switch_frame_chrome.py56
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_switch_remote_frame.py118
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_switch_window_chrome.py124
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_switch_window_content.py171
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_teardown_context_preserved.py21
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_text.py224
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_text_chrome.py44
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_timeouts.py115
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_transport.py172
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_typing.py332
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_using_permissions.py46
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_visibility.py121
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_wait.py347
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_window_close_chrome.py80
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_window_close_content.py81
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_window_handles_chrome.py207
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_window_handles_content.py96
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_window_position.py42
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_window_title.py12
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_window_title_chrome.py26
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_window_type.py27
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/test_with_using_context.py66
-rw-r--r--testing/marionette/harness/marionette_harness/tests/unit/unit-tests.ini132
-rw-r--r--testing/marionette/harness/marionette_harness/tests/webapi-tests.ini8
104 files changed, 10967 insertions, 0 deletions
diff --git a/testing/marionette/harness/marionette_harness/tests/harness_unit/conftest.py b/testing/marionette/harness/marionette_harness/tests/harness_unit/conftest.py
new file mode 100644
index 000000000..6dc0a89a1
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/harness_unit/conftest.py
@@ -0,0 +1,100 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import pytest
+
+from mock import Mock, MagicMock
+
+from marionette_driver.marionette import Marionette
+
+from marionette_harness.runner.httpd import FixtureServer
+
+
+@pytest.fixture(scope='module')
+def logger():
+ """
+ Fake logger to help with mocking out other runner-related classes.
+ """
+ import mozlog
+ return Mock(spec=mozlog.structuredlog.StructuredLogger)
+
+
+@pytest.fixture
+def mach_parsed_kwargs(logger):
+ """
+ Parsed and verified dictionary used during simplest
+ call to mach marionette-test
+ """
+ return {
+ 'adb_path': None,
+ 'addons': None,
+ 'address': None,
+ 'app': None,
+ 'app_args': [],
+ 'avd': None,
+ 'avd_home': None,
+ 'binary': u'/path/to/firefox',
+ 'browsermob_port' : None,
+ 'browsermob_script' : None,
+ 'device_serial': None,
+ 'e10s': True,
+ 'emulator': False,
+ 'emulator_bin': None,
+ 'gecko_log': None,
+ 'jsdebugger': False,
+ 'log_errorsummary': None,
+ 'log_html': None,
+ 'log_mach': None,
+ 'log_mach_buffer': None,
+ 'log_mach_level': None,
+ 'log_mach_verbose': None,
+ 'log_raw': None,
+ 'log_raw_level': None,
+ 'log_tbpl': None,
+ 'log_tbpl_buffer': None,
+ 'log_tbpl_compact': None,
+ 'log_tbpl_level': None,
+ 'log_unittest': None,
+ 'log_xunit': None,
+ 'logger_name': 'Marionette-based Tests',
+ 'prefs': {},
+ 'prefs_args': None,
+ 'prefs_files': None,
+ 'profile': None,
+ 'pydebugger': None,
+ 'repeat': 0,
+ 'server_root': None,
+ 'shuffle': False,
+ 'shuffle_seed': 2276870381009474531,
+ 'socket_timeout': 60.0,
+ 'startup_timeout': 60,
+ 'symbols_path': None,
+ 'test_tags': None,
+ 'tests': [u'/path/to/unit-tests.ini'],
+ 'testvars': None,
+ 'this_chunk': None,
+ 'timeout': None,
+ 'total_chunks': None,
+ 'verbose': None,
+ 'workspace': None,
+ 'logger': logger,
+ }
+
+
+@pytest.fixture
+def mock_httpd(request):
+ """ Mock httpd instance """
+ httpd = MagicMock(spec=FixtureServer)
+ return httpd
+
+
+@pytest.fixture
+def mock_marionette(request):
+ """ Mock marionette instance """
+ marionette_class = MagicMock(spec=Marionette)
+ if 'has_crashed' in request.funcargnames:
+ marionette_class.check_for_crash.return_value = request.getfuncargvalue(
+ 'has_crashed'
+ )
+ return marionette_class
diff --git a/testing/marionette/harness/marionette_harness/tests/harness_unit/test_httpd.py b/testing/marionette/harness/marionette_harness/tests/harness_unit/test_httpd.py
new file mode 100644
index 000000000..bd86b2fff
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/harness_unit/test_httpd.py
@@ -0,0 +1,90 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import json
+import os
+import types
+import urllib2
+
+import pytest
+
+from wptserve.handlers import json_handler
+
+from marionette_harness.runner import httpd
+
+here = os.path.abspath(os.path.dirname(__file__))
+parent = os.path.dirname(here)
+default_doc_root = os.path.join(os.path.dirname(parent), "www")
+
+
+@pytest.yield_fixture
+def server():
+ server = httpd.FixtureServer(default_doc_root)
+ yield server
+ server.stop()
+
+
+def test_ctor():
+ with pytest.raises(ValueError):
+ httpd.FixtureServer("foo")
+ httpd.FixtureServer(default_doc_root)
+
+
+def test_start_stop(server):
+ server.start()
+ server.stop()
+
+
+def test_get_url(server):
+ server.start()
+ url = server.get_url("/")
+ assert isinstance(url, types.StringTypes)
+ assert "http://" in url
+
+ server.stop()
+ with pytest.raises(httpd.NotAliveError):
+ server.get_url("/")
+
+
+def test_doc_root(server):
+ server.start()
+ assert isinstance(server.doc_root, types.StringTypes)
+ server.stop()
+ assert isinstance(server.doc_root, types.StringTypes)
+
+
+def test_router(server):
+ assert server.router is not None
+
+
+def test_routes(server):
+ assert server.routes is not None
+
+
+def test_is_alive(server):
+ assert server.is_alive == False
+ server.start()
+ assert server.is_alive == True
+
+
+def test_handler(server):
+ counter = 0
+
+ @json_handler
+ def handler(request, response):
+ return {"count": counter}
+
+ route = ("GET", "/httpd/test_handler", handler)
+ server.router.register(*route)
+ server.start()
+
+ url = server.get_url("/httpd/test_handler")
+ body = urllib2.urlopen(url).read()
+ res = json.loads(body)
+ assert res["count"] == counter
+
+
+if __name__ == "__main__":
+ import sys
+ sys.exit(pytest.main(["--verbose", __file__]))
diff --git a/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_arguments.py b/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_arguments.py
new file mode 100644
index 000000000..1a0687028
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_arguments.py
@@ -0,0 +1,32 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+import pytest
+
+from marionette_harness.runtests import MarionetteArguments
+
+
+@pytest.mark.parametrize("socket_timeout", ['A', '10', '1B-', '1C2', '44.35'])
+def test_parse_arg_socket_timeout(socket_timeout):
+ argv = ['marionette', '--socket-timeout', socket_timeout]
+ parser = MarionetteArguments()
+
+ def _is_float_convertible(value):
+ try:
+ float(value)
+ return True
+ except:
+ return False
+
+ if not _is_float_convertible(socket_timeout):
+ with pytest.raises(SystemExit) as ex:
+ parser.parse_args(args=argv)
+ assert ex.value.code == 2
+ else:
+ args = parser.parse_args(args=argv)
+ assert hasattr(args, 'socket_timeout') and args.socket_timeout == float(socket_timeout)
+
+
+if __name__ == '__main__':
+ import sys
+ sys.exit(pytest.main(['--verbose', __file__]))
diff --git a/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_harness.py b/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_harness.py
new file mode 100644
index 000000000..dfbbfb788
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_harness.py
@@ -0,0 +1,108 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import pytest
+
+from mock import Mock, patch, sentinel
+
+import marionette_harness.marionette_test as marionette_test
+
+from marionette_harness.runtests import MarionetteTestRunner, MarionetteHarness, cli
+
+
+@pytest.fixture
+def harness_class(request):
+ """
+ Mock based on MarionetteHarness whose run method just returns a number of
+ failures according to the supplied test parameter
+ """
+ if 'num_fails_crashed' in request.funcargnames:
+ num_fails_crashed = request.getfuncargvalue('num_fails_crashed')
+ else:
+ num_fails_crashed = (0, 0)
+ harness_cls = Mock(spec=MarionetteHarness)
+ harness = harness_cls.return_value
+ if num_fails_crashed is None:
+ harness.run.side_effect = Exception
+ else:
+ harness.run.return_value = sum(num_fails_crashed)
+ return harness_cls
+
+
+@pytest.fixture
+def runner_class(request):
+ """
+ Mock based on MarionetteTestRunner, wherein the runner.failed,
+ runner.crashed attributes are provided by a test parameter
+ """
+ if 'num_fails_crashed' in request.funcargnames:
+ failures, crashed = request.getfuncargvalue('num_fails_crashed')
+ else:
+ failures = 0
+ crashed = 0
+ mock_runner_class = Mock(spec=MarionetteTestRunner)
+ runner = mock_runner_class.return_value
+ runner.failed = failures
+ runner.crashed = crashed
+ return mock_runner_class
+
+
+@pytest.mark.parametrize(
+ "num_fails_crashed,exit_code",
+ [((0, 0), 0), ((1, 0), 10), ((0, 1), 10), (None, 1)],
+)
+def test_cli_exit_code(num_fails_crashed, exit_code, harness_class):
+ with pytest.raises(SystemExit) as err:
+ cli(harness_class=harness_class)
+ assert err.value.code == exit_code
+
+
+@pytest.mark.parametrize("num_fails_crashed", [(0, 0), (1, 0), (1, 1)])
+def test_call_harness_with_parsed_args_yields_num_failures(mach_parsed_kwargs,
+ runner_class,
+ num_fails_crashed):
+ with patch(
+ 'marionette_harness.runtests.MarionetteHarness.parse_args'
+ ) as parse_args:
+ failed_or_crashed = MarionetteHarness(runner_class,
+ args=mach_parsed_kwargs).run()
+ parse_args.assert_not_called()
+ assert failed_or_crashed == sum(num_fails_crashed)
+
+
+def test_call_harness_with_no_args_yields_num_failures(runner_class):
+ with patch(
+ 'marionette_harness.runtests.MarionetteHarness.parse_args',
+ return_value={'tests': []}
+ ) as parse_args:
+ failed_or_crashed = MarionetteHarness(runner_class).run()
+ assert parse_args.call_count == 1
+ assert failed_or_crashed == 0
+
+
+def test_args_passed_to_runner_class(mach_parsed_kwargs, runner_class):
+ arg_list = mach_parsed_kwargs.keys()
+ arg_list.remove('tests')
+ mach_parsed_kwargs.update([(a, getattr(sentinel, a)) for a in arg_list])
+ harness = MarionetteHarness(runner_class, args=mach_parsed_kwargs)
+ harness.process_args = Mock()
+ harness.run()
+ for arg in arg_list:
+ assert harness._runner_class.call_args[1][arg] is getattr(sentinel, arg)
+
+
+def test_harness_sets_up_default_test_handlers(mach_parsed_kwargs):
+ """
+ If the necessary TestCase is not in test_handlers,
+ tests are omitted silently
+ """
+ harness = MarionetteHarness(args=mach_parsed_kwargs)
+ mach_parsed_kwargs.pop('tests')
+ runner = harness._runner_class(**mach_parsed_kwargs)
+ assert marionette_test.MarionetteTestCase in runner.test_handlers
+
+
+if __name__ == '__main__':
+ import sys
+ sys.exit(pytest.main(['--verbose', __file__]))
diff --git a/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_runner.py b/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_runner.py
new file mode 100644
index 000000000..79bdc824e
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_runner.py
@@ -0,0 +1,442 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import manifestparser
+import pytest
+
+from mock import Mock, patch, mock_open, sentinel, DEFAULT
+
+from marionette_harness.runtests import MarionetteTestRunner
+
+
+@pytest.fixture
+def runner(mach_parsed_kwargs):
+ """
+ MarionetteTestRunner instance initialized with default options.
+ """
+ return MarionetteTestRunner(**mach_parsed_kwargs)
+
+
+@pytest.fixture
+def mock_runner(runner, mock_marionette, monkeypatch):
+ """
+ MarionetteTestRunner instance with mocked-out
+ self.marionette and other properties,
+ to enable testing runner.run_tests().
+ """
+ runner.driverclass = mock_marionette
+ for attr in ['run_test_set', '_capabilities']:
+ setattr(runner, attr, Mock())
+ runner._appName = 'fake_app'
+ # simulate that browser runs with e10s by default
+ runner._appinfo = {'browserTabsRemoteAutostart': True}
+ monkeypatch.setattr('marionette_harness.runner.base.mozversion', Mock())
+ return runner
+
+
+@pytest.fixture
+def build_kwargs_using(mach_parsed_kwargs):
+ '''Helper function for test_build_kwargs_* functions'''
+ def kwarg_builder(new_items, return_socket=False):
+ mach_parsed_kwargs.update(new_items)
+ runner = MarionetteTestRunner(**mach_parsed_kwargs)
+ with patch('marionette_harness.runner.base.socket') as socket:
+ built_kwargs = runner._build_kwargs()
+ if return_socket:
+ return built_kwargs, socket
+ return built_kwargs
+ return kwarg_builder
+
+
+@pytest.fixture
+def expected_driver_args(runner):
+ '''Helper fixture for tests of _build_kwargs
+ with binary/emulator.
+ Provides a dictionary of certain arguments
+ related to binary/emulator settings
+ which we expect to be passed to the
+ driverclass constructor. Expected values can
+ be updated in tests as needed.
+ Provides convenience methods for comparing the
+ expected arguments to the argument dictionary
+ created by _build_kwargs. '''
+
+ class ExpectedDict(dict):
+ def assert_matches(self, actual):
+ for (k, v) in self.items():
+ assert actual[k] == v
+
+ def assert_keys_not_in(self, actual):
+ for k in self.keys():
+ assert k not in actual
+
+ expected = ExpectedDict(host=None, port=None, bin=None)
+ for attr in ['app', 'app_args', 'profile', 'addons', 'gecko_log']:
+ expected[attr] = getattr(runner, attr)
+ return expected
+
+
+class ManifestFixture:
+ def __init__(self, name='mock_manifest',
+ tests=[{'path': u'test_something.py', 'expected': 'pass'}]):
+ self.filepath = "/path/to/fake/manifest.ini"
+ self.n_disabled = len([t for t in tests if 'disabled' in t])
+ self.n_enabled = len(tests) - self.n_disabled
+ mock_manifest = Mock(spec=manifestparser.TestManifest,
+ active_tests=Mock(return_value=tests))
+ self.manifest_class = Mock(return_value=mock_manifest)
+ self.__repr__ = lambda: "<ManifestFixture {}>".format(name)
+
+@pytest.fixture
+def manifest():
+ return ManifestFixture()
+
+@pytest.fixture(params=['enabled', 'disabled', 'enabled_disabled', 'empty'])
+def manifest_with_tests(request):
+ '''
+ Fixture for the contents of mock_manifest, where a manifest
+ can include enabled tests, disabled tests, both, or neither (empty)
+ '''
+ included = []
+ if 'enabled' in request.param:
+ included += [(u'test_expected_pass.py', 'pass'),
+ (u'test_expected_fail.py', 'fail')]
+ if 'disabled' in request.param:
+ included += [(u'test_pass_disabled.py', 'pass', 'skip-if: true'),
+ (u'test_fail_disabled.py', 'fail', 'skip-if: true')]
+ keys = ('path', 'expected', 'disabled')
+ active_tests = [dict(zip(keys, values)) for values in included]
+
+ return ManifestFixture(request.param, active_tests)
+
+
+def test_args_passed_to_driverclass(mock_runner):
+ built_kwargs = {'arg1': 'value1', 'arg2': 'value2'}
+ mock_runner._build_kwargs = Mock(return_value=built_kwargs)
+ with pytest.raises(IOError):
+ mock_runner.run_tests(['fake_tests.ini'])
+ assert mock_runner.driverclass.call_args[1] == built_kwargs
+
+
+def test_build_kwargs_basic_args(build_kwargs_using):
+ '''Test the functionality of runner._build_kwargs:
+ make sure that basic arguments (those which should
+ always be included, irrespective of the runner's settings)
+ get passed to the call to runner.driverclass'''
+
+ basic_args = ['socket_timeout', 'prefs',
+ 'startup_timeout', 'verbose', 'symbols_path']
+ args_dict = {a: getattr(sentinel, a) for a in basic_args}
+ # Mock an update method to work with calls to MarionetteTestRunner()
+ args_dict['prefs'].update = Mock(return_value={})
+ built_kwargs = build_kwargs_using([(a, getattr(sentinel, a)) for a in basic_args])
+ for arg in basic_args:
+ assert built_kwargs[arg] is getattr(sentinel, arg)
+
+
+@pytest.mark.parametrize('workspace', ['path/to/workspace', None])
+def test_build_kwargs_with_workspace(build_kwargs_using, workspace):
+ built_kwargs = build_kwargs_using({'workspace': workspace})
+ if workspace:
+ assert built_kwargs['workspace'] == workspace
+ else:
+ assert 'workspace' not in built_kwargs
+
+
+@pytest.mark.parametrize('address', ['host:123', None])
+def test_build_kwargs_with_address(build_kwargs_using, address):
+ built_kwargs, socket = build_kwargs_using(
+ {'address': address, 'binary': None, 'emulator': None},
+ return_socket=True
+ )
+ assert 'connect_to_running_emulator' not in built_kwargs
+ if address is not None:
+ host, port = address.split(":")
+ assert built_kwargs['host'] == host and built_kwargs['port'] == int(port)
+ socket.socket().connect.assert_called_with((host, int(port)))
+ assert socket.socket().close.called
+ else:
+ assert not socket.socket.called
+
+
+@pytest.mark.parametrize('address', ['host:123', None])
+@pytest.mark.parametrize('binary', ['path/to/bin', None])
+def test_build_kwargs_with_binary_or_address(expected_driver_args, build_kwargs_using,
+ binary, address):
+ built_kwargs = build_kwargs_using({'binary': binary, 'address': address, 'emulator': None})
+ if binary:
+ expected_driver_args['bin'] = binary
+ if address:
+ host, port = address.split(":")
+ expected_driver_args.update({'host': host, 'port': int(port)})
+ else:
+ expected_driver_args.update({'host': 'localhost', 'port': 2828})
+ expected_driver_args.assert_matches(built_kwargs)
+ elif address is None:
+ expected_driver_args.assert_keys_not_in(built_kwargs)
+
+
+@pytest.mark.parametrize('address', ['host:123', None])
+@pytest.mark.parametrize('emulator', [True, False, None])
+def test_build_kwargs_with_emulator_or_address(expected_driver_args, build_kwargs_using,
+ emulator, address):
+ emulator_props = [(a, getattr(sentinel, a)) for a in ['avd_home', 'adb_path', 'emulator_bin']]
+ built_kwargs = build_kwargs_using(
+ [('emulator', emulator), ('address', address), ('binary', None)] + emulator_props
+ )
+ if emulator:
+ expected_driver_args.update(emulator_props)
+ expected_driver_args['emulator_binary'] = expected_driver_args.pop('emulator_bin')
+ expected_driver_args['bin'] = True
+ if address:
+ expected_driver_args['connect_to_running_emulator'] = True
+ host, port = address.split(":")
+ expected_driver_args.update({'host': host, 'port': int(port)})
+ else:
+ expected_driver_args.update({'host': 'localhost', 'port': 2828})
+ assert 'connect_to_running_emulator' not in built_kwargs
+ expected_driver_args.assert_matches(built_kwargs)
+ elif not address:
+ expected_driver_args.assert_keys_not_in(built_kwargs)
+
+
+def test_parsing_testvars(mach_parsed_kwargs):
+ mach_parsed_kwargs.pop('tests')
+ testvars_json_loads = [
+ {"wifi": {"ssid": "blah", "keyManagement": "WPA-PSK", "psk": "foo"}},
+ {"wifi": {"PEAP": "bar"}, "device": {"stuff": "buzz"}}
+ ]
+ expected_dict = {
+ "wifi": {
+ "ssid": "blah",
+ "keyManagement": "WPA-PSK",
+ "psk": "foo",
+ "PEAP": "bar"
+ },
+ "device": {"stuff": "buzz"}
+ }
+ with patch(
+ 'marionette_harness.runtests.MarionetteTestRunner._load_testvars',
+ return_value=testvars_json_loads
+ ) as load:
+ runner = MarionetteTestRunner(**mach_parsed_kwargs)
+ assert runner.testvars == expected_dict
+ assert load.call_count == 1
+
+
+def test_load_testvars_throws_expected_errors(mach_parsed_kwargs):
+ mach_parsed_kwargs['testvars'] = ['some_bad_path.json']
+ runner = MarionetteTestRunner(**mach_parsed_kwargs)
+ with pytest.raises(IOError) as io_exc:
+ runner._load_testvars()
+ assert 'does not exist' in io_exc.value.message
+ with patch('os.path.exists', return_value=True):
+ with patch('__builtin__.open', mock_open(read_data='[not {valid JSON]')):
+ with pytest.raises(Exception) as json_exc:
+ runner._load_testvars()
+ assert 'not properly formatted' in json_exc.value.message
+
+
+def _check_crash_counts(has_crashed, runner, mock_marionette):
+ if has_crashed:
+ assert mock_marionette.check_for_crash.call_count == 1
+ assert runner.crashed == 1
+ else:
+ assert runner.crashed == 0
+
+
+@pytest.mark.parametrize("has_crashed", [True, False])
+def test_increment_crash_count_in_run_test_set(runner, has_crashed,
+ mock_marionette):
+ fake_tests = [{'filepath': i,
+ 'expected': 'pass'} for i in 'abc']
+
+ with patch.multiple(runner, run_test=DEFAULT, marionette=mock_marionette):
+ runner.run_test_set(fake_tests)
+ if not has_crashed:
+ assert runner.marionette.check_for_crash.call_count == len(fake_tests)
+ _check_crash_counts(has_crashed, runner, runner.marionette)
+
+
+@pytest.mark.parametrize("has_crashed", [True, False])
+def test_record_crash(runner, has_crashed, mock_marionette):
+ with patch.object(runner, 'marionette', mock_marionette):
+ assert runner.record_crash() == has_crashed
+ _check_crash_counts(has_crashed, runner, runner.marionette)
+
+
+def test_add_test_module(runner):
+ tests = ['test_something.py', 'testSomething.js', 'bad_test.py']
+ assert len(runner.tests) == 0
+ for test in tests:
+ with patch('os.path.abspath', return_value=test) as abspath:
+ runner.add_test(test)
+ assert abspath.called
+ expected = {'filepath': test, 'expected': 'pass'}
+ assert expected in runner.tests
+ # add_test doesn't validate module names; 'bad_test.py' gets through
+ assert len(runner.tests) == 3
+
+
+def test_add_test_directory(runner):
+ test_dir = 'path/to/tests'
+ dir_contents = [
+ (test_dir, ('subdir',), ('test_a.py', 'test_a.js', 'bad_test_a.py', 'bad_test_a.js')),
+ (test_dir + '/subdir', (), ('test_b.py', 'test_b.js', 'bad_test_b.py', 'bad_test_b.js')),
+ ]
+ tests = list(dir_contents[0][2] + dir_contents[1][2])
+ assert len(runner.tests) == 0
+ # Need to use side effect to make isdir return True for test_dir and False for tests
+ with patch('os.path.isdir', side_effect=[True] + [False for t in tests]) as isdir:
+ with patch('os.walk', return_value=dir_contents) as walk:
+ runner.add_test(test_dir)
+ assert isdir.called and walk.called
+ for test in runner.tests:
+ assert test_dir in test['filepath']
+ assert len(runner.tests) == 4
+
+
+@pytest.mark.parametrize("test_files_exist", [True, False])
+def test_add_test_manifest(mock_runner, manifest_with_tests, monkeypatch, test_files_exist):
+ monkeypatch.setattr('marionette_harness.runner.base.TestManifest',
+ manifest_with_tests.manifest_class)
+ mock_runner.marionette = mock_runner.driverclass()
+ with patch('marionette_harness.runner.base.os.path.exists', return_value=test_files_exist):
+ if test_files_exist or manifest_with_tests.n_enabled == 0:
+ mock_runner.add_test(manifest_with_tests.filepath)
+ assert len(mock_runner.tests) == manifest_with_tests.n_enabled
+ assert len(mock_runner.manifest_skipped_tests) == manifest_with_tests.n_disabled
+ for test in mock_runner.tests:
+ assert test['filepath'].endswith(test['expected'] + '.py')
+ else:
+ pytest.raises(IOError, "mock_runner.add_test(manifest_with_tests.filepath)")
+ assert manifest_with_tests.manifest_class().read.called
+ assert manifest_with_tests.manifest_class().active_tests.called
+
+
+def get_kwargs_passed_to_manifest(mock_runner, manifest, monkeypatch, **kwargs):
+ '''Helper function for test_manifest_* tests.
+ Returns the kwargs passed to the call to manifest.active_tests.'''
+ monkeypatch.setattr('marionette_harness.runner.base.TestManifest', manifest.manifest_class)
+ monkeypatch.setattr('marionette_harness.runner.base.mozinfo.info',
+ {'mozinfo_key': 'mozinfo_val'})
+ for attr in kwargs:
+ setattr(mock_runner, attr, kwargs[attr])
+ mock_runner.marionette = mock_runner.driverclass()
+ with patch('marionette_harness.runner.base.os.path.exists', return_value=True):
+ mock_runner.add_test(manifest.filepath)
+ call_args, call_kwargs = manifest.manifest_class().active_tests.call_args
+ return call_kwargs
+
+
+def test_manifest_basic_args(mock_runner, manifest, monkeypatch):
+ kwargs = get_kwargs_passed_to_manifest(mock_runner, manifest, monkeypatch)
+ assert kwargs['exists'] is False
+ assert kwargs['disabled'] is True
+ assert kwargs['appname'] == 'fake_app'
+ assert 'mozinfo_key' in kwargs and kwargs['mozinfo_key'] == 'mozinfo_val'
+
+
+@pytest.mark.parametrize('e10s', (True, False))
+def test_manifest_with_e10s(mock_runner, manifest, monkeypatch, e10s):
+ kwargs = get_kwargs_passed_to_manifest(mock_runner, manifest, monkeypatch, e10s=e10s)
+ assert kwargs['e10s'] == e10s
+
+
+@pytest.mark.parametrize('test_tags', (None, ['tag', 'tag2']))
+def test_manifest_with_test_tags(mock_runner, manifest, monkeypatch, test_tags):
+ kwargs = get_kwargs_passed_to_manifest(mock_runner, manifest, monkeypatch, test_tags=test_tags)
+ if test_tags is None:
+ assert kwargs['filters'] == []
+ else:
+ assert len(kwargs['filters']) == 1 and kwargs['filters'][0].tags == test_tags
+
+
+def test_cleanup_with_manifest(mock_runner, manifest_with_tests, monkeypatch):
+ monkeypatch.setattr('marionette_harness.runner.base.TestManifest',
+ manifest_with_tests.manifest_class)
+ if manifest_with_tests.n_enabled > 0:
+ context = patch('marionette_harness.runner.base.os.path.exists', return_value=True)
+ else:
+ context = pytest.raises(Exception)
+ with context:
+ mock_runner.run_tests([manifest_with_tests.filepath])
+ assert mock_runner.marionette is None
+ assert mock_runner.fixture_servers == {}
+
+
+def test_reset_test_stats(mock_runner):
+ def reset_successful(runner):
+ stats = ['passed', 'failed', 'unexpected_successes', 'todo', 'skipped', 'failures']
+ return all([((s in vars(runner)) and (not vars(runner)[s])) for s in stats])
+ assert reset_successful(mock_runner)
+ mock_runner.passed = 1
+ mock_runner.failed = 1
+ mock_runner.failures.append(['TEST-UNEXPECTED-FAIL'])
+ assert not reset_successful(mock_runner)
+ mock_runner.run_tests([u'test_fake_thing.py'])
+ assert reset_successful(mock_runner)
+
+
+def test_initialize_test_run(mock_runner):
+ tests = [u'test_fake_thing.py']
+ mock_runner.reset_test_stats = Mock()
+ mock_runner.run_tests(tests)
+ assert mock_runner.reset_test_stats.called
+ with pytest.raises(AssertionError) as test_exc:
+ mock_runner.run_tests([])
+ assert "len(tests)" in str(test_exc.traceback[-1].statement)
+ with pytest.raises(AssertionError) as hndl_exc:
+ mock_runner.test_handlers = []
+ mock_runner.run_tests(tests)
+ assert "test_handlers" in str(hndl_exc.traceback[-1].statement)
+ assert mock_runner.reset_test_stats.call_count == 1
+
+
+def test_add_tests(mock_runner):
+ assert len(mock_runner.tests) == 0
+ fake_tests = ["test_" + i + ".py" for i in "abc"]
+ mock_runner.run_tests(fake_tests)
+ assert len(mock_runner.tests) == 3
+ for (test_name, added_test) in zip(fake_tests, mock_runner.tests):
+ assert added_test['filepath'].endswith(test_name)
+
+
+def test_catch_invalid_test_names(runner):
+ good_tests = [u'test_ok.py', u'test_is_ok.py', u'test_is_ok.js', u'testIsOk.js']
+ bad_tests = [u'bad_test.py', u'testbad.py', u'_test_bad.py', u'testBad.notjs',
+ u'test_bad.notpy', u'test_bad', u'testbad.js', u'badtest.js',
+ u'test.py', u'test_.py', u'test.js', u'test_.js']
+ with pytest.raises(Exception) as exc:
+ runner._add_tests(good_tests + bad_tests)
+ msg = exc.value.message
+ assert "Test file names must be of the form" in msg
+ for bad_name in bad_tests:
+ assert bad_name in msg
+ for good_name in good_tests:
+ assert good_name not in msg
+
+@pytest.mark.parametrize('e10s', (True, False))
+def test_e10s_option_sets_prefs(mach_parsed_kwargs, e10s):
+ mach_parsed_kwargs['e10s'] = e10s
+ runner = MarionetteTestRunner(**mach_parsed_kwargs)
+ e10s_prefs = {
+ 'browser.tabs.remote.autostart': True,
+ 'browser.tabs.remote.force-enable': True,
+ 'extensions.e10sBlocksEnabling': False
+ }
+ for k,v in e10s_prefs.iteritems():
+ if k == 'extensions.e10sBlocksEnabling' and not e10s:
+ continue
+ assert runner.prefs.get(k, False) == (v and e10s)
+
+def test_e10s_option_clash_raises(mock_runner):
+ mock_runner.e10s = False
+ with pytest.raises(AssertionError) as e:
+ mock_runner.run_tests([u'test_fake_thing.py'])
+ assert "configuration (self.e10s) does not match browser appinfo" in e.value.message
+
+if __name__ == '__main__':
+ import sys
+ sys.exit(pytest.main(['--verbose', __file__]))
diff --git a/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_test_result.py b/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_test_result.py
new file mode 100644
index 000000000..a69b072cd
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_test_result.py
@@ -0,0 +1,54 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import pytest
+
+from marionette_harness import MarionetteTestResult
+
+
+@pytest.fixture
+def empty_marionette_testcase():
+ """ Testable MarionetteTestCase class """
+ from marionette_harness import MarionetteTestCase
+
+ class EmptyTestCase(MarionetteTestCase):
+ def test_nothing(self):
+ pass
+
+ return EmptyTestCase
+
+
+@pytest.fixture
+def empty_marionette_test(mock_marionette, empty_marionette_testcase):
+ return empty_marionette_testcase(lambda: mock_marionette, lambda: mock_httpd,
+ 'test_nothing')
+
+
+@pytest.mark.parametrize("has_crashed", [True, False])
+def test_crash_is_recorded_as_error(empty_marionette_test,
+ logger,
+ has_crashed):
+ """ Number of errors is incremented by stopTest iff has_crashed is true """
+ # collect results from the empty test
+ result = MarionetteTestResult(
+ marionette=empty_marionette_test._marionette_weakref(),
+ logger=logger, verbosity=None,
+ stream=None, descriptions=None,
+ )
+ result.startTest(empty_marionette_test)
+ assert len(result.errors) == 0
+ assert len(result.failures) == 0
+ assert result.testsRun == 1
+ assert result.shouldStop is False
+ result.stopTest(empty_marionette_test)
+ assert result.shouldStop == has_crashed
+ if has_crashed:
+ assert len(result.errors) == 1
+ else:
+ assert len(result.errors) == 0
+
+
+if __name__ == '__main__':
+ import sys
+ sys.exit(pytest.main(['--verbose', __file__]))
diff --git a/testing/marionette/harness/marionette_harness/tests/harness_unit/test_serve.py b/testing/marionette/harness/marionette_harness/tests/harness_unit/test_serve.py
new file mode 100644
index 000000000..73684c0d6
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/harness_unit/test_serve.py
@@ -0,0 +1,67 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import types
+
+import pytest
+
+from marionette_harness.runner import serve
+from marionette_harness.runner.serve import iter_proc, iter_url
+
+
+def teardown_function(func):
+ for server in [server for server in iter_proc(serve.servers) if server.is_alive]:
+ server.stop()
+ server.kill()
+
+
+def test_registered_servers():
+ # [(name, factory), ...]
+ assert serve.registered_servers[0][0] == "http"
+ assert serve.registered_servers[1][0] == "https"
+
+
+def test_globals():
+ assert serve.default_doc_root is not None
+ assert serve.registered_servers is not None
+ assert serve.servers is not None
+
+
+def test_start():
+ serve.start()
+ assert len(serve.servers) == 2
+ assert "http" in serve.servers
+ assert "https" in serve.servers
+ for url in iter_url(serve.servers):
+ assert isinstance(url, types.StringTypes)
+
+
+def test_start_with_custom_root(tmpdir_factory):
+ tdir = tmpdir_factory.mktemp("foo")
+ serve.start(str(tdir))
+ for server in iter_proc(serve.servers):
+ assert server.doc_root == tdir
+
+
+def test_iter_proc():
+ serve.start()
+ for server in iter_proc(serve.servers):
+ server.stop()
+
+
+def test_iter_url():
+ serve.start()
+ for url in iter_url(serve.servers):
+ assert isinstance(url, types.StringTypes)
+
+
+def test_where_is():
+ serve.start()
+ assert serve.where_is("/") == serve.servers["http"][1].get_url("/")
+ assert serve.where_is("/", on="https") == serve.servers["https"][1].get_url("/")
+
+
+if __name__ == "__main__":
+ import sys
+ sys.exit(pytest.main(["-s", "--verbose", __file__]))
diff --git a/testing/marionette/harness/marionette_harness/tests/unit-tests.ini b/testing/marionette/harness/marionette_harness/tests/unit-tests.ini
new file mode 100644
index 000000000..8d806afea
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit-tests.ini
@@ -0,0 +1,11 @@
+; marionette unit tests
+[include:unit/unit-tests.ini]
+
+; layout tests
+[include:../../../../../layout/base/tests/marionette/manifest.ini]
+
+; microformats tests
+[include:../../../../../toolkit/components/microformats/manifest.ini]
+
+; migration tests
+[include:../../../../../browser/components/migration/tests/marionette/manifest.ini]
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/importanotherscript.js b/testing/marionette/harness/marionette_harness/tests/unit/importanotherscript.js
new file mode 100644
index 000000000..fa6a1fa7c
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/importanotherscript.js
@@ -0,0 +1 @@
+var testAnotherFunc = function() { return "i'm yet another test function!";};
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/importscript.js b/testing/marionette/harness/marionette_harness/tests/unit/importscript.js
new file mode 100644
index 000000000..5a5dd8a18
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/importscript.js
@@ -0,0 +1 @@
+var testFunc = function() { return "i'm a test function!";};
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/mn-restartless-unsigned.xpi b/testing/marionette/harness/marionette_harness/tests/unit/mn-restartless-unsigned.xpi
new file mode 100644
index 000000000..c8877b55d
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/mn-restartless-unsigned.xpi
Binary files differ
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/single_finger_functions.py b/testing/marionette/harness/marionette_harness/tests/unit/single_finger_functions.py
new file mode 100644
index 000000000..c2daf9d54
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/single_finger_functions.py
@@ -0,0 +1,131 @@
+from marionette_driver.marionette import Actions
+from marionette_driver.errors import TimeoutException
+from marionette_driver.by import By
+
+
+def wait_for_condition_else_raise(marionette, wait_for_condition, expected, script):
+ try:
+ wait_for_condition(lambda m: expected in m.execute_script(script))
+ except TimeoutException as e:
+ raise TimeoutException("{0} got {1} instead of {2}".format(
+ e.message, marionette.execute_script(script), expected))
+
+def press_release(marionette, times, wait_for_condition, expected):
+ testAction = marionette.absolute_url("testAction.html")
+ marionette.navigate(testAction)
+ action = Actions(marionette)
+ button = marionette.find_element(By.ID, "button1")
+ action.press(button).release()
+ # Insert wait between each press and release chain.
+ for _ in range(times-1):
+ action.wait(0.1)
+ action.press(button).release()
+ action.perform()
+ wait_for_condition_else_raise(marionette, wait_for_condition, expected, "return document.getElementById('button1').innerHTML;")
+
+def move_element(marionette, wait_for_condition, expected1, expected2):
+ testAction = marionette.absolute_url("testAction.html")
+ marionette.navigate(testAction)
+ ele = marionette.find_element(By.ID, "button1")
+ drop = marionette.find_element(By.ID, "button2")
+ action = Actions(marionette)
+ action.press(ele).move(drop).release()
+ action.perform()
+ wait_for_condition_else_raise(marionette, wait_for_condition, expected1, "return document.getElementById('button1').innerHTML;")
+ wait_for_condition_else_raise(marionette, wait_for_condition, expected2, "return document.getElementById('button2').innerHTML;")
+
+def move_element_offset(marionette, wait_for_condition, expected1, expected2):
+ testAction = marionette.absolute_url("testAction.html")
+ marionette.navigate(testAction)
+ ele = marionette.find_element(By.ID, "button1")
+ action = Actions(marionette)
+ action.press(ele).move_by_offset(0,150).move_by_offset(0, 150).release()
+ action.perform()
+ wait_for_condition_else_raise(marionette, wait_for_condition, expected1, "return document.getElementById('button1').innerHTML;")
+ wait_for_condition_else_raise(marionette, wait_for_condition, expected2, "return document.getElementById('button2').innerHTML;")
+
+def chain(marionette, wait_for_condition, expected1, expected2):
+ testAction = marionette.absolute_url("testAction.html")
+ marionette.navigate(testAction)
+ marionette.timeout.implicit = 15
+ action = Actions(marionette)
+ button1 = marionette.find_element(By.ID, "button1")
+ action.press(button1).perform()
+ button2 = marionette.find_element(By.ID, "delayed")
+ wait_for_condition_else_raise(marionette, wait_for_condition, expected1, "return document.getElementById('button1').innerHTML;")
+ action.move(button2).release().perform()
+ wait_for_condition_else_raise(marionette, wait_for_condition, expected2, "return document.getElementById('delayed').innerHTML;")
+
+def chain_flick(marionette, wait_for_condition, expected1, expected2):
+ testAction = marionette.absolute_url("testAction.html")
+ marionette.navigate(testAction)
+ button = marionette.find_element(By.ID, "button1")
+ action = Actions(marionette)
+ action.flick(button, 0, 0, 0, 200).perform()
+ wait_for_condition_else_raise(marionette, wait_for_condition, expected1,"return document.getElementById('button1').innerHTML;")
+ wait_for_condition_else_raise(marionette, wait_for_condition, expected2,"return document.getElementById('buttonFlick').innerHTML;")
+
+
+def wait(marionette, wait_for_condition, expected):
+ testAction = marionette.absolute_url("testAction.html")
+ marionette.navigate(testAction)
+ action = Actions(marionette)
+ button = marionette.find_element(By.ID, "button1")
+ action.press(button).wait().release().perform()
+ wait_for_condition_else_raise(marionette, wait_for_condition, expected, "return document.getElementById('button1').innerHTML;")
+
+def wait_with_value(marionette, wait_for_condition, expected):
+ testAction = marionette.absolute_url("testAction.html")
+ marionette.navigate(testAction)
+ button = marionette.find_element(By.ID, "button1")
+ action = Actions(marionette)
+ action.press(button).wait(0.01).release()
+ action.perform()
+ wait_for_condition_else_raise(marionette, wait_for_condition, expected, "return document.getElementById('button1').innerHTML;")
+
+def context_menu(marionette, wait_for_condition, expected1, expected2):
+ testAction = marionette.absolute_url("testAction.html")
+ marionette.navigate(testAction)
+ button = marionette.find_element(By.ID, "button1")
+ action = Actions(marionette)
+ action.press(button).wait(5).perform()
+ wait_for_condition_else_raise(marionette, wait_for_condition, expected1, "return document.getElementById('button1').innerHTML;")
+ action.release().perform()
+ wait_for_condition_else_raise(marionette, wait_for_condition, expected2, "return document.getElementById('button1').innerHTML;")
+
+def long_press_action(marionette, wait_for_condition, expected):
+ testAction = marionette.absolute_url("testAction.html")
+ marionette.navigate(testAction)
+ button = marionette.find_element(By.ID, "button1")
+ action = Actions(marionette)
+ action.long_press(button, 5).perform()
+ wait_for_condition_else_raise(marionette, wait_for_condition, expected, "return document.getElementById('button1').innerHTML;")
+
+def long_press_on_xy_action(marionette, wait_for_condition, expected):
+ testAction = marionette.absolute_url("testAction.html")
+ marionette.navigate(testAction)
+ html = marionette.find_element(By.TAG_NAME, "html")
+ button = marionette.find_element(By.ID, "button1")
+ action = Actions(marionette)
+
+ # Press the center of the button with respect to html.
+ x = button.rect['x'] + button.rect['width'] / 2.0
+ y = button.rect['y'] + button.rect['height'] / 2.0
+ action.long_press(html, 5, x, y).perform()
+ wait_for_condition_else_raise(marionette, wait_for_condition, expected, "return document.getElementById('button1').innerHTML;")
+
+def single_tap(marionette, wait_for_condition, expected):
+ testAction = marionette.absolute_url("testAction.html")
+ marionette.navigate(testAction)
+ button = marionette.find_element(By.ID, "button1")
+ action = Actions(marionette)
+ action.tap(button).perform()
+ wait_for_condition_else_raise(marionette, wait_for_condition, expected, "return document.getElementById('button1').innerHTML;")
+
+def double_tap(marionette, wait_for_condition, expected):
+ testAction = marionette.absolute_url("testAction.html")
+ marionette.navigate(testAction)
+ button = marionette.find_element(By.ID, "button1")
+ action = Actions(marionette)
+ action.double_tap(button).perform()
+ wait_for_condition_else_raise(marionette, wait_for_condition, expected, "return document.getElementById('button1').innerHTML;")
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_about_pages.py b/testing/marionette/harness/marionette_harness/tests/unit/test_about_pages.py
new file mode 100644
index 000000000..e9992f8a5
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_about_pages.py
@@ -0,0 +1,134 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver import By, Wait
+from marionette_driver.keys import Keys
+
+from marionette_harness import MarionetteTestCase, skip, skip_if_mobile, WindowManagerMixin
+
+
+class TestAboutPages(WindowManagerMixin, MarionetteTestCase):
+
+ def setUp(self):
+ super(TestAboutPages, self).setUp()
+
+ if self.marionette.session_capabilities['platformName'] == 'darwin':
+ self.mod_key = Keys.META
+ else:
+ self.mod_key = Keys.CONTROL
+
+ self.remote_uri = self.marionette.absolute_url("windowHandles.html")
+
+ def tearDown(self):
+ self.close_all_tabs()
+
+ super(TestAboutPages, self).tearDown()
+
+ def open_tab_with_link(self):
+ with self.marionette.using_context("content"):
+ self.marionette.navigate(self.remote_uri)
+
+ link = self.marionette.find_element(By.ID, "new-tab")
+ link.click()
+
+ @skip_if_mobile("Bug 1333209 - Process killed because of connection loss")
+ def test_back_forward(self):
+ # Bug 1311041 - Prevent changing of window handle by forcing the test
+ # to be run in a new tab.
+ new_tab = self.open_tab(trigger=self.open_tab_with_link)
+ self.marionette.switch_to_window(new_tab)
+
+ self.marionette.navigate("about:blank")
+ self.marionette.navigate(self.remote_uri)
+ self.marionette.navigate("about:support")
+
+ self.marionette.go_back()
+ self.assertEqual(self.marionette.get_url(), self.remote_uri)
+
+ self.marionette.go_forward()
+ self.assertEqual(self.marionette.get_url(), "about:support")
+
+ self.marionette.close()
+ self.marionette.switch_to_window(self.start_tab)
+
+ @skip_if_mobile("Bug 1333209 - Process killed because of connection loss")
+ def test_navigate_non_remote_about_pages(self):
+ # Bug 1311041 - Prevent changing of window handle by forcing the test
+ # to be run in a new tab.
+ new_tab = self.open_tab(trigger=self.open_tab_with_link)
+ self.marionette.switch_to_window(new_tab)
+
+ self.marionette.navigate("about:blank")
+ self.assertEqual(self.marionette.get_url(), "about:blank")
+ self.marionette.navigate("about:support")
+ self.assertEqual(self.marionette.get_url(), "about:support")
+
+ self.marionette.close()
+ self.marionette.switch_to_window(self.start_tab)
+
+ @skip_if_mobile("On Android no shortcuts are available")
+ def test_navigate_shortcut_key(self):
+ def open_with_shortcut():
+ self.marionette.navigate(self.remote_uri)
+ with self.marionette.using_context("chrome"):
+ main_win = self.marionette.find_element(By.ID, "main-window")
+ main_win.send_keys(self.mod_key, Keys.SHIFT, 'a')
+
+ new_tab = self.open_tab(trigger=open_with_shortcut)
+ self.marionette.switch_to_window(new_tab)
+
+ Wait(self.marionette).until(lambda mn: mn.get_url() == "about:addons",
+ message="'about:addons' hasn't been loaded")
+
+ self.marionette.close()
+ self.marionette.switch_to_window(self.start_tab)
+
+ @skip("Bug 1334137 - Intermittent: Process killed because of hang in getCurrentUrl()")
+ @skip_if_mobile("Interacting with chrome elements not available for Fennec")
+ def test_type_to_non_remote_tab(self):
+ # Bug 1311041 - Prevent changing of window handle by forcing the test
+ # to be run in a new tab.
+ new_tab = self.open_tab(trigger=self.open_tab_with_link)
+ self.marionette.switch_to_window(new_tab)
+
+ with self.marionette.using_context("chrome"):
+ urlbar = self.marionette.find_element(By.ID, 'urlbar')
+ urlbar.send_keys(self.mod_key + 'a')
+ urlbar.send_keys(self.mod_key + 'x')
+ urlbar.send_keys('about:support' + Keys.ENTER)
+ Wait(self.marionette).until(lambda mn: mn.get_url() == "about:support",
+ message="'about:support' hasn't been loaded")
+
+ self.marionette.close()
+ self.marionette.switch_to_window(self.start_tab)
+
+ @skip_if_mobile("Interacting with chrome elements not available for Fennec")
+ def test_type_to_remote_tab(self):
+ # Bug 1311041 - Prevent changing of window handle by forcing the test
+ # to be run in a new tab.
+ new_tab = self.open_tab(trigger=self.open_tab_with_link)
+ self.marionette.switch_to_window(new_tab)
+
+ # about:blank keeps remoteness from remote_uri
+ self.marionette.navigate("about:blank")
+ with self.marionette.using_context("chrome"):
+ urlbar = self.marionette.find_element(By.ID, 'urlbar')
+ urlbar.send_keys(self.mod_key + 'a')
+ urlbar.send_keys(self.mod_key + 'x')
+ urlbar.send_keys(self.remote_uri + Keys.ENTER)
+
+ Wait(self.marionette).until(lambda mn: mn.get_url() == self.remote_uri,
+ message="'{}' hasn't been loaded".format(self.remote_uri))
+
+ @skip_if_mobile("Needs application independent method to open a new tab")
+ def test_hang(self):
+ # Bug 1311041 - Prevent changing of window handle by forcing the test
+ # to be run in a new tab.
+ new_tab = self.open_tab(trigger=self.open_tab_with_link)
+
+ # Close the start tab
+ self.marionette.close()
+ self.marionette.switch_to_window(new_tab)
+
+ self.marionette.navigate(self.remote_uri)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_accessibility.py b/testing/marionette/harness/marionette_harness/tests/unit/test_accessibility.py
new file mode 100644
index 000000000..0d8d9dca5
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_accessibility.py
@@ -0,0 +1,210 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+from marionette_driver.errors import (
+ ElementNotAccessibleException,
+ ElementNotInteractableException
+)
+
+from marionette_harness import MarionetteTestCase
+
+
+
+class TestAccessibility(MarionetteTestCase):
+ def setUp(self):
+ super(TestAccessibility, self).setUp()
+ with self.marionette.using_context("chrome"):
+ self.marionette.set_pref("dom.ipc.processCount", 1)
+
+ def tearDown(self):
+ with self.marionette.using_context("chrome"):
+ self.marionette.clear_pref("dom.ipc.processCount")
+
+ # Elements that are accessible with and without the accessibliity API
+ valid_elementIDs = [
+ # Button1 is an accessible button with a valid accessible name
+ # computed from subtree
+ "button1",
+ # Button2 is an accessible button with a valid accessible name
+ # computed from aria-label
+ "button2",
+ # Button13 is an accessible button that is implemented via role="button"
+ # and is explorable using tabindex="0"
+ "button13",
+ # button17 is an accessible button that overrides parent's
+ # pointer-events:none; property with its own pointer-events:all;
+ "button17"
+ ]
+
+ # Elements that are not accessible with the accessibility API
+ invalid_elementIDs = [
+ # Button3 does not have an accessible object
+ "button3",
+ # Button4 does not support any accessible actions
+ "button4",
+ # Button5 does not have a correct accessibility role and may not be
+ # manipulated via the accessibility API
+ "button5",
+ # Button6 is missing an accesible name
+ "button6",
+ # Button7 is not currently visible via the accessibility API and may
+ # not be manipulated by it
+ "button7",
+ # Button8 is not currently visible via the accessibility API and may
+ # not be manipulated by it (in hidden subtree)
+ "button8",
+ # Button14 is accessible button but is not explorable because of lack
+ # of tabindex that would make it focusable.
+ "button14"
+ ]
+
+ # Elements that are either accessible to accessibility API or not accessible
+ # at all
+ falsy_elements = [
+ # Element is only visible to the accessibility API and may be
+ # manipulated by it
+ "button9",
+ # Element is not currently visible
+ "button10"
+ ]
+
+ displayed_elementIDs = [
+ "button1", "button2", "button3", "button4", "button5", "button6",
+ "button9", "no_accessible_but_displayed"
+ ]
+
+ displayed_but_a11y_hidden_elementIDs = ["button7", "button8"]
+
+ disabled_elementIDs = ["button11", "no_accessible_but_disabled"]
+
+ # Elements that are enabled but otherwise disabled or not explorable via the accessibility API
+ disabled_accessibility_elementIDs = ["button12", "button15", "button16"]
+
+ # Elements that are reporting selected state
+ valid_option_elementIDs = ["option1", "option2"]
+
+ def run_element_test(self, ids, testFn):
+ for id in ids:
+ element = self.marionette.find_element(By.ID, id)
+ testFn(element)
+
+ def setup_accessibility(self, enable_a11y_checks=True, navigate=True):
+ self.marionette.delete_session()
+ self.marionette.start_session(
+ {"requiredCapabilities": {"moz:accessibilityChecks": enable_a11y_checks}})
+ self.assertEqual(
+ self.marionette.session_capabilities["moz:accessibilityChecks"],
+ enable_a11y_checks)
+
+ # Navigate to test_accessibility.html
+ if navigate:
+ test_accessibility = self.marionette.absolute_url("test_accessibility.html")
+ self.marionette.navigate(test_accessibility)
+
+ def test_valid_single_tap(self):
+ self.setup_accessibility()
+ # No exception should be raised
+ self.run_element_test(self.valid_elementIDs, lambda button: button.tap())
+
+ def test_single_tap_raises_element_not_accessible(self):
+ self.setup_accessibility()
+ self.run_element_test(self.invalid_elementIDs,
+ lambda button: self.assertRaises(ElementNotAccessibleException,
+ button.tap))
+ self.run_element_test(self.falsy_elements,
+ lambda button: self.assertRaises(ElementNotInteractableException,
+ button.tap))
+
+ def test_single_tap_raises_no_exceptions(self):
+ self.setup_accessibility(False, True)
+ # No exception should be raised
+ self.run_element_test(self.invalid_elementIDs, lambda button: button.tap())
+ # Elements are invisible
+ self.run_element_test(self.falsy_elements,
+ lambda button: self.assertRaises(ElementNotInteractableException,
+ button.tap))
+
+ def test_valid_click(self):
+ self.setup_accessibility()
+ # No exception should be raised
+ self.run_element_test(self.valid_elementIDs, lambda button: button.click())
+
+ def test_click_raises_element_not_accessible(self):
+ self.setup_accessibility()
+ self.run_element_test(self.invalid_elementIDs,
+ lambda button: self.assertRaises(ElementNotAccessibleException,
+ button.click))
+ self.run_element_test(self.falsy_elements,
+ lambda button: self.assertRaises(ElementNotInteractableException,
+ button.click))
+
+ def test_click_raises_no_exceptions(self):
+ self.setup_accessibility(False, True)
+ # No exception should be raised
+ self.run_element_test(self.invalid_elementIDs, lambda button: button.click())
+ # Elements are invisible
+ self.run_element_test(self.falsy_elements,
+ lambda button: self.assertRaises(ElementNotInteractableException,
+ button.click))
+
+ def test_element_visible_but_not_visible_to_accessbility(self):
+ self.setup_accessibility()
+ # Elements are displayed but hidden from accessibility API
+ self.run_element_test(self.displayed_but_a11y_hidden_elementIDs,
+ lambda element: self.assertRaises(ElementNotAccessibleException,
+ element.is_displayed))
+
+ def test_element_is_visible_to_accessibility(self):
+ self.setup_accessibility()
+ # No exception should be raised
+ self.run_element_test(self.displayed_elementIDs, lambda element: element.is_displayed())
+
+ def test_element_is_not_enabled_to_accessbility(self):
+ self.setup_accessibility()
+ # Buttons are enabled but disabled/not-explorable via the accessibility API
+ self.run_element_test(self.disabled_accessibility_elementIDs,
+ lambda element: self.assertRaises(ElementNotAccessibleException,
+ element.is_enabled))
+
+ # Buttons are enabled but disabled/not-explorable via the accessibility API and thus are not
+ # clickable via the accessibility API
+ self.run_element_test(self.disabled_accessibility_elementIDs,
+ lambda element: self.assertRaises(ElementNotAccessibleException,
+ element.click))
+
+ self.setup_accessibility(False, False)
+ self.run_element_test(self.disabled_accessibility_elementIDs,
+ lambda element: element.is_enabled())
+ self.run_element_test(self.disabled_accessibility_elementIDs,
+ lambda element: element.click())
+
+ def test_element_is_enabled_to_accessibility(self):
+ self.setup_accessibility()
+ # No exception should be raised
+ self.run_element_test(self.disabled_elementIDs, lambda element: element.is_enabled())
+
+ def test_send_keys_raises_no_exception(self):
+ self.setup_accessibility()
+ # Sending keys to valid input should not raise any exceptions
+ self.run_element_test(['input1'], lambda element: element.send_keys("a"))
+
+ self.setup_accessibility(False, False)
+ # Sending keys to invalid element should not raise any exceptions when raising accessibility
+ # exceptions is disabled
+ self.run_element_test(['button5'], lambda element: element.send_keys("abc"))
+
+ def test_send_keys_raises_element_not_accessible(self):
+ self.setup_accessibility()
+ # Sending keys to invalid element should raise an exception
+ self.run_element_test(['button5'],
+ lambda element: self.assertRaises(ElementNotAccessibleException,
+ element.send_keys))
+
+ def test_is_selected_raises_no_exception(self):
+ self.setup_accessibility()
+ # No exception should be raised for valid options
+ self.run_element_test(self.valid_option_elementIDs, lambda element: element.is_selected())
+ # No exception should be raised for non-selectable elements
+ self.run_element_test(self.valid_elementIDs, lambda element: element.is_selected())
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_addons.py b/testing/marionette/harness/marionette_harness/tests/unit/test_addons.py
new file mode 100644
index 000000000..25f1c05ab
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_addons.py
@@ -0,0 +1,58 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+
+from marionette_driver.addons import Addons, AddonInstallException
+from marionette_harness import MarionetteTestCase, skip
+
+
+here = os.path.abspath(os.path.dirname(__file__))
+
+
+class TestAddons(MarionetteTestCase):
+
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ self.addons = Addons(self.marionette)
+
+ @property
+ def all_addon_ids(self):
+ with self.marionette.using_context('chrome'):
+ addons = self.marionette.execute_async_script("""
+ Components.utils.import("resource://gre/modules/AddonManager.jsm");
+ AddonManager.getAllAddons(function(addons){
+ let ids = addons.map(function(x) {
+ return x.id;
+ });
+ marionetteScriptFinished(ids);
+ });
+ """)
+
+ return addons
+
+ def test_install_and_remove_temporary_unsigned_addon(self):
+ addon_path = os.path.join(here, 'mn-restartless-unsigned.xpi')
+
+ addon_id = self.addons.install(addon_path, temp=True)
+ self.assertIn(addon_id, self.all_addon_ids)
+
+ self.addons.uninstall(addon_id)
+ self.assertNotIn(addon_id, self.all_addon_ids)
+
+ def test_install_unsigned_addon(self):
+ addon_path = os.path.join(here, 'mn-restartless-unsigned.xpi')
+
+ with self.assertRaises(AddonInstallException):
+ self.addons.install(addon_path)
+
+ @skip("Need to get the test extension signed")
+ def test_install_and_remove_signed_addon(self):
+ addon_path = os.path.join(here, 'mn-restartless-signed.xpi')
+
+ addon_id = self.addons.install(addon_path)
+ self.assertIn(addon_id, self.all_addon_ids)
+
+ self.addons.uninstall(addon_id)
+ self.assertNotIn(addon_id, self.all_addon_ids)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_anonymous_content.py b/testing/marionette/harness/marionette_harness/tests/unit/test_anonymous_content.py
new file mode 100644
index 000000000..1e7779661
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_anonymous_content.py
@@ -0,0 +1,90 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+from marionette_driver.errors import NoSuchElementException
+from marionette_driver.marionette import HTMLElement
+
+from marionette_harness import MarionetteTestCase, WindowManagerMixin
+
+
+class TestAnonymousNodes(WindowManagerMixin, MarionetteTestCase):
+
+ def setUp(self):
+ super(TestAnonymousNodes, self).setUp()
+ self.marionette.set_context("chrome")
+
+ def open_window_with_js():
+ self.marionette.execute_script("""
+ window.open('chrome://marionette/content/test_anonymous_content.xul',
+ 'foo', 'chrome,centerscreen');
+ """)
+
+ new_window = self.open_window(trigger=open_window_with_js)
+ self.marionette.switch_to_window(new_window)
+
+ def tearDown(self):
+ self.close_all_windows()
+
+ super(TestAnonymousNodes, self).tearDown()
+
+ def test_switch_to_anonymous_frame(self):
+ self.marionette.find_element(By.ID, "testAnonymousContentBox")
+ anon_browser_el = self.marionette.find_element(By.ID, "browser")
+ self.assertTrue("test_anonymous_content.xul" in self.marionette.get_url())
+ self.marionette.switch_to_frame(anon_browser_el)
+ self.assertTrue("test.xul" in self.marionette.get_url())
+ self.marionette.find_element(By.ID, "testXulBox")
+ self.assertRaises(NoSuchElementException,
+ self.marionette.find_element, By.ID, "testAnonymousContentBox")
+
+ def test_switch_to_anonymous_iframe(self):
+ self.marionette.find_element(By.ID, "testAnonymousContentBox")
+ el = self.marionette.find_element(By.ID, "container2")
+ anon_iframe_el = el.find_element(By.ANON_ATTRIBUTE, {"anonid": "iframe"})
+ self.marionette.switch_to_frame(anon_iframe_el)
+ self.assertTrue("test.xul" in self.marionette.get_url())
+ self.marionette.find_element(By.ID, "testXulBox")
+ self.assertRaises(NoSuchElementException, self.marionette.find_element, By.ID,
+ "testAnonymousContentBox")
+
+ def test_find_anonymous_element_by_attribute(self):
+ accept_button = (By.ANON_ATTRIBUTE, {"dlgtype": "accept"},)
+ not_existent = (By.ANON_ATTRIBUTE, {"anonid": "notexistent"},)
+
+ # By using the window document element
+ start_node = self.marionette.find_element(By.CSS_SELECTOR, ":root")
+ button = start_node.find_element(*accept_button)
+ self.assertEquals(HTMLElement, type(button))
+ with self.assertRaises(NoSuchElementException):
+ start_node.find_element(*not_existent)
+
+ # By using the default start node
+ self.assertEquals(button, self.marionette.find_element(*accept_button))
+ with self.assertRaises(NoSuchElementException):
+ self.marionette.find_element(*not_existent)
+
+ def test_find_anonymous_elements_by_attribute(self):
+ dialog_buttons = (By.ANON_ATTRIBUTE, {"anonid": "buttons"},)
+ not_existent = (By.ANON_ATTRIBUTE, {"anonid": "notexistent"},)
+
+ # By using the window document element
+ start_node = self.marionette.find_element(By.CSS_SELECTOR, ":root")
+ buttons = start_node.find_elements(*dialog_buttons)
+ self.assertEquals(1, len(buttons))
+ self.assertEquals(HTMLElement, type(buttons[0]))
+ self.assertListEqual([], start_node.find_elements(*not_existent))
+
+ # By using the default start node
+ self.assertListEqual(buttons, self.marionette.find_elements(*dialog_buttons))
+ self.assertListEqual([], self.marionette.find_elements(*not_existent))
+
+ def test_find_anonymous_children(self):
+ self.assertEquals(HTMLElement, type(self.marionette.find_element(By.ANON, None)))
+ self.assertEquals(2, len(self.marionette.find_elements(By.ANON, None)))
+
+ frame = self.marionette.find_element(By.ID, "framebox")
+ with self.assertRaises(NoSuchElementException):
+ frame.find_element(By.ANON, None)
+ self.assertListEqual([], frame.find_elements(By.ANON, None))
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_browsermobproxy.py b/testing/marionette/harness/marionette_harness/tests/unit/test_browsermobproxy.py
new file mode 100644
index 000000000..64b3d1a77
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_browsermobproxy.py
@@ -0,0 +1,34 @@
+import datetime
+
+from marionette_harness.runner import BrowserMobTestCase
+
+
+class TestBrowserMobProxy(BrowserMobTestCase):
+ """To run this test, you'll need to download the browsermob-proxy from
+ http://bmp.lightbody.net/, and then pass the path to the startup
+ script (typically /path/to/browsermob-proxy-2.0.0/bin/browsermob-proxy)
+ as the --browsermob-script argument when running runtests.py.
+
+ You can additionally pass --browsermob-port to specify the port that
+ the proxy will run on; it defaults to 8080.
+
+ This test is NOT run in CI, as bmp and dependencies aren't available
+ there.
+ """
+
+ def test_browsermob_proxy_limits(self):
+ """This illustrates the use of download limits in the proxy,
+ and verifies that it's slower to load a page @100kbps
+ than it is to download the same page @1000kbps.
+ """
+ proxy = self.create_browsermob_proxy()
+ proxy.limits({'downstream_kbps': 1000})
+ time1 = datetime.datetime.now()
+ self.marionette.navigate('http://forecast.weather.gov')
+ time2 = datetime.datetime.now()
+ proxy.limits({'downstream_kbps': 100})
+ time3 = datetime.datetime.now()
+ self.marionette.refresh()
+ time4 = datetime.datetime.now()
+ self.assertTrue(time4 - time3 > time2 - time1,
+ "page load @ 100kbps not slower than page load @ 1000kbps")
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_capabilities.py b/testing/marionette/harness/marionette_harness/tests/unit/test_capabilities.py
new file mode 100644
index 000000000..d3386316d
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_capabilities.py
@@ -0,0 +1,253 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.errors import SessionNotCreatedException
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestCapabilities(MarionetteTestCase):
+
+ def setUp(self):
+ super(TestCapabilities, self).setUp()
+ self.caps = self.marionette.session_capabilities
+ with self.marionette.using_context("chrome"):
+ self.appinfo = self.marionette.execute_script(
+ "return Services.appinfo")
+ self.os_name = self.marionette.execute_script(
+ "return Services.sysinfo.getProperty('name')").lower()
+ self.os_version = self.marionette.execute_script(
+ "return Services.sysinfo.getProperty('version')")
+
+ def test_mandated_capabilities(self):
+ self.assertIn("browserName", self.caps)
+ self.assertIn("browserVersion", self.caps)
+ self.assertIn("platformName", self.caps)
+ self.assertIn("platformVersion", self.caps)
+ self.assertIn("acceptInsecureCerts", self.caps)
+ self.assertIn("timeouts", self.caps)
+
+ self.assertEqual(self.caps["browserName"], self.appinfo["name"].lower())
+ self.assertEqual(self.caps["browserVersion"], self.appinfo["version"])
+ self.assertEqual(self.caps["platformName"], self.os_name)
+ self.assertEqual(self.caps["platformVersion"], self.os_version)
+ self.assertFalse(self.caps["acceptInsecureCerts"])
+ self.assertDictEqual(self.caps["timeouts"],
+ {"implicit": 0,
+ "page load": 300000,
+ "script": 30000})
+
+ def test_supported_features(self):
+ self.assertIn("rotatable", self.caps)
+
+ def test_additional_capabilities(self):
+ self.assertIn("moz:processID", self.caps)
+ self.assertEqual(self.caps["moz:processID"], self.appinfo["processID"])
+ self.assertEqual(self.marionette.process_id, self.appinfo["processID"])
+
+ self.assertIn("moz:profile", self.caps)
+ if self.marionette.instance is not None:
+ if self.caps["browserName"] == "fennec":
+ current_profile = self.marionette.instance.runner.device.app_ctx.remote_profile
+ else:
+ current_profile = self.marionette.instance.runner.profile.profile
+ self.assertEqual(self.caps["moz:profile"], current_profile)
+ self.assertEqual(self.marionette.profile, current_profile)
+
+ self.assertIn("moz:accessibilityChecks", self.caps)
+ self.assertFalse(self.caps["moz:accessibilityChecks"])
+ self.assertIn("specificationLevel", self.caps)
+ self.assertEqual(self.caps["specificationLevel"], 0)
+
+ def test_set_specification_level(self):
+ self.marionette.delete_session()
+ self.marionette.start_session({"desiredCapabilities": {"specificationLevel": 2}})
+ caps = self.marionette.session_capabilities
+ self.assertEqual(2, caps["specificationLevel"])
+
+ self.marionette.delete_session()
+ self.marionette.start_session({"requiredCapabilities": {"specificationLevel": 3}})
+ caps = self.marionette.session_capabilities
+ self.assertEqual(3, caps["specificationLevel"])
+
+ def test_we_can_pass_in_required_capabilities_on_session_start(self):
+ self.marionette.delete_session()
+ capabilities = {"requiredCapabilities": {"browserName": self.appinfo["name"].lower()}}
+ self.marionette.start_session(capabilities)
+ caps = self.marionette.session_capabilities
+ self.assertIn("browserName", caps)
+
+ # Start a new session just to make sure we leave the browser in the
+ # same state it was before it started the test
+ self.marionette.start_session()
+
+ def test_capability_types(self):
+ for value in ["", "invalid", True, 42, []]:
+ print("testing value {}".format(value))
+ with self.assertRaises(SessionNotCreatedException):
+ print(" with desiredCapabilities")
+ self.marionette.delete_session()
+ self.marionette.start_session({"desiredCapabilities": value})
+ with self.assertRaises(SessionNotCreatedException):
+ print(" with requiredCapabilities")
+ self.marionette.delete_session()
+ self.marionette.start_session({"requiredCapabilities": value})
+
+ def test_we_get_valid_uuid4_when_creating_a_session(self):
+ self.assertNotIn("{", self.marionette.session_id,
+ "Session ID has {{}} in it: {}".format(
+ self.marionette.session_id))
+
+
+class TestCapabilityMatching(MarionetteTestCase):
+ allowed = [None, "*"]
+ disallowed = ["", 42, True, {}, []]
+
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ self.browser_name = self.marionette.session_capabilities["browserName"]
+ self.platform_name = self.marionette.session_capabilities["platformName"]
+ self.delete_session()
+
+ def delete_session(self):
+ if self.marionette.session is not None:
+ self.marionette.delete_session()
+
+ def test_browser_name_desired(self):
+ self.marionette.start_session({"desiredCapabilities": {"browserName": self.browser_name}})
+ self.assertEqual(self.marionette.session_capabilities["browserName"], self.browser_name)
+
+ def test_browser_name_required(self):
+ self.marionette.start_session({"requiredCapabilities": {"browserName": self.browser_name}})
+ self.assertEqual(self.marionette.session_capabilities["browserName"], self.browser_name)
+
+ def test_browser_name_desired_allowed_types(self):
+ for typ in self.allowed:
+ self.delete_session()
+ self.marionette.start_session({"desiredCapabilities": {"browserName": typ}})
+ self.assertEqual(self.marionette.session_capabilities["browserName"], self.browser_name)
+
+ def test_browser_name_desired_disallowed_types(self):
+ for typ in self.disallowed:
+ with self.assertRaises(SessionNotCreatedException):
+ self.marionette.start_session({"desiredCapabilities": {"browserName": typ}})
+
+ def test_browser_name_required_allowed_types(self):
+ for typ in self.allowed:
+ self.delete_session()
+ self.marionette.start_session({"requiredCapabilities": {"browserName": typ}})
+ self.assertEqual(self.marionette.session_capabilities["browserName"], self.browser_name)
+
+ def test_browser_name_requried_disallowed_types(self):
+ for typ in self.disallowed:
+ with self.assertRaises(SessionNotCreatedException):
+ self.marionette.start_session({"requiredCapabilities": {"browserName": typ}})
+
+ def test_browser_name_prefers_required(self):
+ caps = {"desiredCapabilities": {"browserName": "invalid"},
+ "requiredCapabilities": {"browserName": "*"}}
+ self.marionette.start_session(caps)
+
+ def test_browser_name_error_on_invalid_required(self):
+ with self.assertRaises(SessionNotCreatedException):
+ caps = {"desiredCapabilities": {"browserName": "*"},
+ "requiredCapabilities": {"browserName": "invalid"}}
+ self.marionette.start_session(caps)
+
+ # TODO(ato): browser version comparison not implemented yet
+
+ def test_platform_name_desired(self):
+ self.marionette.start_session({"desiredCapabilities": {"platformName": self.platform_name}})
+ self.assertEqual(self.marionette.session_capabilities["platformName"], self.platform_name)
+
+ def test_platform_name_required(self):
+ self.marionette.start_session({"requiredCapabilities": {"platformName": self.platform_name}})
+ self.assertEqual(self.marionette.session_capabilities["platformName"], self.platform_name)
+
+ def test_platform_name_desired_allowed_types(self):
+ for typ in self.allowed:
+ self.delete_session()
+ self.marionette.start_session({"desiredCapabilities": {"platformName": typ}})
+ self.assertEqual(self.marionette.session_capabilities["platformName"], self.platform_name)
+
+ def test_platform_name_desired_disallowed_types(self):
+ for typ in self.disallowed:
+ with self.assertRaises(SessionNotCreatedException):
+ self.marionette.start_session({"desiredCapabilities": {"platformName": typ}})
+
+ def test_platform_name_required_allowed_types(self):
+ for typ in self.allowed:
+ self.delete_session()
+ self.marionette.start_session({"requiredCapabilities": {"platformName": typ}})
+ self.assertEqual(self.marionette.session_capabilities["platformName"], self.platform_name)
+
+ def test_platform_name_requried_disallowed_types(self):
+ for typ in self.disallowed:
+ with self.assertRaises(SessionNotCreatedException):
+ self.marionette.start_session({"requiredCapabilities": {"platformName": typ}})
+
+ def test_platform_name_prefers_required(self):
+ caps = {"desiredCapabilities": {"platformName": "invalid"},
+ "requiredCapabilities": {"platformName": "*"}}
+ self.marionette.start_session(caps)
+
+ def test_platform_name_error_on_invalid_required(self):
+ with self.assertRaises(SessionNotCreatedException):
+ caps = {"desiredCapabilities": {"platformName": "*"},
+ "requiredCapabilities": {"platformName": "invalid"}}
+ self.marionette.start_session(caps)
+
+ # TODO(ato): platform version comparison not imlpemented yet
+
+ def test_accept_insecure_certs(self):
+ for capability_type in ["desiredCapabilities", "requiredCapabilities"]:
+ print("testing {}".format(capability_type))
+ for value in ["", 42, {}, []]:
+ print(" type {}".format(type(value)))
+ with self.assertRaises(SessionNotCreatedException):
+ self.marionette.start_session({capability_type: {"acceptInsecureCerts": value}})
+
+ self.delete_session()
+ self.marionette.start_session({"desiredCapabilities": {"acceptInsecureCerts": True}})
+ self.assertTrue(self.marionette.session_capabilities["acceptInsecureCerts"])
+ self.delete_session()
+ self.marionette.start_session({"requiredCapabilities": {"acceptInsecureCerts": True}})
+
+ self.assertTrue(self.marionette.session_capabilities["acceptInsecureCerts"])
+
+ def test_page_load_strategy(self):
+ for strategy in ["none", "eager", "normal"]:
+ print("valid strategy {}".format(strategy))
+ self.delete_session()
+ self.marionette.start_session({"desiredCapabilities": {"pageLoadStrategy": strategy}})
+ self.assertEqual(self.marionette.session_capabilities["pageLoadStrategy"], strategy)
+
+ for value in ["", "EAGER", True, 42, {}, []]:
+ print("invalid strategy {}".format(value))
+ with self.assertRaises(SessionNotCreatedException):
+ self.marionette.start_session({"desiredCapabilities": {"pageLoadStrategy": value}})
+
+ def test_proxy_default(self):
+ self.marionette.start_session()
+ self.assertNotIn("proxy", self.marionette.session_capabilities)
+
+ def test_proxy_desired(self):
+ self.marionette.start_session({"desiredCapabilities": {"proxy": {"proxyType": "manual"}}})
+ self.assertIn("proxy", self.marionette.session_capabilities)
+ self.assertEqual(self.marionette.session_capabilities["proxy"]["proxyType"], "manual")
+ self.assertEqual(self.marionette.get_pref("network.proxy.type"), 1)
+
+ def test_proxy_required(self):
+ self.marionette.start_session({"requiredCapabilities": {"proxy": {"proxyType": "manual"}}})
+ self.assertIn("proxy", self.marionette.session_capabilities)
+ self.assertEqual(self.marionette.session_capabilities["proxy"]["proxyType"], "manual")
+ self.assertEqual(self.marionette.get_pref("network.proxy.type"), 1)
+
+ def test_timeouts(self):
+ timeouts = {u"implicit": 123, u"page load": 456, u"script": 789}
+ caps = {"desiredCapabilities": {"timeouts": timeouts}}
+ self.marionette.start_session(caps)
+ self.assertIn("timeouts", self.marionette.session_capabilities)
+ self.assertDictEqual(self.marionette.session_capabilities["timeouts"], timeouts)
+ self.assertDictEqual(self.marionette._send_message("getTimeouts"), timeouts)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_checkbox.py b/testing/marionette/harness/marionette_harness/tests/unit/test_checkbox.py
new file mode 100644
index 000000000..8709d6e32
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_checkbox.py
@@ -0,0 +1,17 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestCheckbox(MarionetteTestCase):
+ def test_selected(self):
+ test_html = self.marionette.absolute_url("test.html")
+ self.marionette.navigate(test_html)
+ box = self.marionette.find_element(By.NAME, "myCheckBox")
+ self.assertFalse(box.is_selected())
+ box.click()
+ self.assertTrue(box.is_selected())
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_checkbox_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_checkbox_chrome.py
new file mode 100644
index 000000000..8d800f939
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_checkbox_chrome.py
@@ -0,0 +1,36 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+
+from marionette_harness import MarionetteTestCase, WindowManagerMixin
+
+
+class TestSelectedChrome(WindowManagerMixin, MarionetteTestCase):
+
+ def setUp(self):
+ super(TestSelectedChrome, self).setUp()
+
+ self.marionette.set_context("chrome")
+
+ def open_window_with_js():
+ self.marionette.execute_script("""
+ window.open('chrome://marionette/content/test.xul',
+ '_blank', 'chrome,centerscreen');
+ """)
+
+ new_window = self.open_window(trigger=open_window_with_js)
+ self.marionette.switch_to_window(new_window)
+
+ def tearDown(self):
+ try:
+ self.close_all_windows()
+ finally:
+ super(TestSelectedChrome, self).tearDown()
+
+ def test_selected(self):
+ box = self.marionette.find_element(By.ID, "testBox")
+ self.assertFalse(box.is_selected())
+ self.assertFalse(self.marionette.execute_script("arguments[0].checked = true;", [box]))
+ self.assertTrue(box.is_selected())
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_chrome.py
new file mode 100644
index 000000000..8a9e53bd6
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_chrome.py
@@ -0,0 +1,51 @@
+#Copyright 2007-2009 WebDriver committers
+#Copyright 2007-2009 Google Inc.
+#
+#Licensed under the Apache License, Version 2.0 (the "License");
+#you may not use this file except in compliance with the License.
+#You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#Unless required by applicable law or agreed to in writing, software
+#distributed under the License is distributed on an "AS IS" BASIS,
+#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#See the License for the specific language governing permissions and
+#limitations under the License.
+
+from marionette_driver import By
+
+from marionette_harness import MarionetteTestCase, WindowManagerMixin
+
+
+class ChromeTests(WindowManagerMixin, MarionetteTestCase):
+
+ def setUp(self):
+ super(ChromeTests, self).setUp()
+
+ self.marionette.set_context('chrome')
+
+ def tearDown(self):
+ self.close_all_windows()
+ super(ChromeTests, self).tearDown()
+
+ def test_hang_until_timeout(self):
+ def open_with_menu():
+ menu = self.marionette.find_element(By.ID, 'aboutName')
+ menu.click()
+
+ new_window = self.open_window(trigger=open_with_menu)
+ self.marionette.switch_to_window(new_window)
+
+ try:
+ try:
+ # Raise an exception type which should not be thrown by Marionette
+ # while running this test. Otherwise it would mask eg. IOError as
+ # thrown for a socket timeout.
+ raise NotImplementedError('Exception should not cause a hang when '
+ 'closing the chrome window')
+ finally:
+ self.marionette.close_chrome_window()
+ self.marionette.switch_to_window(self.start_window)
+ except NotImplementedError:
+ pass
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_chrome_async_finish.js b/testing/marionette/harness/marionette_harness/tests/unit/test_chrome_async_finish.js
new file mode 100644
index 000000000..8d2df3ac2
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_chrome_async_finish.js
@@ -0,0 +1,6 @@
+MARIONETTE_TIMEOUT = 60000;
+MARIONETTE_CONTEXT = "chrome";
+ok(true);
+(function () {
+ finish();
+})();
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_chrome_element_css.py b/testing/marionette/harness/marionette_harness/tests/unit/test_chrome_element_css.py
new file mode 100644
index 000000000..fde7e8373
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_chrome_element_css.py
@@ -0,0 +1,23 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestChromeElementCSS(MarionetteTestCase):
+
+ def test_we_can_get_css_value_on_chrome_element(self):
+ self.marionette.navigate("about:blank")
+ with self.marionette.using_context("chrome"):
+ element = self.marionette.find_element(By.ID, "identity-icon")
+ favicon_image = element.value_of_css_property("list-style-image")
+
+ self.assertIn("identity-icon.svg", favicon_image)
+
+ element = self.marionette.find_element(By.ID, "identity-box")
+ background_colour = element.value_of_css_property("background-color")
+
+ self.assertEqual("transparent", background_colour)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_clearing.py b/testing/marionette/harness/marionette_harness/tests/unit/test_clearing.py
new file mode 100644
index 000000000..3fcad45fc
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_clearing.py
@@ -0,0 +1,72 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+from marionette_driver.errors import InvalidElementStateException
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestClear(MarionetteTestCase):
+ def testWriteableTextInputShouldClear(self):
+ test_html = self.marionette.absolute_url("test_clearing.html")
+ self.marionette.navigate(test_html)
+ element = self.marionette.find_element(By.ID, "writableTextInput")
+ element.clear()
+ self.assertEqual("", element.get_property("value"))
+
+ def testTextInputShouldNotClearWhenReadOnly(self):
+ test_html = self.marionette.absolute_url("test_clearing.html")
+ self.marionette.navigate(test_html)
+ element = self.marionette.find_element(By.ID,"readOnlyTextInput")
+ try:
+ element.clear()
+ self.fail("Should not have been able to clear")
+ except InvalidElementStateException:
+ pass
+
+ def testWritableTextAreaShouldClear(self):
+ test_html = self.marionette.absolute_url("test_clearing.html")
+ self.marionette.navigate(test_html)
+ element = self.marionette.find_element(By.ID,"writableTextArea")
+ element.clear()
+ self.assertEqual("", element.get_property("value"))
+
+ def testTextAreaShouldNotClearWhenDisabled(self):
+ test_html = self.marionette.absolute_url("test_clearing.html")
+ self.marionette.navigate(test_html)
+ element = self.marionette.find_element(By.ID,"textAreaNotenabled")
+ try:
+ element.clear()
+ self.fail("Should not have been able to clear")
+ except InvalidElementStateException:
+ pass
+
+ def testTextAreaShouldNotClearWhenReadOnly(self):
+ test_html = self.marionette.absolute_url("test_clearing.html")
+ self.marionette.navigate(test_html)
+ element = self.marionette.find_element(By.ID,"textAreaReadOnly")
+ try:
+ element.clear()
+ self.fail("Should not have been able to clear")
+ except InvalidElementStateException:
+ pass
+
+ def testContentEditableAreaShouldClear(self):
+ test_html = self.marionette.absolute_url("test_clearing.html")
+ self.marionette.navigate(test_html)
+ element = self.marionette.find_element(By.ID,"content-editable")
+ element.clear()
+ self.assertEqual("", element.text)
+
+ def testTextInputShouldNotClearWhenDisabled(self):
+ test_html = self.marionette.absolute_url("test_clearing.html")
+ self.marionette.navigate(test_html)
+ try:
+ element = self.marionette.find_element(By.ID,"textInputnotenabled")
+ self.assertFalse(element.is_enabled())
+ element.clear()
+ self.fail("Should not have been able to clear")
+ except InvalidElementStateException:
+ pass
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_click.py b/testing/marionette/harness/marionette_harness/tests/unit/test_click.py
new file mode 100644
index 000000000..d03062e85
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_click.py
@@ -0,0 +1,254 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import urllib
+
+from marionette_driver.by import By
+from marionette_driver import errors
+from marionette_driver.wait import Wait
+
+from marionette_harness import MarionetteTestCase
+
+
+def inline(doc):
+ return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+
+
+# The <a> element in the following HTML is not interactable because it
+# is hidden by an overlay when scrolled into the top of the viewport.
+# It should be interactable when scrolled in at the bottom of the
+# viewport.
+fixed_overlay = inline("""
+<style>
+* { margin: 0; padding: 0; }
+body { height: 300vh }
+div, a { display: block }
+div {
+ background-color: pink;
+ position: fixed;
+ width: 100%;
+ height: 40px;
+ top: 0;
+}
+a {
+ margin-top: 1000px;
+}
+</style>
+
+<div>overlay</div>
+<a href=#>link</a>
+
+<script>
+window.clicked = false;
+
+let link = document.querySelector("a");
+link.addEventListener("click", () => window.clicked = true);
+</script>
+""")
+
+
+obscured_overlay = inline("""
+<style>
+* { margin: 0; padding: 0; }
+body { height: 100vh }
+#overlay {
+ background-color: pink;
+ position: absolute;
+ width: 100%;
+ height: 100%;
+}
+</style>
+
+<div id=overlay></div>
+<a id=obscured href=#>link</a>
+
+<script>
+window.clicked = false;
+
+let link = document.querySelector("#obscured");
+link.addEventListener("click", () => window.clicked = true);
+</script>
+""")
+
+
+class TestLegacyClick(MarionetteTestCase):
+ """Uses legacy Selenium element displayedness checks."""
+
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ self.marionette.delete_session()
+ self.marionette.start_session()
+
+ def test_click(self):
+ self.marionette.navigate(inline("""
+ <button>click me</button>
+ <script>
+ window.clicks = 0;
+ let button = document.querySelector("button");
+ button.addEventListener("click", () => window.clicks++);
+ </script>
+ """))
+ button = self.marionette.find_element(By.TAG_NAME, "button")
+ button.click()
+ self.assertEqual(1, self.marionette.execute_script("return window.clicks", sandbox=None))
+
+ def test_click_number_link(self):
+ test_html = self.marionette.absolute_url("clicks.html")
+ self.marionette.navigate(test_html)
+ self.marionette.find_element(By.LINK_TEXT, "333333").click()
+ Wait(self.marionette, timeout=30, ignored_exceptions=errors.NoSuchElementException).until(
+ lambda m: m.find_element(By.ID, "username"))
+ self.assertEqual(self.marionette.title, "XHTML Test Page")
+
+ def test_clicking_an_element_that_is_not_displayed_raises(self):
+ test_html = self.marionette.absolute_url("hidden.html")
+ self.marionette.navigate(test_html)
+
+ with self.assertRaises(errors.ElementNotInteractableException):
+ self.marionette.find_element(By.ID, "child").click()
+
+ def test_clicking_on_a_multiline_link(self):
+ test_html = self.marionette.absolute_url("clicks.html")
+ self.marionette.navigate(test_html)
+ self.marionette.find_element(By.ID, "overflowLink").click()
+ self.wait_for_condition(lambda mn: self.marionette.title == "XHTML Test Page")
+
+ def test_scroll_into_view_near_end(self):
+ self.marionette.navigate(fixed_overlay)
+ link = self.marionette.find_element(By.TAG_NAME, "a")
+ link.click()
+ self.assertTrue(self.marionette.execute_script("return window.clicked", sandbox=None))
+
+
+class TestClick(TestLegacyClick):
+ """Uses WebDriver specification compatible element interactability
+ checks.
+ """
+
+ def setUp(self):
+ TestLegacyClick.setUp(self)
+ self.marionette.delete_session()
+ self.marionette.start_session(
+ {"requiredCapabilities": {"specificationLevel": 1}})
+
+ def test_click_element_obscured_by_absolute_positioned_element(self):
+ self.marionette.navigate(obscured_overlay)
+ overlay = self.marionette.find_element(By.ID, "overlay")
+ obscured = self.marionette.find_element(By.ID, "obscured")
+
+ overlay.click()
+ with self.assertRaises(errors.ElementClickInterceptedException):
+ obscured.click()
+
+ def test_centre_outside_viewport_vertically(self):
+ self.marionette.navigate(inline("""
+ <style>
+ * { margin: 0; padding: 0; }
+ div {
+ display: block;
+ position: absolute;
+ background-color: blue;
+ width: 200px;
+ height: 200px;
+
+ /* move centre point off viewport vertically */
+ top: -105px;
+ }
+ </style>
+
+ <div></div>"""))
+
+ self.marionette.find_element(By.TAG_NAME, "div").click()
+
+ def test_centre_outside_viewport_horizontally(self):
+ self.marionette.navigate(inline("""
+ <style>
+ * { margin: 0; padding: 0; }
+ div {
+ display: block;
+ position: absolute;
+ background-color: blue;
+ width: 200px;
+ height: 200px;
+
+ /* move centre point off viewport horizontally */
+ left: -105px;
+ }
+ </style>
+
+ <div></div>"""))
+
+ self.marionette.find_element(By.TAG_NAME, "div").click()
+
+ def test_centre_outside_viewport(self):
+ self.marionette.navigate(inline("""
+ <style>
+ * { margin: 0; padding: 0; }
+ div {
+ display: block;
+ position: absolute;
+ background-color: blue;
+ width: 200px;
+ height: 200px;
+
+ /* move centre point off viewport */
+ left: -105px;
+ top: -105px;
+ }
+ </style>
+
+ <div></div>"""))
+
+ self.marionette.find_element(By.TAG_NAME, "div").click()
+
+ def test_css_transforms(self):
+ self.marionette.navigate(inline("""
+ <style>
+ * { margin: 0; padding: 0; }
+ div {
+ display: block;
+ background-color: blue;
+ width: 200px;
+ height: 200px;
+
+ transform: translateX(-105px);
+ }
+ </style>
+
+ <div></div>"""))
+
+ self.marionette.find_element(By.TAG_NAME, "div").click()
+
+ def test_input_file(self):
+ self.marionette.navigate(inline("<input type=file>"))
+ with self.assertRaises(errors.InvalidArgumentException):
+ self.marionette.find_element(By.TAG_NAME, "input").click()
+
+ def test_container_element(self):
+ self.marionette.navigate(inline("""
+ <select>
+ <option>foo</option>
+ </select>"""))
+ option = self.marionette.find_element(By.TAG_NAME, "option")
+ option.click()
+ self.assertTrue(option.get_property("selected"))
+
+ def test_container_element_outside_view(self):
+ self.marionette.navigate(inline("""
+ <select style="margin-top: 100vh">
+ <option>foo</option>
+ </select>"""))
+ option = self.marionette.find_element(By.TAG_NAME, "option")
+ option.click()
+ self.assertTrue(option.get_property("selected"))
+
+ def test_obscured_element(self):
+ self.marionette.navigate(obscured_overlay)
+ overlay = self.marionette.find_element(By.ID, "overlay")
+ obscured = self.marionette.find_element(By.ID, "obscured")
+
+ overlay.click()
+ with self.assertRaises(errors.ElementClickInterceptedException):
+ obscured.click()
+ self.assertFalse(self.marionette.execute_script("return window.clicked", sandbox=None))
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_click_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_click_chrome.py
new file mode 100644
index 000000000..d16b4f105
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_click_chrome.py
@@ -0,0 +1,35 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestClickChrome(MarionetteTestCase):
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ self.root_window = self.marionette.current_window_handle
+ self.marionette.set_context("chrome")
+ self.marionette.execute_script(
+ "window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen')")
+ self.marionette.switch_to_window("foo")
+ self.assertNotEqual(self.root_window, self.marionette.current_window_handle)
+
+ def tearDown(self):
+ self.assertNotEqual(self.root_window, self.marionette.current_window_handle)
+ self.marionette.execute_script("window.close()")
+ self.marionette.switch_to_window(self.root_window)
+ MarionetteTestCase.tearDown(self)
+
+ def test_click(self):
+ def checked():
+ return self.marionette.execute_script(
+ "return arguments[0].checked",
+ script_args=[box])
+
+ box = self.marionette.find_element(By.ID, "testBox")
+ self.assertFalse(checked())
+ box.click()
+ self.assertTrue(checked())
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_click_scrolling.py b/testing/marionette/harness/marionette_harness/tests/unit/test_click_scrolling.py
new file mode 100644
index 000000000..437c15e70
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_click_scrolling.py
@@ -0,0 +1,117 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+from marionette_driver.errors import MoveTargetOutOfBoundsException
+
+from marionette_harness import MarionetteTestCase, skip, skip_if_mobile
+
+
+class TestClickScrolling(MarionetteTestCase):
+
+
+ def test_clicking_on_anchor_scrolls_page(self):
+ scrollScript = """
+ var pageY;
+ if (typeof(window.pageYOffset) == 'number') {
+ pageY = window.pageYOffset;
+ } else {
+ pageY = document.documentElement.scrollTop;
+ }
+ return pageY;"""
+
+ test_html = self.marionette.absolute_url("macbeth.html")
+ self.marionette.navigate(test_html)
+
+ self.marionette.find_element(By.PARTIAL_LINK_TEXT, "last speech").click()
+ y_offset = self.marionette.execute_script(scrollScript)
+
+ # Focusing on to click, but not actually following,
+ # the link will scroll it in to view, which is a few
+ # pixels further than 0
+
+ self.assertTrue(y_offset > 300)
+
+ def test_should_scroll_to_click_on_an_element_hidden_by_overflow(self):
+ test_html = self.marionette.absolute_url("click_out_of_bounds_overflow.html")
+ self.marionette.navigate(test_html)
+
+ link = self.marionette.find_element(By.ID, "link")
+ try:
+ link.click()
+ except MoveTargetOutOfBoundsException:
+ self.fail("Should not be out of bounds")
+
+ @skip("Bug 1200197 - Cannot interact with elements hidden inside overflow:scroll")
+ def test_should_be_able_to_click_on_an_element_hidden_by_overflow(self):
+ test_html = self.marionette.absolute_url("scroll.html")
+ self.marionette.navigate(test_html)
+
+ link = self.marionette.find_element(By.ID, "line8")
+ link.click()
+ self.assertEqual("line8", self.marionette.find_element(By.ID, "clicked").text)
+
+ def test_should_not_scroll_overflow_elements_which_are_visible(self):
+ test_html = self.marionette.absolute_url("scroll2.html")
+ self.marionette.navigate(test_html)
+
+ list_el = self.marionette.find_element(By.TAG_NAME, "ul")
+ item = list_el.find_element(By.ID, "desired")
+ item.click()
+ y_offset = self.marionette.execute_script("return arguments[0].scrollTop;", script_args=[list_el])
+ self.assertEqual(0, y_offset)
+
+ def test_should_not_scroll_if_already_scrolled_and_element_is_in_view(self):
+ test_html = self.marionette.absolute_url("scroll3.html")
+ self.marionette.navigate(test_html)
+
+ button1 = self.marionette.find_element(By.ID, "button1")
+ button2 = self.marionette.find_element(By.ID, "button2")
+
+ button2.click()
+ scroll_top = self.marionette.execute_script("return document.body.scrollTop;")
+ button1.click()
+
+ self.assertEqual(scroll_top, self.marionette.execute_script("return document.body.scrollTop;"))
+
+ def test_should_be_able_to_click_radio_button_scrolled_into_view(self):
+ test_html = self.marionette.absolute_url("scroll4.html")
+ self.marionette.navigate(test_html)
+
+ # If we dont throw we are good
+ self.marionette.find_element(By.ID, "radio").click()
+
+ def test_should_scroll_elements_if_click_point_is_out_of_view_but_element_is_in_view(self):
+ test_html = self.marionette.absolute_url("element_outside_viewport.html")
+
+ for s in ["top", "bottom"]:
+ self.marionette.navigate(test_html)
+ scroll_y = self.marionette.execute_script("return window.scrollY;")
+ self.marionette.find_element(By.ID, "{}-70".format(s)).click()
+ self.assertNotEqual(scroll_y, self.marionette.execute_script("return window.scrollY;"))
+
+ for s in ["left", "right"]:
+ self.marionette.navigate(test_html)
+ scroll_x = self.marionette.execute_script("return window.scrollX;")
+ self.marionette.find_element(By.ID, "{}-70".format(s)).click()
+ self.assertNotEqual(scroll_x, self.marionette.execute_script("return window.scrollX;"))
+
+ @skip_if_mobile("Bug 1293855 - Lists differ: [70, 70] != [70, 120]")
+ def test_should_not_scroll_elements_if_click_point_is_in_view(self):
+ test_html = self.marionette.absolute_url("element_outside_viewport.html")
+
+ for s in ["top", "right", "bottom", "left"]:
+ for p in ["50", "30"]:
+ self.marionette.navigate(test_html)
+ scroll = self.marionette.execute_script("return [window.scrollX, window.scrollY];")
+ self.marionette.find_element(By.ID, "{0}-{1}".format(s, p)).click()
+ self.assertEqual(scroll, self.marionette.execute_script("return [window.scrollX, window.scrollY];"))
+
+ @skip("Bug 1003687")
+ def test_should_scroll_overflow_elements_if_click_point_is_out_of_view_but_element_is_in_view(self):
+ test_html = self.marionette.absolute_url("scroll5.html")
+ self.marionette.navigate(test_html)
+
+ self.marionette.find_element(By.ID, "inner").click()
+ self.assertEqual("clicked", self.marionette.find_element(By.ID, "clicked").text)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_cookies.py b/testing/marionette/harness/marionette_harness/tests/unit/test_cookies.py
new file mode 100644
index 000000000..f7841c73e
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_cookies.py
@@ -0,0 +1,115 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import calendar
+import random
+import time
+
+from marionette_driver.errors import UnsupportedOperationException
+from marionette_harness import MarionetteTestCase
+
+
+class CookieTest(MarionetteTestCase):
+
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ test_url = self.marionette.absolute_url('test.html')
+ self.marionette.navigate(test_url)
+ self.COOKIE_A = {"name": "foo",
+ "value": "bar",
+ "path": "/",
+ "secure": False}
+
+ def tearDown(self):
+ self.marionette.delete_all_cookies()
+ MarionetteTestCase.tearDown(self)
+
+ def test_add_cookie(self):
+ self.marionette.add_cookie(self.COOKIE_A)
+ cookie_returned = str(self.marionette.execute_script("return document.cookie"))
+ self.assertTrue(self.COOKIE_A["name"] in cookie_returned)
+
+ def test_adding_a_cookie_that_expired_in_the_past(self):
+ cookie = self.COOKIE_A.copy()
+ cookie["expiry"] = calendar.timegm(time.gmtime()) - 1
+ self.marionette.add_cookie(cookie)
+ cookies = self.marionette.get_cookies()
+ self.assertEquals(0, len(cookies))
+
+ def test_chrome_error(self):
+ with self.marionette.using_context("chrome"):
+ self.assertRaises(UnsupportedOperationException,
+ self.marionette.add_cookie, self.COOKIE_A)
+ self.assertRaises(UnsupportedOperationException,
+ self.marionette.delete_cookie, self.COOKIE_A)
+ self.assertRaises(UnsupportedOperationException,
+ self.marionette.delete_all_cookies)
+ self.assertRaises(UnsupportedOperationException,
+ self.marionette.get_cookies)
+
+ def test_delete_all_cookie(self):
+ self.marionette.add_cookie(self.COOKIE_A)
+ cookie_returned = str(self.marionette.execute_script("return document.cookie"))
+ print cookie_returned
+ self.assertTrue(self.COOKIE_A["name"] in cookie_returned)
+ self.marionette.delete_all_cookies()
+ self.assertFalse(self.marionette.get_cookies())
+
+ def test_delete_cookie(self):
+ self.marionette.add_cookie(self.COOKIE_A)
+ cookie_returned = str(self.marionette.execute_script("return document.cookie"))
+ self.assertTrue(self.COOKIE_A["name"] in cookie_returned)
+ self.marionette.delete_cookie("foo")
+ cookie_returned = str(self.marionette.execute_script("return document.cookie"))
+ self.assertFalse(self.COOKIE_A["name"] in cookie_returned)
+
+ def test_should_get_cookie_by_name(self):
+ key = "key_{}".format(int(random.random()*10000000))
+ self.marionette.execute_script("document.cookie = arguments[0] + '=set';", [key])
+
+ cookie = self.marionette.get_cookie(key)
+ self.assertEquals("set", cookie["value"])
+
+ def test_get_all_cookies(self):
+ key1 = "key_{}".format(int(random.random()*10000000))
+ key2 = "key_{}".format(int(random.random()*10000000))
+
+ cookies = self.marionette.get_cookies()
+ count = len(cookies)
+
+ one = {"name" :key1,
+ "value": "value"}
+ two = {"name":key2,
+ "value": "value"}
+
+ self.marionette.add_cookie(one)
+ self.marionette.add_cookie(two)
+
+ test_url = self.marionette.absolute_url('test.html')
+ self.marionette.navigate(test_url)
+ cookies = self.marionette.get_cookies()
+ self.assertEquals(count + 2, len(cookies))
+
+ def test_should_not_delete_cookies_with_a_similar_name(self):
+ cookieOneName = "fish"
+ cookie1 = {"name" :cookieOneName,
+ "value":"cod"}
+ cookie2 = {"name" :cookieOneName + "x",
+ "value": "earth"}
+ self.marionette.add_cookie(cookie1)
+ self.marionette.add_cookie(cookie2)
+
+ self.marionette.delete_cookie(cookieOneName)
+ cookies = self.marionette.get_cookies()
+
+ self.assertFalse(cookie1["name"] == cookies[0]["name"], msg=str(cookies))
+ self.assertEquals(cookie2["name"] , cookies[0]["name"], msg=str(cookies))
+
+ def test_we_get_required_elements_when_available(self):
+ self.marionette.add_cookie(self.COOKIE_A)
+ cookies = self.marionette.get_cookies()
+
+ self.assertIn("name", cookies[0], 'name not available')
+ self.assertIn("value", cookies[0], 'value not available')
+ self.assertIn("httpOnly", cookies[0], 'httpOnly not available')
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_crash.py b/testing/marionette/harness/marionette_harness/tests/unit/test_crash.py
new file mode 100644
index 000000000..7e74f0857
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_crash.py
@@ -0,0 +1,155 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import glob
+import shutil
+
+from marionette_driver.errors import MarionetteException
+# Import runner module to monkey patch mozcrash module
+from mozrunner.base import runner
+
+from marionette_harness import MarionetteTestCase, expectedFailure, run_if_e10s
+
+
+class MockMozCrash(object):
+ """Mock object to replace original mozcrash methods."""
+
+ def __init__(self, marionette):
+ self.marionette = marionette
+
+ with self.marionette.using_context('chrome'):
+ self.crash_reporter_enabled = self.marionette.execute_script("""
+ try {
+ Components.classes["@mozilla.org/toolkit/crash-reporter;1"].
+ getService(Components.interfaces.nsICrashReporter);
+ return true;
+ } catch (exc) {
+ return false;
+ }
+ """)
+
+ def check_for_crashes(self, dump_directory, *args, **kwargs):
+ minidump_files = glob.glob('{}/*.dmp'.format(dump_directory))
+ shutil.rmtree(dump_directory, ignore_errors=True)
+
+ if self.crash_reporter_enabled:
+ return len(minidump_files)
+ else:
+ return len(minidump_files) == 0
+
+ def log_crashes(self, logger, dump_directory, *args, **kwargs):
+ return self.check_for_crashes(dump_directory, *args, **kwargs)
+
+
+class BaseCrashTestCase(MarionetteTestCase):
+
+ # Reduce the timeout for faster processing of the tests
+ socket_timeout = 10
+
+ def setUp(self):
+ super(BaseCrashTestCase, self).setUp()
+
+ self.mozcrash_mock = MockMozCrash(self.marionette)
+ self.crash_count = self.marionette.crashed
+ self.pid = self.marionette.process_id
+ self.remote_uri = self.marionette.absolute_url("javascriptPage.html")
+
+ def tearDown(self):
+ self.marionette.crashed = self.crash_count
+
+ super(BaseCrashTestCase, self).tearDown()
+
+ def crash(self, chrome=True):
+ context = 'chrome' if chrome else 'content'
+ sandbox = None if chrome else 'system'
+
+ # Monkey patch mozcrash to avoid crash info output only for our triggered crashes.
+ mozcrash = runner.mozcrash
+ runner.mozcrash = self.mozcrash_mock
+
+ socket_timeout = self.marionette.client.socket_timeout
+
+ self.marionette.set_context(context)
+ try:
+ self.marionette.client.socket_timeout = self.socket_timeout
+ self.marionette.execute_script("""
+ // Copied from crash me simple
+ Components.utils.import("resource://gre/modules/ctypes.jsm");
+
+ // ctypes checks for NULL pointer derefs, so just go near-NULL.
+ var zero = new ctypes.intptr_t(8);
+ var badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
+ var crash = badptr.contents;
+ """, sandbox=sandbox)
+ finally:
+ runner.mozcrash = mozcrash
+ self.marionette.client.socket_timeout = socket_timeout
+
+
+class TestCrash(BaseCrashTestCase):
+
+ def test_crash_chrome_process(self):
+ self.assertRaisesRegexp(IOError, 'Process crashed',
+ self.crash, chrome=True)
+ self.assertEqual(self.marionette.crashed, 1)
+ self.assertIsNone(self.marionette.session)
+ self.assertRaisesRegexp(MarionetteException, 'Please start a session',
+ self.marionette.get_url)
+
+ self.marionette.start_session()
+ self.assertNotEqual(self.marionette.process_id, self.pid)
+
+ # TODO: Bug 1314594 - Causes a hang for the communication between the
+ # chrome and frame script.
+ # self.marionette.get_url()
+
+ @run_if_e10s("Content crashes only exist in e10s mode")
+ def test_crash_content_process(self):
+ # If e10s is disabled the chrome process crashes
+ self.marionette.navigate(self.remote_uri)
+
+ self.assertRaisesRegexp(IOError, 'Content process crashed',
+ self.crash, chrome=False)
+ self.assertEqual(self.marionette.crashed, 1)
+ self.assertIsNone(self.marionette.session)
+ self.assertRaisesRegexp(MarionetteException, 'Please start a session',
+ self.marionette.get_url)
+
+ self.marionette.start_session()
+ self.assertNotEqual(self.marionette.process_id, self.pid)
+ self.marionette.get_url()
+
+ @expectedFailure
+ def test_unexpected_crash(self):
+ self.crash(chrome=True)
+
+
+class TestCrashInSetUp(BaseCrashTestCase):
+
+ def setUp(self):
+ super(TestCrashInSetUp, self).setUp()
+
+ self.assertRaisesRegexp(IOError, 'Process crashed',
+ self.crash, chrome=True)
+ self.assertEqual(self.marionette.crashed, 1)
+ self.assertIsNone(self.marionette.session)
+
+ def test_crash_in_setup(self):
+ self.marionette.start_session()
+ self.assertNotEqual(self.marionette.process_id, self.pid)
+
+
+class TestCrashInTearDown(BaseCrashTestCase):
+
+ def tearDown(self):
+ try:
+ self.assertRaisesRegexp(IOError, 'Process crashed',
+ self.crash, chrome=True)
+ finally:
+ self.assertEqual(self.marionette.crashed, 1)
+ self.assertIsNone(self.marionette.session)
+ super(TestCrashInTearDown, self).tearDown()
+
+ def test_crash_in_teardown(self):
+ pass
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_data_driven.py b/testing/marionette/harness/marionette_harness/tests/unit/test_data_driven.py
new file mode 100644
index 000000000..8e4ae0d32
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_data_driven.py
@@ -0,0 +1,67 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_harness.marionette_test import (
+ parameterized,
+ with_parameters,
+ MetaParameterized,
+ MarionetteTestCase
+)
+
+class Parameterizable(object):
+ __metaclass__ = MetaParameterized
+
+class TestDataDriven(MarionetteTestCase):
+ def test_parameterized(self):
+ class Test(Parameterizable):
+ def __init__(self):
+ self.parameters = []
+
+ @parameterized('1', 'thing', named=43)
+ @parameterized('2', 'thing2')
+ def test(self, thing, named=None):
+ self.parameters.append((thing, named))
+
+ self.assertFalse(hasattr(Test, 'test'))
+ self.assertTrue(hasattr(Test, 'test_1'))
+ self.assertTrue(hasattr(Test, 'test_2'))
+
+ test = Test()
+ test.test_1()
+ test.test_2()
+
+ self.assertEquals(test.parameters, [('thing', 43), ('thing2', None)])
+
+ def test_with_parameters(self):
+ DATA = [('1', ('thing',), {'named': 43}),
+ ('2', ('thing2',), {'named': None})]
+
+ class Test(Parameterizable):
+ def __init__(self):
+ self.parameters = []
+
+ @with_parameters(DATA)
+ def test(self, thing, named=None):
+ self.parameters.append((thing, named))
+
+ self.assertFalse(hasattr(Test, 'test'))
+ self.assertTrue(hasattr(Test, 'test_1'))
+ self.assertTrue(hasattr(Test, 'test_2'))
+
+ test = Test()
+ test.test_1()
+ test.test_2()
+
+ self.assertEquals(test.parameters, [('thing', 43), ('thing2', None)])
+
+ def test_parameterized_same_name_raises_error(self):
+ with self.assertRaises(KeyError):
+ class Test(Parameterizable):
+ @parameterized('1', 'thing', named=43)
+ @parameterized('1', 'thing2')
+ def test(self, thing, named=None):
+ pass
+
+ def test_marionette_test_case_is_parameterizable(self):
+ self.assertTrue(issubclass(MarionetteTestCase.__metaclass__, MetaParameterized))
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_date_time_value.py b/testing/marionette/harness/marionette_harness/tests/unit/test_date_time_value.py
new file mode 100644
index 000000000..2d224fff2
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_date_time_value.py
@@ -0,0 +1,29 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from datetime import datetime
+
+from marionette_driver.by import By
+from marionette_driver.date_time_value import DateTimeValue
+from marionette_harness import MarionetteTestCase
+
+
+class TestDateTime(MarionetteTestCase):
+ def test_set_date(self):
+ test_html = self.marionette.absolute_url("datetimePage.html")
+ self.marionette.navigate(test_html)
+
+ element = self.marionette.find_element(By.ID, "date-test")
+ dt_value = DateTimeValue(element)
+ dt_value.date = datetime(1998, 6, 2)
+ self.assertEqual("1998-06-02", element.get_property("value"))
+
+ def test_set_time(self):
+ test_html = self.marionette.absolute_url("datetimePage.html")
+ self.marionette.navigate(test_html)
+
+ element = self.marionette.find_element(By.ID, "time-test")
+ dt_value = DateTimeValue(element)
+ dt_value.time = datetime(1998, 11, 19, 9, 8, 7)
+ self.assertEqual("09:08:07", element.get_property("value"))
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_element_retrieval.py b/testing/marionette/harness/marionette_harness/tests/unit/test_element_retrieval.py
new file mode 100644
index 000000000..9023a84ab
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_element_retrieval.py
@@ -0,0 +1,483 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import re
+import urllib
+
+from marionette_driver.by import By
+from marionette_driver.errors import NoSuchElementException, InvalidSelectorException
+from marionette_driver.marionette import HTMLElement
+
+from marionette_harness import MarionetteTestCase, skip
+
+
+def inline(doc, doctype="html"):
+ if doctype == "html":
+ return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+ elif doctype == "xhtml":
+ return "data:application/xhtml+xml,{}".format(urllib.quote(
+r"""<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ <head>
+ <title>XHTML might be the future</title>
+ </head>
+
+ <body>
+ {}
+ </body>
+</html>""".format(doc)))
+
+
+id_html = inline("<p id=foo></p>", doctype="html")
+id_xhtml = inline('<p id="foo"></p>', doctype="xhtml")
+parent_child_html = inline("<div id=parent><p id=child></p></div>", doctype="html")
+parent_child_xhtml = inline('<div id="parent"><p id="child"></p></div>', doctype="xhtml")
+children_html = inline("<div><p>foo <p>bar</div>", doctype="html")
+children_xhtml = inline("<div><p>foo</p> <p>bar</p></div>", doctype="xhtml")
+class_html = inline("<p class='foo bar'>", doctype="html")
+class_xhtml = inline('<p class="foo bar"></p>', doctype="xhtml")
+name_html = inline("<p name=foo>", doctype="html")
+name_xhtml = inline('<p name="foo"></p>', doctype="xhtml")
+link_html = inline("<p><a href=#>foo bar</a>", doctype="html")
+link_html_with_trailing_space = inline("<p><a href=#>a link with a trailing space </a>")
+link_xhtml = inline('<p><a href="#">foo bar</a></p>', doctype="xhtml")
+
+
+class TestFindElementHTML(MarionetteTestCase):
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ self.marionette.timeout.implicit = 0
+
+ def test_id(self):
+ self.marionette.navigate(id_html)
+ expected = self.marionette.execute_script("return document.querySelector('p')")
+ found = self.marionette.find_element(By.ID, "foo")
+ self.assertIsInstance(found, HTMLElement)
+ self.assertEqual(expected, found)
+
+ def test_child_element(self):
+ self.marionette.navigate(parent_child_html)
+ parent = self.marionette.find_element(By.ID, "parent")
+ child = self.marionette.find_element(By.ID, "child")
+ found = parent.find_element(By.TAG_NAME, "p")
+ self.assertEqual(found.tag_name, "p")
+ self.assertIsInstance(found, HTMLElement)
+ self.assertEqual(child, found)
+
+ def test_tag_name(self):
+ self.marionette.navigate(children_html)
+ el = self.marionette.execute_script("return document.querySelector('p')")
+ found = self.marionette.find_element(By.TAG_NAME, "p")
+ self.assertIsInstance(found, HTMLElement)
+ self.assertEqual(el, found)
+
+ def test_class_name(self):
+ self.marionette.navigate(class_html)
+ el = self.marionette.execute_script("return document.querySelector('.foo')")
+ found = self.marionette.find_element(By.CLASS_NAME, "foo")
+ self.assertIsInstance(found, HTMLElement)
+ self.assertEqual(el, found)
+
+ def test_by_name(self):
+ self.marionette.navigate(name_html)
+ el = self.marionette.execute_script("return document.querySelector('[name=foo]')")
+ found = self.marionette.find_element(By.NAME, "foo")
+ self.assertIsInstance(found, HTMLElement)
+ self.assertEqual(el, found)
+
+ def test_css_selector(self):
+ self.marionette.navigate(children_html)
+ el = self.marionette.execute_script("return document.querySelector('p')")
+ found = self.marionette.find_element(By.CSS_SELECTOR, "p")
+ self.assertIsInstance(found, HTMLElement)
+ self.assertEqual(el, found)
+
+ def test_invalid_css_selector_should_throw(self):
+ with self.assertRaises(InvalidSelectorException):
+ self.marionette.find_element(By.CSS_SELECTOR, "#")
+
+ def test_link_text(self):
+ self.marionette.navigate(link_html)
+ el = self.marionette.execute_script("return document.querySelector('a')")
+ found = self.marionette.find_element(By.LINK_TEXT, "foo bar")
+ self.assertIsInstance(found, HTMLElement)
+ self.assertEqual(el, found)
+
+ def test_link_text_with_trailing_space(self):
+ self.marionette.navigate(link_html_with_trailing_space)
+ el = self.marionette.execute_script("return document.querySelector('a')")
+ found = self.marionette.find_element(By.LINK_TEXT, "a link with a trailing space")
+ self.assertIsInstance(found, HTMLElement)
+ self.assertEqual(el, found)
+
+ def test_partial_link_text(self):
+ self.marionette.navigate(link_html)
+ el = self.marionette.execute_script("return document.querySelector('a')")
+ found = self.marionette.find_element(By.PARTIAL_LINK_TEXT, "foo")
+ self.assertIsInstance(found, HTMLElement)
+ self.assertEqual(el, found)
+
+ def test_xpath(self):
+ self.marionette.navigate(id_html)
+ el = self.marionette.execute_script("return document.querySelector('#foo')")
+ found = self.marionette.find_element(By.XPATH, "id('foo')")
+ self.assertIsInstance(found, HTMLElement)
+ self.assertEqual(el, found)
+
+ def test_not_found(self):
+ self.marionette.timeout.implicit = 0
+ self.assertRaises(NoSuchElementException, self.marionette.find_element, By.CLASS_NAME, "cheese")
+ self.assertRaises(NoSuchElementException, self.marionette.find_element, By.CSS_SELECTOR, "cheese")
+ self.assertRaises(NoSuchElementException, self.marionette.find_element, By.ID, "cheese")
+ self.assertRaises(NoSuchElementException, self.marionette.find_element, By.LINK_TEXT, "cheese")
+ self.assertRaises(NoSuchElementException, self.marionette.find_element, By.NAME, "cheese")
+ self.assertRaises(NoSuchElementException, self.marionette.find_element, By.PARTIAL_LINK_TEXT, "cheese")
+ self.assertRaises(NoSuchElementException, self.marionette.find_element, By.TAG_NAME, "cheese")
+ self.assertRaises(NoSuchElementException, self.marionette.find_element, By.XPATH, "cheese")
+
+ def test_not_found_implicit_wait(self):
+ self.marionette.timeout.implicit = 0.5
+ self.assertRaises(NoSuchElementException, self.marionette.find_element, By.CLASS_NAME, "cheese")
+ self.assertRaises(NoSuchElementException, self.marionette.find_element, By.CSS_SELECTOR, "cheese")
+ self.assertRaises(NoSuchElementException, self.marionette.find_element, By.ID, "cheese")
+ self.assertRaises(NoSuchElementException, self.marionette.find_element, By.LINK_TEXT, "cheese")
+ self.assertRaises(NoSuchElementException, self.marionette.find_element, By.NAME, "cheese")
+ self.assertRaises(NoSuchElementException, self.marionette.find_element, By.PARTIAL_LINK_TEXT, "cheese")
+ self.assertRaises(NoSuchElementException, self.marionette.find_element, By.TAG_NAME, "cheese")
+ self.assertRaises(NoSuchElementException, self.marionette.find_element, By.XPATH, "cheese")
+
+ def test_not_found_from_element(self):
+ self.marionette.timeout.implicit = 0
+ self.marionette.navigate(id_html)
+ el = self.marionette.find_element(By.ID, "foo")
+ self.assertRaises(NoSuchElementException, el.find_element, By.CLASS_NAME, "cheese")
+ self.assertRaises(NoSuchElementException, el.find_element, By.CSS_SELECTOR, "cheese")
+ self.assertRaises(NoSuchElementException, el.find_element, By.ID, "cheese")
+ self.assertRaises(NoSuchElementException, el.find_element, By.LINK_TEXT, "cheese")
+ self.assertRaises(NoSuchElementException, el.find_element, By.NAME, "cheese")
+ self.assertRaises(NoSuchElementException, el.find_element, By.PARTIAL_LINK_TEXT, "cheese")
+ self.assertRaises(NoSuchElementException, el.find_element, By.TAG_NAME, "cheese")
+ self.assertRaises(NoSuchElementException, el.find_element, By.XPATH, "cheese")
+
+ def test_not_found_implicit_wait_from_element(self):
+ self.marionette.timeout.implicit = 0.5
+ self.marionette.navigate(id_html)
+ el = self.marionette.find_element(By.ID, "foo")
+ self.assertRaises(NoSuchElementException, el.find_element, By.CLASS_NAME, "cheese")
+ self.assertRaises(NoSuchElementException, el.find_element, By.CSS_SELECTOR, "cheese")
+ self.assertRaises(NoSuchElementException, el.find_element, By.ID, "cheese")
+ self.assertRaises(NoSuchElementException, el.find_element, By.LINK_TEXT, "cheese")
+ self.assertRaises(NoSuchElementException, el.find_element, By.NAME, "cheese")
+ self.assertRaises(NoSuchElementException, el.find_element, By.PARTIAL_LINK_TEXT, "cheese")
+ self.assertRaises(NoSuchElementException, el.find_element, By.TAG_NAME, "cheese")
+ self.assertRaises(NoSuchElementException, el.find_element, By.XPATH, "cheese")
+
+ def test_css_selector_scope_doesnt_start_at_rootnode(self):
+ self.marionette.navigate(parent_child_html)
+ el = self.marionette.find_element(By.ID, "child")
+ parent = self.marionette.find_element(By.ID, "parent")
+ found = parent.find_element(By.CSS_SELECTOR, "p")
+ self.assertEqual(el, found)
+
+ def test_unknown_selector(self):
+ with self.assertRaises(InvalidSelectorException):
+ self.marionette.find_elements("foo", "bar")
+
+ def test_element_id_is_valid_uuid(self):
+ self.marionette.navigate(id_html)
+ el = self.marionette.find_element(By.TAG_NAME, "p")
+ uuid_regex = re.compile('^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$')
+ self.assertIsNotNone(re.search(uuid_regex, el.id),
+ 'UUID for the WebElement is not valid. ID is {}'\
+ .format(el.id))
+
+ def test_invalid_xpath_selector(self):
+ with self.assertRaises(InvalidSelectorException):
+ self.marionette.find_element(By.XPATH, "count(//input)")
+ with self.assertRaises(InvalidSelectorException):
+ parent = self.marionette.execute_script("return document.documentElement")
+ parent.find_element(By.XPATH, "count(//input)")
+
+ def test_invalid_css_selector(self):
+ with self.assertRaises(InvalidSelectorException):
+ self.marionette.find_element(By.CSS_SELECTOR, "")
+ with self.assertRaises(InvalidSelectorException):
+ parent = self.marionette.execute_script("return document.documentElement")
+ parent.find_element(By.CSS_SELECTOR, "")
+
+ def test_finding_active_element_returns_element(self):
+ self.marionette.navigate(id_html)
+ active = self.marionette.execute_script("return document.activeElement")
+ self.assertEqual(active, self.marionette.get_active_element())
+
+
+class TestFindElementXHTML(MarionetteTestCase):
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ self.marionette.timeout.implicit = 0
+
+ def test_id(self):
+ self.marionette.navigate(id_xhtml)
+ expected = self.marionette.execute_script("return document.querySelector('p')")
+ found = self.marionette.find_element(By.ID, "foo")
+ self.assertIsInstance(found, HTMLElement)
+ self.assertEqual(expected, found)
+
+ def test_child_element(self):
+ self.marionette.navigate(parent_child_xhtml)
+ parent = self.marionette.find_element(By.ID, "parent")
+ child = self.marionette.find_element(By.ID, "child")
+ found = parent.find_element(By.TAG_NAME, "p")
+ self.assertEqual(found.tag_name, "p")
+ self.assertIsInstance(found, HTMLElement)
+ self.assertEqual(child, found)
+
+ def test_tag_name(self):
+ self.marionette.navigate(children_xhtml)
+ el = self.marionette.execute_script("return document.querySelector('p')")
+ found = self.marionette.find_element(By.TAG_NAME, "p")
+ self.assertIsInstance(found, HTMLElement)
+ self.assertEqual(el, found)
+
+ def test_class_name(self):
+ self.marionette.navigate(class_xhtml)
+ el = self.marionette.execute_script("return document.querySelector('.foo')")
+ found = self.marionette.find_element(By.CLASS_NAME, "foo")
+ self.assertIsInstance(found, HTMLElement)
+ self.assertEqual(el, found)
+
+ def test_by_name(self):
+ self.marionette.navigate(name_xhtml)
+ el = self.marionette.execute_script("return document.querySelector('[name=foo]')")
+ found = self.marionette.find_element(By.NAME, "foo")
+ self.assertIsInstance(found, HTMLElement)
+ self.assertEqual(el, found)
+
+ def test_css_selector(self):
+ self.marionette.navigate(children_xhtml)
+ el = self.marionette.execute_script("return document.querySelector('p')")
+ found = self.marionette.find_element(By.CSS_SELECTOR, "p")
+ self.assertIsInstance(found, HTMLElement)
+ self.assertEqual(el, found)
+
+ def test_link_text(self):
+ self.marionette.navigate(link_xhtml)
+ el = self.marionette.execute_script("return document.querySelector('a')")
+ found = self.marionette.find_element(By.LINK_TEXT, "foo bar")
+ self.assertIsInstance(found, HTMLElement)
+ self.assertEqual(el, found)
+
+ def test_partial_link_text(self):
+ self.marionette.navigate(link_xhtml)
+ el = self.marionette.execute_script("return document.querySelector('a')")
+ found = self.marionette.find_element(By.PARTIAL_LINK_TEXT, "foo")
+ self.assertIsInstance(found, HTMLElement)
+ self.assertEqual(el, found)
+
+ def test_xpath(self):
+ self.marionette.navigate(id_xhtml)
+ el = self.marionette.execute_script("return document.querySelector('#foo')")
+ found = self.marionette.find_element(By.XPATH, "id('foo')")
+ self.assertIsInstance(found, HTMLElement)
+ self.assertEqual(el, found)
+
+ def test_css_selector_scope_does_not_start_at_rootnode(self):
+ self.marionette.navigate(parent_child_xhtml)
+ el = self.marionette.find_element(By.ID, "child")
+ parent = self.marionette.find_element(By.ID, "parent")
+ found = parent.find_element(By.CSS_SELECTOR, "p")
+ self.assertEqual(el, found)
+
+ def test_active_element(self):
+ self.marionette.navigate(id_xhtml)
+ active = self.marionette.execute_script("return document.activeElement")
+ self.assertEqual(active, self.marionette.get_active_element())
+
+
+class TestFindElementsHTML(MarionetteTestCase):
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ self.marionette.timeout.implicit = 0
+
+ def assertItemsIsInstance(self, items, typ):
+ for item in items:
+ self.assertIsInstance(item, typ)
+
+ def test_child_elements(self):
+ self.marionette.navigate(children_html)
+ parent = self.marionette.find_element(By.TAG_NAME, "div")
+ children = self.marionette.find_elements(By.TAG_NAME, "p")
+ found = parent.find_elements(By.TAG_NAME, "p")
+ self.assertItemsIsInstance(found, HTMLElement)
+ self.assertSequenceEqual(found, children)
+
+ def test_tag_name(self):
+ self.marionette.navigate(children_html)
+ els = self.marionette.execute_script("return document.querySelectorAll('p')")
+ found = self.marionette.find_elements(By.TAG_NAME, "p")
+ self.assertItemsIsInstance(found, HTMLElement)
+ self.assertSequenceEqual(els, found)
+
+ def test_class_name(self):
+ self.marionette.navigate(class_html)
+ els = self.marionette.execute_script("return document.querySelectorAll('.foo')")
+ found = self.marionette.find_elements(By.CLASS_NAME, "foo")
+ self.assertItemsIsInstance(found, HTMLElement)
+ self.assertSequenceEqual(els, found)
+
+ def test_by_name(self):
+ self.marionette.navigate(name_html)
+ els = self.marionette.execute_script("return document.querySelectorAll('[name=foo]')")
+ found = self.marionette.find_elements(By.NAME, "foo")
+ self.assertItemsIsInstance(found, HTMLElement)
+ self.assertSequenceEqual(els, found)
+
+ def test_css_selector(self):
+ self.marionette.navigate(children_html)
+ els = self.marionette.execute_script("return document.querySelectorAll('p')")
+ found = self.marionette.find_elements(By.CSS_SELECTOR, "p")
+ self.assertItemsIsInstance(found, HTMLElement)
+ self.assertSequenceEqual(els, found)
+
+ def test_invalid_css_selector_should_throw(self):
+ with self.assertRaises(InvalidSelectorException):
+ self.marionette.find_elements(By.CSS_SELECTOR, "#")
+
+ def test_link_text(self):
+ self.marionette.navigate(link_html)
+ els = self.marionette.execute_script("return document.querySelectorAll('a')")
+ found = self.marionette.find_elements(By.LINK_TEXT, "foo bar")
+ self.assertItemsIsInstance(found, HTMLElement)
+ self.assertSequenceEqual(els, found)
+
+ def test_link_text_with_trailing_space(self):
+ self.marionette.navigate(link_html_with_trailing_space)
+ els = self.marionette.execute_script("return document.querySelectorAll('a')")
+ found = self.marionette.find_elements(By.LINK_TEXT, "a link with a trailing space")
+ self.assertItemsIsInstance(found, HTMLElement)
+ self.assertSequenceEqual(els, found)
+
+
+ def test_partial_link_text(self):
+ self.marionette.navigate(link_html)
+ els = self.marionette.execute_script("return document.querySelectorAll('a')")
+ found = self.marionette.find_elements(By.PARTIAL_LINK_TEXT, "foo")
+ self.assertItemsIsInstance(found, HTMLElement)
+ self.assertSequenceEqual(els, found)
+
+ def test_xpath(self):
+ self.marionette.navigate(children_html)
+ els = self.marionette.execute_script("return document.querySelectorAll('p')")
+ found = self.marionette.find_elements(By.XPATH, ".//p")
+ self.assertItemsIsInstance(found, HTMLElement)
+ self.assertSequenceEqual(els, found)
+
+ def test_css_selector_scope_doesnt_start_at_rootnode(self):
+ self.marionette.navigate(parent_child_html)
+ els = self.marionette.find_elements(By.ID, "child")
+ parent = self.marionette.find_element(By.ID, "parent")
+ found = parent.find_elements(By.CSS_SELECTOR, "p")
+ self.assertSequenceEqual(els, found)
+
+ def test_unknown_selector(self):
+ with self.assertRaises(InvalidSelectorException):
+ self.marionette.find_element("foo", "bar")
+
+ def test_element_id_is_valid_uuid(self):
+ self.marionette.navigate(id_html)
+ els = self.marionette.find_elements(By.TAG_NAME, "p")
+ uuid_regex = re.compile('^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$')
+ self.assertIsNotNone(re.search(uuid_regex, els[0].id),
+ 'UUID for the WebElement is not valid. ID is {}'\
+ .format(els[0].id))
+
+ def test_invalid_xpath_selector(self):
+ with self.assertRaises(InvalidSelectorException):
+ self.marionette.find_elements(By.XPATH, "count(//input)")
+ with self.assertRaises(InvalidSelectorException):
+ parent = self.marionette.execute_script("return document.documentElement")
+ parent.find_elements(By.XPATH, "count(//input)")
+
+ def test_invalid_css_selector(self):
+ with self.assertRaises(InvalidSelectorException):
+ self.marionette.find_elements(By.CSS_SELECTOR, "")
+ with self.assertRaises(InvalidSelectorException):
+ parent = self.marionette.execute_script("return document.documentElement")
+ parent.find_elements(By.CSS_SELECTOR, "")
+
+
+class TestFindElementsXHTML(MarionetteTestCase):
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ self.marionette.timeout.implicit = 0
+
+ def assertItemsIsInstance(self, items, typ):
+ for item in items:
+ self.assertIsInstance(item, typ)
+
+ def test_child_elements(self):
+ self.marionette.navigate(children_xhtml)
+ parent = self.marionette.find_element(By.TAG_NAME, "div")
+ children = self.marionette.find_elements(By.TAG_NAME, "p")
+ found = parent.find_elements(By.TAG_NAME, "p")
+ self.assertItemsIsInstance(found, HTMLElement)
+ self.assertSequenceEqual(found, children)
+
+ def test_tag_name(self):
+ self.marionette.navigate(children_xhtml)
+ els = self.marionette.execute_script("return document.querySelectorAll('p')")
+ found = self.marionette.find_elements(By.TAG_NAME, "p")
+ self.assertItemsIsInstance(found, HTMLElement)
+ self.assertSequenceEqual(els, found)
+
+ def test_class_name(self):
+ self.marionette.navigate(class_xhtml)
+ els = self.marionette.execute_script("return document.querySelectorAll('.foo')")
+ found = self.marionette.find_elements(By.CLASS_NAME, "foo")
+ self.assertItemsIsInstance(found, HTMLElement)
+ self.assertSequenceEqual(els, found)
+
+ def test_by_name(self):
+ self.marionette.navigate(name_xhtml)
+ els = self.marionette.execute_script("return document.querySelectorAll('[name=foo]')")
+ found = self.marionette.find_elements(By.NAME, "foo")
+ self.assertItemsIsInstance(found, HTMLElement)
+ self.assertSequenceEqual(els, found)
+
+ def test_css_selector(self):
+ self.marionette.navigate(children_xhtml)
+ els = self.marionette.execute_script("return document.querySelectorAll('p')")
+ found = self.marionette.find_elements(By.CSS_SELECTOR, "p")
+ self.assertItemsIsInstance(found, HTMLElement)
+ self.assertSequenceEqual(els, found)
+
+ def test_link_text(self):
+ self.marionette.navigate(link_xhtml)
+ els = self.marionette.execute_script("return document.querySelectorAll('a')")
+ found = self.marionette.find_elements(By.LINK_TEXT, "foo bar")
+ self.assertItemsIsInstance(found, HTMLElement)
+ self.assertSequenceEqual(els, found)
+
+ def test_partial_link_text(self):
+ self.marionette.navigate(link_xhtml)
+ els = self.marionette.execute_script("return document.querySelectorAll('a')")
+ found = self.marionette.find_elements(By.PARTIAL_LINK_TEXT, "foo")
+ self.assertItemsIsInstance(found, HTMLElement)
+ self.assertSequenceEqual(els, found)
+
+ @skip("XHTML namespace not yet supported")
+ def test_xpath(self):
+ self.marionette.navigate(children_xhtml)
+ els = self.marionette.execute_script("return document.querySelectorAll('p')")
+ found = self.marionette.find_elements(By.XPATH, "//xhtml:p")
+ self.assertItemsIsInstance(found, HTMLElement)
+ self.assertSequenceEqual(els, found)
+
+ def test_css_selector_scope_doesnt_start_at_rootnode(self):
+ self.marionette.navigate(parent_child_xhtml)
+ els = self.marionette.find_elements(By.ID, "child")
+ parent = self.marionette.find_element(By.ID, "parent")
+ found = parent.find_elements(By.CSS_SELECTOR, "p")
+ self.assertSequenceEqual(els, found)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_element_state.py b/testing/marionette/harness/marionette_harness/tests/unit/test_element_state.py
new file mode 100644
index 000000000..0344b4b9c
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_element_state.py
@@ -0,0 +1,162 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import types
+import urllib
+
+from marionette_driver.by import By
+
+from marionette_harness import MarionetteTestCase
+
+
+boolean_attributes = {
+ "audio": ["autoplay", "controls", "loop", "muted"],
+ "button": ["autofocus", "disabled", "formnovalidate"],
+ "details": ["open"],
+ "dialog": ["open"],
+ "fieldset": ["disabled"],
+ "form": ["novalidate"],
+ "iframe": ["allowfullscreen"],
+ "img": ["ismap"],
+ "input": ["autofocus", "checked", "disabled", "formnovalidate", "multiple", "readonly", "required"],
+ "menuitem": ["checked", "default", "disabled"],
+ "object": ["typemustmatch"],
+ "ol": ["reversed"],
+ "optgroup": ["disabled"],
+ "option": ["disabled", "selected"],
+ "script": ["async", "defer"],
+ "select": ["autofocus", "disabled", "multiple", "required"],
+ "textarea": ["autofocus", "disabled", "readonly", "required"],
+ "track": ["default"],
+ "video": ["autoplay", "controls", "loop", "muted"],
+}
+
+
+def inline(doc, doctype="html"):
+ if doctype == "html":
+ return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+ elif doctype == "xhtml":
+ return "data:application/xhtml+xml,{}".format(urllib.quote(
+r"""<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ <head>
+ <title>XHTML might be the future</title>
+ </head>
+
+ <body>
+ {}
+ </body>
+</html>""".format(doc)))
+
+
+attribute = inline("<input foo=bar>")
+input = inline("<input>")
+disabled = inline("<input disabled=baz>")
+check = inline("<input type=checkbox>")
+
+
+class TestIsElementEnabled(MarionetteTestCase):
+ def test_is_enabled(self):
+ test_html = self.marionette.absolute_url("test.html")
+ self.marionette.navigate(test_html)
+ l = self.marionette.find_element(By.NAME, "myCheckBox")
+ self.assertTrue(l.is_enabled())
+ self.marionette.execute_script("arguments[0].disabled = true;", [l])
+ self.assertFalse(l.is_enabled())
+
+
+class TestIsElementDisplayed(MarionetteTestCase):
+ def test_is_displayed(self):
+ test_html = self.marionette.absolute_url("test.html")
+ self.marionette.navigate(test_html)
+ l = self.marionette.find_element(By.NAME, "myCheckBox")
+ self.assertTrue(l.is_displayed())
+ self.marionette.execute_script("arguments[0].hidden = true;", [l])
+ self.assertFalse(l.is_displayed())
+
+
+class TestGetElementAttribute(MarionetteTestCase):
+ def test_normal_attribute(self):
+ self.marionette.navigate(inline("<p style=foo>"))
+ el = self.marionette.find_element(By.TAG_NAME, "p")
+ attr = el.get_attribute("style")
+ self.assertIsInstance(attr, types.StringTypes)
+ self.assertEqual("foo", attr)
+
+ def test_boolean_attributes(self):
+ for tag, attrs in boolean_attributes.iteritems():
+ for attr in attrs:
+ print("testing boolean attribute <{0} {1}>".format(tag, attr))
+ doc = inline("<{0} {1}>".format(tag, attr))
+ self.marionette.navigate(doc)
+ el = self.marionette.find_element(By.TAG_NAME, tag)
+ res = el.get_attribute(attr)
+ self.assertIsInstance(res, types.StringTypes)
+ self.assertEqual("true", res)
+
+ def test_global_boolean_attributes(self):
+ self.marionette.navigate(inline("<p hidden>foo"))
+ el = self.marionette.find_element(By.TAG_NAME, "p")
+ attr = el.get_attribute("hidden")
+ self.assertIsInstance(attr, types.StringTypes)
+ self.assertEqual("true", attr)
+
+ self.marionette.navigate(inline("<p>foo"))
+ el = self.marionette.find_element(By.TAG_NAME, "p")
+ attr = el.get_attribute("hidden")
+ self.assertIsNone(attr)
+
+ self.marionette.navigate(inline("<p itemscope>foo"))
+ el = self.marionette.find_element(By.TAG_NAME, "p")
+ attr = el.get_attribute("itemscope")
+ self.assertIsInstance(attr, types.StringTypes)
+ self.assertEqual("true", attr)
+
+ self.marionette.navigate(inline("<p>foo"))
+ el = self.marionette.find_element(By.TAG_NAME, "p")
+ attr = el.get_attribute("itemscope")
+ self.assertIsNone(attr)
+
+ # TODO(ato): Test for custom elements
+
+ def test_xhtml(self):
+ doc = inline("<p hidden=\"true\">foo</p>", doctype="xhtml")
+ self.marionette.navigate(doc)
+ el = self.marionette.find_element(By.TAG_NAME, "p")
+ attr = el.get_attribute("hidden")
+ self.assertIsInstance(attr, types.StringTypes)
+ self.assertEqual("true", attr)
+
+
+class TestGetElementProperty(MarionetteTestCase):
+ def test_get(self):
+ self.marionette.navigate(disabled)
+ el = self.marionette.find_element(By.TAG_NAME, "input")
+ prop = el.get_property("disabled")
+ self.assertIsInstance(prop, bool)
+ self.assertTrue(prop)
+
+ def test_missing_property_returns_default(self):
+ self.marionette.navigate(input)
+ el = self.marionette.find_element(By.TAG_NAME, "input")
+ prop = el.get_property("checked")
+ self.assertIsInstance(prop, bool)
+ self.assertFalse(prop)
+
+ def test_attribute_not_returned(self):
+ self.marionette.navigate(attribute)
+ el = self.marionette.find_element(By.TAG_NAME, "input")
+ self.assertEqual(el.get_property("foo"), None)
+
+ def test_manipulated_element(self):
+ self.marionette.navigate(check)
+ el = self.marionette.find_element(By.TAG_NAME, "input")
+ self.assertEqual(el.get_property("checked"), False)
+
+ el.click()
+ self.assertEqual(el.get_property("checked"), True)
+
+ el.click()
+ self.assertEqual(el.get_property("checked"), False)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_element_state_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_element_state_chrome.py
new file mode 100644
index 000000000..01ed355c4
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_element_state_chrome.py
@@ -0,0 +1,85 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+
+from marionette_harness import MarionetteTestCase, skip
+
+
+class TestIsElementEnabledChrome(MarionetteTestCase):
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ self.marionette.set_context("chrome")
+ self.win = self.marionette.current_window_handle
+ self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen');")
+ self.marionette.switch_to_window('foo')
+ self.assertNotEqual(self.win, self.marionette.current_window_handle)
+
+ def tearDown(self):
+ self.assertNotEqual(self.win, self.marionette.current_window_handle)
+ self.marionette.execute_script("window.close();")
+ self.marionette.switch_to_window(self.win)
+ MarionetteTestCase.tearDown(self)
+
+ def test_enabled(self):
+ l = self.marionette.find_element(By.ID, "textInput")
+ self.assertTrue(l.is_enabled())
+ self.marionette.execute_script("arguments[0].disabled = true;", [l])
+ self.assertFalse(l.is_enabled())
+ self.marionette.execute_script("arguments[0].disabled = false;", [l])
+
+ def test_can_get_element_rect(self):
+ l = self.marionette.find_element(By.ID, "textInput")
+ rect = l.rect
+ self.assertTrue(rect['x'] > 0)
+ self.assertTrue(rect['y'] > 0)
+
+
+@skip("Switched off in bug 896043, and to be turned on in bug 896046")
+class TestIsElementDisplayed(MarionetteTestCase):
+ def test_isDisplayed(self):
+ l = self.marionette.find_element(By.ID, "textInput")
+ self.assertTrue(l.is_displayed())
+ self.marionette.execute_script("arguments[0].hidden = true;", [l])
+ self.assertFalse(l.is_displayed())
+ self.marionette.execute_script("arguments[0].hidden = false;", [l])
+
+
+class TestGetElementAttributeChrome(MarionetteTestCase):
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ self.marionette.set_context("chrome")
+ self.win = self.marionette.current_window_handle
+ self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen');")
+ self.marionette.switch_to_window('foo')
+ self.assertNotEqual(self.win, self.marionette.current_window_handle)
+
+ def tearDown(self):
+ self.assertNotEqual(self.win, self.marionette.current_window_handle)
+ self.marionette.execute_script("window.close();")
+ self.marionette.switch_to_window(self.win)
+ MarionetteTestCase.tearDown(self)
+
+ def test_get(self):
+ el = self.marionette.execute_script("return window.document.getElementById('textInput');")
+ self.assertEqual(el.get_attribute("id"), "textInput")
+
+class TestGetElementProperty(MarionetteTestCase):
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ self.marionette.set_context("chrome")
+ self.win = self.marionette.current_window_handle
+ self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen');")
+ self.marionette.switch_to_window('foo')
+ self.assertNotEqual(self.win, self.marionette.current_window_handle)
+
+ def tearDown(self):
+ self.assertNotEqual(self.win, self.marionette.current_window_handle)
+ self.marionette.execute_script("window.close();")
+ self.marionette.switch_to_window(self.win)
+ MarionetteTestCase.tearDown(self)
+
+ def test_get(self):
+ el = self.marionette.execute_script("return window.document.getElementById('textInput');")
+ self.assertEqual(el.get_property("id"), "textInput")
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_elementsize.py b/testing/marionette/harness/marionette_harness/tests/unit/test_elementsize.py
new file mode 100644
index 000000000..ebabd3344
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_elementsize.py
@@ -0,0 +1,17 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestElementSize(MarionetteTestCase):
+ def testShouldReturnTheSizeOfALink(self):
+ test_html = self.marionette.absolute_url("testSize.html")
+ self.marionette.navigate(test_html)
+ shrinko = self.marionette.find_element(By.ID, 'linkId')
+ size = shrinko.rect
+ self.assertTrue(size['width'] > 0)
+ self.assertTrue(size['height'] > 0)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_elementsize_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_elementsize_chrome.py
new file mode 100644
index 000000000..e2bb34715
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_elementsize_chrome.py
@@ -0,0 +1,34 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+
+from marionette_harness import MarionetteTestCase, WindowManagerMixin
+
+
+class TestElementSizeChrome(WindowManagerMixin, MarionetteTestCase):
+
+ def setUp(self):
+ super(TestElementSizeChrome, self).setUp()
+
+ self.marionette.set_context("chrome")
+
+ def open_window_with_js():
+ self.marionette.execute_script("""
+ window.open('chrome://marionette/content/test2.xul',
+ 'foo', 'chrome,centerscreen');
+ """)
+
+ new_window = self.open_window(trigger=open_window_with_js)
+ self.marionette.switch_to_window(new_window)
+
+ def tearDown(self):
+ self.close_all_windows()
+ super(TestElementSizeChrome, self).tearDown()
+
+ def testShouldReturnTheSizeOfAnInput(self):
+ shrinko = self.marionette.find_element(By.ID, 'textInput')
+ size = shrinko.rect
+ self.assertTrue(size['width'] > 0)
+ self.assertTrue(size['height'] > 0)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_errors.py b/testing/marionette/harness/marionette_harness/tests/unit/test_errors.py
new file mode 100644
index 000000000..f6a9c285c
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_errors.py
@@ -0,0 +1,77 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import sys
+
+from marionette_driver import errors
+
+from marionette_harness import marionette_test
+
+
+def fake_cause():
+ try:
+ raise ValueError("bar")
+ except ValueError as e:
+ return sys.exc_info()
+
+message = "foo"
+cause = fake_cause()
+stacktrace = "first\nsecond"
+
+class TestErrors(marionette_test.MarionetteTestCase):
+ def test_defaults(self):
+ exc = errors.MarionetteException()
+ self.assertIsNone(exc.message)
+ self.assertIsNone(exc.cause)
+ self.assertIsNone(exc.stacktrace)
+
+ def test_construction(self):
+ exc = errors.MarionetteException(
+ message=message, cause=cause, stacktrace=stacktrace)
+ self.assertEquals(exc.message, message)
+ self.assertEquals(exc.cause, cause)
+ self.assertEquals(exc.stacktrace, stacktrace)
+
+ def test_str(self):
+ exc = errors.MarionetteException(
+ message=message, cause=cause, stacktrace=stacktrace)
+ r = str(exc)
+ self.assertIn(message, r)
+ self.assertIn(", caused by {0!r}".format(cause[0]), r)
+ self.assertIn("\nstacktrace:\n\tfirst\n\tsecond", r)
+
+ def test_cause_string(self):
+ exc = errors.MarionetteException(cause="foo")
+ self.assertEqual(exc.cause, "foo")
+ r = str(exc)
+ self.assertIn(", caused by foo", r)
+
+ def test_cause_tuple(self):
+ exc = errors.MarionetteException(cause=cause)
+ self.assertEqual(exc.cause, cause)
+ r = str(exc)
+ self.assertIn(", caused by {0!r}".format(cause[0]), r)
+
+
+class TestLookup(marionette_test.MarionetteTestCase):
+ def test_by_unknown_number(self):
+ self.assertEqual(errors.MarionetteException, errors.lookup(123456))
+
+ def test_by_known_string(self):
+ self.assertEqual(errors.NoSuchElementException,
+ errors.lookup("no such element"))
+
+ def test_by_unknown_string(self):
+ self.assertEqual(errors.MarionetteException, errors.lookup("barbera"))
+
+ def test_by_known_unicode_string(self):
+ self.assertEqual(errors.NoSuchElementException,
+ errors.lookup(u"no such element"))
+
+
+class TestAllErrors(marionette_test.MarionetteTestCase):
+ def test_properties(self):
+ for exc in errors.es_:
+ self.assertTrue(hasattr(exc, "status"),
+ "expected exception to have attribute `status'")
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_execute_async_script.py b/testing/marionette/harness/marionette_harness/tests/unit/test_execute_async_script.py
new file mode 100644
index 000000000..8a5472b3a
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_execute_async_script.py
@@ -0,0 +1,156 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.errors import (
+ JavascriptException,
+ ScriptTimeoutException,
+)
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestExecuteAsyncContent(MarionetteTestCase):
+ def setUp(self):
+ super(TestExecuteAsyncContent, self).setUp()
+ self.marionette.timeout.script = 1
+
+ def test_execute_async_simple(self):
+ self.assertEqual(1, self.marionette.execute_async_script("arguments[arguments.length-1](1);"))
+
+ def test_execute_async_ours(self):
+ self.assertEqual(1, self.marionette.execute_async_script("marionetteScriptFinished(1);"))
+
+ def test_execute_async_timeout(self):
+ self.assertRaises(ScriptTimeoutException, self.marionette.execute_async_script, "var x = 1;")
+
+ def test_execute_async_unique_timeout(self):
+ self.assertEqual(2, self.marionette.execute_async_script("setTimeout(function() {marionetteScriptFinished(2);}, 2000);", script_timeout=5000))
+ self.assertRaises(ScriptTimeoutException, self.marionette.execute_async_script, "setTimeout(function() {marionetteScriptFinished(3);}, 2000);")
+
+ def test_no_timeout(self):
+ self.marionette.timeout.script = 10
+ self.assertTrue(self.marionette.execute_async_script("""
+ var callback = arguments[arguments.length - 1];
+ setTimeout(function() { callback(true); }, 500);
+ """))
+
+ def test_execute_async_unload(self):
+ self.marionette.timeout.script = 5
+ unload = """
+ window.location.href = "about:blank";
+ """
+ self.assertRaises(JavascriptException, self.marionette.execute_async_script, unload)
+
+ def test_check_window(self):
+ self.assertTrue(self.marionette.execute_async_script("marionetteScriptFinished(window !=null && window != undefined);"))
+
+ def test_same_context(self):
+ var1 = 'testing'
+ self.assertEqual(self.marionette.execute_script("""
+ this.testvar = '{}';
+ return this.testvar;
+ """.format(var1)), var1)
+ self.assertEqual(self.marionette.execute_async_script(
+ "marionetteScriptFinished(this.testvar);", new_sandbox=False), var1)
+
+ def test_execute_no_return(self):
+ self.assertEqual(self.marionette.execute_async_script("marionetteScriptFinished()"), None)
+
+ def test_execute_js_exception(self):
+ try:
+ self.marionette.execute_async_script("""
+ let a = 1;
+ foo(bar);
+ """)
+ self.assertFalse(True)
+ except JavascriptException, inst:
+ self.assertTrue('foo(bar)' in inst.stacktrace)
+
+ def test_execute_async_js_exception(self):
+ self.assertRaises(JavascriptException,
+ self.marionette.execute_async_script, """
+ var callback = arguments[arguments.length - 1];
+ callback(foo());
+ """)
+
+ def test_script_finished(self):
+ self.assertTrue(self.marionette.execute_async_script("""
+ marionetteScriptFinished(true);
+ """))
+
+ def test_execute_permission(self):
+ self.assertRaises(JavascriptException, self.marionette.execute_async_script, """
+let prefs = Components.classes["@mozilla.org/preferences-service;1"]
+ .getService(Components.interfaces.nsIPrefBranch);
+marionetteScriptFinished(4);
+""")
+
+ def test_sandbox_reuse(self):
+ # Sandboxes between `execute_script()` invocations are shared.
+ self.marionette.execute_async_script("this.foobar = [23, 42];"
+ "marionetteScriptFinished();")
+ self.assertEqual(self.marionette.execute_async_script(
+ "marionetteScriptFinished(this.foobar);", new_sandbox=False), [23, 42])
+
+ self.marionette.execute_async_script("global.barfoo = [42, 23];"
+ "marionetteScriptFinished();")
+ self.assertEqual(self.marionette.execute_async_script(
+ "marionetteScriptFinished(global.barfoo);", new_sandbox=False), [42, 23])
+
+ def test_sandbox_refresh_arguments(self):
+ self.marionette.execute_async_script("this.foobar = [arguments[0], arguments[1]];"
+ "marionetteScriptFinished();",
+ script_args=[23, 42])
+ self.assertEqual(self.marionette.execute_async_script(
+ "marionetteScriptFinished(this.foobar);", new_sandbox=False),
+ [23, 42])
+
+ self.marionette.execute_async_script("global.barfoo = [arguments[0], arguments[1]];"
+ "marionetteScriptFinished()",
+ script_args=[42, 23], new_sandbox=False)
+ self.assertEqual(self.marionette.execute_async_script(
+ "marionetteScriptFinished(global.barfoo);", new_sandbox=False),
+ [42, 23])
+
+ # Functions defined in higher privilege scopes, such as the privileged
+ # content frame script listener.js runs in, cannot be accessed from
+ # content. This tests that it is possible to introspect the objects on
+ # `arguments` without getting permission defined errors. This is made
+ # possible because the last argument is always the callback/complete
+ # function.
+ #
+ # See bug 1290966.
+ def test_introspection_of_arguments(self):
+ self.marionette.execute_async_script(
+ "arguments[0].cheese; __webDriverCallback();",
+ script_args=[], sandbox=None)
+
+
+class TestExecuteAsyncChrome(TestExecuteAsyncContent):
+ def setUp(self):
+ super(TestExecuteAsyncChrome, self).setUp()
+ self.marionette.set_context("chrome")
+
+ def test_execute_async_unload(self):
+ pass
+
+ def test_execute_permission(self):
+ self.assertEqual(5, self.marionette.execute_async_script("""
+var c = Components.classes;
+marionetteScriptFinished(5);
+"""))
+
+ def test_execute_async_js_exception(self):
+ # Javascript exceptions are not propagated in chrome code
+ self.marionette.timeout.script = 0.2
+ self.assertRaises(ScriptTimeoutException,
+ self.marionette.execute_async_script, """
+ var callback = arguments[arguments.length - 1];
+ setTimeout("callback(foo())", 50);
+ """)
+ self.assertRaises(JavascriptException,
+ self.marionette.execute_async_script, """
+ var callback = arguments[arguments.length - 1];
+ setTimeout("callback(foo())", 50);
+ """, debug_script=True)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_execute_isolate.py b/testing/marionette/harness/marionette_harness/tests/unit/test_execute_isolate.py
new file mode 100644
index 000000000..7e09451e4
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_execute_isolate.py
@@ -0,0 +1,37 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.errors import ScriptTimeoutException
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestExecuteIsolationContent(MarionetteTestCase):
+ def setUp(self):
+ super(TestExecuteIsolationContent, self).setUp()
+ self.content = True
+
+ def test_execute_async_isolate(self):
+ # Results from one execute call that has timed out should not
+ # contaminate a future call.
+ multiplier = "*3" if self.content else "*1"
+ self.marionette.timeout.script = 0.5
+ self.assertRaises(ScriptTimeoutException,
+ self.marionette.execute_async_script,
+ ("setTimeout(function() {{ marionetteScriptFinished(5{}); }}, 3000);"
+ .format(multiplier)))
+
+ self.marionette.timeout.script = 6
+ result = self.marionette.execute_async_script("""
+ setTimeout(function() {{ marionetteScriptFinished(10{}); }}, 5000);
+ """.format(multiplier))
+ self.assertEqual(result, 30 if self.content else 10)
+
+class TestExecuteIsolationChrome(TestExecuteIsolationContent):
+ def setUp(self):
+ super(TestExecuteIsolationChrome, self).setUp()
+ self.marionette.set_context("chrome")
+ self.content = False
+
+
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_execute_sandboxes.py b/testing/marionette/harness/marionette_harness/tests/unit/test_execute_sandboxes.py
new file mode 100644
index 000000000..d7cb0444b
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_execute_sandboxes.py
@@ -0,0 +1,79 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.errors import JavascriptException
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestExecuteSandboxes(MarionetteTestCase):
+ def setUp(self):
+ super(TestExecuteSandboxes, self).setUp()
+
+ def test_execute_system_sandbox(self):
+ # Test that "system" sandbox has elevated privileges in execute_script
+ result = self.marionette.execute_script(
+ "return Components.interfaces.nsIPermissionManager.ALLOW_ACTION",
+ sandbox="system")
+ self.assertEqual(result, 1)
+
+ def test_execute_async_system_sandbox(self):
+ # Test that "system" sandbox has elevated privileges in
+ # execute_async_script.
+ result = self.marionette.execute_async_script("""
+ const Ci = Components.interfaces;
+ let result = Ci.nsIPermissionManager.ALLOW_ACTION;
+ marionetteScriptFinished(result);""",
+ sandbox="system")
+ self.assertEqual(result, 1)
+
+ def test_execute_switch_sandboxes(self):
+ # Test that sandboxes are retained when switching between them
+ # for execute_script.
+ self.marionette.execute_script("foo = 1", sandbox="1")
+ self.marionette.execute_script("foo = 2", sandbox="2")
+ foo = self.marionette.execute_script(
+ "return foo", sandbox="1", new_sandbox=False)
+ self.assertEqual(foo, 1)
+ foo = self.marionette.execute_script(
+ "return foo", sandbox="2", new_sandbox=False)
+ self.assertEqual(foo, 2)
+
+ def test_execute_new_sandbox(self):
+ # test that clearing a sandbox does not affect other sandboxes
+ self.marionette.execute_script("foo = 1", sandbox="1")
+ self.marionette.execute_script("foo = 2", sandbox="2")
+
+ # deprecate sandbox 1 by asking explicitly for a fresh one
+ with self.assertRaises(JavascriptException):
+ self.marionette.execute_script("return foo",
+ sandbox="1", new_sandbox=True)
+
+ foo = self.marionette.execute_script(
+ "return foo", sandbox="2", new_sandbox=False)
+ self.assertEqual(foo, 2)
+
+ def test_execute_async_switch_sandboxes(self):
+ # Test that sandboxes are retained when switching between them
+ # for execute_async_script.
+ self.marionette.execute_async_script(
+ "foo = 1; marionetteScriptFinished()", sandbox="1")
+ self.marionette.execute_async_script(
+ "foo = 2; marionetteScriptFinished()", sandbox='2')
+ foo = self.marionette.execute_async_script(
+ "marionetteScriptFinished(foo)",
+ sandbox="1",
+ new_sandbox=False)
+ self.assertEqual(foo, 1)
+ foo = self.marionette.execute_async_script(
+ "marionetteScriptFinished(foo)",
+ sandbox="2",
+ new_sandbox=False)
+ self.assertEqual(foo, 2)
+
+
+class TestExecuteSandboxesChrome(TestExecuteSandboxes):
+ def setUp(self):
+ super(TestExecuteSandboxesChrome, self).setUp()
+ self.marionette.set_context("chrome")
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_execute_script.py b/testing/marionette/harness/marionette_harness/tests/unit/test_execute_script.py
new file mode 100644
index 000000000..1ef4549d3
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_execute_script.py
@@ -0,0 +1,402 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+import urllib
+
+from marionette_driver import By, errors
+from marionette_driver.marionette import HTMLElement
+from marionette_driver.wait import Wait
+
+from marionette_harness import MarionetteTestCase, skip_if_mobile, WindowManagerMixin
+
+
+def inline(doc):
+ return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+
+
+elements = inline("<p>foo</p> <p>bar</p>")
+
+globals = set([
+ "atob",
+ "Audio",
+ "btoa",
+ "document",
+ "navigator",
+ "URL",
+ "window",
+ ])
+
+
+class TestExecuteSimpleTestContent(MarionetteTestCase):
+ def test_stack_trace(self):
+ try:
+ self.marionette.execute_js_script("""
+ let a = 1;
+ throwHere();
+ """, filename="file.js")
+ self.assertFalse(True)
+ except errors.JavascriptException as e:
+ self.assertIn("throwHere is not defined", e.message)
+ self.assertIn("@file.js:2", e.stacktrace)
+
+
+class TestExecuteContent(MarionetteTestCase):
+
+ def assert_is_defined(self, property, sandbox="default"):
+ self.assertTrue(self.marionette.execute_script(
+ "return typeof arguments[0] != 'undefined'", [property], sandbox=sandbox),
+ "property {} is undefined".format(property))
+
+ def test_return_number(self):
+ self.assertEqual(1, self.marionette.execute_script("return 1"))
+ self.assertEqual(1.5, self.marionette.execute_script("return 1.5"))
+
+ def test_return_boolean(self):
+ self.assertTrue(self.marionette.execute_script("return true"))
+
+ def test_return_string(self):
+ self.assertEqual("foo", self.marionette.execute_script("return 'foo'"))
+
+ def test_return_array(self):
+ self.assertEqual(
+ [1, 2], self.marionette.execute_script("return [1, 2]"))
+ self.assertEqual(
+ [1.25, 1.75], self.marionette.execute_script("return [1.25, 1.75]"))
+ self.assertEqual(
+ [True, False], self.marionette.execute_script("return [true, false]"))
+ self.assertEqual(
+ ["foo", "bar"], self.marionette.execute_script("return ['foo', 'bar']"))
+ self.assertEqual(
+ [1, 1.5, True, "foo"], self.marionette.execute_script("return [1, 1.5, true, 'foo']"))
+ self.assertEqual(
+ [1, [2]], self.marionette.execute_script("return [1, [2]]"))
+
+ def test_return_object(self):
+ self.assertEqual(
+ {"foo": 1}, self.marionette.execute_script("return {foo: 1}"))
+ self.assertEqual(
+ {"foo": 1.5}, self.marionette.execute_script("return {foo: 1.5}"))
+ self.assertEqual(
+ {"foo": True}, self.marionette.execute_script("return {foo: true}"))
+ self.assertEqual(
+ {"foo": "bar"}, self.marionette.execute_script("return {foo: 'bar'}"))
+ self.assertEqual(
+ {"foo": [1, 2]}, self.marionette.execute_script("return {foo: [1, 2]}"))
+ self.assertEqual(
+ {"foo": {"bar": [1, 2]}},
+ self.marionette.execute_script("return {foo: {bar: [1, 2]}}"))
+
+ def test_no_return_value(self):
+ self.assertIsNone(self.marionette.execute_script("true"))
+
+ def test_argument_null(self):
+ self.assertIsNone(self.marionette.execute_script("return arguments[0]", [None]))
+
+ def test_argument_number(self):
+ self.assertEqual(
+ 1, self.marionette.execute_script("return arguments[0]", [1]))
+ self.assertEqual(
+ 1.5, self.marionette.execute_script("return arguments[0]", [1.5]))
+
+ def test_argument_boolean(self):
+ self.assertTrue(self.marionette.execute_script("return arguments[0]", [True]))
+
+ def test_argument_string(self):
+ self.assertEqual(
+ "foo", self.marionette.execute_script("return arguments[0]", ["foo"]))
+
+ def test_argument_array(self):
+ self.assertEqual(
+ [1, 2], self.marionette.execute_script("return arguments[0]", [[1, 2]]))
+
+ def test_argument_object(self):
+ self.assertEqual({"foo": 1}, self.marionette.execute_script(
+ "return arguments[0]", [{"foo": 1}]))
+
+ def test_globals(self):
+ for property in globals:
+ self.assert_is_defined(property)
+ self.assert_is_defined("Components")
+ self.assert_is_defined("window.wrappedJSObject")
+
+ def test_system_globals(self):
+ for property in globals:
+ self.assert_is_defined(property, sandbox="system")
+ self.assert_is_defined("Components", sandbox="system")
+ self.assert_is_defined("window.wrappedJSObject")
+
+ def test_exception(self):
+ self.assertRaises(errors.JavascriptException,
+ self.marionette.execute_script, "return foo")
+
+ def test_stacktrace(self):
+ with self.assertRaises(errors.JavascriptException) as cm:
+ self.marionette.execute_script("return b")
+
+ # by default execute_script pass the name of the python file
+ self.assertIn(os.path.basename(__file__.replace(".pyc", ".py")),
+ cm.exception.stacktrace)
+ self.assertIn("b is not defined", cm.exception.message)
+ self.assertIn("return b", cm.exception.stacktrace)
+
+ def test_permission(self):
+ with self.assertRaises(errors.JavascriptException):
+ self.marionette.execute_script("""
+ var c = Components.classes["@mozilla.org/preferences-service;1"];
+ """)
+
+ def test_return_web_element(self):
+ self.marionette.navigate(elements)
+ expected = self.marionette.find_element(By.TAG_NAME, "p")
+ actual = self.marionette.execute_script(
+ "return document.querySelector('p')")
+ self.assertEqual(expected, actual)
+
+ def test_return_web_element_array(self):
+ self.marionette.navigate(elements)
+ expected = self.marionette.find_elements(By.TAG_NAME, "p")
+ actual = self.marionette.execute_script("""
+ let els = document.querySelectorAll('p')
+ return [els[0], els[1]]""")
+ self.assertEqual(expected, actual)
+
+ # Bug 938228 identifies a problem with unmarshaling NodeList
+ # objects from the DOM. document.querySelectorAll returns this
+ # construct.
+ def test_return_web_element_nodelist(self):
+ self.marionette.navigate(elements)
+ expected = self.marionette.find_elements(By.TAG_NAME, "p")
+ actual = self.marionette.execute_script(
+ "return document.querySelectorAll('p')")
+ self.assertEqual(expected, actual)
+
+ def test_sandbox_reuse(self):
+ # Sandboxes between `execute_script()` invocations are shared.
+ self.marionette.execute_script("this.foobar = [23, 42];")
+ self.assertEqual(self.marionette.execute_script(
+ "return this.foobar;", new_sandbox=False), [23, 42])
+
+ self.marionette.execute_script("global.barfoo = [42, 23];")
+ self.assertEqual(self.marionette.execute_script(
+ "return global.barfoo;", new_sandbox=False), [42, 23])
+
+ def test_sandbox_refresh_arguments(self):
+ self.marionette.execute_script(
+ "this.foobar = [arguments[0], arguments[1]]", [23, 42])
+ self.assertEqual(self.marionette.execute_script(
+ "return this.foobar", new_sandbox=False), [23, 42])
+
+ def test_wrappedjsobject(self):
+ try:
+ self.marionette.execute_script("window.wrappedJSObject.foo = 3")
+ self.assertEqual(
+ self.marionette.execute_script("return window.wrappedJSObject.foo"), 3)
+ finally:
+ self.marionette.execute_script("delete window.wrappedJSObject.foo")
+
+ def test_system_sandbox_wrappedjsobject(self):
+ self.marionette.execute_script(
+ "window.wrappedJSObject.foo = 4", sandbox="system")
+ self.assertEqual(self.marionette.execute_script(
+ "return window.wrappedJSObject.foo", sandbox="system"), 4)
+
+ def test_system_dead_object(self):
+ self.marionette.execute_script(
+ "window.wrappedJSObject.foo = function() { return 'yo' }",
+ sandbox="system")
+ self.marionette.execute_script(
+ "dump(window.wrappedJSObject.foo)", sandbox="system")
+
+ self.marionette.execute_script(
+ "window.wrappedJSObject.foo = function() { return 'yolo' }",
+ sandbox="system")
+ typ = self.marionette.execute_script(
+ "return typeof window.wrappedJSObject.foo", sandbox="system")
+ self.assertEqual("function", typ)
+ obj = self.marionette.execute_script(
+ "return window.wrappedJSObject.foo.toString()", sandbox="system")
+ self.assertIn("yolo", obj)
+
+ def test_lasting_side_effects(self):
+ def send(script):
+ return self.marionette._send_message(
+ "executeScript", {"script": script}, key="value")
+
+ send("window.foo = 1")
+ foo = send("return window.foo")
+ self.assertEqual(1, foo)
+
+ for property in globals:
+ exists = send("return typeof {} != 'undefined'".format(property))
+ self.assertTrue(exists, "property {} is undefined".format(property))
+
+ self.assertTrue(send("return typeof Components.utils == 'undefined'"))
+ self.assertTrue(send("return typeof window.wrappedJSObject == 'undefined'"))
+
+ def test_no_callback(self):
+ self.assertTrue(self.marionette.execute_script(
+ "return typeof arguments[0] == 'undefined'"))
+
+ def test_window_set_timeout_is_not_cancelled(self):
+ def content_timeout_triggered(mn):
+ return mn.execute_script("return window.n", sandbox=None) > 0
+
+ # subsequent call to execute_script after this
+ # should not cancel the setTimeout event
+ self.marionette.navigate(inline("""
+ <script>
+ window.n = 0;
+ setTimeout(() => ++window.n, 4000);
+ </script>"""))
+
+ # as debug builds are inherently slow,
+ # we need to assert the event did not already fire
+ self.assertEqual(0, self.marionette.execute_script(
+ "return window.n", sandbox=None),
+ "setTimeout already fired")
+
+ # if event was cancelled, this will time out
+ Wait(self.marionette, timeout=8).until(
+ content_timeout_triggered,
+ message="Scheduled setTimeout event was cancelled by call to execute_script")
+
+ def test_privileged_code_inspection(self):
+ # test permission denied on toString of unload event handler
+ self.marionette.navigate(inline("""
+ <script>
+ window.addEventListener = (type, handler) => handler.toString();
+ </script>"""))
+ self.marionette.execute_script("", sandbox=None)
+
+ # test inspection of arguments
+ self.marionette.execute_script("__webDriverArguments.toString()")
+
+
+class TestExecuteChrome(WindowManagerMixin, TestExecuteContent):
+
+ def setUp(self):
+ super(TestExecuteChrome, self).setUp()
+
+ self.marionette.set_context("chrome")
+
+ def tearDown(self):
+ super(TestExecuteChrome, self).tearDown()
+
+ def test_permission(self):
+ self.assertEqual(1, self.marionette.execute_script("""
+ var c = Components.classes["@mozilla.org/preferences-service;1"]; return 1;"""))
+
+ @skip_if_mobile("New windows not supported in Fennec")
+ def test_unmarshal_element_collection(self):
+
+ def open_window_with_js():
+ self.marionette.execute_script(
+ "window.open('chrome://marionette/content/test.xul', 'xul', 'chrome');")
+
+ try:
+ win = self.open_window(trigger=open_window_with_js)
+ self.marionette.switch_to_window(win)
+
+ expected = self.marionette.find_elements(By.TAG_NAME, "textbox")
+ actual = self.marionette.execute_script(
+ "return document.querySelectorAll('textbox')")
+ self.assertEqual(expected, actual)
+
+ finally:
+ self.close_all_windows()
+
+ def test_async_script_timeout(self):
+ with self.assertRaises(errors.ScriptTimeoutException):
+ self.marionette.execute_async_script("""
+ var cb = arguments[arguments.length - 1];
+ setTimeout(function() { cb() }, 250);
+ """, script_timeout=100)
+
+ @skip_if_mobile("New windows not supported in Fennec")
+ def test_invalid_chrome_handle(self):
+ try:
+ win = self.open_window()
+ self.marionette.switch_to_window(win)
+
+ # Close new window and don't switch back to the original one
+ self.marionette.close_chrome_window()
+ self.assertNotEqual(self.start_window, win)
+
+ # Call execute_script on an invalid chrome handle
+ with self.marionette.using_context('chrome'):
+ self.marionette.execute_script("""
+ return true;
+ """)
+
+ finally:
+ self.close_all_windows()
+
+ def test_lasting_side_effects(self):
+ pass
+
+ def test_return_web_element(self):
+ pass
+
+ def test_return_web_element_array(self):
+ pass
+
+ def test_return_web_element_nodelist(self):
+ pass
+
+ def test_window_set_timeout_is_not_cancelled(self):
+ pass
+
+ def test_privileged_code_inspection(self):
+ pass
+
+
+class TestElementCollections(MarionetteTestCase):
+
+ def assertSequenceIsInstance(self, seq, typ):
+ for item in seq:
+ self.assertIsInstance(item, typ)
+
+ def test_array(self):
+ self.marionette.navigate(inline("<p>foo <p>bar"))
+ els = self.marionette.execute_script("return Array.from(document.querySelectorAll('p'))")
+ self.assertIsInstance(els, list)
+ self.assertEqual(2, len(els))
+ self.assertSequenceIsInstance(els, HTMLElement)
+
+ def test_html_all_collection(self):
+ self.marionette.navigate(inline("<p>foo <p>bar"))
+ els = self.marionette.execute_script("return document.all")
+ self.assertIsInstance(els, list)
+ # <html>, <head>, <body>, <p>, <p>
+ self.assertEqual(5, len(els))
+ self.assertSequenceIsInstance(els, HTMLElement)
+
+ def test_html_collection(self):
+ self.marionette.navigate(inline("<p>foo <p>bar"))
+ els = self.marionette.execute_script("return document.getElementsByTagName('p')")
+ self.assertIsInstance(els, list)
+ self.assertEqual(2, len(els))
+ self.assertSequenceIsInstance(els, HTMLElement)
+
+ def test_html_form_controls_collection(self):
+ self.marionette.navigate(inline("<form><input><input></form>"))
+ els = self.marionette.execute_script("return document.forms[0].elements")
+ self.assertIsInstance(els, list)
+ self.assertEqual(2, len(els))
+ self.assertSequenceIsInstance(els, HTMLElement)
+
+ def test_html_options_collection(self):
+ self.marionette.navigate(inline("<select><option><option></select>"))
+ els = self.marionette.execute_script("return document.querySelector('select').options")
+ self.assertIsInstance(els, list)
+ self.assertEqual(2, len(els))
+ self.assertSequenceIsInstance(els, HTMLElement)
+
+ def test_node_list(self):
+ self.marionette.navigate(inline("<p>foo <p>bar"))
+ els = self.marionette.execute_script("return document.querySelectorAll('p')")
+ self.assertIsInstance(els, list)
+ self.assertEqual(2, len(els))
+ self.assertSequenceIsInstance(els, HTMLElement)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_expected.py b/testing/marionette/harness/marionette_harness/tests/unit/test_expected.py
new file mode 100644
index 000000000..ff8717c69
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_expected.py
@@ -0,0 +1,228 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import urllib
+
+from marionette_driver import expected
+from marionette_driver.by import By
+
+from marionette_harness import marionette_test
+
+
+def inline(doc):
+ return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+
+static_element = inline("""<p>foo</p>""")
+static_elements = static_element + static_element
+
+remove_element_by_tag_name = \
+ """var el = document.getElementsByTagName('{}')[0];
+ document.getElementsByTagName("body")[0].remove(el);"""
+
+hidden_element = inline("<p style='display: none'>hidden</p>")
+
+selected_element = inline("<option selected>selected</option>")
+unselected_element = inline("<option>unselected</option>")
+
+enabled_element = inline("<input>")
+disabled_element = inline("<input disabled>")
+
+def no_such_element(marionette):
+ return marionette.find_element(By.ID, "nosuchelement")
+
+def no_such_elements(marionette):
+ return marionette.find_elements(By.ID, "nosuchelement")
+
+def p(marionette):
+ return marionette.find_element(By.TAG_NAME, "p")
+
+def ps(marionette):
+ return marionette.find_elements(By.TAG_NAME, "p")
+
+class TestExpected(marionette_test.MarionetteTestCase):
+ def test_element_present_func(self):
+ self.marionette.navigate(static_element)
+ el = expected.element_present(p)(self.marionette)
+ self.assertIsNotNone(el)
+
+ def test_element_present_locator(self):
+ self.marionette.navigate(static_element)
+ el = expected.element_present(By.TAG_NAME, "p")(self.marionette)
+ self.assertIsNotNone(el)
+
+ def test_element_present_not_present(self):
+ r = expected.element_present(no_such_element)(self.marionette)
+ self.assertIsInstance(r, bool)
+ self.assertFalse(r)
+
+ def test_element_not_present_func(self):
+ r = expected.element_not_present(no_such_element)(self.marionette)
+ self.assertIsInstance(r, bool)
+ self.assertTrue(r)
+
+ def test_element_not_present_locator(self):
+ r = expected.element_not_present(By.ID, "nosuchelement")(self.marionette)
+ self.assertIsInstance(r, bool)
+ self.assertTrue(r)
+
+ def test_element_not_present_is_present(self):
+ self.marionette.navigate(static_element)
+ r = expected.element_not_present(p)(self.marionette)
+ self.assertIsInstance(r, bool)
+ self.assertFalse(r)
+
+ def test_element_stale(self):
+ self.marionette.navigate(static_element)
+ el = self.marionette.find_element(By.TAG_NAME, "p")
+ self.assertIsNotNone(el)
+ self.marionette.execute_script(remove_element_by_tag_name.format("p"))
+ r = expected.element_stale(el)(self.marionette)
+ self.assertTrue(r)
+
+ def test_element_stale_is_not_stale(self):
+ self.marionette.navigate(static_element)
+ el = self.marionette.find_element(By.TAG_NAME, "p")
+ r = expected.element_stale(el)(self.marionette)
+ self.assertFalse(r)
+
+ def test_elements_present_func(self):
+ self.marionette.navigate(static_elements)
+ els = expected.elements_present(ps)(self.marionette)
+ self.assertEqual(len(els), 2)
+
+ def test_elements_present_locator(self):
+ self.marionette.navigate(static_elements)
+ els = expected.elements_present(By.TAG_NAME, "p")(self.marionette)
+ self.assertEqual(len(els), 2)
+
+ def test_elements_present_not_present(self):
+ r = expected.elements_present(no_such_elements)(self.marionette)
+ self.assertEqual(r, [])
+
+ def test_elements_not_present_func(self):
+ r = expected.element_not_present(no_such_elements)(self.marionette)
+ self.assertIsInstance(r, bool)
+ self.assertTrue(r)
+
+ def test_elements_not_present_locator(self):
+ r = expected.element_not_present(By.ID, "nosuchelement")(self.marionette)
+ self.assertIsInstance(r, bool)
+ self.assertTrue(r)
+
+ def test_elements_not_present_is_present(self):
+ self.marionette.navigate(static_elements)
+ r = expected.elements_not_present(ps)(self.marionette)
+ self.assertIsInstance(r, bool)
+ self.assertFalse(r)
+
+ def test_element_displayed(self):
+ self.marionette.navigate(static_element)
+ el = self.marionette.find_element(By.TAG_NAME, "p")
+ visible = expected.element_displayed(el)(self.marionette)
+ self.assertTrue(visible)
+
+ def test_element_displayed_locator(self):
+ self.marionette.navigate(static_element)
+ visible = expected.element_displayed(By.TAG_NAME, "p")(self.marionette)
+ self.assertTrue(visible)
+
+ def test_element_displayed_when_hidden(self):
+ self.marionette.navigate(hidden_element)
+ el = self.marionette.find_element(By.TAG_NAME, "p")
+ visible = expected.element_displayed(el)(self.marionette)
+ self.assertFalse(visible)
+
+ def test_element_displayed_when_hidden_locator(self):
+ self.marionette.navigate(hidden_element)
+ visible = expected.element_displayed(By.TAG_NAME, "p")(self.marionette)
+ self.assertFalse(visible)
+
+ def test_element_displayed_when_not_present(self):
+ self.marionette.navigate("about:blank")
+ visible = expected.element_displayed(By.TAG_NAME, "p")(self.marionette)
+ self.assertFalse(visible)
+
+ def test_element_displayed_when_stale_element(self):
+ self.marionette.navigate(static_element)
+ el = self.marionette.find_element(By.TAG_NAME, "p")
+ self.marionette.navigate("about:blank")
+ missing = expected.element_displayed(el)(self.marionette)
+ self.assertFalse(missing)
+
+ def test_element_not_displayed(self):
+ self.marionette.navigate(hidden_element)
+ el = self.marionette.find_element(By.TAG_NAME, "p")
+ hidden = expected.element_not_displayed(el)(self.marionette)
+ self.assertTrue(hidden)
+
+ def test_element_not_displayed_locator(self):
+ self.marionette.navigate(hidden_element)
+ hidden = expected.element_not_displayed(By.TAG_NAME, "p")(self.marionette)
+ self.assertTrue(hidden)
+
+ def test_element_not_displayed_when_visible(self):
+ self.marionette.navigate(static_element)
+ el = self.marionette.find_element(By.TAG_NAME, "p")
+ hidden = expected.element_not_displayed(el)(self.marionette)
+ self.assertFalse(hidden)
+
+ def test_element_not_displayed_when_visible_locator(self):
+ self.marionette.navigate(static_element)
+ hidden = expected.element_not_displayed(By.TAG_NAME, "p")(self.marionette)
+ self.assertFalse(hidden)
+
+ def test_element_not_displayed_when_stale_element(self):
+ self.marionette.navigate(static_element)
+ el = self.marionette.find_element(By.TAG_NAME, "p")
+ self.marionette.navigate("about:blank")
+ missing = expected.element_not_displayed(el)(self.marionette)
+ self.assertTrue(missing)
+
+ def test_element_selected(self):
+ self.marionette.navigate(selected_element)
+ el = self.marionette.find_element(By.TAG_NAME, "option")
+ selected = expected.element_selected(el)(self.marionette)
+ self.assertTrue(selected)
+
+ def test_element_selected_when_not_selected(self):
+ self.marionette.navigate(unselected_element)
+ el = self.marionette.find_element(By.TAG_NAME, "option")
+ unselected = expected.element_selected(el)(self.marionette)
+ self.assertFalse(unselected)
+
+ def test_element_not_selected(self):
+ self.marionette.navigate(unselected_element)
+ el = self.marionette.find_element(By.TAG_NAME, "option")
+ unselected = expected.element_not_selected(el)(self.marionette)
+ self.assertTrue(unselected)
+
+ def test_element_not_selected_when_selected(self):
+ self.marionette.navigate(selected_element)
+ el = self.marionette.find_element(By.TAG_NAME, "option")
+ selected = expected.element_not_selected(el)(self.marionette)
+ self.assertFalse(selected)
+
+ def test_element_enabled(self):
+ self.marionette.navigate(enabled_element)
+ el = self.marionette.find_element(By.TAG_NAME, "input")
+ enabled = expected.element_enabled(el)(self.marionette)
+ self.assertTrue(enabled)
+
+ def test_element_enabled_when_disabled(self):
+ self.marionette.navigate(disabled_element)
+ el = self.marionette.find_element(By.TAG_NAME, "input")
+ disabled = expected.element_enabled(el)(self.marionette)
+ self.assertFalse(disabled)
+
+ def test_element_not_enabled(self):
+ self.marionette.navigate(disabled_element)
+ el = self.marionette.find_element(By.TAG_NAME, "input")
+ disabled = expected.element_not_enabled(el)(self.marionette)
+ self.assertTrue(disabled)
+
+ def test_element_not_enabled_when_enabled(self):
+ self.marionette.navigate(enabled_element)
+ el = self.marionette.find_element(By.TAG_NAME, "input")
+ enabled = expected.element_not_enabled(el)(self.marionette)
+ self.assertFalse(enabled)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_expectedfail.py b/testing/marionette/harness/marionette_harness/tests/unit/test_expectedfail.py
new file mode 100644
index 000000000..138a36c58
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_expectedfail.py
@@ -0,0 +1,11 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestFail(MarionetteTestCase):
+ def test_fails(self):
+ # this test is supposed to fail!
+ self.assertEquals(True, False)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_file_upload.py b/testing/marionette/harness/marionette_harness/tests/unit/test_file_upload.py
new file mode 100644
index 000000000..f67be9556
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_file_upload.py
@@ -0,0 +1,152 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import contextlib
+import urllib
+
+from tempfile import NamedTemporaryFile as tempfile
+
+from marionette_driver import By, errors, expected
+from marionette_driver.wait import Wait
+from marionette_harness import MarionetteTestCase, skip
+
+
+single = "data:text/html,{}".format(urllib.quote("<input type=file>"))
+multiple = "data:text/html,{}".format(urllib.quote("<input type=file multiple>"))
+upload = lambda url: "data:text/html,{}".format(urllib.quote("""
+ <form action='{}' method=post enctype='multipart/form-data'>
+ <input type=file>
+ <input type=submit>
+ </form>""".format(url)))
+
+
+class TestFileUpload(MarionetteTestCase):
+ def test_sets_one_file(self):
+ self.marionette.navigate(single)
+ input = self.input
+
+ exp = None
+ with tempfile() as f:
+ input.send_keys(f.name)
+ exp = [f.name]
+
+ files = self.get_file_names(input)
+ self.assertEqual(len(files), 1)
+ self.assertFileNamesEqual(files, exp)
+
+ def test_sets_multiple_files(self):
+ self.marionette.navigate(multiple)
+ input = self.input
+
+ exp = None
+ with contextlib.nested(tempfile(), tempfile()) as (a, b):
+ input.send_keys(a.name)
+ input.send_keys(b.name)
+ exp = [a.name, b.name]
+
+ files = self.get_file_names(input)
+ self.assertEqual(len(files), 2)
+ self.assertFileNamesEqual(files, exp)
+
+ def test_sets_multiple_indentical_files(self):
+ self.marionette.navigate(multiple)
+ input = self.input
+
+ exp = []
+ with tempfile() as f:
+ input.send_keys(f.name)
+ input.send_keys(f.name)
+ exp = f.name
+
+ files = self.get_file_names(input)
+ self.assertEqual(len(files), 2)
+ self.assertFileNamesEqual(files, exp)
+
+ def test_clear_file(self):
+ self.marionette.navigate(single)
+ input = self.input
+
+ with tempfile() as f:
+ input.send_keys(f.name)
+
+ self.assertEqual(len(self.get_files(input)), 1)
+ input.clear()
+ self.assertEqual(len(self.get_files(input)), 0)
+
+ def test_clear_files(self):
+ self.marionette.navigate(multiple)
+ input = self.input
+
+ with contextlib.nested(tempfile(), tempfile()) as (a, b):
+ input.send_keys(a.name)
+ input.send_keys(b.name)
+
+ self.assertEqual(len(self.get_files(input)), 2)
+ input.clear()
+ self.assertEqual(len(self.get_files(input)), 0)
+
+ def test_illegal_file(self):
+ self.marionette.navigate(single)
+ with self.assertRaisesRegexp(errors.MarionetteException, "File not found"):
+ self.input.send_keys("rochefort")
+
+ def test_upload(self):
+ self.marionette.navigate(
+ upload(self.marionette.absolute_url("file_upload")))
+ url = self.marionette.get_url()
+
+ with tempfile() as f:
+ f.write("camembert")
+ f.flush()
+ self.input.send_keys(f.name)
+ self.submit.click()
+
+ Wait(self.marionette).until(lambda m: m.get_url() != url)
+ self.assertIn("multipart/form-data", self.body.text)
+
+ def test_change_event(self):
+ self.marionette.navigate(single)
+ self.marionette.execute_script("""
+ window.changeEvs = [];
+ let el = arguments[arguments.length - 1];
+ el.addEventListener("change", ev => window.changeEvs.push(ev));
+ console.log(window.changeEvs.length);
+ """, script_args=(self.input,), sandbox=None)
+
+ with tempfile() as f:
+ self.input.send_keys(f.name)
+
+ nevs = self.marionette.execute_script(
+ "return window.changeEvs.length", sandbox=None)
+ self.assertEqual(1, nevs)
+
+ def find_inputs(self):
+ return self.marionette.find_elements(By.TAG_NAME, "input")
+
+ @property
+ def input(self):
+ return self.find_inputs()[0]
+
+ @property
+ def submit(self):
+ return self.find_inputs()[1]
+
+ @property
+ def body(self):
+ return Wait(self.marionette).until(
+ expected.element_present(By.TAG_NAME, "body"))
+
+ def get_file_names(self, el):
+ fl = self.get_files(el)
+ return [f["name"] for f in fl]
+
+ def get_files(self, el):
+ return self.marionette.execute_script(
+ "return arguments[0].files", script_args=[el])
+
+ def assertFileNamesEqual(self, act, exp):
+ # File array returned from browser doesn't contain full path names,
+ # this cuts off the path of the expected files.
+ filenames = [f.rsplit("/", 0)[-1] for f in act]
+ self.assertListEqual(filenames, act)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_findelement_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_findelement_chrome.py
new file mode 100644
index 000000000..e6b2d63bf
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_findelement_chrome.py
@@ -0,0 +1,82 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+from marionette_driver.errors import NoSuchElementException
+from marionette_driver.marionette import HTMLElement
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestElementsChrome(MarionetteTestCase):
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ self.marionette.set_context("chrome")
+ self.win = self.marionette.current_window_handle
+ self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen');")
+ self.marionette.switch_to_window('foo')
+ self.assertNotEqual(self.win, self.marionette.current_window_handle)
+
+ def tearDown(self):
+ self.assertNotEqual(self.win, self.marionette.current_window_handle)
+ self.marionette.execute_script("window.close();")
+ self.marionette.switch_to_window(self.win)
+ MarionetteTestCase.tearDown(self)
+
+ def test_id(self):
+ el = self.marionette.execute_script("return window.document.getElementById('textInput');")
+ found_el = self.marionette.find_element(By.ID, "textInput")
+ self.assertEqual(HTMLElement, type(found_el))
+ self.assertEqual(el, found_el)
+
+ def test_that_we_can_find_elements_from_css_selectors(self):
+ el = self.marionette.execute_script("return window.document.getElementById('textInput');")
+ found_el = self.marionette.find_element(By.CSS_SELECTOR, "#textInput")
+ self.assertEqual(HTMLElement, type(found_el))
+ self.assertEqual(el, found_el)
+
+ def test_child_element(self):
+ el = self.marionette.find_element(By.ID, "textInput")
+ parent = self.marionette.find_element(By.ID, "things")
+ found_el = parent.find_element(By.TAG_NAME, "textbox")
+ self.assertEqual(HTMLElement, type(found_el))
+ self.assertEqual(el, found_el)
+
+ def test_child_elements(self):
+ el = self.marionette.find_element(By.ID, "textInput3")
+ parent = self.marionette.find_element(By.ID, "things")
+ found_els = parent.find_elements(By.TAG_NAME, "textbox")
+ self.assertTrue(el.id in [found_el.id for found_el in found_els])
+
+ def test_tag_name(self):
+ el = self.marionette.execute_script("return window.document.getElementsByTagName('vbox')[0];")
+ found_el = self.marionette.find_element(By.TAG_NAME, "vbox")
+ self.assertEquals('vbox', found_el.tag_name)
+ self.assertEqual(HTMLElement, type(found_el))
+ self.assertEqual(el, found_el)
+
+ def test_class_name(self):
+ el = self.marionette.execute_script("return window.document.getElementsByClassName('asdf')[0];")
+ found_el = self.marionette.find_element(By.CLASS_NAME, "asdf")
+ self.assertEqual(HTMLElement, type(found_el))
+ self.assertEqual(el, found_el)
+
+ def test_xpath(self):
+ el = self.marionette.execute_script("return window.document.getElementById('testBox');")
+ found_el = self.marionette.find_element(By.XPATH, "id('testBox')")
+ self.assertEqual(HTMLElement, type(found_el))
+ self.assertEqual(el, found_el)
+
+ def test_not_found(self):
+ self.marionette.timeout.implicit = 1
+ self.assertRaises(NoSuchElementException, self.marionette.find_element, By.ID, "I'm not on the page")
+ self.marionette.timeout.implicit = 0
+ self.assertRaises(NoSuchElementException, self.marionette.find_element, By.ID, "I'm not on the page")
+
+ def test_timeout(self):
+ self.assertRaises(NoSuchElementException, self.marionette.find_element, By.ID, "myid")
+ self.marionette.timeout.implicit = 4
+ self.marionette.execute_script("window.setTimeout(function() {var b = window.document.createElement('button'); b.id = 'myid'; document.getElementById('things').appendChild(b);}, 1000)")
+ self.assertEqual(HTMLElement, type(self.marionette.find_element(By.ID, "myid")))
+ self.marionette.execute_script("window.document.getElementById('things').removeChild(window.document.getElementById('myid'));")
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_geckoinstance.py b/testing/marionette/harness/marionette_harness/tests/unit/test_geckoinstance.py
new file mode 100644
index 000000000..540550296
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_geckoinstance.py
@@ -0,0 +1,25 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.geckoinstance import apps, GeckoInstance
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestGeckoInstance(MarionetteTestCase):
+
+ def test_create(self):
+ """Test that the correct gecko instance is determined."""
+ for app in apps:
+ # If app has been specified we directly return the appropriate instance class
+ self.assertEqual(type(GeckoInstance.create(app=app, bin="n/a")),
+ apps[app])
+
+ # Unknown applications and binaries should fail
+ self.assertRaises(NotImplementedError, GeckoInstance.create,
+ app="n/a", bin=self.marionette.bin)
+ self.assertRaises(NotImplementedError, GeckoInstance.create,
+ bin="n/a")
+ self.assertRaises(NotImplementedError, GeckoInstance.create,
+ bin=None)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_getactiveframe_oop.py b/testing/marionette/harness/marionette_harness/tests/unit/test_getactiveframe_oop.py
new file mode 100644
index 000000000..9325d4892
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_getactiveframe_oop.py
@@ -0,0 +1,93 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+
+from marionette_harness import MarionetteTestCase
+
+
+OOP_BY_DEFAULT = "dom.ipc.browser_frames.oop_by_default"
+BROWSER_FRAMES_ENABLED = "dom.mozBrowserFramesEnabeld"
+
+
+class TestGetActiveFrameOOP(MarionetteTestCase):
+ def setUp(self):
+ super(TestGetActiveFrameOOP, self).setUp()
+ with self.marionette.using_context("chrome"):
+ self.oop_by_default = self.marionette.get_pref(OOP_BY_DEFAULT)
+ self.mozBrowserFramesEnabled = self.marionette.get_pref(BROWSER_FRAMES_ENABLED)
+ self.marionette.set_pref(OOP_BY_DEFAULT, True)
+ self.marionette.set_pref(BROWSER_FRAMES_ENABLED, True)
+
+ def tearDown(self):
+ with self.marionette.using_context("chrome"):
+ if self.oop_by_default is None:
+ self.marionette.clear_pref(OOP_BY_DEFAULT)
+ else:
+ self.marionette.set_pref(OOP_BY_DEFAULT, self.oop_by_default)
+
+ if self.mozBrowserFramesEnabled is None:
+ self.marionette.clear_pref(BROWSER_FRAMES_ENABLED)
+ else:
+ self.marionette.set_pref(BROWSER_FRAMES_ENABLED, self.mozBrowserFramesEnabled)
+
+ def test_active_frame_oop(self):
+ self.marionette.navigate(self.marionette.absolute_url("test.html"))
+ self.marionette.push_permission('browser', True)
+
+ # Create first OOP frame
+ self.marionette.execute_script("""
+ let iframe1 = document.createElement("iframe");
+ iframe1.id = "remote_iframe1";
+ iframe1.setAttribute('remote', true);
+ iframe1.setAttribute('mozbrowser', true);
+ iframe1.style.height = "100px";
+ iframe1.style.width = "100%%";
+ iframe1.src = "{}";
+ document.body.appendChild(iframe1);
+ """.format(self.marionette.absolute_url("test_oop_1.html")))
+
+ # Currently no active frame
+ self.assertEqual(self.marionette.get_active_frame(), None)
+ self.assertTrue("test.html" in self.marionette.get_url())
+
+ # Switch to iframe1, get active frame
+ frame = self.marionette.find_element(By.ID, 'remote_iframe1')
+ self.marionette.switch_to_frame(frame)
+ active_frame1 = self.marionette.get_active_frame()
+ self.assertNotEqual(active_frame1.id, None)
+
+ # Switch to top-level then back to active frame, verify correct frame
+ self.marionette.switch_to_frame()
+ self.marionette.switch_to_frame(active_frame1)
+ self.assertTrue("test_oop_1.html" in self.marionette.execute_script("return document.wrappedJSObject.location.href"))
+
+ # Create another OOP frame
+ self.marionette.switch_to_frame()
+ self.marionette.execute_script("""
+ let iframe2 = document.createElement("iframe");
+ iframe2.setAttribute('mozbrowser', true);
+ iframe2.setAttribute('remote', true);
+ iframe2.id = "remote_iframe2";
+ iframe2.style.height = "100px";
+ iframe2.style.width = "100%%";
+ iframe2.src = "{}";
+ document.body.appendChild(iframe2);
+ """.format(self.marionette.absolute_url("test_oop_2.html")))
+
+ # Switch to iframe2, get active frame
+ frame2 = self.marionette.find_element(By.ID, 'remote_iframe2')
+ self.marionette.switch_to_frame(frame2)
+ active_frame2 = self.marionette.get_active_frame()
+ self.assertNotEqual(active_frame2.id, None)
+
+ # Switch to top-level then back to active frame 1, verify correct frame
+ self.marionette.switch_to_frame()
+ self.marionette.switch_to_frame(active_frame1)
+ self.assertTrue("test_oop_1.html" in self.marionette.execute_script("return document.wrappedJSObject.location.href"))
+
+ # Switch to top-level then back to active frame 2, verify correct frame
+ self.marionette.switch_to_frame()
+ self.marionette.switch_to_frame(active_frame2)
+ self.assertTrue("test_oop_2.html" in self.marionette.execute_script("return document.wrappedJSObject.location.href"))
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_implicit_waits.py b/testing/marionette/harness/marionette_harness/tests/unit/test_implicit_waits.py
new file mode 100644
index 000000000..954443ac3
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_implicit_waits.py
@@ -0,0 +1,26 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+from marionette_driver.errors import NoSuchElementException
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestImplicitWaits(MarionetteTestCase):
+ def test_implicitly_wait_for_single_element(self):
+ test_html = self.marionette.absolute_url("test_dynamic.html")
+ self.marionette.navigate(test_html)
+ add = self.marionette.find_element(By.ID, "adder")
+ self.marionette.timeout.implicit = 30
+ add.click()
+ # all is well if this does not throw
+ self.marionette.find_element(By.ID, "box0")
+
+ def test_implicit_wait_reaches_timeout(self):
+ test_html = self.marionette.absolute_url("test_dynamic.html")
+ self.marionette.navigate(test_html)
+ self.marionette.timeout.implicit = 3
+ with self.assertRaises(NoSuchElementException):
+ self.marionette.find_element(By.ID, "box0")
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_import_script.py b/testing/marionette/harness/marionette_harness/tests/unit/test_import_script.py
new file mode 100644
index 000000000..e86de2bd5
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_import_script.py
@@ -0,0 +1,138 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+
+from marionette_driver.by import By
+from marionette_driver.errors import JavascriptException
+
+from marionette_harness import (
+ MarionetteTestCase,
+ skip_if_chrome,
+ skip_if_mobile,
+ WindowManagerMixin,
+)
+
+
+class TestImportScriptContent(WindowManagerMixin, MarionetteTestCase):
+ contexts = set(["chrome", "content"])
+
+ script_file = os.path.abspath(
+ os.path.join(__file__, os.path.pardir, "importscript.js"))
+ another_script_file = os.path.abspath(
+ os.path.join(__file__, os.path.pardir, "importanotherscript.js"))
+
+ def setUp(self):
+ super(TestImportScriptContent, self).setUp()
+
+ for context in self.contexts:
+ with self.marionette.using_context(context):
+ self.marionette.clear_imported_scripts()
+ self.reset_context()
+
+ def tearDown(self):
+ self.close_all_windows()
+
+ super(TestImportScriptContent, self).tearDown()
+
+ def reset_context(self):
+ self.marionette.set_context("content")
+
+ @property
+ def current_context(self):
+ return self.marionette._send_message("getContext", key="value")
+
+ @property
+ def other_context(self):
+ return self.contexts.copy().difference([self.current_context]).pop()
+
+ def is_defined(self, symbol):
+ return self.marionette.execute_script(
+ "return typeof {} != 'undefined'".format(symbol))
+
+ def assert_defined(self, symbol, msg=None):
+ if msg is None:
+ msg = "Expected symbol {} to be defined".format(symbol)
+ self.assertTrue(self.is_defined(symbol), msg)
+
+ def assert_undefined(self, symbol, msg=None):
+ if msg is None:
+ msg = "Expected symbol {} to be undefined".format(symbol)
+ self.assertFalse(self.is_defined(symbol), msg)
+
+ def assert_scripts_cleared(self):
+ self.marionette.import_script(self.script_file)
+ self.assert_defined("testFunc")
+ self.marionette.clear_imported_scripts()
+ self.assert_undefined("testFunc")
+
+ def test_import_script(self):
+ self.marionette.import_script(self.script_file)
+ self.assertEqual(
+ "i'm a test function!", self.marionette.execute_script("return testFunc();"))
+ self.assertEqual("i'm a test function!", self.marionette.execute_async_script(
+ "marionetteScriptFinished(testFunc());"))
+
+ def test_import_script_twice(self):
+ self.marionette.import_script(self.script_file)
+ self.assert_defined("testFunc")
+
+ # TODO(ato): Note that the WebDriver command primitives
+ # does not allow us to check what scripts have been imported.
+ # I suspect we must to do this through an xpcshell test.
+
+ self.marionette.import_script(self.script_file)
+ self.assert_defined("testFunc")
+
+ def test_import_script_and_clear(self):
+ self.marionette.import_script(self.script_file)
+ self.assert_defined("testFunc")
+ self.marionette.clear_imported_scripts()
+ self.assert_scripts_cleared()
+ self.assert_undefined("testFunc")
+ with self.assertRaises(JavascriptException):
+ self.marionette.execute_script("return testFunc()")
+ with self.assertRaises(JavascriptException):
+ self.marionette.execute_async_script(
+ "marionetteScriptFinished(testFunc())")
+
+ def test_clear_scripts_in_other_context(self):
+ self.marionette.import_script(self.script_file)
+ self.assert_defined("testFunc")
+
+ # clearing other context's script file should not affect ours
+ with self.marionette.using_context(self.other_context):
+ self.marionette.clear_imported_scripts()
+ self.assert_undefined("testFunc")
+
+ self.assert_defined("testFunc")
+
+ def test_multiple_imports(self):
+ self.marionette.import_script(self.script_file)
+ self.marionette.import_script(self.another_script_file)
+ self.assert_defined("testFunc")
+ self.assert_defined("testAnotherFunc")
+
+ @skip_if_chrome("Needs content scope")
+ @skip_if_mobile("New windows not supported in Fennec")
+ def test_imports_apply_globally(self):
+ self.marionette.navigate(
+ self.marionette.absolute_url("test_windows.html"))
+
+ def open_window_with_link():
+ self.marionette.find_element(By.LINK_TEXT, "Open new window").click()
+
+ new_window = self.open_window(trigger=open_window_with_link)
+ self.marionette.switch_to_window(new_window)
+
+ self.marionette.import_script(self.script_file)
+ self.marionette.close_chrome_window()
+
+ self.marionette.switch_to_window(self.start_window)
+ self.assert_defined("testFunc")
+
+
+class TestImportScriptChrome(TestImportScriptContent):
+ def reset_context(self):
+ self.marionette.set_context("chrome")
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_key_actions.py b/testing/marionette/harness/marionette_harness/tests/unit/test_key_actions.py
new file mode 100644
index 000000000..60e38b3c6
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_key_actions.py
@@ -0,0 +1,91 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+from marionette_driver.keys import Keys
+from marionette_driver.marionette import Actions
+
+from marionette_harness import MarionetteTestCase, skip_if_mobile, WindowManagerMixin
+
+
+class TestKeyActions(WindowManagerMixin, MarionetteTestCase):
+
+ def setUp(self):
+ super(TestKeyActions, self).setUp()
+ if self.marionette.session_capabilities["platformName"] == "darwin":
+ self.mod_key = Keys.META
+ else:
+ self.mod_key = Keys.CONTROL
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ self.reporter_element = self.marionette.find_element(By.ID, "keyReporter")
+ self.reporter_element.click()
+ self.key_action = Actions(self.marionette)
+
+ @property
+ def key_reporter_value(self):
+ return self.reporter_element.get_property("value")
+
+ def test_key_action_basic_input(self):
+ self.key_action.key_down("a").key_down("b").key_down("c").perform()
+ self.assertEqual(self.key_reporter_value, "abc")
+
+ def test_upcase_input(self):
+ (self.key_action.key_down(Keys.SHIFT)
+ .key_down("a")
+ .key_up(Keys.SHIFT)
+ .key_down("b")
+ .key_down("c")
+ .perform())
+ self.assertEqual(self.key_reporter_value, "Abc")
+
+ def test_replace_input(self):
+ self.key_action.key_down("a").key_down("b").key_down("c").perform()
+ self.assertEqual(self.key_reporter_value, "abc")
+ (self.key_action.key_down(self.mod_key)
+ .key_down("a")
+ .key_up(self.mod_key)
+ .key_down("x")
+ .perform())
+ self.assertEqual(self.key_reporter_value, "x")
+
+ def test_clear_input(self):
+ self.key_action.key_down("a").key_down("b").key_down("c").perform()
+ self.assertEqual(self.key_reporter_value, "abc")
+ (self.key_action.key_down(self.mod_key)
+ .key_down("a")
+ .key_down("x")
+ .perform())
+ self.assertEqual(self.key_reporter_value, "")
+ self.key_action.key_down("a").key_down("b").key_down("c").perform()
+ self.assertEqual(self.key_reporter_value, "abc")
+
+ def test_input_with_wait(self):
+ self.key_action.key_down("a").key_down("b").key_down("c").perform()
+ (self.key_action.key_down(self.mod_key)
+ .key_down("a")
+ .wait(.5)
+ .key_down("x")
+ .perform())
+ self.assertEqual(self.key_reporter_value, "")
+
+ @skip_if_mobile("Interacting with chrome windows not available for Fennec")
+ def test_open_in_new_window_shortcut(self):
+
+ def open_window_with_action():
+ el = self.marionette.find_element(By.ID, "updatediv")
+ # Ensure that the element is in the current view port because press() doesn't
+ # handle that inside the action chain (bug 1295538).
+ self.marionette.execute_script('arguments[0].scrollIntoView()', script_args=[el])
+ (self.key_action.key_down(Keys.SHIFT)
+ .press(el)
+ .release()
+ .key_up(Keys.SHIFT)
+ .perform())
+
+ new_window = self.open_window(trigger=open_window_with_action)
+ self.marionette.switch_to_window(new_window)
+ self.marionette.close_chrome_window()
+ self.marionette.switch_to_window(self.start_window)
+ self.assertEqual(self.key_reporter_value, "")
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_localization.py b/testing/marionette/harness/marionette_harness/tests/unit/test_localization.py
new file mode 100644
index 000000000..1b89f6f34
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_localization.py
@@ -0,0 +1,56 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver import By
+from marionette_driver.errors import (
+ InvalidArgumentException,
+ NoSuchElementException,
+ UnknownException
+)
+from marionette_driver.localization import L10n
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestL10n(MarionetteTestCase):
+
+ def setUp(self):
+ super(TestL10n, self).setUp()
+
+ self.l10n = L10n(self.marionette)
+
+ def test_localize_entity(self):
+ dtds = ['chrome://marionette/content/test_dialog.dtd']
+ value = self.l10n.localize_entity(dtds, 'testDialog.title')
+
+ self.assertEqual(value, 'Test Dialog')
+
+ def test_localize_entity_invalid_arguments(self):
+ dtds = ['chrome://marionette/content/test_dialog.dtd']
+
+ self.assertRaises(NoSuchElementException,
+ self.l10n.localize_entity, dtds, 'notExistent')
+ self.assertRaises(InvalidArgumentException,
+ self.l10n.localize_entity, dtds[0], 'notExistent')
+ self.assertRaises(InvalidArgumentException,
+ self.l10n.localize_entity, dtds, True)
+
+ def test_localize_property(self):
+ properties = ['chrome://marionette/content/test_dialog.properties']
+
+ value = self.l10n.localize_property(properties, 'testDialog.title')
+ self.assertEqual(value, 'Test Dialog')
+
+ self.assertRaises(NoSuchElementException,
+ self.l10n.localize_property, properties, 'notExistent')
+
+ def test_localize_property_invalid_arguments(self):
+ properties = ['chrome://global/locale/filepicker.properties']
+
+ self.assertRaises(NoSuchElementException,
+ self.l10n.localize_property, properties, 'notExistent')
+ self.assertRaises(InvalidArgumentException,
+ self.l10n.localize_property, properties[0], 'notExistent')
+ self.assertRaises(InvalidArgumentException,
+ self.l10n.localize_property, properties, True)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_log.py b/testing/marionette/harness/marionette_harness/tests/unit/test_log.py
new file mode 100644
index 000000000..dd3cf82b4
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_log.py
@@ -0,0 +1,64 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestLog(MarionetteTestCase):
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ # clears log cache
+ self.marionette.get_logs()
+
+ def test_log(self):
+ self.marionette.log("foo")
+ logs = self.marionette.get_logs()
+ self.assertEqual("INFO", logs[0][0])
+ self.assertEqual("foo", logs[0][1])
+
+ def test_log_level(self):
+ self.marionette.log("foo", "ERROR")
+ logs = self.marionette.get_logs()
+ self.assertEqual("ERROR", logs[0][0])
+ self.assertEqual("foo", logs[0][1])
+
+ def test_clear(self):
+ self.marionette.log("foo")
+ self.assertEqual(1, len(self.marionette.get_logs()))
+ self.assertEqual(0, len(self.marionette.get_logs()))
+
+ def test_multiple_entries(self):
+ self.marionette.log("foo")
+ self.marionette.log("bar")
+ self.assertEqual(2, len(self.marionette.get_logs()))
+
+ def test_log_from_sync_script(self):
+ self.marionette.execute_script("log('foo')")
+ logs = self.marionette.get_logs()
+ self.assertEqual("INFO", logs[0][0])
+ self.assertEqual("foo", logs[0][1])
+
+ def test_log_from_sync_script_level(self):
+ self.marionette.execute_script("log('foo', 'ERROR')")
+ logs = self.marionette.get_logs()
+ self.assertEqual("ERROR", logs[0][0])
+ self.assertEqual("foo", logs[0][1])
+
+ def test_log_from_async_script(self):
+ self.marionette.execute_async_script("log('foo'); arguments[0]();")
+ logs = self.marionette.get_logs()
+ self.assertEqual("INFO", logs[0][0])
+ self.assertEqual("foo", logs[0][1])
+
+ def test_log_from_async_script_variable_arguments(self):
+ self.marionette.execute_async_script("log('foo', 'ERROR'); arguments[0]();")
+ logs = self.marionette.get_logs()
+ self.assertEqual("ERROR", logs[0][0])
+ self.assertEqual("foo", logs[0][1])
+
+
+class TestLogChrome(TestLog):
+ def setUp(self):
+ TestLog.setUp(self)
+ self.marionette.set_context("chrome")
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_marionette.py b/testing/marionette/harness/marionette_harness/tests/unit/test_marionette.py
new file mode 100644
index 000000000..e68312872
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_marionette.py
@@ -0,0 +1,67 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import itertools
+import time
+
+from marionette_driver import errors
+
+from marionette_harness import MarionetteTestCase, run_if_manage_instance, skip_if_mobile
+
+
+class TestMarionette(MarionetteTestCase):
+
+ def test_correct_test_name(self):
+ """Test that the correct test name gets set."""
+ expected_test_name = '{module}.py {cls}.{func}'.format(
+ module=__name__,
+ cls=self.__class__.__name__,
+ func=self.test_correct_test_name.__name__,
+ )
+
+ self.assertEqual(self.marionette.test_name, expected_test_name)
+
+ @run_if_manage_instance("Only runnable if Marionette manages the instance")
+ @skip_if_mobile("Bug 1322993 - Missing temporary folder")
+ def test_wait_for_port_non_existing_process(self):
+ """Test that wait_for_port doesn't run into a timeout if instance is not running."""
+ self.marionette.quit()
+ self.assertIsNotNone(self.marionette.instance.runner.returncode)
+ start_time = time.time()
+ self.assertFalse(self.marionette.wait_for_port(timeout=5))
+ self.assertLess(time.time() - start_time, 5)
+
+
+class TestProtocol2Errors(MarionetteTestCase):
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ self.op = self.marionette.protocol
+ self.marionette.protocol = 2
+
+ def tearDown(self):
+ self.marionette.protocol = self.op
+ MarionetteTestCase.tearDown(self)
+
+ def test_malformed_packet(self):
+ req = ["error", "message", "stacktrace"]
+ ps = []
+ for p in [p for i in range(0, len(req) + 1) for p in itertools.permutations(req, i)]:
+ ps.append(dict((x, None) for x in p))
+
+ for p in filter(lambda p: len(p) < 3, ps):
+ self.assertRaises(KeyError, self.marionette._handle_error, p)
+
+ def test_known_error_status(self):
+ with self.assertRaises(errors.NoSuchElementException):
+ self.marionette._handle_error(
+ {"error": errors.NoSuchElementException.status,
+ "message": None,
+ "stacktrace": None})
+
+ def test_unknown_error_status(self):
+ with self.assertRaises(errors.MarionetteException):
+ self.marionette._handle_error(
+ {"error": "barbera",
+ "message": None,
+ "stacktrace": None})
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_modal_dialogs.py b/testing/marionette/harness/marionette_harness/tests/unit/test_modal_dialogs.py
new file mode 100644
index 000000000..f7108bdff
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_modal_dialogs.py
@@ -0,0 +1,198 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+from marionette_driver.errors import NoAlertPresentException, ElementNotInteractableException
+from marionette_driver.marionette import Alert
+from marionette_driver.wait import Wait
+
+from marionette_harness import MarionetteTestCase, skip_if_e10s
+
+
+class TestTabModals(MarionetteTestCase):
+
+ def setUp(self):
+ super(TestTabModals, self).setUp()
+ self.marionette.set_pref("prompts.tab_modal.enabled", True)
+ self.marionette.navigate(self.marionette.absolute_url('modal_dialogs.html'))
+
+ def tearDown(self):
+ # Ensure an alert is absent before proceeding past this test.
+ Wait(self.marionette).until(lambda _: not self.alert_present())
+ self.marionette.execute_script("window.onbeforeunload = null;")
+ self.marionette.clear_pref("prompts.tab_modal.enabled")
+ super(TestTabModals, self).tearDown()
+
+ def alert_present(self):
+ try:
+ Alert(self.marionette).text
+ return True
+ except NoAlertPresentException:
+ return False
+
+ def wait_for_alert(self):
+ Wait(self.marionette).until(lambda _: self.alert_present())
+
+ def test_no_alert_raises(self):
+ self.assertRaises(NoAlertPresentException, Alert(self.marionette).accept)
+ self.assertRaises(NoAlertPresentException, Alert(self.marionette).dismiss)
+
+ def test_alert_accept(self):
+ self.marionette.find_element(By.ID, 'modal-alert').click()
+ self.wait_for_alert()
+ alert = self.marionette.switch_to_alert()
+ alert.accept()
+
+ def test_alert_dismiss(self):
+ self.marionette.find_element(By.ID, 'modal-alert').click()
+ self.wait_for_alert()
+ alert = self.marionette.switch_to_alert()
+ alert.dismiss()
+
+ def test_confirm_accept(self):
+ self.marionette.find_element(By.ID, 'modal-confirm').click()
+ self.wait_for_alert()
+ alert = self.marionette.switch_to_alert()
+ alert.accept()
+ self.wait_for_condition(lambda mn: mn.find_element(By.ID, 'confirm-result').text == 'true')
+
+ def test_confirm_dismiss(self):
+ self.marionette.find_element(By.ID, 'modal-confirm').click()
+ self.wait_for_alert()
+ alert = self.marionette.switch_to_alert()
+ alert.dismiss()
+ self.wait_for_condition(lambda mn: mn.find_element(By.ID, 'confirm-result').text == 'false')
+
+ def test_prompt_accept(self):
+ self.marionette.find_element(By.ID, 'modal-prompt').click()
+ self.wait_for_alert()
+ alert = self.marionette.switch_to_alert()
+ alert.accept()
+ self.wait_for_condition(lambda mn: mn.find_element(By.ID, 'prompt-result').text == '')
+
+ def test_prompt_dismiss(self):
+ self.marionette.find_element(By.ID, 'modal-prompt').click()
+ self.wait_for_alert()
+ alert = self.marionette.switch_to_alert()
+ alert.dismiss()
+ self.wait_for_condition(lambda mn: mn.find_element(By.ID, 'prompt-result').text == 'null')
+
+ def test_alert_text(self):
+ with self.assertRaises(NoAlertPresentException):
+ alert = self.marionette.switch_to_alert()
+ alert.text
+ self.marionette.find_element(By.ID, 'modal-alert').click()
+ self.wait_for_alert()
+ alert = self.marionette.switch_to_alert()
+ self.assertEqual(alert.text, 'Marionette alert')
+ alert.accept()
+
+ def test_prompt_text(self):
+ with self.assertRaises(NoAlertPresentException):
+ alert = self.marionette.switch_to_alert()
+ alert.text
+ self.marionette.find_element(By.ID, 'modal-prompt').click()
+ self.wait_for_alert()
+ alert = self.marionette.switch_to_alert()
+ self.assertEqual(alert.text, 'Marionette prompt')
+ alert.accept()
+
+ def test_confirm_text(self):
+ with self.assertRaises(NoAlertPresentException):
+ alert = self.marionette.switch_to_alert()
+ alert.text
+ self.marionette.find_element(By.ID, 'modal-confirm').click()
+ self.wait_for_alert()
+ alert = self.marionette.switch_to_alert()
+ self.assertEqual(alert.text, 'Marionette confirm')
+ alert.accept()
+
+ def test_set_text_throws(self):
+ self.assertRaises(NoAlertPresentException, Alert(self.marionette).send_keys, "Foo")
+ self.marionette.find_element(By.ID, 'modal-alert').click()
+ self.wait_for_alert()
+ alert = self.marionette.switch_to_alert()
+ self.assertRaises(ElementNotInteractableException, alert.send_keys, "Foo")
+ alert.accept()
+
+ def test_set_text_accept(self):
+ self.marionette.find_element(By.ID, 'modal-prompt').click()
+ self.wait_for_alert()
+ alert = self.marionette.switch_to_alert()
+ alert.send_keys("Some text!");
+ alert.accept()
+ self.wait_for_condition(lambda mn: mn.find_element(By.ID, 'prompt-result').text == 'Some text!')
+
+ def test_set_text_dismiss(self):
+ self.marionette.find_element(By.ID, 'modal-prompt').click()
+ self.wait_for_alert()
+ alert = self.marionette.switch_to_alert()
+ alert.send_keys("Some text!");
+ alert.dismiss()
+ self.wait_for_condition(lambda mn: mn.find_element(By.ID, 'prompt-result').text == 'null')
+
+ def test_onbeforeunload_dismiss(self):
+ start_url = self.marionette.get_url()
+ self.marionette.find_element(By.ID, 'onbeforeunload-handler').click()
+ self.wait_for_condition(
+ lambda mn: mn.execute_script("""
+ return window.onbeforeunload !== null;
+ """))
+ self.marionette.navigate("about:blank")
+ self.wait_for_alert()
+ alert = self.marionette.switch_to_alert()
+ self.assertTrue(alert.text.startswith("This page is asking you to confirm"))
+ alert.dismiss()
+ self.assertTrue(self.marionette.get_url().startswith(start_url))
+
+ def test_onbeforeunload_accept(self):
+ self.marionette.find_element(By.ID, 'onbeforeunload-handler').click()
+ self.wait_for_condition(
+ lambda mn: mn.execute_script("""
+ return window.onbeforeunload !== null;
+ """))
+ self.marionette.navigate("about:blank")
+ self.wait_for_alert()
+ alert = self.marionette.switch_to_alert()
+ self.assertTrue(alert.text.startswith("This page is asking you to confirm"))
+ alert.accept()
+ self.wait_for_condition(lambda mn: mn.get_url() == "about:blank")
+
+ @skip_if_e10s("Bug 1325044")
+ def test_unrelated_command_when_alert_present(self):
+ click_handler = self.marionette.find_element(By.ID, 'click-handler')
+ text = self.marionette.find_element(By.ID, 'click-result').text
+ self.assertEqual(text, '')
+
+ self.marionette.find_element(By.ID, 'modal-alert').click()
+ self.wait_for_alert()
+
+ # Commands succeed, but because the dialog blocks the event loop,
+ # our actions aren't reflected on the page.
+ text = self.marionette.find_element(By.ID, 'click-result').text
+ self.assertEqual(text, '')
+ click_handler.click()
+ text = self.marionette.find_element(By.ID, 'click-result').text
+ self.assertEqual(text, '')
+
+ alert = self.marionette.switch_to_alert()
+ alert.accept()
+
+ Wait(self.marionette).until(lambda _: not self.alert_present())
+
+ click_handler.click()
+ text = self.marionette.find_element(By.ID, 'click-result').text
+ self.assertEqual(text, 'result')
+
+
+class TestGlobalModals(TestTabModals):
+
+ def setUp(self):
+ super(TestGlobalModals, self).setUp()
+ self.marionette.set_pref("prompts.tab_modal.enabled", False)
+
+ def test_unrelated_command_when_alert_present(self):
+ # The assumptions in this test do not hold on certain platforms, and not when
+ # e10s is enabled.
+ pass
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_mouse_action.py b/testing/marionette/harness/marionette_harness/tests/unit/test_mouse_action.py
new file mode 100644
index 000000000..246068215
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_mouse_action.py
@@ -0,0 +1,114 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+from marionette_driver.keys import Keys
+from marionette_driver.marionette import Actions
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestMouseAction(MarionetteTestCase):
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ if self.marionette.session_capabilities["platformName"] == "darwin":
+ self.mod_key = Keys.META
+ else:
+ self.mod_key = Keys.CONTROL
+ self.action = Actions(self.marionette)
+
+ def test_click_action(self):
+ test_html = self.marionette.absolute_url("test.html")
+ self.marionette.navigate(test_html)
+ link = self.marionette.find_element(By.ID, "mozLink")
+ self.action.click(link).perform()
+ self.assertEqual("Clicked", self.marionette.execute_script(
+ "return document.getElementById('mozLink').innerHTML"))
+
+ def test_clicking_element_out_of_view_succeeds(self):
+ # The action based click doesn"t check for visibility.
+ test_html = self.marionette.absolute_url("hidden.html")
+ self.marionette.navigate(test_html)
+ el = self.marionette.find_element(By.ID, "child")
+ self.action.click(el).perform()
+
+ def test_double_click_action(self):
+ test_html = self.marionette.absolute_url("double_click.html")
+ self.marionette.navigate(test_html)
+ el = self.marionette.find_element(By.ID, "one-word-div")
+ self.action.double_click(el).perform()
+ el.send_keys(self.mod_key + "c")
+ rel = self.marionette.find_element(By.ID, "input-field")
+ rel.send_keys(self.mod_key + "v")
+ self.assertEqual("zyxw", rel.get_property("value"))
+
+ def test_context_click_action(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ click_el = self.marionette.find_element(By.ID, "resultContainer")
+
+ def context_menu_state():
+ with self.marionette.using_context("chrome"):
+ cm_el = self.marionette.find_element(By.ID, "contentAreaContextMenu")
+ return cm_el.get_property("state")
+
+ self.assertEqual("closed", context_menu_state())
+ self.action.context_click(click_el).perform()
+ self.wait_for_condition(lambda _: context_menu_state() == "open")
+
+ with self.marionette.using_context("chrome"):
+ self.marionette.find_element(By.ID, "main-window").send_keys(Keys.ESCAPE)
+ self.wait_for_condition(lambda _: context_menu_state() == "closed")
+
+ def test_middle_click_action(self):
+ test_html = self.marionette.absolute_url("clicks.html")
+ self.marionette.navigate(test_html)
+
+ self.marionette.find_element(By.ID, "addbuttonlistener").click()
+
+ el = self.marionette.find_element(By.ID, "showbutton")
+ self.action.middle_click(el).perform()
+
+ self.wait_for_condition(lambda _: el.get_property("innerHTML") == "1")
+
+ def test_chrome_click(self):
+ self.marionette.navigate("about:blank")
+ data_uri = "data:text/html,<html></html>"
+ with self.marionette.using_context("chrome"):
+ urlbar = self.marionette.find_element(By.ID, "urlbar")
+ urlbar.send_keys(data_uri)
+ go_button = self.marionette.find_element(By.ID, "urlbar-go-button")
+ self.action.click(go_button).perform()
+ self.wait_for_condition(lambda mn: mn.get_url() == data_uri)
+
+ def test_chrome_double_click(self):
+ self.marionette.navigate("about:blank")
+ test_word = "quux"
+
+ with self.marionette.using_context("chrome"):
+ urlbar = self.marionette.find_element(By.ID, "urlbar")
+ self.assertEqual("", urlbar.get_property("value"))
+
+ urlbar.send_keys(test_word)
+ self.assertEqual(urlbar.get_property("value"), test_word)
+ (self.action.double_click(urlbar).perform()
+ .key_down(self.mod_key)
+ .key_down("x").perform())
+ self.assertEqual(urlbar.get_property("value"), "")
+
+ def test_chrome_context_click_action(self):
+ self.marionette.set_context("chrome")
+ def context_menu_state():
+ cm_el = self.marionette.find_element(By.ID, "tabContextMenu")
+ return cm_el.get_property("state")
+
+ currtab = self.marionette.execute_script("return gBrowser.selectedTab")
+ self.assertEqual("closed", context_menu_state())
+ self.action.context_click(currtab).perform()
+ self.wait_for_condition(lambda _: context_menu_state() == "open")
+
+ (self.marionette.find_element(By.ID, "main-window")
+ .send_keys(Keys.ESCAPE))
+
+ self.wait_for_condition(lambda _: context_menu_state() == "closed")
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py b/testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py
new file mode 100644
index 000000000..75ed37ecd
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py
@@ -0,0 +1,447 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import contextlib
+import time
+import urllib
+
+from marionette_driver import By, errors, expected, Wait
+from marionette_harness import (
+ MarionetteTestCase,
+ run_if_e10s,
+ run_if_manage_instance,
+ skip,
+ skip_if_mobile,
+ WindowManagerMixin,
+)
+
+
+def inline(doc):
+ return "data:text/html;charset=utf-8,%s" % urllib.quote(doc)
+
+
+class TestBackForwardNavigation(WindowManagerMixin, MarionetteTestCase):
+
+ def setUp(self):
+ super(TestBackForwardNavigation, self).setUp()
+
+ self.test_page = self.marionette.absolute_url('test.html')
+
+ def open_with_link():
+ link = self.marionette.find_element(By.ID, "new-blank-tab")
+ link.click()
+
+ # Always use a blank new tab for an empty history
+ self.marionette.navigate(self.marionette.absolute_url("windowHandles.html"))
+ self.new_tab = self.open_tab(open_with_link)
+ self.marionette.switch_to_window(self.new_tab)
+ Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
+ lambda _: self.history_length == 1,
+ message="The newly opened tab doesn't have a browser history length of 1")
+
+ def tearDown(self):
+ self.marionette.switch_to_parent_frame()
+ self.close_all_tabs()
+
+ super(TestBackForwardNavigation, self).tearDown()
+
+ @property
+ def history_length(self):
+ return self.marionette.execute_script("return window.history.length;")
+
+ def run_test(self, test_pages):
+ # Helper method to run simple back and forward testcases.
+ for index, page in enumerate(test_pages):
+ if "error" in page:
+ with self.assertRaises(page["error"]):
+ self.marionette.navigate(page["url"])
+ else:
+ self.marionette.navigate(page["url"])
+ self.assertEqual(page["url"], self.marionette.get_url())
+ self.assertEqual(self.history_length, index + 1)
+
+ for page in test_pages[-2::-1]:
+ if "error" in page:
+ with self.assertRaises(page["error"]):
+ self.marionette.go_back()
+ else:
+ self.marionette.go_back()
+ self.assertEqual(page["url"], self.marionette.get_url())
+
+ for page in test_pages[1::]:
+ if "error" in page:
+ with self.assertRaises(page["error"]):
+ self.marionette.go_forward()
+ else:
+ self.marionette.go_forward()
+ self.assertEqual(page["url"], self.marionette.get_url())
+
+ def test_no_history_items(self):
+ # Both methods should not raise a failure if no navigation is possible
+ self.marionette.go_back()
+ self.marionette.go_forward()
+
+ def test_data_urls(self):
+ test_pages = [
+ {"url": inline("<p>foobar</p>")},
+ {"url": self.test_page},
+ {"url": inline("<p>foobar</p>")},
+ ]
+ self.run_test(test_pages)
+
+ def test_same_document_hash_change(self):
+ test_pages = [
+ {"url": "{}#23".format(self.test_page)},
+ {"url": self.test_page},
+ {"url": "{}#42".format(self.test_page)},
+ ]
+ self.run_test(test_pages)
+
+ @skip("Causes crashes for JS GC (bug 1344863) and a11y (bug 1344868)")
+ def test_frameset(self):
+ test_pages = [
+ {"url": self.marionette.absolute_url("frameset.html")},
+ {"url": self.test_page},
+ {"url": self.marionette.absolute_url("frameset.html")},
+ ]
+ self.run_test(test_pages)
+
+ def test_frameset_after_navigating_in_frame(self):
+ test_element_locator = (By.ID, "email")
+
+ self.marionette.navigate(self.test_page)
+ self.assertEqual(self.marionette.get_url(), self.test_page)
+ self.assertEqual(self.history_length, 1)
+ page = self.marionette.absolute_url("frameset.html")
+ self.marionette.navigate(page)
+ self.assertEqual(self.marionette.get_url(), page)
+ self.assertEqual(self.history_length, 2)
+ frame = self.marionette.find_element(By.ID, "fifth")
+ self.marionette.switch_to_frame(frame)
+ link = self.marionette.find_element(By.ID, "linkId")
+ link.click()
+
+ # We cannot use get_url() to wait until the target page has been loaded,
+ # because it will return the URL of the top browsing context and doesn't
+ # wait for the page load to be complete.
+ Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
+ expected.element_present(*test_element_locator),
+ message="Target element 'email' has not been found")
+ self.assertEqual(self.history_length, 3)
+
+ # Go back to the frame the click navigated away from
+ self.marionette.go_back()
+ self.assertEqual(self.marionette.get_url(), page)
+ with self.assertRaises(errors.NoSuchElementException):
+ self.marionette.find_element(*test_element_locator)
+
+ # Go back to the non-frameset page
+ self.marionette.switch_to_parent_frame()
+ self.marionette.go_back()
+ self.assertEqual(self.marionette.get_url(), self.test_page)
+
+ # Go forward to the frameset page
+ self.marionette.go_forward()
+ self.assertEqual(self.marionette.get_url(), page)
+
+ # Go forward to the frame the click navigated to
+ # TODO: See above for automatic browser context switches. Hard to do here
+ frame = self.marionette.find_element(By.ID, "fifth")
+ self.marionette.switch_to_frame(frame)
+ self.marionette.go_forward()
+ self.marionette.find_element(*test_element_locator)
+ self.assertEqual(self.marionette.get_url(), page)
+
+ def test_image_document_to_html(self):
+ test_pages = [
+ {"url": self.marionette.absolute_url('black.png')},
+ {"url": self.test_page},
+ {"url": self.marionette.absolute_url('white.png')},
+ ]
+ self.run_test(test_pages)
+
+ def test_image_document_to_image_document(self):
+ test_pages = [
+ {"url": self.marionette.absolute_url('black.png')},
+ {"url": self.marionette.absolute_url('white.png')},
+ ]
+ self.run_test(test_pages)
+
+ @run_if_e10s("Requires e10s mode enabled")
+ def test_remoteness_change(self):
+ # TODO: Verify that a remoteness change happened
+ # like: self.assertNotEqual(self.marionette.current_window_handle, self.new_tab)
+
+ # about:robots is always a non-remote page for now
+ test_pages = [
+ {"url": "about:robots"},
+ {"url": self.test_page},
+ {"url": "about:robots"},
+ ]
+ self.run_test(test_pages)
+
+ def test_navigate_to_requested_about_page_after_error_page(self):
+ test_pages = [
+ {"url": "about:neterror"},
+ {"url": self.marionette.absolute_url("test.html")},
+ {"url": "about:blocked"},
+ ]
+ self.run_test(test_pages)
+
+ def test_timeout_error(self):
+ # Bug 1354908 - Disabled on Windows XP due to intermittent failures
+ caps = self.marionette.session_capabilities
+ if caps["platformName"] == "windows_nt" and float(caps["platformVersion"]) < 6:
+ return
+
+ urls = [
+ self.marionette.absolute_url('slow'),
+ self.test_page,
+ self.marionette.absolute_url('slow'),
+ ]
+
+ # First, load all pages completely to get them added to the cache
+ for index, url in enumerate(urls):
+ self.marionette.navigate(url)
+ self.assertEqual(url, self.marionette.get_url())
+ self.assertEqual(self.history_length, index + 1)
+
+ self.marionette.go_back()
+ self.assertEqual(urls[1], self.marionette.get_url())
+
+ # Force triggering a timeout error
+ self.marionette.timeout.page_load = 0.1
+ with self.assertRaises(errors.TimeoutException):
+ self.marionette.go_back()
+ self.assertEqual(urls[0], self.marionette.get_url())
+ self.marionette.timeout.page_load = 300000
+
+ self.marionette.go_forward()
+ self.assertEqual(urls[1], self.marionette.get_url())
+
+ # Force triggering a timeout error
+ self.marionette.timeout.page_load = 0.1
+ with self.assertRaises(errors.TimeoutException):
+ self.marionette.go_forward()
+ self.assertEqual(urls[2], self.marionette.get_url())
+ self.marionette.timeout.page_load = 300000
+
+ def test_certificate_error(self):
+ test_pages = [
+ {"url": self.fixtures.where_is("/test.html", on="https"),
+ "error": errors.InsecureCertificateException},
+ {"url": self.test_page},
+ {"url": self.fixtures.where_is("/test.html", on="https"),
+ "error": errors.InsecureCertificateException},
+ ]
+ self.run_test(test_pages)
+
+
+class TestNavigate(WindowManagerMixin, MarionetteTestCase):
+
+ def setUp(self):
+ super(TestNavigate, self).setUp()
+
+ self.marionette.navigate("about:")
+ self.test_doc = self.marionette.absolute_url("test.html")
+ self.iframe_doc = self.marionette.absolute_url("test_iframe.html")
+
+ def tearDown(self):
+ self.marionette.timeout.reset()
+ self.close_all_tabs()
+
+ super(TestNavigate, self).tearDown()
+
+ @property
+ def location_href(self):
+ # Windows 8 has recently seen a proliferation of intermittent
+ # test failures to do with failing to compare "about:blank" ==
+ # u"about:blank". For the sake of consistenty, we encode the
+ # returned URL as Unicode here to ensure that the values are
+ # absolutely of the same type.
+ #
+ # (https://bugzilla.mozilla.org/show_bug.cgi?id=1322862)
+ return self.marionette.execute_script("return window.location.href").encode("utf-8")
+
+ def test_set_location_through_execute_script(self):
+ self.marionette.execute_script(
+ "window.location.href = '%s'" % self.test_doc)
+ Wait(self.marionette).until(
+ lambda _: self.test_doc == self.location_href)
+ self.assertEqual("Marionette Test", self.marionette.title)
+
+ def test_navigate_chrome_error(self):
+ with self.marionette.using_context("chrome"):
+ self.assertRaises(errors.UnsupportedOperationException,
+ self.marionette.navigate, "about:blank")
+ self.assertRaises(errors.UnsupportedOperationException, self.marionette.go_back)
+ self.assertRaises(errors.UnsupportedOperationException, self.marionette.go_forward)
+ self.assertRaises(errors.UnsupportedOperationException, self.marionette.refresh)
+
+ def test_get_current_url_returns_top_level_browsing_context_url(self):
+ self.marionette.navigate(self.iframe_doc)
+ self.assertEqual(self.iframe_doc, self.location_href)
+ frame = self.marionette.find_element(By.CSS_SELECTOR, "#test_iframe")
+ self.marionette.switch_to_frame(frame)
+ self.assertEqual(self.iframe_doc, self.marionette.get_url())
+
+ def test_get_current_url(self):
+ self.marionette.navigate(self.test_doc)
+ self.assertEqual(self.test_doc, self.marionette.get_url())
+ self.marionette.navigate("about:blank")
+ self.assertEqual("about:blank", self.marionette.get_url())
+
+ def test_refresh(self):
+ self.marionette.navigate(self.test_doc)
+ self.assertEqual("Marionette Test", self.marionette.title)
+ self.assertTrue(self.marionette.execute_script(
+ """var elem = window.document.createElement('div'); elem.id = 'someDiv';
+ window.document.body.appendChild(elem); return true;"""))
+ self.assertFalse(self.marionette.execute_script(
+ "return window.document.getElementById('someDiv') == undefined"))
+ self.marionette.refresh()
+ # TODO(ato): Bug 1291320
+ time.sleep(0.2)
+ self.assertEqual("Marionette Test", self.marionette.title)
+ self.assertTrue(self.marionette.execute_script(
+ "return window.document.getElementById('someDiv') == undefined"))
+
+ def test_navigate_in_child_frame_changes_to_top(self):
+ frame_html = self.marionette.absolute_url("frameset.html")
+
+ self.marionette.navigate(frame_html)
+ frame = self.marionette.find_element(By.NAME, "third")
+ self.marionette.switch_to_frame(frame)
+ self.assertRaises(errors.NoSuchElementException,
+ self.marionette.find_element, By.NAME, "third")
+
+ self.marionette.navigate(frame_html)
+ self.marionette.find_element(By.NAME, "third")
+
+ @skip_if_mobile("Bug 1323755 - Socket timeout")
+ def test_invalid_protocol(self):
+ with self.assertRaises(errors.MarionetteException):
+ self.marionette.navigate("thisprotocoldoesnotexist://")
+
+ def test_find_element_state_complete(self):
+ self.marionette.navigate(self.test_doc)
+ state = self.marionette.execute_script(
+ "return window.document.readyState")
+ self.assertEqual("complete", state)
+ self.assertTrue(self.marionette.find_element(By.ID, "mozLink"))
+
+ def test_error_when_exceeding_page_load_timeout(self):
+ self.marionette.timeout.page_load = 0.1
+ with self.assertRaises(errors.TimeoutException):
+ self.marionette.navigate(self.marionette.absolute_url("slow"))
+
+ def test_navigate_to_same_image_document_twice(self):
+ self.marionette.navigate(self.fixtures.where_is("black.png"))
+ self.assertIn("black.png", self.marionette.title)
+ self.marionette.navigate(self.fixtures.where_is("black.png"))
+ self.assertIn("black.png", self.marionette.title)
+
+ def test_navigate_hash_change(self):
+ doc = inline("<p id=foo>")
+ self.marionette.navigate(doc)
+ self.marionette.execute_script("window.visited = true", sandbox=None)
+ self.marionette.navigate("{}#foo".format(doc))
+ self.assertTrue(self.marionette.execute_script(
+ "return window.visited", sandbox=None))
+
+ @skip_if_mobile("Bug 1334095 - Timeout: No new tab has been opened")
+ def test_about_blank_for_new_docshell(self):
+ """ Bug 1312674 - Hang when loading about:blank for a new docshell."""
+ def open_with_link():
+ link = self.marionette.find_element(By.ID, "new-blank-tab")
+ link.click()
+
+ # Open a new tab to get a new docshell created
+ self.marionette.navigate(self.marionette.absolute_url("windowHandles.html"))
+ new_tab = self.open_tab(trigger=open_with_link)
+ self.marionette.switch_to_window(new_tab)
+ self.assertEqual(self.marionette.get_url(), "about:blank")
+
+ self.marionette.navigate('about:blank')
+ self.marionette.close()
+ self.marionette.switch_to_window(self.start_window)
+
+ @skip("Bug 1332064 - NoSuchElementException: Unable to locate element: :focus")
+ @run_if_manage_instance("Only runnable if Marionette manages the instance")
+ @skip_if_mobile("Bug 1322993 - Missing temporary folder")
+ def test_focus_after_navigation(self):
+ self.marionette.quit()
+ self.marionette.start_session()
+
+ self.marionette.navigate(inline("<input autofocus>"))
+ active_el = self.marionette.execute_script("return document.activeElement")
+ focus_el = self.marionette.find_element(By.CSS_SELECTOR, ":focus")
+ self.assertEqual(active_el, focus_el)
+
+
+class TestTLSNavigation(MarionetteTestCase):
+ insecure_tls = {"acceptInsecureCerts": True}
+ secure_tls = {"acceptInsecureCerts": False}
+
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ self.marionette.delete_session()
+ self.capabilities = self.marionette.start_session(
+ {"requiredCapabilities": self.insecure_tls})
+
+ def tearDown(self):
+ try:
+ self.marionette.delete_session()
+ except:
+ pass
+ MarionetteTestCase.tearDown(self)
+
+ @contextlib.contextmanager
+ def safe_session(self):
+ try:
+ self.capabilities = self.marionette.start_session(
+ {"requiredCapabilities": self.secure_tls})
+ self.assertFalse(self.capabilities["acceptInsecureCerts"])
+ yield self.marionette
+ finally:
+ self.marionette.delete_session()
+
+ @contextlib.contextmanager
+ def unsafe_session(self):
+ try:
+ self.capabilities = self.marionette.start_session(
+ {"requiredCapabilities": self.insecure_tls})
+ self.assertTrue(self.capabilities["acceptInsecureCerts"])
+ yield self.marionette
+ finally:
+ self.marionette.delete_session()
+
+ def test_navigate_by_command(self):
+ self.marionette.navigate(
+ self.fixtures.where_is("/test.html", on="https"))
+ self.assertIn("https", self.marionette.get_url())
+
+ def test_navigate_by_click(self):
+ link_url = self.fixtures.where_is("/test.html", on="https")
+ self.marionette.navigate(
+ inline("<a href=%s>https is the future</a>" % link_url))
+ self.marionette.find_element(By.TAG_NAME, "a").click()
+ self.assertIn("https", self.marionette.get_url())
+
+ def test_deactivation(self):
+ invalid_cert_url = self.fixtures.where_is("/test.html", on="https")
+
+ print "with safe session"
+ with self.safe_session() as session:
+ with self.assertRaises(errors.InsecureCertificateException):
+ session.navigate(invalid_cert_url)
+
+ print "with unsafe session"
+ with self.unsafe_session() as session:
+ session.navigate(invalid_cert_url)
+
+ print "with safe session again"
+ with self.safe_session() as session:
+ with self.assertRaises(errors.InsecureCertificateException):
+ session.navigate(invalid_cert_url)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_pagesource.py b/testing/marionette/harness/marionette_harness/tests/unit/test_pagesource.py
new file mode 100644
index 000000000..c88666986
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_pagesource.py
@@ -0,0 +1,33 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestPageSource(MarionetteTestCase):
+ def testShouldReturnTheSourceOfAPage(self):
+ test_html = self.marionette.absolute_url("testPageSource.html")
+ self.marionette.navigate(test_html)
+ source = self.marionette.page_source
+ from_web_api = self.marionette.execute_script("return document.documentElement.outerHTML")
+ self.assertTrue("<html" in source)
+ self.assertTrue("PageSource" in source)
+ self.assertEqual(source, from_web_api)
+
+ def testShouldReturnTheSourceOfAPageWhenThereAreUnicodeChars(self):
+ test_html = self.marionette.absolute_url("testPageSourceWithUnicodeChars.html")
+ self.marionette.navigate(test_html)
+ # if we don't throw on the next line we are good!
+ source = self.marionette.page_source
+ from_web_api = self.marionette.execute_script("return document.documentElement.outerHTML")
+ self.assertEqual(source, from_web_api)
+
+ def testShouldReturnAXMLDocumentSource(self):
+ test_xml = self.marionette.absolute_url("testPageSource.xml")
+ self.marionette.navigate(test_xml)
+ source = self.marionette.page_source
+ from_web_api = self.marionette.execute_script("return document.documentElement.outerHTML")
+ import re
+ self.assertEqual(re.sub("\s", "", source), "<xml><foo><bar>baz</bar></foo></xml>")
+ self.assertEqual(source, from_web_api)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_pagesource_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_pagesource_chrome.py
new file mode 100644
index 000000000..5f60e6010
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_pagesource_chrome.py
@@ -0,0 +1,29 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_harness import MarionetteTestCase, WindowManagerMixin
+
+
+class TestPageSourceChrome(WindowManagerMixin, MarionetteTestCase):
+
+ def setUp(self):
+ super(TestPageSourceChrome, self).setUp()
+ self.marionette.set_context("chrome")
+
+ def open_with_js():
+ self.marionette.execute_script("""
+ window.open('chrome://marionette/content/test.xul',
+ 'foo', 'chrome,centerscreen');
+ """)
+
+ new_window = self.open_window(open_with_js)
+ self.marionette.switch_to_window(new_window)
+
+ def tearDown(self):
+ self.close_all_windows()
+ super(TestPageSourceChrome, self).tearDown()
+
+ def testShouldReturnXULDetails(self):
+ source = self.marionette.page_source
+ self.assertTrue('<textbox id="textInput"' in source)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_position.py b/testing/marionette/harness/marionette_harness/tests/unit/test_position.py
new file mode 100644
index 000000000..2cc4d5947
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_position.py
@@ -0,0 +1,19 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestPosition(MarionetteTestCase):
+
+ def test_should_get_element_position_back(self):
+ test_url = self.marionette.absolute_url('rectangles.html')
+ self.marionette.navigate(test_url)
+
+ r2 = self.marionette.find_element(By.ID, "r2")
+ location = r2.rect
+ self.assertEqual(11, location['x'])
+ self.assertEqual(10, location['y'])
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_prefs.py b/testing/marionette/harness/marionette_harness/tests/unit/test_prefs.py
new file mode 100644
index 000000000..9cfbe1df1
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_prefs.py
@@ -0,0 +1,167 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.errors import JavascriptException
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestPreferences(MarionetteTestCase):
+ prefs = {
+ "bool": "marionette.test.bool",
+ "int": "marionette.test.int",
+ "string": "marionette.test.string",
+ }
+
+ def tearDown(self):
+ for pref in self.prefs.values():
+ self.marionette.clear_pref(pref)
+
+ super(TestPreferences, self).tearDown()
+
+ def test_clear_pref(self):
+ self.assertIsNone(self.marionette.get_pref(self.prefs["bool"]))
+
+ self.marionette.set_pref(self.prefs["bool"], True)
+ self.assertTrue(self.marionette.get_pref(self.prefs["bool"]))
+
+ self.marionette.clear_pref(self.prefs["bool"])
+ self.assertIsNone(self.marionette.get_pref(self.prefs["bool"]))
+
+ def test_get_and_set_pref(self):
+ # By default none of the preferences are set
+ self.assertIsNone(self.marionette.get_pref(self.prefs["bool"]))
+ self.assertIsNone(self.marionette.get_pref(self.prefs["int"]))
+ self.assertIsNone(self.marionette.get_pref(self.prefs["string"]))
+
+ # Test boolean values
+ self.marionette.set_pref(self.prefs["bool"], True)
+ value = self.marionette.get_pref(self.prefs["bool"])
+ self.assertTrue(value)
+ self.assertEqual(type(value), bool)
+
+ # Test int values
+ self.marionette.set_pref(self.prefs["int"], 42)
+ value = self.marionette.get_pref(self.prefs["int"])
+ self.assertEqual(value, 42)
+ self.assertEqual(type(value), int)
+
+ # Test string values
+ self.marionette.set_pref(self.prefs["string"], "abc")
+ value = self.marionette.get_pref(self.prefs["string"])
+ self.assertEqual(value, "abc")
+ self.assertTrue(isinstance(value, basestring))
+
+ # Test reset value
+ self.marionette.set_pref(self.prefs["string"], None)
+ self.assertIsNone(self.marionette.get_pref(self.prefs["string"]))
+
+ def test_get_set_pref_default_branch(self):
+ pref_default = "marionette.test.pref_default1"
+ self.assertIsNone(self.marionette.get_pref(self.prefs["string"]))
+
+ self.marionette.set_pref(pref_default, "default_value", default_branch=True)
+ self.assertEqual(self.marionette.get_pref(pref_default), "default_value")
+ self.assertEqual(self.marionette.get_pref(pref_default, default_branch=True),
+ "default_value")
+
+ self.marionette.set_pref(pref_default, "user_value")
+ self.assertEqual(self.marionette.get_pref(pref_default), "user_value")
+ self.assertEqual(self.marionette.get_pref(pref_default, default_branch=True),
+ "default_value")
+
+ self.marionette.clear_pref(pref_default)
+ self.assertEqual(self.marionette.get_pref(pref_default), "default_value")
+
+ def test_get_pref_value_type(self):
+ # Without a given value type the properties URL will be returned only
+ pref_complex = "browser.menu.showCharacterEncoding"
+ properties_file = "chrome://browser/locale/browser.properties"
+ self.assertEqual(self.marionette.get_pref(pref_complex, default_branch=True),
+ properties_file)
+
+ # Otherwise the property named like the pref will be translated
+ value = self.marionette.get_pref(pref_complex, default_branch=True,
+ value_type="nsIPrefLocalizedString")
+ self.assertNotEqual(value, properties_file)
+
+ def test_set_prefs(self):
+ # By default none of the preferences are set
+ self.assertIsNone(self.marionette.get_pref(self.prefs["bool"]))
+ self.assertIsNone(self.marionette.get_pref(self.prefs["int"]))
+ self.assertIsNone(self.marionette.get_pref(self.prefs["string"]))
+
+ # Set a value on the default branch first
+ pref_default = "marionette.test.pref_default2"
+ self.assertIsNone(self.marionette.get_pref(pref_default))
+ self.marionette.set_prefs({pref_default: "default_value"}, default_branch=True)
+
+ # Set user values
+ prefs = {self.prefs["bool"]: True, self.prefs["int"]: 42,
+ self.prefs["string"]: "abc", pref_default: "user_value"}
+ self.marionette.set_prefs(prefs)
+
+ self.assertTrue(self.marionette.get_pref(self.prefs["bool"]))
+ self.assertEqual(self.marionette.get_pref(self.prefs["int"]), 42)
+ self.assertEqual(self.marionette.get_pref(self.prefs["string"]), "abc")
+ self.assertEqual(self.marionette.get_pref(pref_default), "user_value")
+ self.assertEqual(self.marionette.get_pref(pref_default, default_branch=True),
+ "default_value")
+
+ def test_using_prefs(self):
+ # Test that multiple preferences can be set with "using_prefs", and that
+ # they are set correctly and unset correctly after leaving the context
+ # manager.
+ pref_not_existent = "marionette.test.not_existent1"
+ pref_default = "marionette.test.pref_default3"
+
+ self.marionette.set_prefs({self.prefs["string"]: "abc",
+ self.prefs["int"]: 42,
+ self.prefs["bool"]: False,
+ })
+ self.assertFalse(self.marionette.get_pref(self.prefs["bool"]))
+ self.assertEqual(self.marionette.get_pref(self.prefs["int"]), 42)
+ self.assertEqual(self.marionette.get_pref(self.prefs["string"]), "abc")
+ self.assertIsNone(self.marionette.get_pref(pref_not_existent))
+
+ with self.marionette.using_prefs({self.prefs["bool"]: True,
+ self.prefs["int"]: 24,
+ self.prefs["string"]: "def",
+ pref_not_existent: "existent"}):
+
+ self.assertTrue(self.marionette.get_pref(self.prefs["bool"]), True)
+ self.assertEquals(self.marionette.get_pref(self.prefs["int"]), 24)
+ self.assertEquals(self.marionette.get_pref(self.prefs["string"]), "def")
+ self.assertEquals(self.marionette.get_pref(pref_not_existent), "existent")
+
+ self.assertFalse(self.marionette.get_pref(self.prefs["bool"]))
+ self.assertEqual(self.marionette.get_pref(self.prefs["int"]), 42)
+ self.assertEqual(self.marionette.get_pref(self.prefs["string"]), "abc")
+ self.assertIsNone(self.marionette.get_pref(pref_not_existent))
+
+ # Using context with default branch
+ self.marionette.set_pref(pref_default, "default_value", default_branch=True)
+ self.assertEqual(self.marionette.get_pref(pref_default, default_branch=True),
+ "default_value")
+
+ with self.marionette.using_prefs({pref_default: "new_value"}, default_branch=True):
+ self.assertEqual(self.marionette.get_pref(pref_default, default_branch=True),
+ "new_value")
+
+ self.assertEqual(self.marionette.get_pref(pref_default, default_branch=True),
+ "default_value")
+
+ def test_using_prefs_exception(self):
+ # Test that throwing an exception inside the context manager doesn"t
+ # prevent the preferences from being restored at context manager exit.
+ self.marionette.set_pref(self.prefs["string"], "abc")
+
+ try:
+ with self.marionette.using_prefs({self.prefs["string"]: "def"}):
+ self.assertEquals(self.marionette.get_pref(self.prefs["string"]), "def")
+ self.marionette.execute_script("return foo.bar.baz;")
+ except JavascriptException:
+ pass
+
+ self.assertEquals(self.marionette.get_pref(self.prefs["string"]), "abc")
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_profile_management.py b/testing/marionette/harness/marionette_harness/tests/unit/test_profile_management.py
new file mode 100644
index 000000000..f8ec952b2
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_profile_management.py
@@ -0,0 +1,34 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestProfileManagement(MarionetteTestCase):
+
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ self.marionette.enforce_gecko_prefs(
+ {"marionette.test.bool": True,
+ "marionette.test.string": "testing",
+ "marionette.test.int": 3
+ })
+ self.marionette.set_context("chrome")
+
+ def test_preferences_are_set(self):
+ self.assertTrue(self.marionette.get_pref("marionette.test.bool"))
+ self.assertEqual(self.marionette.get_pref("marionette.test.string"), "testing")
+ self.assertEqual(self.marionette.get_pref("marionette.test.int"), 3)
+
+ def test_change_preference(self):
+ self.assertTrue(self.marionette.get_pref("marionette.test.bool"))
+
+ self.marionette.enforce_gecko_prefs({"marionette.test.bool": False})
+
+ self.assertFalse(self.marionette.get_pref("marionette.test.bool"))
+
+ def test_clean_profile(self):
+ self.marionette.restart(clean=True)
+
+ self.assertEqual(self.marionette.get_pref("marionette.test.bool"), None)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_proxy.py b/testing/marionette/harness/marionette_harness/tests/unit/test_proxy.py
new file mode 100644
index 000000000..887d69b76
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_proxy.py
@@ -0,0 +1,252 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.errors import InvalidArgumentException
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestProxy(MarionetteTestCase):
+
+ def setUp(self):
+ super(TestProxy, self).setUp()
+ self.marionette.delete_session()
+
+ def test_that_we_can_set_a_autodetect_proxy(self):
+ capabilities = {"requiredCapabilities":
+ {
+ "proxy":{
+ "proxyType": "autodetect",
+ }
+ }
+ }
+ self.marionette.start_session(capabilities)
+ result = None
+ with self.marionette.using_context('chrome'):
+ result = self.marionette.execute_script("""return {
+ "proxyType" : Services.prefs.getIntPref('network.proxy.type'),
+ }
+ """)
+
+ self.assertEqual(result["proxyType"], 4)
+
+ def test_that_capabilities_returned_have_proxy_details(self):
+ capabilities = {"requiredCapabilities":
+ {
+ "proxy":{
+ "proxyType": "autodetect",
+ }
+ }
+ }
+ self.marionette.start_session(capabilities)
+ result = self.marionette.session_capabilities
+
+ self.assertEqual(result["proxy"]["proxyType"], "autodetect")
+
+ def test_that_we_can_set_a_system_proxy(self):
+ capabilities = {"requiredCapabilities":
+ {
+ "proxy":{
+ "proxyType": "system",
+ }
+ }
+ }
+ self.marionette.start_session(capabilities)
+ result = None
+ with self.marionette.using_context('chrome'):
+ result = self.marionette.execute_script("""return {
+ "proxyType" : Services.prefs.getIntPref('network.proxy.type'),
+ }
+ """)
+
+ self.assertEqual(result["proxyType"], 5)
+
+ def test_we_can_set_a_pac_proxy(self):
+ url = "http://marionette.test"
+ capabilities = {"requiredCapabilities":
+ {
+ "proxy":{
+ "proxyType": "pac",
+ "proxyAutoconfigUrl": url,
+ }
+ }
+ }
+ self.marionette.start_session(capabilities)
+ result = None
+ with self.marionette.using_context('chrome'):
+ result = self.marionette.execute_script("""return {
+ "proxyType" : Services.prefs.getIntPref('network.proxy.type'),
+ "proxyAutoconfigUrl" : Services.prefs.getCharPref('network.proxy.autoconfig_url'),
+ }
+ """)
+
+ self.assertEqual(result["proxyType"], 2)
+ self.assertEqual(result["proxyAutoconfigUrl"], url, 'proxyAutoconfigUrl was not set')
+
+ def test_that_we_can_set_a_manual_proxy(self):
+ port = 4444
+ url = "http://marionette.test"
+ capabilities = {"requiredCapabilities":
+ {
+ "proxy":{
+ "proxyType": "manual",
+ "ftpProxy": url,
+ "ftpProxyPort": port,
+ "httpProxy": url,
+ "httpProxyPort": port,
+ "sslProxy": url,
+ "sslProxyPort": port,
+ }
+ }
+ }
+ self.marionette.start_session(capabilities)
+ result = None
+ with self.marionette.using_context('chrome'):
+ result = self.marionette.execute_script("""return {
+ "proxyType" : Services.prefs.getIntPref('network.proxy.type'),
+ "httpProxy" : Services.prefs.getCharPref('network.proxy.http'),
+ "httpProxyPort": Services.prefs.getIntPref('network.proxy.http_port'),
+ "sslProxy": Services.prefs.getCharPref('network.proxy.ssl'),
+ "sslProxyPort": Services.prefs.getIntPref('network.proxy.ssl_port'),
+ "ftpProxy": Services.prefs.getCharPref('network.proxy.ftp'),
+ "ftpProxyPort": Services.prefs.getIntPref('network.proxy.ftp_port'),
+ }
+ """)
+
+ self.assertEqual(result["proxyType"], 1)
+ self.assertEqual(result["httpProxy"], url, 'httpProxy was not set')
+ self.assertEqual(result["httpProxyPort"], port, 'httpProxyPort was not set')
+ self.assertEqual(result["sslProxy"], url, 'sslProxy url was not set')
+ self.assertEqual(result["sslProxyPort"], port, 'sslProxyPort was not set')
+ self.assertEqual(result["ftpProxy"], url, 'ftpProxy was not set')
+ self.assertEqual(result["ftpProxyPort"], port, 'ftpProxyPort was not set')
+
+ def test_we_can_set_a_manual_proxy_with_a_socks_proxy_with_socks_version(self):
+ port = 4444
+ url = "http://marionette.test"
+ capabilities = {"requiredCapabilities":
+ {
+ "proxy":{
+ "proxyType": "manual",
+ "socksProxy": url,
+ "socksProxyPort": port,
+ "socksVersion": 4,
+ "socksUsername": "cake",
+ "socksPassword": "made with cake"
+ }
+ }
+ }
+ self.marionette.start_session(capabilities)
+ result = None
+ with self.marionette.using_context('chrome'):
+ result = self.marionette.execute_script("""return {
+ "proxyType" : Services.prefs.getIntPref('network.proxy.type'),
+ "socksProxy" : Services.prefs.getCharPref('network.proxy.socks'),
+ "socksProxyPort": Services.prefs.getIntPref('network.proxy.socks_port'),
+ "socksVersion": Services.prefs.getIntPref('network.proxy.socks_version'),
+ }
+ """)
+ self.assertEqual(result["socksProxy"], url, 'socksProxy was not set')
+ self.assertEqual(result["socksProxyPort"], port, 'socksProxyPort was not set')
+ self.assertEqual(result["socksVersion"], 4, 'socksVersion was not set to 4')
+
+ def test_we_can_set_a_manual_proxy_with_a_socks_proxy_with_no_socks_version(self):
+ port = 4444
+ url = "http://marionette.test"
+ capabilities = {"requiredCapabilities":
+ {
+ "proxy":{
+ "proxyType": "manual",
+ "socksProxy": url,
+ "socksProxyPort": port,
+ "socksUsername": "cake",
+ "socksPassword": "made with cake"
+ }
+ }
+ }
+ self.marionette.start_session(capabilities)
+ result = None
+ with self.marionette.using_context('chrome'):
+ result = self.marionette.execute_script("""return {
+ "proxyType" : Services.prefs.getIntPref('network.proxy.type'),
+ "socksProxy" : Services.prefs.getCharPref('network.proxy.socks'),
+ "socksProxyPort": Services.prefs.getIntPref('network.proxy.socks_port'),
+ "socksVersion": Services.prefs.getIntPref('network.proxy.socks_version'),
+
+ }
+ """)
+ self.assertEqual(result["socksProxy"], url, 'socksProxy was not set')
+ self.assertEqual(result["socksProxyPort"], port, 'socksProxyPort was not set')
+ self.assertEqual(result["socksVersion"], 5, 'socksVersion was not set to 5')
+
+ def test_when_not_all_manual_proxy_details_are_in_capabilities(self):
+ port = 4444
+ url = "http://marionette.test"
+ capabilities = {"requiredCapabilities":
+ {
+ "proxy":{
+ "proxyType": "manual",
+ "ftpProxy": url,
+ "ftpProxyPort": port,
+ }
+ }
+ }
+ self.marionette.start_session(capabilities)
+ result = None
+ with self.marionette.using_context('chrome'):
+ result = self.marionette.execute_script("""return {
+ "proxyType" : Services.prefs.getIntPref('network.proxy.type'),
+ "httpProxy" : Services.prefs.getCharPref('network.proxy.http'),
+ "httpProxyPort": Services.prefs.getIntPref('network.proxy.http_port'),
+ "sslProxy": Services.prefs.getCharPref('network.proxy.ssl'),
+ "sslProxyPort": Services.prefs.getIntPref('network.proxy.ssl_port'),
+ "ftpProxy": Services.prefs.getCharPref('network.proxy.ftp'),
+ "ftpProxyPort": Services.prefs.getIntPref('network.proxy.ftp_port'),
+ }
+ """)
+
+ self.assertEqual(result["proxyType"], 1)
+ self.assertNotEqual(result["httpProxy"], url,
+ 'httpProxy was set. {}'.format(result["httpProxy"]))
+ self.assertNotEqual(result["httpProxyPort"], port, 'httpProxyPort was set')
+ self.assertNotEqual(result["sslProxy"], url, 'sslProxy url was set')
+ self.assertNotEqual(result["sslProxyPort"], port, 'sslProxyPort was set')
+ self.assertEqual(result["ftpProxy"], url, 'ftpProxy was set')
+ self.assertEqual(result["ftpProxyPort"], port, 'ftpProxyPort was set')
+
+
+
+ def test_proxy_is_a_string_should_throw_invalid_argument(self):
+ capabilities = {"requiredCapabilities":
+ {
+ "proxy":"I really should be a dictionary"
+ }
+ }
+ try:
+ self.marionette.start_session(capabilities)
+ self.fail("We should have started a session because proxy should be a dict")
+ except InvalidArgumentException as e:
+ assert e.message == "Value of 'proxy' should be an object"
+
+ def test_proxy_is_passed_in_with_no_proxy_doesnt_set_it(self):
+ capabilities = {"requiredCapabilities":
+ {
+ "proxy": {"proxyType": "NOPROXY"},
+ }
+ }
+ self.marionette.start_session(capabilities)
+ result = None
+ with self.marionette.using_context('chrome'):
+ result = self.marionette.execute_script("""return {
+ "proxyType": Services.prefs.getIntPref('network.proxy.type'),
+ };
+ """)
+
+ self.assertEqual(result["proxyType"], 0)
+
+ def tearDown(self):
+ if not self.marionette.session:
+ self.marionette.start_session()
+ else:
+ self.marionette.restart(clean=True)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_quit_restart.py b/testing/marionette/harness/marionette_harness/tests/unit/test_quit_restart.py
new file mode 100644
index 000000000..38c678556
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_quit_restart.py
@@ -0,0 +1,173 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.errors import MarionetteException
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestQuitRestart(MarionetteTestCase):
+
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+
+ self.pid = self.marionette.process_id
+ self.session_id = self.marionette.session_id
+
+ self.assertNotEqual(self.marionette.get_pref("browser.startup.page"), 3)
+ self.marionette.set_pref("browser.startup.page", 3)
+
+ def tearDown(self):
+ # Ensure to restart a session if none exist for clean-up
+ if not self.marionette.session:
+ self.marionette.start_session()
+
+ self.marionette.clear_pref("browser.startup.page")
+
+ MarionetteTestCase.tearDown(self)
+
+ def test_force_restart(self):
+ self.marionette.restart()
+ self.assertEqual(self.marionette.session_id, self.session_id)
+
+ # A forced restart will cause a new process id
+ self.assertNotEqual(self.marionette.process_id, self.pid)
+
+ # If a preference value is not forced, a restart will cause a reset
+ self.assertNotEqual(self.marionette.get_pref("browser.startup.page"), 3)
+
+ def test_force_quit(self):
+ self.marionette.quit()
+
+ self.assertEqual(self.marionette.session, None)
+ with self.assertRaisesRegexp(MarionetteException, "Please start a session"):
+ self.marionette.get_url()
+
+ self.marionette.start_session()
+ self.assertNotEqual(self.marionette.session_id, self.session_id)
+ self.assertNotEqual(self.marionette.get_pref("browser.startup.page"), 3)
+
+ def test_in_app_clean_restart(self):
+ with self.assertRaises(ValueError):
+ self.marionette.restart(in_app=True, clean=True)
+
+ def test_in_app_restart(self):
+ self.marionette.restart(in_app=True)
+ self.assertEqual(self.marionette.session_id, self.session_id)
+
+ # An in-app restart will keep the same process id only on Linux
+ if self.marionette.session_capabilities['platformName'] == 'linux':
+ self.assertEqual(self.marionette.process_id, self.pid)
+ else:
+ self.assertNotEqual(self.marionette.process_id, self.pid)
+
+ # If a preference value is not forced, a restart will cause a reset
+ self.assertNotEqual(self.marionette.get_pref("browser.startup.page"), 3)
+
+ def test_in_app_restart_with_callback(self):
+ self.marionette.restart(in_app=True,
+ callback=lambda: self.shutdown(restart=True))
+
+ self.assertEqual(self.marionette.session_id, self.session_id)
+
+ # An in-app restart will keep the same process id only on Linux
+ if self.marionette.session_capabilities['platformName'] == 'linux':
+ self.assertEqual(self.marionette.process_id, self.pid)
+ else:
+ self.assertNotEqual(self.marionette.process_id, self.pid)
+
+ # If a preference value is not forced, a restart will cause a reset
+ self.assertNotEqual(self.marionette.get_pref("browser.startup.page"), 3)
+
+ def test_in_app_quit(self):
+ self.marionette.quit(in_app=True)
+
+ self.assertEqual(self.marionette.session, None)
+ with self.assertRaisesRegexp(MarionetteException, "Please start a session"):
+ self.marionette.get_url()
+
+ self.marionette.start_session()
+ self.assertNotEqual(self.marionette.session_id, self.session_id)
+ self.assertNotEqual(self.marionette.get_pref("browser.startup.page"), 3)
+
+ def test_in_app_quit_with_callback(self):
+ self.marionette.quit(in_app=True, callback=self.shutdown)
+ self.assertEqual(self.marionette.session, None)
+ with self.assertRaisesRegexp(MarionetteException, "Please start a session"):
+ self.marionette.get_url()
+
+ self.marionette.start_session()
+ self.assertNotEqual(self.marionette.session_id, self.session_id)
+ self.assertNotEqual(self.marionette.get_pref("browser.startup.page"), 3)
+
+ def test_reset_context_after_quit_by_set_context(self):
+ # Check that we are in content context which is used by default in Marionette
+ self.assertNotIn('chrome://', self.marionette.get_url(),
+ "Context doesn't default to content")
+
+ self.marionette.set_context('chrome')
+ self.marionette.quit(in_app=True)
+ self.assertEqual(self.marionette.session, None)
+ self.marionette.start_session()
+ self.assertNotIn('chrome://', self.marionette.get_url(),
+ "Not in content context after quit with using_context")
+
+ def test_reset_context_after_quit_by_using_context(self):
+ # Check that we are in content context which is used by default in Marionette
+ self.assertNotIn('chrome://', self.marionette.get_url(),
+ "Context doesn't default to content")
+
+ with self.marionette.using_context('chrome'):
+ self.marionette.quit(in_app=True)
+ self.assertEqual(self.marionette.session, None)
+ self.marionette.start_session()
+ self.assertNotIn('chrome://', self.marionette.get_url(),
+ "Not in content context after quit with using_context")
+
+ def test_keep_context_after_restart_by_set_context(self):
+ # Check that we are in content context which is used by default in Marionette
+ self.assertNotIn('chrome://', self.marionette.get_url(),
+ "Context doesn't default to content")
+
+ # restart while we are in chrome context
+ self.marionette.set_context('chrome')
+ self.marionette.restart(in_app=True)
+
+ # An in-app restart will keep the same process id only on Linux
+ if self.marionette.session_capabilities['platformName'] == 'linux':
+ self.assertEqual(self.marionette.process_id, self.pid)
+ else:
+ self.assertNotEqual(self.marionette.process_id, self.pid)
+
+ self.assertIn('chrome://', self.marionette.get_url(),
+ "Not in chrome context after a restart with set_context")
+
+ def test_keep_context_after_restart_by_using_context(self):
+ # Check that we are in content context which is used by default in Marionette
+ self.assertNotIn('chrome://', self.marionette.get_url(),
+ "Context doesn't default to content")
+
+ # restart while we are in chrome context
+ with self.marionette.using_context('chrome'):
+ self.marionette.restart(in_app=True)
+
+ # An in-app restart will keep the same process id only on Linux
+ if self.marionette.session_capabilities['platformName'] == 'linux':
+ self.assertEqual(self.marionette.process_id, self.pid)
+ else:
+ self.assertNotEqual(self.marionette.process_id, self.pid)
+
+ self.assertIn('chrome://', self.marionette.get_url(),
+ "Not in chrome context after a restart with using_context")
+
+ def shutdown(self, restart=False):
+ self.marionette.set_context("chrome")
+ self.marionette.execute_script("""
+ Components.utils.import("resource://gre/modules/Services.jsm");
+ let flags = Ci.nsIAppStartup.eAttemptQuit
+ if(arguments[0]) {
+ flags |= Ci.nsIAppStartup.eRestart;
+ }
+ Services.startup.quit(flags);
+ """, script_args=[restart])
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_rendered_element.py b/testing/marionette/harness/marionette_harness/tests/unit/test_rendered_element.py
new file mode 100644
index 000000000..508870e91
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_rendered_element.py
@@ -0,0 +1,34 @@
+#Copyright 2007-2009 WebDriver committers
+#Copyright 2007-2009 Google Inc.
+#
+#Licensed under the Apache License, Version 2.0 (the "License");
+#you may not use this file except in compliance with the License.
+#You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#Unless required by applicable law or agreed to in writing, software
+#distributed under the License is distributed on an "AS IS" BASIS,
+#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#See the License for the specific language governing permissions and
+#limitations under the License.
+
+from marionette_driver.by import By
+
+from marionette_harness import MarionetteTestCase
+
+
+class RenderedElementTests(MarionetteTestCase):
+
+ def testWeCanGetComputedStyleValueOnElement(self):
+ test_url = self.marionette.absolute_url('javascriptPage.html')
+ self.marionette.navigate(test_url)
+ element = self.marionette.find_element(By.ID, "green-parent")
+ backgroundColour = element.value_of_css_property("background-color")
+
+ self.assertEqual("rgb(0, 128, 0)", backgroundColour)
+
+ element = self.marionette.find_element(By.ID, "red-item")
+ backgroundColour = element.value_of_css_property("background-color")
+
+ self.assertEqual("rgb(255, 0, 0)", backgroundColour)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_report.py b/testing/marionette/harness/marionette_harness/tests/unit/test_report.py
new file mode 100644
index 000000000..f22c3db4b
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_report.py
@@ -0,0 +1,29 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_harness import MarionetteTestCase, expectedFailure, skip
+
+
+class TestReport(MarionetteTestCase):
+
+ def test_pass(self):
+ assert True
+
+ def test_fail(self):
+ assert False
+
+ @skip('Skip Message')
+ def test_skip(self):
+ assert False
+
+ @expectedFailure
+ def test_expected_fail(self):
+ assert False
+
+ @expectedFailure
+ def test_unexpected_pass(self):
+ assert True
+
+ def test_error(self):
+ raise Exception()
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_run_js_test.py b/testing/marionette/harness/marionette_harness/tests/unit/test_run_js_test.py
new file mode 100644
index 000000000..134223ce1
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_run_js_test.py
@@ -0,0 +1,10 @@
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestRunJSTest(MarionetteTestCase):
+ def test_basic(self):
+ self.run_js_test('test_simpletest_pass.js')
+ self.run_js_test('test_simpletest_fail.js')
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_screen_orientation.py b/testing/marionette/harness/marionette_harness/tests/unit/test_screen_orientation.py
new file mode 100644
index 000000000..830795a1e
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_screen_orientation.py
@@ -0,0 +1,86 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver import errors
+from mozrunner.devices.emulator_screen import EmulatorScreen
+
+from marionette_harness import MarionetteTestCase, skip_if_desktop, skip_if_mobile
+
+
+default_orientation = "portrait-primary"
+unknown_orientation = "Unknown screen orientation: {}"
+
+
+class TestScreenOrientation(MarionetteTestCase):
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ self.is_mobile = self.marionette.session_capabilities.get("rotatable", False)
+
+ def tearDown(self):
+ if self.is_mobile:
+ self.marionette.set_orientation(default_orientation)
+ self.assertEqual(self.marionette.orientation, default_orientation, "invalid state")
+ MarionetteTestCase.tearDown(self)
+
+ @skip_if_desktop("Not supported in Firefox")
+ def test_set_orientation_to_portrait_primary(self):
+ self.marionette.set_orientation("portrait-primary")
+ new_orientation = self.marionette.orientation
+ self.assertEqual(new_orientation, "portrait-primary")
+
+ @skip_if_desktop("Not supported in Firefox")
+ def test_set_orientation_to_landscape_primary(self):
+ self.marionette.set_orientation("landscape-primary")
+ new_orientation = self.marionette.orientation
+ self.assertEqual(new_orientation, "landscape-primary")
+
+ @skip_if_desktop("Not supported in Firefox")
+ def test_set_orientation_to_portrait_secondary(self):
+ self.marionette.set_orientation("portrait-secondary")
+ new_orientation = self.marionette.orientation
+ self.assertEqual(new_orientation, "portrait-secondary")
+
+ @skip_if_desktop("Not supported in Firefox")
+ def test_set_orientation_to_landscape_secondary(self):
+ self.marionette.set_orientation("landscape-secondary")
+ new_orientation = self.marionette.orientation
+ self.assertEqual(new_orientation, "landscape-secondary")
+
+ @skip_if_desktop("Not supported in Firefox")
+ def test_set_orientation_to_shorthand_portrait(self):
+ # Set orientation to something other than portrait-primary first, since the default is
+ # portrait-primary.
+ self.marionette.set_orientation("landscape-primary")
+ self.assertEqual(self.marionette.orientation, "landscape-primary", "invalid state")
+
+ self.marionette.set_orientation("portrait")
+ new_orientation = self.marionette.orientation
+ self.assertEqual(new_orientation, "portrait-primary")
+
+ @skip_if_desktop("Not supported in Firefox")
+ def test_set_orientation_to_shorthand_landscape(self):
+ self.marionette.set_orientation("landscape")
+ new_orientation = self.marionette.orientation
+ self.assertEqual(new_orientation, "landscape-primary")
+
+ @skip_if_desktop("Not supported in Firefox")
+ def test_set_orientation_with_mixed_casing(self):
+ self.marionette.set_orientation("lAnDsCaPe")
+ new_orientation = self.marionette.orientation
+ self.assertEqual(new_orientation, "landscape-primary")
+
+ @skip_if_desktop("Not supported in Firefox")
+ def test_set_invalid_orientation(self):
+ with self.assertRaisesRegexp(errors.MarionetteException, unknown_orientation.format("cheese")):
+ self.marionette.set_orientation("cheese")
+
+ @skip_if_desktop("Not supported in Firefox")
+ def test_set_null_orientation(self):
+ with self.assertRaisesRegexp(errors.MarionetteException, unknown_orientation.format("null")):
+ self.marionette.set_orientation(None)
+
+ @skip_if_mobile("Specific test for Firefox")
+ def test_unsupported_operation_on_desktop(self):
+ with self.assertRaises(errors.UnsupportedOperationException):
+ self.marionette.set_orientation("landscape-primary")
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_screenshot.py b/testing/marionette/harness/marionette_harness/tests/unit/test_screenshot.py
new file mode 100644
index 000000000..aa0e6ab1c
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_screenshot.py
@@ -0,0 +1,428 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import base64
+import hashlib
+import imghdr
+import struct
+import urllib
+
+from marionette_driver import By
+from marionette_driver.errors import JavascriptException, NoSuchWindowException
+from marionette_harness import (
+ MarionetteTestCase,
+ skip,
+ skip_if_mobile,
+ WindowManagerMixin,
+)
+
+
+def inline(doc, mime="text/html;charset=utf-8"):
+ return "data:{0},{1}".format(mime, urllib.quote(doc))
+
+
+box = inline("<body><div id='box'><p id='green' style='width: 50px; height: 50px; "
+ "background: silver;'></p></div></body>")
+input = inline("<body><input id='text-input'></input></body>")
+long = inline("<body style='height: 300vh'><p style='margin-top: 100vh'>foo</p></body>")
+short = inline("<body style='height: 10vh'></body>")
+svg = inline("""
+ <svg xmlns="http://www.w3.org/2000/svg" height="20" width="20">
+ <rect height="20" width="20"/>
+ </svg>""", mime="image/svg+xml")
+
+
+class ScreenCaptureTestCase(MarionetteTestCase):
+
+ def setUp(self):
+ super(ScreenCaptureTestCase, self).setUp()
+
+ self._device_pixel_ratio = None
+
+ @property
+ def device_pixel_ratio(self):
+ if self._device_pixel_ratio is None:
+ self._device_pixel_ratio = self.marionette.execute_script("""
+ return window.devicePixelRatio
+ """)
+ return self._device_pixel_ratio
+
+ @property
+ def document_element(self):
+ return self.marionette.find_element(By.CSS_SELECTOR, ":root")
+
+ @property
+ def page_y_offset(self):
+ return self.marionette.execute_script("return window.pageYOffset")
+
+ @property
+ def viewport_dimensions(self):
+ return self.marionette.execute_script("""
+ return [arguments[0].clientWidth,
+ arguments[0].clientHeight];
+ """, script_args=[self.document_element])
+
+ def assert_png(self, screenshot):
+ """Test that screenshot is a Base64 encoded PNG file."""
+ image = base64.decodestring(screenshot)
+ self.assertEqual(imghdr.what("", image), "png")
+
+ def assert_formats(self, element=None):
+ if element is None:
+ element = self.document_element
+
+ screenshot_default = self.marionette.screenshot(element=element)
+ screenshot_image = self.marionette.screenshot(element=element, format="base64")
+ binary1 = self.marionette.screenshot(element=element, format="binary")
+ binary2 = self.marionette.screenshot(element=element, format="binary")
+ hash1 = self.marionette.screenshot(element=element, format="hash")
+ hash2 = self.marionette.screenshot(element=element, format="hash")
+
+ # Valid data should have been returned
+ self.assert_png(screenshot_image)
+ self.assertEqual(imghdr.what("", binary1), "png")
+ self.assertEqual(screenshot_image, base64.b64encode(binary1))
+ self.assertEqual(hash1, hashlib.sha256(screenshot_image).hexdigest())
+
+ # Different formats produce different data
+ self.assertNotEqual(screenshot_image, binary1)
+ self.assertNotEqual(screenshot_image, hash1)
+ self.assertNotEqual(binary1, hash1)
+
+ # A second capture should be identical
+ self.assertEqual(screenshot_image, screenshot_default)
+ self.assertEqual(binary1, binary2)
+ self.assertEqual(hash1, hash2)
+
+ def get_element_dimensions(self, element):
+ rect = element.rect
+ return rect["width"], rect["height"]
+
+ def get_image_dimensions(self, screenshot):
+ self.assert_png(screenshot)
+ image = base64.decodestring(screenshot)
+ width, height = struct.unpack(">LL", image[16:24])
+ return int(width), int(height)
+
+ def scale(self, rect):
+ return (int(rect[0] * self.device_pixel_ratio),
+ int(rect[1] * self.device_pixel_ratio))
+
+
+class TestScreenCaptureChrome(WindowManagerMixin, ScreenCaptureTestCase):
+
+ def setUp(self):
+ super(TestScreenCaptureChrome, self).setUp()
+ self.marionette.set_context("chrome")
+
+ def tearDown(self):
+ self.close_all_windows()
+ super(TestScreenCaptureChrome, self).tearDown()
+
+ @property
+ def window_dimensions(self):
+ return tuple(self.marionette.execute_script("""
+ let el = document.documentElement;
+ let rect = el.getBoundingClientRect();
+ return [rect.width, rect.height];
+ """))
+
+ def open_dialog(self, url=None, width=None, height=None):
+ if url is None:
+ url = "chrome://marionette/content/test_dialog.xul"
+
+ def opener():
+ features = "chrome"
+ if height is not None:
+ features += ",height={}".format(height)
+ if width is not None:
+ features += ",width={}".format(width)
+
+ self.marionette.execute_script("""
+ window.open(arguments[0], "", arguments[1]);
+ """, script_args=[url, features])
+
+ return self.open_window(opener)
+
+ def test_capture_different_context(self):
+ """Check that screenshots in content and chrome are different."""
+ with self.marionette.using_context("content"):
+ screenshot_content = self.marionette.screenshot()
+ screenshot_chrome = self.marionette.screenshot()
+ self.assertNotEqual(screenshot_content, screenshot_chrome)
+
+ @skip_if_mobile("Fennec doesn't support other chrome windows")
+ def test_capture_element(self):
+ dialog = self.open_dialog()
+ self.marionette.switch_to_window(dialog)
+
+ # Ensure we only capture the element
+ el = self.marionette.find_element(By.ID, "test-list")
+ screenshot_element = self.marionette.screenshot(element=el)
+ self.assertEqual(self.scale(self.get_element_dimensions(el)),
+ self.get_image_dimensions(screenshot_element))
+
+ # Ensure we do not capture the full window
+ screenshot_dialog = self.marionette.screenshot()
+ self.assertNotEqual(screenshot_dialog, screenshot_element)
+
+ self.marionette.close_chrome_window()
+ self.marionette.switch_to_window(self.start_window)
+
+ @skip_if_mobile("Fennec doesn't support other chrome windows")
+ def test_capture_flags(self):
+ dialog = self.open_dialog()
+ self.marionette.switch_to_window(dialog)
+
+ textbox = self.marionette.find_element(By.ID, "text-box")
+ textbox.send_keys("")
+ screenshot_focus = self.marionette.screenshot()
+
+ self.marionette.execute_script("arguments[0].blur();", script_args=[textbox])
+ screenshot_no_focus = self.marionette.screenshot()
+
+ self.marionette.close_chrome_window()
+ self.marionette.switch_to_window(self.start_window)
+
+ self.assertNotEqual(screenshot_focus, screenshot_no_focus)
+
+ def test_capture_full_area(self):
+ # A full capture is not the outer dimensions of the window,
+ # but instead the bounding box of the window's root node (documentElement).
+ screenshot_full = self.marionette.screenshot()
+ screenshot_root = self.marionette.screenshot(element=self.document_element)
+
+ self.assert_png(screenshot_full)
+ self.assert_png(screenshot_root)
+ self.assertEqual(screenshot_root, screenshot_full)
+ self.assertEqual(self.scale(self.get_element_dimensions(self.document_element)),
+ self.get_image_dimensions(screenshot_full))
+
+ @skip_if_mobile("Fennec doesn't support other chrome windows")
+ def test_capture_viewport(self):
+ # Load a HTML test page into the chrome window to get scrollbars
+ test_page = self.marionette.absolute_url("test.html")
+ dialog = self.open_dialog(url=test_page, width=50, height=50)
+ self.marionette.switch_to_window(dialog)
+
+ # Size of screenshot has to match viewport size
+ screenshot = self.marionette.screenshot(full=False)
+ self.assert_png(screenshot)
+ self.assertEqual(self.scale(self.viewport_dimensions),
+ self.get_image_dimensions(screenshot))
+ self.assertNotEqual(self.scale(self.window_dimensions),
+ self.get_image_dimensions(screenshot))
+
+ self.marionette.close_chrome_window()
+ self.marionette.switch_to_window(self.start_window)
+
+ @skip_if_mobile("Fennec doesn't support other chrome windows")
+ def test_capture_window_already_closed(self):
+ dialog = self.open_dialog()
+ self.marionette.switch_to_window(dialog)
+ self.marionette.close_chrome_window()
+
+ self.assertRaises(NoSuchWindowException, self.marionette.screenshot)
+ self.marionette.switch_to_window(self.start_window)
+
+ @skip_if_mobile("Fennec doesn't support other chrome windows")
+ def test_formats(self):
+ dialog = self.open_dialog()
+ self.marionette.switch_to_window(dialog)
+
+ self.assert_formats()
+
+ self.marionette.close_chrome_window()
+ self.marionette.switch_to_window(self.start_window)
+
+ def test_format_unknown(self):
+ with self.assertRaises(ValueError):
+ self.marionette.screenshot(format="cheese")
+
+ @skip_if_mobile("Fennec doesn't support other chrome windows")
+ def test_highlight_elements(self):
+ dialog = self.open_dialog()
+ self.marionette.switch_to_window(dialog)
+
+ # Highlighting the element itself shouldn't make the image larger
+ element = self.marionette.find_element(By.ID, "test-list")
+ screenshot_element = self.marionette.screenshot(element=element)
+ screenshot_highlight = self.marionette.screenshot(element=element,
+ highlights=[element])
+ self.assertEqual(self.scale(self.get_element_dimensions(element)),
+ self.get_image_dimensions(screenshot_element))
+ self.assertNotEqual(screenshot_element, screenshot_highlight)
+
+ # Highlighting a sub element
+ button = self.marionette.find_element(By.ID, "choose-button")
+ screenshot_highlight_button = self.marionette.screenshot(element=element,
+ highlights=[button])
+ self.assertNotEqual(screenshot_element, screenshot_highlight_button)
+ self.assertNotEqual(screenshot_highlight, screenshot_highlight_button)
+
+ self.marionette.close_chrome_window()
+ self.marionette.switch_to_window(self.start_window)
+
+ def test_highlight_element_not_seen(self):
+ """Check that for not found elements an exception is raised."""
+ with self.marionette.using_context('content'):
+ self.marionette.navigate(box)
+ content_element = self.marionette.find_element(By.ID, "green")
+
+ self.assertRaisesRegexp(JavascriptException, "Element reference not seen before",
+ self.marionette.screenshot, highlights=[content_element])
+
+ chrome_document_element = self.document_element
+ with self.marionette.using_context('content'):
+ self.assertRaisesRegexp(JavascriptException, "Element reference not seen before",
+ self.marionette.screenshot,
+ highlights=[chrome_document_element])
+
+
+class TestScreenCaptureContent(WindowManagerMixin, ScreenCaptureTestCase):
+
+ def setUp(self):
+ super(TestScreenCaptureContent, self).setUp()
+ self.marionette.set_context("content")
+
+ def tearDown(self):
+ self.close_all_tabs()
+ super(TestScreenCaptureContent, self).tearDown()
+
+ @property
+ def scroll_dimensions(self):
+ return tuple(self.marionette.execute_script("""
+ return [document.body.scrollWidth, document.body.scrollHeight]
+ """))
+
+ @skip_if_mobile("Needs application independent method to open a new tab")
+ def test_capture_tab_already_closed(self):
+ tab = self.open_tab()
+ self.marionette.switch_to_window(tab)
+ self.marionette.close()
+
+ self.assertRaises(NoSuchWindowException, self.marionette.screenshot)
+ self.marionette.switch_to_window(self.start_tab)
+
+ def test_capture_element(self):
+ self.marionette.navigate(box)
+ el = self.marionette.find_element(By.TAG_NAME, "div")
+ screenshot = self.marionette.screenshot(element=el)
+ self.assert_png(screenshot)
+ self.assertEqual(self.scale(self.get_element_dimensions(el)),
+ self.get_image_dimensions(screenshot))
+
+ @skip("Bug 1213875")
+ def test_capture_element_scrolled_into_view(self):
+ self.marionette.navigate(long)
+ el = self.marionette.find_element(By.TAG_NAME, "p")
+ screenshot = self.marionette.screenshot(element=el)
+ self.assert_png(screenshot)
+ self.assertEqual(self.scale(self.get_element_dimensions(el)),
+ self.get_image_dimensions(screenshot))
+ self.assertGreater(self.page_y_offset, 0)
+
+ @skip("Bug 1330560 - AssertionError: u'iVBORw0KGgoA... (images unexpectedly equal)")
+ def test_capture_flags(self):
+ self.marionette.navigate(input)
+
+ textbox = self.marionette.find_element(By.ID, "text-input")
+ textbox.send_keys("")
+ screenshot_focus = self.marionette.screenshot()
+
+ self.marionette.execute_script("arguments[0].blur();", script_args=[textbox])
+ screenshot_no_focus = self.marionette.screenshot()
+
+ self.assertNotEqual(screenshot_focus, screenshot_no_focus)
+
+ @skip_if_mobile("Bug 1330642 - Tuples differ: (1960, 11130) != (1960, 11129)")
+ def test_capture_html_document_element(self):
+ self.marionette.navigate(long)
+ screenshot = self.marionette.screenshot()
+ self.assert_png(screenshot)
+ self.assertEqual(self.scale(self.scroll_dimensions),
+ self.get_image_dimensions(screenshot))
+
+ def test_capture_svg_document_element(self):
+ self.marionette.navigate(svg)
+ screenshot = self.marionette.screenshot()
+ self.assert_png(screenshot)
+ self.assertEqual(self.scale(self.get_element_dimensions(self.document_element)),
+ self.get_image_dimensions(screenshot))
+
+ def test_capture_viewport(self):
+ url = self.marionette.absolute_url("clicks.html")
+ self.marionette.navigate(short)
+ self.marionette.navigate(url)
+ screenshot = self.marionette.screenshot(full=False)
+ self.assert_png(screenshot)
+ self.assertEqual(self.scale(self.viewport_dimensions),
+ self.get_image_dimensions(screenshot))
+
+ def test_capture_viewport_after_scroll(self):
+ self.marionette.navigate(long)
+ before = self.marionette.screenshot()
+ el = self.marionette.find_element(By.TAG_NAME, "p")
+ self.marionette.execute_script(
+ "arguments[0].scrollIntoView()", script_args=[el])
+ after = self.marionette.screenshot(full=False)
+ self.assertNotEqual(before, after)
+ self.assertGreater(self.page_y_offset, 0)
+
+ def test_formats(self):
+ self.marionette.navigate(box)
+
+ # Use a smaller region to speed up the test
+ element = self.marionette.find_element(By.TAG_NAME, "div")
+ self.assert_formats(element=element)
+
+ def test_format_unknown(self):
+ with self.assertRaises(ValueError):
+ self.marionette.screenshot(format="cheese")
+
+ def test_highlight_elements(self):
+ self.marionette.navigate(box)
+ element = self.marionette.find_element(By.TAG_NAME, "div")
+
+ # Highlighting the element itself shouldn't make the image larger
+ screenshot_element = self.marionette.screenshot(element=element)
+ screenshot_highlight = self.marionette.screenshot(element=element,
+ highlights=[element])
+ self.assertEqual(self.scale(self.get_element_dimensions(element)),
+ self.get_image_dimensions(screenshot_highlight))
+ self.assertNotEqual(screenshot_element, screenshot_highlight)
+
+ # Highlighting a sub element
+ paragraph = self.marionette.find_element(By.ID, "green")
+ screenshot_highlight_paragraph = self.marionette.screenshot(element=element,
+ highlights=[paragraph])
+ self.assertNotEqual(screenshot_element, screenshot_highlight_paragraph)
+ self.assertNotEqual(screenshot_highlight, screenshot_highlight_paragraph)
+
+ def test_scroll_default(self):
+ self.marionette.navigate(long)
+ before = self.page_y_offset
+ el = self.marionette.find_element(By.TAG_NAME, "p")
+ self.marionette.screenshot(element=el, format="hash")
+ self.assertNotEqual(before, self.page_y_offset)
+
+ def test_scroll(self):
+ self.marionette.navigate(long)
+ before = self.page_y_offset
+ el = self.marionette.find_element(By.TAG_NAME, "p")
+ self.marionette.screenshot(element=el, format="hash", scroll=True)
+ self.assertNotEqual(before, self.page_y_offset)
+
+ def test_scroll_off(self):
+ self.marionette.navigate(long)
+ el = self.marionette.find_element(By.TAG_NAME, "p")
+ before = self.page_y_offset
+ self.marionette.screenshot(element=el, format="hash", scroll=False)
+ self.assertEqual(before, self.page_y_offset)
+
+ def test_scroll_no_element(self):
+ self.marionette.navigate(long)
+ before = self.page_y_offset
+ self.marionette.screenshot(format="hash", scroll=True)
+ self.assertEqual(before, self.page_y_offset)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_select.py b/testing/marionette/harness/marionette_harness/tests/unit/test_select.py
new file mode 100644
index 000000000..3c9522bea
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_select.py
@@ -0,0 +1,164 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import urllib
+
+from marionette_driver.by import By
+
+from marionette_harness import MarionetteTestCase
+
+
+def inline(doc):
+ return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+
+
+class SelectTestCase(MarionetteTestCase):
+ def assertSelected(self, option_element):
+ self.assertTrue(option_element.is_selected(), "<option> element not selected")
+ self.assertTrue(self.marionette.execute_script(
+ "return arguments[0].selected", script_args=[option_element], sandbox=None),
+ "<option> selected attribute not updated")
+
+ def assertNotSelected(self, option_element):
+ self.assertFalse(option_element.is_selected(), "<option> is selected")
+ self.assertFalse(self.marionette.execute_script(
+ "return arguments[0].selected", script_args=[option_element], sandbox=None),
+ "<option> selected attribute not updated")
+
+
+class TestSelect(SelectTestCase):
+ def test_single(self):
+ self.marionette.navigate(inline("""
+ <select>
+ <option>first
+ <option>second
+ </select>"""))
+ select = self.marionette.find_element(By.TAG_NAME, "select")
+ options = self.marionette.find_elements(By.TAG_NAME, "option")
+
+ self.assertSelected(options[0])
+ options[1].click()
+ self.assertSelected(options[1])
+
+ def test_deselect(self):
+ self.marionette.navigate(inline("""
+ <select>
+ <option>first
+ <option>second
+ <option>third
+ </select>"""))
+ select = self.marionette.find_element(By.TAG_NAME, "select")
+ options = self.marionette.find_elements(By.TAG_NAME, "option")
+
+ options[0].click()
+ self.assertSelected(options[0])
+ options[1].click()
+ self.assertSelected(options[1])
+ options[2].click()
+ self.assertSelected(options[2])
+ options[0].click()
+ self.assertSelected(options[0])
+
+ def test_out_of_view(self):
+ self.marionette.navigate(inline("""
+ <select>
+ <option>1
+ <option>2
+ <option>3
+ <option>4
+ <option>5
+ <option>6
+ <option>7
+ <option>8
+ <option>9
+ <option>10
+ <option>11
+ <option>12
+ <option>13
+ <option>14
+ <option>15
+ <option>16
+ <option>17
+ <option>18
+ <option>19
+ <option>20
+ </select>"""))
+ select = self.marionette.find_element(By.TAG_NAME, "select")
+ options = self.marionette.find_elements(By.TAG_NAME, "option")
+
+ options[14].click()
+ self.assertSelected(options[14])
+
+
+class TestSelectMultiple(SelectTestCase):
+ def test_single(self):
+ self.marionette.navigate(inline("<select multiple> <option>first </select>"))
+ option = self.marionette.find_element(By.TAG_NAME, "option")
+ option.click()
+ self.assertSelected(option)
+
+ def test_multiple(self):
+ self.marionette.navigate(inline("""
+ <select multiple>
+ <option>first
+ <option>second
+ <option>third
+ </select>"""))
+ select = self.marionette.find_element(By.TAG_NAME, "select")
+ options = select.find_elements(By.TAG_NAME, "option")
+
+ options[1].click()
+ self.assertSelected(options[1])
+
+ options[2].click()
+ self.assertSelected(options[2])
+ self.assertSelected(options[1])
+
+ def test_deselect_selected(self):
+ self.marionette.navigate(inline("<select multiple> <option>first </select>"))
+ option = self.marionette.find_element(By.TAG_NAME, "option")
+ option.click()
+ self.assertSelected(option)
+ option.click()
+ self.assertNotSelected(option)
+
+ def test_deselect_preselected(self):
+ self.marionette.navigate(inline("""
+ <select multiple>
+ <option selected>first
+ </select>"""))
+ option = self.marionette.find_element(By.TAG_NAME, "option")
+ self.assertSelected(option)
+ option.click()
+ self.assertNotSelected(option)
+
+ def test_out_of_view(self):
+ self.marionette.navigate(inline("""
+ <select multiple>
+ <option>1
+ <option>2
+ <option>3
+ <option>4
+ <option>5
+ <option>6
+ <option>7
+ <option>8
+ <option>9
+ <option>10
+ <option>11
+ <option>12
+ <option>13
+ <option>14
+ <option>15
+ <option>16
+ <option>17
+ <option>18
+ <option>19
+ <option>20
+ </select>"""))
+ select = self.marionette.find_element(By.TAG_NAME, "select")
+ options = self.marionette.find_elements(By.TAG_NAME, "option")
+
+ options[-1].click()
+ self.assertSelected(options[-1])
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_session.py b/testing/marionette/harness/marionette_harness/tests/unit/test_session.py
new file mode 100644
index 000000000..1676df51f
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_session.py
@@ -0,0 +1,56 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver import errors
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestSession(MarionetteTestCase):
+ def setUp(self):
+ super(TestSession, self).setUp()
+ self.marionette.delete_session()
+
+ def test_new_session_returns_capabilities(self):
+ # Sends newSession
+ caps = self.marionette.start_session()
+
+ # Check that session was created. This implies the server
+ # sent us the sessionId and status fields.
+ self.assertIsNotNone(self.marionette.session)
+
+ # Required capabilities mandated by WebDriver spec
+ self.assertIn("browserName", caps)
+ self.assertIn("browserVersion", caps)
+ self.assertIn("platformName", caps)
+ self.assertIn("platformVersion", caps)
+
+ # Optional capabilities we want Marionette to support
+ self.assertIn("rotatable", caps)
+
+ def test_get_session_id(self):
+ # Sends newSession
+ self.marionette.start_session()
+
+ self.assertTrue(self.marionette.session_id is not None)
+ self.assertTrue(isinstance(self.marionette.session_id, unicode))
+
+ def test_set_the_session_id(self):
+ # Sends newSession
+ self.marionette.start_session(session_id="ILoveCheese")
+
+ self.assertEqual(self.marionette.session_id, "ILoveCheese")
+ self.assertTrue(isinstance(self.marionette.session_id, unicode))
+
+ def test_session_already_started(self):
+ self.marionette.start_session()
+ self.assertTrue(isinstance(self.marionette.session_id, unicode))
+ with self.assertRaises(errors.SessionNotCreatedException):
+ self.marionette._send_message("newSession", {})
+
+ def test_no_session(self):
+ with self.assertRaises(errors.InvalidSessionIdException):
+ self.marionette.get_url()
+ self.marionette.start_session()
+ self.marionette.get_url()
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_set_window_size.py b/testing/marionette/harness/marionette_harness/tests/unit/test_set_window_size.py
new file mode 100644
index 000000000..e1bd5e684
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_set_window_size.py
@@ -0,0 +1,84 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestSetWindowSize(MarionetteTestCase):
+ def setUp(self):
+ super(MarionetteTestCase, self).setUp()
+ self.start_size = self.marionette.window_size
+ self.max_width = self.marionette.execute_script("return window.screen.availWidth;")
+ self.max_height = self.marionette.execute_script("return window.screen.availHeight;")
+
+ def tearDown(self):
+ # WebDriver spec says a resize cannot result in window being maximized, an
+ # error is returned if that is the case; therefore if the window is maximized
+ # at the start of this test, returning to the original size via set_window_size
+ # size will result in error; so reset to original size minus 1 pixel width
+ if self.start_size['width'] == self.max_width and self.start_size['height'] == self.max_height:
+ self.start_size['width']-=1
+ self.marionette.set_window_size(self.start_size['width'], self.start_size['height'])
+ super(MarionetteTestCase, self).tearDown()
+
+ def test_that_we_can_get_and_set_window_size(self):
+ # event handler
+ self.marionette.execute_script("""
+ window.wrappedJSObject.rcvd_event = false;
+ window.onresize = function() {
+ window.wrappedJSObject.rcvd_event = true;
+ };
+ """)
+
+ # valid size
+ width = self.max_width - 100
+ height = self.max_height - 100
+ self.marionette.set_window_size(width, height)
+ self.wait_for_condition(lambda m: m.execute_script("return window.wrappedJSObject.rcvd_event;"))
+ size = self.marionette.window_size
+ self.assertEqual(size['width'], width,
+ "Window width is {0} but should be {1}".format(size['width'], width))
+ self.assertEqual(size['height'], height,
+ "Window height is {0} but should be {1}".format(size['height'], height))
+
+ def test_that_we_can_get_new_size_when_set_window_size(self):
+ actual = self.marionette.window_size
+ width = actual['width'] - 50
+ height = actual['height'] - 50
+ size = self.marionette.set_window_size(width, height)
+ self.assertIsNotNone(size, "Response is None")
+ self.assertEqual(size['width'], width,
+ "New width is {0} but should be {1}".format(size['width'], width))
+ self.assertEqual(size['height'], height,
+ "New height is {0} but should be {1}".format(size['height'], height))
+
+ def test_possible_to_request_window_larger_than_screen(self):
+ self.marionette.set_window_size(4 * self.max_width, 4 * self.max_height)
+ size = self.marionette.window_size
+
+ # In X the window size may be greater than the bounds of the screen
+ self.assertGreaterEqual(size["width"], self.max_width)
+ self.assertGreaterEqual(size["height"], self.max_height)
+
+ def test_that_we_can_maximise_the_window(self):
+ # valid size
+ width = self.max_width - 100
+ height = self.max_height - 100
+ self.marionette.set_window_size(width, height)
+
+ # event handler
+ self.marionette.execute_script("""
+ window.wrappedJSObject.rcvd_event = false;
+ window.onresize = function() {
+ window.wrappedJSObject.rcvd_event = true;
+ };
+ """)
+ self.marionette.maximize_window()
+ self.wait_for_condition(lambda m: m.execute_script("return window.wrappedJSObject.rcvd_event;"))
+
+ size = self.marionette.window_size
+ self.assertGreaterEqual(size['width'], self.max_width,
+ "Window width does not use availWidth, current width: {0}, max width: {1}".format(size['width'], self.max_width))
+ self.assertGreaterEqual(size['height'], self.max_height,
+ "Window height does not use availHeight. current width: {0}, max width: {1}".format(size['height'], self.max_height))
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_shadow_dom.py b/testing/marionette/harness/marionette_harness/tests/unit/test_shadow_dom.py
new file mode 100644
index 000000000..3f91d7cc0
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_shadow_dom.py
@@ -0,0 +1,80 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+from marionette_driver.errors import (
+ NoSuchElementException,
+ StaleElementException,
+ UnsupportedOperationException,
+)
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestShadowDom(MarionetteTestCase):
+
+ def setUp(self):
+ super(TestShadowDom, self).setUp()
+ self.marionette.set_pref("dom.webcomponents.enabled", True)
+ self.marionette.navigate(self.marionette.absolute_url("test_shadow_dom.html"))
+
+ self.host = self.marionette.find_element(By.ID, "host")
+ self.marionette.switch_to_shadow_root(self.host)
+ self.button = self.marionette.find_element(By.ID, "button")
+
+ def tearDown(self):
+ self.marionette.clear_pref("dom.webcomponents.enabled")
+ super(TestShadowDom, self).tearDown()
+
+ def test_chrome_error(self):
+ with self.marionette.using_context("chrome"):
+ self.assertRaises(UnsupportedOperationException,
+ self.marionette.switch_to_shadow_root)
+
+ def test_shadow_dom(self):
+ # Button in shadow root should be actionable
+ self.button.click()
+
+ def test_shadow_dom_after_switch_away_from_shadow_root(self):
+ # Button in shadow root should be actionable
+ self.button.click()
+ self.marionette.switch_to_shadow_root()
+ # After switching back to top content, button should be stale
+ self.assertRaises(StaleElementException, self.button.click)
+
+ def test_shadow_dom_raises_stale_element_exception_when_button_remove(self):
+ self.marionette.execute_script(
+ 'document.getElementById("host").shadowRoot.getElementById("button").remove();')
+ # After removing button from shadow DOM, button should be stale
+ self.assertRaises(StaleElementException, self.button.click)
+
+ def test_shadow_dom_raises_stale_element_exception_when_host_removed(self):
+ self.marionette.execute_script('document.getElementById("host").remove();')
+ # After removing shadow DOM host element, button should be stale
+ self.assertRaises(StaleElementException, self.button.click)
+
+ def test_non_existent_shadow_dom(self):
+ # Jump back to top level content
+ self.marionette.switch_to_shadow_root()
+ # When no ShadowRoot is found, switch_to_shadow_root throws NoSuchElementException
+ self.assertRaises(NoSuchElementException, self.marionette.switch_to_shadow_root,
+ self.marionette.find_element(By.ID, "empty-host"))
+
+ def test_inner_shadow_dom(self):
+ # Button in shadow root should be actionable
+ self.button.click()
+ self.inner_host = self.marionette.find_element(By.ID, "inner-host")
+ self.marionette.switch_to_shadow_root(self.inner_host)
+ self.inner_button = self.marionette.find_element(By.ID, "inner-button")
+ # Nested nutton in nested shadow root should be actionable
+ self.inner_button.click()
+ self.marionette.switch_to_shadow_root()
+ # After jumping back to parent shadow root, button should again be actionable but inner
+ # button should now be stale
+ self.button.click()
+ self.assertRaises(StaleElementException, self.inner_button.click)
+ self.marionette.switch_to_shadow_root()
+ # After switching back to top content, both buttons should now be stale
+ self.assertRaises(StaleElementException, self.button.click)
+ self.assertRaises(StaleElementException, self.inner_button.click)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_chrome.js b/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_chrome.js
new file mode 100644
index 000000000..d5edffa67
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_chrome.js
@@ -0,0 +1,12 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+MARIONETTE_TIMEOUT = 1000;
+MARIONETTE_CONTEXT = 'chrome';
+
+is(2, 2, "test for is()");
+isnot(2, 3, "test for isnot()");
+ok(2 == 2, "test for ok()");
+finish();
+
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_fail.js b/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_fail.js
new file mode 100644
index 000000000..16d9aea59
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_fail.js
@@ -0,0 +1,16 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+MARIONETTE_TIMEOUT = 1000;
+
+/* this test will fail */
+
+setTimeout(function() {
+ is(1, 2, "is(1,2) should fail", TEST_UNEXPECTED_FAIL, TEST_PASS);
+ finish();
+}, 100);
+isnot(1, 1, "isnot(1,1) should fail", TEST_UNEXPECTED_FAIL, TEST_PASS);
+ok(1 == 2, "ok(1==2) should fail", TEST_UNEXPECTED_FAIL, TEST_PASS);
+
+
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_pass.js b/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_pass.js
new file mode 100644
index 000000000..93ee67619
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_pass.js
@@ -0,0 +1,12 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+MARIONETTE_TIMEOUT = 1000;
+
+is(2, 2, "test for is()");
+isnot(2, 3, "test for isnot()");
+ok(2 == 2, "test for ok()");
+
+setTimeout(finish, 100);
+
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_sanity.py b/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_sanity.py
new file mode 100644
index 000000000..8f9728561
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_sanity.py
@@ -0,0 +1,107 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_harness import MarionetteTestCase
+
+
+class SimpletestSanityTest(MarionetteTestCase):
+ callFinish = "return finish();"
+
+ def run_sync(self, test):
+ return self.marionette.execute_js_script(test, async=False)
+
+ def run_async(self, test):
+ return self.marionette.execute_js_script(test)
+
+ def test_is(self):
+ def runtests():
+ sentFail1 = "is(true, false, 'isTest1', TEST_UNEXPECTED_FAIL, TEST_PASS);" + self.callFinish
+ sentFail2 = "is(true, false, 'isTest2', TEST_UNEXPECTED_FAIL, TEST_PASS);" + self.callFinish
+ sentPass1 = "is(true, true, 'isTest3');" + self.callFinish
+ sentPass2 = "is(true, true, 'isTest4');" + self.callFinish
+
+ self.assertEqual(1, len(self.run_sync(sentFail1)["failures"]))
+ self.assertEqual(0, self.run_sync(sentFail2)["passed"])
+ self.assertEqual(1, self.run_sync(sentPass1)["passed"])
+ self.assertEqual(0, len(self.run_sync(sentPass2)["failures"]))
+
+ self.marionette.timeout.script = 1
+ self.assertEqual(1, len(self.run_async(sentFail1)["failures"]))
+ self.assertEqual(0, self.run_async(sentFail2)["passed"])
+ self.assertEqual(1, self.run_async(sentPass1)["passed"])
+ self.assertEqual(0, len(self.run_async(sentPass2)["failures"]))
+
+ self.marionette.set_context("content")
+ runtests()
+ self.marionette.set_context("chrome")
+ runtests()
+
+ def test_isnot(self):
+ def runtests():
+ sentFail1 = "isnot(true, true, 'isnotTest3', TEST_UNEXPECTED_FAIL, TEST_PASS);" + self.callFinish
+ sentFail2 = "isnot(true, true, 'isnotTest4', TEST_UNEXPECTED_FAIL, TEST_PASS);" + self.callFinish
+ sentPass1 = "isnot(true, false, 'isnotTest1');" + self.callFinish
+ sentPass2 = "isnot(true, false, 'isnotTest2');" + self.callFinish
+
+ self.assertEqual(1, len(self.run_sync(sentFail1)["failures"]));
+ self.assertEqual(0, self.run_sync(sentFail2)["passed"]);
+ self.assertEqual(0, len(self.run_sync(sentPass1)["failures"]));
+ self.assertEqual(1, self.run_sync(sentPass2)["passed"]);
+
+ self.marionette.timeout.script = 1
+ self.assertEqual(1, len(self.run_async(sentFail1)["failures"]));
+ self.assertEqual(0, self.run_async(sentFail2)["passed"]);
+ self.assertEqual(0, len(self.run_async(sentPass1)["failures"]));
+ self.assertEqual(1, self.run_async(sentPass2)["passed"]);
+
+ self.marionette.set_context("content")
+ runtests()
+ self.marionette.set_context("chrome")
+ runtests()
+
+ def test_ok(self):
+ def runtests():
+ sentFail1 = "ok(1==2, 'testOk1', TEST_UNEXPECTED_FAIL, TEST_PASS);" + self.callFinish
+ sentFail2 = "ok(1==2, 'testOk2', TEST_UNEXPECTED_FAIL, TEST_PASS);" + self.callFinish
+ sentPass1 = "ok(1==1, 'testOk3');" + self.callFinish
+ sentPass2 = "ok(1==1, 'testOk4');" + self.callFinish
+
+ self.assertEqual(1, len(self.run_sync(sentFail1)["failures"]));
+ self.assertEqual(0, self.run_sync(sentFail2)["passed"]);
+ self.assertEqual(0, len(self.run_sync(sentPass1)["failures"]));
+ self.assertEqual(1, self.run_sync(sentPass2)["passed"]);
+
+ self.marionette.timeout.script = 1
+ self.assertEqual(1, len(self.run_async(sentFail1)["failures"]));
+ self.assertEqual(0, self.run_async(sentFail2)["passed"]);
+ self.assertEqual(0, len(self.run_async(sentPass1)["failures"]));
+ self.assertEqual(1, self.run_async(sentPass2)["passed"]);
+
+ self.marionette.set_context("content")
+ runtests()
+ self.marionette.set_context("chrome")
+ runtests()
+
+ def test_todo(self):
+ def runtests():
+ sentFail1 = "todo(1==1, 'testTodo1', TEST_UNEXPECTED_PASS, TEST_KNOWN_FAIL);" + self.callFinish
+ sentFail2 = "todo(1==1, 'testTodo2', TEST_UNEXPECTED_PASS, TEST_KNOWN_FAIL);" + self.callFinish
+ sentPass1 = "todo(1==2, 'testTodo3');" + self.callFinish
+ sentPass2 = "todo(1==2, 'testTodo4');" + self.callFinish
+
+ self.assertEqual(1, len(self.run_sync(sentFail1)["unexpectedSuccesses"]));
+ self.assertEqual(0, len(self.run_sync(sentFail2)["expectedFailures"]));
+ self.assertEqual(0, len(self.run_sync(sentPass1)["unexpectedSuccesses"]));
+ self.assertEqual(1, len(self.run_sync(sentPass2)["expectedFailures"]));
+
+ self.marionette.timeout.script = 1
+ self.assertEqual(1, len(self.run_async(sentFail1)["unexpectedSuccesses"]));
+ self.assertEqual(0, len(self.run_async(sentFail2)["expectedFailures"]));
+ self.assertEqual(0, len(self.run_async(sentPass1)["unexpectedSuccesses"]));
+ self.assertEqual(1, len(self.run_async(sentPass2)["expectedFailures"]));
+
+ self.marionette.set_context("content")
+ runtests()
+ self.marionette.set_context("chrome")
+ runtests()
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_timeout.js b/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_timeout.js
new file mode 100644
index 000000000..9792a936a
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_simpletest_timeout.js
@@ -0,0 +1,16 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+MARIONETTE_TIMEOUT = 100;
+
+/* this test will timeout */
+
+function do_test() {
+ is(1, 1);
+ isnot(1, 2);
+ ok(1 == 1);
+ finish();
+}
+
+setTimeout(do_test, 1000);
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_single_finger_desktop.py b/testing/marionette/harness/marionette_harness/tests/unit/test_single_finger_desktop.py
new file mode 100644
index 000000000..8ac80c3c5
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_single_finger_desktop.py
@@ -0,0 +1,123 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+import sys
+
+from marionette_driver.errors import MarionetteException
+from marionette_driver import Actions, By
+
+from marionette_harness import MarionetteTestCase, skip
+
+# add this directory to the path
+sys.path.append(os.path.dirname(__file__))
+
+from single_finger_functions import (
+ chain, chain_flick, context_menu, double_tap,
+ long_press_action, long_press_on_xy_action,
+ move_element, move_element_offset, press_release, single_tap, wait,
+ wait_with_value
+)
+
+
+class testSingleFingerMouse(MarionetteTestCase):
+ def setUp(self):
+ super(MarionetteTestCase, self).setUp()
+ # set context menu related preferences needed for some tests
+ self.marionette.set_context("chrome")
+ self.enabled = self.marionette.execute_script("""
+let prefs = Components.classes["@mozilla.org/preferences-service;1"]
+ .getService(Components.interfaces.nsIPrefBranch);
+let value = false;
+try {
+ value = prefs.getBoolPref("ui.click_hold_context_menus");
+}
+catch (e) {}
+prefs.setBoolPref("ui.click_hold_context_menus", true);
+return value;
+""")
+ self.wait_time = self.marionette.execute_script("""
+let prefs = Components.classes["@mozilla.org/preferences-service;1"]
+ .getService(Components.interfaces.nsIPrefBranch);
+let value = 750;
+try {
+ value = prefs.getIntPref("ui.click_hold_context_menus.delay");
+}
+catch (e) {}
+prefs.setIntPref("ui.click_hold_context_menus.delay", value);
+return value;
+""")
+ self.marionette.set_context("content")
+
+ def tearDown(self):
+ self.marionette.set_context("chrome")
+ self.marionette.execute_script(
+ """
+let prefs = Components.classes["@mozilla.org/preferences-service;1"]
+ .getService(Components.interfaces.nsIPrefBranch);
+prefs.setBoolPref("ui.click_hold_context_menus", arguments[0]);
+""", [self.enabled])
+ self.marionette.execute_script(
+ """
+let prefs = Components.classes["@mozilla.org/preferences-service;1"]
+ .getService(Components.interfaces.nsIPrefBranch);
+prefs.setIntPref("ui.click_hold_context_menus.delay", arguments[0]);
+""", [self.wait_time])
+ self.marionette.set_context("content")
+ super(MarionetteTestCase, self).tearDown()
+
+ def test_press_release(self):
+ press_release(self.marionette, 1, self.wait_for_condition, "button1-mousemove-mousedown-mouseup-click")
+
+ def test_press_release_twice(self):
+ press_release(self.marionette, 2, self.wait_for_condition, "button1-mousemove-mousedown-mouseup-click-mousemove-mousedown-mouseup-click")
+
+ def test_move_element(self):
+ move_element(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown", "button2-mousemove-mouseup")
+
+ def test_move_by_offset(self):
+ move_element_offset(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown", "button2-mousemove-mouseup")
+
+ def test_wait(self):
+ wait(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown-mouseup-click")
+
+ def test_wait_with_value(self):
+ wait_with_value(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown-mouseup-click")
+
+ @skip("Bug 1191066")
+ def test_context_menu(self):
+ context_menu(self.marionette, self.wait_for_condition,
+ "button1-mousemove-mousedown-contextmenu",
+ "button1-mousemove-mousedown-contextmenu-mouseup-click")
+
+ @skip("Bug 1191066")
+ def test_long_press_action(self):
+ long_press_action(self.marionette, self.wait_for_condition,
+ "button1-mousemove-mousedown-contextmenu-mouseup-click")
+
+ @skip("Bug 1191066")
+ def test_long_press_on_xy_action(self):
+ long_press_on_xy_action(self.marionette, self.wait_for_condition,
+ "button1-mousemove-mousedown-contextmenu-mouseup-click")
+
+ @skip("Bug 865334")
+ def test_long_press_fail(self):
+ testAction = self.marionette.absolute_url("testAction.html")
+ self.marionette.navigate(testAction)
+ button = self.marionette.find_element(By.ID, "button1Copy")
+ action = Actions(self.marionette)
+ action.press(button).long_press(button, 5)
+ self.assertRaises(MarionetteException, action.perform)
+
+ def test_chain(self):
+ chain(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown", "delayed-mousemove-mouseup")
+
+ def test_chain_flick(self):
+ chain_flick(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown-mousemove", "buttonFlick-mousemove-mouseup")
+
+ def test_single_tap(self):
+ single_tap(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown-mouseup-click")
+
+ def test_double_tap(self):
+ double_tap(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown-mouseup-click-mousemove-mousedown-mouseup-click")
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_skip_setup.py b/testing/marionette/harness/marionette_harness/tests/unit/test_skip_setup.py
new file mode 100644
index 000000000..9a0432fb7
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_skip_setup.py
@@ -0,0 +1,35 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_harness import MarionetteTestCase, SkipTest
+
+
+class TestSetUpSkipped(MarionetteTestCase):
+
+ testVar = {'test':'SkipTest'}
+
+ def setUp(self):
+ try:
+ self.testVar['email']
+ except KeyError:
+ raise SkipTest('email key not present in dict, skip ...')
+ MarionetteTestCase.setUp(self)
+
+ def test_assert(self):
+ assert True
+
+class TestSetUpNotSkipped(MarionetteTestCase):
+
+ testVar = {'test':'SkipTest'}
+
+ def setUp(self):
+ try:
+ self.testVar['test']
+ except KeyError:
+ raise SkipTest('email key not present in dict, skip ...')
+ MarionetteTestCase.setUp(self)
+
+ def test_assert(self):
+ assert True
+
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_switch_frame.py b/testing/marionette/harness/marionette_harness/tests/unit/test_switch_frame.py
new file mode 100644
index 000000000..18eb34169
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_switch_frame.py
@@ -0,0 +1,183 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+from marionette_driver.errors import (
+ JavascriptException,
+ NoSuchFrameException,
+)
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestSwitchFrame(MarionetteTestCase):
+ def test_switch_simple(self):
+ start_url = "test_iframe.html"
+ verify_title = "Marionette IFrame Test"
+ test_html = self.marionette.absolute_url(start_url)
+ self.marionette.navigate(test_html)
+ self.assertEqual(self.marionette.get_active_frame(), None)
+ frame = self.marionette.find_element(By.ID, "test_iframe")
+ self.marionette.switch_to_frame(frame)
+ self.assertTrue(start_url in self.marionette.get_url())
+ inner_frame_element = self.marionette.get_active_frame()
+ # test that we can switch back to main frame, then switch back to the
+ # inner frame with the value we got from get_active_frame
+ self.marionette.switch_to_frame()
+ self.assertEqual(verify_title, self.marionette.title)
+ self.marionette.switch_to_frame(inner_frame_element)
+ self.assertTrue(start_url in self.marionette.get_url())
+
+ def test_switch_nested(self):
+ start_url = "test_nested_iframe.html"
+ verify_title = "Marionette IFrame Test"
+ test_html = self.marionette.absolute_url(start_url)
+ self.marionette.navigate(test_html)
+ frame = self.marionette.find_element(By.ID, "test_iframe")
+ self.assertEqual(self.marionette.get_active_frame(), None)
+ self.marionette.switch_to_frame(frame)
+ self.assertTrue(start_url in self.marionette.get_url())
+ inner_frame_element = self.marionette.get_active_frame()
+ # test that we can switch back to main frame, then switch back to the
+ # inner frame with the value we got from get_active_frame
+ self.marionette.switch_to_frame()
+ self.assertEqual(verify_title, self.marionette.title)
+ self.marionette.switch_to_frame(inner_frame_element)
+ self.assertTrue(start_url in self.marionette.get_url())
+ inner_frame = self.marionette.find_element(By.ID, 'inner_frame')
+ self.marionette.switch_to_frame(inner_frame)
+ self.assertTrue(start_url in self.marionette.get_url())
+ self.marionette.switch_to_frame() # go back to main frame
+ self.assertTrue(start_url in self.marionette.get_url())
+ #test that we're using the right window object server-side
+ self.assertTrue("test_nested_iframe.html" in self.marionette.execute_script("return window.location.href;"))
+
+ def test_stack_trace(self):
+ start_url = "test_iframe.html"
+ verify_title = "Marionette IFrame Test"
+ test_html = self.marionette.absolute_url(start_url)
+ self.marionette.navigate(test_html)
+ frame = self.marionette.find_element(By.ID, "test_iframe")
+ self.assertEqual(self.marionette.get_active_frame(), None)
+ self.marionette.switch_to_frame(frame)
+ self.assertTrue(start_url in self.marionette.get_url())
+ inner_frame_element = self.marionette.get_active_frame()
+ # test that we can switch back to main frame, then switch back to the
+ # inner frame with the value we got from get_active_frame
+ self.marionette.switch_to_frame()
+ self.assertEqual(verify_title, self.marionette.title)
+ self.marionette.switch_to_frame(inner_frame_element)
+ self.assertTrue(start_url in self.marionette.get_url())
+
+ try:
+ self.marionette.execute_async_script("foo();")
+ except JavascriptException as e:
+ self.assertTrue("foo" in e.message)
+
+ def test_should_be_able_to_carry_on_working_if_the_frame_is_deleted_from_under_us(self):
+ test_html = self.marionette.absolute_url("deletingFrame.html")
+ self.marionette.navigate(test_html)
+
+ self.marionette.switch_to_frame(self.marionette.find_element(By.ID,
+ 'iframe1'))
+ killIframe = self.marionette.find_element(By.ID, "killIframe")
+ killIframe.click()
+ self.marionette.switch_to_frame()
+
+ self.assertEqual(0, len(self.marionette.find_elements(By.ID, "iframe1")))
+
+ addIFrame = self.marionette.find_element(By.ID, "addBackFrame")
+ addIFrame.click()
+ self.marionette.find_element(By.ID, "iframe1")
+
+ self.marionette.switch_to_frame(self.marionette.find_element(By.ID,
+ "iframe1"))
+
+ self.marionette.find_element(By.ID, "checkbox")
+
+ def test_should_allow_a_user_to_switch_from_an_iframe_back_to_the_main_content_of_the_page(self):
+ test_iframe = self.marionette.absolute_url("test_iframe.html")
+ self.marionette.navigate(test_iframe)
+ self.marionette.switch_to_frame(0)
+ self.marionette.switch_to_default_content()
+ header = self.marionette.find_element(By.ID, "iframe_page_heading")
+ self.assertEqual(header.text, "This is the heading")
+
+ def test_should_be_able_to_switch_to_a_frame_by_its_index(self):
+ test_html = self.marionette.absolute_url("frameset.html")
+ self.marionette.navigate(test_html)
+ self.marionette.switch_to_frame(2)
+ element = self.marionette.find_element(By.ID, "email")
+ self.assertEquals("email", element.get_attribute("type"))
+
+ def test_should_be_able_to_switch_to_a_frame_using_a_previously_located_element(self):
+ test_html = self.marionette.absolute_url("frameset.html")
+ self.marionette.navigate(test_html)
+ frame = self.marionette.find_element(By.NAME, "third")
+ self.marionette.switch_to_frame(frame)
+
+ element = self.marionette.find_element(By.ID, "email")
+ self.assertEquals("email", element.get_attribute("type"))
+
+ def test_switch_to_frame_with_out_of_bounds_index(self):
+ self.marionette.navigate(self.marionette.absolute_url("test_iframe.html"))
+ count = self.marionette.execute_script("return window.frames.length;")
+ self.assertRaises(NoSuchFrameException, self.marionette.switch_to_frame, count)
+
+ def test_switch_to_frame_with_negative_index(self):
+ self.marionette.navigate(self.marionette.absolute_url("test_iframe.html"))
+ self.assertRaises(NoSuchFrameException, self.marionette.switch_to_frame, -1)
+
+ def test_switch_to_parent_frame(self):
+ frame_html = self.marionette.absolute_url("frameset.html")
+ self.marionette.navigate(frame_html)
+ frame = self.marionette.find_element(By.NAME, "third")
+ self.marionette.switch_to_frame(frame)
+
+ # If we don't find the following element we aren't on the right page
+ self.marionette.find_element(By.ID, "checky")
+ form_page_title = self.marionette.execute_script("return document.title")
+ self.assertEqual("We Leave From Here", form_page_title)
+
+ self.marionette.switch_to_parent_frame()
+
+ current_page_title = self.marionette.execute_script("return document.title")
+ self.assertEqual("Unique title", current_page_title)
+
+ def test_switch_to_parent_frame_from_default_context_is_a_noop(self):
+ formpage = self.marionette.absolute_url("formPage.html")
+ self.marionette.navigate(formpage)
+
+ self.marionette.switch_to_parent_frame()
+
+ form_page_title = self.marionette.execute_script("return document.title")
+ self.assertEqual("We Leave From Here", form_page_title)
+
+ def test_should_be_able_to_switch_to_parent_from_second_level(self):
+ frame_html = self.marionette.absolute_url("frameset.html")
+ self.marionette.navigate(frame_html)
+ frame = self.marionette.find_element(By.NAME, "fourth")
+ self.marionette.switch_to_frame(frame)
+
+ second_level = self.marionette.find_element(By.NAME, "child1")
+ self.marionette.switch_to_frame(second_level)
+ self.marionette.find_element(By.NAME, "myCheckBox")
+
+ self.marionette.switch_to_parent_frame()
+
+ second_level = self.marionette.find_element(By.NAME, "child1")
+
+ def test_should_be_able_to_switch_to_parent_from_iframe(self):
+ frame_html = self.marionette.absolute_url("test_iframe.html")
+ self.marionette.navigate(frame_html)
+ frame = self.marionette.find_element(By.ID, "test_iframe")
+ self.marionette.switch_to_frame(frame)
+
+ current_page_title = self.marionette.execute_script("return document.title")
+ self.assertEqual("Marionette Test", current_page_title)
+
+ self.marionette.switch_to_parent_frame()
+
+ parent_page_title = self.marionette.execute_script("return document.title")
+ self.assertEqual("Marionette IFrame Test", parent_page_title)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_switch_frame_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_switch_frame_chrome.py
new file mode 100644
index 000000000..03c13026e
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_switch_frame_chrome.py
@@ -0,0 +1,56 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.errors import JavascriptException
+
+from marionette_harness import MarionetteTestCase, WindowManagerMixin
+
+
+class TestSwitchFrameChrome(WindowManagerMixin, MarionetteTestCase):
+
+ def setUp(self):
+ super(TestSwitchFrameChrome, self).setUp()
+ self.marionette.set_context("chrome")
+
+ def open_window_with_js():
+ self.marionette.execute_script("""
+ window.open('chrome://marionette/content/test.xul',
+ 'foo', 'chrome,centerscreen');
+ """)
+
+ new_window = self.open_window(trigger=open_window_with_js)
+ self.marionette.switch_to_window(new_window)
+ self.assertNotEqual(self.start_window, self.marionette.current_chrome_window_handle)
+
+ def tearDown(self):
+ self.close_all_windows()
+ super(TestSwitchFrameChrome, self).tearDown()
+
+ def test_switch_simple(self):
+ self.assertIn("test.xul", self.marionette.get_url(), "Initial navigation has failed")
+ self.marionette.switch_to_frame(0)
+ self.assertIn("test2.xul", self.marionette.get_url(),"Switching by index failed")
+ self.marionette.switch_to_frame()
+ self.assertEqual(None, self.marionette.get_active_frame(), "Switiching by null failed")
+ self.assertIn("test.xul", self.marionette.get_url(), "Switching by null failed")
+ self.marionette.switch_to_frame("iframe")
+ self.assertIn("test2.xul", self.marionette.get_url(), "Switching by name failed")
+ self.marionette.switch_to_frame()
+ self.assertIn("test.xul", self.marionette.get_url(), "Switching by null failed")
+ self.marionette.switch_to_frame("iframename")
+ self.assertIn("test2.xul", self.marionette.get_url(), "Switching by name failed")
+ iframe_element = self.marionette.get_active_frame()
+ self.marionette.switch_to_frame()
+ self.assertIn("test.xul", self.marionette.get_url(), "Switching by null failed")
+ self.marionette.switch_to_frame(iframe_element)
+ self.assertIn("test2.xul", self.marionette.get_url(), "Switching by element failed")
+
+ def test_stack_trace(self):
+ self.assertIn("test.xul", self.marionette.get_url(), "Initial navigation has failed")
+ self.marionette.switch_to_frame(0)
+ self.assertRaises(JavascriptException, self.marionette.execute_async_script, "foo();")
+ try:
+ self.marionette.execute_async_script("foo();")
+ except JavascriptException as e:
+ self.assertIn("foo", e.message)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_switch_remote_frame.py b/testing/marionette/harness/marionette_harness/tests/unit/test_switch_remote_frame.py
new file mode 100644
index 000000000..07ddeef2a
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_switch_remote_frame.py
@@ -0,0 +1,118 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+
+from marionette_harness import MarionetteTestCase
+
+
+OOP_BY_DEFAULT = "dom.ipc.browser_frames.oop_by_default"
+BROWSER_FRAMES_ENABLED = "dom.mozBrowserFramesEnabled"
+
+
+class TestSwitchRemoteFrame(MarionetteTestCase):
+ def setUp(self):
+ super(TestSwitchRemoteFrame, self).setUp()
+ with self.marionette.using_context('chrome'):
+ self.oop_by_default = self.marionette.get_pref(OOP_BY_DEFAULT)
+ self.mozBrowserFramesEnabled = self.marionette.get_pref(BROWSER_FRAMES_ENABLED)
+ self.marionette.set_pref(OOP_BY_DEFAULT, True)
+ self.marionette.set_pref(BROWSER_FRAMES_ENABLED, True)
+
+ self.multi_process_browser = self.marionette.execute_script("""
+ try {
+ return Services.appinfo.browserTabsRemoteAutostart;
+ } catch (e) {
+ return false;
+ }""")
+
+ def tearDown(self):
+ with self.marionette.using_context("chrome"):
+ if self.oop_by_default is None:
+ self.marionette.clear_pref(OOP_BY_DEFAULT)
+ else:
+ self.marionette.set_pref(OOP_BY_DEFAULT, self.oop_by_default)
+
+ if self.mozBrowserFramesEnabled is None:
+ self.marionette.clear_pref(BROWSER_FRAMES_ENABLED)
+ else:
+ self.marionette.set_pref(BROWSER_FRAMES_ENABLED, self.mozBrowserFramesEnabled)
+
+ @property
+ def is_main_process(self):
+ return self.marionette.execute_script("""
+ return Components.classes["@mozilla.org/xre/app-info;1"].
+ getService(Components.interfaces.nsIXULRuntime).
+ processType == Components.interfaces.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+ """, sandbox="system")
+
+ def test_remote_frame(self):
+ self.marionette.navigate(self.marionette.absolute_url("test.html"))
+ self.marionette.push_permission('browser', True)
+ self.marionette.execute_script("""
+ let iframe = document.createElement("iframe");
+ iframe.setAttribute('mozbrowser', true);
+ iframe.setAttribute('remote', true);
+ iframe.id = "remote_iframe";
+ iframe.style.height = "100px";
+ iframe.style.width = "100%%";
+ iframe.src = "{}";
+ document.body.appendChild(iframe);
+ """.format(self.marionette.absolute_url("test.html")))
+ remote_iframe = self.marionette.find_element(By.ID, "remote_iframe")
+ self.marionette.switch_to_frame(remote_iframe)
+ main_process = self.is_main_process
+ self.assertFalse(main_process)
+
+ def test_remote_frame_revisit(self):
+ # test if we can revisit a remote frame (this takes a different codepath)
+ self.marionette.navigate(self.marionette.absolute_url("test.html"))
+ self.marionette.push_permission('browser', True)
+ self.marionette.execute_script("""
+ let iframe = document.createElement("iframe");
+ iframe.setAttribute('mozbrowser', true);
+ iframe.setAttribute('remote', true);
+ iframe.id = "remote_iframe";
+ iframe.style.height = "100px";
+ iframe.style.width = "100%%";
+ iframe.src = "{}";
+ document.body.appendChild(iframe);
+ """.format(self.marionette.absolute_url("test.html")))
+ self.marionette.switch_to_frame(self.marionette.find_element(By.ID,
+ "remote_iframe"))
+ main_process = self.is_main_process
+ self.assertFalse(main_process)
+ self.marionette.switch_to_frame()
+ main_process = self.is_main_process
+ should_be_main_process = not self.multi_process_browser
+ self.assertEqual(main_process, should_be_main_process)
+ self.marionette.switch_to_frame(self.marionette.find_element(By.ID,
+ "remote_iframe"))
+ main_process = self.is_main_process
+ self.assertFalse(main_process)
+
+ def test_we_can_switch_to_a_remote_frame_by_index(self):
+ # test if we can revisit a remote frame (this takes a different codepath)
+ self.marionette.navigate(self.marionette.absolute_url("test.html"))
+ self.marionette.push_permission('browser', True)
+ self.marionette.execute_script("""
+ let iframe = document.createElement("iframe");
+ iframe.setAttribute('mozbrowser', true);
+ iframe.setAttribute('remote', true);
+ iframe.id = "remote_iframe";
+ iframe.style.height = "100px";
+ iframe.style.width = "100%%";
+ iframe.src = "{}";
+ document.body.appendChild(iframe);
+ """.format(self.marionette.absolute_url("test.html")))
+ self.marionette.switch_to_frame(0)
+ main_process = self.is_main_process
+ self.assertFalse(main_process)
+ self.marionette.switch_to_frame()
+ main_process = self.is_main_process
+ should_be_main_process = not self.multi_process_browser
+ self.assertEqual(main_process, should_be_main_process)
+ self.marionette.switch_to_frame(0)
+ main_process = self.is_main_process
+ self.assertFalse(main_process)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_switch_window_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_switch_window_chrome.py
new file mode 100644
index 000000000..0ad63b6ce
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_switch_window_chrome.py
@@ -0,0 +1,124 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+import sys
+from unittest import skipIf
+
+from marionette_driver import By
+
+# add this directory to the path
+sys.path.append(os.path.dirname(__file__))
+
+from test_switch_window_content import TestSwitchToWindowContent
+
+
+class TestSwitchWindowChrome(TestSwitchToWindowContent):
+
+ def setUp(self):
+ super(TestSwitchWindowChrome, self).setUp()
+
+ self.marionette.set_context("chrome")
+
+ def tearDown(self):
+ self.close_all_windows()
+
+ super(TestSwitchWindowChrome, self).tearDown()
+
+ def open_window_in_background(self):
+ with self.marionette.using_context("chrome"):
+ self.marionette.execute_script("""
+ window.open("about:blank", null, "location=1,toolbar=1");
+ window.focus();
+ """)
+
+ def open_window_in_foreground(self):
+ with self.marionette.using_context("content"):
+ self.marionette.navigate(self.test_page)
+ link = self.marionette.find_element(By.ID, "new-window")
+ link.click()
+
+ @skipIf(sys.platform.startswith("linux"),
+ "Bug 1335457 - Fails to open a background window on Linux")
+ def test_switch_tabs_for_new_background_window_without_focus_change(self):
+ # Bug 1334981 - with testmode enabled getMostRecentWindow detects the wrong window
+ with self.marionette.using_prefs({"focusmanager.testmode": False}):
+ # Open an addition tab in the original window so we can better check
+ # the selected index in thew new window to be opened.
+ second_tab = self.open_tab(trigger=self.open_tab_in_foreground)
+ self.marionette.switch_to_window(second_tab, focus=True)
+ second_tab_index = self.get_selected_tab_index()
+ self.assertNotEqual(second_tab_index, self.selected_tab_index)
+
+ # Opens a new background window, but we are interested in the tab
+ tab_in_new_window = self.open_tab(trigger=self.open_window_in_background)
+ self.assertEqual(self.marionette.current_window_handle, second_tab)
+ self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
+ self.assertEqual(self.get_selected_tab_index(), second_tab_index)
+ with self.marionette.using_context("content"):
+ self.assertEqual(self.marionette.get_url(), self.empty_page)
+
+ # Switch to the tab in the new window but don't focus it
+ self.marionette.switch_to_window(tab_in_new_window, focus=False)
+ self.assertEqual(self.marionette.current_window_handle, tab_in_new_window)
+ self.assertNotEqual(self.marionette.current_chrome_window_handle, self.start_window)
+ self.assertEqual(self.get_selected_tab_index(), second_tab_index)
+ with self.marionette.using_context("content"):
+ self.assertEqual(self.marionette.get_url(), "about:blank")
+
+ def test_switch_tabs_for_new_foreground_window_with_focus_change(self):
+ # Open an addition tab in the original window so we can better check
+ # the selected index in thew new window to be opened.
+ second_tab = self.open_tab(trigger=self.open_tab_in_foreground)
+ self.marionette.switch_to_window(second_tab, focus=True)
+ second_tab_index = self.get_selected_tab_index()
+ self.assertNotEqual(second_tab_index, self.selected_tab_index)
+
+ # Opens a new window, but we are interested in the tab
+ tab_in_new_window = self.open_tab(trigger=self.open_window_in_foreground)
+ self.assertEqual(self.marionette.current_window_handle, second_tab)
+ self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
+ self.assertNotEqual(self.get_selected_tab_index(), second_tab_index)
+ with self.marionette.using_context("content"):
+ self.assertEqual(self.marionette.get_url(), self.test_page)
+
+ self.marionette.switch_to_window(tab_in_new_window)
+ self.assertEqual(self.marionette.current_window_handle, tab_in_new_window)
+ self.assertNotEqual(self.marionette.current_chrome_window_handle, self.start_window)
+ self.assertNotEqual(self.get_selected_tab_index(), second_tab_index)
+ with self.marionette.using_context("content"):
+ self.assertEqual(self.marionette.get_url(), self.empty_page)
+
+ self.marionette.switch_to_window(second_tab, focus=True)
+ self.assertEqual(self.marionette.current_window_handle, second_tab)
+ self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
+ # Bug 1335085 - The focus doesn't change even as requested so.
+ # self.assertEqual(self.get_selected_tab_index(), second_tab_index)
+ with self.marionette.using_context("content"):
+ self.assertEqual(self.marionette.get_url(), self.test_page)
+
+ def test_switch_tabs_for_new_foreground_window_without_focus_change(self):
+ # Open an addition tab in the original window so we can better check
+ # the selected index in thew new window to be opened.
+ second_tab = self.open_tab(trigger=self.open_tab_in_foreground)
+ self.marionette.switch_to_window(second_tab, focus=True)
+ second_tab_index = self.get_selected_tab_index()
+ self.assertNotEqual(second_tab_index, self.selected_tab_index)
+
+ # Opens a new window, but we are interested in the tab which automatically
+ # gets the focus.
+ self.open_tab(trigger=self.open_window_in_foreground)
+ self.assertEqual(self.marionette.current_window_handle, second_tab)
+ self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
+ self.assertNotEqual(self.get_selected_tab_index(), second_tab_index)
+ with self.marionette.using_context("content"):
+ self.assertEqual(self.marionette.get_url(), self.test_page)
+
+ # Switch to the second tab in the first window, but don't focus it.
+ self.marionette.switch_to_window(second_tab, focus=False)
+ self.assertEqual(self.marionette.current_window_handle, second_tab)
+ self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
+ self.assertNotEqual(self.get_selected_tab_index(), second_tab_index)
+ with self.marionette.using_context("content"):
+ self.assertEqual(self.marionette.get_url(), self.test_page)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_switch_window_content.py b/testing/marionette/harness/marionette_harness/tests/unit/test_switch_window_content.py
new file mode 100644
index 000000000..fbab1898f
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_switch_window_content.py
@@ -0,0 +1,171 @@
+# This Source Code Form is subject to the terms of the Mozilla ublic
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver import Actions, By, Wait
+from marionette_driver.keys import Keys
+
+from marionette_harness import MarionetteTestCase, skip_if_mobile, WindowManagerMixin
+
+
+class TestSwitchToWindowContent(WindowManagerMixin, MarionetteTestCase):
+
+ def setUp(self):
+ super(TestSwitchToWindowContent, self).setUp()
+
+ if self.marionette.session_capabilities["platformName"] == "darwin":
+ self.mod_key = Keys.META
+ else:
+ self.mod_key = Keys.CONTROL
+
+ self.empty_page = self.marionette.absolute_url("empty.html")
+ self.test_page = self.marionette.absolute_url("windowHandles.html")
+
+ self.selected_tab_index = self.get_selected_tab_index()
+
+ with self.marionette.using_context("content"):
+ self.marionette.navigate(self.test_page)
+
+ def tearDown(self):
+ self.close_all_tabs()
+
+ super(TestSwitchToWindowContent, self).tearDown()
+
+ def get_selected_tab_index(self):
+ with self.marionette.using_context("chrome"):
+ return self.marionette.execute_script("""
+ Components.utils.import("resource://gre/modules/AppConstants.jsm");
+
+ let win = null;
+
+ if (AppConstants.MOZ_APP_NAME == "fennec") {
+ Components.utils.import("resource://gre/modules/Services.jsm");
+ win = Services.wm.getMostRecentWindow("navigator:browser");
+ } else {
+ Components.utils.import("resource:///modules/RecentWindow.jsm");
+ win = RecentWindow.getMostRecentBrowserWindow();
+ }
+
+ let tabBrowser = null;
+
+ // Fennec
+ if (win.BrowserApp) {
+ tabBrowser = win.BrowserApp;
+
+ // Firefox
+ } else if (win.gBrowser) {
+ tabBrowser = win.gBrowser;
+
+ } else {
+ return null;
+ }
+
+ for (let i = 0; i < tabBrowser.tabs.length; i++) {
+ if (tabBrowser.tabs[i] == tabBrowser.selectedTab) {
+ return i;
+ }
+ }
+ """)
+
+ def open_tab_in_background(self):
+ with self.marionette.using_context("content"):
+ link = self.marionette.find_element(By.ID, "new-tab")
+
+ action = Actions(self.marionette)
+ action.key_down(self.mod_key).click(link).perform()
+
+ def open_tab_in_foreground(self):
+ with self.marionette.using_context("content"):
+ link = self.marionette.find_element(By.ID, "new-tab")
+ link.click()
+
+ def test_switch_tabs_with_focus_change(self):
+ new_tab = self.open_tab(self.open_tab_in_foreground)
+ self.assertEqual(self.marionette.current_window_handle, self.start_tab)
+ self.assertNotEqual(self.get_selected_tab_index(), self.selected_tab_index)
+ with self.marionette.using_context("content"):
+ self.assertEqual(self.marionette.get_url(), self.test_page)
+
+ self.marionette.switch_to_window(new_tab)
+ self.assertEqual(self.marionette.current_window_handle, new_tab)
+ self.assertNotEqual(self.get_selected_tab_index(), self.selected_tab_index)
+
+ with self.marionette.using_context("content"):
+ Wait(self.marionette).until(
+ lambda _: self.marionette.get_url() == self.empty_page,
+ message="{} has been loaded in the newly opened tab.".format(self.empty_page))
+
+ self.marionette.switch_to_window(self.start_tab, focus=True)
+ self.assertEqual(self.marionette.current_window_handle, self.start_tab)
+ self.assertEqual(self.get_selected_tab_index(), self.selected_tab_index)
+ with self.marionette.using_context("content"):
+ self.assertEqual(self.marionette.get_url(), self.test_page)
+
+ self.marionette.switch_to_window(new_tab)
+ self.marionette.close()
+ self.marionette.switch_to_window(self.start_tab)
+
+ self.assertEqual(self.marionette.current_window_handle, self.start_tab)
+ self.assertEqual(self.get_selected_tab_index(), self.selected_tab_index)
+ with self.marionette.using_context("content"):
+ self.assertEqual(self.marionette.get_url(), self.test_page)
+
+ def test_switch_tabs_without_focus_change(self):
+ new_tab = self.open_tab(self.open_tab_in_foreground)
+ self.assertEqual(self.marionette.current_window_handle, self.start_tab)
+ self.assertNotEqual(self.get_selected_tab_index(), self.selected_tab_index)
+ with self.marionette.using_context("content"):
+ self.assertEqual(self.marionette.get_url(), self.test_page)
+
+ # Switch to new tab first because it is already selected
+ self.marionette.switch_to_window(new_tab)
+ self.assertEqual(self.marionette.current_window_handle, new_tab)
+
+ self.marionette.switch_to_window(self.start_tab, focus=False)
+ self.assertEqual(self.marionette.current_window_handle, self.start_tab)
+ self.assertNotEqual(self.get_selected_tab_index(), self.selected_tab_index)
+
+ with self.marionette.using_context("content"):
+ self.assertEqual(self.marionette.get_url(), self.test_page)
+
+ self.marionette.switch_to_window(new_tab)
+ self.marionette.close()
+
+ self.marionette.switch_to_window(self.start_tab)
+ self.assertEqual(self.marionette.current_window_handle, self.start_tab)
+ self.assertEqual(self.get_selected_tab_index(), self.selected_tab_index)
+ with self.marionette.using_context("content"):
+ self.assertEqual(self.marionette.get_url(), self.test_page)
+
+ def test_switch_from_content_to_chrome_window_should_not_change_selected_tab(self):
+ new_tab = self.open_tab(self.open_tab_in_foreground)
+
+ self.marionette.switch_to_window(new_tab)
+ self.assertEqual(self.marionette.current_window_handle, new_tab)
+ new_tab_index = self.get_selected_tab_index()
+
+ self.marionette.switch_to_window(self.start_window)
+ self.assertEqual(self.marionette.current_window_handle, new_tab)
+ self.assertEqual(self.get_selected_tab_index(), new_tab_index)
+
+ @skip_if_mobile("New windows not supported in Fennec")
+ def test_switch_to_new_private_browsing_window_has_to_register_browsers(self):
+ # Test that tabs (browsers) are correctly registered for a newly opened
+ # private browsing window. This has to also happen without explicitely
+ # switching to the tab itself before using any commands in content scope.
+ #
+ # Note: Not sure why this only affects private browsing windows only.
+
+ def open_private_browsing_window():
+ with self.marionette.using_context("content"):
+ self.marionette.navigate("about:privatebrowsing")
+ button = self.marionette.find_element(By.ID, "startPrivateBrowsing")
+ button.click()
+
+ new_window = self.open_window(open_private_browsing_window)
+ self.marionette.switch_to_window(new_window)
+ self.assertEqual(self.marionette.current_chrome_window_handle, new_window)
+ self.assertNotEqual(self.marionette.current_window_handle, self.start_tab)
+
+ with self.marionette.using_context("content"):
+ self.marionette.execute_script(" return true; ")
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_teardown_context_preserved.py b/testing/marionette/harness/marionette_harness/tests/unit/test_teardown_context_preserved.py
new file mode 100644
index 000000000..843152bc5
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_teardown_context_preserved.py
@@ -0,0 +1,21 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_harness import MarionetteTestCase, SkipTest
+
+
+class TestTearDownContext(MarionetteTestCase):
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ self.marionette.set_context(self.marionette.CONTEXT_CHROME)
+
+ def tearDown(self):
+ self.assertEqual(self.get_context(), self.marionette.CONTEXT_CHROME)
+ MarionetteTestCase.tearDown(self)
+
+ def get_context(self):
+ return self.marionette._send_message("getContext", key="value")
+
+ def test_skipped_teardown_ok(self):
+ raise SkipTest("This should leave our teardown method in chrome context")
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_text.py b/testing/marionette/harness/marionette_harness/tests/unit/test_text.py
new file mode 100644
index 000000000..e2025e9b6
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_text.py
@@ -0,0 +1,224 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+from marionette_driver.keys import Keys
+
+from marionette_harness import MarionetteTestCase, skip_if_mobile
+
+
+class TestText(MarionetteTestCase):
+ def test_getText(self):
+ test_html = self.marionette.absolute_url("test.html")
+ self.marionette.navigate(test_html)
+ l = self.marionette.find_element(By.ID, "mozLink")
+ self.assertEqual("Click me!", l.text)
+
+ def test_clearText(self):
+ test_html = self.marionette.absolute_url("test.html")
+ self.marionette.navigate(test_html)
+ l = self.marionette.find_element(By.NAME, "myInput")
+ self.assertEqual("asdf", self.marionette.execute_script("return arguments[0].value;", [l]))
+ l.clear()
+ self.assertEqual("", self.marionette.execute_script("return arguments[0].value;", [l]))
+
+ def test_sendKeys(self):
+ test_html = self.marionette.absolute_url("test.html")
+ self.marionette.navigate(test_html)
+ l = self.marionette.find_element(By.NAME, "myInput")
+ self.assertEqual("asdf", self.marionette.execute_script("return arguments[0].value;", [l]))
+
+ # Set caret position to the middle of the input text.
+ self.marionette.execute_script(
+ """var el = arguments[0];
+ el.selectionStart = el.selectionEnd = el.value.length / 2;""",
+ script_args=[l])
+
+ l.send_keys("o")
+ self.assertEqual("asodf", self.marionette.execute_script("return arguments[0].value;", [l]))
+
+ def test_send_keys_to_type_input(self):
+ test_html = self.marionette.absolute_url("html5/test_html_inputs.html")
+ self.marionette.navigate(test_html)
+ num_input = self.marionette.find_element(By.ID, 'number')
+ self.assertEqual("", self.marionette.execute_script("return arguments[0].value", [num_input]))
+ num_input.send_keys("1234")
+ self.assertEqual('1234', self.marionette.execute_script("return arguments[0].value", [num_input]))
+
+ def test_should_fire_key_press_events(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ key_reporter = self.marionette.find_element(By.ID, "keyReporter")
+ key_reporter.send_keys("a")
+
+ result = self.marionette.find_element(By.ID, "result")
+ self.assertIn("press:", result.text)
+
+ def test_should_fire_key_down_events(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ key_reporter = self.marionette.find_element(By.ID, "keyReporter")
+ key_reporter.send_keys("a")
+
+ result = self.marionette.find_element(By.ID, "result")
+ self.assertIn("down:", result.text)
+
+ def test_should_fire_key_up_events(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ key_reporter = self.marionette.find_element(By.ID, "keyReporter")
+ key_reporter.send_keys("a")
+
+ result = self.marionette.find_element(By.ID, "result")
+ self.assertIn("up:", result.text)
+
+ def test_should_type_lowercase_characters(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ key_reporter = self.marionette.find_element(By.ID, "keyReporter")
+ key_reporter.send_keys("abc def")
+
+ self.assertEqual("abc def", key_reporter.get_property("value"))
+
+ def test_should_type_uppercase_characters(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ key_reporter = self.marionette.find_element(By.ID, "keyReporter")
+ key_reporter.send_keys("ABC DEF")
+
+ self.assertEqual("ABC DEF", key_reporter.get_property("value"))
+
+ def test_should_type_a_quote_characters(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ key_reporter = self.marionette.find_element(By.ID, "keyReporter")
+ key_reporter.send_keys('"')
+
+ self.assertEqual('"', key_reporter.get_property("value"))
+
+ def test_should_type_an_at_character(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ key_reporter = self.marionette.find_element(By.ID, "keyReporter")
+ key_reporter.send_keys('@')
+
+ self.assertEqual("@", key_reporter.get_property("value"))
+
+ def test_should_type_a_mix_of_upper_and_lower_case_character(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ key_reporter = self.marionette.find_element(By.ID, "keyReporter")
+ key_reporter.send_keys("me@EXampLe.com")
+
+ self.assertEqual("me@EXampLe.com", key_reporter.get_property("value"))
+
+ def test_arrow_keys_are_not_printable(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ key_reporter = self.marionette.find_element(By.ID, "keyReporter")
+ key_reporter.send_keys(Keys.ARROW_LEFT)
+
+ self.assertEqual("", key_reporter.get_property("value"))
+
+ def test_will_simulate_a_key_up_when_entering_text_into_input_elements(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ element = self.marionette.find_element(By.ID, "keyUp")
+ element.send_keys("I like cheese")
+
+ result = self.marionette.find_element(By.ID, "result")
+ self.assertEqual(result.text, "I like cheese")
+
+ def test_will_simulate_a_key_down_when_entering_text_into_input_elements(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ element = self.marionette.find_element(By.ID, "keyDown")
+ element.send_keys("I like cheese")
+
+ result = self.marionette.find_element(By.ID, "result")
+ # Because the key down gets the result before the input element is
+ # filled, we're a letter short here
+ self.assertEqual(result.text, "I like chees")
+
+ def test_will_simulate_a_key_press_when_entering_text_into_input_elements(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ element = self.marionette.find_element(By.ID, "keyPress")
+ element.send_keys("I like cheese")
+
+ result = self.marionette.find_element(By.ID, "result")
+ # Because the key down gets the result before the input element is
+ # filled, we're a letter short here
+ self.assertEqual(result.text, "I like chees")
+
+ def test_will_simulate_a_keyup_when_entering_text_into_textareas(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ element = self.marionette.find_element(By.ID, "keyUpArea")
+ element.send_keys("I like cheese")
+
+ result = self.marionette.find_element(By.ID, "result")
+ self.assertEqual(result.text, "I like cheese")
+
+ def test_will_simulate_a_keydown_when_entering_text_into_textareas(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ element = self.marionette.find_element(By.ID, "keyDownArea")
+ element.send_keys("I like cheese")
+
+ result = self.marionette.find_element(By.ID, "result")
+ # Because the key down gets the result before the input element is
+ # filled, we're a letter short here
+ self.assertEqual(result.text, "I like chees")
+
+ def test_will_simulate_a_keypress_when_entering_text_into_textareas(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ element = self.marionette.find_element(By.ID, "keyPressArea")
+ element.send_keys("I like cheese")
+
+ result = self.marionette.find_element(By.ID, "result")
+ # Because the key down gets the result before the input element is
+ # filled, we're a letter short here
+ self.assertEqual(result.text, "I like chees")
+
+ @skip_if_mobile("Bug 1333069 - Assertion: 'down: 40' not found in u''")
+ def test_should_report_key_code_of_arrow_keys_up_down_events(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ result = self.marionette.find_element(By.ID, "result")
+ element = self.marionette.find_element(By.ID, "keyReporter")
+ element.send_keys(Keys.ARROW_DOWN)
+ self.assertIn("down: 40", result.text.strip())
+ self.assertIn("up: 40", result.text.strip())
+
+ element.send_keys(Keys.ARROW_UP)
+ self.assertIn("down: 38", result.text.strip())
+ self.assertIn("up: 38", result.text.strip())
+
+ element.send_keys(Keys.ARROW_LEFT)
+ self.assertIn("down: 37", result.text.strip())
+ self.assertIn("up: 37", result.text.strip())
+
+ element.send_keys(Keys.ARROW_RIGHT)
+ self.assertIn("down: 39", result.text.strip())
+ self.assertIn("up: 39", result.text.strip())
+
+ # And leave no rubbish/printable keys in the "keyReporter"
+ self.assertEqual("", element.get_property("value"))
+
+ def testNumericNonShiftKeys(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ element = self.marionette.find_element(By.ID, "keyReporter")
+ numericLineCharsNonShifted = "`1234567890-=[]\\,.'/42"
+ element.send_keys(numericLineCharsNonShifted)
+ self.assertEqual(numericLineCharsNonShifted, element.get_property("value"))
+
+ def testShouldTypeAnInteger(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ element = self.marionette.find_element(By.ID, "keyReporter")
+ element.send_keys(1234)
+ self.assertEqual("1234", element.get_property("value"))
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_text_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_text_chrome.py
new file mode 100644
index 000000000..e0b63de16
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_text_chrome.py
@@ -0,0 +1,44 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+
+from marionette_harness import MarionetteTestCase, skip, WindowManagerMixin
+
+
+@skip("Disabled in bug 896043 and when working on Chrome code re-enable for bug 896046")
+class TestTextChrome(WindowManagerMixin, MarionetteTestCase):
+
+ def setUp(self):
+ super(TestTextChrome, self).setUp()
+ self.marionette.set_context("chrome")
+
+ def open_window_with_js():
+ self.marionette.execute_script("""
+ window.open('chrome://marionette/content/test.xul',
+ 'foo', 'chrome,centerscreen');
+ """)
+
+ new_window = self.open_window(trigger=open_window_with_js)
+ self.marionette.switch_to_window(new_window)
+
+ def tearDown(self):
+ self.close_all_windows()
+ super(TestTextChrome, self).tearDown()
+
+ def test_getText(self):
+ box = self.marionette.find_element(By.ID, "textInput")
+ self.assertEqual("test", box.text)
+
+ def test_clearText(self):
+ box = self.marionette.find_element(By.ID, "textInput")
+ self.assertEqual("test", box.text)
+ box.clear()
+ self.assertEqual("", box.text)
+
+ def test_sendKeys(self):
+ box = self.marionette.find_element(By.ID, "textInput")
+ self.assertEqual("test", box.text)
+ box.send_keys("at")
+ self.assertEqual("attest", box.text)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_timeouts.py b/testing/marionette/harness/marionette_harness/tests/unit/test_timeouts.py
new file mode 100644
index 000000000..354e6a7eb
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_timeouts.py
@@ -0,0 +1,115 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+from marionette_driver.errors import (
+ MarionetteException,
+ NoSuchElementException,
+ ScriptTimeoutException,
+)
+from marionette_driver.marionette import HTMLElement
+
+from marionette_harness import MarionetteTestCase, run_if_manage_instance, skip_if_mobile
+
+
+class TestTimeouts(MarionetteTestCase):
+ def tearDown(self):
+ self.marionette.timeout.reset()
+ MarionetteTestCase.tearDown(self)
+
+ def test_page_timeout_notdefinetimeout_pass(self):
+ test_html = self.marionette.absolute_url("test.html")
+ self.marionette.navigate(test_html)
+
+ def test_page_timeout_fail(self):
+ self.marionette.timeout.page_load = 0
+ test_html = self.marionette.absolute_url("test.html")
+ self.assertRaises(MarionetteException, self.marionette.navigate, test_html)
+
+ def test_page_timeout_pass(self):
+ self.marionette.timeout.page_load = 60
+ test_html = self.marionette.absolute_url("test.html")
+ self.marionette.navigate(test_html)
+
+ def test_search_timeout_notfound_settimeout(self):
+ test_html = self.marionette.absolute_url("test.html")
+ self.marionette.navigate(test_html)
+ self.marionette.timeout.implicit = 1
+ self.assertRaises(NoSuchElementException, self.marionette.find_element, By.ID, "I'm not on the page")
+ self.marionette.timeout.implicit = 0
+ self.assertRaises(NoSuchElementException, self.marionette.find_element, By.ID, "I'm not on the page")
+
+ def test_search_timeout_found_settimeout(self):
+ test_html = self.marionette.absolute_url("test.html")
+ self.marionette.navigate(test_html)
+ button = self.marionette.find_element(By.ID, "createDivButton")
+ button.click()
+ self.marionette.timeout.implicit = 8
+ self.assertEqual(HTMLElement, type(self.marionette.find_element(By.ID, "newDiv")))
+
+ def test_search_timeout_found(self):
+ test_html = self.marionette.absolute_url("test.html")
+ self.marionette.navigate(test_html)
+ button = self.marionette.find_element(By.ID, "createDivButton")
+ button.click()
+ self.assertRaises(NoSuchElementException, self.marionette.find_element, By.ID, "newDiv")
+
+ @run_if_manage_instance("Only runnable if Marionette manages the instance")
+ @skip_if_mobile("Bug 1322993 - Missing temporary folder")
+ def test_reset_timeout(self):
+ timeouts = [getattr(self.marionette.timeout, f) for f in (
+ 'implicit', 'page_load', 'script',)]
+
+ def do_check(callback):
+ for timeout in timeouts:
+ timeout = 10000
+ self.assertEqual(timeout, 10000)
+ callback()
+ for timeout in timeouts:
+ self.assertNotEqual(timeout, 10000)
+
+ def callback_quit():
+ self.marionette.quit()
+ self.marionette.start_session()
+
+ do_check(self.marionette.restart)
+ do_check(callback_quit)
+
+ def test_execute_async_timeout_settimeout(self):
+ test_html = self.marionette.absolute_url("test.html")
+ self.marionette.navigate(test_html)
+ self.marionette.timeout.script = 1
+ self.assertRaises(ScriptTimeoutException, self.marionette.execute_async_script, "var x = 1;")
+
+ def test_no_timeout_settimeout(self):
+ test_html = self.marionette.absolute_url("test.html")
+ self.marionette.navigate(test_html)
+ self.marionette.timeout.script = 1
+ self.assertTrue(self.marionette.execute_async_script("""
+ var callback = arguments[arguments.length - 1];
+ setTimeout(function() { callback(true); }, 500);
+ """))
+
+ def test_compat_input_types(self):
+ # When using the spec-incompatible input format which we have
+ # for backwards compatibility, it should be possible to send ms
+ # as a string type and have the server parseInt it to an integer.
+ body = {"type": "script", "ms": "30000"}
+ self.marionette._send_message("setTimeouts", body)
+
+ def test_deprecated_set_timeouts_command(self):
+ body = {"implicit": 3000}
+ self.marionette._send_message("timeouts", body)
+
+ def test_deprecated_set_search_timeout(self):
+ self.marionette.set_search_timeout(1000)
+ self.assertEqual(1, self.marionette.timeout.implicit)
+
+ def test_deprecated_set_script_timeout(self):
+ self.marionette.set_script_timeout(2000)
+ self.assertEqual(2, self.marionette.timeout.script)
+
+ def test_deprecated_set_page_load_timeout(self):
+ self.marionette.set_page_load_timeout(3000)
+ self.assertEqual(3, self.marionette.timeout.page_load)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_transport.py b/testing/marionette/harness/marionette_harness/tests/unit/test_transport.py
new file mode 100644
index 000000000..39e36a9b2
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_transport.py
@@ -0,0 +1,172 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import json
+
+from marionette_driver.transport import (
+ Command,
+ Proto2Command,
+ Proto2Response,
+ Response,
+)
+
+from marionette_harness import MarionetteTestCase, skip_unless_protocol
+
+
+get_current_url = ("getCurrentUrl", None)
+execute_script = ("executeScript", {"script": "return 42"})
+
+
+class TestMessageSequencing(MarionetteTestCase):
+ @property
+ def last_id(self):
+ return self.marionette.client.last_id
+
+ @last_id.setter
+ def last_id(self, new_id):
+ self.marionette.client.last_id = new_id
+
+ def send(self, name, params):
+ self.last_id = self.last_id + 1
+ cmd = Command(self.last_id, name, params)
+ self.marionette.client.send(cmd)
+ return self.last_id
+
+ @skip_unless_protocol("Skip for level < 3", lambda level: level >= 3)
+ def test_discard_older_messages(self):
+ first = self.send(*get_current_url)
+ second = self.send(*execute_script)
+ resp = self.marionette.client.receive()
+ self.assertEqual(second, resp.id)
+
+ @skip_unless_protocol("Skip for level < 3", lambda level: level >= 3)
+ def test_last_id_incremented(self):
+ before = self.last_id
+ self.send(*get_current_url)
+ self.assertGreater(self.last_id, before)
+
+
+class MessageTestCase(MarionetteTestCase):
+ def assert_attr(self, obj, attr):
+ self.assertTrue(hasattr(obj, attr),
+ "object does not have attribute {}".format(attr))
+
+
+class TestCommand(MessageTestCase):
+ def create(self, msgid="msgid", name="name", params="params"):
+ return Command(msgid, name, params)
+
+ def test_initialise(self):
+ cmd = self.create()
+ self.assert_attr(cmd, "id")
+ self.assert_attr(cmd, "name")
+ self.assert_attr(cmd, "params")
+ self.assertEqual("msgid", cmd.id)
+ self.assertEqual("name", cmd.name)
+ self.assertEqual("params", cmd.params)
+
+ def test_stringify(self):
+ cmd = self.create()
+ string = str(cmd)
+ self.assertIn("Command", string)
+ self.assertIn("id=msgid", string)
+ self.assertIn("name=name", string)
+ self.assertIn("params=params", string)
+
+ def test_to_msg(self):
+ cmd = self.create()
+ msg = json.loads(cmd.to_msg())
+ self.assertEquals(msg[0], Command.TYPE)
+ self.assertEquals(msg[1], "msgid")
+ self.assertEquals(msg[2], "name")
+ self.assertEquals(msg[3], "params")
+
+ def test_from_msg(self):
+ msg = [Command.TYPE, "msgid", "name", "params"]
+ payload = json.dumps(msg)
+ cmd = Command.from_msg(payload)
+ self.assertEquals(msg[1], cmd.id)
+ self.assertEquals(msg[2], cmd.name)
+ self.assertEquals(msg[3], cmd.params)
+
+
+class TestResponse(MessageTestCase):
+ def create(self, msgid="msgid", error="error", result="result"):
+ return Response(msgid, error, result)
+
+ def test_initialise(self):
+ resp = self.create()
+ self.assert_attr(resp, "id")
+ self.assert_attr(resp, "error")
+ self.assert_attr(resp, "result")
+ self.assertEqual("msgid", resp.id)
+ self.assertEqual("error", resp.error)
+ self.assertEqual("result", resp.result)
+
+ def test_stringify(self):
+ resp = self.create()
+ string = str(resp)
+ self.assertIn("Response", string)
+ self.assertIn("id=msgid", string)
+ self.assertIn("error=error", string)
+ self.assertIn("result=result", string)
+
+ def test_to_msg(self):
+ resp = self.create()
+ msg = json.loads(resp.to_msg())
+ self.assertEquals(msg[0], Response.TYPE)
+ self.assertEquals(msg[1], "msgid")
+ self.assertEquals(msg[2], "error")
+ self.assertEquals(msg[3], "result")
+
+ def test_from_msg(self):
+ msg = [Response.TYPE, "msgid", "error", "result"]
+ payload = json.dumps(msg)
+ resp = Response.from_msg(payload)
+ self.assertEquals(msg[1], resp.id)
+ self.assertEquals(msg[2], resp.error)
+ self.assertEquals(msg[3], resp.result)
+
+
+class TestProto2Command(MessageTestCase):
+ def create(self, name="name", params="params"):
+ return Proto2Command(name, params)
+
+ def test_initialise(self):
+ cmd = self.create()
+ self.assert_attr(cmd, "id")
+ self.assert_attr(cmd, "name")
+ self.assert_attr(cmd, "params")
+ self.assertEqual(None, cmd.id)
+ self.assertEqual("name", cmd.name)
+ self.assertEqual("params", cmd.params)
+
+ def test_from_data_unknown(self):
+ with self.assertRaises(ValueError):
+ cmd = Proto2Command.from_data({})
+
+
+class TestProto2Response(MessageTestCase):
+ def create(self, error="error", result="result"):
+ return Proto2Response(error, result)
+
+ def test_initialise(self):
+ resp = self.create()
+ self.assert_attr(resp, "id")
+ self.assert_attr(resp, "error")
+ self.assert_attr(resp, "result")
+ self.assertEqual(None, resp.id)
+ self.assertEqual("error", resp.error)
+ self.assertEqual("result", resp.result)
+
+ def test_from_data_error(self):
+ data = {"error": "error"}
+ resp = Proto2Response.from_data(data)
+ self.assertEqual(data, resp.error)
+ self.assertEqual(None, resp.result)
+
+ def test_from_data_result(self):
+ resp = Proto2Response.from_data("result")
+ self.assertEqual(None, resp.error)
+ self.assertEqual("result", resp.result)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_typing.py b/testing/marionette/harness/marionette_harness/tests/unit/test_typing.py
new file mode 100644
index 000000000..ca63a0dc7
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_typing.py
@@ -0,0 +1,332 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import urllib
+
+from marionette_driver.by import By
+from marionette_driver.errors import ElementNotInteractableException
+from marionette_driver.keys import Keys
+
+from marionette_harness import MarionetteTestCase, skip, skip_if_mobile
+
+
+def inline(doc):
+ return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+
+
+class TypingTestCase(MarionetteTestCase):
+
+ def setUp(self):
+ super(TypingTestCase, self).setUp()
+
+ if self.marionette.session_capabilities["platformName"] == "darwin":
+ self.mod_key = Keys.META
+ else:
+ self.mod_key = Keys.CONTROL
+
+
+class TestTypingChrome(TypingTestCase):
+
+ def setUp(self):
+ super(TestTypingChrome, self).setUp()
+ self.marionette.set_context("chrome")
+
+ @skip_if_mobile("Interacting with chrome elements not available for Fennec")
+ def test_cut_and_paste_shortcuts(self):
+ with self.marionette.using_context("content"):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ keyReporter = self.marionette.find_element(By.ID, "keyReporter")
+ self.assertEqual("", keyReporter.get_property("value"))
+ keyReporter.send_keys("zyxwvutsr")
+ self.assertEqual("zyxwvutsr", keyReporter.get_property("value"))
+
+ # select all and cut
+ keyReporter.send_keys(self.mod_key, "a")
+ keyReporter.send_keys(self.mod_key, "x")
+ self.assertEqual("", keyReporter.get_property("value"))
+
+ url_bar = self.marionette.find_element(By.ID, "urlbar")
+
+ # Clear contents first
+ url_bar.send_keys(self.mod_key, "a")
+ url_bar.send_keys(Keys.BACK_SPACE)
+ self.assertEqual("", url_bar.get_attribute("value"))
+
+ url_bar.send_keys(self.mod_key, "v")
+ self.assertEqual("zyxwvutsr", url_bar.get_property("value"))
+
+
+class TestTypingContent(TypingTestCase):
+
+ def testShouldFireKeyPressEvents(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ keyReporter = self.marionette.find_element(By.ID, "keyReporter")
+ keyReporter.send_keys("a")
+ result = self.marionette.find_element(By.ID, "result")
+ self.assertTrue("press:" in result.text)
+
+ def testShouldFireKeyDownEvents(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ keyReporter = self.marionette.find_element(By.ID, "keyReporter")
+ keyReporter.send_keys("I")
+ result = self.marionette.find_element(By.ID, "result")
+ self.assertTrue("down" in result.text)
+
+ def testShouldFireKeyUpEvents(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ keyReporter = self.marionette.find_element(By.ID, "keyReporter")
+ keyReporter.send_keys("a")
+ result = self.marionette.find_element(By.ID, "result")
+ self.assertTrue("up:" in result.text)
+
+ def testShouldTypeLowerCaseLetters(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ keyReporter = self.marionette.find_element(By.ID, "keyReporter")
+ keyReporter.send_keys("abc def")
+ self.assertEqual("abc def", keyReporter.get_property("value"))
+
+ def testShouldBeAbleToTypeCapitalLetters(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ keyReporter = self.marionette.find_element(By.ID, "keyReporter")
+ keyReporter.send_keys("ABC DEF")
+ self.assertEqual("ABC DEF", keyReporter.get_property("value"))
+
+ def testCutAndPasteShortcuts(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ keyReporter = self.marionette.find_element(By.ID, "keyReporter")
+ self.assertEqual("", keyReporter.get_property("value"))
+ keyReporter.send_keys("zyxwvutsr")
+ self.assertEqual("zyxwvutsr", keyReporter.get_property("value"))
+
+ # select all and cut
+ keyReporter.send_keys(self.mod_key, "a")
+ keyReporter.send_keys(self.mod_key, "x")
+ self.assertEqual("", keyReporter.get_property("value"))
+
+ keyReporter.send_keys(self.mod_key, "v")
+ self.assertEqual("zyxwvutsr", keyReporter.get_property("value"))
+
+ def testShouldBeAbleToTypeQuoteMarks(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ keyReporter = self.marionette.find_element(By.ID, "keyReporter")
+ keyReporter.send_keys("\"")
+ self.assertEqual("\"", keyReporter.get_property("value"))
+
+ def testShouldBeAbleToTypeTheAtCharacter(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ keyReporter = self.marionette.find_element(By.ID, "keyReporter")
+ keyReporter.send_keys("@")
+ self.assertEqual("@", keyReporter.get_property("value"))
+
+ def testShouldBeAbleToMixUpperAndLowerCaseLetters(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ keyReporter = self.marionette.find_element(By.ID, "keyReporter")
+ keyReporter.send_keys("me@eXample.com")
+ self.assertEqual("me@eXample.com", keyReporter.get_property("value"))
+
+ def testArrowKeysShouldNotBePrintable(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ keyReporter = self.marionette.find_element(By.ID, "keyReporter")
+ keyReporter.send_keys(Keys.ARROW_LEFT)
+ self.assertEqual("", keyReporter.get_property("value"))
+
+ def testWillSimulateAKeyUpWhenEnteringTextIntoInputElements(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ element = self.marionette.find_element(By.ID, "keyUp")
+ element.send_keys("I like cheese")
+ result = self.marionette.find_element(By.ID, "result")
+ self.assertEqual(result.text, "I like cheese")
+
+ def testWillSimulateAKeyDownWhenEnteringTextIntoInputElements(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ element = self.marionette.find_element(By.ID, "keyDown")
+ element.send_keys("I like cheese")
+ result = self.marionette.find_element(By.ID, "result")
+ # Because the key down gets the result before the input element is
+ # filled, we're a letter short here
+ self.assertEqual(result.text, "I like chees")
+
+ def testWillSimulateAKeyPressWhenEnteringTextIntoInputElements(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ element = self.marionette.find_element(By.ID, "keyPress")
+ element.send_keys("I like cheese")
+ result = self.marionette.find_element(By.ID, "result")
+ # Because the key down gets the result before the input element is
+ # filled, we're a letter short here
+ self.assertEqual(result.text, "I like chees")
+
+ def testWillSimulateAKeyUpWhenEnteringTextIntoTextAreas(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ element = self.marionette.find_element(By.ID, "keyUpArea")
+ element.send_keys("I like cheese")
+ result = self.marionette.find_element(By.ID, "result")
+ self.assertEqual("I like cheese", result.text)
+
+ def testWillSimulateAKeyDownWhenEnteringTextIntoTextAreas(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ element = self.marionette.find_element(By.ID, "keyDownArea")
+ element.send_keys("I like cheese")
+ result = self.marionette.find_element(By.ID, "result")
+ # Because the key down gets the result before the input element is
+ # filled, we're a letter short here
+ self.assertEqual(result.text, "I like chees")
+
+ def testWillSimulateAKeyPressWhenEnteringTextIntoTextAreas(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ element = self.marionette.find_element(By.ID, "keyPressArea")
+ element.send_keys("I like cheese")
+ result = self.marionette.find_element(By.ID, "result")
+ # Because the key down gets the result before the input element is
+ # filled, we're a letter short here
+ self.assertEqual(result.text, "I like chees")
+
+ @skip_if_mobile("Bug 1324752 - Arrow keys cannot be sent in Fennec")
+ def testShouldReportKeyCodeOfArrowKeysUpDownEvents(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ result = self.marionette.find_element(By.ID, "result")
+ element = self.marionette.find_element(By.ID, "keyReporter")
+
+ element.send_keys(Keys.ARROW_DOWN)
+
+ self.assertIn("down: 40", result.text.strip())
+ self.assertIn("up: 40", result.text.strip())
+
+ element.send_keys(Keys.ARROW_UP)
+ self.assertIn("down: 38", result.text.strip())
+ self.assertIn("up: 38", result.text.strip())
+
+ element.send_keys(Keys.ARROW_LEFT)
+ self.assertIn("down: 37", result.text.strip())
+ self.assertIn("up: 37", result.text.strip())
+
+ element.send_keys(Keys.ARROW_RIGHT)
+ self.assertIn("down: 39", result.text.strip())
+ self.assertIn("up: 39", result.text.strip())
+
+ # And leave no rubbish/printable keys in the "keyReporter"
+ self.assertEqual("", element.get_property("value"))
+
+ @skip("Reenable in Bug 1068728")
+ def testNumericShiftKeys(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ result = self.marionette.find_element(By.ID, "result")
+ element = self.marionette.find_element(By.ID, "keyReporter")
+ numericShiftsEtc = "~!@#$%^&*()_+{}:i\"<>?|END~"
+ element.send_keys(numericShiftsEtc)
+ self.assertEqual(numericShiftsEtc, element.get_property("value"))
+ self.assertIn(" up: 16", result.text.strip())
+
+ def testLowerCaseAlphaKeys(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ element = self.marionette.find_element(By.ID, "keyReporter")
+ lowerAlphas = "abcdefghijklmnopqrstuvwxyz"
+ element.send_keys(lowerAlphas)
+ self.assertEqual(lowerAlphas, element.get_property("value"))
+
+ @skip("Reenable in Bug 1068735")
+ def testUppercaseAlphaKeys(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ result = self.marionette.find_element(By.ID, "result")
+ element = self.marionette.find_element(By.ID, "keyReporter")
+ upperAlphas = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ element.send_keys(upperAlphas)
+ self.assertEqual(upperAlphas, element.get_property("value"))
+ self.assertIn(" up: 16", result.text.strip())
+
+ @skip("Reenable in Bug 1068726")
+ def testAllPrintableKeys(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ result = self.marionette.find_element(By.ID, "result")
+ element = self.marionette.find_element(By.ID, "keyReporter")
+ allPrintable = "!\"#$%&'()*+,-./0123456789:<=>?@ ABCDEFGHIJKLMNOPQRSTUVWXYZ [\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
+ element.send_keys(allPrintable)
+
+ self.assertTrue(allPrintable, element.get_property("value"))
+ self.assertIn(" up: 16", result.text.strip())
+
+ @skip("Reenable in Bug 1068733")
+ def testSpecialSpaceKeys(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ element = self.marionette.find_element(By.ID, "keyReporter")
+ element.send_keys("abcd" + Keys.SPACE + "fgh" + Keys.SPACE + "ij")
+ self.assertEqual("abcd fgh ij", element.get_property("value"))
+
+ def testShouldTypeAnInteger(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ element = self.marionette.find_element(By.ID, "keyReporter")
+ element.send_keys(1234)
+ self.assertEqual("1234", element.get_property("value"))
+
+ def testShouldSendKeysToElementsWithoutTheValueAttribute(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ # If we don't get an error below we are good
+ self.marionette.find_element(By.TAG_NAME, "body").send_keys("foo")
+
+ def test_not_interactable_if_hidden(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ not_displayed = self.marionette.find_element(By.ID, "notDisplayed")
+ self.assertRaises(ElementNotInteractableException, not_displayed.send_keys, "foo")
+
+ def test_appends_to_input_text(self):
+ self.marionette.navigate(inline("<input>"))
+ el = self.marionette.find_element(By.TAG_NAME, "input")
+ el.send_keys("foo")
+ el.send_keys("bar")
+ self.assertEqual("foobar", el.get_property("value"))
+
+ def test_appends_to_textarea(self):
+ self.marionette.navigate(inline("<textarea></textarea>"))
+ textarea = self.marionette.find_element(By.TAG_NAME, "textarea")
+ textarea.send_keys("foo")
+ textarea.send_keys("bar")
+ self.assertEqual("foobar", textarea.get_property("value"))
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_using_permissions.py b/testing/marionette/harness/marionette_harness/tests/unit/test_using_permissions.py
new file mode 100644
index 000000000..71e271dd4
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_using_permissions.py
@@ -0,0 +1,46 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.errors import JavascriptException
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestUsingPermssions(MarionetteTestCase):
+
+ def test_using_permissions(self):
+ # Test that multiple permissions can be set with 'using_permissions',
+ # and that they are set correctly and unset correctly after leaving
+ # the context manager.
+ original_perm = self.marionette.get_permission('systemXHR')
+ original_alarm = self.marionette.get_permission('alarms')
+ new_perm = True if original_perm != 1 else False
+ new_alarm = True if original_alarm != 1 else False
+ with self.marionette.using_permissions({'systemXHR': new_perm,
+ 'alarms': new_alarm}):
+ now_perm = self.marionette.get_permission('systemXHR')
+ now_alarm = self.marionette.get_permission('alarms')
+ self.assertEquals(new_perm, now_perm)
+ self.assertNotEquals(now_perm, original_perm)
+ self.assertEquals(new_alarm, now_alarm)
+ self.assertNotEquals(now_alarm, original_alarm)
+ self.assertEquals(original_perm,
+ self.marionette.get_permission('systemXHR'))
+ self.assertEquals(original_alarm,
+ self.marionette.get_permission('alarms'))
+
+ def test_exception_using_permissions(self):
+ # Test that throwing an exception inside the context manager doesn't
+ # prevent the permissions from being restored at context manager exit.
+ original_perm = self.marionette.get_permission('systemXHR')
+ new_perm = True if original_perm != 1 else False
+ with self.marionette.using_permissions({'systemXHR': new_perm}):
+ now_perm = self.marionette.get_permission('systemXHR')
+ self.assertEquals(new_perm, now_perm)
+ self.assertNotEquals(now_perm, original_perm)
+ self.assertRaises(JavascriptException,
+ self.marionette.execute_script,
+ "return foo.bar.baz;")
+ self.assertEquals(original_perm,
+ self.marionette.get_permission('systemXHR'))
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_visibility.py b/testing/marionette/harness/marionette_harness/tests/unit/test_visibility.py
new file mode 100644
index 000000000..750ecf20a
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_visibility.py
@@ -0,0 +1,121 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.by import By
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestVisibility(MarionetteTestCase):
+
+ def testShouldAllowTheUserToTellIfAnElementIsDisplayedOrNot(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ self.assertTrue(self.marionette.find_element(By.ID, "displayed").is_displayed())
+ self.assertFalse(self.marionette.find_element(By.ID, "none").is_displayed())
+ self.assertFalse(self.marionette.find_element(By.ID,
+ "suppressedParagraph").is_displayed())
+ self.assertFalse(self.marionette.find_element(By.ID, "hidden").is_displayed())
+
+ def testVisibilityShouldTakeIntoAccountParentVisibility(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ childDiv = self.marionette.find_element(By.ID, "hiddenchild")
+ hiddenLink = self.marionette.find_element(By.ID, "hiddenlink")
+
+ self.assertFalse(childDiv.is_displayed())
+ self.assertFalse(hiddenLink.is_displayed())
+
+ def testShouldCountElementsAsVisibleIfStylePropertyHasBeenSet(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ shown = self.marionette.find_element(By.ID, "visibleSubElement")
+ self.assertTrue(shown.is_displayed())
+
+ def testShouldModifyTheVisibilityOfAnElementDynamically(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+ element = self.marionette.find_element(By.ID, "hideMe")
+ self.assertTrue(element.is_displayed())
+ element.click()
+ self.assertFalse(element.is_displayed())
+
+ def testHiddenInputElementsAreNeverVisible(self):
+ test_html = self.marionette.absolute_url("javascriptPage.html")
+ self.marionette.navigate(test_html)
+
+ shown = self.marionette.find_element(By.NAME, "hidden")
+
+ self.assertFalse(shown.is_displayed())
+
+ def testShouldSayElementsWithNegativeTransformAreNotDisplayed(self):
+ test_html = self.marionette.absolute_url("cssTransform.html")
+ self.marionette.navigate(test_html)
+
+ elementX = self.marionette.find_element(By.ID, 'parentX')
+ self.assertFalse(elementX.is_displayed())
+ elementY = self.marionette.find_element(By.ID, 'parentY')
+ self.assertFalse(elementY.is_displayed())
+
+ def testShouldSayElementsWithParentWithNegativeTransformAreNotDisplayed(self):
+ test_html = self.marionette.absolute_url("cssTransform.html")
+ self.marionette.navigate(test_html)
+
+ elementX = self.marionette.find_element(By.ID, 'childX')
+ self.assertFalse(elementX.is_displayed())
+ elementY = self.marionette.find_element(By.ID, 'childY')
+ self.assertFalse(elementY.is_displayed())
+
+ def testShouldSayElementWithZeroTransformIsVisible(self):
+ test_html = self.marionette.absolute_url("cssTransform.html")
+ self.marionette.navigate(test_html)
+
+ zero_tranform = self.marionette.find_element(By.ID, 'zero-tranform')
+ self.assertTrue(zero_tranform.is_displayed())
+
+ def testShouldSayElementIsVisibleWhenItHasNegativeTransformButElementisntInANegativeSpace(self):
+ test_html = self.marionette.absolute_url("cssTransform2.html")
+ self.marionette.navigate(test_html)
+ negative_percent__tranform = self.marionette.find_element(By.ID, 'negative-percentage-transformY')
+ self.assertTrue(negative_percent__tranform.is_displayed())
+
+ def testShouldSayElementIsInvisibleWhenOverflowXIsHiddenAndOutOfViewport(self):
+ test_html = self.marionette.absolute_url("bug814037.html")
+ self.marionette.navigate(test_html)
+ overflow_x = self.marionette.find_element(By.ID, "assertMe2")
+ self.assertFalse(overflow_x.is_displayed())
+
+ def testShouldShowElementNotVisibleWithHiddenAttribute(self):
+ test_html = self.marionette.absolute_url("hidden.html")
+ self.marionette.navigate(test_html)
+ singleHidden = self.marionette.find_element(By.ID, 'singleHidden')
+ self.assertFalse(singleHidden.is_displayed())
+
+ def testShouldShowElementNotVisibleWhenParentElementHasHiddenAttribute(self):
+ test_html = self.marionette.absolute_url("hidden.html")
+ self.marionette.navigate(test_html)
+ child = self.marionette.find_element(By.ID, 'child')
+ self.assertFalse(child.is_displayed())
+
+ def testShouldClickOnELementPartiallyOffLeft(self):
+ test_html = self.marionette.absolute_url("element_left.html")
+ self.marionette.navigate(test_html)
+ self.marionette.find_element(By.CSS_SELECTOR, '.element').click()
+
+ def testShouldClickOnELementPartiallyOffRight(self):
+ test_html = self.marionette.absolute_url("element_right.html")
+ self.marionette.navigate(test_html)
+ self.marionette.find_element(By.CSS_SELECTOR, '.element').click()
+
+ def testShouldClickOnELementPartiallyOffTop(self):
+ test_html = self.marionette.absolute_url("element_top.html")
+ self.marionette.navigate(test_html)
+ self.marionette.find_element(By.CSS_SELECTOR, '.element').click()
+
+ def testShouldClickOnELementPartiallyOffBottom(self):
+ test_html = self.marionette.absolute_url("element_bottom.html")
+ self.marionette.navigate(test_html)
+ self.marionette.find_element(By.CSS_SELECTOR, '.element').click()
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_wait.py b/testing/marionette/harness/marionette_harness/tests/unit/test_wait.py
new file mode 100644
index 000000000..6a4872773
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_wait.py
@@ -0,0 +1,347 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import sys
+import time
+
+from marionette_driver import errors, wait
+from marionette_driver.wait import Wait
+
+from marionette_harness import MarionetteTestCase
+
+
+class TickingClock(object):
+
+ def __init__(self, incr=1):
+ self.ticks = 0
+ self.increment = incr
+
+ def sleep(self, dur=None):
+ dur = dur if dur is not None else self.increment
+ self.ticks += dur
+
+ @property
+ def now(self):
+ return self.ticks
+
+
+class SequenceClock(object):
+
+ def __init__(self, times):
+ self.times = times
+ self.i = 0
+
+ @property
+ def now(self):
+ if len(self.times) > self.i:
+ self.i += 1
+ return self.times[self.i - 1]
+
+ def sleep(self, dur):
+ pass
+
+
+class MockMarionette(object):
+
+ def __init__(self):
+ self.waited = 0
+
+ def exception(self, e=None, wait=1):
+ self.wait()
+ if self.waited == wait:
+ if e is None:
+ e = Exception
+ raise e
+
+ def true(self, wait=1):
+ self.wait()
+ if self.waited == wait:
+ return True
+ return None
+
+ def false(self, wait=1):
+ self.wait()
+ return False
+
+ def none(self, wait=1):
+ self.wait()
+ return None
+
+ def value(self, value, wait=1):
+ self.wait()
+ if self.waited == wait:
+ return value
+ return None
+
+ def wait(self):
+ self.waited += 1
+
+
+def at_third_attempt(clock, end):
+ return clock.now == 2
+
+
+def now(clock, end):
+ return True
+
+
+class SystemClockTest(MarionetteTestCase):
+
+ def setUp(self):
+ super(SystemClockTest, self).setUp()
+ self.clock = wait.SystemClock()
+
+ def test_construction_initializes_time(self):
+ self.assertEqual(self.clock._time, time)
+
+ def test_sleep(self):
+ start = time.time()
+ self.clock.sleep(0.1)
+ end = time.time() - start
+ self.assertGreater(end, 0)
+
+ def test_time_now(self):
+ self.assertIsNotNone(self.clock.now)
+
+
+class FormalWaitTest(MarionetteTestCase):
+
+ def setUp(self):
+ super(FormalWaitTest, self).setUp()
+ self.m = MockMarionette()
+ self.m.timeout = 123
+
+ def test_construction_with_custom_timeout(self):
+ wt = Wait(self.m, timeout=42)
+ self.assertEqual(wt.timeout, 42)
+
+ def test_construction_with_custom_interval(self):
+ wt = Wait(self.m, interval=42)
+ self.assertEqual(wt.interval, 42)
+
+ def test_construction_with_custom_clock(self):
+ c = TickingClock(1)
+ wt = Wait(self.m, clock=c)
+ self.assertEqual(wt.clock, c)
+
+ def test_construction_with_custom_exception(self):
+ wt = Wait(self.m, ignored_exceptions=Exception)
+ self.assertIn(Exception, wt.exceptions)
+ self.assertEqual(len(wt.exceptions), 1)
+
+ def test_construction_with_custom_exception_list(self):
+ exc = [Exception, ValueError]
+ wt = Wait(self.m, ignored_exceptions=exc)
+ for e in exc:
+ self.assertIn(e, wt.exceptions)
+ self.assertEqual(len(wt.exceptions), len(exc))
+
+ def test_construction_with_custom_exception_tuple(self):
+ exc = (Exception, ValueError)
+ wt = Wait(self.m, ignored_exceptions=exc)
+ for e in exc:
+ self.assertIn(e, wt.exceptions)
+ self.assertEqual(len(wt.exceptions), len(exc))
+
+ def test_duplicate_exceptions(self):
+ wt = Wait(self.m, ignored_exceptions=[Exception, Exception])
+ self.assertIn(Exception, wt.exceptions)
+ self.assertEqual(len(wt.exceptions), 1)
+
+ def test_default_timeout(self):
+ self.assertEqual(wait.DEFAULT_TIMEOUT, 5)
+
+ def test_default_interval(self):
+ self.assertEqual(wait.DEFAULT_INTERVAL, 0.1)
+
+ def test_end_property(self):
+ wt = Wait(self.m)
+ self.assertIsNotNone(wt.end)
+
+ def test_marionette_property(self):
+ wt = Wait(self.m)
+ self.assertEqual(wt.marionette, self.m)
+
+ def test_clock_property(self):
+ wt = Wait(self.m)
+ self.assertIsInstance(wt.clock, wait.SystemClock)
+
+ def test_timeout_uses_default_if_marionette_timeout_is_none(self):
+ self.m.timeout = None
+ wt = Wait(self.m)
+ self.assertEqual(wt.timeout, wait.DEFAULT_TIMEOUT)
+
+
+class PredicatesTest(MarionetteTestCase):
+
+ def test_until(self):
+ c = wait.SystemClock()
+ self.assertFalse(wait.until_pred(c, sys.maxint))
+ self.assertTrue(wait.until_pred(c, 0))
+
+
+class WaitUntilTest(MarionetteTestCase):
+
+ def setUp(self):
+ super(WaitUntilTest, self).setUp()
+
+ self.m = MockMarionette()
+ self.clock = TickingClock()
+ self.wt = Wait(self.m, timeout=10, interval=1, clock=self.clock)
+
+ def test_true(self):
+ r = self.wt.until(lambda x: x.true())
+ self.assertTrue(r)
+ self.assertEqual(self.clock.ticks, 0)
+
+ def test_true_within_timeout(self):
+ r = self.wt.until(lambda x: x.true(wait=5))
+ self.assertTrue(r)
+ self.assertEqual(self.clock.ticks, 4)
+
+ def test_timeout(self):
+ with self.assertRaises(errors.TimeoutException):
+ r = self.wt.until(lambda x: x.true(wait=15))
+ self.assertEqual(self.clock.ticks, 10)
+
+ def test_exception_raises_immediately(self):
+ with self.assertRaises(TypeError):
+ self.wt.until(lambda x: x.exception(e=TypeError))
+ self.assertEqual(self.clock.ticks, 0)
+
+ def test_ignored_exception(self):
+ self.wt.exceptions = (TypeError,)
+ with self.assertRaises(errors.TimeoutException):
+ self.wt.until(lambda x: x.exception(e=TypeError))
+
+ def test_ignored_exception_wrapped_in_timeoutexception(self):
+ self.wt.exceptions = (TypeError,)
+
+ exc = None
+ try:
+ self.wt.until(lambda x: x.exception(e=TypeError))
+ except Exception as e:
+ exc = e
+
+ s = str(exc)
+ self.assertIsNotNone(exc)
+ self.assertIsInstance(exc, errors.TimeoutException)
+ self.assertIn(", caused by {0!r}".format(TypeError), s)
+ self.assertIn("self.wt.until(lambda x: x.exception(e=TypeError))", s)
+
+ def test_ignored_exception_after_timeout_is_not_raised(self):
+ with self.assertRaises(errors.TimeoutException):
+ r = self.wt.until(lambda x: x.exception(wait=15))
+ self.assertEqual(self.clock.ticks, 10)
+
+ def test_keyboard_interrupt(self):
+ with self.assertRaises(KeyboardInterrupt):
+ self.wt.until(lambda x: x.exception(e=KeyboardInterrupt))
+
+ def test_system_exit(self):
+ with self.assertRaises(SystemExit):
+ self.wt.until(lambda x: x.exception(SystemExit))
+
+ def test_true_condition_returns_immediately(self):
+ r = self.wt.until(lambda x: x.true())
+ self.assertIsInstance(r, bool)
+ self.assertTrue(r)
+ self.assertEqual(self.clock.ticks, 0)
+
+ def test_value(self):
+ r = self.wt.until(lambda x: "foo")
+ self.assertEqual(r, "foo")
+ self.assertEqual(self.clock.ticks, 0)
+
+ def test_custom_predicate(self):
+ r = self.wt.until(lambda x: x.true(wait=2), is_true=at_third_attempt)
+ self.assertTrue(r)
+ self.assertEqual(self.clock.ticks, 1)
+
+ def test_custom_predicate_times_out(self):
+ with self.assertRaises(errors.TimeoutException):
+ self.wt.until(lambda x: x.true(wait=4), is_true=at_third_attempt)
+
+ self.assertEqual(self.clock.ticks, 2)
+
+ def test_timeout_elapsed_duration(self):
+ with self.assertRaisesRegexp(errors.TimeoutException,
+ "Timed out after 2.0 seconds"):
+ self.wt.until(lambda x: x.true(wait=4), is_true=at_third_attempt)
+
+ def test_timeout_elapsed_rounding(self):
+ wt = Wait(self.m, clock=SequenceClock([1, 0.01, 1]), timeout=0)
+ with self.assertRaisesRegexp(errors.TimeoutException,
+ "Timed out after 1.0 seconds"):
+ wt.until(lambda x: x.true(), is_true=now)
+
+ def test_timeout_elapsed_interval_by_delayed_condition_return(self):
+ def callback(mn):
+ self.clock.sleep(11)
+ return mn.false()
+
+ with self.assertRaisesRegexp(errors.TimeoutException,
+ "Timed out after 11.0 seconds"):
+ self.wt.until(callback)
+ # With a delayed conditional return > timeout, only 1 iteration is
+ # possible
+ self.assertEqual(self.m.waited, 1)
+
+ def test_timeout_with_delayed_condition_return(self):
+ def callback(mn):
+ self.clock.sleep(.5)
+ return mn.false()
+
+ with self.assertRaisesRegexp(errors.TimeoutException,
+ "Timed out after 10.0 seconds"):
+ self.wt.until(callback)
+ # With a delayed conditional return < interval, 10 iterations should be
+ # possible
+ self.assertEqual(self.m.waited, 10)
+
+ def test_timeout_interval_shorter_than_delayed_condition_return(self):
+ def callback(mn):
+ self.clock.sleep(2)
+ return mn.false()
+
+ with self.assertRaisesRegexp(errors.TimeoutException,
+ "Timed out after 10.0 seconds"):
+ self.wt.until(callback)
+ # With a delayed return of the conditional which takes twice that long than the interval,
+ # half of the iterations should be possible
+ self.assertEqual(self.m.waited, 5)
+
+ def test_message(self):
+ self.wt.exceptions = (TypeError,)
+ exc = None
+ try:
+ self.wt.until(lambda x: x.exception(e=TypeError), message="hooba")
+ except errors.TimeoutException as e:
+ exc = e
+
+ result = str(exc)
+ self.assertIn("seconds with message: hooba, caused by", result)
+
+ def test_no_message(self):
+ self.wt.exceptions = (TypeError,)
+ exc = None
+ try:
+ self.wt.until(lambda x: x.exception(e=TypeError), message="")
+ except errors.TimeoutException as e:
+ exc = e
+
+ result = str(exc)
+ self.assertIn("seconds, caused by", result)
+
+ def test_message_has_none_as_its_value(self):
+ self.wt.exceptions = (TypeError,)
+ exc = None
+ try:
+ self.wt.until(False, None, None)
+ except errors.TimeoutException as e:
+ exc = e
+
+ result = str(exc)
+ self.assertNotIn("with message:", result)
+ self.assertNotIn("secondsNone", result)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_window_close_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_window_close_chrome.py
new file mode 100644
index 000000000..18ae191c1
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_window_close_chrome.py
@@ -0,0 +1,80 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_harness import MarionetteTestCase, WindowManagerMixin
+
+
+class TestCloseWindow(WindowManagerMixin, MarionetteTestCase):
+
+ def setUp(self):
+ super(TestCloseWindow, self).setUp()
+
+ self.marionette.set_context("chrome")
+
+ def tearDown(self):
+ self.close_all_windows()
+ self.close_all_tabs()
+
+ super(TestCloseWindow, self).tearDown()
+
+ def test_close_chrome_window_for_browser_window(self):
+ win = self.open_window()
+ self.marionette.switch_to_window(win)
+
+ self.assertNotIn(win, self.marionette.window_handles)
+ chrome_window_handles = self.marionette.close_chrome_window()
+ self.assertNotIn(win, chrome_window_handles)
+ self.assertListEqual(self.start_windows, chrome_window_handles)
+ self.assertNotIn(win, self.marionette.window_handles)
+
+ def test_close_chrome_window_for_non_browser_window(self):
+
+ def open_window_with_js():
+ self.marionette.execute_script("""
+ window.open('chrome://marionette/content/test.xul',
+ 'foo', 'chrome,centerscreen');
+ """)
+
+ win = self.open_window(trigger=open_window_with_js)
+ self.marionette.switch_to_window(win)
+
+ self.assertIn(win, self.marionette.window_handles)
+ chrome_window_handles = self.marionette.close_chrome_window()
+ self.assertNotIn(win, chrome_window_handles)
+ self.assertListEqual(self.start_windows, chrome_window_handles)
+ self.assertNotIn(win, self.marionette.window_handles)
+
+ def test_close_chrome_window_for_last_open_window(self):
+ self.close_all_windows()
+
+ self.assertListEqual([], self.marionette.close_chrome_window())
+ self.assertListEqual([self.start_tab], self.marionette.window_handles)
+ self.assertListEqual([self.start_window], self.marionette.chrome_window_handles)
+ self.assertIsNotNone(self.marionette.session)
+
+ def test_close_window_for_browser_tab(self):
+ tab = self.open_tab()
+ self.marionette.switch_to_window(tab)
+
+ window_handles = self.marionette.close()
+ self.assertNotIn(tab, window_handles)
+ self.assertListEqual(self.start_tabs, window_handles)
+
+ def test_close_window_for_browser_window_with_single_tab(self):
+ win = self.open_window()
+ self.marionette.switch_to_window(win)
+
+ self.assertEqual(len(self.start_tabs) + 1, len(self.marionette.window_handles))
+ window_handles = self.marionette.close()
+ self.assertNotIn(win, window_handles)
+ self.assertListEqual(self.start_tabs, window_handles)
+ self.assertListEqual(self.start_windows, self.marionette.chrome_window_handles)
+
+ def test_close_window_for_last_open_tab(self):
+ self.close_all_tabs()
+
+ self.assertListEqual([], self.marionette.close())
+ self.assertListEqual([self.start_tab], self.marionette.window_handles)
+ self.assertListEqual([self.start_window], self.marionette.chrome_window_handles)
+ self.assertIsNotNone(self.marionette.session)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_window_close_content.py b/testing/marionette/harness/marionette_harness/tests/unit/test_window_close_content.py
new file mode 100644
index 000000000..8e6485e54
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_window_close_content.py
@@ -0,0 +1,81 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_harness import MarionetteTestCase, skip_if_mobile, WindowManagerMixin
+
+
+class TestCloseWindow(WindowManagerMixin, MarionetteTestCase):
+
+ def tearDown(self):
+ self.close_all_windows()
+ self.close_all_tabs()
+
+ super(TestCloseWindow, self).tearDown()
+
+ @skip_if_mobile("Interacting with chrome windows not available for Fennec")
+ def test_close_chrome_window_for_browser_window(self):
+ win = self.open_window()
+ self.marionette.switch_to_window(win)
+
+ self.assertNotIn(win, self.marionette.window_handles)
+ chrome_window_handles = self.marionette.close_chrome_window()
+ self.assertNotIn(win, chrome_window_handles)
+ self.assertListEqual(self.start_windows, chrome_window_handles)
+ self.assertNotIn(win, self.marionette.window_handles)
+
+ @skip_if_mobile("Interacting with chrome windows not available for Fennec")
+ def test_close_chrome_window_for_non_browser_window(self):
+
+ def open_window_with_js():
+ with self.marionette.using_context("chrome"):
+ self.marionette.execute_script("""
+ window.open('chrome://marionette/content/test.xul',
+ 'foo', 'chrome,centerscreen');
+ """)
+
+ win = self.open_window(trigger=open_window_with_js)
+ self.marionette.switch_to_window(win)
+
+ self.assertIn(win, self.marionette.window_handles)
+ chrome_window_handles = self.marionette.close_chrome_window()
+ self.assertNotIn(win, chrome_window_handles)
+ self.assertListEqual(self.start_windows, chrome_window_handles)
+ self.assertNotIn(win, self.marionette.window_handles)
+
+ @skip_if_mobile("Interacting with chrome windows not available for Fennec")
+ def test_close_chrome_window_for_last_open_window(self):
+ self.close_all_windows()
+
+ self.assertListEqual([], self.marionette.close_chrome_window())
+ self.assertListEqual([self.start_tab], self.marionette.window_handles)
+ self.assertListEqual([self.start_window], self.marionette.chrome_window_handles)
+ self.assertIsNotNone(self.marionette.session)
+
+ @skip_if_mobile("Needs application independent method to open a new tab")
+ def test_close_window_for_browser_tab(self):
+ tab = self.open_tab()
+ self.marionette.switch_to_window(tab)
+
+ window_handles = self.marionette.close()
+ self.assertNotIn(tab, window_handles)
+ self.assertListEqual(self.start_tabs, window_handles)
+
+ @skip_if_mobile("Interacting with chrome windows not available for Fennec")
+ def test_close_window_for_browser_window_with_single_tab(self):
+ win = self.open_window()
+ self.marionette.switch_to_window(win)
+
+ self.assertEqual(len(self.start_tabs) + 1, len(self.marionette.window_handles))
+ window_handles = self.marionette.close()
+ self.assertNotIn(win, window_handles)
+ self.assertListEqual(self.start_tabs, window_handles)
+ self.assertListEqual(self.start_windows, self.marionette.chrome_window_handles)
+
+ def test_close_window_for_last_open_tab(self):
+ self.close_all_tabs()
+
+ self.assertListEqual([], self.marionette.close())
+ self.assertListEqual([self.start_tab], self.marionette.window_handles)
+ self.assertListEqual([self.start_window], self.marionette.chrome_window_handles)
+ self.assertIsNotNone(self.marionette.session)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_window_handles_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_window_handles_chrome.py
new file mode 100644
index 000000000..7260d6324
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_window_handles_chrome.py
@@ -0,0 +1,207 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver import By, Wait
+
+from marionette_harness import MarionetteTestCase, WindowManagerMixin
+
+
+class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
+
+ def setUp(self):
+ super(TestWindowHandles, self).setUp()
+
+ self.empty_page = self.marionette.absolute_url("empty.html")
+ self.test_page = self.marionette.absolute_url("windowHandles.html")
+ self.marionette.navigate(self.test_page)
+
+ self.marionette.set_context("chrome")
+
+ def tearDown(self):
+ self.close_all_windows()
+ self.close_all_tabs()
+
+ super(TestWindowHandles, self).tearDown()
+
+ def test_chrome_window_handles_with_scopes(self):
+ # Open a browser and a non-browser (about window) chrome window
+ self.open_window(
+ trigger=lambda: self.marionette.execute_script("window.open();"))
+ self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows) + 1)
+ self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
+
+ self.open_window(
+ trigger=lambda: self.marionette.find_element(By.ID, "aboutName").click())
+ self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows) + 2)
+ self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
+
+ chrome_window_handles_in_chrome_scope = self.marionette.chrome_window_handles
+ window_handles_in_chrome_scope = self.marionette.window_handles
+
+ with self.marionette.using_context("content"):
+ self.assertEqual(self.marionette.chrome_window_handles,
+ chrome_window_handles_in_chrome_scope)
+ self.assertEqual(self.marionette.window_handles,
+ window_handles_in_chrome_scope)
+
+ def test_chrome_window_handles_after_opening_new_window(self):
+ def open_with_link():
+ with self.marionette.using_context("content"):
+ link = self.marionette.find_element(By.ID, "new-window")
+ link.click()
+
+ # We open a new window but are actually interested in the new tab
+ new_win = self.open_window(trigger=open_with_link)
+ self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows) + 1)
+ self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
+
+ # Check that the new tab has the correct page loaded
+ self.marionette.switch_to_window(new_win)
+ self.assertEqual(self.marionette.current_chrome_window_handle, new_win)
+ with self.marionette.using_context("content"):
+ Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
+ lambda mn: mn.get_url() == self.empty_page,
+ message="{} did not load after opening a new tab".format(self.empty_page))
+
+ # Ensure navigate works in our current window
+ other_page = self.marionette.absolute_url("test.html")
+ with self.marionette.using_context("content"):
+ self.marionette.navigate(other_page)
+ self.assertEqual(self.marionette.get_url(), other_page)
+
+ # Close the opened window and carry on in our original tab.
+ self.marionette.close()
+ self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows))
+
+ self.marionette.switch_to_window(self.start_window)
+ self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
+ with self.marionette.using_context("content"):
+ self.assertEqual(self.marionette.get_url(), self.test_page)
+
+ def test_window_handles_after_opening_new_tab(self):
+ def open_with_link():
+ with self.marionette.using_context("content"):
+ link = self.marionette.find_element(By.ID, "new-tab")
+ link.click()
+
+ new_tab = self.open_tab(trigger=open_with_link)
+ self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
+ self.assertEqual(self.marionette.current_window_handle, self.start_tab)
+
+ self.marionette.switch_to_window(new_tab)
+ self.assertEqual(self.marionette.current_window_handle, new_tab)
+ with self.marionette.using_context("content"):
+ Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
+ lambda mn: mn.get_url() == self.empty_page,
+ message="{} did not load after opening a new tab".format(self.empty_page))
+
+ # Ensure navigate works in our current tab
+ other_page = self.marionette.absolute_url("test.html")
+ with self.marionette.using_context("content"):
+ self.marionette.navigate(other_page)
+ self.assertEqual(self.marionette.get_url(), other_page)
+
+ self.marionette.switch_to_window(self.start_tab)
+ self.assertEqual(self.marionette.current_window_handle, self.start_tab)
+ with self.marionette.using_context("content"):
+ self.assertEqual(self.marionette.get_url(), self.test_page)
+
+ self.marionette.switch_to_window(new_tab)
+ self.marionette.close()
+ self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs))
+
+ self.marionette.switch_to_window(self.start_tab)
+ self.assertEqual(self.marionette.current_window_handle, self.start_tab)
+
+ def test_window_handles_after_opening_new_window(self):
+ def open_with_link():
+ with self.marionette.using_context("content"):
+ link = self.marionette.find_element(By.ID, "new-window")
+ link.click()
+
+ # We open a new window but are actually interested in the new tab
+ new_tab = self.open_tab(trigger=open_with_link)
+ self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
+ self.assertEqual(self.marionette.current_window_handle, self.start_tab)
+
+ # Check that the new tab has the correct page loaded
+ self.marionette.switch_to_window(new_tab)
+ self.assertEqual(self.marionette.current_window_handle, new_tab)
+ with self.marionette.using_context("content"):
+ Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
+ lambda mn: mn.get_url() == self.empty_page,
+ message="{} did not load after opening a new tab".format(self.empty_page))
+
+ # Ensure navigate works in our current window
+ other_page = self.marionette.absolute_url("test.html")
+ with self.marionette.using_context("content"):
+ self.marionette.navigate(other_page)
+ self.assertEqual(self.marionette.get_url(), other_page)
+
+ # Close the opened window and carry on in our original tab.
+ self.marionette.close()
+ self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs))
+
+ self.marionette.switch_to_window(self.start_tab)
+ self.assertEqual(self.marionette.current_window_handle, self.start_tab)
+ with self.marionette.using_context("content"):
+ self.assertEqual(self.marionette.get_url(), self.test_page)
+
+ def test_window_handles_after_closing_original_tab(self):
+ def open_with_link():
+ with self.marionette.using_context("content"):
+ link = self.marionette.find_element(By.ID, "new-tab")
+ link.click()
+
+ new_tab = self.open_tab(trigger=open_with_link)
+ self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
+ self.assertEqual(self.marionette.current_window_handle, self.start_tab)
+
+ self.marionette.close()
+ self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs))
+
+ self.marionette.switch_to_window(new_tab)
+ self.assertEqual(self.marionette.current_window_handle, new_tab)
+ with self.marionette.using_context("content"):
+ Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
+ lambda mn: mn.get_url() == self.empty_page,
+ message="{} did not load after opening a new tab".format(self.empty_page))
+
+ def test_window_handles_no_switch(self):
+ """Regression test for bug 1294456.
+ This test is testing the case where Marionette attempts to send a
+ command to a window handle when the browser has opened and selected
+ a new tab. Before bug 1294456 landed, the Marionette driver was getting
+ confused about which window handle the client cared about, and assumed
+ it was the window handle for the newly opened and selected tab.
+
+ This caused Marionette to think that the browser needed to do a remoteness
+ flip in the e10s case, since the tab opened by menu_newNavigatorTab is
+ about:newtab (which is currently non-remote). This meant that commands
+ sent to what should have been the original window handle would be
+ queued and never sent, since the remoteness flip in the new tab was
+ never going to happen.
+ """
+ def open_with_menu():
+ menu_new_tab = self.marionette.find_element(By.ID, 'menu_newNavigatorTab')
+ menu_new_tab.click()
+
+ new_tab = self.open_tab(trigger=open_with_menu)
+
+ # We still have the default tab set as our window handle. This
+ # get_url command should be sent immediately, and not be forever-queued.
+ with self.marionette.using_context("content"):
+ self.assertEqual(self.marionette.get_url(), self.test_page)
+
+ self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
+ self.assertEqual(self.marionette.current_window_handle, self.start_tab)
+
+ self.marionette.switch_to_window(new_tab)
+ self.assertEqual(self.marionette.current_window_handle, new_tab)
+
+ self.marionette.close()
+ self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs))
+
+ self.marionette.switch_to_window(self.start_tab)
+ self.assertEqual(self.marionette.current_window_handle, self.start_tab)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_window_handles_content.py b/testing/marionette/harness/marionette_harness/tests/unit/test_window_handles_content.py
new file mode 100644
index 000000000..b6ad3a6c3
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_window_handles_content.py
@@ -0,0 +1,96 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver import By, Wait
+
+from marionette_harness import MarionetteTestCase, WindowManagerMixin
+
+
+class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
+
+ def setUp(self):
+ super(TestWindowHandles, self).setUp()
+
+ self.empty_page = self.marionette.absolute_url("empty.html")
+ self.test_page = self.marionette.absolute_url("windowHandles.html")
+ self.marionette.navigate(self.test_page)
+
+ def tearDown(self):
+ self.close_all_tabs()
+
+ super(TestWindowHandles, self).tearDown()
+
+ def test_window_handles_after_opening_new_tab(self):
+ def open_with_link():
+ link = self.marionette.find_element(By.ID, "new-tab")
+ link.click()
+
+ new_tab = self.open_tab(trigger=open_with_link)
+ self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
+ self.assertEqual(self.marionette.current_window_handle, self.start_tab)
+
+ self.marionette.switch_to_window(new_tab)
+ self.assertEqual(self.marionette.current_window_handle, new_tab)
+ Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
+ lambda mn: mn.get_url() == self.empty_page,
+ message="{} did not load after opening a new tab".format(self.empty_page))
+
+ self.marionette.switch_to_window(self.start_tab)
+ self.assertEqual(self.marionette.current_window_handle, self.start_tab)
+ self.assertEqual(self.marionette.get_url(), self.test_page)
+
+ self.marionette.switch_to_window(new_tab)
+ self.marionette.close()
+ self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs))
+
+ self.marionette.switch_to_window(self.start_tab)
+ self.assertEqual(self.marionette.current_window_handle, self.start_tab)
+
+ def test_window_handles_after_opening_new_window(self):
+ def open_with_link():
+ link = self.marionette.find_element(By.ID, "new-window")
+ link.click()
+
+ # We open a new window but are actually interested in the new tab
+ new_tab = self.open_tab(trigger=open_with_link)
+ self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
+ self.assertEqual(self.marionette.current_window_handle, self.start_tab)
+
+ # Check that the new tab has the correct page loaded
+ self.marionette.switch_to_window(new_tab)
+ self.assertEqual(self.marionette.current_window_handle, new_tab)
+ Wait(self.marionette, self.marionette.timeout.page_load).until(
+ lambda _: self.marionette.get_url() == self.empty_page,
+ message="The expected page '{}' has not been loaded".format(self.empty_page))
+
+ # Ensure navigate works in our current window
+ other_page = self.marionette.absolute_url("test.html")
+ self.marionette.navigate(other_page)
+ self.assertEqual(self.marionette.get_url(), other_page)
+
+ # Close the opened window and carry on in our original tab.
+ self.marionette.close()
+ self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs))
+
+ self.marionette.switch_to_window(self.start_tab)
+ self.assertEqual(self.marionette.current_window_handle, self.start_tab)
+ self.assertEqual(self.marionette.get_url(), self.test_page)
+
+ def test_window_handles_after_closing_original_tab(self):
+ def open_with_link():
+ link = self.marionette.find_element(By.ID, "new-tab")
+ link.click()
+
+ new_tab = self.open_tab(trigger=open_with_link)
+ self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
+ self.assertEqual(self.marionette.current_window_handle, self.start_tab)
+
+ self.marionette.close()
+ self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs))
+
+ self.marionette.switch_to_window(new_tab)
+ self.assertEqual(self.marionette.current_window_handle, new_tab)
+ Wait(self.marionette, self.marionette.timeout.page_load).until(
+ lambda _: self.marionette.get_url() == self.empty_page,
+ message="The expected page '{}' has not been loaded".format(self.empty_page))
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_window_position.py b/testing/marionette/harness/marionette_harness/tests/unit/test_window_position.py
new file mode 100644
index 000000000..ac5365806
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_window_position.py
@@ -0,0 +1,42 @@
+#Copyright 2007-2009 WebDriver committers
+#
+#Licensed under the Apache License, Version 2.0 (the "License");
+#you may not use this file except in compliance with the License.
+#You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#Unless required by applicable law or agreed to in writing, software
+#distributed under the License is distributed on an "AS IS" BASIS,
+#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#See the License for the specific language governing permissions and
+#limitations under the License.
+
+from marionette_driver.errors import InvalidArgumentException
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestWindowPosition(MarionetteTestCase):
+ def test_get_types(self):
+ position = self.marionette.get_window_position()
+ self.assertTrue(isinstance(position["x"], int))
+ self.assertTrue(isinstance(position["y"], int))
+
+ def test_set_types(self):
+ for x, y in (["a", "b"], [1.2, 3.4], [True, False], [[], []], [{}, {}]):
+ with self.assertRaises(InvalidArgumentException):
+ self.marionette.set_window_position(x, y)
+
+ def test_out_of_bounds_arguments(self):
+ with self.assertRaises(InvalidArgumentException):
+ self.marionette.set_window_position(-1, 0)
+ with self.assertRaises(InvalidArgumentException):
+ self.marionette.set_window_position(0, -1)
+
+ def test_move(self):
+ old_position = self.marionette.get_window_position()
+ new_position = {"x": old_position["x"] + 10, "y": old_position["y"] + 10}
+ self.marionette.set_window_position(new_position["x"], new_position["y"])
+ self.assertNotEqual(old_position['x'], new_position["x"])
+ self.assertNotEqual(old_position['y'], new_position["y"])
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_window_title.py b/testing/marionette/harness/marionette_harness/tests/unit/test_window_title.py
new file mode 100644
index 000000000..4f6e3ccf7
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_window_title.py
@@ -0,0 +1,12 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestTitle(MarionetteTestCase):
+ def test_get_html_title(self):
+ test_html = self.marionette.absolute_url("test.html")
+ self.marionette.navigate(test_html)
+ self.assertEqual('Marionette Test', self.marionette.title)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_window_title_chrome.py b/testing/marionette/harness/marionette_harness/tests/unit/test_window_title_chrome.py
new file mode 100644
index 000000000..7bee682fd
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_window_title_chrome.py
@@ -0,0 +1,26 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestTitleChrome(MarionetteTestCase):
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ self.marionette.set_context("chrome")
+ self.win = self.marionette.current_window_handle
+ self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen');")
+ self.marionette.switch_to_window('foo')
+ self.assertNotEqual(self.win, self.marionette.current_window_handle)
+
+ def tearDown(self):
+ self.assertNotEqual(self.win, self.marionette.current_window_handle)
+ self.marionette.execute_script("window.close();")
+ self.marionette.switch_to_window(self.win)
+ MarionetteTestCase.tearDown(self)
+
+ def test_get_chrome_title(self):
+ title = self.marionette.execute_script("return window.document.documentElement.getAttribute('title');")
+ self.assertEqual(title, self.marionette.title)
+ self.assertEqual('Title Test', self.marionette.title)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_window_type.py b/testing/marionette/harness/marionette_harness/tests/unit/test_window_type.py
new file mode 100644
index 000000000..6c5e75f51
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_window_type.py
@@ -0,0 +1,27 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestWindowTypeChrome(MarionetteTestCase):
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+ self.marionette.set_context("chrome")
+ self.win = self.marionette.current_window_handle
+ self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen');")
+ self.marionette.switch_to_window('foo')
+ self.assertNotEqual(self.win, self.marionette.current_window_handle)
+
+ def tearDown(self):
+ self.assertNotEqual(self.win, self.marionette.current_window_handle)
+ self.marionette.execute_script("window.close();")
+ self.marionette.switch_to_window(self.win)
+ MarionetteTestCase.tearDown(self)
+
+ def test_get_window_type(self):
+ window_type = self.marionette.execute_script("return window.document.documentElement.getAttribute('windowtype');")
+ self.assertEqual(window_type, self.marionette.get_window_type())
+ self.assertEqual('Test Type', self.marionette.get_window_type())
+
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_with_using_context.py b/testing/marionette/harness/marionette_harness/tests/unit/test_with_using_context.py
new file mode 100644
index 000000000..1b2d60d2d
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_with_using_context.py
@@ -0,0 +1,66 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_driver.decorators import using_context
+from marionette_driver.errors import MarionetteException
+
+from marionette_harness import MarionetteTestCase
+
+
+class TestSetContext(MarionetteTestCase):
+ def setUp(self):
+ MarionetteTestCase.setUp(self)
+
+ # shortcuts to improve readability of these tests
+ self.chrome = self.marionette.CONTEXT_CHROME
+ self.content = self.marionette.CONTEXT_CONTENT
+
+ test_url = self.marionette.absolute_url("empty.html")
+ self.marionette.navigate(test_url)
+ self.marionette.set_context(self.content)
+ self.assertEquals(self.get_context(), self.content)
+
+ def get_context(self):
+ return self.marionette._send_message("getContext", key="value")
+
+ def test_set_different_context_using_with_block(self):
+ with self.marionette.using_context(self.chrome):
+ self.assertEquals(self.get_context(), self.chrome)
+ self.assertEquals(self.get_context(), self.content)
+
+ def test_set_same_context_using_with_block(self):
+ with self.marionette.using_context(self.content):
+ self.assertEquals(self.get_context(), self.content)
+ self.assertEquals(self.get_context(), self.content)
+
+ def test_nested_with_blocks(self):
+ with self.marionette.using_context(self.chrome):
+ self.assertEquals(self.get_context(), self.chrome)
+ with self.marionette.using_context(self.content):
+ self.assertEquals(self.get_context(), self.content)
+ self.assertEquals(self.get_context(), self.chrome)
+ self.assertEquals(self.get_context(), self.content)
+
+ def test_set_scope_while_in_with_block(self):
+ with self.marionette.using_context(self.chrome):
+ self.assertEquals(self.get_context(), self.chrome)
+ self.marionette.set_context(self.content)
+ self.assertEquals(self.get_context(), self.content)
+ self.assertEquals(self.get_context(), self.content)
+
+ def test_exception_raised_while_in_with_block_is_propagated(self):
+ with self.assertRaises(MarionetteException):
+ with self.marionette.using_context(self.chrome):
+ raise MarionetteException
+ self.assertEquals(self.get_context(), self.content)
+
+ def test_with_using_context_decorator(self):
+ @using_context('content')
+ def inner_content(m):
+ self.assertEquals(self.get_context(), 'content')
+ @using_context('chrome')
+ def inner_chrome(m):
+ self.assertEquals(self.get_context(), 'chrome')
+ inner_content(self.marionette)
+ inner_chrome(self.marionette)
diff --git a/testing/marionette/harness/marionette_harness/tests/unit/unit-tests.ini b/testing/marionette/harness/marionette_harness/tests/unit/unit-tests.ini
new file mode 100644
index 000000000..573096378
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/unit-tests.ini
@@ -0,0 +1,132 @@
+[test_marionette.py]
+[test_geckoinstance.py]
+[test_data_driven.py]
+[test_session.py]
+[test_capabilities.py]
+[test_accessibility.py]
+[test_expectedfail.py]
+expected = fail
+[test_import_script.py]
+[test_click.py]
+[test_click_chrome.py]
+skip-if = appname == 'fennec'
+[test_checkbox.py]
+[test_checkbox_chrome.py]
+skip-if = appname == 'fennec'
+[test_elementsize.py]
+[test_elementsize_chrome.py]
+skip-if = appname == 'fennec'
+[test_position.py]
+[test_rendered_element.py]
+[test_chrome_element_css.py]
+skip-if = appname == 'fennec'
+[test_element_state.py]
+[test_element_state_chrome.py]
+skip-if = appname == 'fennec'
+[test_text.py]
+[test_text_chrome.py]
+skip-if = true # "Bug 896046"
+
+[test_clearing.py]
+[test_typing.py]
+
+[test_log.py]
+
+[test_about_pages.py]
+
+[test_execute_async_script.py]
+[test_execute_script.py]
+[test_simpletest_fail.js]
+[test_element_retrieval.py]
+[test_findelement_chrome.py]
+skip-if = appname == 'fennec'
+
+[test_navigation.py]
+
+[test_timeouts.py]
+
+[test_single_finger_desktop.py]
+skip-if = appname == 'fennec' || os == "win" # Bug 1025040
+
+[test_simpletest_pass.js]
+[test_simpletest_sanity.py]
+[test_simpletest_chrome.js]
+[test_simpletest_timeout.js]
+[test_anonymous_content.py]
+skip-if = appname == 'fennec'
+[test_switch_frame.py]
+skip-if = os == "win" # Bug 1078237
+[test_switch_frame_chrome.py]
+skip-if = appname == 'fennec'
+[test_switch_remote_frame.py]
+skip-if = appname == 'fennec'
+[test_switch_window_chrome.py]
+skip-if = appname == 'fennec'
+[test_switch_window_content.py]
+
+[test_pagesource.py]
+[test_pagesource_chrome.py]
+skip-if = appname == 'fennec'
+
+[test_visibility.py]
+[test_window_handles_chrome.py]
+skip-if = appname == 'fennec'
+[test_window_handles_content.py]
+[test_window_close_chrome.py]
+skip-if = appname == 'fennec'
+[test_window_close_content.py]
+[test_window_position.py]
+skip-if = appname == 'fennec'
+
+[test_screenshot.py]
+[test_cookies.py]
+[test_window_title.py]
+[test_window_title_chrome.py]
+skip-if = appname == 'fennec'
+[test_window_type.py]
+skip-if = appname == 'fennec'
+[test_implicit_waits.py]
+[test_wait.py]
+[test_expected.py]
+[test_date_time_value.py]
+[test_getactiveframe_oop.py]
+skip-if = true # Bug 925688
+[test_chrome_async_finish.js]
+[test_screen_orientation.py]
+[test_errors.py]
+
+[test_execute_isolate.py]
+[test_click_scrolling.py]
+[test_profile_management.py]
+skip-if = manage_instance == false || appname == 'fennec' # Bug 1298921 and bug 1322993
+[test_quit_restart.py]
+skip-if = manage_instance == false || appname == 'fennec' # Bug 1298921 and bug 1322993
+[test_set_window_size.py]
+skip-if = os == "linux" || appname == 'fennec' # Bug 1085717
+[test_with_using_context.py]
+
+[test_modal_dialogs.py]
+skip-if = appname == 'fennec' # Bug 1325738
+[test_key_actions.py]
+[test_mouse_action.py]
+skip-if = appname == 'fennec'
+[test_teardown_context_preserved.py]
+[test_file_upload.py]
+skip-if = appname == 'fennec' || os == "win" # http://bugs.python.org/issue14574
+
+[test_execute_sandboxes.py]
+[test_using_permissions.py]
+[test_prefs.py]
+
+[test_shadow_dom.py]
+
+[test_chrome.py]
+skip-if = appname == 'fennec'
+
+[test_addons.py]
+skip-if = appname == 'fennec' # Bug 1330598
+
+[test_select.py]
+[test_crash.py]
+skip-if = manage_instance == false || appname == 'fennec' # Bug 1298921 and bug 1322993
+[test_localization.py]
diff --git a/testing/marionette/harness/marionette_harness/tests/webapi-tests.ini b/testing/marionette/harness/marionette_harness/tests/webapi-tests.ini
new file mode 100644
index 000000000..2c9dd1dce
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/webapi-tests.ini
@@ -0,0 +1,8 @@
+[include:../../../../../dom/system/gonk/tests/marionette/manifest.ini]
+[include:../../../../../dom/system/tests/marionette/manifest.ini]
+skip-if = android_version > '15' # Bug 1203072
+[include:../../../../../dom/events/test/marionette/manifest.ini]
+[include:../../../../../dom/wifi/test/marionette/manifest.ini]
+[include:../../../../../dom/tethering/tests/marionette/manifest.ini]
+skip-if = android_version > '15' # Bug 1203075
+[include:../../../../../dom/network/tests/marionette/manifest.ini]