WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
Patch
wd-update-tests.diff (text/plain), 716.67 KB, created by
Carlos Garcia Campos
on 2021-09-22 06:14:14 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Carlos Garcia Campos
Created:
2021-09-22 06:14:14 PDT
Size:
716.67 KB
patch
obsolete
>diff --git a/Tools/ChangeLog b/Tools/ChangeLog >index 18856cda3fb8..ab1cee0e73a2 100644 >--- a/Tools/ChangeLog >+++ b/Tools/ChangeLog >@@ -1,3 +1,25 @@ >+2021-09-22 Carlos Garcia Campos <cgarcia@igalia.com> >+ >+ [WebDriver] Update w3c and selenium tests >+ https://bugs.webkit.org/show_bug.cgi?id=230551 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * Scripts/run-webdriver-tests: Run with python3 >+ * Scripts/webkitpy/__init__.py: Use a newer version of pytest and its dependencies to match what tests expect. >+ * Scripts/webkitpy/webdriver_tests/pytest_runner.py: >+ (collect): Use contextlib to redirect stdout to /dev/null while collecting tests. >+ (run): Use ExitCode new enum added to pytest. >+ * Scripts/webkitpy/webdriver_tests/webdriver_selenium_executor.py: >+ (WebDriverSeleniumExecutor.__init__): Do not encode paths. >+ * Scripts/webkitpy/webdriver_tests/webdriver_test_runner_selenium.py: >+ (WebDriverTestRunnerSelenium.collect_tests): Use items() instead of iteritems() that doesn't exist in python3. >+ * Scripts/webkitpy/webdriver_tests/webdriver_w3c_executor.py: >+ (WebKitDriverServer.__init__): >+ (WebDriverW3CExecutor.__init__): >+ (WebDriverW3CExecutor.setup): >+ (WebDriverW3CExecutor._runner): >+ > 2021-09-22 Carlos Garcia Campos <cgarcia@igalia.com> > > REGRESSION(r282082): [GTK] Tests timeout when using xvfb after r282082 >diff --git a/Tools/Scripts/run-webdriver-tests b/Tools/Scripts/run-webdriver-tests >index f49174963f8e..e246843453fa 100755 >--- a/Tools/Scripts/run-webdriver-tests >+++ b/Tools/Scripts/run-webdriver-tests >@@ -1,4 +1,4 @@ >-#!/usr/bin/env python >+#!/usr/bin/env python3 > > # Copyright (C) 2017 Igalia S.L. > # >diff --git a/Tools/Scripts/webkitpy/__init__.py b/Tools/Scripts/webkitpy/__init__.py >index eb680e5a489c..1768e886b5dc 100644 >--- a/Tools/Scripts/webkitpy/__init__.py >+++ b/Tools/Scripts/webkitpy/__init__.py >@@ -53,16 +53,17 @@ AutoInstall.register(Package('funcsigs', Version(1, 0, 2))) > AutoInstall.register(Package('importlib_metadata', Version(1, 7, 0))) > AutoInstall.register(Package('genshi', Version(0, 7, 3), pypi_name='Genshi')) > AutoInstall.register(Package('html5lib', Version(1, 1))) >+AutoInstall.register(Package('iniconfig', Version(1, 1, 1))) > AutoInstall.register(Package('mechanize', Version(0, 4, 5))) > AutoInstall.register(Package('more_itertools', Version(4, 2, 0), pypi_name='more-itertools')) >-AutoInstall.register(Package('mozprocess', Version(1, 2, 0))) >-AutoInstall.register(Package('mozlog', Version(6, 1))) >+AutoInstall.register(Package('mozprocess', Version(1, 3, 0))) >+AutoInstall.register(Package('mozlog', Version(7, 1, 0), wheel=True)) > AutoInstall.register(Package('mozterm', Version(1, 0, 0))) >-AutoInstall.register(Package('pluggy', Version(0, 5, 2))) >+AutoInstall.register(Package('pluggy', Version(0, 13, 1))) > AutoInstall.register(Package('py', Version(1, 5, 2))) >+AutoInstall.register(Package('pytest_asyncio', Version(0, 14), pypi_name='pytest-asyncio')) > AutoInstall.register(Package('pytest_timeout', Version(1, 4, 2), pypi_name='pytest-timeout')) >-# Pytest held to 3.x due to WPT webdriver compatibility >-AutoInstall.register(Package('pytest', Version(3, 6, 2), implicit_deps=['pytest_timeout'])) >+AutoInstall.register(Package('pytest', Version(6, 1, 2), implicit_deps=['pytest_asyncio', 'pytest_timeout'])) > AutoInstall.register(Package('pycodestyle', Version(2, 5, 0))) > AutoInstall.register(Package('scandir', Version(1, 10, 0))) > AutoInstall.register(Package('soupsieve', Version(1, 9, 6))) >@@ -70,6 +71,7 @@ AutoInstall.register(Package('selenium', Version(3, 141, 0))) > AutoInstall.register(Package('toml', Version(0, 10, 1))) > AutoInstall.register(Package('wcwidth', Version(0, 2, 5))) > AutoInstall.register(Package('webencodings', Version(0, 5, 1))) >+AutoInstall.register(Package('websockets', Version(8, 1))) > AutoInstall.register(Package('zipp', Version(1, 2, 0))) > AutoInstall.register(Package('zope.interface', Version(5, 1, 0), aliases=['zope'], pypi_name='zope-interface')) > >diff --git a/Tools/Scripts/webkitpy/webdriver_tests/pytest_runner.py b/Tools/Scripts/webkitpy/webdriver_tests/pytest_runner.py >index 0ec65f94bfac..f1e4148522b1 100644 >--- a/Tools/Scripts/webkitpy/webdriver_tests/pytest_runner.py >+++ b/Tools/Scripts/webkitpy/webdriver_tests/pytest_runner.py >@@ -20,6 +20,7 @@ > # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS > # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. > >+import contextlib > import errno > import os > import shutil >@@ -29,7 +30,7 @@ import tempfile > from webkitpy.common.system.filesystem import FileSystem > from webkitpy.common.webkit_finder import WebKitFinder > import pytest >-from _pytest.main import EXIT_INTERNALERROR >+from _pytest.config import ExitCode > > > class TemporaryDirectory(object): >@@ -168,16 +169,14 @@ class TestExpectationsMarker(object): > > def collect(directory, args, ignore_param=None): > collect_recorder = CollectRecorder(ignore_param) >- stdout = sys.stdout >- with open(os.devnull, 'wb') as devnull: >- sys.stdout = devnull >- with TemporaryDirectory() as cache_directory: >- cmd = ['--collect-only', >- '--basetemp=%s' % cache_directory] >- cmd.extend(args) >- cmd.append(directory) >- pytest.main(cmd, plugins=[collect_recorder]) >- sys.stdout = stdout >+ with open(os.devnull, 'w') as f: >+ with contextlib.redirect_stdout(f): >+ with TemporaryDirectory() as cache_directory: >+ cmd = ['--collect-only', >+ '--basetemp', cache_directory] >+ cmd.extend(args) >+ cmd.append(directory) >+ pytest.main(cmd, plugins=[collect_recorder]) > return collect_recorder.tests > > >@@ -192,16 +191,16 @@ def run(path, args, timeout, env, expectations, ignore_param=None): > with TemporaryDirectory() as cache_directory: > try: > cmd = ['-vv', >- '--capture=no', >- '--basetemp=%s' % cache_directory, >+ '--capture', 'no', >+ '--basetemp', cache_directory, > '--showlocals', >- '--timeout=%s' % timeout, >+ '--timeout', str(timeout), > '-p', 'no:cacheprovider'] > cmd.extend(args) > cmd.append(path) > result = pytest.main(cmd, plugins=[harness_recorder, subtests_recorder, expectations_marker]) > >- if result == EXIT_INTERNALERROR: >+ if result == ExitCode.INTERNAL_ERROR: > harness_recorder.outcome = ('ERROR', None) > except Exception as e: > harness_recorder.outcome = ('ERROR', str(e)) >diff --git a/Tools/Scripts/webkitpy/webdriver_tests/webdriver_selenium_executor.py b/Tools/Scripts/webkitpy/webdriver_tests/webdriver_selenium_executor.py >index fbd7626f559e..5e57483da624 100644 >--- a/Tools/Scripts/webkitpy/webdriver_tests/webdriver_selenium_executor.py >+++ b/Tools/Scripts/webkitpy/webdriver_tests/webdriver_selenium_executor.py >@@ -40,8 +40,8 @@ class WebDriverSeleniumExecutor(object): > self._env = env > self._env.update(driver.browser_env()) > >- self._args = ['--driver=%s' % self._driver_name, '--driver-binary=%s' % driver.binary_path().encode()] >- browser_path = driver.browser_path().encode() >+ self._args = ['--driver=%s' % self._driver_name, '--driver-binary=%s' % driver.binary_path()] >+ browser_path = driver.browser_path() > if browser_path: > self._args.extend(['--browser-binary=%s' % browser_path]) > browser_args = driver.browser_args() >diff --git a/Tools/Scripts/webkitpy/webdriver_tests/webdriver_test_runner_selenium.py b/Tools/Scripts/webkitpy/webdriver_tests/webdriver_test_runner_selenium.py >index b0beea0f9142..06512e4fd76f 100644 >--- a/Tools/Scripts/webkitpy/webdriver_tests/webdriver_test_runner_selenium.py >+++ b/Tools/Scripts/webkitpy/webdriver_tests/webdriver_test_runner_selenium.py >@@ -50,7 +50,7 @@ class WebDriverTestRunnerSelenium(object): > # Collected tests are relative to test directory. > base_dir = os.path.join(self._tests_dir, os.path.dirname(relative_tests_dir)) > collected_tests = {} >- for test, subtests in executor.collect(os.path.join(self._tests_dir, relative_tests_dir)).iteritems(): >+ for test, subtests in executor.collect(os.path.join(self._tests_dir, relative_tests_dir)).items(): > collected_tests[os.path.join(base_dir, test)] = subtests > selenium_tests = [] > if not tests: >diff --git a/Tools/Scripts/webkitpy/webdriver_tests/webdriver_w3c_executor.py b/Tools/Scripts/webkitpy/webdriver_tests/webdriver_w3c_executor.py >index cc2d075bc0af..1843de553779 100644 >--- a/Tools/Scripts/webkitpy/webdriver_tests/webdriver_w3c_executor.py >+++ b/Tools/Scripts/webkitpy/webdriver_tests/webdriver_w3c_executor.py >@@ -115,8 +115,8 @@ class WebKitDriverServer(WebDriverServer): > default_base_path = '/' > test_env = None > >- def __init__(self, logger, binary=None, port=None, base_path='', args=None): >- WebDriverServer.__init__(self, logger, binary, port=port, base_path=base_path, args=args, env=self.test_env) >+ def __init__(self, logger, binary=None, port=None, base_path='', env=None, args=None): >+ WebDriverServer.__init__(self, logger, binary, port=port, base_path=base_path, env=self.test_env, args=args) > > def make_command(self): > return [self.binary, '--port=%s' % str(self.port)] + self._args >@@ -135,8 +135,8 @@ class WebDriverW3CExecutor(WdspecExecutor): > server_config = {'browser_host': server.host(), > 'domains': {'': {'': server.host()}, > 'alt':{ '': '127.0.0.1'}}, >- 'ports': {'http': [str(server.http_port())], >- 'https': [str(server.https_port())]}, >+ 'ports': {'http': [server.http_port()], >+ 'https': [server.https_port()]}, > 'doc_root': server.document_root()} > self.runner = TestRunner() > WdspecExecutor.__init__(self, self.runner.logger, driver.browser_name(), server_config, driver.binary_path(), None, capabilities=driver.capabilities()) >@@ -147,13 +147,13 @@ class WebDriverW3CExecutor(WdspecExecutor): > self._result_queue = Queue() > > def setup(self): >- self.protocol.setup(self.runner) >+ super(WebDriverW3CExecutor, self).setup(self.runner) > args = (self._test_queue, > self._result_queue, > self.protocol.session_config['host'], > str(self.protocol.session_config['port']), > json.dumps(self.protocol.session_config['capabilities']), >- json.dumps(self.server_config), >+ self.server_config, > self._timeout, > self._expectations) > self._process = Process(target=WebDriverW3CExecutor._runner, args=args) >@@ -174,13 +174,18 @@ class WebDriverW3CExecutor(WdspecExecutor): > if test == 'TEARDOWN': > break > >- env = {'WD_HOST': host, >- 'WD_PORT': port, >- 'WD_CAPABILITIES': capabilities, >- 'WD_SERVER_CONFIG': server_config} >- env.update(WebKitDriverServer.test_env) >- args = ['--strict', '-p', 'no:mozlog'] >- result_queue.put(pytest_runner.run(test, args, timeout, env, expectations)) >+ with pytest_runner.TemporaryDirectory() as cache_directory: >+ server_config_path = os.path.join(cache_directory, 'wd_server_config.json') >+ with open(server_config_path, 'w') as f: >+ json.dump(server_config, f) >+ >+ env = {'WD_HOST': host, >+ 'WD_PORT': port, >+ 'WD_CAPABILITIES': capabilities, >+ 'WD_SERVER_CONFIG_FILE': server_config_path} >+ env.update(WebKitDriverServer.test_env) >+ args = ['--strict', '-p', 'no:mozlog'] >+ result_queue.put(pytest_runner.run(test, args, timeout, env, expectations)) > > def run(self, test): > self._test_queue.put(test) >diff --git a/WebDriverTests/ChangeLog b/WebDriverTests/ChangeLog >index 902b92bfef9b..6d45808f50ef 100644 >--- a/WebDriverTests/ChangeLog >+++ b/WebDriverTests/ChangeLog >@@ -1,3 +1,17 @@ >+2021-09-22 Carlos Garcia Campos <cgarcia@igalia.com> >+ >+ [WebDriver] Update w3c and selenium tests >+ https://bugs.webkit.org/show_bug.cgi?id=230551 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * imported/selenium/common/src/web/javascriptPage.html: >+ * imported/selenium/importer.json: >+ * imported/selenium/py/: >+ * imported/w3c/importer.json: >+ * imported/w3c/tools/: >+ * imported/w3c/webdriver/: >+ > 2021-08-22 Lauro Moura <lmoura@igalia.com> > > [WPE][WebDriver] Basic fullscreen support for the headless backend >diff --git a/WebDriverTests/TestExpectations.json b/WebDriverTests/TestExpectations.json >index 45667f972f03..a2e670485c4e 100644 >--- a/WebDriverTests/TestExpectations.json >+++ b/WebDriverTests/TestExpectations.json >@@ -291,6 +291,30 @@ > }, > "test_no_browsing_context": { > "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}} >+ }, >+ "test_cookie_unsupported_scheme[about]": { >+ "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/230616"}} >+ }, >+ "test_cookie_unsupported_scheme[blob]": { >+ "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/230616"}} >+ }, >+ "test_cookie_unsupported_scheme[data]": { >+ "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/230616"}} >+ }, >+ "test_cookie_unsupported_scheme[file]": { >+ "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/230616"}} >+ }, >+ "test_cookie_unsupported_scheme[ftp]": { >+ "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/230616"}} >+ }, >+ "test_cookie_unsupported_scheme[javascript]": { >+ "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/230616"}} >+ }, >+ "test_cookie_unsupported_scheme[websocket]": { >+ "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/230616"}} >+ }, >+ "test_cookie_unsupported_scheme[secure websocket]": { >+ "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/230616"}} > } > } > }, >@@ -744,7 +768,10 @@ > "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/180645"}} > }, > "test_no_top_browsing_context": { >- "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}} >+ "expected": { >+ "wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}, >+ "gtk": {"status": ["FAIL"], "bug": "webkit.org/b/230612"} >+ } > }, > "test_no_browsing_context": { > "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}} >@@ -763,7 +790,10 @@ > "imported/w3c/webdriver/tests/element_click/click.py": { > "subtests": { > "test_no_top_browsing_context": { >- "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}} >+ "expected": { >+ "wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}, >+ "gtk": {"status": ["FAIL"], "bug": "webkit.org/b/230612"} >+ } > }, > "test_no_browsing_context": { > "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}} >@@ -935,7 +965,10 @@ > "imported/w3c/webdriver/tests/element_send_keys/send_keys.py": { > "subtests": { > "test_no_top_browsing_context": { >- "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}} >+ "expected": { >+ "wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}, >+ "gtk": {"status": ["FAIL"], "bug": "webkit.org/b/230612"} >+ } > }, > "test_no_browsing_context": { > "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}} >@@ -1152,7 +1185,10 @@ > "imported/w3c/webdriver/tests/get_element_attribute/get.py": { > "subtests": { > "test_no_top_browsing_context": { >- "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}} >+ "expected": { >+ "wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}, >+ "gtk": {"status": ["FAIL"], "bug": "webkit.org/b/230612"} >+ } > }, > "test_no_browsing_context": { > "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}} >@@ -1163,7 +1199,10 @@ > "imported/w3c/webdriver/tests/get_element_css_value/get.py": { > "subtests": { > "test_no_top_browsing_context": { >- "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}} >+ "expected": { >+ "wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}, >+ "gtk": {"status": ["FAIL"], "bug": "webkit.org/b/230612"} >+ } > }, > "test_no_browsing_context": { > "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}} >@@ -1174,7 +1213,10 @@ > "imported/w3c/webdriver/tests/get_element_property/get.py": { > "subtests": { > "test_no_top_browsing_context": { >- "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}} >+ "expected": { >+ "wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}, >+ "gtk": {"status": ["FAIL"], "bug": "webkit.org/b/230612"} >+ } > }, > "test_no_browsing_context": { > "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}} >@@ -1196,7 +1238,10 @@ > "imported/w3c/webdriver/tests/get_element_tag_name/get.py": { > "subtests": { > "test_no_top_browsing_context": { >- "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}} >+ "expected": { >+ "wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}, >+ "gtk": {"status": ["FAIL"], "bug": "webkit.org/b/230612"} >+ } > }, > "test_no_browsing_context": { > "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}} >@@ -1218,7 +1263,10 @@ > "imported/w3c/webdriver/tests/get_element_text/get.py": { > "subtests": { > "test_no_top_browsing_context": { >- "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}} >+ "expected": { >+ "wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}, >+ "gtk": {"status": ["FAIL"], "bug": "webkit.org/b/230612"} >+ } > }, > "test_no_browsing_context": { > "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}} >@@ -1368,7 +1416,10 @@ > "expected": {"all": {"status": ["FAIL"], "bug": "webkit.org/b/205923"}} > }, > "test_no_top_browsing_context": { >- "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}} >+ "expected": { >+ "wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}, >+ "gtk": {"status": ["FAIL"], "bug": "webkit.org/b/230612"} >+ } > }, > "test_no_browsing_context": { > "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}} >@@ -1379,7 +1430,10 @@ > "imported/w3c/webdriver/tests/is_element_selected/selected.py": { > "subtests": { > "test_no_top_browsing_context": { >- "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}} >+ "expected": { >+ "wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}, >+ "gtk": {"status": ["FAIL"], "bug": "webkit.org/b/230612"} >+ } > }, > "test_no_browsing_context": { > "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/212950"}} >@@ -1442,7 +1496,10 @@ > "imported/w3c/webdriver/tests/switch_to_window/switch.py": { > "subtests": { > "test_no_top_browsing_context": { >- "expected": {"wpe": {"status": ["FAIL"], "bug": "webkit.org/b/205994"}} >+ "expected": { >+ "wpe": {"status": ["FAIL"], "bug": "webkit.org/b/205994"}, >+ "gtk": {"status": ["FAIL"], "bug": "webkit.org/b/230612"} >+ } > } > } > }, >@@ -1495,5 +1552,29 @@ > }, > "imported/w3c/webdriver/tests/minimize_window/minimize.py" : { > "expected": { "wpe": { "status": ["SKIP"] } } >+ }, >+ "imported/w3c/webdriver/tests/find_element_from_shadow_root/find.py": { >+ "expected": { "all": { "status": ["SKIP"], "bug": "webkit.org/b/230614"}} >+ }, >+ "imported/w3c/webdriver/tests/find_element_from_shadow_root/user_prompts.py": { >+ "expected": { "all": { "status": ["SKIP"], "bug": "webkit.org/b/230614"}} >+ }, >+ "imported/w3c/webdriver/tests/find_elements_from_shadow_root/find.py": { >+ "expected": { "all": { "status": ["SKIP"], "bug": "webkit.org/b/230614"}} >+ }, >+ "imported/w3c/webdriver/tests/find_elements_from_shadow_root/user_prompts.py": { >+ "expected": { "all": { "status": ["SKIP"], "bug": "webkit.org/b/230614"}} >+ }, >+ "imported/w3c/webdriver/tests/get_element_shadow_root/get.py": { >+ "expected": { "all": { "status": ["SKIP"], "bug": "webkit.org/b/230614"}} >+ }, >+ "imported/w3c/webdriver/tests/get_element_shadow_root/user_prompts.py": { >+ "expected": { "all": { "status": ["SKIP"], "bug": "webkit.org/b/230614"}} >+ }, >+ "imported/w3c/webdriver/tests/bidi/new_session/connect.py": { >+ "expected": { "all": { "status": ["SKIP"], "bug": "webkit.org/b/230615"}} >+ }, >+ "imported/w3c/webdriver/tests/new_session/websocket_url.py": { >+ "expected": { "all": { "status": ["SKIP"], "bug": "webkit.org/b/230615"}} > } > } >diff --git a/WebDriverTests/imported/selenium/common/src/web/javascriptPage.html b/WebDriverTests/imported/selenium/common/src/web/javascriptPage.html >index fcac252e899a..1690ad07dd79 100644 >--- a/WebDriverTests/imported/selenium/common/src/web/javascriptPage.html >+++ b/WebDriverTests/imported/selenium/common/src/web/javascriptPage.html >@@ -86,7 +86,7 @@ > </div> > > </div> >- >+ > <div id="formageddon"> > <form action="#"> > Key Up: <input type="text" id="keyUp" onkeyup="javascript:updateContent(this)"/><br/> >@@ -151,15 +151,15 @@ > /> > > <input id="changeable" name="changeable" onfocus="appendMessage('focus')" onchange="appendMessage('change')" onblur="appendMessage('blur')"/> >- >- <button type="button" id="plainButton" >+ >+ <button type="button" id="plainButton" > onfocus="appendMessage('focus')" > onkeydown="appendMessage('keydown')" > onkeypress="appendMessage('keypress')" > onkeyup="appendMessage('keyup')" > onblur="appendMessage('blur')" > onclick="appendMessage('click')" >- onmousedown="appendMessage('mousedown ')" >+ onmousedown="appendMessage('mousedown ')" > onmouseup="appendMessage('mouseup ')" > onmouseover="register('mouseover ')" > onmousemove="register('mousemove ')" >@@ -173,7 +173,7 @@ > <button type="button" onclick="var element = document.getElementById('switchFocus'); var clickEvent = document.createEvent('MouseEvents'); clickEvent.initMouseEvent('click', true, true, null, 0, 0, 0, 0, 0,false, false, false, false, 0, element);element.dispatchEvent(clickEvent);">Do magic</button><br/> > <label id="labelForCheckbox" for="labeledCheckbox" onclick="appendMessage('labelclick')">Toggle checkbox</label><input type="checkbox" id="labeledCheckbox" onclick="appendMessage('chboxclick')"/> > </form> >- >+ > <form action="javascriptPage.html" id="submitListeningForm" onsubmit="appendMessage('form-onsubmit '); return false;"> > <p> > <input id="submitListeningForm-text" type="text" onsubmit="appendMessage('text-onsubmit ')" onclick="appendMessage('text-onclick ');" /> >@@ -198,7 +198,7 @@ > <a href="#" id="hiddenlink">ok</a> > </div> > </div> >- >+ > <div style="visibility: hidden;"> > <span> > <input id="unclickable" /> >@@ -265,7 +265,7 @@ > > <a id="new_window" onmouseup="window.open('closeable_window.html', 'close_me')" href="#">Click me to open a new window</a> > >-<a id="throwing-mouseover" onmouseover="throw new Error()" href="#throwing-mouseover">Mouse over me will throw a JS error</a> >+<a id="throwing-mouseover" onmouseover="throw new Error('I like cheese')" href="#throwing-mouseover">Mouse over me will throw a JS error</a> > > <div id="parent"> > <span id="movable" onmouseover="var p = document.getElementById('movable'); displayMessage('parent matches? ' + (p != event.relatedTarget));"> >diff --git a/WebDriverTests/imported/selenium/importer.json b/WebDriverTests/imported/selenium/importer.json >index d985c8edac75..5ba0c3a5d303 100644 >--- a/WebDriverTests/imported/selenium/importer.json >+++ b/WebDriverTests/imported/selenium/importer.json >@@ -1,6 +1,6 @@ > { > "repository": "https://github.com/SeleniumHQ/selenium.git", >- "revision": "d4f9fe213dd6e5b10b89be360be635747ed3e138", >+ "revision": "e3fcf791239c0e9ba5c04686025f578b561ecd6f", > "paths_to_import": [ > "common", > "py/conftest.py", >diff --git a/WebDriverTests/imported/selenium/py/conftest.py b/WebDriverTests/imported/selenium/py/conftest.py >index 6d64f3ffe7c0..b2358b7f65e9 100644 >--- a/WebDriverTests/imported/selenium/py/conftest.py >+++ b/WebDriverTests/imported/selenium/py/conftest.py >@@ -16,31 +16,25 @@ > # under the License. > > import os >+import platform > import socket > import subprocess >-import sys > import time > > import pytest >-from _pytest.skipping import MarkEvaluator > > from selenium import webdriver > from selenium.webdriver import DesiredCapabilities > from test.selenium.webdriver.common.webserver import SimpleWebServer > from test.selenium.webdriver.common.network import get_lan_ip > >-if sys.version_info[0] == 3: >- from urllib.request import urlopen >-else: >- from urllib import urlopen >+from urllib.request import urlopen > > drivers = ( >- 'BlackBerry', > 'Chrome', > 'Edge', > 'Firefox', > 'Ie', >- 'Marionette', > 'Remote', > 'Safari', > 'WebKitGTK', >@@ -82,10 +76,28 @@ def driver(request): > except AttributeError: > raise Exception('This test requires a --driver to be specified.') > >+ # skip tests if not available on the platform >+ _platform = platform.system() >+ if driver_class == "Safari" and _platform != "Darwin": >+ pytest.skip("Safari tests can only run on an Apple OS") >+ if (driver_class == "Ie" or driver_class == "Edge") and _platform != "Windows": >+ pytest.skip("IE and EdgeHTML Tests can only run on Windows") >+ if "WebKit" in driver_class and _platform != "Linux": >+ pytest.skip("Webkit tests can only run on Linux") >+ > # conditionally mark tests as expected to fail based on driver >- request.node._evalxfail = request.node._evalxfail or MarkEvaluator( >- request.node, 'xfail_{0}'.format(driver_class.lower())) >- if request.node._evalxfail.istrue(): >+ marker = request.node.get_closest_marker('xfail_{0}'.format(driver_class.lower())) >+ >+ if marker is not None: >+ if "run" in marker.kwargs: >+ if marker.kwargs["run"] is False: >+ pytest.skip() >+ yield >+ return >+ if "raises" in marker.kwargs: >+ marker.kwargs.pop("raises") >+ pytest.xfail(**marker.kwargs) >+ > def fin(): > global driver_instance > if driver_instance is not None: >@@ -93,25 +105,12 @@ def driver(request): > driver_instance = None > request.addfinalizer(fin) > >- # skip driver instantiation if xfail(run=False) >- if not request.config.getoption('runxfail'): >- if request.node._evalxfail.istrue(): >- if request.node._evalxfail.get('run') is False: >- yield >- return >- > driver_path = request.config.option.executable > options = None > > global driver_instance > if driver_instance is None: >- if driver_class == 'BlackBerry': >- kwargs.update({'device_password': 'password'}) > if driver_class == 'Firefox': >- kwargs.update({'capabilities': {'marionette': False}}) >- options = get_options(driver_class, request.config) >- if driver_class == 'Marionette': >- driver_class = 'Firefox' > options = get_options(driver_class, request.config) > if driver_class == 'Remote': > capabilities = DesiredCapabilities.FIREFOX.copy() >@@ -129,7 +128,8 @@ def driver(request): > kwargs['options'] = options > driver_instance = getattr(webdriver, driver_class)(**kwargs) > yield driver_instance >- if MarkEvaluator(request.node, 'no_driver_after_test').istrue(): >+ >+ if request.node.get_closest_marker("no_driver_after_test"): > driver_instance = None > > >diff --git a/WebDriverTests/imported/selenium/py/selenium/__init__.py b/WebDriverTests/imported/selenium/py/selenium/__init__.py >index 4a049b859132..323caaeb790c 100644 >--- a/WebDriverTests/imported/selenium/py/selenium/__init__.py >+++ b/WebDriverTests/imported/selenium/py/selenium/__init__.py >@@ -16,4 +16,4 @@ > # under the License. > > >-__version__ = "4.0.0a6" >+__version__ = "4.0.0a7" >diff --git a/WebDriverTests/imported/selenium/py/selenium/common/exceptions.py b/WebDriverTests/imported/selenium/py/selenium/common/exceptions.py >index 4cb11c89d58e..e827b07e1e3a 100644 >--- a/WebDriverTests/imported/selenium/py/selenium/common/exceptions.py >+++ b/WebDriverTests/imported/selenium/py/selenium/common/exceptions.py >@@ -158,7 +158,7 @@ class ElementNotVisibleException(InvalidElementStateException): > class ElementNotInteractableException(InvalidElementStateException): > """ > Thrown when an element is present in the DOM but interactions >- with that element will hit another element do to paint order >+ with that element will hit another element due to paint order > """ > pass > >@@ -273,7 +273,7 @@ class ScreenshotException(WebDriverException): > class ElementClickInterceptedException(WebDriverException): > """ > The Element Click command could not be completed because the element receiving the events >- is obscuring the element that was requested clicked. >+ is obscuring the element that was requested to be clicked. > """ > pass > >@@ -288,7 +288,7 @@ class InsecureCertificateException(WebDriverException): > > class InvalidCoordinatesException(WebDriverException): > """ >- The coordinates provided to an interactions operation are invalid. >+ The coordinates provided to an interaction's operation are invalid. > """ > pass > >diff --git a/WebDriverTests/imported/selenium/py/selenium/webdriver/__init__.py b/WebDriverTests/imported/selenium/py/selenium/webdriver/__init__.py >index 9b93dcc2847c..e757636ac75b 100644 >--- a/WebDriverTests/imported/selenium/py/selenium/webdriver/__init__.py >+++ b/WebDriverTests/imported/selenium/py/selenium/webdriver/__init__.py >@@ -16,14 +16,14 @@ > # under the License. > > from .safari.webdriver import WebDriver as Safari # noqa >-from .webkitgtk.webdriver import WebDriver as WebKitGTK # noqa >-from .webkitgtk.options import Options as WebKitGTKOptions # noqa >-from .wpewebkit.webdriver import WebDriver as WPEWebKit # noqa >-from .wpewebkit.options import Options as WPEWebKitOptions # noqa >+from .webkitgtk.webdriver import WebDriver as WebKitGTK # noqa >+from .webkitgtk.options import Options as WebKitGTKOptions # noqa >+from .wpewebkit.webdriver import WebDriver as WPEWebKit # noqa >+from .wpewebkit.options import Options as WPEWebKitOptions # noqa > from .remote.webdriver import WebDriver as Remote # noqa > from .common.desired_capabilities import DesiredCapabilities # noqa > from .common.action_chains import ActionChains # noqa > from .common.touch_actions import TouchActions # noqa > from .common.proxy import Proxy # noqa > >-__version__ = '4.0.0a6' >+__version__ = '4.0.0a7' >diff --git a/WebDriverTests/imported/selenium/py/selenium/webdriver/common/options.py b/WebDriverTests/imported/selenium/py/selenium/webdriver/common/options.py >index bff251727b3e..9203481289d2 100644 >--- a/WebDriverTests/imported/selenium/py/selenium/webdriver/common/options.py >+++ b/WebDriverTests/imported/selenium/py/selenium/webdriver/common/options.py >@@ -49,12 +49,12 @@ class BaseOptions(object): > """Return minimal capabilities necessary as a dictionary.""" > > >- > class ArgOptions(BaseOptions): > > def __init__(self): > super(ArgOptions, self).__init__() > self._arguments = [] >+ self._ignore_local_proxy = False > > @property > def arguments(self): >@@ -75,6 +75,12 @@ class ArgOptions(BaseOptions): > else: > raise ValueError('argument can not be null') > >+ def ignore_local_proxy_environment_variables(self): >+ """ >+ By calling this you will ignore HTTP_PROXY and HTTPS_PROXY from being picked up and used. >+ """ >+ self._ignore_local_proxy = True >+ > def to_capabilities(self): > return self._caps > >diff --git a/WebDriverTests/imported/selenium/py/selenium/webdriver/common/service.py b/WebDriverTests/imported/selenium/py/selenium/webdriver/common/service.py >index d20a902fac7e..8c32e0dbecc1 100644 >--- a/WebDriverTests/imported/selenium/py/selenium/webdriver/common/service.py >+++ b/WebDriverTests/imported/selenium/py/selenium/webdriver/common/service.py >@@ -46,6 +46,8 @@ class Service(object): > > self.start_error_message = start_error_message > self.log_file = log_file >+ # Default value for every python subprocess: subprocess.Popen(..., creationflags=0) >+ self.creationflags = 0 > self.env = env or os.environ > > @property >@@ -73,7 +75,8 @@ class Service(object): > close_fds=platform.system() != 'Windows', > stdout=self.log_file, > stderr=self.log_file, >- stdin=PIPE) >+ stdin=PIPE, >+ creationflags=self.creationflags) > except TypeError: > raise > except OSError as err: >@@ -98,9 +101,10 @@ class Service(object): > self.assert_process_still_running() > if self.is_connectable(): > break >+ > count += 1 >- time.sleep(1) >- if count == 30: >+ time.sleep(0.5) >+ if count == 60: > raise WebDriverException("Can not connect to the Service %s" % self.path) > > def assert_process_still_running(self): >diff --git a/WebDriverTests/imported/selenium/py/selenium/webdriver/common/utils.py b/WebDriverTests/imported/selenium/py/selenium/webdriver/common/utils.py >index 957f2fd26699..1f60e44573a2 100644 >--- a/WebDriverTests/imported/selenium/py/selenium/webdriver/common/utils.py >+++ b/WebDriverTests/imported/selenium/py/selenium/webdriver/common/utils.py >@@ -36,7 +36,7 @@ def free_port(): > Determines a free port using sockets. > """ > free_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) >- free_socket.bind(('0.0.0.0', 0)) >+ free_socket.bind(('127.0.0.1', 0)) > free_socket.listen(5) > port = free_socket.getsockname()[1] > free_socket.close() >diff --git a/WebDriverTests/imported/selenium/py/selenium/webdriver/remote/remote_connection.py b/WebDriverTests/imported/selenium/py/selenium/webdriver/remote/remote_connection.py >index 6f1c1b471207..2448c1b06b4c 100644 >--- a/WebDriverTests/imported/selenium/py/selenium/webdriver/remote/remote_connection.py >+++ b/WebDriverTests/imported/selenium/py/selenium/webdriver/remote/remote_connection.py >@@ -141,7 +141,7 @@ class RemoteConnection(object): > return urllib3.PoolManager(**pool_manager_init_args) if self._proxy_url is None else \ > urllib3.ProxyManager(self._proxy_url, **pool_manager_init_args) > >- def __init__(self, remote_server_addr, keep_alive=False, resolve_ip=None): >+ def __init__(self, remote_server_addr, keep_alive=False, resolve_ip=None, ignore_proxy=False): > if resolve_ip is not None: > import warnings > warnings.warn( >@@ -149,7 +149,7 @@ class RemoteConnection(object): > DeprecationWarning) > self.keep_alive = keep_alive > self._url = remote_server_addr >- self._proxy_url = self._get_proxy_url() >+ self._proxy_url = self._get_proxy_url() if not ignore_proxy else None > if keep_alive: > self._conn = self._get_connection_manager() > >@@ -197,7 +197,7 @@ class RemoteConnection(object): > ('POST', '/session/$sessionId/element/$id/value'), > Command.SEND_KEYS_TO_ACTIVE_ELEMENT: > ('POST', '/session/$sessionId/keys'), >- Command.UPLOAD_FILE: ('POST', "/session/$sessionId/file"), >+ Command.UPLOAD_FILE: ('POST', "/session/$sessionId/se/file"), > Command.GET_ELEMENT_VALUE: > ('GET', '/session/$sessionId/element/$id/value'), > Command.GET_ELEMENT_TAG_NAME: >diff --git a/WebDriverTests/imported/selenium/py/selenium/webdriver/remote/webdriver.py b/WebDriverTests/imported/selenium/py/selenium/webdriver/remote/webdriver.py >index 62282d52695b..c483103d3a50 100644 >--- a/WebDriverTests/imported/selenium/py/selenium/webdriver/remote/webdriver.py >+++ b/WebDriverTests/imported/selenium/py/selenium/webdriver/remote/webdriver.py >@@ -20,19 +20,23 @@ > from abc import ABCMeta > import base64 > import copy >-from contextlib import contextmanager >+from contextlib import (contextmanager, asynccontextmanager) >+import importlib > import pkgutil > import warnings >+import sys > > from .command import Command > from .errorhandler import ErrorHandler > from .file_detector import FileDetector, LocalFileDetector > from .mobile import Mobile > from .remote_connection import RemoteConnection >+from .script_key import ScriptKey > from .switch_to import SwitchTo > from .webelement import WebElement > > from selenium.common.exceptions import (InvalidArgumentException, >+ JavascriptException, > WebDriverException, > NoSuchCookieException, > UnknownMethodException) >@@ -48,6 +52,14 @@ try: > except NameError: > pass > >+cdp = None >+ >+ >+def import_cdp(): >+ global cdp >+ if cdp is None: >+ cdp = importlib.import_module("selenium.webdriver.common.bidi.cdp") >+ > > _W3C_CAPABILITY_NAMES = frozenset([ > 'acceptInsecureCerts', >@@ -68,6 +80,8 @@ _OSS_W3C_CONVERSION = { > 'platform': 'platformName' > } > >+devtools = None >+ > > def _make_w3c_caps(caps): > """Makes a W3C alwaysMatch capabilities object. >@@ -102,7 +116,7 @@ def _make_w3c_caps(caps): > return {"firstMatch": [{}], "alwaysMatch": always_match} > > >-def get_remote_connection(capabilities, command_executor, keep_alive): >+def get_remote_connection(capabilities, command_executor, keep_alive, ignore_local_proxy=False): > from selenium.webdriver.safari.remote_connection import SafariRemoteConnection > > candidates = [RemoteConnection] + [SafariRemoteConnection] >@@ -111,7 +125,7 @@ def get_remote_connection(capabilities, command_executor, keep_alive): > RemoteConnection > ) > >- return handler(command_executor, keep_alive=keep_alive) >+ return handler(command_executor, keep_alive=keep_alive, ignore_proxy=ignore_local_proxy) > > > @add_metaclass(ABCMeta) >@@ -163,8 +177,10 @@ class WebDriver(BaseWebDriver): > - options - instance of a driver options.Options class > """ > capabilities = {} >+ _ignore_local_proxy = False > if options is not None: > capabilities = options.to_capabilities() >+ _ignore_local_proxy = options._ignore_local_proxy > if desired_capabilities is not None: > if not isinstance(desired_capabilities, dict): > raise WebDriverException("Desired Capabilities must be a dictionary") >@@ -172,10 +188,13 @@ class WebDriver(BaseWebDriver): > capabilities.update(desired_capabilities) > self.command_executor = command_executor > if isinstance(self.command_executor, (str, bytes)): >- self.command_executor = get_remote_connection(capabilities, command_executor=command_executor, keep_alive=keep_alive) >+ self.command_executor = get_remote_connection(capabilities, command_executor=command_executor, >+ keep_alive=keep_alive, >+ ignore_local_proxy=_ignore_local_proxy) > self._is_remote = True > self.session_id = None >- self.capabilities = {} >+ self.caps = {} >+ self.pinned_scripts = {} > self.error_handler = ErrorHandler() > self.start_client() > self.start_session(capabilities, browser_profile) >@@ -235,8 +254,8 @@ class WebDriver(BaseWebDriver): > > name = driver.name > """ >- if 'browserName' in self.capabilities: >- return self.capabilities['browserName'] >+ if 'browserName' in self.caps: >+ return self.caps['browserName'] > else: > raise KeyError('browserName not specified in session capabilities') > >@@ -279,12 +298,12 @@ class WebDriver(BaseWebDriver): > if 'sessionId' not in response: > response = response['value'] > self.session_id = response['sessionId'] >- self.capabilities = response.get('value') >+ self.caps = response.get('value') > > # if capabilities is none we are probably speaking to > # a W3C endpoint >- if self.capabilities is None: >- self.capabilities = response.get('capabilities') >+ if self.caps is None: >+ self.caps = response.get('capabilities') > > # Double check to see if we have a W3C Compliant browser > self.w3c = response.get('status') is None >@@ -689,6 +708,26 @@ class WebDriver(BaseWebDriver): > warnings.warn("find_elements_by_* commands are deprecated. Please use find_elements() instead") > return self.find_elements(by=By.CSS_SELECTOR, value=css_selector) > >+ def pin_script(self, script): >+ """ >+ >+ """ >+ script_key = ScriptKey() >+ self.pinned_scripts[script_key.id] = script >+ return script_key >+ >+ def unpin(self, script_key): >+ """ >+ >+ """ >+ self.pinned_scripts.pop(script_key.id) >+ >+ def get_pinned_scripts(self): >+ """ >+ >+ """ >+ return list(self.pinned_scripts.keys()) >+ > def execute_script(self, script, *args): > """ > Synchronously Executes JavaScript in the current window/frame. >@@ -702,6 +741,12 @@ class WebDriver(BaseWebDriver): > > driver.execute_script('return document.title;') > """ >+ if isinstance(script, ScriptKey): >+ try: >+ script = self.pinned_scripts[script.id] >+ except KeyError: >+ raise JavascriptException("Pinned script could not be found") >+ > converted_args = list(args) > command = None > if self.w3c: >@@ -1067,8 +1112,7 @@ class WebDriver(BaseWebDriver): > > def find_element(self, by=By.ID, value=None): > """ >- Find an element given a By strategy and locator. Prefer the find_element_by_* methods when >- possible. >+ Find an element given a By strategy and locator. > > :Usage: > :: >@@ -1095,8 +1139,7 @@ class WebDriver(BaseWebDriver): > > def find_elements(self, by=By.ID, value=None): > """ >- Find elements given a By strategy and locator. Prefer the find_elements_by_* methods when >- possible. >+ Find elements given a By strategy and locator. > > :Usage: > :: >@@ -1108,7 +1151,7 @@ class WebDriver(BaseWebDriver): > if isinstance(by, RelativeBy): > _pkg = '.'.join(__name__.split('.')[:-1]) > raw_function = pkgutil.get_data(_pkg, 'findElements.js').decode('utf8') >- find_element_js = "return (%s).apply(null, arguments);" % raw_function >+ find_element_js = "return ({}).apply(null, arguments);".format(raw_function) > return self.execute_script(find_element_js, by.to_dict()) > > if self.w3c: >@@ -1135,7 +1178,16 @@ class WebDriver(BaseWebDriver): > """ > returns the drivers current desired capabilities being used > """ >- return self.capabilities >+ warnings.warn("desired_capabilities is deprecated. Please call capabilities.", >+ DeprecationWarning, stacklevel=2) >+ return self.caps >+ >+ @property >+ def capabilities(self): >+ """ >+ returns the drivers current capabilities being used. >+ """ >+ return self.caps > > def get_screenshot_as_file(self, filename): > """ >@@ -1417,3 +1469,110 @@ class WebDriver(BaseWebDriver): > driver.get_log('server') > """ > return self.execute(Command.GET_LOG, {'type': log_type})['value'] >+ >+ @asynccontextmanager >+ async def add_js_error_listener(self): >+ """ >+ Listens for JS errors and when the contextmanager exits check if there were JS Errors >+ >+ :Usage: >+ :: >+ >+ async with driver.add_js_error_listener() as error: >+ driver.find_element(By.ID, "throwing-mouseover").click() >+ assert error is not None >+ assert error.exception_details.stack_trace.call_frames[0].function_name == "onmouseover" >+ """ >+ assert sys.version_info >= (3, 7) >+ global cdp >+ async with self._get_bidi_connection(): >+ global devtools >+ session = cdp.get_session_context('page.enable') >+ await session.execute(devtools.page.enable()) >+ session = cdp.get_session_context('runtime.enable') >+ await session.execute(devtools.runtime.enable()) >+ js_exception = devtools.runtime.ExceptionThrown(None, None) >+ async with session.wait_for(devtools.runtime.ExceptionThrown) as exception: >+ yield js_exception >+ js_exception.timestamp = exception.value.timestamp >+ js_exception.exception_details = exception.value.exception_details >+ >+ @asynccontextmanager >+ async def add_listener(self, event_type): >+ ''' >+ Listens for certain events that are passed in. >+ >+ :Args: >+ - event_type: The type of event that we want to look at. >+ >+ :Usage: >+ :: >+ >+ async with driver.add_listener(Console.log) as messages: >+ driver.execute_script("console.log('I like cheese')") >+ assert messages["message"] == "I love cheese" >+ >+ ''' >+ assert sys.version_info >= (3, 7) >+ global cdp >+ from selenium.webdriver.common.bidi.console import Console >+ >+ async with self._get_bidi_connection(): >+ global devtools >+ session = cdp.get_session_context('page.enable') >+ await session.execute(devtools.page.enable()) >+ session = cdp.get_session_context('console.enable') >+ await session.execute(devtools.console.enable()) >+ console = { >+ "message": None, >+ "level": None >+ } >+ async with session.wait_for(devtools.console.MessageAdded) as messages: >+ yield console >+ if event_type == Console.ERROR: >+ if messages.value.message.level == "error": >+ console["message"] = messages.value.message.text >+ console["level"] = messages.value.message.level >+ elif event_type == Console.ALL: >+ console["message"] = messages.value.message.text >+ console["level"] = messages.value.message.level >+ >+ @asynccontextmanager >+ async def _get_bidi_connection(self): >+ global cdp >+ import_cdp() >+ ws_url = None >+ if self.caps.get("se:options"): >+ ws_url = self.caps.get("se:options").get("cdp") >+ else: >+ version, ws_url = self._get_cdp_details() >+ >+ if ws_url is None: >+ raise WebDriverException("Unable to find url to connect to from capabilities") >+ >+ cdp.import_devtools(version) >+ >+ global devtools >+ devtools = importlib.import_module("selenium.webdriver.common.devtools.v{}".format(version)) >+ async with cdp.open_cdp(ws_url) as conn: >+ targets = await conn.execute(devtools.target.get_targets()) >+ target_id = targets[0].target_id >+ async with conn.open_session(target_id) as session: >+ yield session >+ >+ def _get_cdp_details(self): >+ import json >+ import urllib3 >+ >+ http = urllib3.PoolManager() >+ debugger_address = self.caps.get("{0}:chromeOptions".format(self.vendor_prefix)).get("debuggerAddress") >+ res = http.request('GET', "http://{0}/json/version".format(debugger_address)) >+ data = json.loads(res.data) >+ >+ browser_version = data.get("Browser") >+ websocket_url = data.get("webSocketDebuggerUrl") >+ >+ import re >+ version = re.search(r".*/(\d+)\.", browser_version).group(1) >+ >+ return version, websocket_url >diff --git a/WebDriverTests/imported/selenium/py/selenium/webdriver/remote/webelement.py b/WebDriverTests/imported/selenium/py/selenium/webdriver/remote/webelement.py >index 6f3ebbcc3b54..093e42a5ba13 100644 >--- a/WebDriverTests/imported/selenium/py/selenium/webdriver/remote/webelement.py >+++ b/WebDriverTests/imported/selenium/py/selenium/webdriver/remote/webelement.py >@@ -534,9 +534,14 @@ class WebElement(BaseWebElement): > # transfer file to another machine only if remote driver is used > # the same behaviour as for java binding > if self.parent._is_remote: >- local_file = self.parent.file_detector.is_local_file(*value) >- if local_file is not None: >- value = self._upload(local_file) >+ local_files = list(map(lambda keys_to_send: >+ self.parent.file_detector.is_local_file(keys_to_send), >+ ''.join(value).split('\n'))) >+ if None not in local_files: >+ remote_files = [] >+ for file in local_files: >+ remote_files.append(self._upload(file)) >+ value = '\n'.join(remote_files) > > self._execute(Command.SEND_KEYS_TO_ELEMENT, > {'text': "".join(keys_to_typing(value)), >@@ -704,8 +709,7 @@ class WebElement(BaseWebElement): > > def find_element(self, by=By.ID, value=None): > """ >- Find an element given a By strategy and locator. Prefer the find_element_by_* methods when >- possible. >+ Find an element given a By strategy and locator. > > :Usage: > :: >@@ -732,8 +736,7 @@ class WebElement(BaseWebElement): > > def find_elements(self, by=By.ID, value=None): > """ >- Find elements given a By strategy and locator. Prefer the find_elements_by_* methods when >- possible. >+ Find elements given a By strategy and locator. > > :Usage: > :: >diff --git a/WebDriverTests/imported/selenium/py/selenium/webdriver/safari/remote_connection.py b/WebDriverTests/imported/selenium/py/selenium/webdriver/safari/remote_connection.py >index 22c623e610af..d692428998f5 100644 >--- a/WebDriverTests/imported/selenium/py/selenium/webdriver/safari/remote_connection.py >+++ b/WebDriverTests/imported/selenium/py/selenium/webdriver/safari/remote_connection.py >@@ -23,8 +23,8 @@ class SafariRemoteConnection(RemoteConnection): > > browser_name = DesiredCapabilities.SAFARI['browserName'] > >- def __init__(self, remote_server_addr, keep_alive=True): >- RemoteConnection.__init__(self, remote_server_addr, keep_alive) >+ def __init__(self, remote_server_addr, keep_alive=True, ignore_proxy=False): >+ RemoteConnection.__init__(self, remote_server_addr, keep_alive, ignore_proxy=ignore_proxy) > > self._commands["GET_PERMISSIONS"] = ('GET', '/session/$sessionId/apple/permissions') > self._commands["SET_PERMISSIONS"] = ('POST', '/session/$sessionId/apple/permissions') >diff --git a/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/api_example_tests.py b/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/api_example_tests.py >index 397492549c2d..2e7a06b4efe8 100644 >--- a/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/api_example_tests.py >+++ b/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/api_example_tests.py >@@ -1,20 +1,3 @@ >-# Licensed to the Software Freedom Conservancy (SFC) under one >-# or more contributor license agreements. See the NOTICE file >-# distributed with this work for additional information >-# regarding copyright ownership. The SFC licenses this file >-# to you 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. >- > # Copyright 2008-2009 WebDriver committers > # Copyright 2008-2009 Google Inc. > # >diff --git a/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/click_scrolling_tests.py b/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/click_scrolling_tests.py >index ca0d0800a32c..4c91c227f0db 100644 >--- a/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/click_scrolling_tests.py >+++ b/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/click_scrolling_tests.py >@@ -178,14 +178,14 @@ def getScrollTop(driver): > return driver.execute_script("return document.body.scrollTop") > > >-@pytest.mark.xfail_firefox( >- reason='https://github.com/w3c/webdriver/issues/408') >-@pytest.mark.xfail_remote( >- reason='https://github.com/w3c/webdriver/issues/408') >-@pytest.mark.xfail_safari >-def testShouldBeAbleToClickElementInATallFrame(driver, pages): >- pages.load("scrolling_tests/page_with_tall_frame.html") >- driver.switch_to.frame(driver.find_element(By.NAME, "tall_frame")) >- element = driver.find_element(By.NAME, "checkbox") >- element.click() >- assert element.is_selected() >+# @pytest.mark.xfail_firefox( >+# reason='https://github.com/w3c/webdriver/issues/408') >+# @pytest.mark.xfail_remote( >+# reason='https://github.com/w3c/webdriver/issues/408') >+# @pytest.mark.xfail_safari >+# def testShouldBeAbleToClickElementInATallFrame(driver, pages): >+# pages.load("scrolling_tests/page_with_tall_frame.html") >+# driver.switch_to.frame(driver.find_element(By.NAME, "tall_frame")) >+# element = driver.find_element(By.NAME, "checkbox") >+# element.click() >+# assert element.is_selected() >diff --git a/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/driver_element_finding_tests.py b/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/driver_element_finding_tests.py >index d6e784280573..9b801dc30d7c 100644 >--- a/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/driver_element_finding_tests.py >+++ b/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/driver_element_finding_tests.py >@@ -335,16 +335,16 @@ def test_Finding_ALink_By_Xpath_Using_Contains_Keyword_Should_Work(driver, pages > assert "hello world" in element.text > > >-@pytest.mark.xfail_chrome(raises=InvalidSelectorException) >-@pytest.mark.xfail_chromiumedge(raises=InvalidSelectorException) >-@pytest.mark.xfail_firefox(raises=InvalidSelectorException) >-@pytest.mark.xfail_remote(raises=InvalidSelectorException) >-@pytest.mark.xfail_safari(raises=NoSuchElementException) >-@pytest.mark.xfail_webkitgtk(raises=InvalidSelectorException) >-def test_Should_Be_Able_To_Find_Element_By_XPath_With_Namespace(driver, pages): >- pages.load("svgPage.html") >- element = driver.find_element(By.XPATH, "//svg:svg//svg:text") >- assert element.text == "Test Chart" >+# @pytest.mark.xfail_chrome(raises=InvalidSelectorException) >+# @pytest.mark.xfail_chromiumedge(raises=InvalidSelectorException) >+# @pytest.mark.xfail_firefox(raises=InvalidSelectorException) >+# @pytest.mark.xfail_remote(raises=InvalidSelectorException) >+# @pytest.mark.xfail_safari(raises=NoSuchElementException) >+# @pytest.mark.xfail_webkitgtk(raises=InvalidSelectorException) >+# def test_Should_Be_Able_To_Find_Element_By_XPath_With_Namespace(driver, pages): >+# pages.load("svgPage.html") >+# element = driver.find_element(By.XPATH, "//svg:svg//svg:text") >+# assert element.text == "Test Chart" > > > def test_Should_Be_Able_To_Find_Element_By_XPath_In_Xml_Document(driver, pages): >diff --git a/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/frame_switching_tests.py b/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/frame_switching_tests.py >index 8a759b600b98..18b346575816 100644 >--- a/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/frame_switching_tests.py >+++ b/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/frame_switching_tests.py >@@ -368,23 +368,23 @@ def testShouldBeAbleToSwitchToTheTopIfTheFrameIsDeletedFromUnderUsWithWebelement > WebDriverWait(driver, 3).until(EC.presence_of_element_located((By.ID, "success"))) > > >-@pytest.mark.xfail_chrome(raises=NoSuchElementException) >-@pytest.mark.xfail_chromiumedge(raises=NoSuchElementException) >-@pytest.mark.xfail_firefox(raises=WebDriverException, >- reason='https://github.com/mozilla/geckodriver/issues/614') >-@pytest.mark.xfail_remote(raises=WebDriverException, >- reason='https://github.com/mozilla/geckodriver/issues/614') >-@pytest.mark.xfail_webkitgtk(raises=NoSuchElementException) >-@pytest.mark.xfail_safari >-def testShouldNotBeAbleToDoAnythingTheFrameIsDeletedFromUnderUs(driver, pages): >- pages.load("frame_switching_tests/deletingFrame.html") >- driver.switch_to.frame(driver.find_element(By.ID, "iframe1")) >- >- killIframe = driver.find_element(By.ID, "killIframe") >- killIframe.click() >- >- with pytest.raises(NoSuchFrameException): >- driver.find_element(By.ID, "killIframe").click() >+# @pytest.mark.xfail_chrome(raises=NoSuchElementException) >+# @pytest.mark.xfail_chromiumedge(raises=NoSuchElementException) >+# @pytest.mark.xfail_firefox(raises=WebDriverException, >+# reason='https://github.com/mozilla/geckodriver/issues/614') >+# @pytest.mark.xfail_remote(raises=WebDriverException, >+# reason='https://github.com/mozilla/geckodriver/issues/614') >+# @pytest.mark.xfail_webkitgtk(raises=NoSuchElementException) >+# @pytest.mark.xfail_safari >+# def testShouldNotBeAbleToDoAnythingTheFrameIsDeletedFromUnderUs(driver, pages): >+# pages.load("frame_switching_tests/deletingFrame.html") >+# driver.switch_to.frame(driver.find_element(By.ID, "iframe1")) >+ >+# killIframe = driver.find_element(By.ID, "killIframe") >+# killIframe.click() >+ >+# with pytest.raises(NoSuchFrameException): >+# driver.find_element(By.ID, "killIframe").click() > > > def testShouldReturnWindowTitleInAFrameset(driver, pages): >diff --git a/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/page_loading_tests.py b/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/page_loading_tests.py >index 1411ee59da1b..9bc7a5b50ecd 100644 >--- a/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/page_loading_tests.py >+++ b/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/page_loading_tests.py >@@ -15,12 +15,12 @@ > # specific language governing permissions and limitations > # under the License. > >-import pytest >- >-from selenium.common.exceptions import WebDriverException >-from selenium.webdriver.common.by import By >-from selenium.webdriver.support.wait import WebDriverWait >-from selenium.webdriver.support import expected_conditions as EC >+# import pytest >+# >+# from selenium.common.exceptions import WebDriverException >+# from selenium.webdriver.common.by import By >+# from selenium.webdriver.support.wait import WebDriverWait >+# from selenium.webdriver.support import expected_conditions as EC > > > def testShouldWaitForDocumentToBeLoaded(driver, pages): >@@ -40,25 +40,25 @@ def testShouldWaitForDocumentToBeLoaded(driver, pages): > # assert driver.title == "We Arrive Here" > > >-def testShouldBeAbleToGetAFragmentOnTheCurrentPage(driver, pages): >- pages.load("xhtmlTest.html") >- location = driver.current_url >- driver.get(location + "#text") >- driver.find_element(by=By.ID, value="id1") >+# def testShouldBeAbleToGetAFragmentOnTheCurrentPage(driver, pages): >+# pages.load("xhtmlTest.html") >+# location = driver.current_url >+# driver.get(location + "#text") >+# driver.find_element(by=By.ID, value="id1") > > >-@pytest.mark.xfail_firefox(raises=WebDriverException) >-@pytest.mark.xfail_remote(raises=WebDriverException) >-def testShouldReturnWhenGettingAUrlThatDoesNotResolve(driver): >- # Of course, we're up the creek if this ever does get registered >- driver.get("http://www.thisurldoesnotexist.comx/") >+# @pytest.mark.xfail_firefox(raises=WebDriverException) >+# @pytest.mark.xfail_remote(raises=WebDriverException) >+# def testShouldReturnWhenGettingAUrlThatDoesNotResolve(driver): >+# # Of course, we're up the creek if this ever does get registered >+# driver.get("http://www.thisurldoesnotexist.comx/") > > >-@pytest.mark.xfail_firefox(raises=WebDriverException) >-@pytest.mark.xfail_remote(raises=WebDriverException) >-def testShouldReturnWhenGettingAUrlThatDoesNotConnect(driver): >- # Here's hoping that there's nothing here. There shouldn't be >- driver.get("http://localhost:3001") >+# @pytest.mark.xfail_firefox(raises=WebDriverException) >+# @pytest.mark.xfail_remote(raises=WebDriverException) >+# def testShouldReturnWhenGettingAUrlThatDoesNotConnect(driver): >+# # Here's hoping that there's nothing here. There shouldn't be >+# driver.get("http://localhost:3001") > > # def testShouldBeAbleToLoadAPageWithFramesetsAndWaitUntilAllFramesAreLoaded() { > # driver.get(pages.framesetPage) >@@ -81,55 +81,55 @@ def testShouldReturnWhenGettingAUrlThatDoesNotConnect(driver): > # self.assertEqual(driver.title, anyOf(equalTo(originalTitle), equalTo("We Leave From Here"))); > > >-@pytest.mark.xfail_safari >-def testShouldBeAbleToNavigateBackInTheBrowserHistory(driver, pages): >- pages.load("formPage.html") >+# @pytest.mark.xfail_safari >+# def testShouldBeAbleToNavigateBackInTheBrowserHistory(driver, pages): >+# pages.load("formPage.html") > >- driver.find_element(by=By.ID, value="imageButton").submit() >- WebDriverWait(driver, 3).until(EC.title_is("We Arrive Here")) >+# driver.find_element(by=By.ID, value="imageButton").submit() >+# WebDriverWait(driver, 3).until(EC.title_is("We Arrive Here")) > >- driver.back() >- assert driver.title == "We Leave From Here" >+# driver.back() >+# assert driver.title == "We Leave From Here" > > >-@pytest.mark.xfail_safari >-def testShouldBeAbleToNavigateBackInTheBrowserHistoryInPresenceOfIframes(driver, pages): >- pages.load("xhtmlTest.html") >+# @pytest.mark.xfail_safari >+# def testShouldBeAbleToNavigateBackInTheBrowserHistoryInPresenceOfIframes(driver, pages): >+# pages.load("xhtmlTest.html") > >- driver.find_element(by=By.NAME, value="sameWindow").click() >+# driver.find_element(by=By.NAME, value="sameWindow").click() > >- assert driver.title == "This page has iframes" >+# assert driver.title == "This page has iframes" > >- driver.back() >- assert driver.title == "XHTML Test Page" >+# driver.back() >+# assert driver.title == "XHTML Test Page" > > >-def testShouldBeAbleToNavigateForwardsInTheBrowserHistory(driver, pages): >- pages.load("formPage.html") >+# def testShouldBeAbleToNavigateForwardsInTheBrowserHistory(driver, pages): >+# pages.load("formPage.html") > >- driver.find_element(by=By.ID, value="imageButton").submit() >- WebDriverWait(driver, 3).until(EC.title_is("We Arrive Here")) >+# driver.find_element(by=By.ID, value="imageButton").submit() >+# WebDriverWait(driver, 3).until(EC.title_is("We Arrive Here")) > >- driver.back() >- assert driver.title == "We Leave From Here" >+# driver.back() >+# assert driver.title == "We Leave From Here" > >- driver.forward() >- assert driver.title == "We Arrive Here" >+# driver.forward() >+# assert driver.title == "We Arrive Here" > > >-@pytest.mark.xfail_ie >-@pytest.mark.xfail_firefox(run=False) >-@pytest.mark.xfail_remote(run=False) >-@pytest.mark.xfail_chrome(run=False) >-@pytest.mark.xfail_chromiumedge(run=False) >-def testShouldNotHangifDocumentOpenCallIsNeverFollowedByDocumentCloseCall(driver, pages): >- pages.load("document_write_in_onload.html") >- driver.find_element(By.XPATH, "//body") >+# @pytest.mark.xfail_ie >+# @pytest.mark.xfail_firefox(run=False) >+# @pytest.mark.xfail_remote(run=False) >+# @pytest.mark.xfail_chrome(run=False) >+# @pytest.mark.xfail_chromiumedge(run=False) >+# def testShouldNotHangifDocumentOpenCallIsNeverFollowedByDocumentCloseCall(driver, pages): >+# pages.load("document_write_in_onload.html") >+# driver.find_element(By.XPATH, "//body") > > >-def testShouldBeAbleToRefreshAPage(driver, pages): >- pages.load("xhtmlTest.html") >+# def testShouldBeAbleToRefreshAPage(driver, pages): >+# pages.load("xhtmlTest.html") > >- driver.refresh() >+# driver.refresh() > >- assert driver.title == "XHTML Test Page" >+# assert driver.title == "XHTML Test Page" >diff --git a/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/script_pinning_tests.py b/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/script_pinning_tests.py >new file mode 100644 >index 000000000000..5ef9ce3a7f5e >--- /dev/null >+++ b/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/script_pinning_tests.py >@@ -0,0 +1,69 @@ >+# Licensed to the Software Freedom Conservancy (SFC) under one >+# or more contributor license agreements. See the NOTICE file >+# distributed with this work for additional information >+# regarding copyright ownership. The SFC licenses this file >+# to you 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. >+ >+import pytest >+ >+from selenium.common.exceptions import JavascriptException >+ >+ >+def test_should_allow_script_pinning(driver, pages): >+ pages.load("simpleTest.html") >+ driver.pinned_scripts = {} >+ script_key = driver.pin_script("return 'i like cheese';") >+ >+ result = driver.execute_script(script_key) >+ >+ assert result == 'i like cheese' >+ >+ >+def test_should_allow_pinned_scripts_to_take_arguments(driver, pages): >+ pages.load("simpleTest.html") >+ driver.pinned_scripts = {} >+ hello = driver.pin_script("return arguments[0]") >+ >+ result = driver.execute_script(hello, "cheese") >+ >+ assert result == "cheese" >+ >+ >+def test_should_list_all_pinned_scripts(driver, pages): >+ pages.load("simpleTest.html") >+ driver.pinned_scripts = {} >+ expected = [] >+ expected.append(driver.pin_script("return arguments[0];").id) >+ expected.append(driver.pin_script("return 'cheese';").id) >+ expected.append(driver.pin_script("return 42;").id) >+ >+ result = driver.get_pinned_scripts() >+ assert expected == result >+ >+ >+def test_should_allow_scripts_to_be_unpinned(driver, pages): >+ pages.load("simpleTest.html") >+ driver.pinned_scripts = {} >+ cheese = driver.pin_script("return 'cheese';") >+ driver.unpin(cheese) >+ results = driver.get_pinned_scripts() >+ assert cheese not in results >+ >+ >+def test_calling_unpinned_script_causes_error(driver, pages): >+ pages.load("simpleTest.html") >+ cheese = driver.pin_script("return 'brie';") >+ driver.unpin(cheese) >+ with pytest.raises(JavascriptException): >+ driver.execute_script(cheese) >diff --git a/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/select_class_tests.py b/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/select_class_tests.py >index 52b7d9b98e1c..31526ad42cd2 100644 >--- a/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/select_class_tests.py >+++ b/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/select_class_tests.py >@@ -91,20 +91,20 @@ def testSelectByVisibleTextSingle(driver, pages): > assert sel.first_selected_option.text == select['values'][x] > > >-@pytest.mark.xfail_chrome( >- reason='https://bugs.chromium.org/p/chromedriver/issues/detail?id=822') >-@pytest.mark.xfail_chromiumedge( >- reason='https://bugs.chromium.org/p/chromedriver/issues/detail?id=822') >-@pytest.mark.xfail_safari >-def testSelectByVisibleTextShouldNormalizeSpaces(driver, pages): >- pages.load("formPage.html") >- >- for select in [singleSelectValuesWithSpaces]: >- sel = Select(driver.find_element(By.NAME, select['name'])) >- for x in range(len(select['values'])): >- print(select['values'][x]) >- sel.select_by_visible_text(select['values'][x]) >- assert sel.first_selected_option.text == select['values'][x] >+# @pytest.mark.xfail_chrome( >+# reason='https://bugs.chromium.org/p/chromedriver/issues/detail?id=822') >+# @pytest.mark.xfail_chromiumedge( >+# reason='https://bugs.chromium.org/p/chromedriver/issues/detail?id=822') >+# @pytest.mark.xfail_safari >+# def testSelectByVisibleTextShouldNormalizeSpaces(driver, pages): >+# pages.load("formPage.html") >+ >+# for select in [singleSelectValuesWithSpaces]: >+# sel = Select(driver.find_element(By.NAME, select['name'])) >+# for x in range(len(select['values'])): >+# print(select['values'][x]) >+# sel.select_by_visible_text(select['values'][x]) >+# assert sel.first_selected_option.text == select['values'][x] > > > @pytest.mark.xfail_firefox(reason='https://bugzilla.mozilla.org/show_bug.cgi?id=1429403') >diff --git a/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/window_tests.py b/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/window_tests.py >index 85eb247b2986..98509b141525 100644 >--- a/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/window_tests.py >+++ b/WebDriverTests/imported/selenium/py/test/selenium/webdriver/common/window_tests.py >@@ -14,30 +14,30 @@ > # KIND, either express or implied. See the License for the > # specific language governing permissions and limitations > # under the License. >-import os >+ > import pytest > > from selenium.common.exceptions import WebDriverException > from selenium.webdriver.support.wait import WebDriverWait > > >-@pytest.mark.xfail_ie >-@pytest.mark.xfail_chromiumedge(reason="Fails on Travis") >-@pytest.mark.xfail_firefox(reason="Fails on Travis") >-@pytest.mark.xfail_remote(reason="Fails on Travis") >-def testShouldMaximizeTheWindow(driver): >- resize_timeout = 5 >- wait = WebDriverWait(driver, resize_timeout) >- old_size = driver.get_window_size() >- driver.set_window_size(200, 200) >- wait.until( >- lambda dr: dr.get_window_size() != old_size if old_size["width"] != 200 and old_size["height"] != 200 else True) >- size = driver.get_window_size() >- driver.maximize_window() >- wait.until(lambda dr: dr.get_window_size() != size) >- new_size = driver.get_window_size() >- assert new_size["width"] > size["width"] >- assert new_size["height"] > size["height"] >+# @pytest.mark.xfail_ie >+# @pytest.mark.xfail_chromiumedge(reason="Fails on Travis") >+# @pytest.mark.xfail_firefox(reason="Fails on Travis") >+# @pytest.mark.xfail_remote(reason="Fails on Travis") >+# def testShouldMaximizeTheWindow(driver): >+# resize_timeout = 5 >+# wait = WebDriverWait(driver, resize_timeout) >+# old_size = driver.get_window_size() >+# driver.set_window_size(200, 200) >+# wait.until( >+# lambda dr: dr.get_window_size() != old_size if old_size["width"] != 200 and old_size["height"] != 200 else True) >+# size = driver.get_window_size() >+# driver.maximize_window() >+# wait.until(lambda dr: dr.get_window_size() != size) >+# new_size = driver.get_window_size() >+# assert new_size["width"] > size["width"] >+# assert new_size["height"] > size["height"] > > > def test_should_get_the_size_of_the_current_window(driver): >@@ -112,37 +112,37 @@ def test_should_set_the_rect_of_the_current_window(driver): > assert new_rect.get('height') == target_height > > >-@pytest.mark.xfail_safari(raises=WebDriverException, >- reason='Fullscreen command not implemented') >-@pytest.mark.skipif(os.environ.get('TRAVIS') == 'true', >- reason='Fullscreen command causes Travis to hang') >-@pytest.mark.no_driver_after_test >-def test_should_fullscreen_the_current_window(driver): >- start_width = driver.execute_script('return window.innerWidth;') >- start_height = driver.execute_script('return window.innerHeight;') >+# @pytest.mark.xfail_safari(raises=WebDriverException, >+# reason='Fullscreen command not implemented') >+# @pytest.mark.skipif(os.environ.get('TRAVIS') == 'true', >+# reason='Fullscreen command causes Travis to hang') >+# @pytest.mark.no_driver_after_test >+# def test_should_fullscreen_the_current_window(driver): >+# start_width = driver.execute_script('return window.innerWidth;') >+# start_height = driver.execute_script('return window.innerHeight;') > >- driver.fullscreen_window() >+# driver.fullscreen_window() > >- WebDriverWait(driver, 2)\ >- .until(lambda d: driver.execute_script('return window.innerWidth;') > start_width) >+# WebDriverWait(driver, 2)\ >+# .until(lambda d: driver.execute_script('return window.innerWidth;') > start_width) > >- end_width = driver.execute_script('return window.innerWidth;') >- end_height = driver.execute_script('return window.innerHeight;') >+# end_width = driver.execute_script('return window.innerWidth;') >+# end_height = driver.execute_script('return window.innerHeight;') > >- driver.quit() # Kill driver so we aren't running fullscreen after >+# driver.quit() # Kill driver so we aren't running fullscreen after > >- assert end_width > start_width >- assert end_height > start_height >+# assert end_width > start_width >+# assert end_height > start_height > > >-@pytest.mark.xfail_safari(raises=WebDriverException, >- reason='Minimize command not implemented') >-@pytest.mark.skipif(os.environ.get('TRAVIS') == 'true', >- reason='Minimize command causes Travis to hang') >-@pytest.mark.no_driver_after_test >-def test_should_minimize_the_current_window(driver): >- driver.minimize_window() >- minimized = driver.execute_script('return document.hidden;') >- driver.quit() # Kill driver so we aren't running minimized after >- >- assert minimized is True >+# @pytest.mark.xfail_safari(raises=WebDriverException, >+# reason='Minimize command not implemented') >+# @pytest.mark.skipif(os.environ.get('TRAVIS') == 'true', >+# reason='Minimize command causes Travis to hang') >+# @pytest.mark.no_driver_after_test >+# def test_should_minimize_the_current_window(driver): >+# driver.minimize_window() >+# minimized = driver.execute_script('return document.hidden;') >+# driver.quit() # Kill driver so we aren't running minimized after >+ >+# assert minimized is True >diff --git a/WebDriverTests/imported/w3c/importer.json b/WebDriverTests/imported/w3c/importer.json >index f400ef5a3957..eaa801872305 100644 >--- a/WebDriverTests/imported/w3c/importer.json >+++ b/WebDriverTests/imported/w3c/importer.json >@@ -1,6 +1,6 @@ > { > "repository": "https://github.com/w3c/web-platform-tests.git", >- "revision": "b57da97fc05e5440ae675027dd45a26be48e2dbd", >+ "revision": "eb8bdce552a2a9b10010f7755ed9d8d0773d548e", > "paths_to_import": [ > "tools/webdriver", > "tools/wptrunner", >diff --git a/WebDriverTests/imported/w3c/tools/webdriver/webdriver/__init__.py b/WebDriverTests/imported/w3c/tools/webdriver/webdriver/__init__.py >index d860b9ea2533..a81751407e70 100644 >--- a/WebDriverTests/imported/w3c/tools/webdriver/webdriver/__init__.py >+++ b/WebDriverTests/imported/w3c/tools/webdriver/webdriver/__init__.py >@@ -6,6 +6,7 @@ from .client import ( > Find, > Frame, > Session, >+ ShadowRoot, > Timeouts, > Window) > from .error import ( >@@ -34,3 +35,5 @@ from .error import ( > UnknownMethodException, > UnsupportedOperationException, > WebDriverException) >+from .bidi import ( >+ BidiSession) >diff --git a/WebDriverTests/imported/w3c/tools/webdriver/webdriver/bidi.py b/WebDriverTests/imported/w3c/tools/webdriver/webdriver/bidi.py >new file mode 100644 >index 000000000000..0bcd489cd35e >--- /dev/null >+++ b/WebDriverTests/imported/w3c/tools/webdriver/webdriver/bidi.py >@@ -0,0 +1,56 @@ >+import copy >+import websockets >+ >+from . import client >+ >+class BidiSession(client.Session): >+ def __init__(self, >+ host, >+ port, >+ url_prefix="/", >+ capabilities=None, >+ extension=None): >+ """ >+ Add a capability of "webSocketUrl": True to enable >+ Bidirectional connection in session creation. >+ """ >+ self.websocket_transport = None >+ capabilities = self._enable_websocket(capabilities) >+ super().__init__(host, port, url_prefix, capabilities, extension) >+ >+ def _enable_websocket(self, caps): >+ if caps: >+ caps.setdefault("alwaysMatch", {}).update({"webSocketUrl": True}) >+ else: >+ caps = {"alwaysMatch": {"webSocketUrl": True}} >+ return caps >+ >+ def match(self, capabilities): >+ """Expensive match to see if capabilities is the same as previously >+ requested capabilities if websocket would be enabled. >+ >+ :return Boolean. >+ """ >+ caps = copy.deepcopy(capabilities) >+ caps = self._enable_websocket(caps) >+ return super().match(caps) >+ >+ async def start(self): >+ """Start a new WebDriver Bidirectional session >+ with websocket connected. >+ >+ :return: Dictionary with `capabilities` and `sessionId`. >+ """ >+ value = super().start() >+ >+ if not self.websocket_transport or not self.websocket_transport.open: >+ self.websocket_transport = await websockets.connect(self.capabilities["webSocketUrl"]) >+ return value >+ >+ async def end(self): >+ """Close websocket connection first before closing session. >+ """ >+ if self.websocket_transport: >+ await self.websocket_transport.close() >+ self.websocket_transport = None >+ super().end() >diff --git a/WebDriverTests/imported/w3c/tools/webdriver/webdriver/bidi/__init__.py b/WebDriverTests/imported/w3c/tools/webdriver/webdriver/bidi/__init__.py >new file mode 100644 >index 000000000000..e7c56332f902 >--- /dev/null >+++ b/WebDriverTests/imported/w3c/tools/webdriver/webdriver/bidi/__init__.py >@@ -0,0 +1,3 @@ >+# flake8: noqa >+ >+from .client import BidiSession >diff --git a/WebDriverTests/imported/w3c/tools/webdriver/webdriver/bidi/client.py b/WebDriverTests/imported/w3c/tools/webdriver/webdriver/bidi/client.py >new file mode 100644 >index 000000000000..4309a25e3873 >--- /dev/null >+++ b/WebDriverTests/imported/w3c/tools/webdriver/webdriver/bidi/client.py >@@ -0,0 +1,393 @@ >+import asyncio >+import functools >+import json >+import logging >+import sys >+from collections import defaultdict >+from typing import Any, Awaitable, Callable, Coroutine, List, Optional, Mapping, MutableMapping >+from urllib.parse import urljoin, urlparse >+ >+import websockets >+ >+ >+logger = logging.getLogger("webdriver.bidi") >+ >+ >+class BidiException(Exception): >+ def __init__(self, err: str, msg: str, stack: Optional[str] = None): >+ self.err = err >+ self.msg = msg >+ self.stack = stack >+ >+ >+def get_running_loop() -> asyncio.AbstractEventLoop: >+ if sys.version_info >= (3, 7): >+ return asyncio.get_running_loop() >+ # Unlike the above, this will actually create an event loop >+ # if there isn't one; hopefully running tests in Python >= 3.7 >+ # will allow us to catch any behaviour difference >+ return asyncio.get_event_loop() >+ >+ >+class BidiSession: >+ """A WebDriver BiDi session. >+ >+ This is the main representation of a BiDi session and provides the >+ interface for running commands in the session, and for attaching >+ event handlers to the session. For example: >+ >+ async def on_log(data): >+ print(data) >+ >+ session = BidiSession("ws://localhost:4445", capabilities) >+ session.add_event_listener("log.entryAdded", on_log) >+ await session.start() >+ await session.subscribe("log.entryAdded") >+ # Do some stuff with the session >+ session.end() >+ >+ If the session id is provided it's assumed that the underlying >+ WebDriver session was already created, and the WebSocket URL was >+ taken from the new session response. If no session id is provided, it's >+ assumed that a BiDi-only session should be created when start() is called. >+ >+ It can also be used as a context manager, with the WebSocket transport >+ implictly being created when the context is entered, and closed when >+ the context is exited. >+ >+ :param websocket_url: WebSockets URL on which to connect to the session. >+ This excludes any path component. >+ :param session_id: String id of existing HTTP session >+ :param capabilities: Capabilities response of existing session >+ :param requested_capabilities: Dictionary representing the capabilities request. >+ >+ """ >+ >+ def __init__(self, >+ websocket_url: str, >+ session_id: Optional[str] = None, >+ capabilities: Optional[Mapping[str, Any]] = None, >+ requested_capabilities: Optional[Mapping[str, Any]] = None, >+ loop: Optional[asyncio.AbstractEventLoop] = None): >+ self.transport: Optional[Transport] = None >+ >+ # The full URL for a websocket looks like >+ # ws://<host>:<port>/session when we're creating a session and >+ # ws://<host>:<port>/session/<sessionid> when we're connecting to an existing session. >+ # To be user friendly, handle the case where the class was created with either a >+ # full URL including the path, and also the case where just a server url is passed in. >+ parsed_url = urlparse(websocket_url) >+ if parsed_url.path == "" or parsed_url.path == "/": >+ if session_id is None: >+ websocket_url = urljoin(websocket_url, "session") >+ else: >+ websocket_url = urljoin(websocket_url, f"session/{session_id}") >+ else: >+ if session_id is not None: >+ if parsed_url.path != f"/session/{session_id}": >+ raise ValueError(f"WebSocket URL {session_id} doesn't match session id") >+ else: >+ if parsed_url.path != "/session": >+ raise ValueError(f"WebSocket URL {session_id} doesn't match session url") >+ >+ if session_id is None and capabilities is not None: >+ raise ValueError("Tried to create BiDi-only session with existing capabilities") >+ >+ self.websocket_url = websocket_url >+ self.requested_capabilities = requested_capabilities >+ self.capabilities = capabilities >+ self.session_id = session_id >+ >+ self.command_id = 0 >+ self.pending_commands: MutableMapping[int, "asyncio.Future[Any]"] = {} >+ self.event_listeners: MutableMapping[Optional[str], List[Callable[[str, Mapping[str, Any]], Any]]] = defaultdict(list) >+ >+ # Modules. >+ # For each module, have a property representing that module >+ self.session = Session(self) >+ >+ if loop is None: >+ loop = get_running_loop() >+ self.loop = loop >+ >+ @classmethod >+ def from_http(cls, >+ session_id: str, >+ capabilities: Mapping[str, Any], >+ loop: Optional[asyncio.AbstractEventLoop] = None) -> "BidiSession": >+ """Create a BiDi session from an existing HTTP session >+ >+ :param session_id: String id of the session >+ :param capabilities: Capabilities returned in the New Session HTTP response.""" >+ websocket_url = capabilities.get("webSocketUrl") >+ if websocket_url is None: >+ raise ValueError("No webSocketUrl found in capabilities") >+ if not isinstance(websocket_url, str): >+ raise ValueError("webSocketUrl is not a string") >+ return cls(websocket_url, session_id=session_id, capabilities=capabilities, loop=loop) >+ >+ @classmethod >+ def bidi_only(cls, >+ websocket_url: str, >+ requested_capabilities: Optional[Mapping[str, Any]], >+ loop: Optional[asyncio.AbstractEventLoop] = None) -> "BidiSession": >+ """Create a BiDi session where there is no existing HTTP session >+ >+ :param webdocket_url: URL to the WebSocket server listening for BiDi connections >+ :param requested_capabilities: Capabilities request for establishing the session.""" >+ return cls(websocket_url, requested_capabilities=requested_capabilities, loop=loop) >+ >+ async def __aenter__(self) -> "BidiSession": >+ await self.start() >+ return self >+ >+ async def __aexit__(self, *args: Any) -> None: >+ await self.end() >+ >+ async def start(self) -> None: >+ """Connect to the WebDriver BiDi remote via WebSockets""" >+ self.transport = Transport(self.websocket_url, self.on_message, loop=self.loop) >+ >+ if self.session_id is None: >+ self.session_id, self.capabilities = await self.session.new(self.requested_capabilities) >+ >+ await self.transport.start() >+ >+ async def send_command(self, method: str, params: Mapping[str, Any]) -> Awaitable[Mapping[str, Any]]: >+ """Send a command to the remote server""" >+ # this isn't threadsafe >+ self.command_id += 1 >+ command_id = self.command_id >+ >+ body = { >+ "id": command_id, >+ "method": method, >+ "params": params >+ } >+ assert command_id not in self.pending_commands >+ self.pending_commands[command_id] = self.loop.create_future() >+ assert self.transport is not None >+ await self.transport.send(body) >+ >+ return self.pending_commands[command_id] >+ >+ async def on_message(self, data: Mapping[str, Any]) -> None: >+ """Handle a message from the remote server""" >+ if "id" in data: >+ # This is a command response or error >+ future = self.pending_commands.get(data["id"]) >+ if future is None: >+ raise ValueError(f"No pending command with id {data['id']}") >+ if "result" in data: >+ future.set_result(data["result"]) >+ elif "error" in data and "message" in data: >+ assert isinstance(data["error"], str) >+ assert isinstance(data["message"], str) >+ future.set_exception(BidiException(data["error"], >+ data["message"], >+ data.get("stacktrace"))) >+ else: >+ raise ValueError(f"Unexpected message: {data!r}") >+ elif "method" in data and "params" in data: >+ # This is an event >+ method = data["method"] >+ listeners = self.event_listeners.get(method, []) >+ if not listeners: >+ listeners = self.event_listeners.get(None, []) >+ for listener in listeners: >+ await listener(method, data["params"]) >+ else: >+ raise ValueError(f"Unexpected message: {data!r}") >+ >+ async def end(self) -> None: >+ """Close websocket connection.""" >+ assert self.transport is not None >+ await self.transport.end() >+ >+ def add_event_listener(self, >+ name: Optional[str], >+ fn: Callable[[str, Mapping[str, Any]], Awaitable[Any]]) -> None: >+ """Add a listener for the event with a given name. >+ >+ If name is None, the listener is called for all messages that are not otherwise >+ handled. >+ >+ :param name: Name of event to listen for or None to register a default handler >+ :param fn: Async callback function that receives event data >+ """ >+ self.event_listeners[name].append(fn) >+ >+ >+class Transport: >+ """Low level message handler for the WebSockets connection""" >+ def __init__(self, url: str, >+ msg_handler: Callable[[Mapping[str, Any]], Coroutine[Any, Any, None]], >+ loop: Optional[asyncio.AbstractEventLoop] = None): >+ self.url = url >+ self.connection: Optional[websockets.WebSocketClientProtocol] = None >+ self.msg_handler = msg_handler >+ self.send_buf: List[Mapping[str, Any]] = [] >+ >+ if loop is None: >+ loop = get_running_loop() >+ self.loop = loop >+ >+ self.read_message_task: Optional[asyncio.Task[Any]] = None >+ >+ async def start(self) -> None: >+ self.connection = await websockets.client.connect(self.url) >+ self.read_message_task = self.loop.create_task(self.read_messages()) >+ >+ for msg in self.send_buf: >+ await self._send(self.connection, msg) >+ >+ async def send(self, data: Mapping[str, Any]) -> None: >+ if self.connection is not None: >+ await self._send(self.connection, data) >+ else: >+ self.send_buf.append(data) >+ >+ @staticmethod >+ async def _send(connection: websockets.WebSocketClientProtocol, data: Mapping[str, Any]) -> None: >+ msg = json.dumps(data) >+ logger.debug("â %s", msg) >+ await connection.send(msg) >+ >+ async def handle(self, msg: str) -> None: >+ logger.debug("â %s", msg) >+ data = json.loads(msg) >+ await self.msg_handler(data) >+ >+ async def end(self) -> None: >+ if self.connection: >+ await self.connection.close() >+ self.connection = None >+ >+ async def read_messages(self) -> None: >+ assert self.connection is not None >+ async for msg in self.connection: >+ if not isinstance(msg, str): >+ raise ValueError("Got a binary message") >+ await self.handle(msg) >+ >+ >+class command: >+ """Decorator for implementing bidi commands >+ >+ Implementing a command involves specifying an async function that >+ builds the parameters to the command. The decorator arranges those >+ parameters to be turned into a send_command call, using the class >+ and method names to determine the method in the call. >+ >+ Commands decorated in this way don't return a future, but await >+ the actual response. In some cases it can be useful to >+ post-process this response before returning it to the client. This >+ can be done by specifying a second decorated method like >+ @command_name.result. That method will then be called once the >+ result of the original command is known, and the return value of >+ the method used as the response of the command. >+ >+ So for an example, if we had a command test.testMethod, which >+ returned a result which we want to convert to a TestResult type, >+ the implementation might look like: >+ >+ class Test(BidiModule): >+ @command >+ def test_method(self, test_data=None): >+ return {"testData": test_data} >+ >+ @test_method.result >+ def convert_test_method_result(self, result): >+ return TestData(**result) >+ """ >+ >+ def __init__(self, fn: Callable[..., Mapping[str, Any]]): >+ self.params_fn = fn >+ self.result_fn: Optional[Callable[..., Any]] = None >+ >+ def result(self, fn: Callable[[Any, MutableMapping[str, Any]], Mapping[str, Any]]) -> None: >+ self.result_fn = fn >+ >+ def __set_name__(self, owner: Any, name: str) -> None: >+ # This is called when the class is created >+ # see https://docs.python.org/3/reference/datamodel.html#object.__set_name__ >+ params_fn = self.params_fn >+ result_fn = self.result_fn >+ >+ @functools.wraps(params_fn) >+ async def inner(self: Any, **kwargs: Any) -> Any: >+ params = params_fn(self, **kwargs) >+ >+ # Convert the classname and the method name to a bidi command name >+ mod_name = owner.__name__.lower() >+ if hasattr(owner, "prefix"): >+ mod_name = f"{owner.prefix}:{mod_name}" >+ cmd_name = f"{mod_name}.{to_camelcase(name)}" >+ >+ future = await self.session.send_command(cmd_name, params) >+ result = await future >+ >+ if result_fn is not None: >+ # Convert the result if we have a conversion function defined >+ result = result_fn(self, result) >+ return result >+ >+ # Overwrite the method on the owner class with the wrapper >+ setattr(owner, name, inner) >+ >+ def __call__(*args: Any, **kwargs: Any) -> Awaitable[Any]: >+ # This isn't really used, but mypy doesn't understand __set_name__ >+ pass >+ >+ >+def to_camelcase(name: str) -> str: >+ """Convert a python style method name foo_bar to a BiDi command name fooBar""" >+ parts = name.split("_") >+ parts[0] = parts[0].lower() >+ for i in range(1, len(parts)): >+ parts[i] = parts[i].title() >+ return "".join(parts) >+ >+ >+class BidiModule: >+ def __init__(self, session: BidiSession): >+ self.session = session >+ >+ >+class Session(BidiModule): >+ @command >+ def new(self, capabilities: Mapping[str, Any]) -> Mapping[str, Mapping[str, Any]]: >+ return {"capabilities": capabilities} >+ >+ @new.result >+ def _new(self, result: Mapping[str, Any]) -> Any: >+ return result.get("session_id"), result.get("capabilities", {}) >+ >+ @command >+ def subscribe(self, >+ events: Optional[List[str]] = None, >+ contexts: Optional[List[str]] = None) -> Mapping[str, Any]: >+ params: MutableMapping[str, Any] = {"events": events if events is not None else []} >+ if contexts is not None: >+ params["contexts"] = contexts >+ return params >+ >+ @command >+ def unsubscribe(self, >+ events: Optional[List[str]] = None, >+ contexts: Optional[List[str]] = None) -> Mapping[str, Any]: >+ params: MutableMapping[str, Any] = {"events": events if events is not None else []} >+ if contexts is not None: >+ params["contexts"] = contexts >+ return params >+ >+ >+class Test(BidiModule): >+ """Very temporary module that does nothing, except demonstrate a vendor prefix and >+ provide a way to work with Gecko's current skeleton implementation.""" >+ >+ prefix = "moz" >+ >+ @command >+ def test_method(self, **kwargs): >+ return kwargs >diff --git a/WebDriverTests/imported/w3c/tools/webdriver/webdriver/client.py b/WebDriverTests/imported/w3c/tools/webdriver/webdriver/client.py >index f4e3c763795e..2c7b1c5dbeb1 100644 >--- a/WebDriverTests/imported/w3c/tools/webdriver/webdriver/client.py >+++ b/WebDriverTests/imported/w3c/tools/webdriver/webdriver/client.py >@@ -1,9 +1,9 @@ > from . import error > from . import protocol > from . import transport >+from .bidi.client import BidiSession > >-from six import string_types >-from six.moves.urllib import parse as urlparse >+from urllib import parse as urlparse > > > def command(func): >@@ -116,14 +116,49 @@ class ActionSequence(object): > def _key_action(self, subtype, value): > self._actions.append({"type": subtype, "value": value}) > >- def _pointer_action(self, subtype, button): >- self._actions.append({"type": subtype, "button": button}) >+ def _pointer_action(self, subtype, button=None, x=None, y=None, duration=None, origin=None, width=None, >+ height=None, pressure=None, tangential_pressure=None, tilt_x=None, >+ tilt_y=None, twist=None, altitude_angle=None, azimuth_angle=None): >+ action = { >+ "type": subtype >+ } >+ if button is not None: >+ action["button"] = button >+ if x is not None: >+ action["x"] = x >+ if y is not None: >+ action["y"] = y >+ if duration is not None: >+ action["duration"] = duration >+ if origin is not None: >+ action["origin"] = origin >+ if width is not None: >+ action["width"] = width >+ if height is not None: >+ action["height"] = height >+ if pressure is not None: >+ action["pressure"] = pressure >+ if tangential_pressure is not None: >+ action["tangentialPressure"] = tangential_pressure >+ if tilt_x is not None: >+ action["tiltX"] = tilt_x >+ if tilt_y is not None: >+ action["tiltY"] = tilt_y >+ if twist is not None: >+ action["twist"] = twist >+ if altitude_angle is not None: >+ action["altitudeAngle"] = altitude_angle >+ if azimuth_angle is not None: >+ action["azimuthAngle"] = azimuth_angle >+ self._actions.append(action) > > def pause(self, duration): > self._actions.append({"type": "pause", "duration": duration}) > return self > >- def pointer_move(self, x, y, duration=None, origin=None): >+ def pointer_move(self, x, y, duration=None, origin=None, width=None, height=None, >+ pressure=None, tangential_pressure=None, tilt_x=None, tilt_y=None, >+ twist=None, altitude_angle=None, azimuth_angle=None): > """Queue a pointerMove action. > > :param x: Destination x-axis coordinate of pointer in CSS pixels. >@@ -133,16 +168,10 @@ class ActionSequence(object): > :param origin: Origin of coordinates, either "viewport", "pointer" or > an Element. If None, remote end defaults to "viewport". > """ >- action = { >- "type": "pointerMove", >- "x": x, >- "y": y >- } >- if duration is not None: >- action["duration"] = duration >- if origin is not None: >- action["origin"] = origin >- self._actions.append(action) >+ self._pointer_action("pointerMove", x=x, y=y, duration=duration, origin=origin, >+ width=width, height=height, pressure=pressure, >+ tangential_pressure=tangential_pressure, tilt_x=tilt_x, tilt_y=tilt_y, >+ twist=twist, altitude_angle=altitude_angle, azimuth_angle=azimuth_angle) > return self > > def pointer_up(self, button=0): >@@ -151,16 +180,21 @@ class ActionSequence(object): > :param button: Pointer button to perform action with. > Default: 0, which represents main device button. > """ >- self._pointer_action("pointerUp", button) >+ self._pointer_action("pointerUp", button=button) > return self > >- def pointer_down(self, button=0): >+ def pointer_down(self, button=0, width=None, height=None, pressure=None, >+ tangential_pressure=None, tilt_x=None, tilt_y=None, >+ twist=None, altitude_angle=None, azimuth_angle=None): > """Queue a pointerDown action for `button`. > > :param button: Pointer button to perform action with. > Default: 0, which represents main device button. > """ >- self._pointer_action("pointerDown", button) >+ self._pointer_action("pointerDown", button=button, width=width, height=height, >+ pressure=pressure, tangential_pressure=tangential_pressure, >+ tilt_x=tilt_x, tilt_y=tilt_y, twist=twist, altitude_angle=altitude_angle, >+ azimuth_angle=azimuth_angle) > return self > > def click(self, element=None, button=0): >@@ -273,19 +307,22 @@ class Window(object): > > return handles > >- @property >+ # The many "type: ignore" comments here and below are to silence mypy's >+ # "Decorated property not supported" error, which is due to a limitation >+ # in mypy, see https://github.com/python/mypy/issues/1362. >+ @property # type: ignore > @command > def rect(self): > return self.session.send_session_command("GET", "window/rect") > >- @property >+ @property # type: ignore > @command > def size(self): > """Gets the window size as a tuple of `(width, height)`.""" > rect = self.rect > return (rect["width"], rect["height"]) > >- @size.setter >+ @size.setter # type: ignore > @command > def size(self, new_size): > """Set window size by passing a tuple of `(width, height)`.""" >@@ -293,14 +330,14 @@ class Window(object): > body = {"width": width, "height": height} > self.session.send_session_command("POST", "window/rect", body) > >- @property >+ @property # type: ignore > @command > def position(self): > """Gets the window position as a tuple of `(x, y)`.""" > rect = self.rect > return (rect["x"], rect["y"]) > >- @position.setter >+ @position.setter # type: ignore > @command > def position(self, new_position): > """Set window position by passing a tuple of `(x, y)`.""" >@@ -338,6 +375,42 @@ class Frame(object): > return cls(uuid, session) > > >+class ShadowRoot(object): >+ identifier = "shadow-6066-11e4-a52e-4f735466cecf" >+ >+ def __init__(self, session, id): >+ """ >+ Construct a new shadow root representation. >+ >+ :param id: Shadow root UUID which must be unique across >+ all browsing contexts. >+ :param session: Current ``webdriver.Session``. >+ """ >+ self.id = id >+ self.session = session >+ >+ @classmethod >+ def from_json(cls, json, session): >+ uuid = json[ShadowRoot.identifier] >+ return cls(uuid, session) >+ >+ def send_shadow_command(self, method, uri, body=None): >+ url = "shadow/{}/{}".format(self.id, uri) >+ return self.session.send_session_command(method, url, body) >+ >+ @command >+ def find_element(self, strategy, selector): >+ body = {"using": strategy, >+ "value": selector} >+ return self.send_shadow_command("POST", "element", body) >+ >+ @command >+ def find_elements(self, strategy, selector): >+ body = {"using": strategy, >+ "value": selector} >+ return self.send_shadow_command("POST", "elements", body) >+ >+ > class Find(object): > def __init__(self, session): > self.session = session >@@ -365,7 +438,7 @@ class Cookies(object): > cookie = {"name": name, > "value": None} > >- if isinstance(name, string_types): >+ if isinstance(name, str): > cookie["value"] = value > elif hasattr(value, "value"): > cookie["value"] = value.value >@@ -384,12 +457,12 @@ class UserPrompt(object): > def accept(self): > self.session.send_session_command("POST", "alert/accept") > >- @property >+ @property # type: ignore > @command > def text(self): > return self.session.send_session_command("GET", "alert/text") > >- @text.setter >+ @text.setter # type: ignore > @command > def text(self, value): > body = {"text": value} >@@ -401,8 +474,16 @@ class Session(object): > host, > port, > url_prefix="/", >+ enable_bidi=False, > capabilities=None, > extension=None): >+ >+ if enable_bidi: >+ if capabilities is not None: >+ capabilities.setdefault("alwaysMatch", {}).update({"webSocketUrl": True}) >+ else: >+ capabilities = {"alwaysMatch": {"webSocketUrl": True}} >+ > self.transport = transport.HTTPWireProtocol(host, port, url_prefix) > self.requested_capabilities = capabilities > self.capabilities = None >@@ -410,6 +491,8 @@ class Session(object): > self.timeouts = None > self.window = None > self.find = None >+ self.enable_bidi = enable_bidi >+ self.bidi_session = None > self.extension = None > self.extension_cls = extension > >@@ -436,6 +519,9 @@ class Session(object): > def __del__(self): > self.end() > >+ def match(self, capabilities): >+ return self.requested_capabilities == capabilities >+ > def start(self): > """Start a new WebDriver session. > >@@ -458,6 +544,14 @@ class Session(object): > self.session_id = value["sessionId"] > self.capabilities = value["capabilities"] > >+ if "webSocketUrl" in self.capabilities: >+ self.bidi_session = BidiSession.from_http(self.session_id, >+ self.capabilities) >+ elif self.enable_bidi: >+ self.end() >+ raise error.SessionNotCreatedException( >+ "Requested bidi session, but webSocketUrl capability not found") >+ > if self.extension_cls: > self.extension = self.extension_cls(self) > >@@ -470,7 +564,7 @@ class Session(object): > > try: > self.send_command("DELETE", "session/%s" % self.session_id) >- except error.InvalidSessionIdException: >+ except (OSError, error.InvalidSessionIdException): > pass > finally: > self.session_id = None >@@ -545,12 +639,12 @@ class Session(object): > url = urlparse.urljoin("session/%s/" % self.session_id, uri) > return self.send_command(method, url, body, timeout) > >- @property >+ @property # type: ignore > @command > def url(self): > return self.send_session_command("GET", "url") > >- @url.setter >+ @url.setter # type: ignore > @command > def url(self, url): > if urlparse.urlsplit(url).netloc is None: >@@ -570,12 +664,12 @@ class Session(object): > def refresh(self): > return self.send_session_command("POST", "refresh") > >- @property >+ @property # type: ignore > @command > def title(self): > return self.send_session_command("GET", "title") > >- @property >+ @property # type: ignore > @command > def source(self): > return self.send_session_command("GET", "source") >@@ -587,12 +681,12 @@ class Session(object): > > return value["handle"] > >- @property >+ @property # type: ignore > @command > def window_handle(self): > return self.send_session_command("GET", "window") > >- @window_handle.setter >+ @window_handle.setter # type: ignore > @command > def window_handle(self, handle): > body = {"handle": handle} >@@ -608,12 +702,12 @@ class Session(object): > > return self.send_session_command("POST", url, body) > >- @property >+ @property # type: ignore > @command > def handles(self): > return self.send_session_command("GET", "window/handles") > >- @property >+ @property # type: ignore > @command > def active_element(self): > return self.send_session_command("GET", "element/active") >@@ -683,7 +777,6 @@ class Session(object): > def screenshot(self): > return self.send_session_command("GET", "screenshot") > >- > class Element(object): > """ > Representation of a web element. >@@ -742,12 +835,12 @@ class Element(object): > def send_keys(self, text): > return self.send_element_command("POST", "value", {"text": text}) > >- @property >+ @property # type: ignore > @command > def text(self): > return self.send_element_command("GET", "text") > >- @property >+ @property # type: ignore > @command > def name(self): > return self.send_element_command("GET", "name") >@@ -756,12 +849,12 @@ class Element(object): > def style(self, property_name): > return self.send_element_command("GET", "css/%s" % property_name) > >- @property >+ @property # type: ignore > @command > def rect(self): > return self.send_element_command("GET", "rect") > >- @property >+ @property # type: ignore > @command > def selected(self): > return self.send_element_command("GET", "selected") >@@ -770,6 +863,11 @@ class Element(object): > def screenshot(self): > return self.send_element_command("GET", "screenshot") > >+ @property # type: ignore >+ @command >+ def shadow_root(self): >+ return self.send_element_command("GET", "shadow") >+ > @command > def attribute(self, name): > return self.send_element_command("GET", "attribute/%s" % name) >diff --git a/WebDriverTests/imported/w3c/tools/webdriver/webdriver/error.py b/WebDriverTests/imported/w3c/tools/webdriver/webdriver/error.py >index 8379656d73dd..a413a950d48e 100644 >--- a/WebDriverTests/imported/w3c/tools/webdriver/webdriver/error.py >+++ b/WebDriverTests/imported/w3c/tools/webdriver/webdriver/error.py >@@ -1,12 +1,17 @@ > import collections > import json > >-from six import itervalues >+from typing import ClassVar, DefaultDict, Type > > > class WebDriverException(Exception): >- http_status = None >- status_code = None >+ # The status_code class variable is used to map the JSON Error Code (see >+ # https://w3c.github.io/webdriver/#errors) to a WebDriverException subclass. >+ # However, http_status need not match, and both are set as instance >+ # variables, shadowing the class variables. TODO: Match on both http_status >+ # and status_code and let these be class variables only. >+ http_status = None # type: ClassVar[int] >+ status_code = None # type: ClassVar[str] > > def __init__(self, http_status=None, status_code=None, message=None, stacktrace=None): > super(WebDriverException, self) >@@ -34,6 +39,11 @@ class WebDriverException(Exception): > return message > > >+class DetachedShadowRootException(WebDriverException): >+ http_status = 404 >+ status_code = "detached shadow root" >+ >+ > class ElementClickInterceptedException(WebDriverException): > http_status = 400 > status_code = "element click intercepted" >@@ -114,6 +124,11 @@ class NoSuchFrameException(WebDriverException): > status_code = "no such frame" > > >+class NoSuchShadowRootException(WebDriverException): >+ http_status = 404 >+ status_code = "no such shadow root" >+ >+ > class NoSuchWindowException(WebDriverException): > http_status = 404 > status_code = "no such window" >@@ -209,7 +224,7 @@ def get(error_code): > return _errors.get(error_code, WebDriverException) > > >-_errors = collections.defaultdict() >-for item in list(itervalues(locals())): >+_errors: DefaultDict[str, Type[WebDriverException]] = collections.defaultdict() >+for item in list(locals().values()): > if type(item) == type and issubclass(item, WebDriverException): > _errors[item.status_code] = item >diff --git a/WebDriverTests/imported/w3c/tools/webdriver/webdriver/protocol.py b/WebDriverTests/imported/w3c/tools/webdriver/webdriver/protocol.py >index c811f6f26db7..e71e01d62083 100644 >--- a/WebDriverTests/imported/w3c/tools/webdriver/webdriver/protocol.py >+++ b/WebDriverTests/imported/w3c/tools/webdriver/webdriver/protocol.py >@@ -2,8 +2,6 @@ import json > > import webdriver > >-from six import iteritems >- > > """WebDriver wire protocol codecs.""" > >@@ -22,6 +20,8 @@ class Encoder(json.JSONEncoder): > return {webdriver.Frame.identifier: obj.id} > elif isinstance(obj, webdriver.Window): > return {webdriver.Frame.identifier: obj.id} >+ elif isinstance(obj, webdriver.ShadowRoot): >+ return {webdriver.ShadowRoot.identifier: obj.id} > return super(Encoder, self).default(obj) > > >@@ -40,6 +40,8 @@ class Decoder(json.JSONDecoder): > return webdriver.Frame.from_json(payload, self.session) > elif isinstance(payload, dict) and webdriver.Window.identifier in payload: > return webdriver.Window.from_json(payload, self.session) >+ elif isinstance(payload, dict) and webdriver.ShadowRoot.identifier in payload: >+ return webdriver.ShadowRoot.from_json(payload, self.session) > elif isinstance(payload, dict): >- return {k: self.object_hook(v) for k, v in iteritems(payload)} >+ return {k: self.object_hook(v) for k, v in payload.items()} > return payload >diff --git a/WebDriverTests/imported/w3c/tools/webdriver/webdriver/transport.py b/WebDriverTests/imported/w3c/tools/webdriver/webdriver/transport.py >index e836b19a158d..5a1ce289ec09 100644 >--- a/WebDriverTests/imported/w3c/tools/webdriver/webdriver/transport.py >+++ b/WebDriverTests/imported/w3c/tools/webdriver/webdriver/transport.py >@@ -1,10 +1,9 @@ > import json > import select > >-from six import text_type, PY3 >-from six.moves.collections_abc import Mapping >-from six.moves.http_client import HTTPConnection >-from six.moves.urllib import parse as urlparse >+from http.client import HTTPConnection >+from typing import Dict, List, Mapping, Sequence, Tuple >+from urllib import parse as urlparse > > from . import error > >@@ -14,7 +13,7 @@ from . import error > missing = object() > > >-class ResponseHeaders(Mapping): >+class ResponseHeaders(Mapping[str, str]): > """Read-only dictionary-like API for accessing response headers. > > This class: >@@ -23,11 +22,9 @@ class ResponseHeaders(Mapping): > * Has case-insensitive header lookup. > * Always returns all header values that have the same name, separated by > commas. >- >- It does not ensure header types (e.g. binary vs string). > """ >- def __init__(self, items): >- self.headers_dict = {} >+ def __init__(self, items: Sequence[Tuple[str, str]]): >+ self.headers_dict: Dict[str, List[str]] = {} > for key, value in items: > key = key.lower() > if key not in self.headers_dict: >@@ -145,15 +142,18 @@ class HTTPWireProtocol(object): > def close(self): > """Closes the current HTTP connection, if there is one.""" > if self._conn: >- self._conn.close() >+ try: >+ self._conn.close() >+ except OSError: >+ # The remote closed the connection >+ pass >+ self._conn = None > > @property > def connection(self): > """Gets the current HTTP connection, or lazily creates one.""" > if not self._conn: > conn_kwargs = {} >- if not PY3: >- conn_kwargs["strict"] = True > # We are not setting an HTTP timeout other than the default when the > # connection its created. The send method has a timeout value if needed. > self._conn = HTTPConnection(self.host, self.port, **conn_kwargs) >@@ -235,7 +235,7 @@ class HTTPWireProtocol(object): > return Response.from_http(response, decoder=decoder, **codec_kwargs) > > def _request(self, method, uri, payload, headers=None, timeout=None): >- if isinstance(payload, text_type): >+ if isinstance(payload, str): > payload = payload.encode("utf-8") > > if headers is None: >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/README.rst b/WebDriverTests/imported/w3c/tools/wptrunner/README.rst >index 0adf0a94b15d..4a079c725bd6 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/README.rst >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/README.rst >@@ -6,7 +6,6 @@ wptrunner is a harness for running the W3C `web-platform-tests testsuite`_. > .. toctree:: > :maxdepth: 2 > >- docs/usage > docs/expectation > docs/design > docs/internals >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/docs/usage.rst b/WebDriverTests/imported/w3c/tools/wptrunner/docs/usage.rst >deleted file mode 100644 >index 85fd97bf6c12..000000000000 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/docs/usage.rst >+++ /dev/null >@@ -1,242 +0,0 @@ >-Getting Started >-=============== >- >-Installing wptrunner >--------------------- >- >-The easiest way to install wptrunner is into a virtualenv, using pip:: >- >- virtualenv wptrunner >- cd wptrunner >- source bin/activate >- pip install wptrunner >- >-This will install the base dependencies for wptrunner, but not any >-extra dependencies required to test against specific browsers. In >-order to do this you must use use the extra requirements files in >-``$VIRTUAL_ENV/requirements/requirements_browser.txt``. For example, >-in order to test against Firefox you would have to run:: >- >- pip install -r requirements/requirements_firefox.txt >- >-If you intend to work on the code, the ``-e`` option to pip should be >-used in combination with a source checkout i.e. inside a virtual >-environment created as above:: >- >- git clone https://github.com/w3c/wptrunner.git >- cd wptrunner >- pip install -e ./ >- >-In addition to the dependencies installed by pip, wptrunner requires >-a copy of the web-platform-tests repository. This can be located >-anywhere on the filesystem, but the easiest option is to put it >-under the same parent directory as the wptrunner checkout:: >- >- git clone https://github.com/web-platform-tests/wpt.git >- >-It is also necessary to generate a web-platform-tests ``MANIFEST.json`` >-file. It's recommended to also put that under the same parent directory as >-the wptrunner checkout, in a directory named ``meta``:: >- >- mkdir meta >- cd web-platform-tests >- python manifest --path ../meta/MANIFEST.json >- >-The ``MANIFEST.json`` file needs to be regenerated each time the >-web-platform-tests checkout is updated. To aid with the update process >-there is a tool called ``wptupdate``, which is described in >-:ref:`wptupdate-label`. >- >-Running the Tests >------------------ >- >-A test run is started using the ``wptrunner`` command. The command >-takes multiple options, of which the following are most significant: >- >-``--product`` (defaults to `firefox`) >- The product to test against: `chrome`, `firefox`, or `servo`. >- >-``--binary`` (required if product is `firefox` or `servo`) >- The path to a binary file for the product (browser) to test against. >- >-``--webdriver-binary`` (required if product is `chrome`) >- The path to a `*driver` binary; e.g., a `chromedriver` binary. >- >-``--certutil-binary`` (required if product is `firefox` [#]_) >- The path to a `certutil` binary (for tests that must be run over https). >- >-``--metadata`` (required only when not `using default paths`_) >- The path to a directory containing test metadata. [#]_ >- >-``--tests`` (required only when not `using default paths`_) >- The path to a directory containing a web-platform-tests checkout. >- >-``--prefs-root`` (required only when testing a Firefox binary) >- The path to a directory containing Firefox test-harness preferences. [#]_ >- >-``--config`` (should default to `wptrunner.default.ini`) >- The path to the config (ini) file. >- >-.. [#] The ``--certutil-binary`` option is required when the product is >- ``firefox`` unless ``--ssl-type=none`` is specified. >- >-.. [#] The ``--metadata`` path is to a directory that contains: >- >- * a ``MANIFEST.json`` file (the web-platform-tests documentation has >- instructions on generating this file) >- * (optionally) any expectation files (see :ref:`wptupdate-label`) >- >-.. [#] Example ``--prefs-root`` value: ``~/mozilla-central/testing/profiles``. >- >-There are also a variety of other command-line options available; use >-``--help`` to list them. >- >-The following examples show how to start wptrunner with various options. >- >------------------- >-Starting wptrunner >------------------- >- >-The examples below assume the following directory layout, >-though no specific folder structure is required:: >- >- ~/testtwf/wptrunner # wptrunner checkout >- ~/testtwf/web-platform-tests # web-platform-tests checkout >- ~/testtwf/meta # metadata >- >-To test a Firefox Nightly build in an OS X environment, you might start >-wptrunner using something similar to the following example:: >- >- wptrunner --metadata=~/testtwf/meta/ --tests=~/testtwf/web-platform-tests/ \ >- --binary=~/mozilla-central/obj-x86_64-apple-darwin14.3.0/dist/Nightly.app/Contents/MacOS/firefox \ >- --certutil-binary=~/mozilla-central/obj-x86_64-apple-darwin14.3.0/security/nss/cmd/certutil/certutil \ >- --prefs-root=~/mozilla-central/testing/profiles >- >- >-And to test a Chromium build in an OS X environment, you might start >-wptrunner using something similar to the following example:: >- >- wptrunner --metadata=~/testtwf/meta/ --tests=~/testtwf/web-platform-tests/ \ >- --binary=~/chromium/src/out/Release/Chromium.app/Contents/MacOS/Chromium \ >- --webdriver-binary=/usr/local/bin/chromedriver --product=chrome >- >--------------------- >-Running test subsets >--------------------- >- >-To restrict a test run just to tests in a particular web-platform-tests >-subdirectory, specify the directory name in the positional arguments after >-the options; for example, run just the tests in the `dom` subdirectory:: >- >- wptrunner --metadata=~/testtwf/meta --tests=~/testtwf/web-platform-tests/ \ >- --binary=/path/to/firefox --certutil-binary=/path/to/certutil \ >- --prefs-root=/path/to/testing/profiles \ >- dom >- >-------------------- >-Running in parallel >-------------------- >- >-To speed up the testing process, use the ``--processes`` option to have >-wptrunner run multiple browser instances in parallel. For example, to >-have wptrunner attempt to run tests against with six browser instances >-in parallel, specify ``--processes=6``. But note that behaviour in this >-mode is necessarily less deterministic than with ``--processes=1`` (the >-default), so there may be more noise in the test results. >- >-------------------- >-Using default paths >-------------------- >- >-The (otherwise-required) ``--tests`` and ``--metadata`` command-line >-options/flags be omitted if any configuration file is found that >-contains a section specifying the ``tests`` and ``metadata`` keys. >- >-See the `Configuration File`_ section for more information about >-configuration files, including information about their expected >-locations. >- >-The content of the ``wptrunner.default.ini`` default configuration file >-makes wptrunner look for tests (that is, a web-platform-tests checkout) >-as a subdirectory of the current directory named ``tests``, and for >-metadata files in a subdirectory of the current directory named ``meta``. >- >-Output >------- >- >-wptrunner uses `the mozlog package >-<https://firefox-source-docs.mozilla.org/mozbase/mozlog.html>`_) for >-output. This structures events such as test results or log messages as >-JSON objects that can then be fed to other tools for >-interpretation. More details about the message format are given in >-`the mozlog documentation >-<https://firefox-source-docs.mozilla.org/mozbase/mozlog.html>`_. >- >-By default the raw JSON messages are dumped to stdout. This is >-convenient for piping into other tools, but not ideal for humans >-reading the output. `mozlog >-<https://firefox-source-docs.mozilla.org/mozbase/mozlog.html>`_ comes >-with several other formatters, which are accessible through command >-line options. The general format of these options is >-``--log-name=dest``, where ``name`` is the name of the format and >-``dest`` is a path to a destination file, or ``-`` for stdout. The raw >-JSON data is written by the ``raw`` formatter so, the default setup >-corresponds to ``--log-raw=-``. >- >-A reasonable output format for humans is provided as ``mach``. So in >-order to output the full raw log to a file and a human-readable >-summary to stdout, one might pass the options:: >- >- --log-raw=output.log --log-mach=- >- >-Configuration File >------------------- >- >-wptrunner uses a ``.ini`` file to control some configuration >-sections. The file has three sections; ``[products]``, >-``[manifest:default]`` and ``[web-platform-tests]``. >- >-``[products]`` is used to >-define the set of available products. By default this section is empty >-which means that all the products distributed with wptrunner are >-enabled (although their dependencies may not be installed). The set >-of enabled products can be set by using the product name as the >-key. For built in products the value is empty. It is also possible to >-provide the path to a script implementing the browser functionality >-e.g.:: >- >- [products] >- chrome = >- netscape4 = path/to/netscape.py >- >-``[manifest:default]`` specifies the default paths for the tests and metadata, >-relative to the config file. For example:: >- >- [manifest:default] >- tests = ~/testtwf/web-platform-tests >- metadata = ~/testtwf/meta >- >- >-``[web-platform-tests]`` is used to set the properties of the upstream >-repository when updating the paths. ``remote_url`` specifies the git >-url to pull from; ``branch`` the branch to sync against and >-``sync_path`` the local path, relative to the configuration file, to >-use when checking out the tests e.g.:: >- >- [web-platform-tests] >- remote_url = https://github.com/web-platform-tests/wpt.git >- branch = master >- sync_path = sync >- >-A configuration file must contain all the above fields; falling back >-to the default values for unspecified fields is not yet supported. >- >-The ``wptrunner`` and ``wptupdate`` commands will use configuration >-files in the following order: >- >- * Any path supplied with a ``--config`` flag to the command. >- >- * A file called ``wptrunner.ini`` in the current directory >- >- * The default configuration file (``wptrunner.default.ini`` in the >- source directory) >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/requirements.txt b/WebDriverTests/imported/w3c/tools/wptrunner/requirements.txt >index 1eba114a31c2..8356c4785dc0 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/requirements.txt >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/requirements.txt >@@ -1,9 +1,8 @@ > html5lib==1.1 >-mozinfo==1.2.1 # https://bugzilla.mozilla.org/show_bug.cgi?id=1621226 >-mozlog==6.1 >-mozdebug==0.2 >-# Pillow 7 requires Python 3 >-pillow==6.2.2 # pyup: <7.0 >-urllib3[secure]==1.25.10 >-requests==2.24.0 >-six==1.15.0 >+mozinfo==1.2.2 # https://bugzilla.mozilla.org/show_bug.cgi?id=1621226 >+mozlog==7.1.0 >+mozdebug==0.3.0 >+pillow==8.3.1 >+urllib3[secure]==1.26.6 >+requests==2.25.1 >+six==1.16.0 >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/requirements_chrome.txt b/WebDriverTests/imported/w3c/tools/wptrunner/requirements_chrome.txt >index e7e7161b1426..79619558a5dc 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/requirements_chrome.txt >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/requirements_chrome.txt >@@ -1 +1 @@ >-mozprocess==1.2.0 >+mozprocess==1.3.0 >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/requirements_chrome_android.txt b/WebDriverTests/imported/w3c/tools/wptrunner/requirements_chrome_android.txt >index e7e7161b1426..79619558a5dc 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/requirements_chrome_android.txt >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/requirements_chrome_android.txt >@@ -1 +1 @@ >-mozprocess==1.2.0 >+mozprocess==1.3.0 >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/requirements_edge.txt b/WebDriverTests/imported/w3c/tools/wptrunner/requirements_edge.txt >index 0a61208d77c1..7ad0180b8545 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/requirements_edge.txt >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/requirements_edge.txt >@@ -1,2 +1,2 @@ >-mozprocess==1.2.0 >+mozprocess==1.3.0 > selenium==3.141.0 >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/requirements_firefox.txt b/WebDriverTests/imported/w3c/tools/wptrunner/requirements_firefox.txt >index 8b45703a5596..6bf280c47c92 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/requirements_firefox.txt >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/requirements_firefox.txt >@@ -1,10 +1,10 @@ > marionette_driver==3.1.0 >-mozcrash==2.0.0 >-mozinstall==2.0.0 >+mozcrash==2.1.0 >+mozinstall==2.0.1 > mozleak==0.2 > moznetwork==1.1.0 >-mozprocess==1.2.0 >+mozprocess==1.3.0 > mozprofile==2.5.0 >-mozrunner==8.0.0 >+mozrunner==8.2.1 > mozversion==2.3.0 >-psutil==5.7.2 >+psutil==5.8.0 >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/requirements_ie.txt b/WebDriverTests/imported/w3c/tools/wptrunner/requirements_ie.txt >index 0a61208d77c1..7ad0180b8545 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/requirements_ie.txt >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/requirements_ie.txt >@@ -1,2 +1,2 @@ >-mozprocess==1.2.0 >+mozprocess==1.3.0 > selenium==3.141.0 >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/requirements_opera.txt b/WebDriverTests/imported/w3c/tools/wptrunner/requirements_opera.txt >index 0a61208d77c1..7ad0180b8545 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/requirements_opera.txt >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/requirements_opera.txt >@@ -1,2 +1,2 @@ >-mozprocess==1.2.0 >+mozprocess==1.3.0 > selenium==3.141.0 >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/requirements_safari.txt b/WebDriverTests/imported/w3c/tools/wptrunner/requirements_safari.txt >index e7e7161b1426..65b76aa85cc4 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/requirements_safari.txt >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/requirements_safari.txt >@@ -1 +1,2 @@ >-mozprocess==1.2.0 >+mozprocess==1.3.0 >+psutil==5.8.0 >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/requirements_sauce.txt b/WebDriverTests/imported/w3c/tools/wptrunner/requirements_sauce.txt >index 34c66ee238b6..d1d0f7e2bee2 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/requirements_sauce.txt >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/requirements_sauce.txt >@@ -1,3 +1,3 @@ >-mozprocess==1.2.0 >+mozprocess==1.3.0 > selenium==3.141.0 >-requests==2.24.0 >+requests==2.25.1 >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/requirements_servo.txt b/WebDriverTests/imported/w3c/tools/wptrunner/requirements_servo.txt >index e7e7161b1426..79619558a5dc 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/requirements_servo.txt >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/requirements_servo.txt >@@ -1 +1 @@ >-mozprocess==1.2.0 >+mozprocess==1.3.0 >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/requirements_webkit.txt b/WebDriverTests/imported/w3c/tools/wptrunner/requirements_webkit.txt >index e7e7161b1426..79619558a5dc 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/requirements_webkit.txt >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/requirements_webkit.txt >@@ -1 +1 @@ >-mozprocess==1.2.0 >+mozprocess==1.3.0 >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/setup.py b/WebDriverTests/imported/w3c/tools/wptrunner/setup.py >index a459e2c47141..c20955848dbf 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/setup.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/setup.py >@@ -2,8 +2,6 @@ > # 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 __future__ import print_function >- > import glob > import os > import sys >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/tox.ini b/WebDriverTests/imported/w3c/tools/wptrunner/tox.ini >index 798f64a3e422..c49e759ead8c 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/tox.ini >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/tox.ini >@@ -2,16 +2,12 @@ > xfail_strict=true > > [tox] >-envlist = py27-{base,chrome,edge,firefox,ie,opera,safari,sauce,servo,webkit,webkitgtk_minibrowser,epiphany},{py35,py36,py37,py38}-base >+envlist = py39-{base,chrome,edge,firefox,ie,opera,safari,sauce,servo,webkit,webkitgtk_minibrowser,epiphany},{py36,py37,py38}-base > skip_missing_interpreters = False > > [testenv] > deps = >- pytest>=2.9 >- pytest-cov >- pytest-xdist >- # mock no longer supports Python 2 since v4 >- mock==3.* >+ -r{toxinidir}/../requirements_pytest.txt > -r{toxinidir}/requirements.txt > chrome: -r{toxinidir}/requirements_chrome.txt > edge: -r{toxinidir}/requirements_edge.txt >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/android_weblayer.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/android_weblayer.py >index 6110450b7282..4bd871d1fbd5 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/android_weblayer.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/android_weblayer.py >@@ -1,9 +1,8 @@ >-import subprocess >- >-from .base import Browser, ExecutorBrowser, require_arg >-from .base import get_timeout_multiplier # noqa: F401 >+from .base import NullBrowser # noqa: F401 >+from .base import require_arg >+from .base import get_timeout_multiplier # noqa: F401 > from .chrome import executor_kwargs as chrome_executor_kwargs >-from ..webdriver_server import ChromeDriverServer >+from .chrome_android import ChromeAndroidBrowserBase > from ..executors.executorwebdriver import (WebDriverTestharnessExecutor, # noqa: F401 > WebDriverRefTestExecutor) # noqa: F401 > from ..executors.executorchrome import ChromeDriverWdspecExecutor # noqa: F401 >@@ -11,7 +10,8 @@ from ..executors.executorchrome import ChromeDriverWdspecExecutor # noqa: F401 > > __wptrunner__ = {"product": "android_weblayer", > "check_args": "check_args", >- "browser": "WeblayerShell", >+ "browser": {None: "WeblayerShell", >+ "wdspec": "NullBrowser"}, > "executor": {"testharness": "WebDriverTestharnessExecutor", > "reftest": "WebDriverRefTestExecutor", > "wdspec": "ChromeDriverWdspecExecutor"}, >@@ -28,23 +28,24 @@ def check_args(**kwargs): > require_arg(kwargs, "webdriver_binary") > > >-def browser_kwargs(test_type, run_info_data, config, **kwargs): >+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs): > return {"binary": kwargs["binary"], > "device_serial": kwargs["device_serial"], > "webdriver_binary": kwargs["webdriver_binary"], >- "webdriver_args": kwargs.get("webdriver_args")} >+ "webdriver_args": kwargs.get("webdriver_args"), >+ "stackwalk_binary": kwargs.get("stackwalk_binary"), >+ "symbols_path": kwargs.get("symbols_path")} > > >-def executor_kwargs(test_type, server_config, cache_manager, run_info_data, >+def executor_kwargs(logger, test_type, test_environment, run_info_data, > **kwargs): > # Use update() to modify the global list in place. > _wptserve_ports.update(set( >- server_config['ports']['http'] + server_config['ports']['https'] + >- server_config['ports']['ws'] + server_config['ports']['wss'] >+ test_environment.config['ports']['http'] + test_environment.config['ports']['https'] + >+ test_environment.config['ports']['ws'] + test_environment.config['ports']['wss'] > )) > >- executor_kwargs = chrome_executor_kwargs(test_type, server_config, >- cache_manager, run_info_data, >+ executor_kwargs = chrome_executor_kwargs(logger, test_type, test_environment, run_info_data, > **kwargs) > del executor_kwargs["capabilities"]["goog:chromeOptions"]["prefs"] > capabilities = executor_kwargs["capabilities"] >@@ -74,61 +75,22 @@ def env_options(): > return {"server_host": "127.0.0.1"} > > >-#TODO: refactor common elements of WeblayerShell and ChromeAndroidBrowser >-class WeblayerShell(Browser): >+class WeblayerShell(ChromeAndroidBrowserBase): > """Chrome is backed by chromedriver, which is supplied through > ``wptrunner.webdriver.ChromeDriverServer``. > """ > >- def __init__(self, logger, binary, webdriver_binary="chromedriver", >+ def __init__(self, logger, binary, >+ webdriver_binary="chromedriver", >+ remote_queue=None, > device_serial=None, >- webdriver_args=None): >+ webdriver_args=None, >+ stackwalk_binary=None, >+ symbols_path=None): > """Creates a new representation of Chrome. The `binary` argument gives > the browser binary to use for testing.""" >- Browser.__init__(self, logger) >+ super(WeblayerShell, self).__init__(logger, >+ webdriver_binary, remote_queue, device_serial, >+ webdriver_args, stackwalk_binary, symbols_path) > self.binary = binary >- self.device_serial = device_serial >- self.server = ChromeDriverServer(self.logger, >- binary=webdriver_binary, >- args=webdriver_args) >- self.setup_adb_reverse() >- >- def _adb_run(self, args): >- cmd = ['adb'] >- if self.device_serial: >- cmd.extend(['-s', self.device_serial]) >- cmd.extend(args) >- self.logger.info(' '.join(cmd)) >- subprocess.check_call(cmd) >- >- def setup_adb_reverse(self): >- self._adb_run(['wait-for-device']) >- self._adb_run(['forward', '--remove-all']) >- self._adb_run(['reverse', '--remove-all']) >- # "adb reverse" basically forwards network connection from device to >- # host. >- for port in _wptserve_ports: >- self._adb_run(['reverse', 'tcp:%d' % port, 'tcp:%d' % port]) >- >- def start(self, **kwargs): >- self.server.start(block=False) >- >- def stop(self, force=False): >- self.server.stop(force=force) >- >- def pid(self): >- return self.server.pid >- >- def is_alive(self): >- # TODO(ato): This only indicates the driver is alive, >- # and doesn't say anything about whether a browser session >- # is active. >- return self.server.is_alive() >- >- def cleanup(self): >- self.stop() >- self._adb_run(['forward', '--remove-all']) >- self._adb_run(['reverse', '--remove-all']) >- >- def executor_browser(self): >- return ExecutorBrowser, {"webdriver_url": self.server.url} >+ self.wptserver_ports = _wptserve_ports >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/base.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/base.py >index 99f6e5cff4c8..282e2d7ad5ee 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/base.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/base.py >@@ -1,34 +1,14 @@ >+import enum > import os > import platform > import socket > from abc import ABCMeta, abstractmethod >-from copy import deepcopy >-from six import iteritems > > from ..wptcommandline import require_arg # noqa: F401 > > here = os.path.dirname(__file__) > > >-def inherit(super_module, child_globals, product_name): >- super_wptrunner = super_module.__wptrunner__ >- child_globals["__wptrunner__"] = child_wptrunner = deepcopy(super_wptrunner) >- >- child_wptrunner["product"] = product_name >- >- for k in ("check_args", "browser", "browser_kwargs", "executor_kwargs", >- "env_extras", "env_options", "timeout_multiplier"): >- attr = super_wptrunner[k] >- child_globals[attr] = getattr(super_module, attr) >- >- for v in super_module.__wptrunner__["executor"].values(): >- child_globals[v] = getattr(super_module, v) >- >- if "run_info_extras" in super_wptrunner: >- attr = super_wptrunner["run_info_extras"] >- child_globals[attr] = getattr(super_module, attr) >- >- > def cmd_arg(name, value=None): > prefix = "-" if platform.system() == "Windows" else "--" > rv = prefix + name >@@ -65,7 +45,7 @@ def get_free_port(): > s = socket.socket() > try: > s.bind(("127.0.0.1", 0)) >- except socket.error: >+ except OSError: > continue > else: > return s.getsockname()[1] >@@ -126,6 +106,12 @@ class Browser(object): > pass > > def settings(self, test): >+ """Dictionary of metadata that is constant for a specific launch of a browser. >+ >+ This is used to determine when the browser instance configuration changes, requiring >+ a relaunch of the browser. The test runner calls this method for each test, and if the >+ returned value differs from that for the previous test, the browser is relaunched. >+ """ > return {} > > @abstractmethod >@@ -157,6 +143,10 @@ class Browser(object): > with which it should be instantiated""" > return ExecutorBrowser, {} > >+ def maybe_parse_tombstone(self): >+ """Possibly parse tombstones on Android device for Android target""" >+ pass >+ > def check_crash(self, process, test): > """Check if a crash occured and output any useful information to the > log. Returns a boolean indicating whether a crash occured.""" >@@ -182,11 +172,8 @@ class NullBrowser(Browser): > def is_alive(self): > return True > >- def on_output(self, line): >- raise NotImplementedError > >- >-class ExecutorBrowser(object): >+class ExecutorBrowser: > """View of the Browser used by the Executor object. > This is needed because the Executor runs in a child process and > we can't ship Browser instances between processes on Windows. >@@ -196,5 +183,95 @@ class ExecutorBrowser(object): > up the browser from the runner process. > """ > def __init__(self, **kwargs): >- for k, v in iteritems(kwargs): >+ for k, v in kwargs.items(): > setattr(self, k, v) >+ >+ >+@enum.unique >+class OutputHandlerState(enum.IntEnum): >+ BEFORE_PROCESS_START = 1 >+ AFTER_PROCESS_START = 2 >+ AFTER_HANDLER_START = 3 >+ AFTER_PROCESS_STOP = 4 >+ >+ >+class OutputHandler: >+ """Class for handling output from a browser process. >+ >+ This class is responsible for consuming the logging from a browser process >+ and passing it into the relevant logger. A class instance is designed to >+ be passed as the processOutputLine argument to mozprocess.ProcessHandler. >+ >+ The setup of this class is complex for various reasons: >+ >+ * We need to create an instance of the class before starting the process >+ * We want access to data about the running process e.g. the pid >+ * We want to launch the process and later setup additional log handling >+ which is restrospectively applied to any existing output (this supports >+ prelaunching browsers for performance, but having log output depend on the >+ tests that are run e.g. for leak suppression). >+ >+ Therefore the lifecycle is as follows:: >+ >+ output_handler = OutputHandler(logger, command, **output_handler_kwargs) >+ proc = ProcessHandler(command, ..., processOutputLine=output_handler) >+ output_handler.after_process_start(proc.pid) >+ [...] >+ # All logging to this point was buffered in-memory, but after start() >+ # it's actually sent to the logger. >+ output_handler.start(**output_logger_start_kwargs) >+ [...] >+ proc.wait() >+ output_handler.after_process_stop() >+ >+ Since the process lifetime and the output handler lifetime are coupled (it doesn't >+ work to reuse an output handler for multiple processes), it might make sense to have >+ a single class that owns the process and the output processing for the process. >+ This is complicated by the fact that we don't always run the process directly, >+ but sometimes use a wrapper e.g. mozrunner. >+ """ >+ >+ def __init__(self, logger, command, **kwargs): >+ self.logger = logger >+ self.command = command >+ self.pid = None >+ self.state = OutputHandlerState.BEFORE_PROCESS_START >+ self.line_buffer = [] >+ >+ def after_process_start(self, pid): >+ assert self.state == OutputHandlerState.BEFORE_PROCESS_START >+ self.logger.debug("OutputHandler.after_process_start") >+ self.pid = pid >+ self.state = OutputHandlerState.AFTER_PROCESS_START >+ >+ def start(self, **kwargs): >+ assert self.state == OutputHandlerState.AFTER_PROCESS_START >+ self.logger.debug("OutputHandler.start") >+ # Need to change the state here before we try to empty the buffer >+ # or we'll just re-buffer the existing output. >+ self.state = OutputHandlerState.AFTER_HANDLER_START >+ for item in self.line_buffer: >+ self(item) >+ self.line_buffer = None >+ >+ def after_process_stop(self, clean_shutdown=True): >+ # If we didn't get as far as configure, just >+ # dump all logs with no configuration >+ self.logger.debug("OutputHandler.after_process_stop") >+ if self.state < OutputHandlerState.AFTER_HANDLER_START: >+ self.start() >+ self.state = OutputHandlerState.AFTER_PROCESS_STOP >+ >+ def __call__(self, line): >+ if self.state < OutputHandlerState.AFTER_HANDLER_START: >+ self.line_buffer.append(line) >+ return >+ >+ # Could assert that there's no output handled once we're in the >+ # after_process_stop phase, although technically there's a race condition >+ # here because we don't know the logging thread has finished draining the >+ # logs. The solution might be to move this into mozprocess itself. >+ >+ self.logger.process_output(self.pid, >+ line.decode("utf8", "replace"), >+ command=" ".join(self.command) if self.command else "") >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/chrome.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/chrome.py >index fd251a431c30..0b6a0c618dfc 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/chrome.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/chrome.py >@@ -1,5 +1,6 @@ > from . import chrome_spki_certs > from .base import Browser, ExecutorBrowser, require_arg >+from .base import NullBrowser # noqa: F401 > from .base import get_timeout_multiplier # noqa: F401 > from ..webdriver_server import ChromeDriverServer > from ..executors import executor_kwargs as base_executor_kwargs >@@ -12,7 +13,8 @@ from ..executors.executorchrome import (ChromeDriverWdspecExecutor, # noqa: F40 > > __wptrunner__ = {"product": "chrome", > "check_args": "check_args", >- "browser": "ChromeBrowser", >+ "browser": {None: "ChromeBrowser", >+ "wdspec": "NullBrowser"}, > "executor": {"testharness": "WebDriverTestharnessExecutor", > "reftest": "WebDriverRefTestExecutor", > "print-reftest": "ChromeDriverPrintRefTestExecutor", >@@ -28,16 +30,15 @@ def check_args(**kwargs): > require_arg(kwargs, "webdriver_binary") > > >-def browser_kwargs(test_type, run_info_data, config, **kwargs): >+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs): > return {"binary": kwargs["binary"], > "webdriver_binary": kwargs["webdriver_binary"], > "webdriver_args": kwargs.get("webdriver_args")} > > >-def executor_kwargs(test_type, server_config, cache_manager, run_info_data, >+def executor_kwargs(logger, test_type, test_environment, run_info_data, > **kwargs): >- executor_kwargs = base_executor_kwargs(test_type, server_config, >- cache_manager, run_info_data, >+ executor_kwargs = base_executor_kwargs(test_type, test_environment, run_info_data, > **kwargs) > executor_kwargs["close_after_done"] = True > executor_kwargs["supports_eager_pageload"] = False >@@ -81,9 +82,31 @@ def executor_kwargs(test_type, server_config, cache_manager, run_info_data, > # Point all .test domains to localhost for Chrome > chrome_options["args"].append("--host-resolver-rules=MAP nonexistent.*.test ~NOTFOUND, MAP *.test 127.0.0.1") > >+ # Classify `http-private`, `http-public` and https variants in the >+ # appropriate IP address spaces. >+ # For more details, see: https://github.com/web-platform-tests/rfcs/blob/master/rfcs/address_space_overrides.md >+ address_space_overrides_ports = [ >+ ("http-private", "private"), >+ ("http-public", "public"), >+ ("https-private", "private"), >+ ("https-public", "public"), >+ ] >+ address_space_overrides_arg = ",".join( >+ "127.0.0.1:{}={}".format(port_number, address_space) >+ for port_name, address_space in address_space_overrides_ports >+ for port_number in test_environment.config.ports.get(port_name, []) >+ ) >+ if address_space_overrides_arg: >+ chrome_options["args"].append( >+ "--ip-address-space-overrides=" + address_space_overrides_arg) >+ > if kwargs["enable_mojojs"]: > chrome_options["args"].append("--enable-blink-features=MojoJS,MojoJSTest") > >+ if kwargs["enable_swiftshader"]: >+ # https://chromium.googlesource.com/chromium/src/+/HEAD/docs/gpu/swiftshader.md >+ chrome_options["args"].extend(["--use-gl=angle", "--use-angle=swiftshader"]) >+ > # Copy over any other flags that were passed in via --binary_args > if kwargs["binary_args"] is not None: > chrome_options["args"].extend(kwargs["binary_args"]) >@@ -113,7 +136,7 @@ class ChromeBrowser(Browser): > """ > > def __init__(self, logger, binary, webdriver_binary="chromedriver", >- webdriver_args=None): >+ webdriver_args=None, **kwargs): > """Creates a new representation of Chrome. The `binary` argument gives > the browser binary to use for testing.""" > Browser.__init__(self, logger) >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/chrome_android.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/chrome_android.py >index 485b96f31e7d..3ba01588b294 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/chrome_android.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/chrome_android.py >@@ -1,7 +1,9 @@ >+import mozprocess > import subprocess > > from .base import Browser, ExecutorBrowser, require_arg > from .base import get_timeout_multiplier # noqa: F401 >+from .base import NullBrowser # noqa: F401 > from .chrome import executor_kwargs as chrome_executor_kwargs > from ..webdriver_server import ChromeDriverServer > from ..executors.executorwebdriver import (WebDriverTestharnessExecutor, # noqa: F401 >@@ -11,7 +13,8 @@ from ..executors.executorchrome import ChromeDriverWdspecExecutor # noqa: F401 > > __wptrunner__ = {"product": "chrome_android", > "check_args": "check_args", >- "browser": "ChromeAndroidBrowser", >+ "browser": {None: "ChromeAndroidBrowser", >+ "wdspec": "NullBrowser"}, > "executor": {"testharness": "WebDriverTestharnessExecutor", > "reftest": "WebDriverRefTestExecutor", > "wdspec": "ChromeDriverWdspecExecutor"}, >@@ -29,23 +32,24 @@ def check_args(**kwargs): > require_arg(kwargs, "webdriver_binary") > > >-def browser_kwargs(test_type, run_info_data, config, **kwargs): >+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs): > return {"package_name": kwargs["package_name"], > "device_serial": kwargs["device_serial"], > "webdriver_binary": kwargs["webdriver_binary"], >- "webdriver_args": kwargs.get("webdriver_args")} >+ "webdriver_args": kwargs.get("webdriver_args"), >+ "stackwalk_binary": kwargs.get("stackwalk_binary"), >+ "symbols_path": kwargs.get("symbols_path")} > > >-def executor_kwargs(test_type, server_config, cache_manager, run_info_data, >+def executor_kwargs(logger, test_type, test_environment, run_info_data, > **kwargs): > # Use update() to modify the global list in place. > _wptserve_ports.update(set( >- server_config['ports']['http'] + server_config['ports']['https'] + >- server_config['ports']['ws'] + server_config['ports']['wss'] >+ test_environment.config['ports']['http'] + test_environment.config['ports']['https'] + >+ test_environment.config['ports']['ws'] + test_environment.config['ports']['wss'] > )) > >- executor_kwargs = chrome_executor_kwargs(test_type, server_config, >- cache_manager, run_info_data, >+ executor_kwargs = chrome_executor_kwargs(logger, test_type, test_environment, run_info_data, > **kwargs) > # Remove unsupported options on mobile. > del executor_kwargs["capabilities"]["goog:chromeOptions"]["prefs"] >@@ -69,20 +73,82 @@ def env_options(): > return {"server_host": "127.0.0.1"} > > >-class ChromeAndroidBrowser(Browser): >- """Chrome is backed by chromedriver, which is supplied through >- ``wptrunner.webdriver.ChromeDriverServer``. >- """ >+class LogcatRunner(object): >+ def __init__(self, logger, browser, remote_queue): >+ self.logger = logger >+ self.browser = browser >+ self.remote_queue = remote_queue >+ >+ def start(self): >+ try: >+ self._run() >+ except KeyboardInterrupt: >+ self.stop() >+ >+ def _run(self): >+ try: >+ # TODO: adb logcat -c fail randomly with message >+ # "failed to clear the 'main' log" >+ self.browser.clear_log() >+ except subprocess.CalledProcessError: >+ self.logger.error("Failed to clear logcat buffer") >+ >+ self._cmd = self.browser.logcat_cmd() >+ self._proc = mozprocess.ProcessHandler( >+ self._cmd, >+ processOutputLine=self.on_output, >+ storeOutput=False) >+ self._proc.run() >+ >+ def _send_message(self, command, *args): >+ try: >+ self.remote_queue.put((command, args)) >+ except AssertionError: >+ self.logger.warning("Error when send to remote queue") > >- def __init__(self, logger, package_name, webdriver_binary="chromedriver", >- device_serial=None, webdriver_args=None): >- Browser.__init__(self, logger) >- self.package_name = package_name >+ def stop(self, force=False): >+ if self.is_alive(): >+ kill_result = self._proc.kill() >+ if force and kill_result != 0: >+ self._proc.kill(9) >+ >+ def is_alive(self): >+ return hasattr(self._proc, "proc") and self._proc.poll() is None >+ >+ def on_output(self, line): >+ data = { >+ "action": "process_output", >+ "process": "LOGCAT", >+ "command": "logcat", >+ "data": line >+ } >+ self._send_message("log", data) >+ >+ >+class ChromeAndroidBrowserBase(Browser): >+ def __init__(self, logger, >+ webdriver_binary="chromedriver", >+ remote_queue = None, >+ device_serial=None, >+ webdriver_args=None, >+ stackwalk_binary=None, >+ symbols_path=None): >+ super(ChromeAndroidBrowserBase, self).__init__(logger) > self.device_serial = device_serial >+ self.stackwalk_binary = stackwalk_binary >+ self.symbols_path = symbols_path >+ self.remote_queue = remote_queue > self.server = ChromeDriverServer(self.logger, > binary=webdriver_binary, > args=webdriver_args) >+ if self.remote_queue is not None: >+ self.logcat_runner = LogcatRunner(self.logger, >+ self, self.remote_queue) >+ >+ def setup(self): > self.setup_adb_reverse() >+ if self.remote_queue is not None: >+ self.logcat_runner.start() > > def _adb_run(self, args): > cmd = ['adb'] >@@ -92,14 +158,6 @@ class ChromeAndroidBrowser(Browser): > self.logger.info(' '.join(cmd)) > subprocess.check_call(cmd) > >- def setup_adb_reverse(self): >- self._adb_run(['wait-for-device']) >- self._adb_run(['forward', '--remove-all']) >- self._adb_run(['reverse', '--remove-all']) >- # "adb reverse" forwards network connection from device to host. >- for port in _wptserve_ports: >- self._adb_run(['reverse', 'tcp:%d' % port, 'tcp:%d' % port]) >- > def start(self, **kwargs): > self.server.start(block=False) > >@@ -119,6 +177,60 @@ class ChromeAndroidBrowser(Browser): > self.stop() > self._adb_run(['forward', '--remove-all']) > self._adb_run(['reverse', '--remove-all']) >+ if self.remote_queue is not None: >+ self.logcat_runner.stop(force=True) > > def executor_browser(self): > return ExecutorBrowser, {"webdriver_url": self.server.url} >+ >+ def clear_log(self): >+ self._adb_run(['logcat', '-c']) >+ >+ def logcat_cmd(self): >+ cmd = ['adb'] >+ if self.device_serial: >+ cmd.extend(['-s', self.device_serial]) >+ cmd.extend(['logcat', '*:D']) >+ return cmd >+ >+ def check_crash(self, process, test): >+ self.maybe_parse_tombstone() >+ # Existence of a tombstone does not necessarily mean test target has >+ # crashed. Always return False so we don't change the test results. >+ return False >+ >+ def maybe_parse_tombstone(self): >+ if self.stackwalk_binary: >+ cmd = [self.stackwalk_binary, "-a", "-w"] >+ if self.device_serial: >+ cmd.extend(["--device", self.device_serial]) >+ cmd.extend(["--output-directory", self.symbols_path]) >+ raw_output = subprocess.check_output(cmd) >+ for line in raw_output.splitlines(): >+ self.logger.process_output("TRACE", line, "logcat") >+ >+ def setup_adb_reverse(self): >+ self._adb_run(['wait-for-device']) >+ self._adb_run(['forward', '--remove-all']) >+ self._adb_run(['reverse', '--remove-all']) >+ # "adb reverse" forwards network connection from device to host. >+ for port in self.wptserver_ports: >+ self._adb_run(['reverse', 'tcp:%d' % port, 'tcp:%d' % port]) >+ >+class ChromeAndroidBrowser(ChromeAndroidBrowserBase): >+ """Chrome is backed by chromedriver, which is supplied through >+ ``wptrunner.webdriver.ChromeDriverServer``. >+ """ >+ >+ def __init__(self, logger, package_name, >+ webdriver_binary="chromedriver", >+ remote_queue = None, >+ device_serial=None, >+ webdriver_args=None, >+ stackwalk_binary=None, >+ symbols_path=None): >+ super(ChromeAndroidBrowser, self).__init__(logger, >+ webdriver_binary, remote_queue, device_serial, >+ webdriver_args, stackwalk_binary, symbols_path) >+ self.package_name = package_name >+ self.wptserver_ports = _wptserve_ports >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/edge.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/edge.py >index bdb37b3f84c2..3101e9c7bea3 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/edge.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/edge.py >@@ -1,7 +1,7 @@ >-from __future__ import print_function > import time > import subprocess > from .base import Browser, ExecutorBrowser, require_arg >+from .base import NullBrowser # noqa: F401 > from ..webdriver_server import EdgeDriverServer > from ..executors import executor_kwargs as base_executor_kwargs > from ..executors.executorselenium import (SeleniumTestharnessExecutor, # noqa: F401 >@@ -10,7 +10,8 @@ from ..executors.executoredge import EdgeDriverWdspecExecutor # noqa: F401 > > __wptrunner__ = {"product": "edge", > "check_args": "check_args", >- "browser": "EdgeBrowser", >+ "browser": {None: "EdgeBrowser", >+ "wdspec": "NullBrowser"}, > "executor": {"testharness": "SeleniumTestharnessExecutor", > "reftest": "SeleniumRefTestExecutor", > "wdspec": "EdgeDriverWdspecExecutor"}, >@@ -34,7 +35,7 @@ def check_args(**kwargs): > require_arg(kwargs, "webdriver_binary") > > >-def browser_kwargs(test_type, run_info_data, config, **kwargs): >+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs): > return {"webdriver_binary": kwargs["webdriver_binary"], > "webdriver_args": kwargs.get("webdriver_args"), > "timeout_multiplier": get_timeout_multiplier(test_type, >@@ -42,10 +43,9 @@ def browser_kwargs(test_type, run_info_data, config, **kwargs): > **kwargs)} > > >-def executor_kwargs(test_type, server_config, cache_manager, run_info_data, >+def executor_kwargs(logger, test_type, test_environment, run_info_data, > **kwargs): >- executor_kwargs = base_executor_kwargs(test_type, server_config, >- cache_manager, run_info_data, **kwargs) >+ executor_kwargs = base_executor_kwargs(test_type, test_environment, run_info_data, **kwargs) > executor_kwargs["close_after_done"] = True > executor_kwargs["timeout_multiplier"] = get_timeout_multiplier(test_type, > run_info_data, >@@ -65,10 +65,10 @@ def env_options(): > > > class EdgeBrowser(Browser): >- used_ports = set() > init_timeout = 60 > >- def __init__(self, logger, webdriver_binary, timeout_multiplier=None, webdriver_args=None): >+ def __init__(self, logger, webdriver_binary, >+ timeout_multiplier=None, webdriver_args=None, **kwargs): > Browser.__init__(self, logger) > self.server = EdgeDriverServer(self.logger, > binary=webdriver_binary, >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/edge_webdriver.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/edge_webdriver.py >index c2545de46f0b..e5949fe3e57f 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/edge_webdriver.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/edge_webdriver.py >@@ -1,12 +1,28 @@ >-from .base import inherit >-from . import edge >+from .base import NullBrowser # noqa: F401 >+from .edge import (EdgeBrowser, # noqa: F401 >+ EdgeDriverWdspecExecutor, # noqa: F401 >+ check_args, # noqa: F401 >+ browser_kwargs, # noqa: F401 >+ executor_kwargs, # noqa: F401 >+ env_extras, # noqa: F401 >+ env_options, # noqa: F401 >+ run_info_extras, # noqa: F401 >+ get_timeout_multiplier) # noqa: F401 > > from ..executors.executorwebdriver import (WebDriverTestharnessExecutor, # noqa: F401 > WebDriverRefTestExecutor) # noqa: F401 > > >-inherit(edge, globals(), "edge_webdriver") >- >-# __wptrunner__ magically appears from inherit, F821 is undefined name >-__wptrunner__["executor"]["testharness"] = "WebDriverTestharnessExecutor" # noqa: F821 >-__wptrunner__["executor"]["reftest"] = "WebDriverRefTestExecutor" # noqa: F821 >+__wptrunner__ = {"product": "edge_webdriver", >+ "check_args": "check_args", >+ "browser": {None: "EdgeBrowser", >+ "wdspec": "NullBrowser"}, >+ "executor": {"testharness": "WebDriverTestharnessExecutor", >+ "reftest": "WebDriverRefTestExecutor", >+ "wdspec": "EdgeDriverWdspecExecutor"}, >+ "browser_kwargs": "browser_kwargs", >+ "executor_kwargs": "executor_kwargs", >+ "env_extras": "env_extras", >+ "env_options": "env_options", >+ "run_info_extras": "run_info_extras", >+ "timeout_multiplier": "get_timeout_multiplier"} >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/firefox.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/firefox.py >index 151530897d9b..ac27453127ab 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/firefox.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/firefox.py >@@ -1,9 +1,12 @@ >+import base64 >+import io > import json > import os > import platform > import signal > import subprocess >-import sys >+import tempfile >+import zipfile > from abc import ABCMeta, abstractmethod > > import mozinfo >@@ -15,26 +18,30 @@ from mozrunner import FirefoxRunner > from mozrunner.utils import test_environment, get_stack_fixer_function > from mozcrash import mozcrash > >-from .base import (get_free_port, >- Browser, >+from .base import (Browser, > ExecutorBrowser, >- require_arg, >+ NullBrowser, >+ OutputHandler, >+ OutputHandlerState, >+ browser_command, > cmd_arg, >- browser_command) >+ get_free_port, >+ require_arg) > from ..executors import executor_kwargs as base_executor_kwargs > from ..executors.executormarionette import (MarionetteTestharnessExecutor, # noqa: F401 > MarionetteRefTestExecutor, # noqa: F401 > MarionettePrintRefTestExecutor, # noqa: F401 > MarionetteWdspecExecutor, # noqa: F401 > MarionetteCrashtestExecutor) # noqa: F401 >-from ..process import cast_env >+from ..webdriver_server import WebDriverServer > > > here = os.path.dirname(__file__) > > __wptrunner__ = {"product": "firefox", > "check_args": "check_args", >- "browser": "FirefoxBrowser", >+ "browser": {None: "FirefoxBrowser", >+ "wdspec": "FirefoxWdSpecBrowser"}, > "executor": {"crashtest": "MarionetteCrashtestExecutor", > "testharness": "MarionetteTestharnessExecutor", > "reftest": "MarionetteRefTestExecutor", >@@ -53,11 +60,11 @@ def get_timeout_multiplier(test_type, run_info_data, **kwargs): > if kwargs["timeout_multiplier"] is not None: > return kwargs["timeout_multiplier"] > if test_type == "reftest": >- if run_info_data["debug"] or run_info_data.get("asan"): >+ if run_info_data["debug"] or run_info_data.get("asan") or run_info_data.get("tsan"): > return 4 > else: > return 2 >- elif run_info_data["debug"] or run_info_data.get("asan"): >+ elif run_info_data["debug"] or run_info_data.get("asan") or run_info_data.get("tsan"): > if run_info_data.get("ccov"): > return 4 > else: >@@ -76,7 +83,7 @@ def check_args(**kwargs): > require_arg(kwargs, "binary") > > >-def browser_kwargs(test_type, run_info_data, config, **kwargs): >+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs): > return {"binary": kwargs["binary"], > "prefs_root": kwargs["prefs_root"], > "extra_prefs": kwargs["extra_prefs"], >@@ -101,13 +108,24 @@ def browser_kwargs(test_type, run_info_data, config, **kwargs): > "config": config, > "browser_channel": kwargs["browser_channel"], > "headless": kwargs["headless"], >- "preload_browser": kwargs["preload_browser"]} >+ "preload_browser": kwargs["preload_browser"] and not kwargs["pause_after_test"] and not kwargs["num_test_groups"] == 1, >+ "specialpowers_path": kwargs["specialpowers_path"]} > > >-def executor_kwargs(test_type, server_config, cache_manager, run_info_data, >+class WdSpecProfile(object): >+ def __init__(self, profile): >+ self.profile = profile >+ >+ def __enter__(self): >+ return self >+ >+ def __exit__(self, *args, **kwargs): >+ self.profile.cleanup() >+ >+ >+def executor_kwargs(logger, test_type, test_environment, run_info_data, > **kwargs): >- executor_kwargs = base_executor_kwargs(test_type, server_config, >- cache_manager, run_info_data, >+ executor_kwargs = base_executor_kwargs(test_type, test_environment, run_info_data, > **kwargs) > executor_kwargs["close_after_done"] = test_type != "reftest" > executor_kwargs["timeout_multiplier"] = get_timeout_multiplier(test_type, >@@ -121,22 +139,63 @@ def executor_kwargs(test_type, server_config, cache_manager, run_info_data, > executor_kwargs["reftest_internal"] = kwargs["reftest_internal"] > executor_kwargs["reftest_screenshot"] = kwargs["reftest_screenshot"] > if test_type == "wdspec": >- options = {} >+ options = {"args": []} > if kwargs["binary"]: > options["binary"] = kwargs["binary"] > if kwargs["binary_args"]: > options["args"] = kwargs["binary_args"] >- if kwargs["headless"]: >- if "args" not in options: >- options["args"] = [] >- if "--headless" not in options["args"]: >- options["args"].append("--headless") >- options["prefs"] = { >- "network.dns.localDomains": ",".join(server_config.domains_set) >- } >- for pref, value in kwargs["extra_prefs"]: >- options["prefs"].update({pref: Preferences.cast(value)}) >+ >+ profile_creator = ProfileCreator(logger, >+ kwargs["prefs_root"], >+ test_environment.config, >+ test_type, >+ kwargs["extra_prefs"], >+ kwargs["gecko_e10s"], >+ kwargs["enable_fission"], >+ kwargs["browser_channel"], >+ kwargs["binary"], >+ kwargs["certutil_binary"], >+ test_environment.config.ssl_config["ca_cert_path"]) >+ if kwargs["processes"] > 1: >+ # With multiple processes, we would need a profile directory per process, but we >+ # don't have an easy way to do that, so include the profile in the capabilties >+ # directly instead. This means recreating it per session, which is slow >+ options["profile"] = profile_creator.create_base64() >+ profile = None >+ else: >+ profile = profile_creator.create() >+ options["args"].extend(["--profile", profile.profile]) >+ test_environment.env_extras_cms.append(WdSpecProfile(profile)) >+ > capabilities["moz:firefoxOptions"] = options >+ >+ # This gets reused for firefox_android, but the environment setup >+ # isn't required in that case >+ if kwargs["binary"]: >+ environ = get_environ(logger, >+ kwargs["binary"], >+ kwargs["debug_info"], >+ kwargs["stylo_threads"], >+ kwargs["headless"], >+ kwargs["enable_webrender"], >+ kwargs["chaos_mode_flags"]) >+ leak_report_file = setup_leak_report(kwargs["leak_check"], profile, environ) >+ >+ # This doesn't work with wdspec tests >+ # In particular tests can create a session without passing in the capabilites >+ # and in those cases we get the default geckodriver profile which doesn't >+ # guarantee zero network access >+ del environ["MOZ_DISABLE_NONLOCAL_CONNECTIONS"] >+ executor_kwargs["environ"] = environ >+ else: >+ if kwargs["headless"] and "--headless" not in options["args"]: >+ options["args"].append("--headless") >+ leak_report_file = None >+ >+ executor_kwargs["stackfix_dir"] = kwargs["stackfix_dir"], >+ executor_kwargs["leak_report_file"] = leak_report_file >+ executor_kwargs["asan"] = run_info_data.get("asan") >+ > if kwargs["certutil_binary"] is None: > capabilities["acceptInsecureCerts"] = True > if capabilities: >@@ -144,6 +203,7 @@ def executor_kwargs(test_type, server_config, cache_manager, run_info_data, > executor_kwargs["debug"] = run_info_data["debug"] > executor_kwargs["ccov"] = run_info_data.get("ccov", False) > executor_kwargs["browser_version"] = run_info_data.get("browser_version") >+ executor_kwargs["debug_test"] = kwargs["debug_test"] > return executor_kwargs > > >@@ -177,16 +237,11 @@ def run_info_extras(**kwargs): > "wasm": kwargs.get("wasm", True), > "verify": kwargs["verify"], > "headless": kwargs.get("headless", False) or "MOZ_HEADLESS" in os.environ, >- "sw-e10s": True, >- "fission": kwargs.get("enable_fission") or get_bool_pref("fission.autostart")} >- >- # The value of `sw-e10s` defaults to whether the "parent_intercept" >- # implementation is enabled for the current build. This value, however, >- # can be overridden by explicitly setting the pref with the `--setpref` CLI >- # flag, which is checked here. >- sw_e10s_override = get_bool_pref_if_exists("dom.serviceWorkers.parent_intercept") >- if sw_e10s_override is not None: >- rv["sw-e10s"] = sw_e10s_override >+ "fission": kwargs.get("enable_fission") or get_bool_pref("fission.autostart"), >+ "sessionHistoryInParent": (kwargs.get("enable_fission") or >+ get_bool_pref("fission.autostart") or >+ get_bool_pref("fission.sessionHistoryInParent")), >+ "swgl": get_bool_pref("gfx.webrender.software")} > > rv.update(run_info_browser_version(**kwargs)) > >@@ -208,11 +263,46 @@ def run_info_browser_version(**kwargs): > > > def update_properties(): >- return (["os", "debug", "webrender", "fission", "e10s", "sw-e10s", "processor"], >+ return (["os", "debug", "webrender", "fission", "e10s", "processor", "swgl"], > {"os": ["version"], "processor": ["bits"]}) > > >-class FirefoxInstanceManager(object): >+def get_environ(logger, binary, debug_info, stylo_threads, headless, enable_webrender, >+ chaos_mode_flags=None): >+ env = test_environment(xrePath=os.path.abspath(os.path.dirname(binary)), >+ debugger=debug_info is not None, >+ useLSan=True, >+ log=logger) >+ >+ env["STYLO_THREADS"] = str(stylo_threads) >+ if chaos_mode_flags is not None: >+ env["MOZ_CHAOSMODE"] = str(chaos_mode_flags) >+ if headless: >+ env["MOZ_HEADLESS"] = "1" >+ if enable_webrender: >+ env["MOZ_WEBRENDER"] = "1" >+ env["MOZ_ACCELERATED"] = "1" >+ else: >+ env["MOZ_WEBRENDER"] = "0" >+ return env >+ >+ >+def setup_leak_report(leak_check, profile, env): >+ leak_report_file = None >+ if leak_check: >+ filename = "runtests_leaks_%s.log" % os.getpid() >+ if profile is not None: >+ leak_report_file = os.path.join(profile.profile, filename) >+ else: >+ leak_report_file = os.path.join(tempfile.gettempdir(), filename) >+ if os.path.exists(leak_report_file): >+ os.remove(leak_report_file) >+ env["XPCOM_MEM_BLOAT_LOG"] = leak_report_file >+ >+ return leak_report_file >+ >+ >+class FirefoxInstanceManager: > __metaclass__ = ABCMeta > > def __init__(self, logger, binary, binary_args, profile_creator, debug_info, >@@ -267,20 +357,8 @@ class FirefoxInstanceManager(object): > marionette_port = get_free_port() > profile.set_preferences({"marionette.port": marionette_port}) > >- env = test_environment(xrePath=os.path.dirname(self.binary), >- debugger=self.debug_info is not None, >- useLSan=True, log=self.logger) >- >- env["STYLO_THREADS"] = str(self.stylo_threads) >- if self.chaos_mode_flags is not None: >- env["MOZ_CHAOSMODE"] = str(self.chaos_mode_flags) >- if self.headless: >- env["MOZ_HEADLESS"] = "1" >- if self.enable_webrender: >- env["MOZ_WEBRENDER"] = "1" >- env["MOZ_ACCELERATED"] = "1" >- else: >- env["MOZ_WEBRENDER"] = "0" >+ env = get_environ(self.logger, self.binary, self.debug_info, self.stylo_threads, >+ self.headless, self.enable_webrender, self.chaos_mode_flags) > > args = self.binary_args[:] if self.binary_args else [] > args += [cmd_arg("marionette"), "about:blank"] >@@ -289,26 +367,26 @@ class FirefoxInstanceManager(object): > args, > self.debug_info) > >- if self.leak_check: >- leak_report_file = os.path.join(profile.profile, "runtests_leaks_%s.log" % os.getpid()) >- if os.path.exists(leak_report_file): >- os.remove(leak_report_file) >- env["XPCOM_MEM_BLOAT_LOG"] = leak_report_file >- else: >- leak_report_file = None >- >- output_handler = OutputHandler(self.logger, self.stackfix_dir, self.symbols_path, self.asan) >+ leak_report_file = setup_leak_report(self.leak_check, profile, env) >+ output_handler = FirefoxOutputHandler(self.logger, >+ cmd, >+ stackfix_dir=self.stackfix_dir, >+ symbols_path=self.symbols_path, >+ asan=self.asan, >+ leak_report_file=leak_report_file) > runner = FirefoxRunner(profile=profile, > binary=cmd[0], > cmdargs=cmd[1:], >- env=cast_env(env), >+ env=env, > process_class=ProcessHandler, > process_args={"processOutputLine": [output_handler]}) > instance = BrowserInstance(self.logger, runner, marionette_port, > output_handler, leak_report_file) > > self.logger.debug("Starting Firefox") >- runner.start(debug_args=debug_args, interactive=self.debug_info and self.debug_info.interactive) >+ runner.start(debug_args=debug_args, >+ interactive=self.debug_info and self.debug_info.interactive) >+ output_handler.after_process_start(runner.process_handler.pid) > self.logger.debug("Firefox Started") > > return instance >@@ -330,6 +408,7 @@ class SingleInstanceManager(FirefoxInstanceManager): > if instance: > instance.stop(force) > instance.cleanup() >+ self.base_profile.cleanup() > > > class PreloadInstanceManager(FirefoxInstanceManager): >@@ -352,15 +431,16 @@ class PreloadInstanceManager(FirefoxInstanceManager): > return self.current > > def teardown(self, force=False): >- for instance, skip_marionette in [(self.previous, False), >- (self.current, False), >- (self.pending, True)]: >+ for instance, unused in [(self.previous, False), >+ (self.current, False), >+ (self.pending, True)]: > if instance: >- instance.stop(force, skip_marionette=skip_marionette) >+ instance.stop(force, unused) > instance.cleanup() >+ self.base_profile.cleanup() > > >-class BrowserInstance(object): >+class BrowserInstance: > shutdown_timeout = 70 > > def __init__(self, logger, runner, marionette_port, output_handler, leak_report_file): >@@ -371,25 +451,35 @@ class BrowserInstance(object): > self.output_handler = output_handler > self.leak_report_file = leak_report_file > >- def stop(self, force=False, skip_marionette=False): >- """Stop Firefox""" >+ def stop(self, force=False, unused=False): >+ """Stop Firefox >+ >+ :param force: Signal the firefox process without waiting for a clean shutdown >+ :param unused: This instance was not used for running tests and so >+ doesn't have an active marionette session and doesn't require >+ output postprocessing. >+ """ > is_running = self.runner is not None and self.runner.is_running() > if is_running: > self.logger.debug("Stopping Firefox %s" % self.pid()) > shutdown_methods = [(True, lambda: self.runner.wait(self.shutdown_timeout)), >- (False, lambda: self.runner.stop(signal.SIGTERM)), >- (False, lambda: self.runner.stop(signal.SIGKILL))] >- if skip_marionette: >+ (False, lambda: self.runner.stop(signal.SIGTERM, >+ self.shutdown_timeout))] >+ if hasattr(signal, "SIGKILL"): >+ shutdown_methods.append((False, lambda: self.runner.stop(signal.SIGKILL, >+ self.shutdown_timeout))) >+ if unused or force: >+ # Don't wait for the instance to close itself > shutdown_methods = shutdown_methods[1:] > try: > # For Firefox we assume that stopping the runner prompts the > # browser to shut down. This allows the leak log to be written >- for clean, stop_f in shutdown_methods: >- if not force or not clean: >- retcode = stop_f() >- if retcode is not None: >- self.logger.info("Browser exited with return code %s" % retcode) >- break >+ for i, (clean, stop_f) in enumerate(shutdown_methods): >+ self.logger.debug("Shutting down attempt %i/%i" % (i + 1, len(shutdown_methods))) >+ retcode = stop_f() >+ if retcode is not None: >+ self.logger.info("Browser exited with return code %s" % retcode) >+ break > except OSError: > # This can happen on Windows if the process is already dead > pass >@@ -397,8 +487,8 @@ class BrowserInstance(object): > # The browser was already stopped, which we assume was a crash > # TODO: Should we check the exit code here? > clean = False >- if not skip_marionette: >- self.output_handler.after_stop(clean_shutdown=clean) >+ if not unused: >+ self.output_handler.after_process_stop(clean_shutdown=clean) > > def pid(self): > if self.runner.process_handler is None: >@@ -415,12 +505,13 @@ class BrowserInstance(object): > return False > > def cleanup(self): >- # mozprofile handles deleting the profile when the refcount reaches 0 >+ self.runner.cleanup() > self.runner = None > > >-class OutputHandler(object): >- def __init__(self, logger, stackfix_dir, symbols_path, asan): >+class FirefoxOutputHandler(OutputHandler): >+ def __init__(self, logger, command, symbols_path=None, stackfix_dir=None, asan=False, >+ leak_report_file=None): > """Filter for handling Firefox process output. > > This receives Firefox process output in the __call__ function, does >@@ -431,9 +522,7 @@ class OutputHandler(object): > of the output handlers. > """ > >- self.logger = logger >- # These are filled in after setup() is called >- self.instance = None >+ super().__init__(logger, command) > > self.symbols_path = symbols_path > if stackfix_dir: >@@ -445,21 +534,18 @@ class OutputHandler(object): > else: > self.stack_fixer = None > self.asan = asan >+ self.leak_report_file = leak_report_file > >+ # These are filled in after configure_handlers() is called > self.lsan_handler = None > self.mozleak_allowed = None > self.mozleak_thresholds = None > self.group_metadata = {} > >- self.line_buffer = [] >- self.setup_ran = False >- >- def setup(self, instance=None, group_metadata=None, lsan_disabled=False, >- lsan_allowed=None, lsan_max_stack_depth=None, mozleak_allowed=None, >- mozleak_thresholds=None, **kwargs): >+ def start(self, group_metadata=None, lsan_disabled=False, lsan_allowed=None, >+ lsan_max_stack_depth=None, mozleak_allowed=None, mozleak_thresholds=None, >+ **kwargs): > """Configure the output handler""" >- self.instance = instance >- > if group_metadata is None: > group_metadata = {} > self.group_metadata = group_metadata >@@ -475,18 +561,13 @@ class OutputHandler(object): > allowAll=lsan_disabled) > else: > self.lsan_handler = None >+ super().start() > >- self.setup_ran = True >- >- for line in self.line_buffer: >- self.__call__(line) >- self.line_buffer = [] >- >- def after_stop(self, clean_shutdown=True): >- self.logger.info("PROCESS LEAKS %s" % self.instance.leak_report_file) >+ def after_process_stop(self, clean_shutdown=True): >+ super().after_process_stop(clean_shutdown) > if self.lsan_handler: > self.lsan_handler.process() >- if self.instance.leak_report_file is not None: >+ if self.leak_report_file is not None: > if not clean_shutdown: > # If we didn't get a clean shutdown there probably isn't a leak report file > self.logger.warning("Firefox didn't exit cleanly, not processing leak logs") >@@ -494,21 +575,24 @@ class OutputHandler(object): > # We have to ignore missing leaks in the tab because it can happen that the > # content process crashed and in that case we don't want the test to fail. > # Ideally we would record which content process crashed and just skip those. >+ self.logger.info("PROCESS LEAKS %s" % self.leak_report_file) > mozleak.process_leak_log( >- self.instance.leak_report_file, >+ self.leak_report_file, > leak_thresholds=self.mozleak_thresholds, > ignore_missing_leaks=["tab", "gmplugin"], > log=self.logger, > stack_fixer=self.stack_fixer, > scope=self.group_metadata.get("scope"), > allowed=self.mozleak_allowed) >+ if os.path.exists(self.leak_report_file): >+ os.unlink(self.leak_report_file) > > def __call__(self, line): > """Write a line of output from the firefox process to the log""" > if b"GLib-GObject-CRITICAL" in line: > return > if line: >- if not self.setup_ran: >+ if self.state < OutputHandlerState.AFTER_HANDLER_START: > self.line_buffer.append(line) > return > data = line.decode("utf8", "replace") >@@ -517,14 +601,12 @@ class OutputHandler(object): > if self.lsan_handler: > data = self.lsan_handler.log(data) > if data is not None: >- self.logger.process_output(self.instance and >- self.instance.runner.process_handler and >- self.instance.runner.process_handler.pid, >+ self.logger.process_output(self.pid, > data, >- command=" ".join(self.instance.runner.command)) >+ command=" ".join(self.command)) > > >-class ProfileCreator(object): >+class ProfileCreator: > def __init__(self, logger, prefs_root, config, test_type, extra_prefs, e10s, > enable_fission, browser_channel, binary, certutil_binary, ca_certificate_path): > self.logger = logger >@@ -540,18 +622,38 @@ class ProfileCreator(object): > self.certutil_binary = certutil_binary > self.ca_certificate_path = ca_certificate_path > >- def create(self): >+ def create(self, **kwargs): > """Create a Firefox profile and return the mozprofile Profile object pointing at that >- profile""" >+ profile >+ >+ :param kwargs: Additional arguments to pass into the profile constructor >+ """ > preferences = self._load_prefs() > >- profile = FirefoxProfile(preferences=preferences) >+ profile = FirefoxProfile(preferences=preferences, >+ restore=False, >+ **kwargs) > self._set_required_prefs(profile) > if self.ca_certificate_path is not None: > self._setup_ssl(profile) > > return profile > >+ def create_base64(self, **kwargs): >+ profile = self.create(**kwargs) >+ try: >+ with io.BytesIO() as buf: >+ with zipfile.ZipFile(buf, "w", compression=zipfile.ZIP_DEFLATED) as zipf: >+ for dirpath, _, filenames in os.walk(profile.profile): >+ for filename in filenames: >+ src_path = os.path.join(dirpath, filename) >+ dest_path = os.path.relpath(src_path, profile.profile) >+ with open(src_path, "rb") as f: >+ zipf.writestr(dest_path, f.read()) >+ return base64.b64encode(buf.getvalue()).decode("ascii").strip() >+ finally: >+ profile.cleanup() >+ > def _load_prefs(self): > prefs = Preferences() > >@@ -641,14 +743,13 @@ class ProfileCreator(object): > > > env[env_var] = (os.path.pathsep.join([certutil_dir, env[env_var]]) >- if env_var in env else certutil_dir).encode( >- sys.getfilesystemencoding() or 'utf-8', 'replace') >+ if env_var in env else certutil_dir) > > def certutil(*args): > cmd = [self.certutil_binary] + list(args) > self.logger.process_output("certutil", > subprocess.check_output(cmd, >- env=cast_env(env), >+ env=env, > stderr=subprocess.STDOUT), > " ".join(cmd)) > >@@ -670,7 +771,6 @@ class ProfileCreator(object): > certutil("-L", "-d", cert_db_path) > > >- > class FirefoxBrowser(Browser): > init_timeout = 70 > >@@ -679,7 +779,8 @@ class FirefoxBrowser(Browser): > ca_certificate_path=None, e10s=False, enable_webrender=False, enable_fission=False, > stackfix_dir=None, binary_args=None, timeout_multiplier=None, leak_check=False, > asan=False, stylo_threads=1, chaos_mode_flags=None, config=None, >- browser_channel="nightly", headless=None, preload_browser=False, **kwargs): >+ browser_channel="nightly", headless=None, preload_browser=False, >+ specialpowers_path=None, **kwargs): > Browser.__init__(self, logger) > > self.logger = logger >@@ -688,6 +789,7 @@ class FirefoxBrowser(Browser): > self.init_timeout = self.init_timeout * timeout_multiplier > > self.instance = None >+ self._settings = None > > self.stackfix_dir = stackfix_dir > self.symbols_path = symbols_path >@@ -696,6 +798,8 @@ class FirefoxBrowser(Browser): > self.asan = asan > self.leak_check = leak_check > >+ self.specialpowers_path = specialpowers_path >+ > profile_creator = ProfileCreator(logger, > prefs_root, > config, >@@ -726,19 +830,19 @@ class FirefoxBrowser(Browser): > symbols_path, > asan) > >- > def settings(self, test): >- return {"check_leaks": self.leak_check and not test.leaks, >- "lsan_disabled": test.lsan_disabled, >- "lsan_allowed": test.lsan_allowed, >- "lsan_max_stack_depth": test.lsan_max_stack_depth, >- "mozleak_allowed": self.leak_check and test.mozleak_allowed, >- "mozleak_thresholds": self.leak_check and test.mozleak_threshold} >+ self._settings = {"check_leaks": self.leak_check and not test.leaks, >+ "lsan_disabled": test.lsan_disabled, >+ "lsan_allowed": test.lsan_allowed, >+ "lsan_max_stack_depth": test.lsan_max_stack_depth, >+ "mozleak_allowed": self.leak_check and test.mozleak_allowed, >+ "mozleak_thresholds": self.leak_check and test.mozleak_threshold, >+ "special_powers": self.specialpowers_path and test.url_base == "/_mozilla/"} >+ return self._settings > > def start(self, group_metadata=None, **kwargs): > self.instance = self.instance_manager.get() >- self.instance.output_handler.setup(self.instance, >- group_metadata, >+ self.instance.output_handler.start(group_metadata, > **kwargs) > > def stop(self, force=False): >@@ -756,7 +860,11 @@ class FirefoxBrowser(Browser): > > def executor_browser(self): > assert self.instance is not None >- return ExecutorBrowser, {"marionette_port": self.instance.marionette_port} >+ extensions = [] >+ if self._settings.get("special_powers", False): >+ extensions.append(self.specialpowers_path) >+ return ExecutorBrowser, {"marionette_port": self.instance.marionette_port, >+ "extensions": extensions} > > def check_crash(self, process, test): > dump_dir = os.path.join(self.instance.runner.profile.profile, "minidumps") >@@ -771,3 +879,39 @@ class FirefoxBrowser(Browser): > except IOError: > self.logger.warning("Looking for crash dump files failed") > return False >+ >+ >+class FirefoxWdSpecBrowser(NullBrowser): >+ def __init__(self, logger, leak_check=False, **kwargs): >+ super().__init__(logger, **kwargs) >+ self.leak_check = leak_check >+ >+ def settings(self, test): >+ return {"check_leaks": self.leak_check and not test.leaks, >+ "lsan_disabled": test.lsan_disabled, >+ "lsan_allowed": test.lsan_allowed, >+ "lsan_max_stack_depth": test.lsan_max_stack_depth, >+ "mozleak_allowed": self.leak_check and test.mozleak_allowed, >+ "mozleak_thresholds": self.leak_check and test.mozleak_threshold} >+ >+ >+class GeckoDriverServer(WebDriverServer): >+ output_handler_cls = FirefoxOutputHandler >+ >+ def __init__(self, logger, marionette_port=2828, binary="geckodriver", >+ host="127.0.0.1", port=None, env=None, args=None): >+ if env is None: >+ env = os.environ.copy() >+ env["RUST_BACKTRACE"] = "1" >+ WebDriverServer.__init__(self, logger, binary, >+ host=host, >+ port=port, >+ env=env, >+ args=args) >+ self.marionette_port = marionette_port >+ >+ def make_command(self): >+ return [self.binary, >+ "--marionette-port", str(self.marionette_port), >+ "--host", self.host, >+ "--port", str(self.port)] + self._args >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/ie.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/ie.py >index 78c22b909acf..c851daf0b63c 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/ie.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/ie.py >@@ -1,12 +1,11 @@ >-from .base import Browser, ExecutorBrowser, require_arg >-from .base import get_timeout_multiplier # noqa: F401 >-from ..webdriver_server import InternetExplorerDriverServer >+from .base import require_arg >+from .base import NullBrowser, get_timeout_multiplier # noqa: F401 > from ..executors import executor_kwargs as base_executor_kwargs > from ..executors.executorinternetexplorer import InternetExplorerDriverWdspecExecutor # noqa: F401 > > __wptrunner__ = {"product": "ie", > "check_args": "check_args", >- "browser": "InternetExplorerBrowser", >+ "browser": "NullBrowser", > "executor": {"wdspec": "InternetExplorerDriverWdspecExecutor"}, > "browser_kwargs": "browser_kwargs", > "executor_kwargs": "executor_kwargs", >@@ -18,18 +17,17 @@ __wptrunner__ = {"product": "ie", > def check_args(**kwargs): > require_arg(kwargs, "webdriver_binary") > >-def browser_kwargs(test_type, run_info_data, config, **kwargs): >+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs): > return {"webdriver_binary": kwargs["webdriver_binary"], > "webdriver_args": kwargs.get("webdriver_args")} > >-def executor_kwargs(test_type, server_config, cache_manager, run_info_data, >+def executor_kwargs(logger, test_type, test_environment, run_info_data, > **kwargs): > options = {} > options["requireWindowFocus"] = True > capabilities = {} > capabilities["se:ieOptions"] = options >- executor_kwargs = base_executor_kwargs(test_type, server_config, >- cache_manager, run_info_data, **kwargs) >+ executor_kwargs = base_executor_kwargs(test_type, test_environment, run_info_data, **kwargs) > executor_kwargs["close_after_done"] = True > executor_kwargs["capabilities"] = capabilities > return executor_kwargs >@@ -39,35 +37,3 @@ def env_extras(**kwargs): > > def env_options(): > return {"supports_debugger": False} >- >-class InternetExplorerBrowser(Browser): >- used_ports = set() >- >- def __init__(self, logger, webdriver_binary, webdriver_args=None): >- Browser.__init__(self, logger) >- self.server = InternetExplorerDriverServer(self.logger, >- binary=webdriver_binary, >- args=webdriver_args) >- self.webdriver_host = "localhost" >- self.webdriver_port = self.server.port >- >- def start(self, **kwargs): >- self.server.start() >- >- def stop(self, force=False): >- self.server.stop(force=force) >- >- def pid(self): >- return self.server.pid >- >- def is_alive(self): >- # TODO(ato): This only indicates the server is alive, >- # and doesn't say anything about whether a browser session >- # is active. >- return self.server.is_alive() >- >- def cleanup(self): >- self.stop() >- >- def executor_browser(self): >- return ExecutorBrowser, {"webdriver_url": self.server.url} >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/opera.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/opera.py >index 805fedee636c..f3a2929ce8eb 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/opera.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/opera.py >@@ -1,4 +1,5 @@ > from .base import Browser, ExecutorBrowser, require_arg >+from .base import NullBrowser # noqa: F401 > from .base import get_timeout_multiplier # noqa: F401 > from ..webdriver_server import OperaDriverServer > from ..executors import executor_kwargs as base_executor_kwargs >@@ -9,7 +10,8 @@ from ..executors.executoropera import OperaDriverWdspecExecutor # noqa: F401 > > __wptrunner__ = {"product": "opera", > "check_args": "check_args", >- "browser": "OperaBrowser", >+ "browser": {None: "OperaBrowser", >+ "wdspec": "NullBrowser"}, > "executor": {"testharness": "SeleniumTestharnessExecutor", > "reftest": "SeleniumRefTestExecutor", > "wdspec": "OperaDriverWdspecExecutor"}, >@@ -24,18 +26,17 @@ def check_args(**kwargs): > require_arg(kwargs, "webdriver_binary") > > >-def browser_kwargs(test_type, run_info_data, config, **kwargs): >+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs): > return {"binary": kwargs["binary"], > "webdriver_binary": kwargs["webdriver_binary"], > "webdriver_args": kwargs.get("webdriver_args")} > > >-def executor_kwargs(test_type, server_config, cache_manager, run_info_data, >+def executor_kwargs(logger, test_type, test_environment, run_info_data, > **kwargs): > from selenium.webdriver import DesiredCapabilities > >- executor_kwargs = base_executor_kwargs(test_type, server_config, >- cache_manager, run_info_data, **kwargs) >+ executor_kwargs = base_executor_kwargs(test_type, test_environment, run_info_data, **kwargs) > executor_kwargs["close_after_done"] = True > capabilities = dict(DesiredCapabilities.OPERA.items()) > capabilities.setdefault("operaOptions", {})["prefs"] = { >@@ -71,7 +72,7 @@ class OperaBrowser(Browser): > """ > > def __init__(self, logger, binary, webdriver_binary="operadriver", >- webdriver_args=None): >+ webdriver_args=None, **kwargs): > """Creates a new representation of Opera. The `binary` argument gives > the browser binary to use for testing.""" > Browser.__init__(self, logger) >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/safari.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/safari.py >index cb2b175d4dfb..20a5475acf0b 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/safari.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/safari.py >@@ -1,4 +1,11 @@ >+import os >+import plistlib >+from distutils.spawn import find_executable >+ >+import psutil >+ > from .base import Browser, ExecutorBrowser, require_arg >+from .base import NullBrowser # noqa: F401 > from .base import get_timeout_multiplier # noqa: F401 > from ..webdriver_server import SafariDriverServer > from ..executors import executor_kwargs as base_executor_kwargs >@@ -10,7 +17,8 @@ from ..executors.executorsafari import SafariDriverWdspecExecutor # noqa: F401 > > __wptrunner__ = {"product": "safari", > "check_args": "check_args", >- "browser": "SafariBrowser", >+ "browser": {None: "SafariBrowser", >+ "wdspec": "NullBrowser"}, > "executor": {"testharness": "WebDriverTestharnessExecutor", > "reftest": "WebDriverRefTestExecutor", > "wdspec": "SafariDriverWdspecExecutor", >@@ -26,15 +34,14 @@ def check_args(**kwargs): > require_arg(kwargs, "webdriver_binary") > > >-def browser_kwargs(test_type, run_info_data, config, **kwargs): >+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs): > return {"webdriver_binary": kwargs["webdriver_binary"], >- "webdriver_args": kwargs.get("webdriver_args")} >+ "webdriver_args": kwargs.get("webdriver_args"), >+ "kill_safari": kwargs.get("kill_safari", False)} > > >-def executor_kwargs(test_type, server_config, cache_manager, run_info_data, >- **kwargs): >- executor_kwargs = base_executor_kwargs(test_type, server_config, >- cache_manager, run_info_data, **kwargs) >+def executor_kwargs(logger, test_type, test_environment, run_info_data, **kwargs): >+ executor_kwargs = base_executor_kwargs(test_type, test_environment, run_info_data, **kwargs) > executor_kwargs["close_after_done"] = True > executor_kwargs["capabilities"] = {} > if test_type == "testharness": >@@ -58,15 +65,60 @@ class SafariBrowser(Browser): > ``wptrunner.webdriver.SafariDriverServer``. > """ > >- def __init__(self, logger, webdriver_binary, webdriver_args=None): >+ def __init__(self, logger, webdriver_binary, webdriver_args=None, kill_safari=False, **kwargs): > """Creates a new representation of Safari. The `webdriver_binary` > argument gives the WebDriver binary to use for testing. (The browser > binary location cannot be specified, as Safari and SafariDriver are >- coupled.)""" >+ coupled.) If `kill_safari` is True, then `Browser.stop` will stop Safari.""" > Browser.__init__(self, logger) > self.server = SafariDriverServer(self.logger, > binary=webdriver_binary, > args=webdriver_args) >+ if "/" not in webdriver_binary: >+ wd_path = find_executable(webdriver_binary) >+ else: >+ wd_path = webdriver_binary >+ self.safari_path = self._findAssociatedSafariExecutable(wd_path) >+ >+ logger.debug("WebDriver executable path: %s" % wd_path) >+ logger.debug("Safari executable path: %s" % self.safari_path) >+ >+ self.kill_safari = kill_safari >+ >+ def _findAssociatedSafariExecutable(self, wd_path): >+ bundle_paths = [ >+ os.path.join(os.path.dirname(wd_path), "..", ".."), # bundled Safari (e.g. STP) >+ os.path.join(os.path.dirname(wd_path), "Safari.app"), # local Safari build >+ "/Applications/Safari.app", # system Safari >+ ] >+ >+ for bundle_path in bundle_paths: >+ info_path = os.path.join(bundle_path, "Contents", "Info.plist") >+ if not os.path.isfile(info_path): >+ continue >+ >+ with open(info_path, "rb") as fp: >+ info = plistlib.load(fp) >+ >+ # check we have a Safari family bundle >+ if "CFBundleIdentifier" not in info: >+ continue >+ ident = info["CFBundleIdentifier"] >+ if not isinstance(ident, str) or not ident.startswith("com.apple.Safari"): >+ continue >+ >+ # get the executable name >+ if "CFBundleExecutable" not in info: >+ continue >+ exe = info["CFBundleExecutable"] >+ if not isinstance(exe, str): >+ continue >+ >+ exe_path = os.path.join(bundle_path, "Contents", "MacOS", exe) >+ if not os.path.isfile(exe_path): >+ continue >+ >+ return exe_path > > def start(self, **kwargs): > self.server.start(block=False) >@@ -74,6 +126,20 @@ class SafariBrowser(Browser): > def stop(self, force=False): > self.server.stop(force=force) > >+ if self.kill_safari: >+ self.logger.debug("Going to stop Safari") >+ for proc in psutil.process_iter(attrs=["exe"]): >+ if proc.info["exe"] is not None and os.path.samefile(proc.info["exe"], self.safari_path): >+ self.logger.debug("Stopping Safari %s" % proc.pid) >+ try: >+ proc.terminate() >+ try: >+ proc.wait(10) >+ except psutil.TimeoutExpired: >+ proc.kill() >+ except psutil.NoSuchProcess: >+ pass >+ > def pid(self): > return self.server.pid > >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/sauce.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/sauce.py >index c57ac942ed4e..491319917599 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/sauce.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/sauce.py >@@ -12,7 +12,7 @@ import time > > import requests > >-from six.moves import cStringIO as StringIO >+from io import StringIO > > from .base import Browser, ExecutorBrowser, require_arg > from .base import get_timeout_multiplier # noqa: F401 >@@ -95,16 +95,15 @@ def check_args(**kwargs): > require_arg(kwargs, "sauce_key") > > >-def browser_kwargs(test_type, run_info_data, config, **kwargs): >+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs): > sauce_config = get_sauce_config(**kwargs) > > return {"sauce_config": sauce_config} > > >-def executor_kwargs(test_type, server_config, cache_manager, run_info_data, >+def executor_kwargs(logger, test_type, test_environment, run_info_data, > **kwargs): >- executor_kwargs = base_executor_kwargs(test_type, server_config, >- cache_manager, run_info_data, **kwargs) >+ executor_kwargs = base_executor_kwargs(test_type, test_environment, run_info_data, **kwargs) > > executor_kwargs["capabilities"] = get_capabilities(**kwargs) > >@@ -228,7 +227,7 @@ class SauceException(Exception): > class SauceBrowser(Browser): > init_timeout = 300 > >- def __init__(self, logger, sauce_config): >+ def __init__(self, logger, sauce_config, **kwargs): > Browser.__init__(self, logger) > self.sauce_config = sauce_config > >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/servo.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/servo.py >index b9476540ac7c..1791c1da2706 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/servo.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/servo.py >@@ -33,7 +33,7 @@ def check_args(**kwargs): > require_arg(kwargs, "binary") > > >-def browser_kwargs(test_type, run_info_data, config, **kwargs): >+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs): > return { > "binary": kwargs["binary"], > "debug_info": kwargs["debug_info"], >@@ -43,10 +43,9 @@ def browser_kwargs(test_type, run_info_data, config, **kwargs): > } > > >-def executor_kwargs(test_type, server_config, cache_manager, run_info_data, >+def executor_kwargs(logger, test_type, test_environment, run_info_data, > **kwargs): >- rv = base_executor_kwargs(test_type, server_config, >- cache_manager, run_info_data, **kwargs) >+ rv = base_executor_kwargs(test_type, test_environment, run_info_data, **kwargs) > rv["pause_after_test"] = kwargs["pause_after_test"] > if test_type == "wdspec": > rv["capabilities"] = {} >@@ -71,7 +70,7 @@ def update_properties(): > > class ServoBrowser(NullBrowser): > def __init__(self, logger, binary, debug_info=None, binary_args=None, >- user_stylesheets=None, ca_certificate_path=None): >+ user_stylesheets=None, ca_certificate_path=None, **kwargs): > NullBrowser.__init__(self, logger) > self.binary = binary > self.debug_info = debug_info >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/servodriver.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/servodriver.py >index 43794e41706d..9a6a1a402b36 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/servodriver.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/servodriver.py >@@ -6,12 +6,16 @@ from mozprocess import ProcessHandler > > from tools.serve.serve import make_hosts_file > >-from .base import Browser, require_arg, get_free_port, browser_command, ExecutorBrowser >+from .base import (Browser, >+ ExecutorBrowser, >+ OutputHandler, >+ require_arg, >+ get_free_port, >+ browser_command) > from .base import get_timeout_multiplier # noqa: F401 > from ..executors import executor_kwargs as base_executor_kwargs > from ..executors.executorservodriver import (ServoWebDriverTestharnessExecutor, # noqa: F401 > ServoWebDriverRefTestExecutor) # noqa: F401 >-from ..process import cast_env > > here = os.path.dirname(__file__) > >@@ -36,7 +40,7 @@ def check_args(**kwargs): > require_arg(kwargs, "binary") > > >-def browser_kwargs(test_type, run_info_data, config, **kwargs): >+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs): > return { > "binary": kwargs["binary"], > "binary_args": kwargs["binary_args"], >@@ -47,9 +51,8 @@ def browser_kwargs(test_type, run_info_data, config, **kwargs): > } > > >-def executor_kwargs(test_type, server_config, cache_manager, run_info_data, **kwargs): >- rv = base_executor_kwargs(test_type, server_config, >- cache_manager, run_info_data, **kwargs) >+def executor_kwargs(logger, test_type, test_environment, run_info_data, **kwargs): >+ rv = base_executor_kwargs(test_type, test_environment, run_info_data, **kwargs) > return rv > > >@@ -77,7 +80,8 @@ class ServoWebDriverBrowser(Browser): > init_timeout = 300 # Large timeout for cases where we're booting an Android emulator > > def __init__(self, logger, binary, debug_info=None, webdriver_host="127.0.0.1", >- server_config=None, binary_args=None, user_stylesheets=None, headless=None): >+ server_config=None, binary_args=None, >+ user_stylesheets=None, headless=None, **kwargs): > Browser.__init__(self, logger) > self.binary = binary > self.binary_args = binary_args or [] >@@ -91,6 +95,7 @@ class ServoWebDriverBrowser(Browser): > self.user_stylesheets = user_stylesheets if user_stylesheets else [] > self.headless = headless if headless else False > self.ca_certificate_path = server_config.ssl_config["ca_cert_path"] >+ self.output_handler = None > > def start(self, **kwargs): > self.webdriver_port = get_free_port() >@@ -129,13 +134,16 @@ class ServoWebDriverBrowser(Browser): > self.command = debug_args + self.command > > if not self.debug_info or not self.debug_info.interactive: >+ self.output_handler = OutputHandler(self.logger, self.command) > self.proc = ProcessHandler(self.command, > processOutputLine=[self.on_output], >- env=cast_env(env), >+ env=env, > storeOutput=False) > self.proc.run() >+ self.output_handler.after_process_start(self.proc.pid) >+ self.output_handler.start() > else: >- self.proc = subprocess.Popen(self.command, env=cast_env(env)) >+ self.proc = subprocess.Popen(self.command, env=env) > > self.logger.debug("Servo Started") > >@@ -147,6 +155,8 @@ class ServoWebDriverBrowser(Browser): > except OSError: > # This can happen on Windows if the process is already dead > pass >+ if self.output_handler is not None: >+ self.output_handler.after_process_stop() > > def pid(self): > if self.proc is None: >@@ -157,12 +167,6 @@ class ServoWebDriverBrowser(Browser): > except AttributeError: > return None > >- def on_output(self, line): >- """Write a line of output from the process to the log""" >- self.logger.process_output(self.pid(), >- line.decode("utf8", "replace"), >- command=" ".join(self.command)) >- > def is_alive(self): > return self.proc.poll() is None > >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/webkit.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/webkit.py >index da93cf164399..ad24771ac9ce 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/webkit.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/webkit.py >@@ -1,5 +1,5 @@ > from .base import Browser, ExecutorBrowser, require_arg >-from .base import get_timeout_multiplier, certificate_domain_list # noqa: F401 >+from .base import NullBrowser, get_timeout_multiplier, certificate_domain_list # noqa: F401 > from ..executors import executor_kwargs as base_executor_kwargs > from ..executors.executorwebdriver import (WebDriverTestharnessExecutor, # noqa: F401 > WebDriverRefTestExecutor, # noqa: F401 >@@ -10,7 +10,8 @@ from ..webdriver_server import WebKitDriverServer > > __wptrunner__ = {"product": "webkit", > "check_args": "check_args", >- "browser": "WebKitBrowser", >+ "browser": {None: "WebKitBrowser", >+ "wdspec": "NullBrowser"}, > "browser_kwargs": "browser_kwargs", > "executor": {"testharness": "WebDriverTestharnessExecutor", > "reftest": "WebDriverRefTestExecutor", >@@ -29,7 +30,7 @@ def check_args(**kwargs): > require_arg(kwargs, "webkit_port") > > >-def browser_kwargs(test_type, run_info_data, config, **kwargs): >+def browser_kwargs(logger, test_type, run_info_data, config, **kwargs): > return {"binary": kwargs["binary"], > "webdriver_binary": kwargs["webdriver_binary"], > "webdriver_args": kwargs.get("webdriver_args")} >@@ -54,12 +55,11 @@ def capabilities_for_port(server_config, **kwargs): > return {} > > >-def executor_kwargs(test_type, server_config, cache_manager, run_info_data, >+def executor_kwargs(logger, test_type, test_environment, run_info_data, > **kwargs): >- executor_kwargs = base_executor_kwargs(test_type, server_config, >- cache_manager, run_info_data, **kwargs) >+ executor_kwargs = base_executor_kwargs(test_type, test_environment, run_info_data, **kwargs) > executor_kwargs["close_after_done"] = True >- executor_kwargs["capabilities"] = capabilities_for_port(server_config, >+ executor_kwargs["capabilities"] = capabilities_for_port(test_environment.config, > **kwargs) > return executor_kwargs > >@@ -82,7 +82,7 @@ class WebKitBrowser(Browser): > """ > > def __init__(self, logger, binary, webdriver_binary=None, >- webdriver_args=None): >+ webdriver_args=None, **kwargs): > Browser.__init__(self, logger) > self.binary = binary > self.server = WebKitDriverServer(self.logger, binary=webdriver_binary, >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/config.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/config.py >index d46beb8e71db..8a9bdbf730d6 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/config.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/config.py >@@ -1,11 +1,12 @@ >-from six.moves.configparser import SafeConfigParser >+from configparser import ConfigParser > import os > import sys > from collections import OrderedDict >+from typing import Any, Dict > > here = os.path.dirname(__file__) > >-class ConfigDict(dict): >+class ConfigDict(Dict[str, Any]): > def __init__(self, base_path, *args, **kwargs): > self.base_path = base_path > dict.__init__(self, *args, **kwargs) >@@ -20,7 +21,7 @@ class ConfigDict(dict): > def read(config_path): > config_path = os.path.abspath(config_path) > config_root = os.path.dirname(config_path) >- parser = SafeConfigParser() >+ parser = ConfigParser() > success = parser.read(config_path) > assert config_path in success, success > >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/environment.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/environment.py >index 2527d26d391d..3fe971456ed6 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/environment.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/environment.py >@@ -1,15 +1,14 @@ > import json > import os >-import multiprocessing > import signal > import socket > import sys > import time >-from six import iteritems > >-from mozlog import get_default_logger, handlers, proxy >+from mozlog import get_default_logger, handlers > >-from .wptlogging import LogLevelRewriter >+from . import mpcontext >+from .wptlogging import LogLevelRewriter, QueueHandler, LogQueueThread > > here = os.path.dirname(__file__) > repo_root = os.path.abspath(os.path.join(here, os.pardir, os.pardir, os.pardir)) >@@ -50,24 +49,58 @@ class TestEnvironmentError(Exception): > pass > > >+def get_server_logger(): >+ logger = get_default_logger(component="wptserve") >+ log_filter = handlers.LogLevelFilter(lambda x: x, "info") >+ # Downgrade errors to warnings for the server >+ log_filter = LogLevelRewriter(log_filter, ["error"], "warning") >+ logger.component_filter = log_filter >+ return logger >+ >+ >+class ProxyLoggingContext: >+ """Context manager object that handles setup and teardown of a log queue >+ for handling logging messages from wptserve.""" >+ >+ def __init__(self, logger): >+ mp_context = mpcontext.get_context() >+ self.log_queue = mp_context.Queue() >+ self.logging_thread = LogQueueThread(self.log_queue, logger) >+ self.logger_handler = QueueHandler(self.log_queue) >+ >+ def __enter__(self): >+ self.logging_thread.start() >+ return self.logger_handler >+ >+ def __exit__(self, *args): >+ self.log_queue.put(None) >+ # Wait for thread to shut down but not for too long since it's a daemon >+ self.logging_thread.join(1) >+ >+ > class TestEnvironment(object): > """Context manager that owns the test environment i.e. the http and > websockets servers""" > def __init__(self, test_paths, testharness_timeout_multipler, >- pause_after_test, debug_info, options, ssl_config, env_extras, >+ pause_after_test, debug_test, debug_info, options, ssl_config, env_extras, > enable_quic=False, mojojs_path=None): >+ > self.test_paths = test_paths > self.server = None > self.config_ctx = None > self.config = None >+ self.server_logger = get_server_logger() >+ self.server_logging_ctx = ProxyLoggingContext(self.server_logger) > self.testharness_timeout_multipler = testharness_timeout_multipler > self.pause_after_test = pause_after_test >+ self.debug_test = debug_test > self.test_server_port = options.pop("test_server_port", True) > self.debug_info = debug_info > self.options = options if options is not None else {} > >- self.cache_manager = multiprocessing.Manager() >- self.stash = serve.stash.StashServer() >+ mp_context = mpcontext.get_context() >+ self.cache_manager = mp_context.Manager() >+ self.stash = serve.stash.StashServer(mp_context=mp_context) > self.env_extras = env_extras > self.env_extras_cms = None > self.ssl_config = ssl_config >@@ -75,6 +108,7 @@ class TestEnvironment(object): > self.mojojs_path = mojojs_path > > def __enter__(self): >+ server_log_handler = self.server_logging_ctx.__enter__() > self.config_ctx = self.build_config() > > self.config = self.config_ctx.__enter__() >@@ -82,8 +116,6 @@ class TestEnvironment(object): > self.stash.__enter__() > self.cache_manager.__enter__() > >- self.setup_server_logging() >- > assert self.env_extras_cms is None, ( > "A TestEnvironment object cannot be nested") > >@@ -94,8 +126,11 @@ class TestEnvironment(object): > cm.__enter__() > self.env_extras_cms.append(cm) > >- self.servers = serve.start(self.config, >- self.get_routes()) >+ self.servers = serve.start(self.server_logger, >+ self.config, >+ self.get_routes(), >+ mp_context=mpcontext.get_context(), >+ log_handlers=[server_log_handler]) > > if self.options.get("supports_debugger") and self.debug_info and self.debug_info.interactive: > self.ignore_interrupts() >@@ -104,9 +139,9 @@ class TestEnvironment(object): > def __exit__(self, exc_type, exc_val, exc_tb): > self.process_interrupts() > >- for scheme, servers in iteritems(self.servers): >+ for scheme, servers in self.servers.items(): > for port, server in servers: >- server.kill() >+ server.stop() > for cm in self.env_extras_cms: > cm.__exit__(exc_type, exc_val, exc_tb) > >@@ -115,6 +150,7 @@ class TestEnvironment(object): > self.cache_manager.__exit__(exc_type, exc_val, exc_tb) > self.stash.__exit__() > self.config_ctx.__exit__(exc_type, exc_val, exc_tb) >+ self.server_logging_ctx.__exit__(exc_type, exc_val, exc_tb) > > def ignore_interrupts(self): > signal.signal(signal.SIGINT, signal.SIG_IGN) >@@ -125,11 +161,15 @@ class TestEnvironment(object): > def build_config(self): > override_path = os.path.join(serve_path(self.test_paths), "config.json") > >- config = serve.ConfigBuilder() >+ config = serve.ConfigBuilder(self.server_logger) > > ports = { > "http": [8000, 8001], >+ "http-private": [8002], >+ "http-public": [8003], > "https": [8443, 8444], >+ "https-private": [8445], >+ "https-public": [8446], > "ws": [8888], > "wss": [8889], > "h2": [9000], >@@ -160,24 +200,6 @@ class TestEnvironment(object): > > return config > >- def setup_server_logging(self): >- server_logger = get_default_logger(component="wptserve") >- assert server_logger is not None >- log_filter = handlers.LogLevelFilter(lambda x: x, "info") >- # Downgrade errors to warnings for the server >- log_filter = LogLevelRewriter(log_filter, ["error"], "warning") >- server_logger.component_filter = log_filter >- >- server_logger = proxy.QueuedProxyLogger(server_logger) >- >- try: >- # Set as the default logger for wptserve >- serve.set_logger(server_logger) >- serve.logger = server_logger >- except Exception: >- # This happens if logging has already been set up for wptserve >- pass >- > def get_routes(self): > route_builder = serve.RoutesBuilder() > >@@ -191,7 +213,8 @@ class TestEnvironment(object): > (self.options.get("testharnessreport", "testharnessreport.js"), > {"output": self.pause_after_test, > "timeout_multiplier": self.testharness_timeout_multipler, >- "explicit_timeout": "true" if self.debug_info is not None else "false"}, >+ "explicit_timeout": "true" if self.debug_info is not None else "false", >+ "debug": "true" if self.debug_test else "false"}, > "text/javascript;charset=utf8", > "/resources/testharnessreport.js")]: > path = os.path.normpath(os.path.join(here, path)) >@@ -209,7 +232,7 @@ class TestEnvironment(object): > route_builder.add_handler("GET", "/resources/testdriver.js", > StringHandler(data, "text/javascript")) > >- for url_base, paths in iteritems(self.test_paths): >+ for url_base, paths in self.test_paths.items(): > if url_base == "/": > continue > route_builder.add_mount_point(url_base, paths["tests_path"]) >@@ -241,13 +264,13 @@ class TestEnvironment(object): > failed = [] > pending = [] > host = self.config["server_host"] >- for scheme, servers in iteritems(self.servers): >+ for scheme, servers in self.servers.items(): > for port, server in servers: > if not server.is_alive(): > failed.append((scheme, port)) > > if not failed and self.test_server_port: >- for scheme, servers in iteritems(self.servers): >+ for scheme, servers in self.servers.items(): > # TODO(Hexcles): Find a way to test QUIC's UDP port. > if scheme == "quic-transport": > continue >@@ -256,7 +279,7 @@ class TestEnvironment(object): > s.settimeout(0.1) > try: > s.connect((host, port)) >- except socket.error: >+ except OSError: > pending.append((host, port)) > finally: > s.close() >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/actions.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/actions.py >index b11d30bb94aa..963ed337fde4 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/actions.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/actions.py >@@ -13,6 +13,18 @@ class ClickAction(object): > self.protocol.click.element(element) > > >+class DeleteAllCookiesAction(object): >+ name = "delete_all_cookies" >+ >+ def __init__(self, logger, protocol): >+ self.logger = logger >+ self.protocol = protocol >+ >+ def __call__(self, payload): >+ self.logger.debug("Deleting all cookies") >+ self.protocol.cookies.delete_all_cookies() >+ >+ > class SendKeysAction(object): > name = "send_keys" > >@@ -171,6 +183,7 @@ class SetUserVerifiedAction(object): > > > actions = [ClickAction, >+ DeleteAllCookiesAction, > SendKeysAction, > ActionSequenceAction, > GenerateTestReportAction, >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/base.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/base.py >index 4c3891c1cb67..a88ab82e78d3 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/base.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/base.py >@@ -8,29 +8,30 @@ import traceback > import socket > import sys > from abc import ABCMeta, abstractmethod >-from six import text_type >-from six.moves.http_client import HTTPConnection >-from six.moves.urllib.parse import urljoin, urlsplit, urlunsplit >+from http.client import HTTPConnection >+from typing import Any, Callable, ClassVar, Optional, Tuple, Type, TYPE_CHECKING >+from urllib.parse import urljoin, urlsplit, urlunsplit > >-from ..testrunner import Stop > from .actions import actions > from .protocol import Protocol, BaseProtocolPart > >+if TYPE_CHECKING: >+ from ..webdriver_server import WebDriverServer >+ > here = os.path.dirname(__file__) > > >-def executor_kwargs(test_type, server_config, cache_manager, run_info_data, >- **kwargs): >+def executor_kwargs(test_type, test_environment, run_info_data, **kwargs): > timeout_multiplier = kwargs["timeout_multiplier"] > if timeout_multiplier is None: > timeout_multiplier = 1 > >- executor_kwargs = {"server_config": server_config, >+ executor_kwargs = {"server_config": test_environment.config, > "timeout_multiplier": timeout_multiplier, > "debug_info": kwargs["debug_info"]} > > if test_type in ("reftest", "print-reftest"): >- executor_kwargs["screenshot_cache"] = cache_manager.dict() >+ executor_kwargs["screenshot_cache"] = test_environment.cache_manager.dict() > > if test_type == "wdspec": > executor_kwargs["binary"] = kwargs.get("binary") >@@ -42,7 +43,7 @@ def executor_kwargs(test_type, server_config, cache_manager, run_info_data, > # want to view the results, however, the executor has to skip that cleanup. > if kwargs["pause_after_test"] or kwargs["pause_on_unexpected"]: > executor_kwargs["cleanup_after_test"] = False >- >+ executor_kwargs["debug_test"] = kwargs["debug_test"] > return executor_kwargs > > >@@ -180,11 +181,11 @@ class TimedRunner(object): > self.result_flag = threading.Event() > > def run(self): >- if self.set_timeout() is Stop: >- return Stop >- >- if self.before_run() is Stop: >- return Stop >+ for setup_fn in [self.set_timeout, self.before_run]: >+ err = setup_fn() >+ if err: >+ self.result = (False, err) >+ return self.result > > executor = threading.Thread(target=self.run_func) > executor.start() >@@ -243,8 +244,14 @@ class TestExecutor(object): > """ > __metaclass__ = ABCMeta > >- test_type = None >- convert_result = None >+ test_type = None # type: ClassVar[str] >+ # convert_result is a class variable set to a callable converter >+ # (e.g. reftest_result_converter) converting from an instance of >+ # URLManifestItem (e.g. RefTest) + type-dependent results object + >+ # type-dependent extra data, returning a tuple of Result and list of >+ # SubtestResult. For now, any callable is accepted. TODO: Make this type >+ # stricter when more of the surrounding code is annotated. >+ convert_result = None # type: ClassVar[Callable[..., Any]] > supports_testdriver = False > supports_jsshell = False > # Extra timeout to use after internal test timeout at which the harness >@@ -287,18 +294,15 @@ class TestExecutor(object): > """Run a particular test. > > :param test: The test to run""" >- if test.environment != self.last_environment: >- self.on_environment_change(test.environment) > try: >+ if test.environment != self.last_environment: >+ self.on_environment_change(test.environment) > result = self.do_test(test) > except Exception as e: > exception_string = traceback.format_exc() > self.logger.warning(exception_string) > result = self.result_from_exception(test, e, exception_string) > >- if result is Stop: >- return result >- > # log result of parent test > if result[0].status == "ERROR": > self.logger.debug(result[0].message) >@@ -307,14 +311,18 @@ class TestExecutor(object): > > self.runner.send_message("test_ended", test, result) > >- def server_url(self, protocol): >+ def server_url(self, protocol, subdomain=False): > scheme = "https" if protocol == "h2" else protocol >- return "%s://%s:%s" % (scheme, >- self.server_config["browser_host"], >- self.server_config["ports"][protocol][0]) >+ host = self.server_config["browser_host"] >+ if subdomain: >+ # The only supported subdomain filename flag is "www". >+ host = "{subdomain}.{host}".format(subdomain="www", host=host) >+ return "{scheme}://{host}:{port}".format(scheme=scheme, host=host, >+ port=self.server_config["ports"][protocol][0]) > > def test_url(self, test): >- return urljoin(self.server_url(test.environment["protocol"]), test.url) >+ return urljoin(self.server_url(test.environment["protocol"], >+ test.subdomain), test.url) > > @abstractmethod > def do_test(self, test): >@@ -332,7 +340,7 @@ class TestExecutor(object): > status = e.status > else: > status = "INTERNAL-ERROR" >- message = text_type(getattr(e, "message", "")) >+ message = str(getattr(e, "message", "")) > if message: > message += "\n" > message += exception_string >@@ -423,7 +431,7 @@ class RefTestImplementation(object): > > if len(lhs_hashes) != len(rhs_hashes): > self.logger.info("Got different number of pages") >- return False >+ return relation == "!=", None > > assert len(lhs_screenshots) == len(lhs_hashes) == len(rhs_screenshots) == len(rhs_hashes) > >@@ -590,11 +598,11 @@ class RefTestImplementation(object): > > class WdspecExecutor(TestExecutor): > convert_result = pytest_result_converter >- protocol_cls = None >+ protocol_cls = None # type: ClassVar[Type[Protocol]] > > def __init__(self, logger, browser, server_config, webdriver_binary, > webdriver_args, timeout_multiplier=1, capabilities=None, >- debug_info=None, **kwargs): >+ debug_info=None, environ=None, **kwargs): > self.do_delayed_imports() > TestExecutor.__init__(self, logger, browser, server_config, > timeout_multiplier=timeout_multiplier, >@@ -603,7 +611,13 @@ class WdspecExecutor(TestExecutor): > self.webdriver_args = webdriver_args > self.timeout_multiplier = timeout_multiplier > self.capabilities = capabilities >- self.protocol = self.protocol_cls(self, browser) >+ self.environ = environ if environ is not None else {} >+ self.output_handler_kwargs = None >+ self.output_handler_start_kwargs = None >+ >+ def setup(self, runner): >+ self.protocol = self.protocol_cls(self, self.browser) >+ super().setup(runner) > > def is_alive(self): > return self.protocol.is_alive() >@@ -706,7 +720,7 @@ class ConnectionlessProtocol(Protocol): > > > class WdspecProtocol(Protocol): >- server_cls = None >+ server_cls = None # type: ClassVar[Optional[Type[WebDriverServer]]] > > implements = [ConnectionlessBaseProtocolPart] > >@@ -717,14 +731,21 @@ class WdspecProtocol(Protocol): > self.capabilities = self.executor.capabilities > self.session_config = None > self.server = None >+ self.environ = os.environ.copy() >+ self.environ.update(executor.environ) >+ self.output_handler_kwargs = executor.output_handler_kwargs >+ self.output_handler_start_kwargs = executor.output_handler_start_kwargs > > def connect(self): > """Connect to browser via the HTTP server.""" > self.server = self.server_cls( > self.logger, > binary=self.webdriver_binary, >- args=self.webdriver_args) >- self.server.start(block=False) >+ args=self.webdriver_args, >+ env=self.environ) >+ self.server.start(block=False, >+ output_handler_kwargs=self.output_handler_kwargs, >+ output_handler_start_kwargs=self.output_handler_start_kwargs) > self.logger.info( > "WebDriver HTTP server listening at %s" % self.server.url) > self.session_config = {"host": self.server.host, >@@ -762,7 +783,7 @@ class CallbackHandler(object): > WebDriver. Things that are more different to WebDriver may need to create a > fully custom implementation.""" > >- unimplemented_exc = (NotImplementedError,) >+ unimplemented_exc = (NotImplementedError,) # type: ClassVar[Tuple[Type[Exception], ...]] > > def __init__(self, logger, protocol, test_window): > self.protocol = protocol >@@ -831,7 +852,7 @@ class ActionContext(object): > > self.initial_window = self.protocol.base.current_window > self.logger.debug("Switching to window %s" % self.context) >- self.protocol.testdriver.switch_to_window(self.context) >+ self.protocol.testdriver.switch_to_window(self.context, self.initial_window) > > def __exit__(self, *args): > if self.context is None: >@@ -839,5 +860,4 @@ class ActionContext(object): > > self.logger.debug("Switching back to initial window") > self.protocol.base.set_window(self.initial_window) >- self.protocol.testdriver._switch_to_frame(None) > self.initial_window = None >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executorchrome.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executorchrome.py >index 3a704754137f..431d398ebd27 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executorchrome.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executorchrome.py >@@ -1,7 +1,7 @@ > import os > import traceback > >-from six.moves.urllib.parse import urljoin >+from urllib.parse import urljoin > > from .base import WdspecProtocol, WdspecExecutor, get_pages > from .executorwebdriver import WebDriverProtocol, WebDriverRefTestExecutor, WebDriverRun >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executormarionette.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executormarionette.py >index 7e2cc8e38a44..25a3360b7d80 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executormarionette.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executormarionette.py >@@ -5,8 +5,7 @@ import time > import traceback > import uuid > >-from six import iteritems, iterkeys >-from six.moves.urllib.parse import urljoin >+from urllib.parse import urljoin > > errors = None > marionette = None >@@ -33,27 +32,22 @@ from .protocol import (ActionSequenceProtocolPart, > StorageProtocolPart, > SelectorProtocolPart, > ClickProtocolPart, >+ CookiesProtocolPart, > SendKeysProtocolPart, > TestDriverProtocolPart, > CoverageProtocolPart, > GenerateTestReportProtocolPart, > VirtualAuthenticatorProtocolPart, > SetPermissionProtocolPart, >- PrintProtocolPart) >-from ..testrunner import Stop >-from ..webdriver_server import GeckoDriverServer >+ PrintProtocolPart, >+ DebugProtocolPart) > > > def do_delayed_imports(): >- global errors, marionette >+ global errors, marionette, Addons > >- # Marionette client used to be called marionette, recently it changed >- # to marionette_driver for unfathomable reasons >- try: >- import marionette >- from marionette import errors >- except ImportError: >- from marionette_driver import marionette, errors >+ from marionette_driver import marionette, errors >+ from marionette_driver.addons import Addons > > > def _switch_to_window(marionette, handle): >@@ -133,7 +127,7 @@ class MarionetteBaseProtocolPart(BaseProtocolPart): > pass > except errors.JavascriptException as e: > # This can happen if we navigate, but just keep going >- self.logger.debug(e.message) >+ self.logger.debug(e) > pass > except IOError: > self.logger.debug("Socket closed") >@@ -441,6 +435,15 @@ class MarionetteClickProtocolPart(ClickProtocolPart): > return element.click() > > >+class MarionetteCookiesProtocolPart(CookiesProtocolPart): >+ def setup(self): >+ self.marionette = self.parent.marionette >+ >+ def delete_all_cookies(self): >+ self.logger.info("Deleting all cookies") >+ return self.marionette.delete_all_cookies() >+ >+ > class MarionetteSendKeysProtocolPart(SendKeysProtocolPart): > def setup(self): > self.marionette = self.parent.marionette >@@ -473,8 +476,12 @@ class MarionetteTestDriverProtocolPart(TestDriverProtocolPart): > obj["message"] = str(message) > self.parent.base.execute_script("window.postMessage(%s, '*')" % json.dumps(obj)) > >- def _switch_to_frame(self, frame_number): >- self.marionette.switch_to_frame(frame_number) >+ def _switch_to_frame(self, index_or_elem): >+ try: >+ self.marionette.switch_to_frame(index_or_elem) >+ except (errors.NoSuchFrameException, >+ errors.StaleElementException) as e: >+ raise ValueError from e > > def _switch_to_parent_frame(self): > self.marionette.switch_to_parent_frame() >@@ -489,7 +496,7 @@ class MarionetteCoverageProtocolPart(CoverageProtocolPart): > return > > script = """ >- const {PerTestCoverageUtils} = ChromeUtils.import("chrome://marionette/content/PerTestCoverageUtils.jsm"); >+ const {PerTestCoverageUtils} = ChromeUtils.import("chrome://remote/content/marionette/PerTestCoverageUtils.jsm"); > return PerTestCoverageUtils.enabled; > """ > with self.marionette.using_context(self.marionette.CONTEXT_CHROME): >@@ -499,7 +506,7 @@ class MarionetteCoverageProtocolPart(CoverageProtocolPart): > script = """ > var callback = arguments[arguments.length - 1]; > >- const {PerTestCoverageUtils} = ChromeUtils.import("chrome://marionette/content/PerTestCoverageUtils.jsm"); >+ const {PerTestCoverageUtils} = ChromeUtils.import("chrome://remote/content/marionette/PerTestCoverageUtils.jsm"); > PerTestCoverageUtils.beforeTest().then(callback, callback); > """ > with self.marionette.using_context(self.marionette.CONTEXT_CHROME): >@@ -519,7 +526,7 @@ class MarionetteCoverageProtocolPart(CoverageProtocolPart): > script = """ > var callback = arguments[arguments.length - 1]; > >- const {PerTestCoverageUtils} = ChromeUtils.import("chrome://marionette/content/PerTestCoverageUtils.jsm"); >+ const {PerTestCoverageUtils} = ChromeUtils.import("chrome://remote/content/marionette/PerTestCoverageUtils.jsm"); > PerTestCoverageUtils.afterTest().then(callback, callback); > """ > with self.marionette.using_context(self.marionette.CONTEXT_CHROME): >@@ -563,12 +570,22 @@ class MarionetteVirtualAuthenticatorProtocolPart(VirtualAuthenticatorProtocolPar > def set_user_verified(self, authenticator_id, uv): > raise NotImplementedError("set_user_verified not yet implemented") > >+ > class MarionetteSetPermissionProtocolPart(SetPermissionProtocolPart): > def setup(self): > self.marionette = self.parent.marionette > >- def set_permission(self, name, state, one_realm): >- raise NotImplementedError("set_permission not yet implemented") >+ def set_permission(self, descriptor, state, one_realm): >+ body = { >+ "descriptor": descriptor, >+ "state": state, >+ } >+ if one_realm is not None: >+ body["oneRealm"] = one_realm >+ try: >+ self.marionette._send_message("WebDriver:SetPermission", body) >+ except errors.UnsupportedOperationException: >+ raise NotImplementedError("set_permission not yet implemented") > > > class MarionettePrintProtocolPart(PrintProtocolPart): >@@ -621,6 +638,30 @@ render('%s').then(result => callback(result))""" % pdf_base64, new_sandbox=False > finally: > _switch_to_window(self.marionette, handle) > >+ >+class MarionetteDebugProtocolPart(DebugProtocolPart): >+ def setup(self): >+ self.marionette = self.parent.marionette >+ >+ def load_devtools(self): >+ with self.marionette.using_context(self.marionette.CONTEXT_CHROME): >+ self.parent.base.execute_script(""" >+const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm"); >+const { TargetFactory } = require("devtools/client/framework/target"); >+const { gDevTools } = require("devtools/client/framework/devtools"); >+ >+const callback = arguments[arguments.length - 1]; >+ >+async function loadDevTools() { >+ const target = await TargetFactory.forTab(window.gBrowser.selectedTab); >+ await gDevTools.showToolbox(target, "webconsole", "window"); >+} >+ >+loadDevTools().catch(() => dump("Devtools failed to load")) >+ .then(callback); >+""", asynchronous=True) >+ >+ > class MarionetteProtocol(Protocol): > implements = [MarionetteBaseProtocolPart, > MarionetteTestharnessProtocolPart, >@@ -628,6 +669,7 @@ class MarionetteProtocol(Protocol): > MarionetteStorageProtocolPart, > MarionetteSelectorProtocolPart, > MarionetteClickProtocolPart, >+ MarionetteCookiesProtocolPart, > MarionetteSendKeysProtocolPart, > MarionetteActionSequenceProtocolPart, > MarionetteTestDriverProtocolPart, >@@ -636,7 +678,8 @@ class MarionetteProtocol(Protocol): > MarionetteGenerateTestReportProtocolPart, > MarionetteVirtualAuthenticatorProtocolPart, > MarionetteSetPermissionProtocolPart, >- MarionettePrintProtocolPart] >+ MarionettePrintProtocolPart, >+ MarionetteDebugProtocolPart] > > def __init__(self, executor, browser, capabilities=None, timeout_multiplier=1, e10s=True, ccov=False): > do_delayed_imports() >@@ -680,11 +723,12 @@ class MarionetteProtocol(Protocol): > try: > self.marionette._request_in_app_shutdown() > self.marionette.delete_session(send_request=False) >+ self.marionette.cleanup() > except Exception: > # This is typically because the session never started > pass > if self.marionette is not None: >- del self.marionette >+ self.marionette = None > super(MarionetteProtocol, self).teardown() > > def is_alive(self): >@@ -696,20 +740,19 @@ class MarionetteProtocol(Protocol): > > def on_environment_change(self, old_environment, new_environment): > #Unset all the old prefs >- for name in iterkeys(old_environment.get("prefs", {})): >+ for name in old_environment.get("prefs", {}).keys(): > value = self.executor.original_pref_values[name] > if value is None: > self.prefs.clear(name) > else: > self.prefs.set(name, value) > >- for name, value in iteritems(new_environment.get("prefs", {})): >+ for name, value in new_environment.get("prefs", {}).items(): > self.executor.original_pref_values[name] = self.prefs.get(name) > self.prefs.set(name, value) > > > class ExecuteAsyncScriptRun(TimedRunner): >- > def set_timeout(self): > timeout = self.timeout > >@@ -722,8 +765,9 @@ class ExecuteAsyncScriptRun(TimedRunner): > # timeout is set too high. This works at least. > self.protocol.base.set_timeout(2**28 - 1) > except IOError: >- self.logger.error("Lost marionette connection before starting test") >- return Stop >+ msg = "Lost marionette connection before starting test" >+ self.logger.error(msg) >+ return ("INTERNAL-ERROR", msg) > > def before_run(self): > index = self.url.rfind("/storage/") >@@ -747,7 +791,7 @@ class ExecuteAsyncScriptRun(TimedRunner): > self.logger.info("NoSuchWindowException on command, setting status to CRASH") > self.result = False, ("CRASH", None) > except Exception as e: >- if isinstance(e, errors.JavascriptException) and e.message.startswith("Document was unloaded"): >+ if isinstance(e, errors.JavascriptException) and str(e).startswith("Document was unloaded"): > message = "Document unloaded; maybe test navigated the top-level-browsing context?" > else: > message = getattr(e, "message", "") >@@ -765,7 +809,7 @@ class MarionetteTestharnessExecutor(TestharnessExecutor): > > def __init__(self, logger, browser, server_config, timeout_multiplier=1, > close_after_done=True, debug_info=None, capabilities=None, >- debug=False, ccov=False, **kwargs): >+ debug=False, ccov=False, debug_test=False, **kwargs): > """Marionette-based executor for testharness.js tests""" > TestharnessExecutor.__init__(self, logger, browser, server_config, > timeout_multiplier=timeout_multiplier, >@@ -781,6 +825,9 @@ class MarionetteTestharnessExecutor(TestharnessExecutor): > self.close_after_done = close_after_done > self.window_id = str(uuid.uuid4()) > self.debug = debug >+ self.debug_test = debug_test >+ >+ self.install_extensions = browser.extensions > > self.original_pref_values = {} > >@@ -789,6 +836,11 @@ class MarionetteTestharnessExecutor(TestharnessExecutor): > > def setup(self, runner): > super(MarionetteTestharnessExecutor, self).setup(runner) >+ for extension_path in self.install_extensions: >+ self.logger.info("Installing extension from %s" % extension_path) >+ addons = Addons(self.protocol.marionette) >+ addons.install(extension_path) >+ > self.protocol.testharness.load_runner(self.last_environment["protocol"]) > > def is_alive(self): >@@ -842,6 +894,9 @@ class MarionetteTestharnessExecutor(TestharnessExecutor): > self.protocol.base.set_window(test_window) > protocol.testharness.test_window_loaded() > >+ if self.debug_test: >+ self.protocol.debug.load_devtools() >+ > handler = CallbackHandler(self.logger, protocol, test_window) > protocol.marionette.navigate(url) > while True: >@@ -868,7 +923,7 @@ class MarionetteRefTestExecutor(RefTestExecutor): > debug_info=None, reftest_internal=False, > reftest_screenshot="unexpected", ccov=False, > group_metadata=None, capabilities=None, debug=False, >- browser_version=None, **kwargs): >+ browser_version=None, debug_test=False, **kwargs): > """Marionette-based executor for reftests""" > RefTestExecutor.__init__(self, > logger, >@@ -891,6 +946,7 @@ class MarionetteRefTestExecutor(RefTestExecutor): > self.original_pref_values = {} > self.group_metadata = group_metadata > self.debug = debug >+ self.debug_test = debug_test > > with open(os.path.join(here, "reftest.js")) as f: > self.script = f.read() >@@ -953,7 +1009,12 @@ class MarionetteRefTestExecutor(RefTestExecutor): > assertion_count = self.protocol.asserts.get() > if "extra" not in result: > result["extra"] = {} >- result["extra"]["assertion_count"] = assertion_count >+ if assertion_count is not None: >+ result["extra"]["assertion_count"] = assertion_count >+ >+ if self.debug_test and result["status"] in ["PASS", "FAIL", "ERROR"] and "extra" in result: >+ self.protocol.base.set_window(self.protocol.base.window_handles()[0]) >+ self.protocol.debug.load_reftest_analyzer(test, result) > > return self.convert_result(test, result) > >@@ -1000,8 +1061,7 @@ class InternalRefTestImplementation(RefTestImplementation): > data = {"screenshot": screenshot, "isPrint": self.executor.is_print} > if self.executor.group_metadata is not None: > data["urlCount"] = {urljoin(self.executor.server_url(key[0]), key[1]):value >- for key, value in iteritems( >- self.executor.group_metadata.get("url_count", {})) >+ for key, value in self.executor.group_metadata.get("url_count", {}).items() > if value > 1} > self.chrome_scope = chrome_scope > if chrome_scope: >@@ -1053,12 +1113,32 @@ class InternalRefTestImplementation(RefTestImplementation): > > > class GeckoDriverProtocol(WdspecProtocol): >- server_cls = GeckoDriverServer >+ server_cls = None # To avoid circular imports we set this at runtime > > > class MarionetteWdspecExecutor(WdspecExecutor): > protocol_cls = GeckoDriverProtocol > >+ def __init__(self, logger, browser, server_config, webdriver_binary, >+ webdriver_args, timeout_multiplier=1, capabilities=None, >+ debug_info=None, environ=None, stackfix_dir=None, >+ symbols_path=None, leak_report_file=None, asan=False, >+ group_metadata=None, browser_settings=None, **kwargs): >+ >+ from ..browsers.firefox import GeckoDriverServer >+ super().__init__(logger, browser, server_config, webdriver_binary, >+ webdriver_args, timeout_multiplier=timeout_multiplier, >+ capabilities=capabilities, debug_info=debug_info, >+ environ=environ, **kwargs) >+ self.protocol_cls.server_cls = GeckoDriverServer >+ self.output_handler_kwargs = {"stackfix_dir": stackfix_dir, >+ "symbols_path": symbols_path, >+ "asan": asan, >+ "leak_report_file": leak_report_file} >+ self.output_handler_start_kwargs = {"group_metadata": group_metadata} >+ self.output_handler_start_kwargs.update(browser_settings) >+ >+ > > class MarionetteCrashtestExecutor(CrashtestExecutor): > def __init__(self, logger, browser, server_config, timeout_multiplier=1, >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executorselenium.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executorselenium.py >index ff26351f84ed..9d675cb3ef87 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executorselenium.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executorselenium.py >@@ -1,4 +1,3 @@ >-from __future__ import absolute_import > import json > import os > import socket >@@ -6,7 +5,7 @@ import threading > import time > import traceback > import uuid >-from six.moves.urllib.parse import urljoin >+from urllib.parse import urljoin > > from .base import (CallbackHandler, > RefTestExecutor, >@@ -19,10 +18,10 @@ from .protocol import (BaseProtocolPart, > Protocol, > SelectorProtocolPart, > ClickProtocolPart, >+ CookiesProtocolPart, > SendKeysProtocolPart, > ActionSequenceProtocolPart, > TestDriverProtocolPart) >-from ..testrunner import Stop > > here = os.path.dirname(__file__) > >@@ -183,6 +182,15 @@ class SeleniumClickProtocolPart(ClickProtocolPart): > return element.click() > > >+class SeleniumCookiesProtocolPart(CookiesProtocolPart): >+ def setup(self): >+ self.webdriver = self.parent.webdriver >+ >+ def delete_all_cookies(self): >+ self.logger.info("Deleting all cookies") >+ return self.webdriver.delete_all_cookies() >+ >+ > class SeleniumSendKeysProtocolPart(SendKeysProtocolPart): > def setup(self): > self.webdriver = self.parent.webdriver >@@ -219,6 +227,7 @@ class SeleniumProtocol(Protocol): > SeleniumTestharnessProtocolPart, > SeleniumSelectorProtocolPart, > SeleniumClickProtocolPart, >+ SeleniumCookiesProtocolPart, > SeleniumSendKeysProtocolPart, > SeleniumTestDriverProtocolPart, > SeleniumActionSequenceProtocolPart] >@@ -267,8 +276,9 @@ class SeleniumRun(TimedRunner): > try: > self.protocol.base.set_timeout(timeout + self.extra_timeout) > except exceptions.ErrorInResponseException: >- self.logger.error("Lost WebDriver connection") >- return Stop >+ msg = "Lost WebDriver connection" >+ self.logger.error(msg) >+ return ("INTERNAL-ERROR", msg) > > def run_func(self): > try: >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executorservo.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executorservo.py >index efe93abc69c7..f322421bd3fa 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executorservo.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executorservo.py >@@ -1,4 +1,3 @@ >-from __future__ import print_function > import base64 > import json > import os >@@ -7,7 +6,6 @@ import tempfile > import threading > import traceback > import uuid >-from six import ensure_str, iteritems > > from mozprocess import ProcessHandler > >@@ -23,7 +21,6 @@ from .base import (ConnectionlessProtocol, > WdspecProtocol) > from .process import ProcessTestExecutor > from ..browsers.base import browser_command >-from ..process import cast_env > from ..webdriver_server import ServoDriverServer > > >@@ -48,7 +45,7 @@ def build_servo_command(test, test_url_func, browser, binary, pause_after_test, > args += ["-Z", debug_opts] > for stylesheet in browser.user_stylesheets: > args += ["--user-stylesheet", stylesheet] >- for pref, value in iteritems(test.environment.get('prefs', {})): >+ for pref, value in test.environment.get('prefs', {}).items(): > args += ["--pref", "%s=%s" % (pref, value)] > if browser.ca_certificate_path: > args += ["--certificate-path", browser.ca_certificate_path] >@@ -103,11 +100,11 @@ class ServoTestharnessExecutor(ProcessTestExecutor): > self.proc = ProcessHandler(self.command, > processOutputLine=[self.on_output], > onFinish=self.on_finish, >- env=cast_env(env), >+ env=env, > storeOutput=False) > self.proc.run() > else: >- self.proc = subprocess.Popen(self.command, env=cast_env(env)) >+ self.proc = subprocess.Popen(self.command, env=env) > > try: > timeout = test.timeout * self.timeout_multiplier >@@ -235,7 +232,7 @@ class ServoRefTestExecutor(ProcessTestExecutor): > if not self.interactive: > self.proc = ProcessHandler(self.command, > processOutputLine=[self.on_output], >- env=cast_env(env)) >+ env=env) > > > try: >@@ -247,7 +244,7 @@ class ServoRefTestExecutor(ProcessTestExecutor): > raise > else: > self.proc = subprocess.Popen(self.command, >- env=cast_env(env)) >+ env=env) > try: > rv = self.proc.wait() > except KeyboardInterrupt: >@@ -264,7 +261,9 @@ class ServoRefTestExecutor(ProcessTestExecutor): > with open(output_path, "rb") as f: > # Might need to strip variable headers or something here > data = f.read() >- return True, [ensure_str(base64.b64encode(data))] >+ # Returning the screenshot as a string could potentially be avoided, >+ # see https://github.com/web-platform-tests/wpt/issues/28929. >+ return True, [base64.b64encode(data).decode()] > > def do_test(self, test): > result = self.implementation.run_test(test) >@@ -357,11 +356,11 @@ class ServoCrashtestExecutor(ProcessTestExecutor): > > if not self.interactive: > self.proc = ProcessHandler(command, >- env=cast_env(env), >+ env=env, > storeOutput=False) > self.proc.run() > else: >- self.proc = subprocess.Popen(command, env=cast_env(env)) >+ self.proc = subprocess.Popen(command, env=env) > > self.proc.wait() > >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executorservodriver.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executorservodriver.py >index 2cd1dbf3ad06..039bcd14e2be 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executorservodriver.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executorservodriver.py >@@ -10,7 +10,6 @@ from .base import (Protocol, > TestharnessExecutor, > TimedRunner, > strip_server) >-from ..testrunner import Stop > from ..webdriver_server import wait_for_service > > webdriver = None >@@ -184,8 +183,9 @@ class ServoWebDriverTestharnessExecutor(TestharnessExecutor): > self.protocol.session.timeouts.script = timeout > self.timeout = timeout > except IOError: >- self.logger.error("Lost webdriver connection") >- return Stop >+ msg = "Lost WebDriver connection" >+ self.logger.error(msg) >+ return ("INTERNAL-ERROR", msg) > > success, data = ServoWebDriverRun(self.logger, > self.do_testharness, >@@ -276,8 +276,9 @@ class ServoWebDriverRefTestExecutor(RefTestExecutor): > self.protocol.session.timeouts.script = timeout > self.timeout = timeout > except IOError: >- self.logger.error("Lost webdriver connection") >- return Stop >+ msg = "Lost webdriver connection" >+ self.logger.error(msg) >+ return ("INTERNAL-ERROR", msg) > > return ServoWebDriverRun(self.logger, > self._screenshot, >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executorwebdriver.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executorwebdriver.py >index bad5fd072237..6d5916a6afbc 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executorwebdriver.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executorwebdriver.py >@@ -1,4 +1,3 @@ >-from __future__ import absolute_import > import json > import os > import socket >@@ -6,7 +5,7 @@ import threading > import time > import traceback > import uuid >-from six.moves.urllib.parse import urljoin >+from urllib.parse import urljoin > > from .base import (CallbackHandler, > CrashtestExecutor, >@@ -20,22 +19,23 @@ from .protocol import (BaseProtocolPart, > Protocol, > SelectorProtocolPart, > ClickProtocolPart, >+ CookiesProtocolPart, > SendKeysProtocolPart, > ActionSequenceProtocolPart, > TestDriverProtocolPart, > GenerateTestReportProtocolPart, > SetPermissionProtocolPart, >- VirtualAuthenticatorProtocolPart) >-from ..testrunner import Stop >+ VirtualAuthenticatorProtocolPart, >+ DebugProtocolPart) > >-import webdriver as client >+from webdriver.client import Session > from webdriver import error > > here = os.path.dirname(__file__) > > > class WebDriverCallbackHandler(CallbackHandler): >- unimplemented_exc = (NotImplementedError, client.UnknownCommandException) >+ unimplemented_exc = (NotImplementedError, error.UnknownCommandException) > > > class WebDriverBaseProtocolPart(BaseProtocolPart): >@@ -49,7 +49,7 @@ class WebDriverBaseProtocolPart(BaseProtocolPart): > def set_timeout(self, timeout): > try: > self.webdriver.timeouts.script = timeout >- except client.WebDriverException: >+ except error.WebDriverException: > # workaround https://bugs.chromium.org/p/chromedriver/issues/detail?id=2057 > body = {"type": "script", "ms": timeout * 1000} > self.webdriver.send_session_command("POST", "timeouts", body) >@@ -71,16 +71,16 @@ class WebDriverBaseProtocolPart(BaseProtocolPart): > while True: > try: > self.webdriver.execute_async_script("") >- except (client.TimeoutException, >- client.ScriptTimeoutException, >- client.JavascriptErrorException): >+ except (error.TimeoutException, >+ error.ScriptTimeoutException, >+ error.JavascriptErrorException): > # A JavascriptErrorException will happen when we navigate; > # by ignoring it it's possible to reload the test whilst the > # harness remains paused > pass > except (socket.timeout, >- client.NoSuchWindowException, >- client.UnknownErrorException, >+ error.NoSuchWindowException, >+ error.UnknownErrorException, > IOError): > break > except Exception: >@@ -116,7 +116,7 @@ class WebDriverTestharnessProtocolPart(TestharnessProtocolPart): > try: > self.webdriver.window_handle = handle > self.webdriver.window.close() >- except client.NoSuchWindowException: >+ except error.NoSuchWindowException: > pass > self.webdriver.window_handle = self.runner_handle > return self.runner_handle >@@ -190,6 +190,15 @@ class WebDriverClickProtocolPart(ClickProtocolPart): > return element.click() > > >+class WebDriverCookiesProtocolPart(CookiesProtocolPart): >+ def setup(self): >+ self.webdriver = self.parent.webdriver >+ >+ def delete_all_cookies(self): >+ self.logger.info("Deleting all cookies") >+ return self.webdriver.send_session_command("DELETE", "cookie") >+ >+ > class WebDriverSendKeysProtocolPart(SendKeysProtocolPart): > def setup(self): > self.webdriver = self.parent.webdriver >@@ -197,7 +206,7 @@ class WebDriverSendKeysProtocolPart(SendKeysProtocolPart): > def send_keys(self, element, keys): > try: > return element.send_keys(keys) >- except client.UnknownErrorException as e: >+ except error.UnknownErrorException as e: > # workaround https://bugs.chromium.org/p/chromedriver/issues/detail?id=1999 > if (e.http_status != 500 or > e.status_code != "unknown error"): >@@ -227,8 +236,12 @@ class WebDriverTestDriverProtocolPart(TestDriverProtocolPart): > obj["message"] = str(message) > self.webdriver.execute_script("window.postMessage(%s, '*')" % json.dumps(obj)) > >- def _switch_to_frame(self, frame_number): >- self.webdriver.switch_frame(frame_number) >+ def _switch_to_frame(self, index_or_elem): >+ try: >+ self.webdriver.switch_frame(index_or_elem) >+ except (error.StaleElementReferenceException, >+ error.NoSuchFrameException) as e: >+ raise ValueError from e > > def _switch_to_parent_frame(self): > self.webdriver.switch_frame("parent") >@@ -283,17 +296,24 @@ class WebDriverVirtualAuthenticatorProtocolPart(VirtualAuthenticatorProtocolPart > return self.webdriver.send_session_command("POST", "webauthn/authenticator/%s/uv" % authenticator_id, uv) > > >+class WebDriverDebugProtocolPart(DebugProtocolPart): >+ def load_devtools(self): >+ raise NotImplementedError() >+ >+ > class WebDriverProtocol(Protocol): > implements = [WebDriverBaseProtocolPart, > WebDriverTestharnessProtocolPart, > WebDriverSelectorProtocolPart, > WebDriverClickProtocolPart, >+ WebDriverCookiesProtocolPart, > WebDriverSendKeysProtocolPart, > WebDriverActionSequenceProtocolPart, > WebDriverTestDriverProtocolPart, > WebDriverGenerateTestReportProtocolPart, > WebDriverSetPermissionProtocolPart, >- WebDriverVirtualAuthenticatorProtocolPart] >+ WebDriverVirtualAuthenticatorProtocolPart, >+ WebDriverDebugProtocolPart] > > def __init__(self, executor, browser, capabilities, **kwargs): > super(WebDriverProtocol, self).__init__(executor, browser) >@@ -308,7 +328,7 @@ class WebDriverProtocol(Protocol): > host, port = self.url.split(":")[1].strip("/"), self.url.split(':')[-1].strip("/") > > capabilities = {"alwaysMatch": self.capabilities} >- self.webdriver = client.Session(host, port, capabilities=capabilities) >+ self.webdriver = Session(host, port, capabilities=capabilities) > self.webdriver.start() > > def teardown(self): >@@ -331,7 +351,7 @@ class WebDriverProtocol(Protocol): > # 5 seconds of extra_timeout we have as maximum to end the test before > # the external timeout from testrunner triggers. > self.webdriver.send_session_command("GET", "window", timeout=2) >- except (socket.timeout, client.UnknownErrorException, client.InvalidSessionIdException): >+ except (socket.timeout, error.UnknownErrorException, error.InvalidSessionIdException): > return False > return True > >@@ -343,19 +363,20 @@ class WebDriverRun(TimedRunner): > def set_timeout(self): > try: > self.protocol.base.set_timeout(self.timeout + self.extra_timeout) >- except client.UnknownErrorException: >- self.logger.error("Lost WebDriver connection") >- return Stop >+ except error.UnknownErrorException: >+ msg = "Lost WebDriver connection" >+ self.logger.error(msg) >+ return ("INTERNAL-ERROR", msg) > > def run_func(self): > try: > self.result = True, self.func(self.protocol, self.url, self.timeout) >- except (client.TimeoutException, client.ScriptTimeoutException): >+ except (error.TimeoutException, error.ScriptTimeoutException): > self.result = False, ("EXTERNAL-TIMEOUT", None) >- except (socket.timeout, client.UnknownErrorException): >+ except (socket.timeout, error.UnknownErrorException): > self.result = False, ("CRASH", None) > except Exception as e: >- if (isinstance(e, client.WebDriverException) and >+ if (isinstance(e, error.WebDriverException) and > e.http_status == 408 and > e.status_code == "asynchronous script timeout"): > # workaround for https://bugs.chromium.org/p/chromedriver/issues/detail?id=2001 >@@ -449,7 +470,7 @@ class WebDriverTestharnessExecutor(TestharnessExecutor): > if not isinstance(result, list) or len(result) != 2: > try: > is_alive = self.is_alive() >- except client.WebDriverException: >+ except error.WebDriverException: > is_alive = False > > if not is_alive: >@@ -483,7 +504,7 @@ if (location.href === "about:blank") { > } else { > document.addEventListener("readystatechange", () => {if (document.readyState !== "loading") {callback(true)}}); > }""", asynchronous=True) >- except client.JavascriptErrorException: >+ except error.JavascriptErrorException: > # We can get an error here if the script runs in the initial about:blank > # document before it has navigated, with the driver returning an error > # indicating that the document was unloaded >@@ -497,7 +518,7 @@ class WebDriverRefTestExecutor(RefTestExecutor): > > def __init__(self, logger, browser, server_config, timeout_multiplier=1, > screenshot_cache=None, close_after_done=True, >- debug_info=None, capabilities=None, **kwargs): >+ debug_info=None, capabilities=None, debug_test=False, **kwargs): > """WebDriver-based executor for reftests""" > RefTestExecutor.__init__(self, > logger, >@@ -512,6 +533,7 @@ class WebDriverRefTestExecutor(RefTestExecutor): > self.implementation = RefTestImplementation(self) > self.close_after_done = close_after_done > self.has_window = False >+ self.debug_test = debug_test > > with open(os.path.join(here, "test-wait.js")) as f: > self.wait_script = f.read() % {"classname": "reftest-wait"} >@@ -529,13 +551,16 @@ class WebDriverRefTestExecutor(RefTestExecutor): > ) > try: > self.protocol.webdriver.window.position = (0, 0) >- except client.InvalidArgumentException: >+ except error.InvalidArgumentException: > # Safari 12 throws with 0 or 1, treating them as bools; fixed in STP > self.protocol.webdriver.window.position = (2, 2) > self.protocol.webdriver.window.size = (800 + width_offset, 600 + height_offset) > > result = self.implementation.run_test(test) > >+ if self.debug_test and result["status"] in ["PASS", "FAIL", "ERROR"] and "extra" in result: >+ self.protocol.debug.load_reftest_analyzer(test, result) >+ > return self.convert_result(test, result) > > def screenshot(self, test, viewport_size, dpi, page_ranges): >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/protocol.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/protocol.py >index bd768b1073c7..f9547ad904bc 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/protocol.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/protocol.py >@@ -1,6 +1,7 @@ > import traceback > > from abc import ABCMeta, abstractmethod >+from typing import ClassVar, List, Type > > > class Protocol(object): >@@ -16,7 +17,7 @@ class Protocol(object): > :param Browser browser: The Browser using this protocol""" > __metaclass__ = ABCMeta > >- implements = [] >+ implements = [] # type: ClassVar[List[Type[ProtocolPart]]] > > def __init__(self, executor, browser): > self.executor = executor >@@ -82,7 +83,7 @@ class ProtocolPart(object): > :param Protocol parent: The parent protocol""" > __metaclass__ = ABCMeta > >- name = None >+ name = None # type: ClassVar[str] > > def __init__(self, parent): > self.parent = parent >@@ -281,6 +282,18 @@ class ClickProtocolPart(ProtocolPart): > pass > > >+class CookiesProtocolPart(ProtocolPart): >+ """Protocol part for managing cookies""" >+ __metaclass__ = ABCMeta >+ >+ name = "cookies" >+ >+ @abstractmethod >+ def delete_all_cookies(self): >+ """Delete all cookies.""" >+ pass >+ >+ > class SendKeysProtocolPart(ProtocolPart): > """Protocol part for performing trusted clicks""" > __metaclass__ = ABCMeta >@@ -358,43 +371,72 @@ class TestDriverProtocolPart(ProtocolPart): > :param str message: Additional data to add to the message.""" > pass > >- def switch_to_window(self, wptrunner_id): >+ def switch_to_window(self, wptrunner_id, initial_window=None): > """Switch to a window given a wptrunner window id > >- :param str wptrunner_id: window id""" >+ :param str wptrunner_id: Testdriver-specific id for the target window >+ :param str initial_window: WebDriver window id for the test window""" > if wptrunner_id is None: > return > >+ if initial_window is None: >+ initial_window = self.parent.base.current_window >+ > stack = [str(item) for item in self.parent.base.window_handles()] >+ first = True > while stack: > item = stack.pop() >+ > if item is None: >+ assert first is False > self._switch_to_parent_frame() > continue >- elif isinstance(item, str): >- self.parent.base.set_window(item) >+ >+ if isinstance(item, str): >+ if not first or item != initial_window: >+ self.parent.base.set_window(item) >+ first = False > else: >- self._switch_to_frame(item) >+ assert first is False >+ try: >+ self._switch_to_frame(item) >+ except ValueError: >+ # The frame no longer exists, or doesn't have a nested browsing context, so continue >+ continue > > try: >- handle_window_id = self.parent.base.execute_script("return window.__wptrunner_id") >- if str(handle_window_id) == wptrunner_id: >- return >+ # Get the window id and a list of elements containing nested browsing contexts. >+ # For embed we can't tell fpr sure if there's a nested browsing context, so always return it >+ # and fail later if there isn't >+ result = self.parent.base.execute_script(""" >+ let contextParents = Array.from(document.querySelectorAll("frame, iframe, embed, object")) >+ .filter(elem => elem.localName !== "embed" ? (elem.contentWindow !== null) : true); >+ return [window.__wptrunner_id, contextParents]""") > except Exception: >- pass >- frame_count = self.parent.base.execute_script("return window.length") >- # None here makes us switch back to the parent after we've processed all the subframes >- stack.append(None) >- if frame_count: >- stack.extend(reversed(range(0, frame_count))) >+ continue >+ >+ if result is None: >+ # With marionette at least this is possible if the content process crashed. Not quite >+ # sure how we want to handle that case. >+ continue >+ >+ handle_window_id, nested_context_containers = result >+ >+ if handle_window_id and str(handle_window_id) == wptrunner_id: >+ return >+ >+ for elem in reversed(nested_context_containers): >+ # None here makes us switch back to the parent after we've processed the frame >+ stack.append(None) >+ stack.append(elem) > > raise Exception("Window with id %s not found" % wptrunner_id) > > @abstractmethod >- def _switch_to_frame(self, index): >+ def _switch_to_frame(self, index_or_elem): > """Switch to a frame in the current window > >- :param int index: Frame id""" >+ :param int index_or_elem: Frame id or container element""" > pass > > @abstractmethod >@@ -503,3 +545,32 @@ class PrintProtocolPart(ProtocolPart): > def render_as_pdf(self, width, height): > """Output document as PDF""" > pass >+ >+ >+class DebugProtocolPart(ProtocolPart): >+ """Protocol part for debugging test failures.""" >+ __metaclass__ = ABCMeta >+ >+ name = "debug" >+ >+ @abstractmethod >+ def load_devtools(self): >+ """Load devtools in the current window""" >+ pass >+ >+ def load_reftest_analyzer(self, test, result): >+ import io >+ import mozlog >+ from urllib.parse import quote, urljoin >+ >+ debug_test_logger = mozlog.structuredlog.StructuredLogger("debug_test") >+ output = io.StringIO() >+ debug_test_logger.suite_start([]) >+ debug_test_logger.add_handler(mozlog.handlers.StreamHandler(output, formatter=mozlog.formatters.TbplFormatter())) >+ debug_test_logger.test_start(test.id) >+ # Always use PASS as the expected value so we get output even for expected failures >+ debug_test_logger.test_end(test.id, result["status"], "PASS", extra=result.get("extra")) >+ >+ self.parent.base.load(urljoin(self.parent.executor.server_url("https"), >+ "/common/third_party/reftest-analyzer.xhtml#log=%s" % >+ quote(output.getvalue()))) >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/pytestrunner/runner.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/pytestrunner/runner.py >index 3d32917659b3..50819a935899 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/pytestrunner/runner.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/pytestrunner/runner.py >@@ -24,7 +24,7 @@ def do_delayed_imports(): > import pytest > > >-def run(path, server_config, session_config, timeout=0): >+def run(path, server_config, session_config, timeout=0, environ=None): > """ > Run Python test at ``path`` in pytest. The provided ``session`` > is exposed as a fixture available in the scope of the test functions. >@@ -41,28 +41,40 @@ def run(path, server_config, session_config, timeout=0): > if pytest is None: > do_delayed_imports() > >- os.environ["WD_HOST"] = session_config["host"] >- os.environ["WD_PORT"] = str(session_config["port"]) >- os.environ["WD_CAPABILITIES"] = json.dumps(session_config["capabilities"]) >- os.environ["WD_SERVER_CONFIG"] = json.dumps(server_config.as_dict_for_wd_env_variable()) >- >- harness = HarnessResultRecorder() >- subtests = SubtestResultRecorder() >- >- with TemporaryDirectory() as cache: >- try: >- pytest.main(["--strict", # turn warnings into errors >- "-vv", # show each individual subtest and full failure logs >- "--capture", "no", # enable stdout/stderr from tests >- "--basetemp", cache, # temporary directory >- "--showlocals", # display contents of variables in local scope >- "-p", "no:mozlog", # use the WPT result recorder >- "-p", "no:cacheprovider", # disable state preservation across invocations >- "-o=console_output_style=classic", # disable test progress bar >- path], >- plugins=[harness, subtests]) >- except Exception as e: >- harness.outcome = ("INTERNAL-ERROR", str(e)) >+ old_environ = os.environ.copy() >+ try: >+ with TemporaryDirectory() as cache: >+ os.environ["WD_HOST"] = session_config["host"] >+ os.environ["WD_PORT"] = str(session_config["port"]) >+ os.environ["WD_CAPABILITIES"] = json.dumps(session_config["capabilities"]) >+ >+ config_path = os.path.join(cache, "wd_server_config.json") >+ os.environ["WD_SERVER_CONFIG_FILE"] = config_path >+ with open(config_path, "w") as f: >+ json.dump(server_config.as_dict(), f) >+ >+ if environ: >+ os.environ.update(environ) >+ >+ harness = HarnessResultRecorder() >+ subtests = SubtestResultRecorder() >+ >+ try: >+ pytest.main(["--strict", # turn warnings into errors >+ "-vv", # show each individual subtest and full failure logs >+ "--capture", "no", # enable stdout/stderr from tests >+ "--basetemp", cache, # temporary directory >+ "--showlocals", # display contents of variables in local scope >+ "-p", "no:mozlog", # use the WPT result recorder >+ "-p", "no:cacheprovider", # disable state preservation across invocations >+ "-o=console_output_style=classic", # disable test progress bar >+ path], >+ plugins=[harness, subtests]) >+ except Exception as e: >+ harness.outcome = ("INTERNAL-ERROR", str(e)) >+ >+ finally: >+ os.environ = old_environ > > return (harness.outcome, subtests.results) > >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/font.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/font.py >index 910358f51596..39e248b251b0 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/font.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/font.py >@@ -77,13 +77,15 @@ class FontInstaller(object): > > # Per https://github.com/web-platform-tests/results-collection/issues/218 > # installing Ahem on macOS is flaky, so check if it actually installed >- fonts = check_output(['/usr/sbin/system_profiler', '-xml', 'SPFontsDataType']) >+ with open(os.devnull, 'w') as f: >+ fonts = check_output(['/usr/sbin/system_profiler', '-xml', 'SPFontsDataType'], stderr=f) >+ > try: > # if py3 >- readPlistFromBytes = plistlib.readPlistFromBytes >+ load_plist = plistlib.loads > except AttributeError: >- readPlistFromBytes = plistlib.readPlistFromString >- fonts = readPlistFromBytes(fonts) >+ load_plist = plistlib.readPlistFromString >+ fonts = load_plist(fonts) > assert len(fonts) == 1 > for font in fonts[0]['_items']: > if font['path'] == installed_font_path: >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/formatters/tests/__init__.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/formatters/tests/__init__.py >new file mode 100644 >index 000000000000..e69de29bb2d1 >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/instruments.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/instruments.py >index a887cf4fda72..4e3e013aec43 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/instruments.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/instruments.py >@@ -1,6 +1,6 @@ > import time > import threading >-from six.moves.queue import Queue >+from queue import Queue > > """Instrumentation for measuring high-level time spent on various tasks inside the runner. > >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/manifestexpected.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/manifestexpected.py >index 31c57e9cb734..1cd035a3b243 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/manifestexpected.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/manifestexpected.py >@@ -1,8 +1,6 @@ > import os > from collections import deque >-from six import string_types, text_type >-from six.moves.urllib.parse import urljoin >- >+from urllib.parse import urljoin > > from .wptmanifest.backends import static > from .wptmanifest.backends.base import ManifestItem >@@ -49,7 +47,7 @@ def list_prop(name, node): > """List property""" > try: > list_prop = node.get(name) >- if isinstance(list_prop, string_types): >+ if isinstance(list_prop, str): > return [list_prop] > return list(list_prop) > except KeyError: >@@ -59,7 +57,7 @@ def list_prop(name, node): > def str_prop(name, node): > try: > prop = node.get(name) >- if not isinstance(prop, string_types): >+ if not isinstance(prop, str): > raise ValueError > return prop > except KeyError: >@@ -70,7 +68,7 @@ def tags(node): > """Set of tags that have been applied to the test""" > try: > value = node.get("tags") >- if isinstance(value, text_type): >+ if isinstance(value, str): > return {value} > return set(value) > except KeyError: >@@ -79,7 +77,7 @@ def tags(node): > > def prefs(node): > def value(ini_value): >- if isinstance(ini_value, text_type): >+ if isinstance(ini_value, str): > return tuple(pref_piece.strip() for pref_piece in ini_value.split(':', 1)) > else: > # this should be things like @Reset, which are apparently type 'object' >@@ -87,7 +85,7 @@ def prefs(node): > > try: > node_prefs = node.get("prefs") >- if isinstance(node_prefs, text_type): >+ if isinstance(node_prefs, str): > rv = dict(value(node_prefs)) > else: > rv = dict(value(item) for item in node_prefs) >@@ -99,7 +97,7 @@ def prefs(node): > def set_prop(name, node): > try: > node_items = node.get(name) >- if isinstance(node_items, text_type): >+ if isinstance(node_items, str): > rv = {node_items} > else: > rv = set(node_items) >@@ -112,7 +110,7 @@ def leak_threshold(node): > rv = {} > try: > node_items = node.get("leak-threshold") >- if isinstance(node_items, text_type): >+ if isinstance(node_items, str): > node_items = [node_items] > for item in node_items: > process, value = item.rsplit(":", 1) >@@ -140,7 +138,7 @@ def fuzzy_prop(node): > maxDifferences=10;totalPixels=10-20 > > specifies that for any test/ref pair for which no other rule is supplied, >- there must be a maximum pixel difference of exactly 10, and betwen 10 and >+ there must be a maximum pixel difference of exactly 10, and between 10 and > 20 total pixels different. > > test.html==ref.htm:10;20 >@@ -169,7 +167,7 @@ def fuzzy_prop(node): > if not isinstance(value, list): > value = [value] > for item in value: >- if not isinstance(item, text_type): >+ if not isinstance(item, str): > rv.append(item) > continue > parts = item.rsplit(":", 1) >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/manifestinclude.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/manifestinclude.py >index 79b5b19b3a3f..97348e6d52f9 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/manifestinclude.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/manifestinclude.py >@@ -6,8 +6,7 @@ be included or excluded. > """ > import glob > import os >-from six import iteritems >-from six.moves.urllib.parse import urlparse, urlsplit >+from urllib.parse import urlparse, urlsplit > > from .wptmanifest.node import DataNode > from .wptmanifest.backends import conditional >@@ -95,7 +94,7 @@ class IncludeManifest(ManifestItem): > if paths: > urls = [] > for path in paths: >- for manifest, data in iteritems(test_manifests): >+ for manifest, data in test_manifests.items(): > found = False > rel_path = os.path.relpath(path, data["tests_path"]) > iterator = manifest.iterpath if os.path.isfile(path) else manifest.iterdir >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/manifestupdate.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/manifestupdate.py >index 129457b3fd12..b25d49b0fb7c 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/manifestupdate.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/manifestupdate.py >@@ -1,9 +1,8 @@ >-from __future__ import print_function > import os >-from six.moves.urllib.parse import urljoin, urlsplit >+from urllib.parse import urljoin, urlsplit > from collections import namedtuple, defaultdict, deque > from math import ceil >-from six import integer_types, iterkeys, itervalues, iteritems, string_types, text_type >+from typing import Any, Callable, ClassVar, Dict, List > > from .wptmanifest import serialize > from .wptmanifest.node import (DataNode, ConditionalNode, BinaryExpressionNode, >@@ -76,7 +75,7 @@ class UpdateProperties(object): > return name in self._classes > > def __iter__(self): >- for name in iterkeys(self._classes): >+ for name in self._classes.keys(): > yield getattr(self, name) > > >@@ -313,18 +312,21 @@ def build_conditional_tree(_, run_info_properties, results): > > def build_unconditional_tree(_, run_info_properties, results): > root = expectedtree.Node(None, None) >- for run_info, values in iteritems(results): >- for value, count in iteritems(values): >+ for run_info, values in results.items(): >+ for value, count in values.items(): > root.result_values[value] += count > root.run_info.add(run_info) > return root > > > class PropertyUpdate(object): >- property_name = None >- cls_default_value = None >- value_type = None >- property_builder = None >+ property_name = None # type: ClassVar[str] >+ cls_default_value = None # type: ClassVar[Any] >+ value_type = None # type: ClassVar[type] >+ # property_builder is a class variable set to either build_conditional_tree >+ # or build_unconditional_tree. TODO: Make this type stricter when those >+ # methods are annotated. >+ property_builder = None # type: ClassVar[Callable[..., Any]] > > def __init__(self, node): > self.node = node >@@ -411,7 +413,7 @@ class PropertyUpdate(object): > for e in errors: > if disable_intermittent: > condition = e.cond.children[0] if e.cond else None >- msg = disable_intermittent if isinstance(disable_intermittent, string_types+(text_type,)) else "unstable" >+ msg = disable_intermittent if isinstance(disable_intermittent, str) else "unstable" > self.node.set("disabled", msg, condition) > self.node.new_disabled = True > else: >@@ -499,7 +501,7 @@ class PropertyUpdate(object): > for run_info in node.run_info} > > node_by_run_info = {run_info: node >- for (run_info, node) in iteritems(run_info_index) >+ for (run_info, node) in run_info_index.items() > if node.result_values} > > run_info_by_condition = self.run_info_by_condition(run_info_index, >@@ -512,7 +514,7 @@ class PropertyUpdate(object): > # using the properties we've specified and not matching any run_info > top_level_props, dependent_props = self.node.root.run_info_properties > update_properties = set(top_level_props) >- for item in itervalues(dependent_props): >+ for item in dependent_props.values(): > update_properties |= set(item) > for condition in current_conditions: > if ((not condition.variables.issubset(update_properties) and >@@ -614,7 +616,8 @@ class PropertyUpdate(object): > except ConditionError: > expr = make_expr(prop_set, value) > error = ConditionError(expr) >- expr = make_expr(prop_set, value) >+ else: >+ expr = make_expr(prop_set, value) > else: > # The root node needs special handling > expr = None >@@ -695,7 +698,7 @@ class ExpectedUpdate(PropertyUpdate): > raise ConditionError > > counts = {} >- for status, count in iteritems(new): >+ for status, count in new.items(): > if isinstance(status, tuple): > counts[status[0]] = count > counts.update({intermittent: 0 for intermittent in status[1:] if intermittent not in counts}) >@@ -709,24 +712,27 @@ class ExpectedUpdate(PropertyUpdate): > # Counts with 0 are considered intermittent. > statuses = ["OK", "PASS", "FAIL", "ERROR", "TIMEOUT", "CRASH"] > status_priority = {value: i for i, value in enumerate(statuses)} >- sorted_new = sorted(iteritems(counts), key=lambda x:(-1 * x[1], >- status_priority.get(x[0], >- len(status_priority)))) >+ sorted_new = sorted(counts.items(), key=lambda x:(-1 * x[1], >+ status_priority.get(x[0], >+ len(status_priority)))) > expected = [] > for status, count in sorted_new: > # If we are not removing existing recorded intermittents, with a count of 0, > # add them in to expected. > if count > 0 or not self.remove_intermittent: > expected.append(status) >+ >+ # If the new intermittent is a subset of the existing one, just use the existing one >+ # This prevents frequent flip-flopping of results between e.g. [OK, TIMEOUT] and >+ # [TIMEOUT, OK] >+ if current and set(expected).issubset(set(current)): >+ return current >+ > if self.update_intermittent: > if len(expected) == 1: > return expected[0] > return expected > >- # If nothing has changed and not self.update_intermittent, preserve existing >- # intermittent. >- if set(expected).issubset(set(current)): >- return current > # If we are not updating intermittents, return the status with the highest occurence. > return expected[0] > >@@ -762,7 +768,7 @@ class MinAssertsUpdate(PropertyUpdate): > > > class AppendOnlyListUpdate(PropertyUpdate): >- cls_default_value = [] >+ cls_default_value = [] # type: ClassVar[List[str]] > property_builder = build_unconditional_tree > > def updated_value(self, current, new): >@@ -774,7 +780,7 @@ class AppendOnlyListUpdate(PropertyUpdate): > for item in new: > if item is None: > continue >- elif isinstance(item, text_type): >+ elif isinstance(item, str): > rv.add(item) > else: > rv |= item >@@ -815,14 +821,14 @@ class LeakObjectUpdate(AppendOnlyListUpdate): > > class LeakThresholdUpdate(PropertyUpdate): > property_name = "leak-threshold" >- cls_default_value = {} >+ cls_default_value = {} # type: ClassVar[Dict[str, int]] > property_builder = build_unconditional_tree > > def from_result_value(self, result): > return result > > def to_ini_value(self, data): >- return ["%s:%s" % item for item in sorted(iteritems(data))] >+ return ["%s:%s" % item for item in sorted(data.items())] > > def from_ini_value(self, data): > rv = {} >@@ -897,10 +903,10 @@ def make_expr(prop_set, rhs): > > > def make_node(value): >- if isinstance(value, integer_types+(float,)): >+ if isinstance(value, (int, float,)): > node = NumberNode(value) >- elif isinstance(value, text_type): >- node = StringNode(text_type(value)) >+ elif isinstance(value, str): >+ node = StringNode(str(value)) > elif hasattr(value, "__iter__"): > node = ListNode() > for item in value: >@@ -909,14 +915,14 @@ def make_node(value): > > > def make_value_node(value): >- if isinstance(value, integer_types+(float,)): >+ if isinstance(value, (int, float,)): > node = ValueNode(value) >- elif isinstance(value, text_type): >- node = ValueNode(text_type(value)) >+ elif isinstance(value, str): >+ node = ValueNode(str(value)) > elif hasattr(value, "__iter__"): > node = ListNode() > for item in value: >- node.append(make_node(item)) >+ node.append(make_value_node(item)) > else: > raise ValueError("Don't know how to convert %s into node" % type(value)) > return node >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/metadata.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/metadata.py >index ab8d4740f3fc..9c54e570c01b 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/metadata.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/metadata.py >@@ -1,12 +1,11 @@ >-from __future__ import print_function > import array > import os >-import sys > from collections import defaultdict, namedtuple >+from typing import Dict, List, Tuple > > from mozlog import structuredlog >-from six import ensure_str, ensure_text, iteritems, iterkeys, itervalues, text_type >-from six.moves import intern, range >+from six import ensure_str, ensure_text >+from sys import intern > > from . import manifestupdate > from . import testloader >@@ -22,7 +21,7 @@ logger = structuredlog.StructuredLogger("web-platform-tests") > try: > import ujson as json > except ImportError: >- import json >+ import json # type: ignore > > > class RunInfo(object): >@@ -45,11 +44,11 @@ class RunInfo(object): > return self.canonical_repr == other.canonical_repr > > def iteritems(self): >- for key, value in iteritems(self.data): >+ for key, value in self.data.items(): > yield key, value > > def items(self): >- return list(iteritems(self)) >+ return list(self.items()) > > > def update_expected(test_paths, serve_root, log_file_names, >@@ -93,7 +92,7 @@ def update_expected(test_paths, serve_root, log_file_names, > > def do_delayed_imports(serve_root=None): > global manifest, manifestitem >- from manifest import manifest, item as manifestitem >+ from manifest import manifest, item as manifestitem # type: ignore > > > def files_in_repo(repo_root): >@@ -129,7 +128,7 @@ def unexpected_changes(manifests, change_data, files_changed): > files_changed = set(files_changed) > > root_manifest = None >- for manifest, paths in iteritems(manifests): >+ for manifest, paths in manifests.items(): > if paths["url_base"] == "/": > root_manifest = manifest > break >@@ -170,9 +169,10 @@ class InternedData(object): > type_conv = None > rev_type_conv = None > >- def __init__(self, max_bits=8): >+ def __init__(self, max_bits: int = 8): > self.max_idx = 2**max_bits - 2 > # Reserve 0 as a sentinal >+ self._data: Tuple[List[object], Dict[int, object]] > self._data = [None], {} > > def clear(self): >@@ -240,7 +240,7 @@ def pack_result(data): > def unpack_result(data): > if isinstance(data, int): > return (status_intern.get(data), None) >- if isinstance(data, text_type): >+ if isinstance(data, str): > return (data, None) > # Unpack multiple statuses into a tuple to be used in the Results named tuple below, > # separating `status` and `known_intermittent`. >@@ -259,7 +259,7 @@ def load_test_data(test_paths): > manifests = manifest_loader.load() > > id_test_map = {} >- for test_manifest, paths in iteritems(manifests): >+ for test_manifest, paths in manifests.items(): > id_test_map.update(create_test_tree(paths["metadata_path"], > test_manifest)) > return id_test_map >@@ -287,10 +287,10 @@ def update_results(id_test_map, > disable_intermittent, > update_intermittent, > remove_intermittent): >- test_file_items = set(itervalues(id_test_map)) >+ test_file_items = set(id_test_map.values()) > > default_expected_by_type = {} >- for test_type, test_cls in iteritems(wpttest.manifest_test_cls): >+ for test_type, test_cls in wpttest.manifest_test_cls.items(): > if test_cls.result_cls: > default_expected_by_type[(test_type, False)] = test_cls.result_cls.default_expected > if test_cls.subtest_result_cls: >@@ -327,10 +327,7 @@ def write_new_expected(metadata_path, expected): > try: > with open(tmp_path, "wb") as f: > f.write(manifest_str.encode("utf8")) >- if sys.version_info >= (3, 3): >- os.replace(tmp_path, path) >- else: >- os.rename(tmp_path, path) >+ os.replace(tmp_path, path) > except (Exception, KeyboardInterrupt): > try: > os.unlink(tmp_path) >@@ -431,7 +428,7 @@ class ExpectedUpdater(object): > action_map["lsan_leak"](item) > > mozleak_data = data.get("mozleak", {}) >- for scope, scope_data in iteritems(mozleak_data): >+ for scope, scope_data in mozleak_data.items(): > for key, action in [("objects", "mozleak_object"), > ("total", "mozleak_total")]: > for item in scope_data.get(key, []): >@@ -668,11 +665,11 @@ class TestFileData(object): > # Return subtest nodes present in the expected file, but missing from the data > rv = [] > >- for test_id, subtests in iteritems(self.data): >+ for test_id, subtests in self.data.items(): > test = expected.get_test(ensure_text(test_id)) > if not test: > continue >- seen_subtests = set(ensure_text(item) for item in iterkeys(subtests) if item is not None) >+ seen_subtests = set(ensure_text(item) for item in subtests.keys() if item is not None) > missing_subtests = set(test.subtests.keys()) - seen_subtests > for item in missing_subtests: > expected_subtest = test.get_subtest(item) >@@ -691,7 +688,7 @@ class TestFileData(object): > # since removing these may be inappropriate > top_level_props, dependent_props = update_properties > all_properties = set(top_level_props) >- for item in itervalues(dependent_props): >+ for item in dependent_props.values(): > all_properties |= set(item) > > filtered = [] >@@ -741,9 +738,9 @@ class TestFileData(object): > test_expected = expected.get_test(test_id) > expected_by_test[test_id] = test_expected > >- for test_id, test_data in iteritems(self.data): >+ for test_id, test_data in self.data.items(): > test_id = ensure_str(test_id) >- for subtest_id, results_list in iteritems(test_data): >+ for subtest_id, results_list in test_data.items(): > for prop, run_info, value in results_list: > # Special case directory metadata > if subtest_id is None and test_id.endswith("__dir__"): >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/mpcontext.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/mpcontext.py >new file mode 100644 >index 000000000000..a50c04782a65 >--- /dev/null >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/mpcontext.py >@@ -0,0 +1,11 @@ >+import multiprocessing >+ >+_context = None >+ >+ >+def get_context(): >+ global _context >+ >+ if _context is None: >+ _context = multiprocessing.get_context("spawn") >+ return _context >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/process.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/process.py >deleted file mode 100644 >index 8a2b894cb74d..000000000000 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/process.py >+++ /dev/null >@@ -1,11 +0,0 @@ >-import sys >- >-import six >- >- >-def cast_env(env): >- """Encode all the environment values as the appropriate type for each Python version >- This assumes that all the data is or can be represented as UTF8""" >- >- env_type = six.ensure_binary if sys.version_info[0] < 3 else six.ensure_str >- return {env_type(key): env_type(value) for key, value in six.iteritems(env)} >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/products.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/products.py >index abd84094bb33..e65df5ee8993 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/products.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/products.py >@@ -1,6 +1,5 @@ > import importlib > import imp >-from six import iteritems > > from .browsers import product_list > >@@ -34,34 +33,29 @@ class Product(object): > module = product_module(config, product) > data = module.__wptrunner__ > self.name = product >+ if isinstance(data["browser"], str): >+ self._browser_cls = {None: getattr(module, data["browser"])} >+ else: >+ self._browser_cls = {key: getattr(module, value) >+ for key, value in data["browser"].items()} > self.check_args = getattr(module, data["check_args"]) >- self.browser_cls = getattr(module, data["browser"]) > self.get_browser_kwargs = getattr(module, data["browser_kwargs"]) > self.get_executor_kwargs = getattr(module, data["executor_kwargs"]) > self.env_options = getattr(module, data["env_options"])() > self.get_env_extras = getattr(module, data["env_extras"]) > self.run_info_extras = (getattr(module, data["run_info_extras"]) >- if "run_info_extras" in data else lambda **kwargs:{}) >+ if "run_info_extras" in data else lambda **kwargs:{}) > self.get_timeout_multiplier = getattr(module, data["timeout_multiplier"]) > > self.executor_classes = {} >- for test_type, cls_name in iteritems(data["executor"]): >+ for test_type, cls_name in data["executor"].items(): > cls = getattr(module, cls_name) > self.executor_classes[test_type] = cls > >- >-def load_product(config, product, load_cls=False): >- rv = Product(config, product) >- if not load_cls: >- return (rv.check_args, >- rv.browser_cls, >- rv.get_browser_kwargs, >- rv.executor_classes, >- rv.get_executor_kwargs, >- rv.env_options, >- rv.get_env_extras, >- rv.run_info_extras) >- return rv >+ def get_browser_cls(self, test_type): >+ if test_type in self._browser_cls: >+ return self._browser_cls[test_type] >+ return self._browser_cls[None] > > > def load_product_update(config, product): >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/stability.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/stability.py >index 88d1c232bfb5..eeb5af230029 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/stability.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/stability.py >@@ -5,7 +5,6 @@ import io > import os > from collections import OrderedDict, defaultdict > from datetime import datetime >-from six import iteritems > > from mozlog import reader > from mozlog.formatters import JSONFormatter >@@ -13,8 +12,8 @@ from mozlog.handlers import BaseHandler, StreamHandler, LogLevelFilter > > here = os.path.dirname(__file__) > localpaths = imp.load_source("localpaths", os.path.abspath(os.path.join(here, os.pardir, os.pardir, "localpaths.py"))) >-from ci.tc.github_checks_output import get_gh_checks_outputter >-from wpt.markdown import markdown_adjust, table >+from ci.tc.github_checks_output import get_gh_checks_outputter # type: ignore >+from wpt.markdown import markdown_adjust, table # type: ignore > > > # If a test takes more than (FLAKY_THRESHOLD*timeout) and does not consistently >@@ -22,7 +21,7 @@ from wpt.markdown import markdown_adjust, table > FLAKY_THRESHOLD = 0.8 > > >-class LogActionFilter(BaseHandler): >+class LogActionFilter(BaseHandler): # type: ignore > > """Handler that filters out messages not of a given set of actions. > >@@ -44,7 +43,7 @@ class LogActionFilter(BaseHandler): > return self.inner(item) > > >-class LogHandler(reader.LogHandler): >+class LogHandler(reader.LogHandler): # type: ignore > > """Handle updating test and subtest status in log. > >@@ -142,10 +141,10 @@ def process_results(log, iterations): > handler = LogHandler() > reader.handle_log(reader.read(log), handler) > results = handler.results >- for test_name, test in iteritems(results): >+ for test_name, test in results.items(): > if is_inconsistent(test["status"], iterations): > inconsistent.append((test_name, None, test["status"], [])) >- for subtest_name, subtest in iteritems(test["subtests"]): >+ for subtest_name, subtest in test["subtests"].items(): > if is_inconsistent(subtest["status"], iterations): > inconsistent.append((test_name, subtest_name, subtest["status"], subtest["messages"])) > >@@ -235,7 +234,7 @@ def write_results(log, results, iterations, pr_number=None, use_details=False): > "tests" if len(results) > 1 > else "test")) > >- for test_name, test in iteritems(results): >+ for test_name, test in results.items(): > baseurl = "http://w3c-test.org/submissions" > if "https" in os.path.splitext(test_name)[0].split(".")[1:]: > baseurl = "https://w3c-test.org/submissions" >@@ -305,7 +304,7 @@ def get_steps(logger, repeat_loop, repeat_restart, kwargs_extras): > for kwargs_extra in kwargs_extras: > if kwargs_extra: > flags_string = " with flags %s" % " ".join( >- "%s=%s" % item for item in iteritems(kwargs_extra)) >+ "%s=%s" % item for item in kwargs_extra.items()) > else: > flags_string = "" > >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testdriver-extra.js b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testdriver-extra.js >index 9c0c69af3a3f..842abf411f90 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testdriver-extra.js >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testdriver-extra.js >@@ -34,7 +34,7 @@ > } else if (data.type === "testdriver-complete") { > const cmd_id = data.cmd_id; > const [on_success, on_failure] = pending.get(cmd_id); >- pending.clear(cmd_id); >+ pending.delete(cmd_id); > const resolver = data.status === "success" ? on_success : on_failure; > resolver(data); > if (is_test_context) { >@@ -136,6 +136,9 @@ > if (testharness_context === null) { > throw new Error("Tried to run in a non-testharness window without a call to set_test_context"); > } >+ if (action_msg.context === null) { >+ action_msg.context = get_window_id(window); >+ } > cmd_id = ctx_cmd_id++; > action_msg.cmd_id = cmd_id; > window.test_driver.message_test({type: "testdriver-command", >@@ -169,6 +172,10 @@ > return create_action("click", {selector, context}); > }; > >+ window.test_driver_internal.delete_all_cookies = function(context=null) { >+ return create_action("delete_all_cookies", {context}); >+ }; >+ > window.test_driver_internal.send_keys = function(element, keys) { > const selector = get_selector(element); > const context = get_context(element); >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testharnessreport-servo.js b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testharnessreport-servo.js >index a10b69df5504..4a27dc27ef6f 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testharnessreport-servo.js >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testharnessreport-servo.js >@@ -1,4 +1,4 @@ >-var props = {output:%(output)d}; >+var props = {output:%(output)d, debug: %(debug)s}; > var start_loc = document.createElement('a'); > start_loc.href = location.href; > setup(props); >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testharnessreport-servodriver.js b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testharnessreport-servodriver.js >index c88841360470..7819538dbb8f 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testharnessreport-servodriver.js >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testharnessreport-servodriver.js >@@ -1,4 +1,4 @@ >-setup({output:%(output)d}); >+setup({output:%(output)d, debug: %(debug)s}); > > add_completion_callback(function() { > add_completion_callback(function (tests, status) { >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testharnessreport.js b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testharnessreport.js >index 9cd680e14aed..d385692445c5 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testharnessreport.js >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testharnessreport.js >@@ -73,6 +73,7 @@ window.__wptrunner_process_next_event = function() { > var props = {output: %(output)d, > timeout_multiplier: %(timeout_multiplier)s, > explicit_timeout: %(explicit_timeout)s, >+ debug: %(debug)s, > message_events: ["completion"]}; > > add_completion_callback(function(tests, harness_status) { >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testloader.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testloader.py >index 4a3fa4273654..9492a8bc38e6 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testloader.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testloader.py >@@ -1,16 +1,14 @@ > import hashlib > import json > import os >-from six.moves.urllib.parse import urlsplit >+from urllib.parse import urlsplit > from abc import ABCMeta, abstractmethod >-from six.moves.queue import Empty >+from queue import Empty > from collections import defaultdict, deque >-from multiprocessing import Queue >-from six import ensure_binary, iteritems >-from six.moves import range > > from . import manifestinclude > from . import manifestexpected >+from . import mpcontext > from . import wpttest > from mozlog import structured > >@@ -22,9 +20,9 @@ download_from_github = None > def do_delayed_imports(): > # This relies on an already loaded module having set the sys.path correctly :( > global manifest, manifest_update, download_from_github >- from manifest import manifest >+ from manifest import manifest # type: ignore > from manifest import update as manifest_update >- from manifest.download import download_from_github >+ from manifest.download import download_from_github # type: ignore > > > class TestGroupsFile(object): >@@ -41,7 +39,7 @@ class TestGroupsFile(object): > raise > > self.group_by_test = {} >- for group, test_ids in iteritems(self._data): >+ for group, test_ids in self._data.items(): > for test_id in test_ids: > self.group_by_test[test_id] = group > >@@ -51,6 +49,16 @@ class TestGroupsFile(object): > def __getitem__(self, key): > return self._data[key] > >+def read_include_from_file(file): >+ new_include = [] >+ with open(file) as f: >+ for line in f: >+ line = line.strip() >+ # Allow whole-line comments; >+ # fragments mean we can't have partial line #-based comments >+ if len(line) > 0 and not line.startswith("#"): >+ new_include.append(line) >+ return new_include > > def update_include_for_groups(test_groups, include): > if include is None: >@@ -93,7 +101,7 @@ class HashChunker(TestChunker): > def __call__(self, manifest): > chunk_index = self.chunk_number - 1 > for test_type, test_path, tests in manifest: >- h = int(hashlib.md5(ensure_binary(test_path)).hexdigest(), 16) >+ h = int(hashlib.md5(test_path.encode()).hexdigest(), 16) > if h % self.total_chunks == chunk_index: > yield test_type, test_path, tests > >@@ -112,7 +120,7 @@ class DirectoryHashChunker(TestChunker): > hash_path = os.path.sep.join(os.path.dirname(test_path).split(os.path.sep, depth)[:depth]) > else: > hash_path = os.path.dirname(test_path) >- h = int(hashlib.md5(ensure_binary(hash_path)).hexdigest(), 16) >+ h = int(hashlib.md5(hash_path.encode()).hexdigest(), 16) > if h % self.total_chunks == chunk_index: > yield test_type, test_path, tests > >@@ -173,7 +181,7 @@ class ManifestLoader(object): > > def load(self): > rv = {} >- for url_base, paths in iteritems(self.test_paths): >+ for url_base, paths in self.test_paths.items(): > manifest_file = self.load_manifest(url_base=url_base, > **paths) > path_data = {"url_base": url_base} >@@ -390,7 +398,8 @@ class GroupedSource(TestSource): > > @classmethod > def make_queue(cls, tests, **kwargs): >- test_queue = Queue() >+ mp = mpcontext.get_context() >+ test_queue = mp.Queue() > groups = [] > > state = {} >@@ -423,7 +432,8 @@ class GroupedSource(TestSource): > class SingleTestSource(TestSource): > @classmethod > def make_queue(cls, tests, **kwargs): >- test_queue = Queue() >+ mp = mpcontext.get_context() >+ test_queue = mp.Queue() > processes = kwargs["processes"] > queues = [deque([]) for _ in range(processes)] > metadatas = [cls.group_metadata(None) for _ in range(processes)] >@@ -467,9 +477,10 @@ class GroupFileTestSource(TestSource): > > ids_to_tests = {test.id: test for test in tests} > >- test_queue = Queue() >+ mp = mpcontext.get_context() >+ test_queue = mp.Queue() > >- for group_name, test_ids in iteritems(tests_by_group): >+ for group_name, test_ids in tests_by_group.items(): > group_metadata = {"scope": group_name} > group = deque() > >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testrunner.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testrunner.py >index 0bde5ba348ea..4da1255d13b7 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testrunner.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testrunner.py >@@ -1,14 +1,12 @@ >-from __future__ import unicode_literals >- >-import multiprocessing > import threading > import traceback >-from six.moves.queue import Empty >+from queue import Empty > from collections import namedtuple >-from multiprocessing import Process, current_process, Queue > > from mozlog import structuredlog, capture > >+from . import mpcontext >+ > # Special value used as a sentinal in various commands > Stop = object() > >@@ -24,30 +22,12 @@ def release_mozlog_lock(): > pass > > >-class MessageLogger(object): >- def __init__(self, message_func): >- self.send_message = message_func >- >- def _log_data(self, action, **kwargs): >- self.send_message("log", action, kwargs) >- >- def process_output(self, process, data, command): >- self._log_data("process_output", process=process, data=data, command=command) >- >+class LogMessageHandler: >+ def __init__(self, send_message): >+ self.send_message = send_message > >-def _log_func(level_name): >- def log(self, message): >- self._log_data(level_name.lower(), message=message) >- log.__doc__ = """Log a message with level %s >- >-:param message: The string message to log >-""" % level_name >- log.__name__ = str(level_name).lower() >- return log >- >-# Create all the methods on StructuredLog for debug levels >-for level_name in structuredlog.log_levels: >- setattr(MessageLogger, level_name.lower(), _log_func(level_name)) >+ def __call__(self, data): >+ self.send_message("log", data) > > > class TestRunner(object): >@@ -68,7 +48,7 @@ class TestRunner(object): > self.result_queue = result_queue > > self.executor = executor >- self.name = current_process().name >+ self.name = mpcontext.get_context().current_process().name > self.logger = logger > > def __enter__(self): >@@ -98,7 +78,12 @@ class TestRunner(object): > def run(self): > """Main loop accepting commands over the pipe and triggering > the associated methods""" >- self.setup() >+ try: >+ self.setup() >+ except Exception: >+ self.logger.warning("An error occured during executor setup:\n%s" % >+ traceback.format_exc()) >+ raise > commands = {"run_test": self.run_test, > "reset": self.reset, > "stop": self.stop, >@@ -153,7 +138,9 @@ def start_runner(runner_command_queue, runner_result_queue, > # in the logging module unlocked > release_mozlog_lock() > >- logger = MessageLogger(send_message) >+ proc_name = mpcontext.get_context().current_process().name >+ logger = structuredlog.StructuredLogger(proc_name) >+ logger.add_handler(LogMessageHandler(send_message)) > > with capture.CaptureIO(logger, capture_stdio): > try: >@@ -265,7 +252,7 @@ class BrowserManager(object): > > class _RunnerManagerState(object): > before_init = namedtuple("before_init", []) >- initializing = namedtuple("initializing_browser", >+ initializing = namedtuple("initializing", > ["test", "test_group", "group_metadata", "failure_count"]) > running = namedtuple("running", ["test", "test_group", "group_metadata"]) > restarting = namedtuple("restarting", ["test", "test_group", "group_metadata"]) >@@ -306,9 +293,11 @@ class TestRunnerManager(threading.Thread): > self.executor_cls = executor_cls > self.executor_kwargs = executor_kwargs > >+ mp = mpcontext.get_context() >+ > # Flags used to shut down this thread if we get a sigint > self.parent_stop_flag = stop_flag >- self.child_stop_flag = multiprocessing.Event() >+ self.child_stop_flag = mp.Event() > > self.rerun = rerun > self.run_count = 0 >@@ -321,8 +310,8 @@ class TestRunnerManager(threading.Thread): > assert recording is not None > self.recording = recording > >- self.command_queue = Queue() >- self.remote_queue = Queue() >+ self.command_queue = mp.Queue() >+ self.remote_queue = mp.Queue() > > self.test_runner_proc = None > >@@ -332,6 +321,7 @@ class TestRunnerManager(threading.Thread): > > self.test_count = 0 > self.unexpected_count = 0 >+ self.unexpected_pass_count = 0 > > # This may not really be what we want > self.daemon = True >@@ -354,7 +344,8 @@ class TestRunnerManager(threading.Thread): > spins.""" > self.recording.set(["testrunner", "startup"]) > self.logger = structuredlog.StructuredLogger(self.suite_name) >- with self.browser_cls(self.logger, **self.browser_kwargs) as browser: >+ with self.browser_cls(self.logger, remote_queue=self.command_queue, >+ **self.browser_kwargs) as browser: > self.browser = BrowserManager(self.logger, > browser, > self.command_queue, >@@ -494,6 +485,7 @@ class TestRunnerManager(threading.Thread): > self.state.failure_count + 1) > else: > self.executor_kwargs["group_metadata"] = self.state.group_metadata >+ self.executor_kwargs["browser_settings"] = self.browser.browser_settings > self.start_test_runner() > > def start_test_runner(self): >@@ -514,9 +506,11 @@ class TestRunnerManager(threading.Thread): > executor_browser_kwargs, > self.capture_stdio, > self.child_stop_flag) >- self.test_runner_proc = Process(target=start_runner, >- args=args, >- name="TestRunner-%i" % self.manager_number) >+ >+ mp = mpcontext.get_context() >+ self.test_runner_proc = mp.Process(target=start_runner, >+ args=args, >+ name="TestRunner-%i" % self.manager_number) > self.test_runner_proc.start() > self.logger.debug("Test runner started") > # Now we wait for either an init_succeeded event or an init_failed event >@@ -605,6 +599,7 @@ class TestRunnerManager(threading.Thread): > return > if self.timer is not None: > self.timer.cancel() >+ > # Write the result of each subtest > file_result, test_results = results > subtest_unexpected = False >@@ -625,6 +620,11 @@ class TestRunnerManager(threading.Thread): > self.unexpected_count += 1 > self.logger.debug("Unexpected count in this thread %i" % self.unexpected_count) > subtest_unexpected = True >+ >+ is_unexpected_pass = is_unexpected and result.status == "PASS" >+ if is_unexpected_pass: >+ self.unexpected_pass_count += 1 >+ > self.logger.test_status(test.id, > result.name, > result.status, >@@ -633,34 +633,40 @@ class TestRunnerManager(threading.Thread): > known_intermittent=known_intermittent, > stack=result.stack) > >- # We have a couple of status codes that are used internally, but not exposed to the >- # user. These are used to indicate that some possibly-broken state was reached >- # and we should restart the runner before the next test. >- # INTERNAL-ERROR indicates a Python exception was caught in the harness >- # EXTERNAL-TIMEOUT indicates we had to forcibly kill the browser from the harness >- # because the test didn't return a result after reaching the test-internal timeout >- status_subns = {"INTERNAL-ERROR": "ERROR", >- "EXTERNAL-TIMEOUT": "TIMEOUT"} > expected = test.expected() > known_intermittent = test.known_intermittent() >- status = status_subns.get(file_result.status, file_result.status) >+ status = file_result.status > > if self.browser.check_crash(test.id) and status != "CRASH": >- if test.test_type == "crashtest": >+ if test.test_type == "crashtest" or status == "EXTERNAL-TIMEOUT": > self.logger.info("Found a crash dump file; changing status to CRASH") > status = "CRASH" > else: > self.logger.warning("Found a crash dump; should change status from %s to CRASH but this causes instability" % (status,)) > >+ # We have a couple of status codes that are used internally, but not exposed to the >+ # user. These are used to indicate that some possibly-broken state was reached >+ # and we should restart the runner before the next test. >+ # INTERNAL-ERROR indicates a Python exception was caught in the harness >+ # EXTERNAL-TIMEOUT indicates we had to forcibly kill the browser from the harness >+ # because the test didn't return a result after reaching the test-internal timeout >+ status_subns = {"INTERNAL-ERROR": "ERROR", >+ "EXTERNAL-TIMEOUT": "TIMEOUT"} >+ status = status_subns.get(status, status) >+ > self.test_count += 1 > is_unexpected = expected != status and status not in known_intermittent > if is_unexpected: > self.unexpected_count += 1 > self.logger.debug("Unexpected count in this thread %i" % self.unexpected_count) > >+ is_unexpected_pass = is_unexpected and status == "OK" >+ if is_unexpected_pass: >+ self.unexpected_pass_count += 1 >+ > if "assertion_count" in file_result.extra: >- assertion_count = file_result.extra.pop("assertion_count") >- if assertion_count > 0: >+ assertion_count = file_result.extra["assertion_count"] >+ if assertion_count is not None and assertion_count > 0: > self.logger.assertion_count(test.id, > int(assertion_count), > test.min_assertion_count, >@@ -722,8 +728,8 @@ class TestRunnerManager(threading.Thread): > self.stop_runner() > return RunnerManagerState.initializing(self.state.test, self.state.test_group, self.state.group_metadata, 0) > >- def log(self, action, kwargs): >- getattr(self.logger, action)(**kwargs) >+ def log(self, data): >+ self.logger.log_raw(data) > > def error(self, message): > self.logger.error(message) >@@ -761,20 +767,26 @@ class TestRunnerManager(threading.Thread): > self.logger.debug("waiting for runner process to end") > self.test_runner_proc.join(10) > self.logger.debug("After join") >+ mp = mpcontext.get_context() > if self.test_runner_proc.is_alive(): > # This might leak a file handle from the queue > self.logger.warning("Forcibly terminating runner process") > self.test_runner_proc.terminate() >+ self.logger.debug("After terminating runner process") > > # Multiprocessing queues are backed by operating system pipes. If > # the pipe in the child process had buffered data at the time of > # forced termination, the queue is no longer in a usable state > # (subsequent attempts to retrieve items may block indefinitely). > # Discard the potentially-corrupted queue and create a new one. >+ self.logger.debug("Recreating command queue") >+ self.command_queue.cancel_join_thread() > self.command_queue.close() >- self.command_queue = Queue() >+ self.command_queue = mp.Queue() >+ self.logger.debug("Recreating remote queue") >+ self.remote_queue.cancel_join_thread() > self.remote_queue.close() >- self.remote_queue = Queue() >+ self.remote_queue = mp.Queue() > else: > self.logger.debug("Runner process exited with code %i" % self.test_runner_proc.exitcode) > >@@ -915,3 +927,6 @@ class ManagerGroup(object): > > def unexpected_count(self): > return sum(manager.unexpected_count for manager in self.pool) >+ >+ def unexpected_pass_count(self): >+ return sum(manager.unexpected_pass_count for manager in self.pool) >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/browsers/test_sauce.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/browsers/test_sauce.py >index e1e18ec5e9cd..3245d125c534 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/browsers/test_sauce.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/browsers/test_sauce.py >@@ -1,6 +1,7 @@ >+import logging > import sys >+from unittest import mock > >-import mock > import pytest > > from os.path import join, dirname >@@ -11,6 +12,8 @@ sauce = pytest.importorskip("wptrunner.browsers.sauce") > > from wptserve.config import ConfigBuilder > >+logger = logging.getLogger() >+ > > def test_sauceconnect_success(): > with mock.patch.object(sauce.SauceConnect, "upload_prerun_exec"),\ >@@ -29,7 +32,7 @@ def test_sauceconnect_success(): > sauce_connect_binary="ddd", > sauce_connect_args=[]) > >- with ConfigBuilder(browser_host="example.net") as env_config: >+ with ConfigBuilder(logger, browser_host="example.net") as env_config: > sauce_connect(None, env_config) > with sauce_connect: > pass >@@ -59,7 +62,7 @@ def test_sauceconnect_failure_exit(readyfile, returncode): > sauce_connect_binary="ddd", > sauce_connect_args=[]) > >- with ConfigBuilder(browser_host="example.net") as env_config: >+ with ConfigBuilder(logger, browser_host="example.net") as env_config: > sauce_connect(None, env_config) > with pytest.raises(sauce.SauceException): > with sauce_connect: >@@ -88,7 +91,7 @@ def test_sauceconnect_cleanup(): > sauce_connect_binary="ddd", > sauce_connect_args=[]) > >- with ConfigBuilder(browser_host="example.net") as env_config: >+ with ConfigBuilder(logger, browser_host="example.net") as env_config: > sauce_connect(None, env_config) > with sauce_connect: > Popen.return_value.poll.return_value = None >@@ -112,7 +115,7 @@ def test_sauceconnect_failure_never_ready(): > sauce_connect_binary="ddd", > sauce_connect_args=[]) > >- with ConfigBuilder(browser_host="example.net") as env_config: >+ with ConfigBuilder(logger, browser_host="example.net") as env_config: > sauce_connect(None, env_config) > with pytest.raises(sauce.SauceException): > with sauce_connect: >@@ -141,7 +144,8 @@ def test_sauceconnect_tunnel_domains(): > sauce_connect_binary="ddd", > sauce_connect_args=[]) > >- with ConfigBuilder(browser_host="example.net", >+ with ConfigBuilder(logger, >+ browser_host="example.net", > alternate_hosts={"alt": "example.org"}, > subdomains={"a", "b"}, > not_subdomains={"x", "y"}) as env_config: >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/browsers/test_webkitgtk.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/browsers/test_webkitgtk.py >index 6150d50acf93..b20d3576674e 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/browsers/test_webkitgtk.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/browsers/test_webkitgtk.py >@@ -1,3 +1,4 @@ >+import logging > from os.path import join, dirname > > import pytest >@@ -9,6 +10,8 @@ from wptrunner import environment, products > test_paths = {"/": {"tests_path": join(dirname(__file__), "..", "..", "..", "..", "..")}} # repo root > environment.do_delayed_imports(None, test_paths) > >+logger = logging.getLogger() >+ > > @active_products("product") > def test_webkitgtk_certificate_domain_list(product): >@@ -22,10 +25,7 @@ def test_webkitgtk_certificate_domain_list(product): > if product not in ["epiphany", "webkit", "webkitgtk_minibrowser"]: > pytest.skip("%s doesn't support certificate_domain_list" % product) > >- (check_args, >- target_browser_cls, get_browser_kwargs, >- executor_classes, get_executor_kwargs, >- env_options, get_env_extras, run_info_extras) = products.load_product({}, product) >+ product_data = products.Product({}, product) > > cert_file = "/home/user/wpt/tools/certs/cacert.pem" > valid_domains_test = ["a.example.org", "b.example.org", "example.org", >@@ -41,12 +41,25 @@ def test_webkitgtk_certificate_domain_list(product): > kwargs["webdriver_binary"] = None > kwargs["pause_after_test"] = False > kwargs["pause_on_unexpected"] = False >- with ConfigBuilder(browser_host="example.net", >+ kwargs["debug_test"] = False >+ with ConfigBuilder(logger, >+ browser_host="example.net", > alternate_hosts={"alt": "example.org"}, > subdomains={"a", "b"}, > not_subdomains={"x", "y"}) as env_config: > >- executor_args = get_executor_kwargs(None, env_config, None, None, **kwargs) >+ # We don't want to actually create a test environment; the get_executor_kwargs >+ # function only really wants an object with the config key >+ >+ class MockEnvironment: >+ def __init__(self, config): >+ self.config = config >+ >+ executor_args = product_data.get_executor_kwargs(None, >+ None, >+ MockEnvironment(env_config), >+ {}, >+ **kwargs) > assert('capabilities' in executor_args) > assert('webkitgtk:browserOptions' in executor_args['capabilities']) > assert('certificates' in executor_args['capabilities']['webkitgtk:browserOptions']) >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/test_formatters.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/test_formatters.py >index 60b1b0a5a878..04526da702ba 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/test_formatters.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/test_formatters.py >@@ -1,12 +1,9 @@ > import json > import time >-from six.moves import cStringIO as StringIO >- >-import mock >+from io import StringIO > > from mozlog import handlers, structuredlog > >-from ..formatters import wptreport > from ..formatters.wptscreenshot import WptscreenshotFormatter > from ..formatters.wptreport import WptreportFormatter > >@@ -97,45 +94,6 @@ def test_wptreport_lone_surrogate(capfd): > assert subtest["message"] == u"\U0001F601 U+de0aU+d83d" > > >-def test_wptreport_lone_surrogate_ucs2(capfd): >- # Since UCS4 is a superset of UCS2 we can meaningfully test the UCS2 code on a >- # UCS4 build, but not the reverse. However UCS2 is harder to handle and UCS4 is >- # the commonest (and sensible) configuration, so that's OK. >- output = StringIO() >- logger = structuredlog.StructuredLogger("test_a") >- logger.add_handler(handlers.StreamHandler(output, WptreportFormatter())) >- >- with mock.patch.object(wptreport, >- 'surrogate_replacement', >- wptreport.SurrogateReplacementUcs2()): >- # output a bunch of stuff >- logger.suite_start(["test-id-1"]) # no run_info arg! >- logger.test_start("test-id-1") >- logger.test_status("test-id-1", >- subtest=u"Name with surrogate\uD800", >- status="FAIL", >- message=u"\U0001F601 \uDE0A\uD83D \uD83D\uDE0A") >- logger.test_end("test-id-1", >- status="PASS", >- message=u"\uDE0A\uD83D \uD83D\uDE0A \U0001F601") >- logger.suite_end() >- >- # check nothing got output to stdout/stderr >- # (note that mozlog outputs exceptions during handling to stderr!) >- captured = capfd.readouterr() >- assert captured.out == "" >- assert captured.err == "" >- >- # check the actual output of the formatter >- output.seek(0) >- output_obj = json.load(output) >- test = output_obj["results"][0] >- assert test["message"] == u"U+de0aU+d83d \U0001f60a \U0001F601" >- subtest = test["subtests"][0] >- assert subtest["name"] == u"Name with surrogateU+d800" >- assert subtest["message"] == u"\U0001F601 U+de0aU+d83d \U0001f60a" >- >- > def test_wptreport_known_intermittent(capfd): > output = StringIO() > logger = structuredlog.StructuredLogger("test_a") >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/test_products.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/test_products.py >index 5d2bcf075d0f..120c7e72c63f 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/test_products.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/test_products.py >@@ -1,6 +1,6 @@ > from os.path import join, dirname >+from unittest import mock > >-import mock > import pytest > > from .base import all_products, active_products >@@ -14,7 +14,7 @@ environment.do_delayed_imports(None, test_paths) > @active_products("product") > def test_load_active_product(product): > """test we can successfully load the product of the current testenv""" >- products.load_product({}, product) >+ products.Product({}, product) > # test passes if it doesn't throw > > >@@ -22,7 +22,7 @@ def test_load_active_product(product): > def test_load_all_products(product): > """test every product either loads or throws ImportError""" > try: >- products.load_product({}, product) >+ products.Product({}, product) > except ImportError: > pass > >@@ -31,26 +31,24 @@ def test_load_all_products(product): > "sauce": pytest.mark.skip("needs env extras kwargs"), > }) > def test_server_start_config(product): >- (check_args, >- target_browser_cls, get_browser_kwargs, >- executor_classes, get_executor_kwargs, >- env_options, get_env_extras, run_info_extras) = products.load_product({}, product) >+ product_data = products.Product({}, product) > >- env_extras = get_env_extras() >+ env_extras = product_data.get_env_extras() > > with mock.patch.object(environment.serve, "start") as start: > with environment.TestEnvironment(test_paths, > 1, > False, >+ False, > None, >- env_options, >+ product_data.env_options, > {"type": "none"}, > env_extras): > start.assert_called_once() > args = start.call_args >- config = args[0][0] >- if "server_host" in env_options: >- assert config["server_host"] == env_options["server_host"] >+ config = args[0][1] >+ if "server_host" in product_data.env_options: >+ assert config["server_host"] == product_data.env_options["server_host"] > else: > assert config["server_host"] == config["browser_host"] > assert isinstance(config["bind_address"], bool) >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/test_testloader.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/test_testloader.py >index 394d2a00177b..77cd85b820d7 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/test_testloader.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/test_testloader.py >@@ -1,5 +1,3 @@ >-from __future__ import unicode_literals >- > import os > import sys > import tempfile >@@ -8,6 +6,7 @@ import pytest > > from mozlog import structured > from ..testloader import TestFilter as Filter, TestLoader as Loader >+from ..testloader import read_include_from_file > from .test_wpttest import make_mock_manifest > > here = os.path.dirname(__file__) >@@ -58,6 +57,28 @@ def test_loader_h2_tests(): > assert len(loader.disabled_tests["testharness"]) == 1 > assert loader.disabled_tests["testharness"][0].url == "/a/bar.h2.html" > >+@pytest.mark.xfail(sys.platform == "win32", >+ reason="NamedTemporaryFile cannot be reopened on Win32") >+def test_include_file(): >+ test_cases = """ >+# This is a comment >+/foo/bar-error.https.html >+/foo/bar-success.https.html >+/foo/idlharness.https.any.html >+/foo/idlharness.https.any.worker.html >+ """ >+ >+ with tempfile.NamedTemporaryFile(mode="wt") as f: >+ f.write(test_cases) >+ f.flush() >+ >+ include = read_include_from_file(f.name) >+ >+ assert len(include) == 4 >+ assert "/foo/bar-error.https.html" in include >+ assert "/foo/bar-success.https.html" in include >+ assert "/foo/idlharness.https.any.html" in include >+ assert "/foo/idlharness.https.any.worker.html" in include > > @pytest.mark.xfail(sys.platform == "win32", > reason="NamedTemporaryFile cannot be reopened on Win32") >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/test_update.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/test_update.py >index a1ba8de28780..804f75125180 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/test_update.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/test_update.py >@@ -1,11 +1,12 @@ > import json >-import mock > import os >-import pytest > import sys > from io import BytesIO >+from unittest import mock >+ >+import pytest > >-from .. import metadata, manifestupdate >+from .. import metadata, manifestupdate, wptmanifest > from ..update.update import WPTUpdate > from ..update.base import StepRunner, Step > from mozlog import structuredlog, handlers, formatters >@@ -442,7 +443,7 @@ def test_update_reorder_expected(): > > assert not new_manifest.is_empty > assert new_manifest.get_test(test_id).children[0].get( >- "expected", default_run_info) == ["FAIL", "PASS"] >+ "expected", default_run_info) == ["PASS", "FAIL"] > > > def test_update_and_preserve_unchanged_expected_intermittent(): >@@ -1672,3 +1673,105 @@ def test_update_pickle(): > wptupdate = WPTUpdate(logger, **args2) > wptupdate = WPTUpdate(logger, runner_cls=UpdateRunner, **args) > wptupdate.run() >+ >+ >+def test_update_serialize_quoted(): >+ tests = [("path/to/test.htm", [test_id], "testharness", >+ b"""[test.htm] >+ expected: "ERROR" >+ [test1] >+ expected: >+ if os == "linux": ["PASS", "FAIL"] >+ "ERROR" >+""")] >+ >+ log_0 = suite_log([("test_start", {"test": test_id}), >+ ("test_status", {"test": test_id, >+ "subtest": "test1", >+ "status": "PASS", >+ "known_intermittent": ["FAIL"]}), >+ ("test_end", {"test": test_id, >+ "expected": "ERROR", >+ "status": "OK"})], >+ run_info={"os": "linux"}) >+ log_1 = suite_log([("test_start", {"test": test_id}), >+ ("test_status", {"test": test_id, >+ "subtest": "test1", >+ "status": "FAIL", >+ "expected": "PASS", >+ "known_intermittent": ["FAIL"]}), >+ ("test_end", {"test": test_id, >+ "expected": "ERROR", >+ "status": "OK"})], >+ run_info={"os": "linux"}) >+ log_2 = suite_log([("test_start", {"test": test_id}), >+ ("test_status", {"test": test_id, >+ "subtest": "test1", >+ "status": "ERROR"}), >+ ("test_end", {"test": test_id, >+ "expected": "ERROR", >+ "status": "OK"})], >+ run_info={"os": "win"}) >+ >+ updated = update(tests, log_0, log_1, log_2, full_update=True, update_intermittent=True) >+ >+ >+ manifest_str = wptmanifest.serialize(updated[0][1].node, >+ skip_empty_data=True) >+ assert manifest_str == """[test.htm] >+ [test1] >+ expected: >+ if os == "linux": [PASS, FAIL] >+ ERROR >+""" >+ >+ >+def test_update_serialize_unquoted(): >+ tests = [("path/to/test.htm", [test_id], "testharness", >+ b"""[test.htm] >+ expected: ERROR >+ [test1] >+ expected: >+ if os == "linux": [PASS, FAIL] >+ ERROR >+""")] >+ >+ log_0 = suite_log([("test_start", {"test": test_id}), >+ ("test_status", {"test": test_id, >+ "subtest": "test1", >+ "status": "PASS", >+ "known_intermittent": ["FAIL"]}), >+ ("test_end", {"test": test_id, >+ "expected": "ERROR", >+ "status": "OK"})], >+ run_info={"os": "linux"}) >+ log_1 = suite_log([("test_start", {"test": test_id}), >+ ("test_status", {"test": test_id, >+ "subtest": "test1", >+ "status": "FAIL", >+ "expected": "PASS", >+ "known_intermittent": ["FAIL"]}), >+ ("test_end", {"test": test_id, >+ "expected": "ERROR", >+ "status": "OK"})], >+ run_info={"os": "linux"}) >+ log_2 = suite_log([("test_start", {"test": test_id}), >+ ("test_status", {"test": test_id, >+ "subtest": "test1", >+ "status": "ERROR"}), >+ ("test_end", {"test": test_id, >+ "expected": "ERROR", >+ "status": "OK"})], >+ run_info={"os": "win"}) >+ >+ updated = update(tests, log_0, log_1, log_2, full_update=True, update_intermittent=True) >+ >+ >+ manifest_str = wptmanifest.serialize(updated[0][1].node, >+ skip_empty_data=True) >+ assert manifest_str == """[test.htm] >+ [test1] >+ expected: >+ if os == "linux": [PASS, FAIL] >+ ERROR >+""" >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/test_wpttest.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/test_wpttest.py >index 4eee9ccb1b69..59aa636a9037 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/test_wpttest.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/tests/test_wpttest.py >@@ -1,5 +1,5 @@ >-import mock > from io import BytesIO >+from unittest import mock > > from manifest import manifest as wptmanifest > from manifest.item import TestharnessTest, RefTest >@@ -202,14 +202,16 @@ def test_expect_any_subtest_status(): > > > def test_metadata_fuzzy(): >- item = RefTest(".", "a/fuzzy.html", "/", "a/fuzzy.html", >+ item = RefTest(tests_root=".", >+ path="a/fuzzy.html", >+ url_base="/", >+ url="a/fuzzy.html", > references=[["/a/fuzzy-ref.html", "=="]], > fuzzy=[[["/a/fuzzy.html", '/a/fuzzy-ref.html', '=='], > [[2, 3], [10, 15]]]]) > s = mock.Mock(rel_path="a/fuzzy.html", rel_path_parts=("a", "fuzzy.html"), hash="0"*40) > s.manifest_items = mock.Mock(return_value=(item.item_type, [item])) > >- > manifest = wptmanifest.Manifest("") > > tree, sourcefile_mock = tree_and_sourcefile_mocks([(s, None, True)]) >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/update/base.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/update/base.py >index 547808e52933..47ea7ffb41f0 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/update/base.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/update/base.py >@@ -1,9 +1,11 @@ >+from typing import ClassVar, List, Type >+ > exit_unclean = object() > exit_clean = object() > > > class Step(object): >- provides = [] >+ provides = [] # type: ClassVar[List[str]] > > def __init__(self, logger): > self.logger = logger >@@ -45,7 +47,7 @@ class Step(object): > > > class StepRunner(object): >- steps = [] >+ steps = [] # type: ClassVar[List[Type[Step]]] > > def __init__(self, logger, state): > """Class that runs a specified series of Steps with a common State""" >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/update/state.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/update/state.py >index bdb23fed99e1..a526c24c959f 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/update/state.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/update/state.py >@@ -1,5 +1,5 @@ > import os >-from six.moves import cPickle as pickle # noqa: N813 >+import pickle > > here = os.path.abspath(os.path.dirname(__file__)) > >@@ -97,7 +97,7 @@ class SavedState(BaseState): > try: > if not os.path.isfile(cls.filename): > return None >- with open(cls.filename) as f: >+ with open(cls.filename, "rb") as f: > try: > rv = pickle.load(f) > logger.debug("Loading data %r" % (rv._data,)) >@@ -111,7 +111,7 @@ class SavedState(BaseState): > > def save(self): > """Write the state to disk""" >- with open(self.filename, "w") as f: >+ with open(self.filename, "wb") as f: > pickle.dump(self, f) > > def clear(self): >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/update/sync.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/update/sync.py >index f878752f4514..2c9834c672d6 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/update/sync.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/update/sync.py >@@ -67,7 +67,7 @@ class UpdateCheckout(Step): > state.local_branch) > sync_path = os.path.abspath(sync_tree.root) > if sync_path not in sys.path: >- from update import setup_paths >+ from .update import setup_paths > setup_paths(sync_path) > > def restore(self, state): >@@ -98,7 +98,7 @@ class UpdateManifest(Step): > provides = ["manifest_path", "test_manifest"] > > def create(self, state): >- from manifest import manifest >+ from manifest import manifest # type: ignore > state.manifest_path = os.path.join(state.metadata_path, "MANIFEST.json") > state.test_manifest = manifest.load_and_update(state.sync["path"], > state.manifest_path, >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/update/tree.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/update/tree.py >index f362770ff16d..2f43bacd15bf 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/update/tree.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/update/tree.py >@@ -3,8 +3,6 @@ import re > import subprocess > import tempfile > >-from six.moves import range >- > from .. import vcs > from ..vcs import git, hg > >@@ -87,7 +85,7 @@ class HgTree(object): > > @property > def is_clean(self): >- return self.hg("status").strip() == "" >+ return self.hg("status").strip() == b"" > > def add_new(self, prefix=None): > if prefix is not None: >@@ -105,7 +103,7 @@ class HgTree(object): > except subprocess.CalledProcessError: > pass > >- patch_names = [item.strip() for item in self.hg("qseries").split("\n") if item.strip()] >+ patch_names = [item.strip() for item in self.hg("qseries").split(b"\n") if item.strip()] > > suffix = 0 > test_name = patch_name >@@ -142,7 +140,7 @@ class GitTree(object): > > def __init__(self, root=None, log_error=True): > if root is None: >- root = git("rev-parse", "--show-toplevel", log_error=log_error).strip() >+ root = git("rev-parse", "--show-toplevel", log_error=log_error).strip().decode('utf-8') > self.root = root > self.git = vcs.bind_to_repo(git, self.root, log_error=log_error) > self.message = None >@@ -178,7 +176,7 @@ class GitTree(object): > > @property > def is_clean(self): >- return self.git("status").strip() == "" >+ return self.git("status").strip() == b"" > > def add_new(self, prefix=None): > """Add files to the staging area. >@@ -203,7 +201,7 @@ class GitTree(object): > f.seek(0) > ignored_files = sync_tree.git("check-ignore", "--no-index", "--stdin", "-z", stdin=f) > args = [] >- for entry in ignored_files.split('\0'): >+ for entry in ignored_files.decode('utf-8').split('\0'): > args.append(os.path.join(prefix, entry)) > if args: > self.git("add", "--force", *args) >@@ -219,7 +217,7 @@ class GitTree(object): > args.append(ref_filter) > data = self.git("show-ref", *args) > rv = [] >- for line in data.split("\n"): >+ for line in data.split(b"\n"): > if not line.strip(): > continue > sha1, ref = line.split() >@@ -237,7 +235,7 @@ class GitTree(object): > args.append(ref_filter) > data = self.git("ls-remote", remote, *args) > rv = [] >- for line in data.split("\n"): >+ for line in data.split(b"\n"): > if not line.strip(): > continue > sha1, ref = line.split() >@@ -250,8 +248,8 @@ class GitTree(object): > :param remote: the remote URL > :param branch: the branch name""" > for sha1, ref in self.list_remote(remote, branch): >- if ref == "refs/heads/%s" % branch: >- return self.commit_cls(self, sha1) >+ if ref.decode('utf-8') == "refs/heads/%s" % branch: >+ return self.commit_cls(self, sha1.decode('utf-8')) > assert False > > def create_patch(self, patch_name, message): >@@ -299,8 +297,8 @@ class GitTree(object): > > args = [] > if branch: >- branches = [ref[len("refs/heads/"):] for sha1, ref in self.list_refs() >- if ref.startswith("refs/heads/")] >+ branches = [ref[len("refs/heads/"):].decode('utf-8') for sha1, ref in self.list_refs() >+ if ref.startswith(b"refs/heads/")] > branch = get_unique_name(branches, branch) > > args += ["-b", branch] >@@ -336,8 +334,8 @@ class GitTree(object): > rv = [] > > for repo_path in repo_paths: >- paths = vcs.git("ls-tree", "-r", "--name-only", "HEAD", repo=repo_path).split("\n") >- rv.extend(os.path.relpath(os.path.join(repo_path, item), self.root) for item in paths >+ paths = vcs.git("ls-tree", "-r", "--name-only", "HEAD", repo=repo_path).split(b"\n") >+ rv.extend(os.path.relpath(os.path.join(repo_path, item.decode('utf-8')), self.root) for item in paths > if item.strip()) > return rv > >@@ -345,11 +343,11 @@ class GitTree(object): > """List submodule directories""" > output = self.git("submodule", "status", "--recursive") > rv = [] >- for line in output.split("\n"): >+ for line in output.split(b"\n"): > line = line.strip() > if not line: > continue >- parts = line.split(" ") >+ parts = line.split(b" ") > rv.append(parts[1]) > return rv > >@@ -403,5 +401,5 @@ class Commit(object): > self.git = self.tree.git > > def _get_meta(self): >- author, email, message = self.git("show", "-s", "--format=format:%an\n%ae\n%B", self.sha1).split("\n", 2) >+ author, email, message = self.git("show", "-s", "--format=format:%an\n%ae\n%B", self.sha1).decode('utf-8').split("\n", 2) > return author, email, self.msg_cls(message) >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/update/update.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/update/update.py >index 80e509dd383b..265a3311beb1 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/update/update.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/update/update.py >@@ -1,8 +1,6 @@ > import os > import sys > >-from six import itervalues >- > from .metadata import MetadataUpdateRunner > from .sync import SyncFromUpstreamRunner > from .tree import GitTree, HgTree, NoVCSTree >@@ -111,7 +109,7 @@ class RemoveObsolete(Step): > state.tests_path = state.paths["/"]["tests_path"] > state.metadata_path = state.paths["/"]["metadata_path"] > >- for url_paths in itervalues(paths): >+ for url_paths in paths.values(): > tests_path = url_paths["tests_path"] > metadata_path = url_paths["metadata_path"] > for dirpath, dirnames, filenames in os.walk(metadata_path): >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/vcs.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/vcs.py >index 896d8a0d2c6e..3419a507dbae 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/vcs.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/vcs.py >@@ -1,5 +1,6 @@ > import subprocess > from functools import partial >+from typing import Callable > > from mozlog import get_default_logger > >@@ -7,7 +8,7 @@ from wptserve.utils import isomorphic_decode > > logger = None > >-def vcs(bin_name): >+def vcs(bin_name: str) -> Callable[..., None]: > def inner(command, *args, **kwargs): > global logger > >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/webdriver_server.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/webdriver_server.py >index dfae057fd332..565e3d4e43be 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/webdriver_server.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/webdriver_server.py >@@ -5,14 +5,15 @@ import platform > import socket > import time > import traceback >+from typing import ClassVar, Type > > import mozprocess > >-from .process import cast_env >+from .browsers.base import OutputHandler > > > __all__ = ["SeleniumServer", "ChromeDriverServer", "CWTChromeDriverServer", >- "EdgeChromiumDriverServer", "OperaDriverServer", "GeckoDriverServer", >+ "EdgeChromiumDriverServer", "OperaDriverServer", > "InternetExplorerDriverServer", "EdgeDriverServer", > "ServoDriverServer", "WebKitDriverServer", "WebDriverServer"] > >@@ -21,6 +22,7 @@ class WebDriverServer(object): > __metaclass__ = abc.ABCMeta > > default_base_path = "/" >+ output_handler_cls = OutputHandler # type: ClassVar[Type[OutputHandler]] > > def __init__(self, logger, binary, host="127.0.0.1", port=None, > base_path="", env=None, args=None): >@@ -31,12 +33,14 @@ class WebDriverServer(object): > self.logger = logger > self.binary = binary > self.host = host >+ > if base_path == "": > self.base_path = self.default_base_path > else: > self.base_path = base_path > self.env = os.environ.copy() if env is None else env > >+ self._output_handler = None > self._port = port > self._cmd = None > self._args = args if args is not None else [] >@@ -46,18 +50,28 @@ class WebDriverServer(object): > def make_command(self): > """Returns the full command for starting the server process as a list.""" > >- def start(self, block=False): >+ def start(self, >+ block=False, >+ output_handler_kwargs=None, >+ output_handler_start_kwargs=None): > try: >- self._run(block) >+ self._run(block, output_handler_kwargs, output_handler_start_kwargs) > except KeyboardInterrupt: > self.stop() > >- def _run(self, block): >+ def _run(self, block, output_handler_kwargs, output_handler_start_kwargs): >+ if output_handler_kwargs is None: >+ output_handler_kwargs = {} >+ if output_handler_start_kwargs is None: >+ output_handler_start_kwargs = {} > self._cmd = self.make_command() >+ self._output_handler = self.output_handler_cls(self.logger, >+ self._cmd, >+ **output_handler_kwargs) > self._proc = mozprocess.ProcessHandler( > self._cmd, >- processOutputLine=self.on_output, >- env=cast_env(self.env), >+ processOutputLine=self._output_handler, >+ env=self.env, > storeOutput=False) > > self.logger.debug("Starting WebDriver: %s" % ' '.join(self._cmd)) >@@ -68,6 +82,7 @@ class WebDriverServer(object): > raise IOError( > "WebDriver executable not found: %s" % self.binary) > raise >+ self._output_handler.after_process_start(self._proc.pid) > > self.logger.debug( > "Waiting for WebDriver to become accessible: %s" % self.url) >@@ -78,27 +93,28 @@ class WebDriverServer(object): > "WebDriver was not accessible " > "within the timeout:\n%s" % traceback.format_exc()) > raise >- >+ self._output_handler.start(**output_handler_start_kwargs) > if block: > self._proc.wait() > > def stop(self, force=False): > self.logger.debug("Stopping WebDriver") >+ clean = True > if self.is_alive(): > kill_result = self._proc.kill() > if force and kill_result != 0: >- return self._proc.kill(9) >- return kill_result >- return not self.is_alive() >+ clean = False >+ self._proc.kill(9) >+ success = not self.is_alive() >+ if success and self._output_handler is not None: >+ # Only try to do output post-processing if we managed to shut down >+ self._output_handler.after_process_stop(clean) >+ self._output_handler = None >+ return success > > def is_alive(self): > return hasattr(self._proc, "proc") and self._proc.poll() is None > >- def on_output(self, line): >- self.logger.process_output(self.pid, >- line.decode("utf8", "replace"), >- command=" ".join(self._cmd)) >- > @property > def pid(self): > if self._proc is not None: >@@ -123,88 +139,42 @@ class SeleniumServer(WebDriverServer): > > > class ChromeDriverServer(WebDriverServer): >- def __init__(self, logger, binary="chromedriver", port=None, >- base_path="", args=None): >- WebDriverServer.__init__( >- self, logger, binary, port=port, base_path=base_path, args=args) >- > def make_command(self): > return [self.binary, > cmd_arg("port", str(self.port)), > cmd_arg("url-base", self.base_path) if self.base_path else "", > cmd_arg("enable-chrome-logs")] + self._args > >-class CWTChromeDriverServer(WebDriverServer): >- def __init__(self, logger, binary, port=None, args=None): >- WebDriverServer.__init__(self, logger, binary, port=port, args=args) > >+class CWTChromeDriverServer(WebDriverServer): > def make_command(self): > return [self.binary, > "--port=%s" % str(self.port)] + self._args > >-class EdgeChromiumDriverServer(WebDriverServer): >- def __init__(self, logger, binary="msedgedriver", port=None, >- base_path="", args=None): >- WebDriverServer.__init__( >- self, logger, binary, port=port, base_path=base_path, args=args) > >+class EdgeChromiumDriverServer(WebDriverServer): > def make_command(self): > return [self.binary, > cmd_arg("port", str(self.port)), > cmd_arg("url-base", self.base_path) if self.base_path else ""] + self._args > >-class EdgeDriverServer(WebDriverServer): >- def __init__(self, logger, binary="microsoftwebdriver.exe", port=None, >- base_path="", host="localhost", args=None): >- WebDriverServer.__init__( >- self, logger, binary, host=host, port=port, args=args) > >+class EdgeDriverServer(WebDriverServer): > def make_command(self): > return [self.binary, > "--port=%s" % str(self.port)] + self._args > >-class OperaDriverServer(ChromeDriverServer): >- def __init__(self, logger, binary="operadriver", port=None, >- base_path="", args=None): >- ChromeDriverServer.__init__( >- self, logger, binary, port=port, base_path=base_path, args=args) > >+class OperaDriverServer(ChromeDriverServer): >+ pass > > class InternetExplorerDriverServer(WebDriverServer): >- def __init__(self, logger, binary="IEDriverServer.exe", port=None, >- base_path="", host="localhost", args=None): >- WebDriverServer.__init__( >- self, logger, binary, host=host, port=port, args=args) >- > def make_command(self): > return [self.binary, > "--port=%s" % str(self.port)] + self._args > > >-class GeckoDriverServer(WebDriverServer): >- def __init__(self, logger, marionette_port=2828, binary="geckodriver", >- host="127.0.0.1", port=None, args=None): >- env = os.environ.copy() >- env["RUST_BACKTRACE"] = "1" >- WebDriverServer.__init__(self, logger, binary, >- host=host, >- port=port, >- env=cast_env(env), >- args=args) >- self.marionette_port = marionette_port >- >- def make_command(self): >- return [self.binary, >- "--marionette-port", str(self.marionette_port), >- "--host", self.host, >- "--port", str(self.port)] + self._args >- >- > class SafariDriverServer(WebDriverServer): >- def __init__(self, logger, binary="safaridriver", port=None, args=None): >- WebDriverServer.__init__( >- self, logger, binary, port=port, args=args) >- > def make_command(self): > return [self.binary, > "--port=%s" % str(self.port)] + self._args >@@ -212,13 +182,13 @@ class SafariDriverServer(WebDriverServer): > > class ServoDriverServer(WebDriverServer): > def __init__(self, logger, binary="servo", binary_args=None, host="127.0.0.1", >- port=None, args=None): >- env = os.environ.copy() >+ port=None, env=None, args=None): >+ env = env if env is not None else os.environ.copy() > env["RUST_BACKTRACE"] = "1" > WebDriverServer.__init__(self, logger, binary, > host=host, > port=port, >- env=cast_env(env), >+ env=env, > args=args) > self.binary_args = binary_args > >@@ -233,9 +203,6 @@ class ServoDriverServer(WebDriverServer): > > > class WebKitDriverServer(WebDriverServer): >- def __init__(self, logger, binary=None, port=None, args=None): >- WebDriverServer.__init__(self, logger, binary, port=port, args=args) >- > def make_command(self): > return [self.binary, "--port=%s" % str(self.port)] + self._args > >@@ -254,7 +221,7 @@ def get_free_port(): > s = socket.socket() > try: > s.bind(("127.0.0.1", 0)) >- except socket.error: >+ except OSError: > continue > else: > return s.getsockname()[1] >@@ -265,7 +232,7 @@ def get_free_port(): > def wait_for_service(addr, timeout=60): > """Waits until network service given as a tuple of (host, port) becomes > available or the `timeout` duration is reached, at which point >- ``socket.error`` is raised.""" >+ ``socket.timeout`` is raised.""" > end = time.time() + timeout > while end > time.time(): > so = socket.socket() >@@ -273,7 +240,7 @@ def wait_for_service(addr, timeout=60): > so.connect(addr) > except socket.timeout: > pass >- except socket.error as e: >+ except OSError as e: > if e.errno != errno.ECONNREFUSED: > raise > else: >@@ -281,4 +248,4 @@ def wait_for_service(addr, timeout=60): > finally: > so.close() > time.sleep(0.5) >- raise socket.error("Service is unavailable: %s:%i" % addr) >+ raise socket.timeout("Service is unavailable: %s:%i" % addr) >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptcommandline.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptcommandline.py >index 9d1d40480954..c7050eb80c2b 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptcommandline.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptcommandline.py >@@ -1,11 +1,9 @@ >-from __future__ import absolute_import, print_function > import argparse > import os > import sys > from collections import OrderedDict > from distutils.spawn import find_executable > from datetime import timedelta >-from six import ensure_text, iterkeys, itervalues, iteritems > > from . import config > from . import wpttest >@@ -16,7 +14,7 @@ def abs_path(path): > > > def url_or_path(path): >- from six.moves.urllib.parse import urlparse >+ from urllib.parse import urlparse > > parsed = urlparse(path) > if len(parsed.scheme) > 2: >@@ -72,6 +70,10 @@ scheme host and port.""") > default=True, > dest="fail_on_unexpected", > help="Exit with status code 0 when test expectations are violated") >+ parser.add_argument("--no-fail-on-unexpected-pass", action="store_false", >+ default=True, >+ dest="fail_on_unexpected_pass", >+ help="Exit with status code 0 when all unexpected results are PASS") > > mode_group = parser.add_argument_group("Mode") > mode_group.add_argument("--list-test-groups", action="store_true", >@@ -131,6 +133,8 @@ scheme host and port.""") > help="Test types to run") > test_selection_group.add_argument("--include", action="append", > help="URL prefix to include") >+ test_selection_group.add_argument("--include-file", action="store", >+ help="A file listing URL prefix for tests") > test_selection_group.add_argument("--exclude", action="append", > help="URL prefix to exclude") > test_selection_group.add_argument("--include-manifest", type=abs_path, >@@ -170,6 +174,8 @@ scheme host and port.""") > help="Halt the test runner after each test (this happens by default if only a single test is run)") > debugging_group.add_argument('--no-pause-after-test', dest="pause_after_test", action="store_false", > help="Don't halt the test runner irrespective of the number of tests run") >+ debugging_group.add_argument('--debug-test', dest="debug_test", action="store_true", >+ help="Run tests with additional debugging features enabled") > > debugging_group.add_argument('--pause-on-unexpected', action="store_true", > help="Halt the test runner when an unexpected result is encountered") >@@ -181,7 +187,6 @@ scheme host and port.""") > help="Path or url to symbols file used to analyse crash minidumps.") > debugging_group.add_argument("--stackwalk-binary", action="store", type=abs_path, > help="Path to stackwalker program used to analyse minidumps.") >- > debugging_group.add_argument("--pdb", action="store_true", > help="Drop into pdb on python exception") > >@@ -196,6 +201,8 @@ scheme host and port.""") > config_group.add_argument('--webdriver-arg', > default=[], action="append", dest="webdriver_args", > help="Extra argument for the WebDriver binary") >+ config_group.add_argument("--adb-binary", action="store", >+ help="Path to adb binary to use") > config_group.add_argument("--package-name", action="store", > help="Android package name to run tests against") > config_group.add_argument("--device-serial", action="store", >@@ -284,6 +291,8 @@ scheme host and port.""") > help="Disable fission in Gecko.") > gecko_group.add_argument("--stackfix-dir", dest="stackfix_dir", action="store", > help="Path to directory containing assertion stack fixing scripts") >+ gecko_group.add_argument("--specialpowers-path", action="store", >+ help="Path to specialPowers extension xpi file") > gecko_group.add_argument("--setpref", dest="extra_prefs", action='append', > default=[], metavar="PREF=VALUE", > help="Defines an extra user preference (overrides those in prefs_root)") >@@ -312,14 +321,17 @@ scheme host and port.""") > default=[], action="append", dest="user_stylesheets", > help="Inject a user CSS stylesheet into every test.") > >- servo_group = parser.add_argument_group("Chrome-specific") >- servo_group.add_argument("--enable-mojojs", action="store_true", default=False, >+ chrome_group = parser.add_argument_group("Chrome-specific") >+ chrome_group.add_argument("--enable-mojojs", action="store_true", default=False, > help="Enable MojoJS for testing. Note that this flag is usally " > "enabled automatically by `wpt run`, if it succeeds in downloading " > "the right version of mojojs.zip or if --mojojs-path is specified.") >- servo_group.add_argument("--mojojs-path", >+ chrome_group.add_argument("--mojojs-path", > help="Path to mojojs gen/ directory. If it is not specified, `wpt run` " > "will download and extract mojojs.zip into _venv2/mojojs/gen.") >+ chrome_group.add_argument("--enable-swiftshader", action="store_true", default=False, >+ help="Enable SwiftShader for CPU-based 3D graphics. This can be used " >+ "in environments with no hardware GPU available.") > > sauce_group = parser.add_argument_group("Sauce Labs-specific") > sauce_group.add_argument("--sauce-browser", dest="sauce_browser", >@@ -354,13 +366,17 @@ scheme host and port.""") > > taskcluster_group = parser.add_argument_group("Taskcluster-specific") > taskcluster_group.add_argument("--github-checks-text-file", >- type=ensure_text, >+ type=str, > help="Path to GitHub checks output file") > > webkit_group = parser.add_argument_group("WebKit-specific") > webkit_group.add_argument("--webkit-port", dest="webkit_port", > help="WebKit port") > >+ safari_group = parser.add_argument_group("Safari-specific") >+ safari_group.add_argument("--kill-safari", dest="kill_safari", action="store_true", default=False, >+ help="Kill Safari when stopping the browser") >+ > parser.add_argument("test_list", nargs="*", > help="List of URLs for tests to run, or paths including tests to run. " > "(equivalent to --include)") >@@ -402,7 +418,7 @@ def set_from_config(kwargs): > ("host_cert_path", "host_cert_path", True), > ("host_key_path", "host_key_path", True)]} > >- for section, values in iteritems(keys): >+ for section, values in keys.items(): > for config_value, kw_value, is_path in values: > if kw_value in kwargs and kwargs[kw_value] is None: > if not is_path: >@@ -438,7 +454,7 @@ def get_test_paths(config): > # Set up test_paths > test_paths = OrderedDict() > >- for section in iterkeys(config): >+ for section in config.keys(): > if section.startswith("manifest:"): > manifest_opts = config.get(section) > url_base = manifest_opts.get("url_base", "/") >@@ -464,7 +480,7 @@ def exe_path(name): > > > def check_paths(kwargs): >- for test_paths in itervalues(kwargs["test_paths"]): >+ for test_paths in kwargs["test_paths"].values(): > if not ("tests_path" in test_paths and > "metadata_path" in test_paths): > print("Fatal: must specify both a test path and metadata path") >@@ -472,7 +488,7 @@ def check_paths(kwargs): > if "manifest_path" not in test_paths: > test_paths["manifest_path"] = os.path.join(test_paths["metadata_path"], > "MANIFEST.json") >- for key, path in iteritems(test_paths): >+ for key, path in test_paths.items(): > name = key.split("_", 1)[0] > > if name == "manifest": >@@ -588,7 +604,7 @@ def check_args(kwargs): > kwargs["reftest_internal"] = True > > if kwargs["reftest_screenshot"] is None: >- kwargs["reftest_screenshot"] = "unexpected" >+ kwargs["reftest_screenshot"] = "unexpected" if not kwargs["debug_test"] else "always" > > if kwargs["enable_webrender"] is None: > kwargs["enable_webrender"] = False >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptlogging.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptlogging.py >index 444d1d962d25..b5cb58881fd0 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptlogging.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptlogging.py >@@ -1,4 +1,5 @@ > import logging >+from threading import Thread > > from mozlog import commandline, stdadapter, set_default_logger > from mozlog.structuredlog import StructuredLogger, log_levels >@@ -65,3 +66,42 @@ class LoggedAboveLevelHandler(object): > not self.has_log and > log_levels[data["level"]] <= self.min_level): > self.has_log = True >+ >+ >+class QueueHandler(logging.Handler): >+ def __init__(self, queue, level=logging.NOTSET): >+ self.queue = queue >+ logging.Handler.__init__(self, level=level) >+ >+ def createLock(self): >+ # The queue provides its own locking >+ self.lock = None >+ >+ def emit(self, record): >+ msg = self.format(record) >+ data = {"action": "log", >+ "level": record.levelname, >+ "thread": record.threadName, >+ "pid": record.process, >+ "source": self.name, >+ "message": msg} >+ self.queue.put(data) >+ >+ >+class LogQueueThread(Thread): >+ """Thread for handling log messages from a queue""" >+ def __init__(self, queue, logger): >+ self.queue = queue >+ self.logger = logger >+ super().__init__(name="Thread-Log") >+ >+ def run(self): >+ while True: >+ try: >+ data = self.queue.get() >+ except (EOFError, IOError): >+ break >+ if data is None: >+ # A None message is used to shut down the logging thread >+ break >+ self.logger.log_raw(data) >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptmanifest/backends/conditional.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptmanifest/backends/conditional.py >index 3b11e83012ab..30dd1449156e 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptmanifest/backends/conditional.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptmanifest/backends/conditional.py >@@ -1,5 +1,5 @@ > import operator >-from six import ensure_text, iteritems, iterkeys, text_type >+from six import ensure_text > > from ..node import NodeVisitor, DataNode, ConditionalNode, KeyValueNode, ListNode, ValueNode, BinaryExpressionNode, VariableNode > from ..parser import parse >@@ -300,9 +300,9 @@ class ManifestItem(object): > if isinstance(value, list): > value_node = ListNode() > for item in value: >- value_node.append(ValueNode(text_type(item))) >+ value_node.append(ValueNode(str(item))) > else: >- value_node = ValueNode(text_type(value)) >+ value_node = ValueNode(str(value)) > if condition is not None: > if not isinstance(condition, ConditionalNode): > conditional_node = ConditionalNode() >@@ -368,17 +368,17 @@ class ManifestItem(object): > def _flatten(self): > rv = {} > for node in [self, self.root]: >- for name, value in iteritems(node._data): >+ for name, value in node._data.items(): > if name not in rv: > rv[name] = value > return rv > > def iteritems(self): >- for item in iteritems(self._flatten()): >+ for item in self._flatten().items(): > yield item > > def iterkeys(self): >- for item in iterkeys(self._flatten()): >+ for item in self._flatten().keys(): > yield item > > def iter_properties(self): >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptmanifest/node.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptmanifest/node.py >index 0f82d7006b69..5e9d2b642744 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptmanifest/node.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptmanifest/node.py >@@ -1,5 +1,3 @@ >-from six.moves import range >- > class NodeVisitor(object): > def visit(self, node): > # This is ugly as hell, but we don't have multimethods and >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptmanifest/parser.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptmanifest/parser.py >index 911efac80f27..4c97c6914139 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptmanifest/parser.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptmanifest/parser.py >@@ -12,10 +12,7 @@ > > # TODO: keep comments in the tree > >-from __future__ import unicode_literals >- >-from six import binary_type, text_type, BytesIO, unichr >-from six.moves import range >+from io import BytesIO > > from .node import (Node, AtomNode, BinaryExpressionNode, BinaryOperatorNode, > ConditionalNode, DataNode, IndexNode, KeyValueNode, ListNode, >@@ -50,7 +47,7 @@ atoms = {"True": True, > "Reset": object()} > > def decode(s): >- assert isinstance(s, text_type) >+ assert isinstance(s, str) > return s > > >@@ -59,7 +56,7 @@ def precedence(operator_node): > > > class TokenTypes(object): >- def __init__(self): >+ def __init__(self) -> None: > for type in ["group_start", "group_end", "paren", "list_start", "list_end", "separator", "ident", "string", "number", "atom", "eof"]: > setattr(self, type, type) > >@@ -79,7 +76,7 @@ class Tokenizer(object): > > def tokenize(self, stream): > self.reset() >- assert not isinstance(stream, text_type) >+ assert not isinstance(stream, str) > if isinstance(stream, bytes): > stream = BytesIO(stream) > if not hasattr(stream, "name"): >@@ -89,7 +86,7 @@ class Tokenizer(object): > > self.next_line_state = self.line_start_state > for i, line in enumerate(stream): >- assert isinstance(line, binary_type) >+ assert isinstance(line, bytes) > self.state = self.next_line_state > assert self.state is not None > states = [] >@@ -97,7 +94,7 @@ class Tokenizer(object): > self.line_number = i + 1 > self.index = 0 > self.line = line.decode('utf-8').rstrip() >- assert isinstance(self.line, text_type) >+ assert isinstance(self.line, str) > while self.state != self.eol_state: > states.append(self.state) > tokens = self.state() >@@ -505,7 +502,7 @@ class Tokenizer(object): > value += self.escape_value(c) > self.consume() > >- return unichr(value) >+ return chr(value) > > def escape_value(self, c): > if '0' <= c <= '9': >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptmanifest/serializer.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptmanifest/serializer.py >index ec8d80ed6f71..e31a60791ea7 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptmanifest/serializer.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptmanifest/serializer.py >@@ -1,4 +1,3 @@ >-from __future__ import unicode_literals > from six import ensure_text > > from .node import NodeVisitor, ValueNode, ListNode, BinaryExpressionNode >@@ -34,6 +33,7 @@ class ManifestSerializer(NodeVisitor): > rv = "\n".join(self.visit(root)) > if not rv: > return rv >+ rv = rv.strip() > if rv[-1] != "\n": > rv = rv + "\n" > return rv >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptmanifest/tests/test_serializer.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptmanifest/tests/test_serializer.py >index b247855801ea..02e52815dbdc 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptmanifest/tests/test_serializer.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptmanifest/tests/test_serializer.py >@@ -1,8 +1,5 @@ >-import sys > import unittest > >-import pytest >- > from .. import parser, serializer > > >@@ -192,7 +189,6 @@ class TokenizerTest(unittest.TestCase): > r"""key: "#" > """) > >- @pytest.mark.xfail(sys.maxunicode == 0xFFFF, reason="narrow unicode") > def test_escape_9(self): > self.compare(br"""key: \U10FFFFabc""", > u"""key: \U0010FFFFabc >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptrunner.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptrunner.py >index e1d9ef8b1a91..25a2515ef6f8 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptrunner.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptrunner.py >@@ -1,15 +1,13 @@ >-from __future__ import print_function, unicode_literals >- > import json > import os > import sys >-from six import iteritems, itervalues > > import wptserve > from wptserve import sslutils > > from . import environment as env > from . import instruments >+from . import mpcontext > from . import products > from . import testloader > from . import wptcommandline >@@ -18,7 +16,6 @@ from . import wpttest > from mozlog import capture, handlers > from .font import FontInstaller > from .testrunner import ManagerGroup >-from .browsers.base import NullBrowser > > here = os.path.dirname(__file__) > >@@ -65,6 +62,9 @@ def get_loader(test_paths, product, debug=None, run_info_extras=None, chunker_kw > manifest_filters = [] > > include = kwargs["include"] >+ if kwargs["include_file"]: >+ include = include or [] >+ include.extend(testloader.read_include_from_file(kwargs["include_file"])) > if test_groups: > include = testloader.update_include_for_groups(test_groups, include) > >@@ -96,7 +96,7 @@ def get_loader(test_paths, product, debug=None, run_info_extras=None, chunker_kw > def list_test_groups(test_paths, product, **kwargs): > env.do_delayed_imports(logger, test_paths) > >- run_info_extras = products.load_product(kwargs["config"], product)[-1](**kwargs) >+ run_info_extras = products.Product(kwargs["config"], product).run_info_extras(**kwargs) > > run_info, test_loader = get_loader(test_paths, product, > run_info_extras=run_info_extras, **kwargs) >@@ -110,12 +110,12 @@ def list_disabled(test_paths, product, **kwargs): > > rv = [] > >- run_info_extras = products.load_product(kwargs["config"], product)[-1](**kwargs) >+ run_info_extras = products.Product(kwargs["config"], product).run_info_extras(**kwargs) > > run_info, test_loader = get_loader(test_paths, product, > run_info_extras=run_info_extras, **kwargs) > >- for test_type, tests in iteritems(test_loader.disabled_tests): >+ for test_type, tests in test_loader.disabled_tests.items(): > for test in tests: > rv.append({"test": test.id, "reason": test.disabled()}) > print(json.dumps(rv, indent=2)) >@@ -124,7 +124,7 @@ def list_disabled(test_paths, product, **kwargs): > def list_tests(test_paths, product, **kwargs): > env.do_delayed_imports(logger, test_paths) > >- run_info_extras = products.load_product(kwargs["config"], product)[-1](**kwargs) >+ run_info_extras = products.Product(kwargs["config"], product).run_info_extras(**kwargs) > > run_info, test_loader = get_loader(test_paths, product, > run_info_extras=run_info_extras, **kwargs) >@@ -139,8 +139,10 @@ def get_pause_after_test(test_loader, **kwargs): > return False > if kwargs["headless"]: > return False >+ if kwargs["debug_test"]: >+ return True > tests = test_loader.tests >- is_single_testharness = (sum(len(item) for item in itervalues(tests)) == 1 and >+ is_single_testharness = (sum(len(item) for item in tests.values()) == 1 and > len(tests.get("testharness", [])) == 1) > if kwargs["repeat"] == 1 and kwargs["rerun"] == 1 and is_single_testharness: > return True >@@ -151,15 +153,18 @@ def get_pause_after_test(test_loader, **kwargs): > def run_tests(config, test_paths, product, **kwargs): > """Set up the test environment, load the list of tests to be executed, and > invoke the remainder of the code to execute tests""" >+ mp = mpcontext.get_context() > if kwargs["instrument_to_file"] is None: > recorder = instruments.NullInstrument() > else: > recorder = instruments.Instrument(kwargs["instrument_to_file"]) >- with recorder as recording, capture.CaptureIO(logger, not kwargs["no_capture_stdio"]): >+ with recorder as recording, capture.CaptureIO(logger, >+ not kwargs["no_capture_stdio"], >+ mp_context=mp): > recording.set(["startup"]) > env.do_delayed_imports(logger, test_paths) > >- product = products.load_product(config, product, load_cls=True) >+ product = products.Product(config, product) > > env_extras = product.get_env_extras(**kwargs) > >@@ -194,6 +199,7 @@ def run_tests(config, test_paths, product, **kwargs): > skipped_tests = 0 > test_total = 0 > unexpected_total = 0 >+ unexpected_pass_total = 0 > > if len(test_loader.test_ids) == 0 and kwargs["test_list"]: > logger.critical("Unable to find any tests at the path(s):") >@@ -219,6 +225,7 @@ def run_tests(config, test_paths, product, **kwargs): > with env.TestEnvironment(test_paths, > testharness_timeout_multipler, > kwargs["pause_after_test"], >+ kwargs["debug_test"], > kwargs["debug_info"], > product.env_options, > ssl_config, >@@ -229,7 +236,7 @@ def run_tests(config, test_paths, product, **kwargs): > try: > test_environment.ensure_started() > except env.TestEnvironmentError as e: >- logger.critical("Error starting test environment: %s" % e.message) >+ logger.critical("Error starting test environment: %s" % e) > raise > > recording.set(["startup"]) >@@ -247,6 +254,7 @@ def run_tests(config, test_paths, product, **kwargs): > > test_count = 0 > unexpected_count = 0 >+ unexpected_pass_count = 0 > > tests = [] > for test_type in test_loader.test_types: >@@ -265,25 +273,19 @@ def run_tests(config, test_paths, product, **kwargs): > for test_type in kwargs["test_types"]: > logger.info("Running %s tests" % test_type) > >- # WebDriver tests may create and destroy multiple browser >- # processes as part of their expected behavior. These >- # processes are managed by a WebDriver server binary. This >- # obviates the need for wptrunner to provide a browser, so >- # the NullBrowser is used in place of the "target" browser >- if test_type == "wdspec": >- browser_cls = NullBrowser >- else: >- browser_cls = product.browser_cls >+ browser_cls = product.get_browser_cls(test_type) > >- browser_kwargs = product.get_browser_kwargs(test_type, >+ browser_kwargs = product.get_browser_kwargs(logger, >+ test_type, > run_info, > config=test_environment.config, >+ num_test_groups=len(test_groups), > **kwargs) > > executor_cls = product.executor_classes.get(test_type) >- executor_kwargs = product.get_executor_kwargs(test_type, >- test_environment.config, >- test_environment.cache_manager, >+ executor_kwargs = product.get_executor_kwargs(logger, >+ test_type, >+ test_environment, > run_info, > **kwargs) > >@@ -334,10 +336,13 @@ def run_tests(config, test_paths, product, **kwargs): > raise > test_count += manager_group.test_count() > unexpected_count += manager_group.unexpected_count() >+ unexpected_pass_count += manager_group.unexpected_pass_count() > recording.set(["after-end"]) > test_total += test_count > unexpected_total += unexpected_count >- logger.info("Got %i unexpected results" % unexpected_count) >+ unexpected_pass_total += unexpected_pass_count >+ logger.info("Got %i unexpected results, with %i unexpected passes" % >+ (unexpected_count, unexpected_pass_count)) > logger.suite_end() > if repeat_until_unexpected and unexpected_total > 0: > break >@@ -359,6 +364,13 @@ def run_tests(config, test_paths, product, **kwargs): > logger.info("Tolerating %s unexpected results" % unexpected_total) > return True > >+ all_unexpected_passed = (unexpected_total and >+ unexpected_total == unexpected_pass_total) >+ if all_unexpected_passed and not kwargs["fail_on_unexpected_pass"]: >+ logger.info("Tolerating %i unexpected results because they all PASS" % >+ unexpected_pass_total) >+ return True >+ > return unexpected_total == 0 > > >diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wpttest.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wpttest.py >index 31b245612be0..0cd72e285b00 100644 >--- a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wpttest.py >+++ b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wpttest.py >@@ -1,9 +1,9 @@ > import os > import subprocess > import sys >-from six.moves.urllib.parse import urljoin > from collections import defaultdict >-from six import iteritems, string_types >+from typing import Any, ClassVar, Dict, Type >+from urllib.parse import urljoin > > from .wptmanifest.parser import atoms > >@@ -83,7 +83,7 @@ def get_run_info(metadata_root, product, **kwargs): > return RunInfo(metadata_root, product, **kwargs) > > >-class RunInfo(dict): >+class RunInfo(Dict[str, Any]): > def __init__(self, metadata_root, product, debug, > browser_version=None, > browser_channel=None, >@@ -150,22 +150,28 @@ def server_protocol(manifest_item): > > class Test(object): > >- result_cls = None >- subtest_result_cls = None >- test_type = None >+ result_cls = None # type: ClassVar[Type[Result]] >+ subtest_result_cls = None # type: ClassVar[Type[SubtestResult]] >+ test_type = None # type: ClassVar[str] > > default_timeout = 10 # seconds > long_timeout = 60 # seconds > >- def __init__(self, tests_root, url, inherit_metadata, test_metadata, >- timeout=None, path=None, protocol="http", quic=False): >+ def __init__(self, url_base, tests_root, url, inherit_metadata, test_metadata, >+ timeout=None, path=None, protocol="http", subdomain=False, >+ quic=False): >+ self.url_base = url_base > self.tests_root = tests_root > self.url = url > self._inherit_metadata = inherit_metadata > self._test_metadata = test_metadata > self.timeout = timeout if timeout is not None else self.default_timeout > self.path = path >- self.environment = {"protocol": protocol, "prefs": self.prefs, "quic": quic} >+ self.subdomain = subdomain >+ self.environment = {"url_base": url_base, >+ "protocol": protocol, >+ "prefs": self.prefs, >+ "quic": quic} > > def __eq__(self, other): > if not isinstance(other, Test): >@@ -184,13 +190,15 @@ class Test(object): > @classmethod > def from_manifest(cls, manifest_file, manifest_item, inherit_metadata, test_metadata): > timeout = cls.long_timeout if manifest_item.timeout == "long" else cls.default_timeout >- return cls(manifest_file.tests_root, >+ return cls(manifest_file.url_base, >+ manifest_file.tests_root, > manifest_item.url, > inherit_metadata, > test_metadata, > timeout=timeout, > path=os.path.join(manifest_file.tests_root, manifest_item.path), >- protocol=server_protocol(manifest_item)) >+ protocol=server_protocol(manifest_item), >+ subdomain=manifest_item.subdomain) > > @property > def id(self): >@@ -299,7 +307,7 @@ class Test(object): > rv = {} > for meta in self.itermeta(None): > threshold = meta.leak_threshold >- for key, value in iteritems(threshold): >+ for key, value in threshold.items(): > if key not in rv: > rv[key] = value > return rv >@@ -341,7 +349,7 @@ class Test(object): > > try: > expected = metadata.get("expected") >- if isinstance(expected, string_types): >+ if isinstance(expected, str): > return expected > elif isinstance(expected, list): > return expected[0] >@@ -393,11 +401,11 @@ class TestharnessTest(Test): > subtest_result_cls = TestharnessSubtestResult > test_type = "testharness" > >- def __init__(self, tests_root, url, inherit_metadata, test_metadata, >+ def __init__(self, url_base, tests_root, url, inherit_metadata, test_metadata, > timeout=None, path=None, protocol="http", testdriver=False, >- jsshell=False, scripts=None, quic=False): >- Test.__init__(self, tests_root, url, inherit_metadata, test_metadata, timeout, >- path, protocol, quic) >+ jsshell=False, scripts=None, subdomain=False, quic=False): >+ Test.__init__(self, url_base, tests_root, url, inherit_metadata, test_metadata, timeout, >+ path, protocol, subdomain, quic) > > self.testdriver = testdriver > self.jsshell = jsshell >@@ -412,7 +420,8 @@ class TestharnessTest(Test): > script_metadata = manifest_item.script_metadata or [] > scripts = [v for (k, v) in script_metadata > if k == "script"] >- return cls(manifest_file.tests_root, >+ return cls(manifest_file.url_base, >+ manifest_file.tests_root, > manifest_item.url, > inherit_metadata, > test_metadata, >@@ -422,6 +431,7 @@ class TestharnessTest(Test): > testdriver=testdriver, > jsshell=jsshell, > scripts=scripts, >+ subdomain=manifest_item.subdomain, > quic=quic) > > @property >@@ -452,11 +462,11 @@ class ReftestTest(Test): > result_cls = ReftestResult > test_type = "reftest" > >- def __init__(self, tests_root, url, inherit_metadata, test_metadata, references, >- timeout=None, path=None, viewport_size=None, dpi=None, fuzzy=None, protocol="http", >- quic=False): >- Test.__init__(self, tests_root, url, inherit_metadata, test_metadata, timeout, >- path, protocol, quic) >+ def __init__(self, url_base, tests_root, url, inherit_metadata, test_metadata, references, >+ timeout=None, path=None, viewport_size=None, dpi=None, fuzzy=None, >+ protocol="http", subdomain=False, quic=False): >+ Test.__init__(self, url_base, tests_root, url, inherit_metadata, test_metadata, timeout, >+ path, protocol, subdomain, quic) > > for _, ref_type in references: > if ref_type not in ("==", "!="): >@@ -486,13 +496,15 @@ class ReftestTest(Test): > > url = manifest_test.url > >- node = cls(manifest_file.tests_root, >+ node = cls(manifest_file.url_base, >+ manifest_file.tests_root, > manifest_test.url, > inherit_metadata, > test_metadata, > [], > timeout=timeout, > path=manifest_test.path, >+ subdomain=manifest_test.subdomain, > quic=quic, > **cls.cls_kwargs(manifest_test)) > >@@ -507,20 +519,23 @@ class ReftestTest(Test): > # Per the logic documented above, this means that none of the mismatches provided match, > mismatch_walk = None > if refs_by_type["!="]: >- mismatch_walk = ReftestTest(manifest_file.tests_root, >+ mismatch_walk = ReftestTest(manifest_file.url_base, >+ manifest_file.tests_root, > refs_by_type["!="][0], > [], > None, > []) > cmp_ref = mismatch_walk > for ref_url in refs_by_type["!="][1:]: >- cmp_self = ReftestTest(manifest_file.tests_root, >+ cmp_self = ReftestTest(manifest_file.url_base, >+ manifest_file.tests_root, > url, > [], > None, > []) > cmp_ref.references.append((cmp_self, "!=")) >- cmp_ref = ReftestTest(manifest_file.tests_root, >+ cmp_ref = ReftestTest(manifest_file.url_base, >+ manifest_file.tests_root, > ref_url, > [], > None, >@@ -536,7 +551,8 @@ class ReftestTest(Test): > # For each == ref, add a reference to this node whose tail is the mismatch list. > # Per the logic documented above, this means any one of the matches must pass plus all the mismatches. > for ref_url in refs_by_type["=="]: >- ref = ReftestTest(manifest_file.tests_root, >+ ref = ReftestTest(manifest_file.url_base, >+ manifest_file.tests_root, > ref_url, > [], > None, >@@ -605,12 +621,12 @@ class ReftestTest(Test): > class PrintReftestTest(ReftestTest): > test_type = "print-reftest" > >- def __init__(self, tests_root, url, inherit_metadata, test_metadata, references, >+ def __init__(self, url_base, tests_root, url, inherit_metadata, test_metadata, references, > timeout=None, path=None, viewport_size=None, dpi=None, fuzzy=None, >- page_ranges=None, protocol="http", quic=False): >- super(PrintReftestTest, self).__init__(tests_root, url, inherit_metadata, test_metadata, >+ page_ranges=None, protocol="http", subdomain=False, quic=False): >+ super(PrintReftestTest, self).__init__(url_base, tests_root, url, inherit_metadata, test_metadata, > references, timeout, path, viewport_size, dpi, >- fuzzy, protocol, quic=quic) >+ fuzzy, protocol, subdomain=subdomain, quic=quic) > self._page_ranges = page_ranges > > @classmethod >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/accept_alert/accept.py b/WebDriverTests/imported/w3c/webdriver/tests/accept_alert/accept.py >index 711ad5af79f2..b83477e5ca72 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/accept_alert/accept.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/accept_alert/accept.py >@@ -1,7 +1,9 @@ >+import pytest >+ > from webdriver.error import NoSuchAlertException > > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline >+from tests.support.helpers import wait_for_new_handle > from tests.support.sync import Poll > > >@@ -10,7 +12,7 @@ def accept_alert(session): > "POST", "session/{session_id}/alert/accept".format(**vars(session))) > > >-def test_null_response_value(session, url): >+def test_null_response_value(session, inline): > session.url = inline("<script>window.alert('Hello');</script>") > > response = accept_alert(session) >@@ -33,27 +35,41 @@ def test_no_user_prompt(session): > assert_error(response, "no such alert") > > >-def test_accept_alert(session): >+def test_accept_alert(session, inline): > session.url = inline("<script>window.alert('Hello');</script>") >+ > response = accept_alert(session) > assert_success(response) > >+ with pytest.raises(NoSuchAlertException): >+ session.alert.text >+ > >-def test_accept_confirm(session): >+def test_accept_confirm(session, inline): > session.url = inline("<script>window.result = window.confirm('Hello');</script>") >+ > response = accept_alert(session) > assert_success(response) >+ >+ with pytest.raises(NoSuchAlertException): >+ session.alert.text >+ > assert session.execute_script("return window.result") is True > > >-def test_accept_prompt(session): >+def test_accept_prompt(session, inline): > session.url = inline(""" > <script> > window.result = window.prompt('Enter Your Name: ', 'Federer'); > </script> > """) >+ > response = accept_alert(session) > assert_success(response) >+ >+ with pytest.raises(NoSuchAlertException): >+ session.alert.text >+ > assert session.execute_script("return window.result") == "Federer" > > >@@ -68,3 +84,27 @@ def test_unexpected_alert(session): > > response = accept_alert(session) > assert_success(response) >+ >+ with pytest.raises(NoSuchAlertException): >+ session.alert.text >+ >+ >+def test_accept_in_popup_window(session, inline): >+ orig_handles = session.handles >+ >+ session.url = inline(""" >+ <button onclick="window.open('about:blank', '_blank', 'width=500; height=200;resizable=yes');">open</button> >+ """) >+ button = session.find.css("button", all=False) >+ button.click() >+ >+ session.window_handle = wait_for_new_handle(session, orig_handles) >+ session.url = inline(""" >+ <script>window.alert("Hello")</script> >+ """) >+ >+ response = accept_alert(session) >+ assert_success(response) >+ >+ with pytest.raises(NoSuchAlertException): >+ session.alert.text >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/add_cookie/add.py b/WebDriverTests/imported/w3c/webdriver/tests/add_cookie/add.py >index f05153790261..3a19432fc6a2 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/add_cookie/add.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/add_cookie/add.py >@@ -1,13 +1,13 @@ > import pytest > > from datetime import datetime, timedelta >-from six import text_type > > from webdriver.transport import Response > > from tests.support.asserts import assert_error, assert_success > from tests.support.helpers import clear_all_cookies > >+ > def add_cookie(session, cookie): > return session.transport.send( > "POST", "session/{session_id}/cookie".format(**vars(session)), >@@ -54,6 +54,43 @@ def test_no_browsing_context(session, closed_frame): > assert_error(response, "no such window") > > >+@pytest.mark.parametrize( >+ "page", >+ [ >+ "about:blank", >+ "blob:foo/bar", >+ "data:text/html;charset=utf-8,<p>foo</p>", >+ "file:///foo/bar", >+ "ftp://example.org", >+ "javascript:foo", >+ "ws://example.org", >+ "wss://example.org", >+ ], >+ ids=[ >+ "about", >+ "blob", >+ "data", >+ "file", >+ "ftp", >+ "javascript", >+ "websocket", >+ "secure websocket", >+ ], >+) >+def test_cookie_unsupported_scheme(session, page): >+ new_cookie = { >+ "name": "hello", >+ "value": "world", >+ "domain": page, >+ "path": "/", >+ "httpOnly": False, >+ "secure": False >+ } >+ >+ result = add_cookie(session, new_cookie) >+ assert_error(result, "invalid cookie domain") >+ >+ > def test_add_domain_cookie(session, url, server_config): > new_cookie = { > "name": "hello", >@@ -72,11 +109,11 @@ def test_add_domain_cookie(session, url, server_config): > > cookie = session.cookies("hello") > assert "domain" in cookie >- assert isinstance(cookie["domain"], text_type) >+ assert isinstance(cookie["domain"], str) > assert "name" in cookie >- assert isinstance(cookie["name"], text_type) >+ assert isinstance(cookie["name"], str) > assert "value" in cookie >- assert isinstance(cookie["value"], text_type) >+ assert isinstance(cookie["value"], str) > > assert cookie["name"] == "hello" > assert cookie["value"] == "world" >@@ -102,11 +139,11 @@ def test_add_cookie_for_ip(session, url, server_config, configuration): > > cookie = session.cookies("hello") > assert "name" in cookie >- assert isinstance(cookie["name"], text_type) >+ assert isinstance(cookie["name"], str) > assert "value" in cookie >- assert isinstance(cookie["value"], text_type) >+ assert isinstance(cookie["value"], str) > assert "domain" in cookie >- assert isinstance(cookie["domain"], text_type) >+ assert isinstance(cookie["domain"], str) > > assert cookie["name"] == "hello" > assert cookie["value"] == "world" >@@ -131,9 +168,9 @@ def test_add_non_session_cookie(session, url): > > cookie = session.cookies("hello") > assert "name" in cookie >- assert isinstance(cookie["name"], text_type) >+ assert isinstance(cookie["name"], str) > assert "value" in cookie >- assert isinstance(cookie["value"], text_type) >+ assert isinstance(cookie["value"], str) > assert "expiry" in cookie > assert isinstance(cookie["expiry"], int) > >@@ -156,9 +193,9 @@ def test_add_session_cookie(session, url): > > cookie = session.cookies("hello") > assert "name" in cookie >- assert isinstance(cookie["name"], text_type) >+ assert isinstance(cookie["name"], str) > assert "value" in cookie >- assert isinstance(cookie["value"], text_type) >+ assert isinstance(cookie["value"], str) > if "expiry" in cookie: > assert cookie.get("expiry") is None > >@@ -181,11 +218,11 @@ def test_add_session_cookie_with_leading_dot_character_in_domain(session, url, s > > cookie = session.cookies("hello") > assert "name" in cookie >- assert isinstance(cookie["name"], text_type) >+ assert isinstance(cookie["name"], str) > assert "value" in cookie >- assert isinstance(cookie["value"], text_type) >+ assert isinstance(cookie["value"], str) > assert "domain" in cookie >- assert isinstance(cookie["domain"], text_type) >+ assert isinstance(cookie["domain"], str) > > assert cookie["name"] == "hello" > assert cookie["value"] == "world" >@@ -209,11 +246,11 @@ def test_add_cookie_with_valid_samesite_flag(session, url, same_site): > > cookie = session.cookies("hello") > assert "name" in cookie >- assert isinstance(cookie["name"], text_type) >+ assert isinstance(cookie["name"], str) > assert "value" in cookie >- assert isinstance(cookie["value"], text_type) >+ assert isinstance(cookie["value"], str) > assert "sameSite" in cookie >- assert isinstance(cookie["sameSite"], text_type) >+ assert isinstance(cookie["sameSite"], str) > > assert cookie["name"] == "hello" > assert cookie["value"] == "world" >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/back/back.py b/WebDriverTests/imported/w3c/webdriver/tests/back/back.py >index 9ca52e8f223e..86ad2278ab39 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/back/back.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/back/back.py >@@ -1,4 +1,6 @@ >-from tests.support.inline import inline >+import pytest >+from webdriver import error >+ > from tests.support.asserts import assert_error, assert_success > > >@@ -7,7 +9,7 @@ def back(session): > "POST", "session/{session_id}/back".format(**vars(session))) > > >-def test_null_response_value(session): >+def test_null_response_value(session, inline): > session.url = inline("<div>") > session.url = inline("<p>") > >@@ -31,7 +33,7 @@ def test_no_browsing_history(session): > assert_success(response) > > >-def test_data_urls(session): >+def test_data_urls(session, inline): > test_pages = [ > inline("<p id=1>"), > inline("<p id=2>"), >@@ -46,7 +48,7 @@ def test_data_urls(session): > assert session.url == test_pages[0] > > >-def test_dismissed_beforeunload(session): >+def test_dismissed_beforeunload(session, inline): > url_beforeunload = inline(""" > <input type="text"> > <script> >@@ -88,7 +90,7 @@ def test_fragments(session, url): > assert session.url == test_pages[0] > > >-def test_history_pushstate(session, url): >+def test_history_pushstate(session, inline): > pushstate_page = inline(""" > <script> > function pushState() { >@@ -109,3 +111,42 @@ def test_history_pushstate(session, url): > > assert session.url == pushstate_page > assert session.execute_script("return history.state;") is None >+ >+ >+def test_removed_iframe(session, url, inline): >+ page = inline("<p>foo") >+ >+ session.url = page >+ session.url = url("/webdriver/tests/support/html/frames_no_bfcache.html") >+ >+ subframe = session.find.css("#sub-frame", all=False) >+ session.switch_frame(subframe) >+ >+ response = back(session) >+ assert_success(response) >+ >+ assert session.url == page >+ >+ >+# Capability needed as long as no valid certificate is available: >+# https://github.com/web-platform-tests/wpt/issues/28847 >+@pytest.mark.capabilities({"acceptInsecureCerts": True}) >+def test_cross_origin(session, url): >+ base_path = ("/webdriver/tests/support/html/subframe.html" + >+ "?pipe=header(Cross-Origin-Opener-Policy,same-origin") >+ first_page = url(base_path, protocol="https") >+ second_page = url(base_path, protocol="https", domain="alt") >+ >+ session.url = first_page >+ session.url = second_page >+ >+ elem = session.find.css("#delete", all=False) >+ >+ response = back(session) >+ assert_success(response) >+ >+ assert session.url == first_page >+ >+ with pytest.raises(error.StaleElementReferenceException): >+ elem.click() >+ elem = session.find.css("#delete", all=False) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/back/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/back/user_prompts.py >index 7121f7c6b2a6..9d04f0f4ab12 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/back/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/back/user_prompts.py >@@ -3,7 +3,6 @@ > import pytest > > from tests.support.asserts import assert_dialog_handled, assert_error, assert_success >-from tests.support.inline import inline > > > def back(session): >@@ -12,7 +11,7 @@ def back(session): > > > @pytest.fixture >-def pages(session): >+def pages(session, inline): > pages = [ > inline("<p id=1>"), > inline("<p id=2>"), >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/bidi/new_session/__init__.py b/WebDriverTests/imported/w3c/webdriver/tests/bidi/new_session/__init__.py >new file mode 100644 >index 000000000000..e69de29bb2d1 >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/bidi/new_session/connect.py b/WebDriverTests/imported/w3c/webdriver/tests/bidi/new_session/connect.py >new file mode 100644 >index 000000000000..357da31ba862 >--- /dev/null >+++ b/WebDriverTests/imported/w3c/webdriver/tests/bidi/new_session/connect.py >@@ -0,0 +1,34 @@ >+import pytest >+import asyncio >+import websockets >+import webdriver >+ >+# classic session to enable bidi capability manually >+# Intended to be the first test in this file >+@pytest.mark.asyncio >+@pytest.mark.capabilities({"webSocketUrl": True}) >+async def test_websocket_url_connect(session): >+ websocket_url = session.capabilities["webSocketUrl"] >+ async with websockets.connect(websocket_url) as websocket: >+ await websocket.send("Hello world!") >+ >+# test bidi_session send >+# using bidi_session is the recommended way to test bidi >+@pytest.mark.asyncio >+async def test_bidi_session_send(bidi_session): >+ await bidi_session.send_command("test.test", {}) >+ >+# bidi session following a bidi session with a different capabilities >+# to test session recreation >+@pytest.mark.asyncio >+@pytest.mark.capabilities({"acceptInsecureCerts": True}) >+async def test_bidi_session_with_different_capability(bidi_session): >+ await bidi_session.send_command("test.test", {}) >+ >+# classic session following a bidi session to test session >+# recreation >+# Intended to be the last test in this file to make sure >+# classic session is not impacted by bidi tests >+@pytest.mark.asyncio >+def test_classic_after_bidi_session(session): >+ assert not isinstance(session, webdriver.bidi.BidiSession) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/close_window/close.py b/WebDriverTests/imported/w3c/webdriver/tests/close_window/close.py >index 53af5730f83f..7b382fa9bbcb 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/close_window/close.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/close_window/close.py >@@ -2,7 +2,6 @@ import pytest > from webdriver import error > > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline > > > def close(session): >@@ -46,7 +45,7 @@ def test_close_browsing_context(session): > assert new_handle not in handles > > >-def test_close_browsing_context_with_dismissed_beforeunload_prompt(session): >+def test_close_browsing_context_with_dismissed_beforeunload_prompt(session, inline): > original_handles = session.handles > > new_handle = session.new_window() >@@ -81,3 +80,23 @@ def test_close_last_browsing_context(session): > > # With no more open top-level browsing contexts, the session is closed. > session.session_id = None >+ >+ >+def test_element_usage_after_closing_browsing_context(session, inline): >+ session.url = inline("<p id='a'>foo") >+ a = session.find.css("p", all=False) >+ first = session.window_handle >+ >+ second = session.new_window(type_hint="tab") >+ session.window_handle = second >+ >+ session.url = inline("<p id='b'>other") >+ b = session.find.css("p", all=False) >+ >+ session.window_handle = first >+ response = close(session) >+ assert_success(response) >+ assert len(session.handles) == 1 >+ >+ session.window_handle = second >+ assert b.attribute("id") == "b" >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/delete_session/delete.py b/WebDriverTests/imported/w3c/webdriver/tests/delete_session/delete.py >index d0b4d796308e..a3032cc13475 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/delete_session/delete.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/delete_session/delete.py >@@ -2,7 +2,6 @@ import pytest > from webdriver import error > > from tests.support.asserts import assert_success >-from tests.support.inline import inline > > > def delete_session(session): >@@ -19,7 +18,7 @@ def test_null_response_value(session): > session.end() > > >-def test_dismissed_beforeunload_prompt(session): >+def test_dismissed_beforeunload_prompt(session, inline): > session.url = inline(""" > <input type="text"> > <script> >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/dismiss_alert/dismiss.py b/WebDriverTests/imported/w3c/webdriver/tests/dismiss_alert/dismiss.py >index da65bac388df..a28dec768750 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/dismiss_alert/dismiss.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/dismiss_alert/dismiss.py >@@ -1,7 +1,9 @@ >+import pytest >+ > from webdriver.error import NoSuchAlertException > > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline >+from tests.support.helpers import wait_for_new_handle > from tests.support.sync import Poll > > >@@ -10,7 +12,7 @@ def dismiss_alert(session): > "POST", "session/{session_id}/alert/dismiss".format(**vars(session))) > > >-def test_null_response_value(session, url): >+def test_null_response_value(session, inline): > session.url = inline("<script>window.alert('Hello');</script>") > > response = dismiss_alert(session) >@@ -33,28 +35,45 @@ def test_no_user_prompt(session): > assert_error(response, "no such alert") > > >-def test_dismiss_alert(session): >+def test_dismiss_alert(session, inline): > session.url = inline("<script>window.alert('Hello');</script>") >+ > response = dismiss_alert(session) > assert_success(response) > >+ with pytest.raises(NoSuchAlertException): >+ session.alert.text >+ > >-def test_dismiss_confirm(session): >+def test_dismiss_confirm(session, inline): > session.url = inline("<script>window.result = window.confirm('Hello');</script>") >+ > response = dismiss_alert(session) > assert_success(response) >+ >+ with pytest.raises(NoSuchAlertException): >+ session.alert.text >+ > assert session.execute_script("return window.result;") is False > > >-def test_dismiss_prompt(session): >- session.url = inline("<script>window.result = window.prompt('Enter Your Name: ', 'Federer');</script>") >+def test_dismiss_prompt(session, inline): >+ session.url = inline(""" >+ <script>window.result = window.prompt('Enter Your Name: ', 'Federer');</script> >+ """) >+ > response = dismiss_alert(session) > assert_success(response) >+ >+ with pytest.raises(NoSuchAlertException): >+ session.alert.text >+ > assert session.execute_script("return window.result") is None > > > def test_unexpected_alert(session): > session.execute_script("setTimeout(function() { alert('Hello'); }, 100);") >+ > wait = Poll( > session, > timeout=5, >@@ -64,3 +83,27 @@ def test_unexpected_alert(session): > > response = dismiss_alert(session) > assert_success(response) >+ >+ with pytest.raises(NoSuchAlertException): >+ session.alert.text >+ >+ >+def test_dismiss_in_popup_window(session, inline): >+ orig_handles = session.handles >+ >+ session.url = inline(""" >+ <button onclick="window.open('about:blank', '_blank', 'width=500; height=200;resizable=yes');">open</button> >+ """) >+ button = session.find.css("button", all=False) >+ button.click() >+ >+ session.window_handle = wait_for_new_handle(session, orig_handles) >+ session.url = inline(""" >+ <script>window.alert("Hello")</script> >+ """) >+ >+ response = dismiss_alert(session) >+ assert_success(response) >+ >+ with pytest.raises(NoSuchAlertException): >+ session.alert.text >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_clear/clear.py b/WebDriverTests/imported/w3c/webdriver/tests/element_clear/clear.py >index 0189c38c2770..b65f443aa758 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/element_clear/clear.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/element_clear/clear.py >@@ -11,7 +11,6 @@ from tests.support.asserts import ( > assert_in_events, > assert_success, > ) >-from tests.support.inline import inline > > > @pytest.fixture >@@ -37,7 +36,7 @@ def text_file(tmpdir_factory): > return fh > > >-def test_null_response_value(session): >+def test_null_response_value(session, inline): > session.url = inline("<input>") > element = session.find.css("input", all=False) > >@@ -48,10 +47,17 @@ def test_null_response_value(session): > > def test_no_top_browsing_context(session, closed_window): > element = Element("foo", session) >+ response = element_clear(session, element) >+ assert_error(response, "no such window") > >+ original_handle, element = closed_window > response = element_clear(session, element) > assert_error(response, "no such window") > >+ session.window_handle = original_handle >+ response = element_clear(session, element) >+ assert_error(response, "no such element") >+ > > def test_no_browsing_context(session, closed_frame): > element = Element("foo", session) >@@ -60,7 +66,7 @@ def test_no_browsing_context(session, closed_frame): > assert_error(response, "no such window") > > >-def test_connected_element(session): >+def test_connected_element(session, inline): > session.url = inline("<input>") > element = session.find.css("input", all=False) > >@@ -69,7 +75,7 @@ def test_connected_element(session): > assert_error(response, "stale element reference") > > >-def test_pointer_interactable(session): >+def test_pointer_interactable(session, inline): > session.url = inline("<input style='margin-left: -1000px' value=foobar>") > element = session.find.css("input", all=False) > >@@ -77,7 +83,7 @@ def test_pointer_interactable(session): > assert_error(response, "element not interactable") > > >-def test_keyboard_interactable(session): >+def test_keyboard_interactable(session, inline): > session.url = inline(""" > <input value=foobar> > <div></div> >@@ -114,7 +120,7 @@ def test_keyboard_interactable(session): > ("time", "19:48", ""), > ("month", "2017-11", ""), > ("week", "2017-W52", "")]) >-def test_input(session, add_event_listeners, tracked_events, type, value, default): >+def test_input(session, inline, add_event_listeners, tracked_events, type, value, default): > session.url = inline("<input type=%s value='%s'>" % (type, value)) > element = session.find.css("input", all=False) > add_event_listeners(element, tracked_events) >@@ -144,7 +150,7 @@ def test_input(session, add_event_listeners, tracked_events, type, value, defaul > "month", > "week", > "file"]) >-def test_input_disabled(session, type): >+def test_input_disabled(session, inline, type): > session.url = inline("<input type=%s disabled>" % type) > element = session.find.css("input", all=False) > >@@ -169,7 +175,7 @@ def test_input_disabled(session, type): > "month", > "week", > "file"]) >-def test_input_readonly(session, type): >+def test_input_readonly(session, inline, type): > session.url = inline("<input type=%s readonly>" % type) > element = session.find.css("input", all=False) > >@@ -177,7 +183,7 @@ def test_input_readonly(session, type): > assert_error(response, "invalid element state") > > >-def test_textarea(session, add_event_listeners, tracked_events): >+def test_textarea(session, inline, add_event_listeners, tracked_events): > session.url = inline("<textarea>foobar</textarea>") > element = session.find.css("textarea", all=False) > add_event_listeners(element, tracked_events) >@@ -189,7 +195,7 @@ def test_textarea(session, add_event_listeners, tracked_events): > assert_in_events(session, ["focus", "change", "blur"]) > > >-def test_textarea_disabled(session): >+def test_textarea_disabled(session, inline): > session.url = inline("<textarea disabled></textarea>") > element = session.find.css("textarea", all=False) > >@@ -197,7 +203,7 @@ def test_textarea_disabled(session): > assert_error(response, "invalid element state") > > >-def test_textarea_readonly(session): >+def test_textarea_readonly(session, inline): > session.url = inline("<textarea readonly></textarea>") > element = session.find.css("textarea", all=False) > >@@ -205,7 +211,7 @@ def test_textarea_readonly(session): > assert_error(response, "invalid element state") > > >-def test_input_file(session, text_file): >+def test_input_file(session, text_file, inline): > session.url = inline("<input type=file>") > element = session.find.css("input", all=False) > element.send_keys(str(text_file)) >@@ -215,7 +221,7 @@ def test_input_file(session, text_file): > assert element.property("value") == "" > > >-def test_input_file_multiple(session, text_file): >+def test_input_file_multiple(session, text_file, inline): > session.url = inline("<input type=file multiple>") > element = session.find.css("input", all=False) > element.send_keys(str(text_file)) >@@ -226,7 +232,7 @@ def test_input_file_multiple(session, text_file): > assert element.property("value") == "" > > >-def test_select(session): >+def test_select(session, inline): > session.url = inline(""" > <select> > <option>foo >@@ -241,7 +247,7 @@ def test_select(session): > assert_error(response, "invalid element state") > > >-def test_button(session): >+def test_button(session, inline): > session.url = inline("<button></button>") > button = session.find.css("button", all=False) > >@@ -249,7 +255,7 @@ def test_button(session): > assert_error(response, "invalid element state") > > >-def test_button_with_subtree(session): >+def test_button_with_subtree(session, inline): > """ > Elements inside button elements are interactable. > """ >@@ -264,7 +270,7 @@ def test_button_with_subtree(session): > assert_success(response) > > >-def test_contenteditable(session, add_event_listeners, tracked_events): >+def test_contenteditable(session, inline, add_event_listeners, tracked_events): > session.url = inline("<p contenteditable>foobar</p>") > element = session.find.css("p", all=False) > add_event_listeners(element, tracked_events) >@@ -277,7 +283,7 @@ def test_contenteditable(session, add_event_listeners, tracked_events): > assert_element_has_focus(session.execute_script("return document.body")) > > >-def test_designmode(session): >+def test_designmode(session, inline): > session.url = inline("foobar") > element = session.find.css("body", all=False) > assert element.property("innerHTML") == "foobar" >@@ -289,7 +295,7 @@ def test_designmode(session): > assert_element_has_focus(session.execute_script("return document.body")) > > >-def test_resettable_element_focus_when_empty(session, add_event_listeners, tracked_events): >+def test_resettable_element_focus_when_empty(session, inline, add_event_listeners, tracked_events): > session.url = inline("<input>") > element = session.find.css("input", all=False) > add_event_listeners(element, tracked_events) >@@ -313,7 +319,7 @@ def test_resettable_element_focus_when_empty(session, add_event_listeners, track > ("time", "foo"), > ("month", "foo"), > ("week", "foo")]) >-def test_resettable_element_does_not_satisfy_validation_constraints(session, type, invalid_value): >+def test_resettable_element_does_not_satisfy_validation_constraints(session, inline, type, invalid_value): > """ > Some UAs allow invalid input to certain types of constrained > form controls. For example, Gecko allows non-valid characters >@@ -350,7 +356,7 @@ def test_resettable_element_does_not_satisfy_validation_constraints(session, typ > "submit", > "button", > "image"]) >-def test_non_editable_inputs(session, type): >+def test_non_editable_inputs(session, inline, type): > session.url = inline("<input type=%s>" % type) > element = session.find.css("input", all=False) > >@@ -358,7 +364,7 @@ def test_non_editable_inputs(session, type): > assert_error(response, "invalid element state") > > >-def test_scroll_into_view(session): >+def test_scroll_into_view(session, inline): > session.url = inline(""" > <input value=foobar> > <div style='height: 200vh; width: 5000vh'> >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_clear/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/element_clear/user_prompts.py >index cfb850e08d5a..7a8564a684a7 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/element_clear/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/element_clear/user_prompts.py >@@ -3,7 +3,6 @@ > import pytest > > from tests.support.asserts import assert_dialog_handled, assert_error, assert_success >-from tests.support.inline import inline > > > def element_clear(session, element): >@@ -14,7 +13,7 @@ def element_clear(session, element): > > > @pytest.fixture >-def check_user_prompt_closed_without_exception(session, create_dialog): >+def check_user_prompt_closed_without_exception(session, create_dialog, inline): > def check_user_prompt_closed_without_exception(dialog_type, retval): > session.url = inline("<input type=text>") > element = session.find.css("input", all=False) >@@ -35,7 +34,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_closed_with_exception(session, create_dialog): >+def check_user_prompt_closed_with_exception(session, create_dialog, inline): > def check_user_prompt_closed_with_exception(dialog_type, retval): > session.url = inline("<input type=text>") > element = session.find.css("input", all=False) >@@ -56,7 +55,7 @@ def check_user_prompt_closed_with_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_not_closed_but_exception(session, create_dialog): >+def check_user_prompt_not_closed_but_exception(session, create_dialog, inline): > def check_user_prompt_not_closed_but_exception(dialog_type): > session.url = inline("<input type=text>") > element = session.find.css("input", all=False) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_click/bubbling.py b/WebDriverTests/imported/w3c/webdriver/tests/element_click/bubbling.py >index a069a5a5df3d..7620ec3224fc 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/element_click/bubbling.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/element_click/bubbling.py >@@ -1,5 +1,4 @@ > from tests.support.asserts import assert_success >-from tests.support.inline import inline > > > def element_click(session, element): >@@ -9,7 +8,7 @@ def element_click(session, element): > element_id=element.id)) > > >-def test_click_event_bubbles_to_parents(session): >+def test_click_event_bubbles_to_parents(session, inline): > session.url = inline(""" > <style> > body * { >@@ -45,7 +44,7 @@ def test_click_event_bubbles_to_parents(session): > assert three in clicks > > >-def test_spin_event_loop(session): >+def test_spin_event_loop(session, inline): > """ > Wait until the user agent event loop has spun enough times to > process the DOM events generated by clicking. >@@ -86,7 +85,7 @@ def test_spin_event_loop(session): > assert three in delayed_clicks > > >-def test_element_disappears_during_click(session): >+def test_element_disappears_during_click(session, inline): > """ > When an element in the event bubbling order disappears (its CSS > display style is set to "none") during a click, Gecko and Blink >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_click/center_point.py b/WebDriverTests/imported/w3c/webdriver/tests/element_click/center_point.py >index da60a087277d..ef0c9a7fe27d 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/element_click/center_point.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/element_click/center_point.py >@@ -2,7 +2,6 @@ import pytest > > from tests.support.asserts import assert_error, assert_success > from tests.support.helpers import center_point >-from tests.support.inline import inline > > > def element_click(session, element): >@@ -12,7 +11,7 @@ def element_click(session, element): > element_id=element.id)) > > >-def square(size): >+def square(inline, size): > return inline(""" > <style> > body {{ margin: 0 }} >@@ -41,8 +40,8 @@ def assert_one_click(session): > return tuple(clicks[0]) > > >-def test_entirely_in_view(session): >- session.url = square(444) >+def test_entirely_in_view(session, inline): >+ session.url = square(inline, 444) > element = session.find.css("#target", all=False) > > response = element_click(session, element) >@@ -53,8 +52,8 @@ def test_entirely_in_view(session): > > > @pytest.mark.parametrize("size", range(1, 11)) >-def test_css_pixel_rounding(session, size): >- session.url = square(size) >+def test_css_pixel_rounding(session, inline, size): >+ session.url = square(inline, size) > element = session.find.css("#target", all=False) > expected_click_point = center_point(element) > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_click/click.py b/WebDriverTests/imported/w3c/webdriver/tests/element_click/click.py >index 3a5d737216d9..b1b10162e4d6 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/element_click/click.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/element_click/click.py >@@ -1,7 +1,6 @@ > from webdriver import Element > > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline > > > def element_click(session, element): >@@ -11,7 +10,7 @@ def element_click(session, element): > element_id=element.id)) > > >-def test_null_response_value(session): >+def test_null_response_value(session, inline): > session.url = inline("<p>foo") > element = session.find.css("p", all=False) > >@@ -22,10 +21,17 @@ def test_null_response_value(session): > > def test_no_top_browsing_context(session, closed_window): > element = Element("foo", session) >+ response = element_click(session, element) >+ assert_error(response, "no such window") > >+ original_handle, element = closed_window > response = element_click(session, element) > assert_error(response, "no such window") > >+ session.window_handle = original_handle >+ response = element_click(session, element) >+ assert_error(response, "no such element") >+ > > def test_no_browsing_context(session, closed_frame): > element = Element("foo", session) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_click/events.py b/WebDriverTests/imported/w3c/webdriver/tests/element_click/events.py >new file mode 100644 >index 000000000000..e3d32c1bcb44 >--- /dev/null >+++ b/WebDriverTests/imported/w3c/webdriver/tests/element_click/events.py >@@ -0,0 +1,33 @@ >+from webdriver import Element >+from tests.support.asserts import assert_success >+from tests.support.helpers import filter_dict >+ >+def get_events(session): >+ """Return list of mouse events recorded in the fixture.""" >+ return session.execute_script("return allEvents.events;") or [] >+ >+def element_click(session, element): >+ return session.transport.send( >+ "POST", "session/{session_id}/element/{element_id}/click".format( >+ session_id=session.session_id, >+ element_id=element.id)) >+ >+def test_event_mousemove(session, url): >+ session.url = url("/webdriver/tests/element_click/support/test_click_wdspec.html") >+ >+ element = session.find.css('#outer', all=False) >+ response = element_click(session, element) >+ assert_success(response) >+ >+ events = get_events(session) >+ assert len(events) == 4 >+ >+ expected = [ >+ {"type": "mousemove", "buttons": 0, "button": 0}, >+ {"type": "mousedown", "buttons": 1, "button": 0}, >+ {"type": "mouseup", "buttons": 0, "button": 0}, >+ {"type": "click", "buttons": 0, "button": 0}, >+ ] >+ filtered_events = [filter_dict(e, expected[0]) for e in events] >+ >+ assert expected == filtered_events >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_click/file_upload.py b/WebDriverTests/imported/w3c/webdriver/tests/element_click/file_upload.py >index cd5bb434204d..73832d0f85ae 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/element_click/file_upload.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/element_click/file_upload.py >@@ -1,5 +1,5 @@ > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline >+ > > def element_click(session, element): > return session.transport.send( >@@ -8,7 +8,7 @@ def element_click(session, element): > element_id=element.id)) > > >-def test_file_upload_state(session): >+def test_file_upload_state(session,inline): > session.url = inline("<input type=file>") > > element = session.find.css("input", all=False) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_click/interactability.py b/WebDriverTests/imported/w3c/webdriver/tests/element_click/interactability.py >index 7463d493e3d8..d55860c8746f 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/element_click/interactability.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/element_click/interactability.py >@@ -1,7 +1,6 @@ > import pytest > > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline > > > def element_click(session, element): >@@ -11,7 +10,7 @@ def element_click(session, element): > element_id=element.id)) > > >-def test_display_none(session): >+def test_display_none(session, inline): > session.url = inline("""<button style="display: none">foobar</button>""") > element = session.find.css("button", all=False) > >@@ -19,7 +18,7 @@ def test_display_none(session): > assert_error(response, "element not interactable") > > >-def test_visibility_hidden(session): >+def test_visibility_hidden(session, inline): > session.url = inline("""<button style="visibility: hidden">foobar</button>""") > element = session.find.css("button", all=False) > >@@ -27,7 +26,7 @@ def test_visibility_hidden(session): > assert_error(response, "element not interactable") > > >-def test_hidden(session): >+def test_hidden(session, inline): > session.url = inline("<button hidden>foobar</button>") > element = session.find.css("button", all=False) > >@@ -35,7 +34,7 @@ def test_hidden(session): > assert_error(response, "element not interactable") > > >-def test_disabled(session): >+def test_disabled(session, inline): > session.url = inline("""<button disabled>foobar</button>""") > element = session.find.css("button", all=False) > >@@ -44,7 +43,7 @@ def test_disabled(session): > > > @pytest.mark.parametrize("transform", ["translate(-100px, -100px)", "rotate(50deg)"]) >-def test_element_not_interactable_css_transform(session, transform): >+def test_element_not_interactable_css_transform(session, inline, transform): > session.url = inline(""" > <div style="width: 500px; height: 100px; > background-color: blue; transform: {transform};"> >@@ -55,7 +54,7 @@ def test_element_not_interactable_css_transform(session, transform): > assert_error(response, "element not interactable") > > >-def test_element_not_interactable_out_of_view(session): >+def test_element_not_interactable_out_of_view(session, inline): > session.url = inline(""" > <style> > input { >@@ -73,7 +72,7 @@ def test_element_not_interactable_out_of_view(session): > > > @pytest.mark.parametrize("tag_name", ["div", "span"]) >-def test_zero_sized_element(session, tag_name): >+def test_zero_sized_element(session, inline, tag_name): > session.url = inline("<{0}></{0}>".format(tag_name)) > element = session.find.css(tag_name, all=False) > >@@ -81,7 +80,7 @@ def test_zero_sized_element(session, tag_name): > assert_error(response, "element not interactable") > > >-def test_element_intercepted(session): >+def test_element_intercepted(session, inline): > session.url = inline(""" > <style> > div { >@@ -102,14 +101,14 @@ def test_element_intercepted(session): > assert_error(response, "element click intercepted") > > >-def test_element_intercepted_no_pointer_events(session): >+def test_element_intercepted_no_pointer_events(session, inline): > session.url = inline("""<input type=button value=Roger style="pointer-events: none">""") > element = session.find.css("input", all=False) > response = element_click(session, element) > assert_error(response, "element click intercepted") > > >-def test_element_not_visible_overflow_hidden(session): >+def test_element_not_visible_overflow_hidden(session, inline): > session.url = inline(""" > <style> > div { >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_click/navigate.py b/WebDriverTests/imported/w3c/webdriver/tests/element_click/navigate.py >index d776a1eb84e6..492718292a17 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/element_click/navigate.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/element_click/navigate.py >@@ -1,5 +1,9 @@ >+import pytest >+ >+from webdriver.error import NoSuchElementException >+ > from tests.support.asserts import assert_success >-from tests.support.inline import inline >+from tests.support.helpers import wait_for_new_handle > from tests.support.sync import Poll > > >@@ -10,7 +14,7 @@ def element_click(session, element): > element_id=element.id)) > > >-def test_numbers_link(session, server_config): >+def test_numbers_link(session, server_config, inline): > link = "/webdriver/tests/element_click/support/input.html" > session.url = inline("<a href={url}>123456</a>".format(url=link)) > element = session.find.css("a", all=False) >@@ -22,7 +26,7 @@ def test_numbers_link(session, server_config): > assert session.url == "http://{host}:{port}{url}".format(host=host, port=port, url=link) > > >-def test_multi_line_link(session, server_config): >+def test_multi_line_link(session, server_config, inline): > link = "/webdriver/tests/element_click/support/input.html" > session.url = inline(""" > <p style="background-color: yellow; width: 50px;"> >@@ -37,7 +41,7 @@ def test_multi_line_link(session, server_config): > assert session.url == "http://{host}:{port}{url}".format(host=host, port=port, url=link) > > >-def test_link_unload_event(session, server_config): >+def test_link_unload_event(session, server_config, inline): > link = "/webdriver/tests/element_click/support/input.html" > session.url = inline(""" > <body onunload="checkUnload()"> >@@ -68,7 +72,7 @@ def test_link_unload_event(session, server_config): > assert response is True > > >-def test_link_hash(session): >+def test_link_hash(session, inline): > id = "anchor" > session.url = inline(""" > <a href="#{url}">aaaa</a> >@@ -93,34 +97,94 @@ def test_link_hash(session): > """, args=(element,)) is True > > >-def test_link_open_target_in_new_window(session, url): >- orig_handles = session.handles >+@pytest.mark.parametrize("target", [ >+ "", >+ "_blank", >+ "_parent", >+ "_self", >+ "_top", >+]) >+def test_link_from_toplevel_context_with_target(session, inline, target): >+ target_page = inline("<p id='foo'>foo</p>") > >- session.url = inline(""" >- <a href="{}" target="_blank">Open in new window</a> >- """.format(inline("<p id=foo"))) >+ session.url = inline("<a href='{}' target='{}'>click</a>".format(target_page, target)) > element = session.find.css("a", all=False) > >+ orig_handles = session.handles >+ > response = element_click(session, element) > assert_success(response) > >- def find_new_handle(session): >- new_handles = list(set(session.handles) - set(orig_handles)) >- if new_handles and len(new_handles) == 1: >- return new_handles[0] >- return None >+ if target == "_blank": >+ session.window_handle = wait_for_new_handle(session, orig_handles) > > wait = Poll( > session, > timeout=5, >- message="No new window has been opened") >- new_handle = wait.until(find_new_handle) >+ ignored_exceptions=NoSuchElementException, >+ message="Expected element has not been found") >+ wait.until(lambda s: s.find.css("#foo")) >+ >+ >+@pytest.mark.parametrize("target", [ >+ "", >+ "_blank", >+ "_parent", >+ "_self", >+ "_top", >+]) >+def test_link_from_nested_context_with_target(session, inline, iframe, target): >+ target_page = inline("<p id='foo'>foo</p>") >+ >+ session.url = inline(iframe("<a href='{}' target='{}'>click</a>".format(target_page, target))) >+ frame = session.find.css("iframe", all=False) >+ session.switch_frame(frame) >+ element = session.find.css("a".format(target), all=False) > >- session.window_handle = new_handle >- session.find.css("#foo") >+ orig_handles = session.handles >+ >+ response = element_click(session, element) >+ assert_success(response) >+ >+ if target == "_blank": >+ session.window_handle = wait_for_new_handle(session, orig_handles) >+ >+ # With the current browsing context removed the navigation should >+ # not timeout. Switch to the target context, and wait until the expected >+ # element is available. >+ if target == "_parent": >+ session.switch_frame("parent") >+ elif target == "_top": >+ session.switch_frame(None) >+ >+ wait = Poll( >+ session, >+ timeout=5, >+ ignored_exceptions=NoSuchElementException, >+ message="Expected element has not been found") >+ wait.until(lambda s: s.find.css("#foo")) >+ >+ >+# Capability needed as long as no valid certificate is available: >+# https://github.com/web-platform-tests/wpt/issues/28847 >+@pytest.mark.capabilities({"acceptInsecureCerts": True}) >+def test_link_cross_origin(session, inline, url): >+ base_path = ("/webdriver/tests/support/html/subframe.html" + >+ "?pipe=header(Cross-Origin-Opener-Policy,same-origin") >+ target_page = url(base_path, protocol="https", domain="alt") >+ >+ session.url = inline("<a href='{}'>click me</a>".format(target_page), protocol="https") >+ link = session.find.css("a", all=False) >+ >+ response = element_click(session, link) >+ assert_success(response) >+ >+ assert session.url == target_page >+ >+ session.find.css("#delete", all=False) > > >-def test_link_closes_window(session): >+def test_link_closes_window(session, inline): > new_handle = session.new_window() > session.window_handle = new_handle > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_click/scroll_into_view.py b/WebDriverTests/imported/w3c/webdriver/tests/element_click/scroll_into_view.py >index 4b026e986a21..c2dc6485286e 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/element_click/scroll_into_view.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/element_click/scroll_into_view.py >@@ -2,7 +2,7 @@ import pytest > > from tests.support.asserts import assert_error, assert_success > from tests.support.helpers import center_point >-from tests.support.inline import inline >+ > > def element_click(session, element): > return session.transport.send( >@@ -18,7 +18,7 @@ def assert_one_click(session): > return tuple(clicks[0]) > > >-def test_scroll_into_view(session): >+def test_scroll_into_view(session, inline): > session.url = inline(""" > <input type=text value=Federer > style="position: absolute; left: 0vh; top: 500vh">""") >@@ -38,7 +38,7 @@ def test_scroll_into_view(session): > > > @pytest.mark.parametrize("offset", range(9, 0, -1)) >-def test_partially_visible_does_not_scroll(session, offset): >+def test_partially_visible_does_not_scroll(session, offset, inline): > session.url = inline(""" > <style> > body {{ >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_click/select.py b/WebDriverTests/imported/w3c/webdriver/tests/element_click/select.py >index dcf92e645894..62d40755b59b 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/element_click/select.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/element_click/select.py >@@ -1,7 +1,4 @@ >-from tests.support.inline import inline >- >- >-def test_click_option(session): >+def test_click_option(session, inline): > session.url = inline(""" > <select> > <option>first >@@ -17,7 +14,7 @@ def test_click_option(session): > assert not options[0].selected > > >-def test_click_multiple_option(session): >+def test_click_multiple_option(session, inline): > session.url = inline(""" > <select multiple> > <option>first >@@ -33,7 +30,7 @@ def test_click_multiple_option(session): > assert not options[1].selected > > >-def test_click_preselected_option(session): >+def test_click_preselected_option(session, inline): > session.url = inline(""" > <select> > <option>first >@@ -53,7 +50,7 @@ def test_click_preselected_option(session): > assert not options[1].selected > > >-def test_click_preselected_multiple_option(session): >+def test_click_preselected_multiple_option(session, inline): > session.url = inline(""" > <select multiple> > <option>first >@@ -73,7 +70,7 @@ def test_click_preselected_multiple_option(session): > assert not options[1].selected > > >-def test_click_deselects_others(session): >+def test_click_deselects_others(session, inline): > session.url = inline(""" > <select> > <option>first >@@ -92,7 +89,7 @@ def test_click_deselects_others(session): > assert options[0].selected > > >-def test_click_multiple_does_not_deselect_others(session): >+def test_click_multiple_does_not_deselect_others(session, inline): > session.url = inline(""" > <select multiple> > <option>first >@@ -112,7 +109,7 @@ def test_click_multiple_does_not_deselect_others(session): > assert options[2].selected > > >-def test_click_selected_option(session): >+def test_click_selected_option(session, inline): > session.url = inline(""" > <select> > <option>first >@@ -130,7 +127,7 @@ def test_click_selected_option(session): > assert options[1].selected > > >-def test_click_selected_multiple_option(session): >+def test_click_selected_multiple_option(session, inline): > session.url = inline(""" > <select multiple> > <option>first >@@ -152,7 +149,7 @@ def test_click_selected_multiple_option(session): > assert not options[1].selected > > >-def test_out_of_view_dropdown(session): >+def test_out_of_view_dropdown(session, inline): > session.url = inline(""" > <select> > <option>1 >@@ -182,7 +179,7 @@ def test_out_of_view_dropdown(session): > assert options[14].selected > > >-def test_out_of_view_multiple(session): >+def test_out_of_view_multiple(session, inline): > session.url = inline(""" > <select multiple> > <option>1 >@@ -213,7 +210,7 @@ def test_out_of_view_multiple(session): > assert last_option.selected > > >-def test_option_disabled(session): >+def test_option_disabled(session, inline): > session.url = inline(""" > <select> > <option disabled>foo >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_click/shadow_dom.py b/WebDriverTests/imported/w3c/webdriver/tests/element_click/shadow_dom.py >index b52f27429c19..45493b730d69 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/element_click/shadow_dom.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/element_click/shadow_dom.py >@@ -1,6 +1,5 @@ >-from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline > import pytest >+from tests.support.asserts import assert_error, assert_success > > > def element_click(session, element): >@@ -10,7 +9,8 @@ def element_click(session, element): > element_id=element.id)) > > >-def get_checkbox_dom(): >+@pytest.fixture >+def get_checkbox_dom(inline): > return inline(""" > <style> > custom-checkbox-element { >@@ -32,8 +32,8 @@ def get_checkbox_dom(): > > > @pytest.mark.parametrize("click_on", ["custom_element", "checkbox_element"]) >-def test_shadow_element_click(session, click_on): >- session.url = get_checkbox_dom() >+def test_shadow_element_click(session, get_checkbox_dom, click_on): >+ session.url = get_checkbox_dom > custom_element = session.find.css("custom-checkbox-element", all=False) > checkbox_element = session.execute_script("return arguments[0].shadowRoot.querySelector('input')", > args=(custom_element,)) >@@ -47,7 +47,8 @@ def test_shadow_element_click(session, click_on): > assert is_post_checked == True > > >-def get_nested_shadow_checkbox_dom(): >+@pytest.fixture >+def get_nested_shadow_checkbox_dom(inline): > return inline(""" > <style> > custom-nesting-element { >@@ -83,8 +84,8 @@ def get_nested_shadow_checkbox_dom(): > > > @pytest.mark.parametrize("click_on", ["outer_element", "inner_element", "checkbox_element"]) >-def test_nested_shadow_element_click(session, click_on): >- session.url = get_nested_shadow_checkbox_dom() >+def test_nested_shadow_element_click(session, get_nested_shadow_checkbox_dom, click_on): >+ session.url = get_nested_shadow_checkbox_dom > outer_element = session.find.css("custom-nesting-element", all=False) > inner_element = session.execute_script("return arguments[0].shadowRoot.querySelector('custom-checkbox-element')", > args=(outer_element,)) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_click/stale.py b/WebDriverTests/imported/w3c/webdriver/tests/element_click/stale.py >index cd24dd70603d..8d2ae348ef4f 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/element_click/stale.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/element_click/stale.py >@@ -1,5 +1,4 @@ > from tests.support.asserts import assert_error >-from tests.support.inline import inline > > > def element_click(session, element): >@@ -9,7 +8,7 @@ def element_click(session, element): > element_id=element.id)) > > >-def test_is_stale(session): >+def test_is_stale(session, inline): > session.url = inline("<button>foo</button>") > button = session.find.css("button", all=False) > session.url = inline("<button>bar</button>") >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_click/support/test_click_wdspec.html b/WebDriverTests/imported/w3c/webdriver/tests/element_click/support/test_click_wdspec.html >new file mode 100644 >index 000000000000..a9451ec82bec >--- /dev/null >+++ b/WebDriverTests/imported/w3c/webdriver/tests/element_click/support/test_click_wdspec.html >@@ -0,0 +1,100 @@ >+<!doctype html> >+<meta charset=utf-8> >+<html> >+<head> >+ <title>Test Element Click</title> >+ <style> >+ div { padding:0px; margin: 0px; } >+ #trackPointer { position: fixed; } >+ #resultContainer { width: 600px; height: 60px; } >+ .area { width: 100px; height: 50px; background-color: #ccc; } >+ .block { width: 5px; height: 5px; border: solid 1px red; } >+ </style> >+ <script> >+ "use strict"; >+ var els = {}; >+ var allEvents = { events: [] }; >+ function displayMessage(message) { >+ document.getElementById("events").innerHTML = "<p>" + message + "</p>"; >+ } >+ >+ function appendMessage(message) { >+ document.getElementById("events").innerHTML += "<p>" + message + "</p>"; >+ } >+ >+ function recordPointerEvent(event) { >+ if (event.type === "contextmenu") { >+ event.preventDefault(); >+ } >+ allEvents.events.push({ >+ "type": event.type, >+ "button": event.button, >+ "buttons": event.buttons, >+ "pageX": event.pageX, >+ "pageY": event.pageY, >+ "ctrlKey": event.ctrlKey, >+ "metaKey": event.metaKey, >+ "altKey": event.altKey, >+ "shiftKey": event.shiftKey, >+ "target": event.target.id >+ }); >+ appendMessage(event.type + " " + >+ "button: " + event.button + ", " + >+ "pageX: " + event.pageX + ", " + >+ "pageY: " + event.pageY + ", " + >+ "button: " + event.button + ", " + >+ "buttons: " + event.buttons + ", " + >+ "ctrlKey: " + event.ctrlKey + ", " + >+ "altKey: " + event.altKey + ", " + >+ "metaKey: " + event.metaKey + ", " + >+ "shiftKey: " + event.shiftKey + ", " + >+ "target id: " + event.target.id); >+ } >+ >+ function recordFirstPointerMove(event) { >+ recordPointerEvent(event); >+ window.removeEventListener("mousemove", recordFirstPointerMove); >+ } >+ >+ function resetEvents() { >+ allEvents.events.length = 0; >+ displayMessage(""); >+ } >+ >+ function move(el, offsetX, offsetY, timeout) { >+ return function(event) { >+ setTimeout(function() { >+ el.style.top = event.clientY + offsetY + "px"; >+ el.style.left = event.clientX + offsetX + "px"; >+ }, timeout); >+ }; >+ } >+ >+ document.addEventListener("DOMContentLoaded", function() { >+ var outer = document.getElementById("outer"); >+ window.addEventListener("mousemove", recordFirstPointerMove); >+ outer.addEventListener("click", recordPointerEvent); >+ outer.addEventListener("dblclick", recordPointerEvent); >+ outer.addEventListener("mousedown", recordPointerEvent); >+ outer.addEventListener("mouseup", recordPointerEvent); >+ outer.addEventListener("contextmenu", recordPointerEvent); >+ >+ //visual cue for mousemove >+ var pointer = document.getElementById("trackPointer"); >+ window.addEventListener("mousemove", move(pointer, 15, 15, 30)); >+ }); >+ </script> >+</head> >+<body> >+ <div id="trackPointer" class="block"></div> >+ <div> >+ <h2>ClickReporter</h2> >+ <div id="outer" class="area"> >+ </div> >+ </div> >+ <div id="resultContainer"> >+ <h2>Events</h2> >+ <div id="events"></div> >+ </div> >+</body> >+</html> >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_click/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/element_click/user_prompts.py >index b3706fe4af16..140aceb3cedf 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/element_click/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/element_click/user_prompts.py >@@ -3,7 +3,6 @@ > import pytest > > from tests.support.asserts import assert_dialog_handled, assert_error, assert_success >-from tests.support.inline import inline > > > def element_click(session, element): >@@ -14,7 +13,7 @@ def element_click(session, element): > > > @pytest.fixture >-def check_user_prompt_closed_without_exception(session, create_dialog): >+def check_user_prompt_closed_without_exception(session, create_dialog, inline): > def check_user_prompt_closed_without_exception(dialog_type, retval): > session.url = inline("<input type=text>") > element = session.find.css("input", all=False) >@@ -32,7 +31,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_closed_with_exception(session, create_dialog): >+def check_user_prompt_closed_with_exception(session, create_dialog, inline): > def check_user_prompt_closed_with_exception(dialog_type, retval): > session.url = inline("<input type=text>") > element = session.find.css("input", all=False) >@@ -50,7 +49,7 @@ def check_user_prompt_closed_with_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_not_closed_but_exception(session, create_dialog): >+def check_user_prompt_not_closed_but_exception(session, create_dialog, inline): > def check_user_prompt_not_closed_but_exception(dialog_type): > session.url = inline("<input type=text>") > element = session.find.css("input", all=False) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/content_editable.py b/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/content_editable.py >index 16ebc2d018fb..7f628f96a53a 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/content_editable.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/content_editable.py >@@ -1,9 +1,4 @@ >-import pytest >- >-from tests.support.inline import inline >- >- >-def test_sets_insertion_point_to_end(session): >+def test_sets_insertion_point_to_end(session, inline): > session.url = inline('<div contenteditable=true>Hello,</div>') > input = session.find.css("div", all=False) > input.send_keys(' world!') >@@ -15,7 +10,7 @@ def test_sets_insertion_point_to_end(session): > # > # 13. Set the text insertion caret using set selection range using current > # text length for both the start and end parameters. >-def test_sets_insertion_point_to_after_last_text_node(session): >+def test_sets_insertion_point_to_after_last_text_node(session, inline): > session.url = inline('<div contenteditable=true>Hel<span>lo</span>,</div>') > input = session.find.css("div", all=False) > input.send_keys(" world!") >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/events.py b/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/events.py >index 41b00ef37fa2..4be1432bf3c2 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/events.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/events.py >@@ -5,7 +5,6 @@ from tests.support.asserts import ( > assert_events_equal, > assert_success, > ) >-from tests.support.inline import inline > > from . import map_files_to_multiline_text > >@@ -31,7 +30,7 @@ def element_send_keys(session, element, text): > {"text": text}) > > >-def test_file_upload(session, create_files, add_event_listeners, tracked_events): >+def test_file_upload(session, create_files, add_event_listeners, tracked_events, inline): > expected_events = [ > "input", > "change", >@@ -50,7 +49,7 @@ def test_file_upload(session, create_files, add_event_listeners, tracked_events) > > > @pytest.mark.parametrize("tag", ["input", "textarea"]) >-def test_form_control_send_text(session, add_event_listeners, tracked_events, tag): >+def test_form_control_send_text(session, add_event_listeners, tracked_events, inline, tag): > expected_events = [ > "focus", > "keydown", >@@ -77,7 +76,7 @@ def test_form_control_send_text(session, add_event_listeners, tracked_events, ta > > > @pytest.mark.parametrize("tag", ["input", "textarea"]) >-def test_not_blurred(session, tag): >+def test_not_blurred(session, inline, tag): > session.url = inline("<%s>" % tag) > element = session.find.css(tag, all=False) > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/file_upload.py b/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/file_upload.py >index 7fbab8a66aa4..ef792f4dd855 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/file_upload.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/file_upload.py >@@ -1,7 +1,6 @@ > import pytest > > from tests.support.asserts import assert_error, assert_files_uploaded, assert_success >-from tests.support.inline import inline > > from . import map_files_to_multiline_text > >@@ -14,7 +13,7 @@ def element_send_keys(session, element, text): > {"text": text}) > > >-def test_empty_text(session): >+def test_empty_text(session, inline): > session.url = inline("<input type=file>") > element = session.find.css("input", all=False) > >@@ -22,7 +21,7 @@ def test_empty_text(session): > assert_error(response, "invalid argument") > > >-def test_multiple_files(session, create_files): >+def test_multiple_files(session, create_files, inline): > files = create_files(["foo", "bar"]) > > session.url = inline("<input type=file multiple>") >@@ -35,7 +34,7 @@ def test_multiple_files(session, create_files): > assert_files_uploaded(session, element, files) > > >-def test_multiple_files_last_path_not_found(session, create_files): >+def test_multiple_files_last_path_not_found(session, create_files, inline): > files = create_files(["foo", "bar"]) > files.append("foo bar") > >@@ -49,7 +48,7 @@ def test_multiple_files_last_path_not_found(session, create_files): > assert_files_uploaded(session, element, []) > > >-def test_multiple_files_without_multiple_attribute(session, create_files): >+def test_multiple_files_without_multiple_attribute(session, create_files, inline): > files = create_files(["foo", "bar"]) > > session.url = inline("<input type=file>") >@@ -62,7 +61,7 @@ def test_multiple_files_without_multiple_attribute(session, create_files): > assert_files_uploaded(session, element, []) > > >-def test_multiple_files_send_twice(session, create_files): >+def test_multiple_files_send_twice(session, create_files, inline): > first_files = create_files(["foo", "bar"]) > second_files = create_files(["john", "doe"]) > >@@ -80,7 +79,7 @@ def test_multiple_files_send_twice(session, create_files): > assert_files_uploaded(session, element, first_files + second_files) > > >-def test_multiple_files_reset_with_element_clear(session, create_files): >+def test_multiple_files_reset_with_element_clear(session, create_files, inline): > first_files = create_files(["foo", "bar"]) > second_files = create_files(["john", "doe"]) > >@@ -102,7 +101,7 @@ def test_multiple_files_reset_with_element_clear(session, create_files): > assert_files_uploaded(session, element, second_files) > > >-def test_single_file(session, create_files): >+def test_single_file(session, create_files, inline): > files = create_files(["foo"]) > > session.url = inline("<input type=file>") >@@ -114,7 +113,7 @@ def test_single_file(session, create_files): > assert_files_uploaded(session, element, files) > > >-def test_single_file_replaces_without_multiple_attribute(session, create_files): >+def test_single_file_replaces_without_multiple_attribute(session, create_files, inline): > files = create_files(["foo", "bar"]) > > session.url = inline("<input type=file>") >@@ -129,7 +128,7 @@ def test_single_file_replaces_without_multiple_attribute(session, create_files): > assert_files_uploaded(session, element, [files[1]]) > > >-def test_single_file_appends_with_multiple_attribute(session, create_files): >+def test_single_file_appends_with_multiple_attribute(session, create_files, inline): > files = create_files(["foo", "bar"]) > > session.url = inline("<input type=file multiple>") >@@ -144,7 +143,7 @@ def test_single_file_appends_with_multiple_attribute(session, create_files): > assert_files_uploaded(session, element, files) > > >-def test_transparent(session, create_files): >+def test_transparent(session, create_files, inline): > files = create_files(["foo"]) > session.url = inline("""<input type=file style="opacity: 0">""") > element = session.find.css("input", all=False) >@@ -154,7 +153,7 @@ def test_transparent(session, create_files): > assert_files_uploaded(session, element, files) > > >-def test_obscured(session, create_files): >+def test_obscured(session, create_files, inline): > files = create_files(["foo"]) > session.url = inline(""" > <style> >@@ -178,7 +177,7 @@ def test_obscured(session, create_files): > assert_files_uploaded(session, element, files) > > >-def test_outside_viewport(session, create_files): >+def test_outside_viewport(session, create_files, inline): > files = create_files(["foo"]) > session.url = inline("""<input type=file style="margin-left: -100vh">""") > element = session.find.css("input", all=False) >@@ -188,7 +187,7 @@ def test_outside_viewport(session, create_files): > assert_files_uploaded(session, element, files) > > >-def test_hidden(session, create_files): >+def test_hidden(session, create_files, inline): > files = create_files(["foo"]) > session.url = inline("<input type=file hidden>") > element = session.find.css("input", all=False) >@@ -198,7 +197,7 @@ def test_hidden(session, create_files): > assert_files_uploaded(session, element, files) > > >-def test_display_none(session, create_files): >+def test_display_none(session, create_files, inline): > files = create_files(["foo"]) > session.url = inline("""<input type=file style="display: none">""") > element = session.find.css("input", all=False) >@@ -209,7 +208,7 @@ def test_display_none(session, create_files): > > > @pytest.mark.capabilities({"strictFileInteractability": True}) >-def test_strict_hidden(session, create_files): >+def test_strict_hidden(session, create_files, inline): > files = create_files(["foo"]) > session.url = inline("<input type=file hidden>") > element = session.find.css("input", all=False) >@@ -219,7 +218,7 @@ def test_strict_hidden(session, create_files): > > > @pytest.mark.capabilities({"strictFileInteractability": True}) >-def test_strict_display_none(session, create_files): >+def test_strict_display_none(session, create_files, inline): > files = create_files(["foo"]) > session.url = inline("""<input type=file style="display: none">""") > element = session.find.css("input", all=False) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/form_controls.py b/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/form_controls.py >index e53d0610f1ed..0403dc92372e 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/form_controls.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/form_controls.py >@@ -1,5 +1,4 @@ > from tests.support.asserts import assert_element_has_focus >-from tests.support.inline import inline > > > def element_send_keys(session, element, text): >@@ -10,7 +9,7 @@ def element_send_keys(session, element, text): > {"text": text}) > > >-def test_input(session): >+def test_input(session, inline): > session.url = inline("<input>") > element = session.find.css("input", all=False) > assert element.property("value") == "" >@@ -20,7 +19,7 @@ def test_input(session): > assert_element_has_focus(element) > > >-def test_textarea(session): >+def test_textarea(session, inline): > session.url = inline("<textarea>") > element = session.find.css("textarea", all=False) > assert element.property("value") == "" >@@ -30,7 +29,7 @@ def test_textarea(session): > assert_element_has_focus(element) > > >-def test_input_append(session): >+def test_input_append(session, inline): > session.url = inline("<input value=a>") > element = session.find.css("input", all=False) > assert element.property("value") == "a" >@@ -42,7 +41,7 @@ def test_input_append(session): > assert element.property("value") == "abc" > > >-def test_textarea_append(session): >+def test_textarea_append(session, inline): > session.url = inline("<textarea>a</textarea>") > element = session.find.css("textarea", all=False) > assert element.property("value") == "a" >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/interactability.py b/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/interactability.py >index 0717537ae5d8..273843fb7b46 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/interactability.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/interactability.py >@@ -1,5 +1,4 @@ > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import iframe, inline > > > def element_send_keys(session, element, text): >@@ -10,7 +9,7 @@ def element_send_keys(session, element, text): > {"text": text}) > > >-def test_body_is_interactable(session): >+def test_body_is_interactable(session, inline): > session.url = inline(""" > <body onkeypress="document.querySelector('input').value += event.key"> > <input> >@@ -29,7 +28,7 @@ def test_body_is_interactable(session): > assert result.property("value") == "foo" > > >-def test_document_element_is_interactable(session): >+def test_document_element_is_interactable(session, inline): > session.url = inline(""" > <html onkeypress="document.querySelector('input').value += event.key"> > <input> >@@ -49,7 +48,7 @@ def test_document_element_is_interactable(session): > assert result.property("value") == "foo" > > >-def test_iframe_is_interactable(session): >+def test_iframe_is_interactable(session, inline, iframe): > session.url = inline(iframe(""" > <body onkeypress="document.querySelector('input').value += event.key"> > <input> >@@ -73,7 +72,7 @@ def test_iframe_is_interactable(session): > assert result.property("value") == "foo" > > >-def test_transparent_element(session): >+def test_transparent_element(session, inline): > session.url = inline("""<input style="opacity: 0">""") > element = session.find.css("input", all=False) > >@@ -82,7 +81,7 @@ def test_transparent_element(session): > assert element.property("value") == "foo" > > >-def test_readonly_element(session): >+def test_readonly_element(session, inline): > session.url = inline("<input readonly>") > element = session.find.css("input", all=False) > >@@ -91,7 +90,7 @@ def test_readonly_element(session): > assert element.property("value") == "" > > >-def test_obscured_element(session): >+def test_obscured_element(session, inline): > session.url = inline(""" > <input> > <div style="position: relative; top: -3em; height: 5em; background: blue;"></div> >@@ -103,7 +102,7 @@ def test_obscured_element(session): > assert element.property("value") == "foo" > > >-def test_not_a_focusable_element(session): >+def test_not_a_focusable_element(session, inline): > session.url = inline("<div>foo</div>") > element = session.find.css("div", all=False) > >@@ -111,7 +110,7 @@ def test_not_a_focusable_element(session): > assert_error(response, "element not interactable") > > >-def test_display_none(session): >+def test_display_none(session, inline): > session.url = inline("""<input style="display: none">""") > element = session.find.css("input", all=False) > >@@ -119,7 +118,7 @@ def test_display_none(session): > assert_error(response, "element not interactable") > > >-def test_visibility_hidden(session): >+def test_visibility_hidden(session, inline): > session.url = inline("""<input style="visibility: hidden">""") > element = session.find.css("input", all=False) > >@@ -127,7 +126,7 @@ def test_visibility_hidden(session): > assert_error(response, "element not interactable") > > >-def test_hidden(session): >+def test_hidden(session, inline): > session.url = inline("<input hidden>") > element = session.find.css("input", all=False) > >@@ -135,7 +134,7 @@ def test_hidden(session): > assert_error(response, "element not interactable") > > >-def test_disabled(session): >+def test_disabled(session, inline): > session.url = inline("""<input disabled>""") > element = session.find.css("input", all=False) > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/scroll_into_view.py b/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/scroll_into_view.py >index 18f87ffaaec6..205c4e30e1ed 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/scroll_into_view.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/scroll_into_view.py >@@ -1,6 +1,6 @@ > from tests.support.asserts import assert_success > from tests.support.helpers import is_element_in_viewport >-from tests.support.inline import inline >+ > > def element_send_keys(session, element, text): > return session.transport.send( >@@ -10,7 +10,7 @@ def element_send_keys(session, element, text): > {"text": text}) > > >-def test_element_outside_of_not_scrollable_viewport(session): >+def test_element_outside_of_not_scrollable_viewport(session, inline): > session.url = inline("<input style=\"position: relative; left: -9999px;\">") > element = session.find.css("input", all=False) > >@@ -20,7 +20,7 @@ def test_element_outside_of_not_scrollable_viewport(session): > assert not is_element_in_viewport(session, element) > > >-def test_element_outside_of_scrollable_viewport(session): >+def test_element_outside_of_scrollable_viewport(session, inline): > session.url = inline("<input style=\"margin-top: 102vh;\">") > element = session.find.css("input", all=False) > >@@ -30,7 +30,7 @@ def test_element_outside_of_scrollable_viewport(session): > assert is_element_in_viewport(session, element) > > >-def test_option_select_container_outside_of_scrollable_viewport(session): >+def test_option_select_container_outside_of_scrollable_viewport(session, inline): > session.url = inline(""" > <select style="margin-top: 102vh;"> > <option value="foo">foo</option> >@@ -47,7 +47,7 @@ def test_option_select_container_outside_of_scrollable_viewport(session): > assert is_element_in_viewport(session, element) > > >-def test_option_stays_outside_of_scrollable_viewport(session): >+def test_option_stays_outside_of_scrollable_viewport(session, inline): > session.url = inline(""" > <select multiple style="height: 105vh; margin-top: 100vh;"> > <option value="foo" id="foo" style="height: 100vh;">foo</option> >@@ -66,7 +66,7 @@ def test_option_stays_outside_of_scrollable_viewport(session): > assert not is_element_in_viewport(session, option_bar) > > >-def test_contenteditable_element_outside_of_scrollable_viewport(session): >+def test_contenteditable_element_outside_of_scrollable_viewport(session, inline): > session.url = inline("<div contenteditable style=\"margin-top: 102vh;\"></div>") > element = session.find.css("div", all=False) > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/send_keys.py b/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/send_keys.py >index edf91e776148..aa393e16d814 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/send_keys.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/send_keys.py >@@ -4,7 +4,6 @@ from webdriver import Element > from webdriver.transport import Response > > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline > > > def element_send_keys(session, element, text): >@@ -15,7 +14,7 @@ def element_send_keys(session, element, text): > {"text": text}) > > >-def test_null_parameter_value(session, http): >+def test_null_parameter_value(session, http, inline): > session.url = inline("<input>") > element = session.find.css("input", all=False) > >@@ -25,7 +24,7 @@ def test_null_parameter_value(session, http): > assert_error(Response.from_http(response), "invalid argument") > > >-def test_null_response_value(session): >+def test_null_response_value(session, inline): > session.url = inline("<input>") > element = session.find.css("input", all=False) > >@@ -36,10 +35,17 @@ def test_null_response_value(session): > > def test_no_top_browsing_context(session, closed_window): > element = Element("foo", session) >+ response = element_send_keys(session, element, "foo") >+ assert_error(response, "no such window") > >+ original_handle, element = closed_window > response = element_send_keys(session, element, "foo") > assert_error(response, "no such window") > >+ session.window_handle = original_handle >+ response = element_send_keys(session, element, "foo") >+ assert_error(response, "no such element") >+ > > def test_no_browsing_context(session, closed_frame): > element = Element("foo", session) >@@ -49,7 +55,7 @@ def test_no_browsing_context(session, closed_frame): > > > @pytest.mark.parametrize("value", [True, None, 1, [], {}]) >-def test_invalid_text_type(session, value): >+def test_invalid_text_type(session, inline, value): > session.url = inline("<input>") > element = session.find.css("input", all=False) > >@@ -57,7 +63,7 @@ def test_invalid_text_type(session, value): > assert_error(response, "invalid argument") > > >-def test_stale_element(session): >+def test_stale_element(session, inline): > session.url = inline("<input>") > element = session.find.css("input", all=False) > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/user_prompts.py >index 621bdffa6642..c1046840fa4e 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/user_prompts.py >@@ -3,7 +3,6 @@ > import pytest > > from tests.support.asserts import assert_dialog_handled, assert_error, assert_success >-from tests.support.inline import inline > > > def element_send_keys(session, element, text): >@@ -15,7 +14,7 @@ def element_send_keys(session, element, text): > > > @pytest.fixture >-def check_user_prompt_closed_without_exception(session, create_dialog): >+def check_user_prompt_closed_without_exception(session, create_dialog, inline): > def check_user_prompt_closed_without_exception(dialog_type, retval): > session.url = inline("<input type=text>") > element = session.find.css("input", all=False) >@@ -33,7 +32,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_closed_with_exception(session, create_dialog): >+def check_user_prompt_closed_with_exception(session, create_dialog, inline): > def check_user_prompt_closed_with_exception(dialog_type, retval): > session.url = inline("<input type=text>") > element = session.find.css("input", all=False) >@@ -51,7 +50,7 @@ def check_user_prompt_closed_with_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_not_closed_but_exception(session, create_dialog): >+def check_user_prompt_not_closed_but_exception(session, create_dialog, inline): > def check_user_prompt_not_closed_but_exception(dialog_type): > session.url = inline("<input type=text>") > element = session.find.css("input", all=False) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/execute_async_script/collections.py b/WebDriverTests/imported/w3c/webdriver/tests/execute_async_script/collections.py >index 484a247cffd9..2dc9b9a009ac 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/execute_async_script/collections.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/execute_async_script/collections.py >@@ -1,9 +1,6 @@ > import os > >-from six import text_type >- > from tests.support.asserts import assert_same_element, assert_success >-from tests.support.inline import inline > > > def execute_async_script(session, script, args=None): >@@ -35,7 +32,7 @@ def test_array(session): > assert_success(response, [1, 2]) > > >-def test_file_list(session, tmpdir): >+def test_file_list(session, tmpdir, inline): > files = [tmpdir.join("foo.txt"), tmpdir.join("bar.txt")] > > session.url = inline("<input type=file multiple>") >@@ -54,11 +51,11 @@ def test_file_list(session, tmpdir): > for expected, actual in zip(files, value): > assert isinstance(actual, dict) > assert "name" in actual >- assert isinstance(actual["name"], text_type) >+ assert isinstance(actual["name"], str) > assert os.path.basename(str(expected)) == actual["name"] > > >-def test_html_all_collection(session): >+def test_html_all_collection(session, inline): > session.url = inline(""" > <p>foo > <p>bar >@@ -86,7 +83,7 @@ def test_html_all_collection(session): > assert_same_element(session, ps[1], value[5]) > > >-def test_html_collection(session): >+def test_html_collection(session, inline): > session.url = inline(""" > <p>foo > <p>bar >@@ -104,7 +101,7 @@ def test_html_collection(session): > assert_same_element(session, expected, actual) > > >-def test_html_form_controls_collection(session): >+def test_html_form_controls_collection(session, inline): > session.url = inline(""" > <form> > <input> >@@ -124,7 +121,7 @@ def test_html_form_controls_collection(session): > assert_same_element(session, expected, actual) > > >-def test_html_options_collection(session): >+def test_html_options_collection(session, inline): > session.url = inline(""" > <select> > <option> >@@ -144,7 +141,7 @@ def test_html_options_collection(session): > assert_same_element(session, expected, actual) > > >-def test_node_list(session): >+def test_node_list(session, inline): > session.url = inline(""" > <p>foo > <p>bar >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/execute_async_script/execute_async.py b/WebDriverTests/imported/w3c/webdriver/tests/execute_async_script/execute_async.py >index c769ca811ee0..5746ed6f05f3 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/execute_async_script/execute_async.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/execute_async_script/execute_async.py >@@ -1,8 +1,10 @@ > import pytest > >+from webdriver.error import NoSuchAlertException > from webdriver.transport import Response > > from tests.support.asserts import assert_error, assert_success >+from tests.support.sync import Poll > > > def execute_async_script(session, script, args=None): >@@ -50,9 +52,16 @@ def test_abort_by_user_prompt_twice(session, dialog_type): > > session.alert.accept() > >- # The first alert has been accepted by the user prompt handler, the second one remains. >- # FIXME: this is how browsers currently work, but the spec should clarify if this is the >- # expected behavior, see https://github.com/w3c/webdriver/issues/1153. >- assert session.alert.text == "Bye" >+ # The first alert has been accepted by the user prompt handler, the second >+ # alert will still be opened because the current step isn't aborted. >+ wait = Poll( >+ session, >+ timeout=5, >+ message="Second alert has not been opened", >+ ignored_exceptions=NoSuchAlertException >+ ) >+ text = wait.until(lambda s: s.alert.text) >+ >+ assert text == "Bye" > > session.alert.accept() >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/execute_async_script/properties.py b/WebDriverTests/imported/w3c/webdriver/tests/execute_async_script/properties.py >index 394ae3294cd3..e51b3e3ea579 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/execute_async_script/properties.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/execute_async_script/properties.py >@@ -1,5 +1,4 @@ > from tests.support.asserts import assert_same_element, assert_success >-from tests.support.inline import inline > > > def execute_async_script(session, script, args=None): >@@ -11,7 +10,7 @@ def execute_async_script(session, script, args=None): > body) > > >-def test_content_attribute(session): >+def test_content_attribute(session, inline): > session.url = inline("<input value=foobar>") > response = execute_async_script(session, """ > const resolve = arguments[0]; >@@ -21,7 +20,7 @@ def test_content_attribute(session): > assert_success(response, "foobar") > > >-def test_idl_attribute(session): >+def test_idl_attribute(session, inline): > session.url = inline(""" > <input> > <script> >@@ -37,7 +36,7 @@ def test_idl_attribute(session): > assert_success(response, "foobar") > > >-def test_idl_attribute_element(session): >+def test_idl_attribute_element(session, inline): > session.url = inline(""" > <p>foo > <p>bar >@@ -59,7 +58,7 @@ def test_idl_attribute_element(session): > assert_same_element(session, bar, value) > > >-def test_script_defining_property(session): >+def test_script_defining_property(session, inline): > session.url = inline("<input>") > session.execute_script(""" > const input = document.querySelector("input"); >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/execute_script/collections.py b/WebDriverTests/imported/w3c/webdriver/tests/execute_script/collections.py >index be0b3a018c50..8291fd71388b 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/execute_script/collections.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/execute_script/collections.py >@@ -1,9 +1,6 @@ > import os > >-from six import text_type >- > from tests.support.asserts import assert_same_element, assert_success >-from tests.support.inline import inline > > > def execute_script(session, script, args=None): >@@ -31,7 +28,7 @@ def test_array(session): > assert_success(response, [1, 2]) > > >-def test_file_list(session, tmpdir): >+def test_file_list(session, tmpdir, inline): > files = [tmpdir.join("foo.txt"), tmpdir.join("bar.txt")] > > session.url = inline("<input type=file multiple>") >@@ -47,11 +44,11 @@ def test_file_list(session, tmpdir): > for expected, actual in zip(files, value): > assert isinstance(actual, dict) > assert "name" in actual >- assert isinstance(actual["name"], text_type) >+ assert isinstance(actual["name"], str) > assert os.path.basename(str(expected)) == actual["name"] > > >-def test_html_all_collection(session): >+def test_html_all_collection(session, inline): > session.url = inline(""" > <p>foo > <p>bar >@@ -76,7 +73,7 @@ def test_html_all_collection(session): > assert_same_element(session, ps[1], value[5]) > > >-def test_html_collection(session): >+def test_html_collection(session, inline): > session.url = inline(""" > <p>foo > <p>bar >@@ -91,7 +88,7 @@ def test_html_collection(session): > assert_same_element(session, expected, actual) > > >-def test_html_form_controls_collection(session): >+def test_html_form_controls_collection(session, inline): > session.url = inline(""" > <form> > <input> >@@ -108,7 +105,7 @@ def test_html_form_controls_collection(session): > assert_same_element(session, expected, actual) > > >-def test_html_options_collection(session): >+def test_html_options_collection(session, inline): > session.url = inline(""" > <select> > <option> >@@ -125,7 +122,7 @@ def test_html_options_collection(session): > assert_same_element(session, expected, actual) > > >-def test_node_list(session): >+def test_node_list(session, inline): > session.url = inline(""" > <p>foo > <p>bar >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/execute_script/cyclic.py b/WebDriverTests/imported/w3c/webdriver/tests/execute_script/cyclic.py >index 16bdcdd1dea5..e3094f89f3b7 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/execute_script/cyclic.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/execute_script/cyclic.py >@@ -1,5 +1,4 @@ > from tests.support.asserts import assert_error, assert_same_element, assert_success >-from tests.support.inline import inline > > > def execute_script(session, script, args=None): >@@ -49,7 +48,7 @@ def test_object_in_array(session): > assert_error(response, "javascript error") > > >-def test_element_in_collection(session): >+def test_element_in_collection(session, inline): > session.url = inline("<div></div>") > divs = session.find.css("div") > >@@ -63,7 +62,7 @@ def test_element_in_collection(session): > assert_same_element(session, expected, actual) > > >-def test_element_in_object(session): >+def test_element_in_object(session, inline): > session.url = inline("<div></div>") > div = session.find.css("div", all=False) > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/execute_script/execute.py b/WebDriverTests/imported/w3c/webdriver/tests/execute_script/execute.py >index bd719b27859e..0f934cbb4450 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/execute_script/execute.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/execute_script/execute.py >@@ -1,9 +1,9 @@ > import pytest > >+from webdriver.error import NoSuchAlertException > from webdriver.transport import Response > > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline > from tests.support.sync import Poll > > >@@ -34,7 +34,7 @@ def test_no_browsing_context(session, closed_frame): > assert_error(response, "no such window") > > >-def test_opening_new_window_keeps_current_window_handle(session): >+def test_opening_new_window_keeps_current_window_handle(session, inline): > original_handle = session.window_handle > original_handles = session.handles > >@@ -57,7 +57,7 @@ def test_ending_comment(session): > assert_success(response, 1) > > >-def test_override_listeners(session): >+def test_override_listeners(session, inline): > session.url = inline(""" > <script> > called = []; >@@ -88,9 +88,16 @@ def test_abort_by_user_prompt_twice(session, dialog_type): > > session.alert.accept() > >- # The first alert has been accepted by the user prompt handler, the second one remains. >- # FIXME: this is how browsers currently work, but the spec should clarify if this is the >- # expected behavior, see https://github.com/w3c/webdriver/issues/1153. >- assert session.alert.text == "Bye" >+ # The first alert has been accepted by the user prompt handler, the second >+ # alert will still be opened because the current step isn't aborted. >+ wait = Poll( >+ session, >+ timeout=5, >+ message="Second alert has not been opened", >+ ignored_exceptions=NoSuchAlertException >+ ) >+ text = wait.until(lambda s: s.alert.text) >+ >+ assert text == "Bye" > > session.alert.accept() >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/execute_script/properties.py b/WebDriverTests/imported/w3c/webdriver/tests/execute_script/properties.py >index 2be4f1f3ccc9..51999936e71a 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/execute_script/properties.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/execute_script/properties.py >@@ -1,5 +1,4 @@ > from tests.support.asserts import assert_same_element, assert_success >-from tests.support.inline import inline > > > def execute_script(session, script, args=None): >@@ -11,7 +10,7 @@ def execute_script(session, script, args=None): > body) > > >-def test_content_attribute(session): >+def test_content_attribute(session, inline): > session.url = inline("<input value=foobar>") > response = execute_script(session, """ > const input = document.querySelector("input"); >@@ -20,7 +19,7 @@ def test_content_attribute(session): > assert_success(response, "foobar") > > >-def test_idl_attribute(session): >+def test_idl_attribute(session, inline): > session.url = inline(""" > <input> > <script> >@@ -35,7 +34,7 @@ def test_idl_attribute(session): > assert_success(response, "foobar") > > >-def test_idl_attribute_element(session): >+def test_idl_attribute_element(session, inline): > session.url = inline(""" > <p>foo > <p>bar >@@ -56,7 +55,7 @@ def test_idl_attribute_element(session): > assert_same_element(session, bar, value) > > >-def test_script_defining_property(session): >+def test_script_defining_property(session, inline): > session.url = inline("<input>") > execute_script(session, """ > const input = document.querySelector("input"); >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/find_element/find.py b/WebDriverTests/imported/w3c/webdriver/tests/find_element/find.py >index 9f0911d56dc0..4510617da5a6 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/find_element/find.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/find_element/find.py >@@ -3,7 +3,6 @@ import pytest > from webdriver.transport import Response > > from tests.support.asserts import assert_error, assert_same_element, assert_success >-from tests.support.inline import inline > > > def find_element(session, using, value): >@@ -48,7 +47,7 @@ def test_invalid_selector_argument(session, value): > ("partial link text", "link text"), > ("tag name", "a"), > ("xpath", "//a")]) >-def test_find_element(session, using, value): >+def test_find_element(session, inline, using, value): > # Step 8 - 9 > session.url = inline("<a href=# id=linkText>full link text</a>") > >@@ -64,7 +63,7 @@ def test_find_element(session, using, value): > ("<a href=#>LINK TEXT</a>", "LINK TEXT"), > ("<a href=# style='text-transform: uppercase'>link text</a>", "LINK TEXT"), > ]) >-def test_find_element_link_text(session, document, value): >+def test_find_element_link_text(session, inline, document, value): > # Step 8 - 9 > session.url = inline(document) > >@@ -81,7 +80,7 @@ def test_find_element_link_text(session, document, value): > ("<a href=#>PARTIAL LINK TEXT</a>", "LINK"), > ("<a href=# style='text-transform: uppercase'>partial link text</a>", "LINK"), > ]) >-def test_find_element_partial_link_text(session, document, value): >+def test_find_element_partial_link_text(session, inline, document, value): > # Step 8 - 9 > session.url = inline(document) > >@@ -102,7 +101,7 @@ def test_no_element(session, using, value): > ("partial link text", "link text"), > ("tag name", "a"), > ("xpath", "//*[name()='a']")]) >-def test_xhtml_namespace(session, using, value): >+def test_xhtml_namespace(session, inline, using, value): > session.url = inline("""<a href="#" id="linkText">full link text</a>""", > doctype="xhtml") > expected = session.execute_script("return document.links[0]") >@@ -116,7 +115,7 @@ def test_xhtml_namespace(session, using, value): > [("css selector", ":root"), > ("tag name", "html"), > ("xpath", "/html")]) >-def test_htmldocument(session, using, value): >+def test_htmldocument(session, inline, using, value): > session.url = inline("") > response = find_element(session, using, value) > assert_success(response) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/find_element/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/find_element/user_prompts.py >index 613466b62b51..ada8e8ebee33 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/find_element/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/find_element/user_prompts.py >@@ -8,7 +8,6 @@ from tests.support.asserts import ( > assert_success, > assert_dialog_handled, > ) >-from tests.support.inline import inline > > > def find_element(session, using, value): >@@ -18,7 +17,7 @@ def find_element(session, using, value): > > > @pytest.fixture >-def check_user_prompt_closed_without_exception(session, create_dialog): >+def check_user_prompt_closed_without_exception(session, create_dialog, inline): > def check_user_prompt_closed_without_exception(dialog_type, retval): > session.url = inline("<p>bar</p>") > element = session.find.css("p", all=False) >@@ -36,7 +35,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_closed_with_exception(session, create_dialog): >+def check_user_prompt_closed_with_exception(session, create_dialog, inline): > def check_user_prompt_closed_with_exception(dialog_type, retval): > session.url = inline("<p>bar</p>") > >@@ -51,7 +50,7 @@ def check_user_prompt_closed_with_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_not_closed_but_exception(session, create_dialog): >+def check_user_prompt_not_closed_but_exception(session, create_dialog, inline): > def check_user_prompt_not_closed_but_exception(dialog_type): > session.url = inline("<p>bar</p>") > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/find_element_from_element/find.py b/WebDriverTests/imported/w3c/webdriver/tests/find_element_from_element/find.py >index aa648117976b..879de3713f99 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/find_element_from_element/find.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/find_element_from_element/find.py >@@ -3,7 +3,6 @@ import pytest > from webdriver.transport import Response > > from tests.support.asserts import assert_error, assert_same_element, assert_success >-from tests.support.inline import inline > > > def find_element(session, element_id, using, value): >@@ -14,7 +13,7 @@ def find_element(session, element_id, using, value): > {"using": using, "value": value}) > > >-def test_null_parameter_value(session, http): >+def test_null_parameter_value(session, http, inline): > session.url = inline("<div><a href=# id=linkText>full link text</a></div>") > element = session.find.css("div", all=False) > >@@ -54,7 +53,7 @@ def test_invalid_selector_argument(session, value): > ("partial link text", "link text"), > ("tag name", "a"), > ("xpath", "//a")]) >-def test_find_element(session, using, value): >+def test_find_element(session, inline, using, value): > # Step 8 - 9 > session.url = inline("<div><a href=# id=linkText>full link text</a></div>") > element = session.find.css("div", all=False) >@@ -70,7 +69,7 @@ def test_find_element(session, using, value): > ("<a href=#>LINK TEXT</a>", "LINK TEXT"), > ("<a href=# style='text-transform: uppercase'>link text</a>", "LINK TEXT"), > ]) >-def test_find_element_link_text(session, document, value): >+def test_find_element_link_text(session, inline, document, value): > # Step 8 - 9 > session.url = inline("<div>{0}</div>".format(document)) > element = session.find.css("div", all=False) >@@ -88,7 +87,7 @@ def test_find_element_link_text(session, document, value): > ("<a href=#>PARTIAL LINK TEXT</a>", "LINK"), > ("<a href=# style='text-transform: uppercase'>partial link text</a>", "LINK"), > ]) >-def test_find_element_partial_link_text(session, document, value): >+def test_find_element_partial_link_text(session, inline, document, value): > # Step 8 - 9 > session.url = inline("<div>{0}</div>".format(document)) > element = session.find.css("div", all=False) >@@ -98,7 +97,7 @@ def test_find_element_partial_link_text(session, document, value): > > > @pytest.mark.parametrize("using,value", [("css selector", "#wontExist")]) >-def test_no_element(session, using, value): >+def test_no_element(session, inline, using, value): > # Step 8 - 9 > session.url = inline("<div></div>") > element = session.find.css("div", all=False) >@@ -112,7 +111,7 @@ def test_no_element(session, using, value): > ("partial link text", "link text"), > ("tag name", "a"), > ("xpath", "//*[name()='a']")]) >-def test_xhtml_namespace(session, using, value): >+def test_xhtml_namespace(session, inline, using, value): > session.url = inline("""<p><a href="#" id="linkText">full link text</a></p>""", > doctype="xhtml") > from_element = session.execute_script("""return document.querySelector("p")""") >@@ -123,7 +122,7 @@ def test_xhtml_namespace(session, using, value): > assert_same_element(session, value, expected) > > >-def test_parent_htmldocument(session): >+def test_parent_htmldocument(session, inline): > session.url = inline("") > from_element = session.execute_script("""return document.querySelector("body")""") > expected = session.execute_script("return document.documentElement") >@@ -133,7 +132,7 @@ def test_parent_htmldocument(session): > assert_same_element(session, value, expected) > > >-def test_parent_of_document_node_errors(session): >+def test_parent_of_document_node_errors(session, inline): > session.url = inline("") > from_element = session.execute_script("return document.documentElement") > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/find_element_from_element/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/find_element_from_element/user_prompts.py >index 3fdaf4e47ed7..0537a786189c 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/find_element_from_element/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/find_element_from_element/user_prompts.py >@@ -8,7 +8,6 @@ from tests.support.asserts import ( > assert_success, > assert_dialog_handled, > ) >-from tests.support.inline import inline > > > def find_element(session, element_id, using, value): >@@ -20,7 +19,7 @@ def find_element(session, element_id, using, value): > > > @pytest.fixture >-def check_user_prompt_closed_without_exception(session, create_dialog): >+def check_user_prompt_closed_without_exception(session, create_dialog, inline): > def check_user_prompt_closed_without_exception(dialog_type, retval): > session.url = inline("<div><p>bar</p><div>") > outer_element = session.find.css("div", all=False) >@@ -39,7 +38,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_closed_with_exception(session, create_dialog): >+def check_user_prompt_closed_with_exception(session, create_dialog, inline): > def check_user_prompt_closed_with_exception(dialog_type, retval): > session.url = inline("<div><p>bar</p><div>") > outer_element = session.find.css("div", all=False) >@@ -55,7 +54,7 @@ def check_user_prompt_closed_with_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_not_closed_but_exception(session, create_dialog): >+def check_user_prompt_not_closed_but_exception(session, create_dialog, inline): > def check_user_prompt_not_closed_but_exception(dialog_type): > session.url = inline("<div><p>bar</p><div>") > outer_element = session.find.css("div", all=False) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/find_element_from_shadow_root/conftest.py b/WebDriverTests/imported/w3c/webdriver/tests/find_element_from_shadow_root/conftest.py >new file mode 100644 >index 000000000000..b8fc93319f43 >--- /dev/null >+++ b/WebDriverTests/imported/w3c/webdriver/tests/find_element_from_shadow_root/conftest.py >@@ -0,0 +1,19 @@ >+import pytest >+ >+@pytest.fixture >+def get_shadow_page(inline): >+ def get_shadow_page(shadow_content): >+ return inline(""" >+ <custom-shadow-element></custom-shadow-element> >+ <script> >+ customElements.define('custom-shadow-element', >+ class extends HTMLElement {{ >+ constructor() {{ >+ super(); >+ this.attachShadow({{mode: 'open'}}).innerHTML = ` >+ {{ {0} }} >+ `; >+ }} >+ }}); >+ </script>""".format(shadow_content)) >+ return get_shadow_page >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/find_element_from_shadow_root/find.py b/WebDriverTests/imported/w3c/webdriver/tests/find_element_from_shadow_root/find.py >new file mode 100644 >index 000000000000..87941d495e13 >--- /dev/null >+++ b/WebDriverTests/imported/w3c/webdriver/tests/find_element_from_shadow_root/find.py >@@ -0,0 +1,141 @@ >+import pytest >+ >+from webdriver.transport import Response >+ >+from tests.support.asserts import assert_error, assert_same_element, assert_success >+ >+ >+def find_element(session, shadow_id, using, value): >+ return session.transport.send( >+ "POST", "session/{session_id}/shadow/{shadow_id}/element".format( >+ session_id=session.session_id, >+ shadow_id=shadow_id), >+ {"using": using, "value": value}) >+ >+ >+def test_null_parameter_value(session, http, get_shadow_page): >+ session.url = get_shadow_page("<div><a href=# id=linkText>full link text</a></div>") >+ custom_element = session.find.css("custom-shadow-element", all=False) >+ shadow_root = custom_element.shadow_root >+ >+ path = "/session/{session_id}/shadow/{shadow_id}/element".format( >+ session_id=session.session_id, shadow_id=shadow_root.id) >+ with http.post(path, None) as response: >+ assert_error(Response.from_http(response), "invalid argument") >+ >+ >+def test_no_top_browsing_context(session, closed_window): >+ response = find_element(session, "notReal", "css selector", "foo") >+ assert_error(response, "no such window") >+ >+ >+def test_no_browsing_context(session, closed_frame): >+ response = find_element(session, "notReal", "css selector", "foo") >+ assert_error(response, "no such window") >+ >+ >+@pytest.mark.parametrize("using", ["a", True, None, 1, [], {}]) >+def test_invalid_using_argument(session, using): >+ # Step 1 - 2 >+ response = find_element(session, "notReal", using, "value") >+ assert_error(response, "invalid argument") >+ >+ >+@pytest.mark.parametrize("value", [None, [], {}]) >+def test_invalid_selector_argument(session, value): >+ # Step 3 - 4 >+ response = find_element(session, "notReal", "css selector", value) >+ assert_error(response, "invalid argument") >+ >+ >+def test_detached_shadow_root(session, get_shadow_page): >+ session.url = get_shadow_page("<div><input type='checkbox'/></div>") >+ custom_element = session.find.css("custom-shadow-element", all=False) >+ shadow_root = custom_element.shadow_root >+ session.refresh() >+ >+ response = find_element(session, shadow_root.id, "css", "input") >+ assert_error(response, "detached shadow root") >+ >+ >+def test_found_element_equivalence(session, get_shadow_page): >+ session.url = get_shadow_page("<div><input type='checkbox'/></div>") >+ custom_element = session.find.css("custom-shadow-element", all=False) >+ expected = session.execute_script("return arguments[0].shadowRoot.querySelector('input')", >+ args=(custom_element,)) >+ shadow_root = custom_element.shadow_root >+ response = find_element(session, shadow_root.id, "css", "input") >+ value = assert_success(response) >+ assert_same_element(session, value, expected) >+ >+ >+@pytest.mark.parametrize("using,value", >+ [("css selector", "#linkText"), >+ ("link text", "full link text"), >+ ("partial link text", "link text"), >+ ("tag name", "a"), >+ ("xpath", "//a")]) >+def test_find_element(session, get_shadow_page, using, value): >+ # Step 8 - 9 >+ session.url = get_shadow_page("<div><a href=# id=linkText>full link text</a></div>") >+ custom_element = session.find.css("custom-shadow-element", all=False) >+ expected = session.execute_script("return arguments[0].shadowRoot.querySelector('#linkText')", >+ args=(custom_element,)) >+ shadow_root = custom_element.shadow_root >+ response = find_element(session, shadow_root.id, using, value) >+ assert_success(response) >+ assert_same_element(session, value, expected) >+ >+ >+@pytest.mark.parametrize("document,value", [ >+ ("<a href=#>link text</a>", "link text"), >+ ("<a href=#> link text </a>", "link text"), >+ ("<a href=#>link<br>text</a>", "link\ntext"), >+ ("<a href=#>link&text</a>", "link&text"), >+ ("<a href=#>LINK TEXT</a>", "LINK TEXT"), >+ ("<a href=# style='text-transform: uppercase'>link text</a>", "LINK TEXT"), >+]) >+def test_find_element_link_text(session, get_shadow_page, document, value): >+ # Step 8 - 9 >+ session.url = get_shadow_page("<div>{0}</div>".format(document)) >+ custom_element = session.find.css("custom-shadow-element", all=False) >+ expected = session.execute_script("return arguments[0].shadowRoot.querySelectorAll('a')[0]", >+ args=(custom_element,)) >+ shadow_root = custom_element.shadow_root >+ >+ response = find_element(session, shadow_root.id, "link text", value) >+ assert_success(response) >+ assert_same_element(session, value, expected) >+ >+ >+@pytest.mark.parametrize("document,value", [ >+ ("<a href=#>partial link text</a>", "link"), >+ ("<a href=#> partial link text </a>", "link"), >+ ("<a href=#>partial link text</a>", "k t"), >+ ("<a href=#>partial link<br>text</a>", "k\nt"), >+ ("<a href=#>partial link&text</a>", "k&t"), >+ ("<a href=#>PARTIAL LINK TEXT</a>", "LINK"), >+ ("<a href=# style='text-transform: uppercase'>partial link text</a>", "LINK"), >+]) >+def test_find_element_partial_link_text(session, get_shadow_page, document, value): >+ # Step 8 - 9 >+ session.url = get_shadow_page("<div>{0}</div>".format(document)) >+ custom_element = session.find.css("custom-shadow-element", all=False) >+ expected = session.execute_script("return arguments[0].shadowRoot.querySelectorAll('a')[0]", >+ args=(custom_element,)) >+ shadow_root = custom_element.shadow_root >+ >+ response = find_element(session, shadow_root.id, "partial link text", value) >+ assert_success(response) >+ assert_same_element(session, value, expected) >+ >+ >+@pytest.mark.parametrize("using,value", [("css selector", "#wontExist")]) >+def test_no_element(session, get_shadow_page, using, value): >+ # Step 8 - 9 >+ session.url = get_shadow_page("<div></div>") >+ custom_element = session.find.css("custom-shadow-element", all=False) >+ shadow_root = custom_element.shadow_root >+ >+ response = find_element(session, shadow_root.id, using, value) >+ assert_error(response, "no such element") >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/find_element_from_shadow_root/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/find_element_from_shadow_root/user_prompts.py >new file mode 100644 >index 000000000000..3813a7df5d32 >--- /dev/null >+++ b/WebDriverTests/imported/w3c/webdriver/tests/find_element_from_shadow_root/user_prompts.py >@@ -0,0 +1,129 @@ >+# META: timeout=long >+ >+import pytest >+ >+from tests.support.asserts import ( >+ assert_error, >+ assert_same_element, >+ assert_success, >+ assert_dialog_handled, >+) >+ >+ >+def find_element(session, shadow_id, using, value): >+ return session.transport.send( >+ "POST", "session/{session_id}/shadow/{shadow_id}/element".format( >+ session_id=session.session_id, >+ element_id=shadow_id), >+ {"using": using, "value": value}) >+ >+ >+@pytest.fixture >+def check_user_prompt_closed_without_exception(session, create_dialog, get_shadow_page): >+ def check_user_prompt_closed_without_exception(dialog_type, retval): >+ session.url = get_shadow_page("<div><p>bar</p><div>") >+ outer_element = session.find.css("custom-shadow-element", all=False) >+ shadow_root = outer_element.shadow_root >+ inner_element = session.execute_script("return arguments[0].shadowRoot.querySelector('p')", >+ args=(outer_element,)) >+ >+ create_dialog(dialog_type, text=dialog_type) >+ >+ response = find_element(session, shadow_root.id, "css selector", "p") >+ value = assert_success(response) >+ >+ assert_dialog_handled(session, expected_text=dialog_type, expected_retval=retval) >+ >+ assert_same_element(session, value, inner_element) >+ >+ return check_user_prompt_closed_without_exception >+ >+ >+@pytest.fixture >+def check_user_prompt_closed_with_exception(session, create_dialog, get_shadow_page): >+ def check_user_prompt_closed_with_exception(dialog_type, retval): >+ session.url = get_shadow_page("<div><p>bar</p><div>") >+ outer_element = session.find.css("custom-shadow-element", all=False) >+ shadow_root = outer_element.shadow_root >+ >+ create_dialog(dialog_type, text=dialog_type) >+ >+ response = find_element(session, shadow_root.id, "css selector", "p") >+ assert_error(response, "unexpected alert open") >+ >+ assert_dialog_handled(session, expected_text=dialog_type, expected_retval=retval) >+ >+ return check_user_prompt_closed_with_exception >+ >+ >+@pytest.fixture >+def check_user_prompt_not_closed_but_exception(session, create_dialog, get_shadow_page): >+ def check_user_prompt_not_closed_but_exception(dialog_type): >+ session.url = get_shadow_page("<div><p>bar</p><div>") >+ outer_element = session.find.css("custom-shadow-element", all=False) >+ shadow_root = outer_element.shadow_root >+ >+ create_dialog(dialog_type, text=dialog_type) >+ >+ response = find_element(session, shadow_root.id, "css selector", "p") >+ assert_error(response, "unexpected alert open") >+ >+ assert session.alert.text == dialog_type >+ session.alert.dismiss() >+ >+ return check_user_prompt_not_closed_but_exception >+ >+ >+@pytest.mark.capabilities({"unhandledPromptBehavior": "accept"}) >+@pytest.mark.parametrize("dialog_type, retval", [ >+ ("alert", None), >+ ("confirm", True), >+ ("prompt", ""), >+]) >+def test_accept(check_user_prompt_closed_without_exception, dialog_type, retval): >+ check_user_prompt_closed_without_exception(dialog_type, retval) >+ >+ >+@pytest.mark.capabilities({"unhandledPromptBehavior": "accept and notify"}) >+@pytest.mark.parametrize("dialog_type, retval", [ >+ ("alert", None), >+ ("confirm", True), >+ ("prompt", ""), >+]) >+def test_accept_and_notify(check_user_prompt_closed_with_exception, dialog_type, retval): >+ check_user_prompt_closed_with_exception(dialog_type, retval) >+ >+ >+@pytest.mark.capabilities({"unhandledPromptBehavior": "dismiss"}) >+@pytest.mark.parametrize("dialog_type, retval", [ >+ ("alert", None), >+ ("confirm", False), >+ ("prompt", None), >+]) >+def test_dismiss(check_user_prompt_closed_without_exception, dialog_type, retval): >+ check_user_prompt_closed_without_exception(dialog_type, retval) >+ >+ >+@pytest.mark.capabilities({"unhandledPromptBehavior": "dismiss and notify"}) >+@pytest.mark.parametrize("dialog_type, retval", [ >+ ("alert", None), >+ ("confirm", False), >+ ("prompt", None), >+]) >+def test_dismiss_and_notify(check_user_prompt_closed_with_exception, dialog_type, retval): >+ check_user_prompt_closed_with_exception(dialog_type, retval) >+ >+ >+@pytest.mark.capabilities({"unhandledPromptBehavior": "ignore"}) >+@pytest.mark.parametrize("dialog_type", ["alert", "confirm", "prompt"]) >+def test_ignore(check_user_prompt_not_closed_but_exception, dialog_type): >+ check_user_prompt_not_closed_but_exception(dialog_type) >+ >+ >+@pytest.mark.parametrize("dialog_type, retval", [ >+ ("alert", None), >+ ("confirm", False), >+ ("prompt", None), >+]) >+def test_default(check_user_prompt_closed_with_exception, dialog_type, retval): >+ check_user_prompt_closed_with_exception(dialog_type, retval) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/find_elements/find.py b/WebDriverTests/imported/w3c/webdriver/tests/find_elements/find.py >index c1381a0e82ac..47f009739385 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/find_elements/find.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/find_elements/find.py >@@ -3,7 +3,6 @@ import pytest > from webdriver.transport import Response > > from tests.support.asserts import assert_error, assert_same_element, assert_success >-from tests.support.inline import inline > > > def find_elements(session, using, value): >@@ -48,7 +47,7 @@ def test_invalid_selector_argument(session, value): > ("partial link text", "link text"), > ("tag name", "a"), > ("xpath", "//a")]) >-def test_find_elements(session, using, value): >+def test_find_elements(session, inline, using, value): > # Step 8 - 9 > session.url = inline("<a href=# id=linkText>full link text</a>") > >@@ -65,7 +64,7 @@ def test_find_elements(session, using, value): > ("<a href=#>LINK TEXT</a>", "LINK TEXT"), > ("<a href=# style='text-transform: uppercase'>link text</a>", "LINK TEXT"), > ]) >-def test_find_elements_link_text(session, document, value): >+def test_find_elements_link_text(session, inline, document, value): > # Step 8 - 9 > session.url = inline("<a href=#>not wanted</a><br/>{0}".format(document)) > expected = session.execute_script("return document.links[1];") >@@ -88,7 +87,7 @@ def test_find_elements_link_text(session, document, value): > ("<a href=#>PARTIAL LINK TEXT</a>", "LINK"), > ("<a href=# style='text-transform: uppercase'>partial link text</a>", "LINK"), > ]) >-def test_find_elements_partial_link_text(session, document, value): >+def test_find_elements_partial_link_text(session, inline, document, value): > # Step 8 - 9 > session.url = inline("<a href=#>not wanted</a><br/>{0}".format(document)) > expected = session.execute_script("return document.links[1];") >@@ -116,7 +115,7 @@ def test_no_element(session, using, value): > ("partial link text", "link text"), > ("tag name", "a"), > ("xpath", "//*[name()='a']")]) >-def test_xhtml_namespace(session, using, value): >+def test_xhtml_namespace(session, inline, using, value): > session.url = inline("""<a href="#" id="linkText">full link text</a>""", > doctype="xhtml") > expected = session.execute_script("return document.links[0];") >@@ -134,7 +133,7 @@ def test_xhtml_namespace(session, using, value): > [("css selector", ":root"), > ("tag name", "html"), > ("xpath", "/html")]) >-def test_htmldocument(session, using, value): >+def test_htmldocument(session, inline, using, value): > session.url = inline("") > response = find_elements(session, using, value) > value = assert_success(response) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/find_elements/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/find_elements/user_prompts.py >index 01eb8ff3c863..f9a45e527515 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/find_elements/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/find_elements/user_prompts.py >@@ -8,7 +8,6 @@ from tests.support.asserts import ( > assert_success, > assert_dialog_handled, > ) >-from tests.support.inline import inline > > > def find_elements(session, using, value): >@@ -18,7 +17,7 @@ def find_elements(session, using, value): > > > @pytest.fixture >-def check_user_prompt_closed_without_exception(session, create_dialog): >+def check_user_prompt_closed_without_exception(session, create_dialog, inline): > def check_user_prompt_closed_without_exception(dialog_type, retval): > session.url = inline("<p>bar</p>") > element = session.find.css("p", all=False) >@@ -38,7 +37,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_closed_with_exception(session, create_dialog): >+def check_user_prompt_closed_with_exception(session, create_dialog, inline): > def check_user_prompt_closed_with_exception(dialog_type, retval): > session.url = inline("<p>bar</p>") > >@@ -53,7 +52,7 @@ def check_user_prompt_closed_with_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_not_closed_but_exception(session, create_dialog): >+def check_user_prompt_not_closed_but_exception(session, create_dialog, inline): > def check_user_prompt_not_closed_but_exception(dialog_type): > session.url = inline("<p>bar</p>") > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/find_elements_from_element/find.py b/WebDriverTests/imported/w3c/webdriver/tests/find_elements_from_element/find.py >index 5c267ba6479e..ae6f976e9f97 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/find_elements_from_element/find.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/find_elements_from_element/find.py >@@ -3,7 +3,6 @@ import pytest > from webdriver.transport import Response > > from tests.support.asserts import assert_error, assert_same_element, assert_success >-from tests.support.inline import inline > > > def find_elements(session, element_id, using, value): >@@ -14,7 +13,7 @@ def find_elements(session, element_id, using, value): > {"using": using, "value": value}) > > >-def test_null_parameter_value(session, http): >+def test_null_parameter_value(session, http, inline): > session.url = inline("<div><a href=# id=linkText>full link text</a></div>") > element = session.find.css("div", all=False) > >@@ -54,7 +53,7 @@ def test_invalid_selector_argument(session, value): > ("partial link text", "link text"), > ("tag name", "a"), > ("xpath", "//a")]) >-def test_find_elements(session, using, value): >+def test_find_elements(session, inline, using, value): > # Step 8 - 9 > session.url = inline("<div><a href=# id=linkText>full link text</a></div>") > element = session.find.css("div", all=False) >@@ -70,7 +69,7 @@ def test_find_elements(session, using, value): > ("<a href=#>LINK TEXT</a>", "LINK TEXT"), > ("<a href=# style='text-transform: uppercase'>link text</a>", "LINK TEXT"), > ]) >-def test_find_elements_link_text(session, document, value): >+def test_find_elements_link_text(session, inline, document, value): > # Step 8 - 9 > session.url = inline("<div><a href=#>not wanted</a><br/>{0}</div>".format(document)) > element = session.find.css("div", all=False) >@@ -94,7 +93,7 @@ def test_find_elements_link_text(session, document, value): > ("<a href=#>PARTIAL LINK TEXT</a>", "LINK"), > ("<a href=# style='text-transform: uppercase'>partial link text</a>", "LINK"), > ]) >-def test_find_elements_partial_link_text(session, document, value): >+def test_find_elements_partial_link_text(session, inline, document, value): > # Step 8 - 9 > session.url = inline("<div><a href=#>not wanted</a><br/>{0}</div>".format(document)) > element = session.find.css("div", all=False) >@@ -110,7 +109,7 @@ def test_find_elements_partial_link_text(session, document, value): > > > @pytest.mark.parametrize("using,value", [("css selector", "#wontExist")]) >-def test_no_element(session, using, value): >+def test_no_element(session, inline, using, value): > # Step 8 - 9 > session.url = inline("<div></div>") > element = session.find.css("div", all=False) >@@ -124,7 +123,7 @@ def test_no_element(session, using, value): > ("partial link text", "link text"), > ("tag name", "a"), > ("xpath", "//*[name()='a']")]) >-def test_xhtml_namespace(session, using, value): >+def test_xhtml_namespace(session, inline, using, value): > session.url = inline("""<p><a href="#" id="linkText">full link text</a></p>""", > doctype="xhtml") > from_element = session.execute_script("""return document.querySelector("p")""") >@@ -139,7 +138,7 @@ def test_xhtml_namespace(session, using, value): > assert_same_element(session, found_element, expected) > > >-def test_parent_htmldocument(session): >+def test_parent_htmldocument(session, inline): > session.url = inline("") > from_element = session.execute_script("""return document.querySelector("body")""") > expected = session.execute_script("return document.documentElement") >@@ -153,7 +152,7 @@ def test_parent_htmldocument(session): > assert_same_element(session, found_element, expected) > > >-def test_parent_of_document_node_errors(session): >+def test_parent_of_document_node_errors(session, inline): > session.url = inline("") > from_element = session.execute_script("return document.documentElement") > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/find_elements_from_element/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/find_elements_from_element/user_prompts.py >index ca7bc7735d08..467bec09a1bd 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/find_elements_from_element/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/find_elements_from_element/user_prompts.py >@@ -8,7 +8,6 @@ from tests.support.asserts import ( > assert_success, > assert_dialog_handled, > ) >-from tests.support.inline import inline > > > def find_elements(session, element_id, using, value): >@@ -20,7 +19,7 @@ def find_elements(session, element_id, using, value): > > > @pytest.fixture >-def check_user_prompt_closed_without_exception(session, create_dialog): >+def check_user_prompt_closed_without_exception(session, create_dialog, inline): > def check_user_prompt_closed_without_exception(dialog_type, retval): > session.url = inline("<div><p>bar</p><div>") > outer_element = session.find.css("div", all=False) >@@ -41,7 +40,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_closed_with_exception(session, create_dialog): >+def check_user_prompt_closed_with_exception(session, create_dialog, inline): > def check_user_prompt_closed_with_exception(dialog_type, retval): > session.url = inline("<div><p>bar</p><div>") > outer_element = session.find.css("div", all=False) >@@ -57,7 +56,7 @@ def check_user_prompt_closed_with_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_not_closed_but_exception(session, create_dialog): >+def check_user_prompt_not_closed_but_exception(session, create_dialog, inline): > def check_user_prompt_not_closed_but_exception(dialog_type): > session.url = inline("<div><p>bar</p><div>") > outer_element = session.find.css("div", all=False) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/find_elements_from_shadow_root/conftest.py b/WebDriverTests/imported/w3c/webdriver/tests/find_elements_from_shadow_root/conftest.py >new file mode 100644 >index 000000000000..b8fc93319f43 >--- /dev/null >+++ b/WebDriverTests/imported/w3c/webdriver/tests/find_elements_from_shadow_root/conftest.py >@@ -0,0 +1,19 @@ >+import pytest >+ >+@pytest.fixture >+def get_shadow_page(inline): >+ def get_shadow_page(shadow_content): >+ return inline(""" >+ <custom-shadow-element></custom-shadow-element> >+ <script> >+ customElements.define('custom-shadow-element', >+ class extends HTMLElement {{ >+ constructor() {{ >+ super(); >+ this.attachShadow({{mode: 'open'}}).innerHTML = ` >+ {{ {0} }} >+ `; >+ }} >+ }}); >+ </script>""".format(shadow_content)) >+ return get_shadow_page >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/find_elements_from_shadow_root/find.py b/WebDriverTests/imported/w3c/webdriver/tests/find_elements_from_shadow_root/find.py >new file mode 100644 >index 000000000000..f70a4b45aadb >--- /dev/null >+++ b/WebDriverTests/imported/w3c/webdriver/tests/find_elements_from_shadow_root/find.py >@@ -0,0 +1,144 @@ >+import pytest >+ >+from webdriver.transport import Response >+ >+from tests.support.asserts import assert_error, assert_same_element, assert_success >+ >+ >+def find_elements(session, shadow_id, using, value): >+ return session.transport.send( >+ "POST", "session/{session_id}/shadow/{shadow_id}/elements".format( >+ session_id=session.session_id, >+ shadow_id=shadow_id), >+ {"using": using, "value": value}) >+ >+ >+def test_null_parameter_value(session, http, inline): >+ session.url = inline("<div><a href=# id=linkText>full link text</a></div>") >+ custom_element = session.find.css("custom-shadow-element", all=False) >+ shadow_root = custom_element.shadow_root >+ >+ path = "/session/{session_id}/shadow/{shadow_id}/elements".format( >+ session_id=session.session_id, shadow_id=shadow_root.id) >+ with http.post(path, None) as response: >+ assert_error(Response.from_http(response), "invalid argument") >+ >+ >+def test_no_top_browsing_context(session, closed_window): >+ response = find_elements(session, "notReal", "css selector", "foo") >+ assert_error(response, "no such window") >+ >+ >+def test_no_browsing_context(session, closed_frame): >+ response = find_elements(session, "notReal", "css selector", "foo") >+ assert_error(response, "no such window") >+ >+ >+@pytest.mark.parametrize("using", [("a"), (True), (None), (1), ([]), ({})]) >+def test_invalid_using_argument(session, using): >+ # Step 1 - 2 >+ response = find_elements(session, "notReal", using, "value") >+ assert_error(response, "invalid argument") >+ >+ >+@pytest.mark.parametrize("value", [None, [], {}]) >+def test_invalid_selector_argument(session, value): >+ # Step 3 - 4 >+ response = find_elements(session, "notReal", "css selector", value) >+ assert_error(response, "invalid argument") >+ >+ >+def test_detached_shadow_root(session, get_shadow_page): >+ session.url = get_shadow_page("<div><input type='checkbox'/></div>") >+ custom_element = session.find.css("custom-shadow-element", all=False) >+ shadow_root = custom_element.shadow_root >+ session.refresh() >+ >+ response = find_elements(session, shadow_root.id, "css", "input") >+ assert_error(response, "detached shadow root") >+ >+ >+def test_find_elements_equivalence(session, get_shadow_page): >+ session.url = get_shadow_page("<div><input id='check' type='checkbox'/><input id='text'/></div>") >+ custom_element = session.find.css("custom-shadow-element", all=False) >+ shadow_root = custom_element.shadow_root >+ response = find_elements(session, shadow_root.id, "css", "input") >+ assert_success(response) >+ >+ >+@pytest.mark.parametrize("using,value", >+ [("css selector", "#linkText"), >+ ("link text", "full link text"), >+ ("partial link text", "link text"), >+ ("tag name", "a"), >+ ("xpath", "//a")]) >+def test_find_elements(session, get_shadow_page, using, value): >+ # Step 8 - 9 >+ session.url = get_shadow_page("<div><a href=# id=linkText>full link text</a></div>") >+ custom_element = session.find.css("custom-shadow-element", all=False) >+ shadow_root = custom_element.shadow_root >+ response = find_elements(session, shadow_root.id, using, value) >+ assert_success(response) >+ >+ >+@pytest.mark.parametrize("document,value", [ >+ ("<a href=#>link text</a>", "link text"), >+ ("<a href=#> link text </a>", "link text"), >+ ("<a href=#>link<br>text</a>", "link\ntext"), >+ ("<a href=#>link&text</a>", "link&text"), >+ ("<a href=#>LINK TEXT</a>", "LINK TEXT"), >+ ("<a href=# style='text-transform: uppercase'>link text</a>", "LINK TEXT"), >+]) >+def test_find_elements_link_text(session, get_shadow_page, document, value): >+ # Step 8 - 9 >+ session.url = get_shadow_page("<div><a href=#>not wanted</a><br/>{0}</div>".format(document)) >+ element = session.find.css("div", all=False) >+ custom_element = session.find.css("custom-shadow-element", all=False) >+ shadow_root = custom_element.shadow_root >+ expected = session.execute_script("return arguments[0].shadowRoot.querySelectorAll('a')[1]", >+ args=(custom_element,)) >+ >+ response = find_elements(session, shadow_root.id, "link text", value) >+ value = assert_success(response) >+ assert isinstance(value, list) >+ assert len(value) == 1 >+ >+ found_element = value[0] >+ assert_same_element(session, found_element, expected) >+ >+ >+@pytest.mark.parametrize("document,value", [ >+ ("<a href=#>partial link text</a>", "link"), >+ ("<a href=#> partial link text </a>", "link"), >+ ("<a href=#>partial link text</a>", "k t"), >+ ("<a href=#>partial link<br>text</a>", "k\nt"), >+ ("<a href=#>partial link&text</a>", "k&t"), >+ ("<a href=#>PARTIAL LINK TEXT</a>", "LINK"), >+ ("<a href=# style='text-transform: uppercase'>partial link text</a>", "LINK"), >+]) >+def test_find_elements_partial_link_text(session, get_shadow_page, document, value): >+ # Step 8 - 9 >+ session.url = get_shadow_page("<div><a href=#>not wanted</a><br/>{0}</div>".format(document)) >+ element = session.find.css("div", all=False) >+ custom_element = session.find.css("custom-shadow-element", all=False) >+ shadow_root = custom_element.shadow_root >+ expected = session.execute_script("return arguments[0].shadowRoot.querySelectorAll('a')[1]", >+ args=(custom_element,)) >+ >+ response = find_elements(session, shadow_root.id, "partial link text", value) >+ value = assert_success(response) >+ assert isinstance(value, list) >+ assert len(value) == 1 >+ >+ found_element = value[0] >+ assert_same_element(session, found_element, expected) >+ >+ >+@pytest.mark.parametrize("using,value", [("css selector", "#wontExist")]) >+def test_no_element(session, get_shadow_page, using, value): >+ # Step 8 - 9 >+ session.url = get_shadow_page("<div></div>") >+ custom_element = session.find.css("custom-shadow-element", all=False) >+ shadow_root = custom_element.shadow_root >+ response = find_elements(session, shadow_root.id, using, value) >+ assert response.body["value"] == [] >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/find_elements_from_shadow_root/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/find_elements_from_shadow_root/user_prompts.py >new file mode 100644 >index 000000000000..c4b903c836ef >--- /dev/null >+++ b/WebDriverTests/imported/w3c/webdriver/tests/find_elements_from_shadow_root/user_prompts.py >@@ -0,0 +1,131 @@ >+# META: timeout=long >+ >+import pytest >+ >+from tests.support.asserts import ( >+ assert_error, >+ assert_same_element, >+ assert_success, >+ assert_dialog_handled, >+) >+ >+ >+def find_elements(session, shadow_id, using, value): >+ return session.transport.send( >+ "POST", "session/{session_id}/shadow/{shadow_id}/elements".format( >+ session_id=session.session_id, >+ element_id=shadow_id), >+ {"using": using, "value": value}) >+ >+ >+@pytest.fixture >+def check_user_prompt_closed_without_exception(session, create_dialog, get_shadow_page): >+ def check_user_prompt_closed_without_exception(dialog_type, retval): >+ session.url = get_shadow_page("<div><p>bar</p><div>") >+ outer_element = session.find.css("custom-shadow-element", all=False) >+ shadow_root = outer_element.shadow_root >+ inner_element = session.execute_script("return arguments[0].shadowRoot.querySelector('p')", >+ args=(outer_element,)) >+ >+ create_dialog(dialog_type, text=dialog_type) >+ >+ response = find_elements(session, shadow_root.id, "css selector", "p") >+ value = assert_success(response) >+ assert isinstance(value, list) >+ assert len(value) == 1 >+ >+ assert_dialog_handled(session, expected_text=dialog_type, expected_retval=retval) >+ >+ assert_same_element(session, value[0], inner_element) >+ >+ return check_user_prompt_closed_without_exception >+ >+ >+@pytest.fixture >+def check_user_prompt_closed_with_exception(session, create_dialog, get_shadow_page): >+ def check_user_prompt_closed_with_exception(dialog_type, retval): >+ session.url = get_shadow_page("<div><p>bar</p><div>") >+ outer_element = session.find.css("custom-shadow-element", all=False) >+ shadow_root = outer_element.shadow_root >+ >+ create_dialog(dialog_type, text=dialog_type) >+ >+ response = find_elements(session, shadow_root.id, "css selector", "p") >+ assert_error(response, "unexpected alert open") >+ >+ assert_dialog_handled(session, expected_text=dialog_type, expected_retval=retval) >+ >+ return check_user_prompt_closed_with_exception >+ >+ >+@pytest.fixture >+def check_user_prompt_not_closed_but_exception(session, create_dialog, get_shadow_page): >+ def check_user_prompt_not_closed_but_exception(dialog_type): >+ session.url = get_shadow_page("<div><p>bar</p><div>") >+ outer_element = session.find.css("custom-shadow-element", all=False) >+ shadow_root = outer_element.shadow_root >+ >+ create_dialog(dialog_type, text=dialog_type) >+ >+ response = find_elements(session, shadow_root.id, "css selector", "p") >+ assert_error(response, "unexpected alert open") >+ >+ assert session.alert.text == dialog_type >+ session.alert.dismiss() >+ >+ return check_user_prompt_not_closed_but_exception >+ >+ >+@pytest.mark.capabilities({"unhandledPromptBehavior": "accept"}) >+@pytest.mark.parametrize("dialog_type, retval", [ >+ ("alert", None), >+ ("confirm", True), >+ ("prompt", ""), >+]) >+def test_accept(check_user_prompt_closed_without_exception, dialog_type, retval): >+ check_user_prompt_closed_without_exception(dialog_type, retval) >+ >+ >+@pytest.mark.capabilities({"unhandledPromptBehavior": "accept and notify"}) >+@pytest.mark.parametrize("dialog_type, retval", [ >+ ("alert", None), >+ ("confirm", True), >+ ("prompt", ""), >+]) >+def test_accept_and_notify(check_user_prompt_closed_with_exception, dialog_type, retval): >+ check_user_prompt_closed_with_exception(dialog_type, retval) >+ >+ >+@pytest.mark.capabilities({"unhandledPromptBehavior": "dismiss"}) >+@pytest.mark.parametrize("dialog_type, retval", [ >+ ("alert", None), >+ ("confirm", False), >+ ("prompt", None), >+]) >+def test_dismiss(check_user_prompt_closed_without_exception, dialog_type, retval): >+ check_user_prompt_closed_without_exception(dialog_type, retval) >+ >+ >+@pytest.mark.capabilities({"unhandledPromptBehavior": "dismiss and notify"}) >+@pytest.mark.parametrize("dialog_type, retval", [ >+ ("alert", None), >+ ("confirm", False), >+ ("prompt", None), >+]) >+def test_dismiss_and_notify(check_user_prompt_closed_with_exception, dialog_type, retval): >+ check_user_prompt_closed_with_exception(dialog_type, retval) >+ >+ >+@pytest.mark.capabilities({"unhandledPromptBehavior": "ignore"}) >+@pytest.mark.parametrize("dialog_type", ["alert", "confirm", "prompt"]) >+def test_ignore(check_user_prompt_not_closed_but_exception, dialog_type): >+ check_user_prompt_not_closed_but_exception(dialog_type) >+ >+ >+@pytest.mark.parametrize("dialog_type, retval", [ >+ ("alert", None), >+ ("confirm", False), >+ ("prompt", None), >+]) >+def test_default(check_user_prompt_closed_with_exception, dialog_type, retval): >+ check_user_prompt_closed_with_exception(dialog_type, retval) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/forward/forward.py b/WebDriverTests/imported/w3c/webdriver/tests/forward/forward.py >index 4a1e21581518..97a935c8cd83 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/forward/forward.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/forward/forward.py >@@ -1,5 +1,7 @@ >+import pytest >+from webdriver import error >+ > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline > > > def forward(session): >@@ -7,7 +9,7 @@ def forward(session): > "POST", "session/{session_id}/forward".format(**vars(session))) > > >-def test_null_response_value(session): >+def test_null_response_value(session, inline): > session.url = inline("<div>") > session.url = inline("<p>") > session.back() >@@ -27,7 +29,7 @@ def test_no_browsing_context(session, closed_frame): > assert_success(response) > > >-def test_no_browsing_history(session): >+def test_no_browsing_history(session, inline): > url = inline("<div id=foo>") > > session.url = url >@@ -40,7 +42,7 @@ def test_no_browsing_history(session): > assert element.property("id") == "foo" > > >-def test_data_urls(session): >+def test_data_urls(session, inline): > test_pages = [ > inline("<p id=1>"), > inline("<p id=2>"), >@@ -57,7 +59,7 @@ def test_data_urls(session): > assert session.url == test_pages[1] > > >-def test_dismissed_beforeunload(session): >+def test_dismissed_beforeunload(session, inline): > url_beforeunload = inline(""" > <input type="text"> > <script> >@@ -105,7 +107,7 @@ def test_fragments(session, url): > assert session.url == test_pages[2] > > >-def test_history_pushstate(session, url): >+def test_history_pushstate(session, inline): > pushstate_page = inline(""" > <script> > function pushState() { >@@ -130,3 +132,45 @@ def test_history_pushstate(session, url): > > assert session.url == "{}#pushstate".format(pushstate_page) > assert session.execute_script("return history.state;") == {"foo": "bar"} >+ >+ >+def test_removed_iframe(session, url, inline): >+ page = inline("<p>foo") >+ >+ session.url = url("/webdriver/tests/support/html/frames_no_bfcache.html") >+ session.url = page >+ >+ session.back() >+ >+ subframe = session.find.css("#sub-frame", all=False) >+ session.switch_frame(subframe) >+ >+ response = forward(session) >+ assert_success(response) >+ >+ assert session.url == page >+ >+ >+# Capability needed as long as no valid certificate is available: >+# https://github.com/web-platform-tests/wpt/issues/28847 >+@pytest.mark.capabilities({"acceptInsecureCerts": True}) >+def test_cross_origin(session, url): >+ base_path = ("/webdriver/tests/support/html/subframe.html" + >+ "?pipe=header(Cross-Origin-Opener-Policy,same-origin") >+ first_page = url(base_path, protocol="https") >+ second_page = url(base_path, protocol="https", domain="alt") >+ >+ session.url = first_page >+ session.url = second_page >+ session.back() >+ >+ elem = session.find.css("#delete", all=False) >+ >+ response = forward(session) >+ assert_success(response) >+ >+ assert session.url == second_page >+ >+ with pytest.raises(error.StaleElementReferenceException): >+ elem.click() >+ elem = session.find.css("#delete", all=False) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/forward/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/forward/user_prompts.py >index e5eea8f44571..3eeaf6e71cdb 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/forward/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/forward/user_prompts.py >@@ -3,7 +3,6 @@ > import pytest > > from tests.support.asserts import assert_dialog_handled, assert_error, assert_success >-from tests.support.inline import inline > > > def forward(session): >@@ -12,7 +11,7 @@ def forward(session): > > > @pytest.fixture >-def pages(session): >+def pages(session, inline): > pages = [ > inline("<p id=1>"), > inline("<p id=2>"), >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_active_element/get.py b/WebDriverTests/imported/w3c/webdriver/tests/get_active_element/get.py >index 7f821f3a40d5..1085ad82f18f 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_active_element/get.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_active_element/get.py >@@ -1,5 +1,4 @@ > from tests.support.asserts import assert_error, assert_is_active_element, assert_success >-from tests.support.inline import inline > > > def read_global(session, name): >@@ -21,7 +20,7 @@ def test_no_browsing_context(session, closed_frame): > assert_error(response, "no such window") > > >-def test_success_document(session): >+def test_success_document(session, inline): > session.url = inline(""" > <body> > <h1>Heading</h1> >@@ -36,7 +35,7 @@ def test_success_document(session): > assert_is_active_element(session, element) > > >-def test_sucess_input(session): >+def test_sucess_input(session, inline): > session.url = inline(""" > <body> > <h1>Heading</h1> >@@ -50,7 +49,7 @@ def test_sucess_input(session): > assert_is_active_element(session, element) > > >-def test_sucess_input_non_interactable(session): >+def test_sucess_input_non_interactable(session, inline): > session.url = inline(""" > <body> > <h1>Heading</h1> >@@ -64,7 +63,7 @@ def test_sucess_input_non_interactable(session): > assert_is_active_element(session, element) > > >-def test_success_explicit_focus(session): >+def test_success_explicit_focus(session, inline): > session.url = inline(""" > <body> > <h1>Heading</h1> >@@ -105,7 +104,7 @@ def test_success_explicit_focus(session): > assert_is_active_element(session, element) > > >-def test_success_iframe_content(session): >+def test_success_iframe_content(session, inline): > session.url = inline("<body></body>") > session.execute_script(""" > let iframe = document.createElement('iframe'); >@@ -120,7 +119,7 @@ def test_success_iframe_content(session): > assert_is_active_element(session, element) > > >-def test_missing_document_element(session): >+def test_missing_document_element(session, inline): > session.url = inline("<body></body>") > session.execute_script(""" > if (document.body.remove) { >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_active_element/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/get_active_element/user_prompts.py >index 7fa7be1dde99..1ff77697b707 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_active_element/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_active_element/user_prompts.py >@@ -8,7 +8,6 @@ from tests.support.asserts import ( > assert_is_active_element, > assert_success > ) >-from tests.support.inline import inline > > > def get_active_element(session): >@@ -17,7 +16,7 @@ def get_active_element(session): > > > @pytest.fixture >-def check_user_prompt_closed_without_exception(session, create_dialog): >+def check_user_prompt_closed_without_exception(session, create_dialog, inline): > def check_user_prompt_closed_without_exception(dialog_type, retval): > session.url = inline("<input type=text>") > >@@ -34,7 +33,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_closed_with_exception(session, create_dialog): >+def check_user_prompt_closed_with_exception(session, create_dialog, inline): > def check_user_prompt_closed_with_exception(dialog_type, retval): > session.url = inline("<input type=text>") > >@@ -49,7 +48,7 @@ def check_user_prompt_closed_with_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_not_closed_but_exception(session, create_dialog): >+def check_user_prompt_not_closed_but_exception(session, create_dialog, inline): > def check_user_prompt_not_closed_but_exception(dialog_type): > session.url = inline("<input type=text>") > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_alert_text/get.py b/WebDriverTests/imported/w3c/webdriver/tests/get_alert_text/get.py >index b64678653e08..7ee7ff1808f9 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_alert_text/get.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_alert_text/get.py >@@ -1,9 +1,6 @@ >-from six import text_type >- > from webdriver.error import NoSuchAlertException > > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline > from tests.support.sync import Poll > > >@@ -27,36 +24,36 @@ def test_no_user_prompt(session): > assert_error(response, "no such alert") > > >-def test_get_alert_text(session): >+def test_get_alert_text(session, inline): > session.url = inline("<script>window.alert('Hello');</script>") > response = get_alert_text(session) > assert_success(response) > assert isinstance(response.body, dict) > assert "value" in response.body > alert_text = response.body["value"] >- assert isinstance(alert_text, text_type) >+ assert isinstance(alert_text, str) > assert alert_text == "Hello" > > >-def test_get_confirm_text(session): >+def test_get_confirm_text(session, inline): > session.url = inline("<script>window.confirm('Hello');</script>") > response = get_alert_text(session) > assert_success(response) > assert isinstance(response.body, dict) > assert "value" in response.body > confirm_text = response.body["value"] >- assert isinstance(confirm_text, text_type) >+ assert isinstance(confirm_text, str) > assert confirm_text == "Hello" > > >-def test_get_prompt_text(session): >+def test_get_prompt_text(session, inline): > session.url = inline("<script>window.prompt('Enter Your Name: ', 'Federer');</script>") > response = get_alert_text(session) > assert_success(response) > assert isinstance(response.body, dict) > assert "value" in response.body > prompt_text = response.body["value"] >- assert isinstance(prompt_text, text_type) >+ assert isinstance(prompt_text, str) > assert prompt_text == "Enter Your Name: " > > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_computed_label/__init__.py b/WebDriverTests/imported/w3c/webdriver/tests/get_computed_label/__init__.py >new file mode 100644 >index 000000000000..e69de29bb2d1 >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_computed_label/get.py b/WebDriverTests/imported/w3c/webdriver/tests/get_computed_label/get.py >new file mode 100644 >index 000000000000..3904c6ce41f6 >--- /dev/null >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_computed_label/get.py >@@ -0,0 +1,36 @@ >+import pytest >+ >+from webdriver.error import NoSuchAlertException >+ >+from tests.support.asserts import assert_error, assert_success >+from tests.support.inline import inline >+ >+ >+def get_computed_label(session, element): >+ return session.transport.send( >+ "GET", "session/{session_id}/element/{element_id}/computedlabel".format( >+ session_id=session.session_id, >+ element_id=element)) >+ >+ >+def test_no_browsing_context(session, closed_window): >+ response = get_computed_label(session) >+ assert_error(response, "no such window") >+ >+ >+def test_no_user_prompt(session): >+ response = get_computed_label(session) >+ assert_error(response, "no such alert") >+ >+ >+@pytest.mark.parametrize("html,tag,label", [ >+ ("<button>ok</button>", "button", "ok"), >+ ("<button aria-labelledby=\"one two\"></button><div id=one>ok</div><div id=two>go</div>", "button", "ok go"), >+ ("<button aria-label=foo>bar</button>", "button", "foo"), >+ ("<label><input> foo</label>", "input", "foo"), >+ ("<label for=b>foo<label><input id=b>", "input", "foo")]) >+def test_get_computed_label(session, html, tag, label): >+ session.url = inline("{0}".format(tag)) >+ element = session.find.css(tag, all=False) >+ result = get_computed_label(session, element.id) >+ assert_success(result, label) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_computed_role/__init__.py b/WebDriverTests/imported/w3c/webdriver/tests/get_computed_role/__init__.py >new file mode 100644 >index 000000000000..e69de29bb2d1 >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_computed_role/get.py b/WebDriverTests/imported/w3c/webdriver/tests/get_computed_role/get.py >new file mode 100644 >index 000000000000..10fd628f156b >--- /dev/null >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_computed_role/get.py >@@ -0,0 +1,34 @@ >+import pytest >+ >+from webdriver.error import NoSuchAlertException >+ >+from tests.support.asserts import assert_error, assert_success >+from tests.support.inline import inline >+ >+ >+def get_computed_role(session, element): >+ return session.transport.send( >+ "GET", "session/{session_id}/element/{element_id}/computedrole".format( >+ session_id=session.session_id, >+ element_id=element)) >+ >+ >+def test_no_browsing_context(session, closed_window): >+ response = get_computed_role(session, "id") >+ assert_error(response, "no such window") >+ >+ >+def test_no_user_prompt(session): >+ response = get_computed_role(session, "id") >+ assert_error(response, "no such alert") >+ >+ >+@pytest.mark.parametrize("html,tag,expected", [ >+ ("<li role=menuitem>foo", "li", "menu"), >+ ("<input role=searchbox>", "input", "searchbox"), >+ ("<img role=presentation>", "img", "presentation")]) >+def test_computed_roles(session, html, tag, expected): >+ session.url = inline(html) >+ element = session.find.css(tag, all=False) >+ result = get_computed_role(session, element.id) >+ assert_success(result, expected) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_current_url/get.py b/WebDriverTests/imported/w3c/webdriver/tests/get_current_url/get.py >index 0383a8aba9e9..1ca13a1a6d5e 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_current_url/get.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_current_url/get.py >@@ -1,11 +1,12 @@ >-from six import text_type >+import pytest > > from tests.support import platform_name >-from tests.support.inline import inline > from tests.support.asserts import assert_error, assert_success > >-doc = inline("<p>frame") >-alert_doc = inline("<script>window.alert()</script>") >+ >+@pytest.fixture >+def doc(inline): >+ return inline("<p>frame") > > > def get_current_url(session): >@@ -18,14 +19,14 @@ def test_no_top_browsing_context(session, closed_window): > assert_error(response, "no such window") > > >-def test_no_browsing_context(session, closed_frame): >+def test_no_browsing_context(session, closed_frame, doc): > session.url = doc > > response = get_current_url(session) > assert_success(response, doc) > > >-def test_get_current_url_matches_location(session): >+def test_get_current_url_matches_location(session, doc): > session.url = doc > > response = get_current_url(session) >@@ -37,7 +38,7 @@ def test_get_current_url_payload(session): > > response = get_current_url(session) > value = assert_success(response) >- assert isinstance(value, text_type) >+ assert isinstance(value, str) > > > def test_get_current_url_special_pages(session): >@@ -76,7 +77,7 @@ def test_set_malformed_url(session): > assert_error(response, "invalid argument") > > >-def test_get_current_url_after_modified_location(session): >+def test_get_current_url_after_modified_location(session, doc): > session.url = doc > > response = get_current_url(session) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_current_url/iframe.py b/WebDriverTests/imported/w3c/webdriver/tests/get_current_url/iframe.py >index 944e61b583fd..80a960ce8a15 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_current_url/iframe.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_current_url/iframe.py >@@ -1,7 +1,6 @@ > import pytest > > from tests.support.asserts import assert_success >-from tests.support.inline import iframe, inline > > > """ >@@ -15,9 +14,20 @@ also enforce process isolation based on site origin. > This is known to sometimes cause problems for WebDriver implementations. > """ > >-frame_doc = inline("<p>frame") >-one_frame_doc = inline("<iframe src='%s'></iframe>" % frame_doc) >-nested_frames_doc = inline("<iframe src='%s'></iframe>" % one_frame_doc) >+ >+@pytest.fixture >+def frame_doc(inline): >+ return inline("<p>frame") >+ >+ >+@pytest.fixture >+def one_frame_doc(inline, frame_doc): >+ return inline("<iframe src='%s'></iframe>" % frame_doc) >+ >+ >+@pytest.fixture >+def nested_frames_doc(inline, one_frame_doc): >+ return inline("<iframe src='%s'></iframe>" % one_frame_doc) > > > def get_current_url(session): >@@ -25,7 +35,7 @@ def get_current_url(session): > "GET", "session/{session_id}/url".format(**vars(session))) > > >-def test_iframe(session): >+def test_iframe(session, one_frame_doc): > top_level_doc = one_frame_doc > session.url = top_level_doc > >@@ -37,7 +47,7 @@ def test_iframe(session): > assert_success(response, top_level_doc) > > >-def test_nested_iframe(session): >+def test_nested_iframe(session, nested_frames_doc): > session.url = nested_frames_doc > top_level_doc = session.url > >@@ -53,7 +63,7 @@ def test_nested_iframe(session): > > > @pytest.mark.parametrize("domain", ["", "alt"], ids=["same_origin", "cross_origin"]) >-def test_origin(session, domain, url): >+def test_origin(session, inline, iframe, domain): > top_level_doc = inline(iframe("<p>frame", domain=domain)) > > session.url = top_level_doc >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_current_url/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/get_current_url/user_prompts.py >index f0e95d12f14e..d657c188241d 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_current_url/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_current_url/user_prompts.py >@@ -3,7 +3,6 @@ > import pytest > > from tests.support.asserts import assert_dialog_handled, assert_error, assert_success >-from tests.support.inline import inline > > > def get_current_url(session): >@@ -11,7 +10,7 @@ def get_current_url(session): > > > @pytest.fixture >-def check_user_prompt_closed_without_exception(session, create_dialog): >+def check_user_prompt_closed_without_exception(session, create_dialog, inline): > def check_user_prompt_closed_without_exception(dialog_type, retval): > session.url = inline("<p id=1>") > expected_url = session.url >@@ -27,7 +26,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_closed_with_exception(session, create_dialog): >+def check_user_prompt_closed_with_exception(session, create_dialog, inline): > def check_user_prompt_closed_with_exception(dialog_type, retval): > session.url = inline("<p id=1>") > >@@ -42,7 +41,7 @@ def check_user_prompt_closed_with_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_not_closed_but_exception(session, create_dialog): >+def check_user_prompt_not_closed_but_exception(session, create_dialog, inline): > def check_user_prompt_not_closed_but_exception(dialog_type): > session.url = inline("<p id=1>") > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_element_attribute/get.py b/WebDriverTests/imported/w3c/webdriver/tests/get_element_attribute/get.py >index b18d9ca8b22a..528cbd29f020 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_element_attribute/get.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_element_attribute/get.py >@@ -1,7 +1,6 @@ > import pytest > > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline > > > def get_element_attribute(session, element, attr): >@@ -13,8 +12,14 @@ def get_element_attribute(session, element, attr): > > > def test_no_top_browsing_context(session, closed_window): >+ original_handle, element = closed_window >+ response = get_element_attribute(session, element.id, "id") >+ assert_error(response, "no such window") > response = get_element_attribute(session, "foo", "id") > assert_error(response, "no such window") >+ session.window_handle = original_handle >+ response = get_element_attribute(session, element.id, "id") >+ assert_error(response, "no such element") > > > def test_no_browsing_context(session, closed_frame): >@@ -28,7 +33,7 @@ def test_element_not_found(session): > assert_error(result, "no such element") > > >-def test_element_stale(session): >+def test_element_stale(session, inline): > session.url = inline("<input id=foo>") > element = session.find.css("input", all=False) > session.refresh() >@@ -36,7 +41,7 @@ def test_element_stale(session): > assert_error(result, "stale element reference") > > >-def test_normal(session): >+def test_normal(session, inline): > # 13.2 Step 5 > session.url = inline("<input type=checkbox>") > element = session.find.css("input", all=False) >@@ -73,7 +78,7 @@ def test_normal(session): > ("track", ["default"]), > ("video", ["autoplay", "controls", "loop", "muted"]) > ]) >-def test_boolean_attribute(session, tag, attrs): >+def test_boolean_attribute(session, inline, tag, attrs): > for attr in attrs: > session.url = inline("<{0} {1}>".format(tag, attr)) > element = session.find.css(tag, all=False) >@@ -81,7 +86,7 @@ def test_boolean_attribute(session, tag, attrs): > assert_success(result, "true") > > >-def test_global_boolean_attributes(session): >+def test_global_boolean_attributes(session, inline): > session.url = inline("<p hidden>foo") > element = session.find.css("p", all=False) > result = get_element_attribute(session, element.id, "hidden") >@@ -103,3 +108,14 @@ def test_global_boolean_attributes(session): > element = session.find.css("p", all=False) > result = get_element_attribute(session, element.id, "itemscope") > assert_success(result, None) >+ >+ >+@pytest.mark.parametrize("is_relative", [True, False], ids=["relative", "absolute"]) >+def test_anchor_href(session, inline, url, is_relative): >+ href = "/foo.html" if is_relative else url("/foo.html") >+ >+ session.url = inline("<a href='{}'>foo</a>".format(href)) >+ element = session.find.css("a", all=False) >+ >+ response = get_element_attribute(session, element.id, "href") >+ assert_success(response, href) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_element_attribute/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/get_element_attribute/user_prompts.py >index 5ee7929afdf8..009cb1e5fa1e 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_element_attribute/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_element_attribute/user_prompts.py >@@ -3,7 +3,6 @@ > import pytest > > from tests.support.asserts import assert_error, assert_success, assert_dialog_handled >-from tests.support.inline import inline > > > def get_element_attribute(session, element, attr): >@@ -15,7 +14,7 @@ def get_element_attribute(session, element, attr): > > > @pytest.fixture >-def check_user_prompt_closed_without_exception(session, create_dialog): >+def check_user_prompt_closed_without_exception(session, create_dialog, inline): > def check_user_prompt_closed_without_exception(dialog_type, retval): > session.url = inline("<input id=foo>") > element = session.find.css("#foo", all=False) >@@ -31,7 +30,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_closed_with_exception(session, create_dialog): >+def check_user_prompt_closed_with_exception(session, create_dialog, inline): > def check_user_prompt_closed_with_exception(dialog_type, retval): > session.url = inline("<input id=foo>") > element = session.find.css("#foo", all=False) >@@ -47,7 +46,7 @@ def check_user_prompt_closed_with_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_not_closed_but_exception(session, create_dialog): >+def check_user_prompt_not_closed_but_exception(session, create_dialog, inline): > def check_user_prompt_not_closed_but_exception(dialog_type): > session.url = inline("<input id=foo>") > element = session.find.css("#foo", all=False) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_element_css_value/get.py b/WebDriverTests/imported/w3c/webdriver/tests/get_element_css_value/get.py >index 4e8704d50af9..369ca9c699f8 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_element_css_value/get.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_element_css_value/get.py >@@ -1,5 +1,4 @@ > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline > > > def get_element_css_value(session, element_id, prop): >@@ -14,9 +13,16 @@ def get_element_css_value(session, element_id, prop): > > > def test_no_top_browsing_context(session, closed_window): >+ original_handle, element = closed_window >+ response = get_element_css_value(session, element.id, "display") >+ assert_error(response, "no such window") > response = get_element_css_value(session, "foo", "bar") > assert_error(response, "no such window") > >+ session.window_handle = original_handle >+ response = get_element_css_value(session, element.id, "display") >+ assert_error(response, "no such element") >+ > > def test_no_browsing_context(session, closed_frame): > response = get_element_css_value(session, "foo", "bar") >@@ -28,7 +34,7 @@ def test_element_not_found(session): > assert_error(result, "no such element") > > >-def test_element_stale(session): >+def test_element_stale(session, inline): > session.url = inline("<input>") > element = session.find.css("input", all=False) > session.refresh() >@@ -37,7 +43,7 @@ def test_element_stale(session): > assert_error(result, "stale element reference") > > >-def test_property_name_value(session): >+def test_property_name_value(session, inline): > session.url = inline("""<input style="display: block">""") > element = session.find.css("input", all=False) > >@@ -45,7 +51,7 @@ def test_property_name_value(session): > assert_success(result, "block") > > >-def test_property_name_not_existent(session): >+def test_property_name_not_existent(session, inline): > session.url = inline("<input>") > element = session.find.css("input", all=False) > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_element_css_value/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/get_element_css_value/user_prompts.py >index 529b6ae9183d..b1f9a3fb0a00 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_element_css_value/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_element_css_value/user_prompts.py >@@ -3,7 +3,6 @@ > import pytest > > from tests.support.asserts import assert_error, assert_success, assert_dialog_handled >-from tests.support.inline import inline > > > def get_element_css_value(session, element_id, prop): >@@ -18,7 +17,7 @@ def get_element_css_value(session, element_id, prop): > > > @pytest.fixture >-def check_user_prompt_closed_without_exception(session, create_dialog): >+def check_user_prompt_closed_without_exception(session, create_dialog, inline): > def check_user_prompt_closed_without_exception(dialog_type, retval): > session.url = inline("""<input style="display: block">""") > element = session.find.css("input", all=False) >@@ -34,7 +33,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_closed_with_exception(session, create_dialog): >+def check_user_prompt_closed_with_exception(session, create_dialog, inline): > def check_user_prompt_closed_with_exception(dialog_type, retval): > session.url = inline("""<input style="display: block">""") > element = session.find.css("input", all=False) >@@ -50,7 +49,7 @@ def check_user_prompt_closed_with_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_not_closed_but_exception(session, create_dialog): >+def check_user_prompt_not_closed_but_exception(session, create_dialog, inline): > def check_user_prompt_not_closed_but_exception(dialog_type): > session.url = inline("""<input style="display: block">""") > element = session.find.css("input", all=False) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_element_property/get.py b/WebDriverTests/imported/w3c/webdriver/tests/get_element_property/get.py >index 5c0247edb702..10f7a08ae73e 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_element_property/get.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_element_property/get.py >@@ -1,7 +1,6 @@ > import pytest > > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline > > > def get_element_property(session, element_id, prop): >@@ -13,9 +12,16 @@ def get_element_property(session, element_id, prop): > > > def test_no_top_browsing_context(session, closed_window): >+ original_handle, element = closed_window >+ response = get_element_property(session, element.id, "value") >+ assert_error(response, "no such window") > response = get_element_property(session, "foo", "id") > assert_error(response, "no such window") > >+ session.window_handle = original_handle >+ response = get_element_property(session, element.id, "value") >+ assert_error(response, "no such element") >+ > > def test_no_browsing_context(session, closed_frame): > response = get_element_property(session, "foo", "id") >@@ -27,7 +33,7 @@ def test_element_not_found(session): > assert_error(response, "no such element") > > >-def test_element_stale(session): >+def test_element_stale(session, inline): > session.url = inline("<input id=foobar>") > element = session.find.css("input", all=False) > session.refresh() >@@ -36,7 +42,7 @@ def test_element_stale(session): > assert_error(response, "stale element reference") > > >-def test_property_non_existent(session): >+def test_property_non_existent(session, inline): > session.url = inline("<input>") > element = session.find.css("input", all=False) > >@@ -45,7 +51,7 @@ def test_property_non_existent(session): > assert session.execute_script("return arguments[0].foo", args=(element,)) is None > > >-def test_content_attribute(session): >+def test_content_attribute(session, inline): > session.url = inline("<input value=foobar>") > element = session.find.css("input", all=False) > >@@ -53,7 +59,7 @@ def test_content_attribute(session): > assert_success(response, "foobar") > > >-def test_idl_attribute(session): >+def test_idl_attribute(session, inline): > session.url = inline("<input value=foo>") > element = session.find.css("input", all=False) > session.execute_script("""arguments[0].value = "bar";""", args=(element,)) >@@ -70,7 +76,7 @@ def test_idl_attribute(session): > ("null", None), > ("undefined", None), > ]) >-def test_primitives(session, js_primitive, py_primitive): >+def test_primitives(session, inline, js_primitive, py_primitive): > session.url = inline(""" > <input> > >@@ -93,7 +99,7 @@ def test_primitives(session, js_primitive, py_primitive): > ("null", None), > ("undefined", None), > ]) >-def test_primitives_set_by_execute_script(session, js_primitive, py_primitive): >+def test_primitives_set_by_execute_script(session, inline, js_primitive, py_primitive): > session.url = inline("<input>") > element = session.find.css("input", all=False) > session.execute_script("arguments[0].foobar = {}".format(js_primitive), args=(element,)) >@@ -102,7 +108,7 @@ def test_primitives_set_by_execute_script(session, js_primitive, py_primitive): > assert_success(response, py_primitive) > > >-def test_mutated_element(session): >+def test_mutated_element(session, inline): > session.url = inline("<input type=checkbox>") > element = session.find.css("input", all=False) > element.click() >@@ -114,3 +120,14 @@ def test_mutated_element(session): > > response = get_element_property(session, element.id, "checked") > assert_success(response, True) >+ >+ >+@pytest.mark.parametrize("is_relative", [True, False], ids=["relative", "absolute"]) >+def test_anchor_href(session, inline, url, is_relative): >+ href = "/foo.html" if is_relative else url("/foo.html") >+ >+ session.url = inline("<a href='{}'>foo</a>".format(href)) >+ element = session.find.css("a", all=False) >+ >+ response = get_element_property(session, element.id, "href") >+ assert_success(response, url("/foo.html")) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_element_property/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/get_element_property/user_prompts.py >index 16a098536fde..e5e76947863e 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_element_property/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_element_property/user_prompts.py >@@ -3,7 +3,6 @@ > import pytest > > from tests.support.asserts import assert_error, assert_success, assert_dialog_handled >-from tests.support.inline import inline > > > def get_element_property(session, element_id, name): >@@ -13,7 +12,7 @@ def get_element_property(session, element_id, name): > > > @pytest.fixture >-def check_user_prompt_closed_without_exception(session, create_dialog): >+def check_user_prompt_closed_without_exception(session, create_dialog, inline): > def check_user_prompt_closed_without_exception(dialog_type, retval): > session.url = inline("<input id=foo>") > element = session.find.css("#foo", all=False) >@@ -29,7 +28,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_closed_with_exception(session, create_dialog): >+def check_user_prompt_closed_with_exception(session, create_dialog, inline): > def check_user_prompt_closed_with_exception(dialog_type, retval): > session.url = inline("<input id=foo>") > element = session.find.css("#foo", all=False) >@@ -45,7 +44,7 @@ def check_user_prompt_closed_with_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_not_closed_but_exception(session, create_dialog): >+def check_user_prompt_not_closed_but_exception(session, create_dialog, inline): > def check_user_prompt_not_closed_but_exception(dialog_type): > session.url = inline("<input id=foo>") > element = session.find.css("#foo", all=False) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_element_rect/get.py b/WebDriverTests/imported/w3c/webdriver/tests/get_element_rect/get.py >index 494c45f8e45b..9ab4db4c604f 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_element_rect/get.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_element_rect/get.py >@@ -1,6 +1,5 @@ > from tests.support.asserts import assert_error, assert_success > from tests.support.helpers import element_rect >-from tests.support.inline import inline > > > def get_element_rect(session, element_id): >@@ -14,9 +13,16 @@ def get_element_rect(session, element_id): > > > def test_no_top_browsing_context(session, closed_window): >+ original_handle, element = closed_window >+ response = get_element_rect(session, element.id) >+ assert_error(response, "no such window") > response = get_element_rect(session, "foo") > assert_error(response, "no such window") > >+ session.window_handle = original_handle >+ response = get_element_rect(session, element.id) >+ assert_error(response, "no such element") >+ > > def test_no_browsing_context(session, closed_frame): > response = get_element_rect(session, "foo") >@@ -28,7 +34,7 @@ def test_element_not_found(session): > assert_error(result, "no such element") > > >-def test_element_stale(session): >+def test_element_stale(session, inline): > session.url = inline("<input>") > element = session.find.css("input", all=False) > session.refresh() >@@ -37,7 +43,7 @@ def test_element_stale(session): > assert_error(result, "stale element reference") > > >-def test_basic(session): >+def test_basic(session, inline): > session.url = inline("<input>") > element = session.find.css("input", all=False) > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_element_rect/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/get_element_rect/user_prompts.py >index c51866cb8758..20131603381b 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_element_rect/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_element_rect/user_prompts.py >@@ -4,7 +4,6 @@ import pytest > > from tests.support.asserts import assert_error, assert_success, assert_dialog_handled > from tests.support.helpers import element_rect >-from tests.support.inline import inline > > > def get_element_rect(session, element_id): >@@ -18,7 +17,7 @@ def get_element_rect(session, element_id): > > > @pytest.fixture >-def check_user_prompt_closed_without_exception(session, create_dialog): >+def check_user_prompt_closed_without_exception(session, create_dialog, inline): > def check_user_prompt_closed_without_exception(dialog_type, retval): > session.url = inline("<input>") > element = session.find.css("input", all=False) >@@ -34,7 +33,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_closed_with_exception(session, create_dialog): >+def check_user_prompt_closed_with_exception(session, create_dialog, inline): > def check_user_prompt_closed_with_exception(dialog_type, retval): > session.url = inline("<input>") > element = session.find.css("input", all=False) >@@ -50,7 +49,7 @@ def check_user_prompt_closed_with_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_not_closed_but_exception(session, create_dialog): >+def check_user_prompt_not_closed_but_exception(session, create_dialog, inline): > def check_user_prompt_not_closed_but_exception(dialog_type): > session.url = inline("<input>") > element = session.find.css("input", all=False) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_element_shadow_root/conftest.py b/WebDriverTests/imported/w3c/webdriver/tests/get_element_shadow_root/conftest.py >new file mode 100644 >index 000000000000..afc0c91c3d3f >--- /dev/null >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_element_shadow_root/conftest.py >@@ -0,0 +1,22 @@ >+import pytest >+ >+@pytest.fixture >+def checkbox_dom(inline): >+ return inline(""" >+ <style> >+ custom-checkbox-element { >+ display:block; width:20px; height:20px; >+ } >+ </style> >+ <custom-checkbox-element></custom-checkbox-element> >+ <script> >+ customElements.define('custom-checkbox-element', >+ class extends HTMLElement { >+ constructor() { >+ super(); >+ this.attachShadow({mode: 'open'}).innerHTML = ` >+ <div><input type="checkbox"/></div> >+ `; >+ } >+ }); >+ </script>""") >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_element_shadow_root/get.py b/WebDriverTests/imported/w3c/webdriver/tests/get_element_shadow_root/get.py >new file mode 100644 >index 000000000000..82e52420a2c0 >--- /dev/null >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_element_shadow_root/get.py >@@ -0,0 +1,60 @@ >+import pytest >+ >+from tests.support.asserts import assert_error, assert_same_element, assert_success >+ >+ >+def get_shadow_root(session, element_id): >+ return session.transport.send( >+ "GET", "session/{session_id}/element/{element_id}/shadow".format( >+ session_id=session.session_id, >+ element_id=element_id)) >+ >+ >+def test_no_top_browsing_context(session, closed_window): >+ original_handle, element = closed_window >+ response = get_shadow_root(session, element.id) >+ assert_error(response, "no such window") >+ response = get_shadow_root(session, "foo") >+ assert_error(response, "no such window") >+ >+ session.window_handle = original_handle >+ response = get_shadow_root(session, element.id) >+ assert_error(response, "no such element") >+ >+ >+def test_no_browsing_context(session, closed_frame): >+ response = get_shadow_root(session, "foo") >+ assert_error(response, "no such window") >+ >+ >+def test_element_not_found(session): >+ result = get_shadow_root(session, "foo") >+ assert_error(result, "no such element") >+ >+ >+def test_element_stale(session, checkbox_dom): >+ session.url = checkbox_dom >+ element = session.find.css("custom-checkbox-element", all=False) >+ session.refresh() >+ >+ result = get_shadow_root(session, element.id) >+ assert_error(result, "stale element reference") >+ >+ >+def test_get_shadow_root(session, checkbox_dom): >+ session.url = checkbox_dom >+ expected = session.execute_script( >+ "return document.querySelector('custom-checkbox-element').shadowRoot.host") >+ custom_element = session.find.css("custom-checkbox-element", all=False) >+ response = get_shadow_root(session, custom_element.id) >+ value = assert_success(response) >+ assert isinstance(value, dict) >+ assert "shadow-6066-11e4-a52e-4f735466cecf" in value >+ assert_same_element(session, custom_element, expected) >+ >+ >+def test_no_shadow_root(session, inline): >+ session.url = inline("<div><p>no shadow root</p></div>") >+ element = session.find.css("div", all=False) >+ response = get_shadow_root(session, element.id) >+ assert_error(response, "no such shadow root") >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_element_shadow_root/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/get_element_shadow_root/user_prompts.py >new file mode 100644 >index 000000000000..32671b95a535 >--- /dev/null >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_element_shadow_root/user_prompts.py >@@ -0,0 +1,117 @@ >+# META: timeout=long >+ >+import pytest >+ >+from tests.support.asserts import assert_error, assert_success, assert_dialog_handled >+ >+ >+def get_shadow_root(session, element_id): >+ return session.transport.send( >+ "GET", "session/{session_id}/element/{element_id}/shadow".format( >+ session_id=session.session_id, >+ element_id=element_id)) >+ >+ >+@pytest.fixture >+def check_user_prompt_closed_without_exception(session, create_dialog, checkbox_dom): >+ def check_user_prompt_closed_without_exception(dialog_type, retval): >+ session.url = checkbox_dom >+ element = session.find.css("custom-checkbox-element", all=False) >+ >+ create_dialog(dialog_type, text=dialog_type) >+ >+ response = get_shadow_root(session, element.id) >+ value = assert_success(response) >+ assert isinstance(value, dict) >+ >+ assert_dialog_handled(session, expected_text=dialog_type, expected_retval=retval) >+ >+ return check_user_prompt_closed_without_exception >+ >+ >+@pytest.fixture >+def check_user_prompt_closed_with_exception(session, create_dialog, checkbox_dom): >+ def check_user_prompt_closed_with_exception(dialog_type, retval): >+ session.url = checkbox_dom >+ element = session.find.css("custom-checkbox-element", all=False) >+ >+ create_dialog(dialog_type, text=dialog_type) >+ >+ response = get_shadow_root(session, element.id) >+ assert_error(response, "unexpected alert open") >+ >+ assert_dialog_handled(session, expected_text=dialog_type, expected_retval=retval) >+ >+ return check_user_prompt_closed_with_exception >+ >+ >+@pytest.fixture >+def check_user_prompt_not_closed_but_exception(session, create_dialog, checkbox_dom): >+ def check_user_prompt_not_closed_but_exception(dialog_type): >+ session.url = checkbox_dom >+ element = session.find.css("custom-checkbox-element", all=False) >+ >+ create_dialog(dialog_type, text=dialog_type) >+ >+ response = get_shadow_root(session, element.id) >+ assert_error(response, "unexpected alert open") >+ >+ assert session.alert.text == dialog_type >+ session.alert.dismiss() >+ >+ return check_user_prompt_not_closed_but_exception >+ >+ >+@pytest.mark.capabilities({"unhandledPromptBehavior": "accept"}) >+@pytest.mark.parametrize("dialog_type, retval", [ >+ ("alert", None), >+ ("confirm", True), >+ ("prompt", ""), >+]) >+def test_accept(check_user_prompt_closed_without_exception, dialog_type, retval): >+ check_user_prompt_closed_without_exception(dialog_type, retval) >+ >+ >+@pytest.mark.capabilities({"unhandledPromptBehavior": "accept and notify"}) >+@pytest.mark.parametrize("dialog_type, retval", [ >+ ("alert", None), >+ ("confirm", True), >+ ("prompt", ""), >+]) >+def test_accept_and_notify(check_user_prompt_closed_with_exception, dialog_type, retval): >+ check_user_prompt_closed_with_exception(dialog_type, retval) >+ >+ >+@pytest.mark.capabilities({"unhandledPromptBehavior": "dismiss"}) >+@pytest.mark.parametrize("dialog_type, retval", [ >+ ("alert", None), >+ ("confirm", False), >+ ("prompt", None), >+]) >+def test_dismiss(check_user_prompt_closed_without_exception, dialog_type, retval): >+ check_user_prompt_closed_without_exception(dialog_type, retval) >+ >+ >+@pytest.mark.capabilities({"unhandledPromptBehavior": "dismiss and notify"}) >+@pytest.mark.parametrize("dialog_type, retval", [ >+ ("alert", None), >+ ("confirm", False), >+ ("prompt", None), >+]) >+def test_dismiss_and_notify(check_user_prompt_closed_with_exception, dialog_type, retval): >+ check_user_prompt_closed_with_exception(dialog_type, retval) >+ >+ >+@pytest.mark.capabilities({"unhandledPromptBehavior": "ignore"}) >+@pytest.mark.parametrize("dialog_type", ["alert", "confirm", "prompt"]) >+def test_ignore(check_user_prompt_not_closed_but_exception, dialog_type): >+ check_user_prompt_not_closed_but_exception(dialog_type) >+ >+ >+@pytest.mark.parametrize("dialog_type, retval", [ >+ ("alert", None), >+ ("confirm", False), >+ ("prompt", None), >+]) >+def test_default(check_user_prompt_closed_with_exception, dialog_type, retval): >+ check_user_prompt_closed_with_exception(dialog_type, retval) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_element_tag_name/get.py b/WebDriverTests/imported/w3c/webdriver/tests/get_element_tag_name/get.py >index ffe6dd641e82..cfc63bd788de 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_element_tag_name/get.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_element_tag_name/get.py >@@ -1,5 +1,4 @@ > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline > > > def get_element_tag_name(session, element_id): >@@ -10,9 +9,16 @@ def get_element_tag_name(session, element_id): > > > def test_no_top_browsing_context(session, closed_window): >+ original_handle, element = closed_window >+ response = get_element_tag_name(session, element.id) >+ assert_error(response, "no such window") > response = get_element_tag_name(session, "foo") > assert_error(response, "no such window") > >+ session.window_handle = original_handle >+ response = get_element_tag_name(session, element.id) >+ assert_error(response, "no such element") >+ > > def test_no_browsing_context(session, closed_frame): > response = get_element_tag_name(session, "foo") >@@ -24,7 +30,7 @@ def test_element_not_found(session): > assert_error(result, "no such element") > > >-def test_element_stale(session): >+def test_element_stale(session, inline): > session.url = inline("<input id=foo>") > element = session.find.css("input", all=False) > session.refresh() >@@ -33,7 +39,7 @@ def test_element_stale(session): > assert_error(result, "stale element reference") > > >-def test_get_element_tag_name(session): >+def test_get_element_tag_name(session, inline): > session.url = inline("<input id=foo>") > element = session.find.css("input", all=False) > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_element_tag_name/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/get_element_tag_name/user_prompts.py >index 12a7d448421e..89697d0ad6a2 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_element_tag_name/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_element_tag_name/user_prompts.py >@@ -3,7 +3,6 @@ > import pytest > > from tests.support.asserts import assert_error, assert_success, assert_dialog_handled >-from tests.support.inline import inline > > > def get_element_tag_name(session, element_id): >@@ -12,7 +11,7 @@ def get_element_tag_name(session, element_id): > > > @pytest.fixture >-def check_user_prompt_closed_without_exception(session, create_dialog): >+def check_user_prompt_closed_without_exception(session, create_dialog, inline): > def check_user_prompt_closed_without_exception(dialog_type, retval): > session.url = inline("<input id=foo>") > element = session.find.css("#foo", all=False) >@@ -28,7 +27,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_closed_with_exception(session, create_dialog): >+def check_user_prompt_closed_with_exception(session, create_dialog, inline): > def check_user_prompt_closed_with_exception(dialog_type, retval): > session.url = inline("<input id=foo>") > element = session.find.css("#foo", all=False) >@@ -44,7 +43,7 @@ def check_user_prompt_closed_with_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_not_closed_but_exception(session, create_dialog): >+def check_user_prompt_not_closed_but_exception(session, create_dialog, inline): > def check_user_prompt_not_closed_but_exception(dialog_type): > session.url = inline("<input id=foo>") > element = session.find.css("#foo", all=False) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_element_text/get.py b/WebDriverTests/imported/w3c/webdriver/tests/get_element_text/get.py >index 5f169a3a233b..72dc0cf735e5 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_element_text/get.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_element_text/get.py >@@ -1,5 +1,4 @@ > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline > > > def get_element_text(session, element_id): >@@ -10,29 +9,32 @@ def get_element_text(session, element_id): > > > def test_no_top_browsing_context(session, closed_window): >+ original_handle, element = closed_window >+ response = get_element_text(session, element.id) >+ assert_error(response, "no such window") > response = get_element_text(session, "foo") > assert_error(response, "no such window") > >+ session.window_handle = original_handle >+ response = get_element_text(session, element.id) >+ assert_error(response, "no such element") >+ > > def test_no_browsing_context(session, closed_frame): > response = get_element_text(session, "foo") > assert_error(response, "no such window") > > >-def test_getting_text_of_a_non_existant_element_is_an_error(session): >+def test_getting_text_of_a_non_existant_element_is_an_error(session, inline): > session.url = inline("""<body>Hello world</body>""") > > result = get_element_text(session, "foo") > assert_error(result, "no such element") > > >-def test_read_element_text(session): >- session.url = inline(""" >- <body> >- Noise before <span id='id'>This has an ID</span>. Noise after >- </body>""") >- >+def test_read_element_text(session, inline): >+ session.url = inline("Before f<span id='id'>oo</span> after") > element = session.find.css("#id", all=False) > > result = get_element_text(session, element.id) >- assert_success(result, "This has an ID") >+ assert_success(result, "oo") >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_element_text/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/get_element_text/user_prompts.py >index 1ee5cde4da52..9f0bb386cdf5 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_element_text/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_element_text/user_prompts.py >@@ -3,7 +3,6 @@ > import pytest > > from tests.support.asserts import assert_error, assert_success, assert_dialog_handled >-from tests.support.inline import inline > > > def get_element_text(session, element_id): >@@ -14,7 +13,7 @@ def get_element_text(session, element_id): > > > @pytest.fixture >-def check_user_prompt_closed_without_exception(session, create_dialog): >+def check_user_prompt_closed_without_exception(session, create_dialog, inline): > def check_user_prompt_closed_without_exception(dialog_type, retval): > session.url = inline("<p id=foo>bar</p>") > element = session.find.css("#foo", all=False) >@@ -30,7 +29,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_closed_with_exception(session, create_dialog): >+def check_user_prompt_closed_with_exception(session, create_dialog, inline): > def check_user_prompt_closed_with_exception(dialog_type, retval): > session.url = inline("<p id=foo>bar</p>") > element = session.find.css("#foo", all=False) >@@ -46,7 +45,7 @@ def check_user_prompt_closed_with_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_not_closed_but_exception(session, create_dialog): >+def check_user_prompt_not_closed_but_exception(session, create_dialog, inline): > def check_user_prompt_not_closed_but_exception(dialog_type): > session.url = inline("<p id=foo>bar</p>") > element = session.find.css("#foo", all=False) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_named_cookie/get.py b/WebDriverTests/imported/w3c/webdriver/tests/get_named_cookie/get.py >index 102c18f0275c..d1e83b6a8152 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_named_cookie/get.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_named_cookie/get.py >@@ -1,12 +1,10 @@ > import pytest > > from datetime import datetime, timedelta >-from six import integer_types, text_type > > > from tests.support.asserts import assert_error, assert_success > from tests.support.helpers import clear_all_cookies >-from tests.support.inline import inline > > > def get_named_cookie(session, name): >@@ -38,13 +36,13 @@ def test_get_named_session_cookie(session, url): > # table for cookie conversion > # https://w3c.github.io/webdriver/#dfn-table-for-cookie-conversion > assert "name" in cookie >- assert isinstance(cookie["name"], text_type) >+ assert isinstance(cookie["name"], str) > assert "value" in cookie >- assert isinstance(cookie["value"], text_type) >+ assert isinstance(cookie["value"], str) > assert "path" in cookie >- assert isinstance(cookie["path"], text_type) >+ assert isinstance(cookie["path"], str) > assert "domain" in cookie >- assert isinstance(cookie["domain"], text_type) >+ assert isinstance(cookie["domain"], str) > assert "secure" in cookie > assert isinstance(cookie["secure"], bool) > assert "httpOnly" in cookie >@@ -52,7 +50,7 @@ def test_get_named_session_cookie(session, url): > if "expiry" in cookie: > assert cookie.get("expiry") is None > assert "sameSite" in cookie >- assert isinstance(cookie["sameSite"], text_type) >+ assert isinstance(cookie["sameSite"], str) > > assert cookie["name"] == "foo" > assert cookie["value"] == "bar" >@@ -72,13 +70,13 @@ def test_get_named_cookie(session, url): > assert isinstance(cookie, dict) > > assert "name" in cookie >- assert isinstance(cookie["name"], text_type) >+ assert isinstance(cookie["name"], str) > assert "value" in cookie >- assert isinstance(cookie["value"], text_type) >+ assert isinstance(cookie["value"], str) > assert "expiry" in cookie >- assert isinstance(cookie["expiry"], integer_types) >+ assert isinstance(cookie["expiry"], int) > assert "sameSite" in cookie >- assert isinstance(cookie["sameSite"], text_type) >+ assert isinstance(cookie["sameSite"], str) > > assert cookie["name"] == "foo" > assert cookie["value"] == "bar" >@@ -87,7 +85,7 @@ def test_get_named_cookie(session, url): > cookie["expiry"]).strftime(utc_string_format) == a_day_from_now > > >-def test_duplicated_cookie(session, url, server_config): >+def test_duplicated_cookie(session, url, server_config, inline): > new_cookie = { > "name": "hello", > "value": "world", >@@ -113,11 +111,11 @@ def test_duplicated_cookie(session, url, server_config): > assert isinstance(cookie, dict) > > assert "name" in cookie >- assert isinstance(cookie["name"], text_type) >+ assert isinstance(cookie["name"], str) > assert "value" in cookie >- assert isinstance(cookie["value"], text_type) >+ assert isinstance(cookie["value"], str) > assert "sameSite" in cookie >- assert isinstance(cookie["sameSite"], text_type) >+ assert isinstance(cookie["sameSite"], str) > > assert cookie["name"] == new_cookie["name"] > assert cookie["value"] == "newworld" >@@ -136,11 +134,11 @@ def test_get_cookie_with_same_site_flag(session, url, same_site): > assert isinstance(cookie, dict) > > assert "name" in cookie >- assert isinstance(cookie["name"], text_type) >+ assert isinstance(cookie["name"], str) > assert "value" in cookie >- assert isinstance(cookie["value"], text_type) >+ assert isinstance(cookie["value"], str) > assert "sameSite" in cookie >- assert isinstance(cookie["sameSite"], text_type) >+ assert isinstance(cookie["sameSite"], str) > > assert cookie["name"] == "foo" > assert cookie["value"] == "bar" >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_page_source/source.py b/WebDriverTests/imported/w3c/webdriver/tests/get_page_source/source.py >index 5484ccbf2eef..cc4e208835c8 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_page_source/source.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_page_source/source.py >@@ -1,5 +1,4 @@ > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline > > > def get_page_source(session): >@@ -17,7 +16,7 @@ def test_no_browsing_context(session, closed_frame): > assert_error(response, "no such window") > > >-def test_source_matches_outer_html(session): >+def test_source_matches_outer_html(session, inline): > session.url = inline("<html><head><title>Cheese</title><body>Peas") > > expected = session.execute_script("return document.documentElement.outerHTML") >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_page_source/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/get_page_source/user_prompts.py >index 3b0cdff97dd9..13cb31595e61 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_page_source/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_page_source/user_prompts.py >@@ -3,7 +3,6 @@ > import pytest > > from tests.support.asserts import assert_error, assert_success, assert_dialog_handled >-from tests.support.inline import inline > > > def get_page_source(session): >@@ -12,7 +11,7 @@ def get_page_source(session): > > > @pytest.fixture >-def check_user_prompt_closed_without_exception(session, create_dialog): >+def check_user_prompt_closed_without_exception(session, create_dialog, inline): > def check_user_prompt_closed_without_exception(dialog_type, retval): > session.url = inline("<div/>") > expected = session.execute_script("return document.documentElement.outerHTML") >@@ -28,7 +27,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_closed_with_exception(session, create_dialog): >+def check_user_prompt_closed_with_exception(session, create_dialog, inline): > def check_user_prompt_closed_with_exception(dialog_type, retval): > session.url = inline("<div/>") > >@@ -43,7 +42,7 @@ def check_user_prompt_closed_with_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_not_closed_but_exception(session, create_dialog): >+def check_user_prompt_not_closed_but_exception(session, create_dialog, inline): > def check_user_prompt_not_closed_but_exception(dialog_type): > session.url = inline("<div/>") > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_title/get.py b/WebDriverTests/imported/w3c/webdriver/tests/get_title/get.py >index 34405774ba06..e696ec340339 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_title/get.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_title/get.py >@@ -1,7 +1,4 @@ >-from six import text_type >- > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline > > > def get_title(session): >@@ -14,7 +11,7 @@ def test_payload(session): > > response = get_title(session) > value = assert_success(response) >- assert isinstance(value, text_type) >+ assert isinstance(value, str) > > > def test_no_top_browsing_context(session, closed_window): >@@ -22,28 +19,28 @@ def test_no_top_browsing_context(session, closed_window): > assert_error(response, "no such window") > > >-def test_no_browsing_context(session, closed_frame): >+def test_no_browsing_context(session, closed_frame, inline): > session.url = inline("<title>Foo</title>") > > response = get_title(session) > assert_success(response, "Foo") > > >-def test_with_duplicated_title(session): >+def test_with_duplicated_title(session, inline): > session.url = inline("<title>First</title><title>Second</title>") > > result = get_title(session) > assert_success(result, "First") > > >-def test_without_title(session): >+def test_without_title(session, inline): > session.url = inline("<h2>Hello</h2>") > > result = get_title(session) > assert_success(result, "") > > >-def test_after_modification(session): >+def test_after_modification(session, inline): > session.url = inline("<title>Initial</title><h2>Hello</h2>") > session.execute_script("document.title = 'Updated'") > >@@ -51,7 +48,7 @@ def test_after_modification(session): > assert_success(result, "Updated") > > >-def test_strip_and_collapse(session): >+def test_strip_and_collapse(session, inline): > document = "<title> a b\tc\nd\t \n e\t\n </title><h2>Hello</h2>" > session.url = inline(document) > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_title/iframe.py b/WebDriverTests/imported/w3c/webdriver/tests/get_title/iframe.py >index 5991bf359069..9c5ab0b595d3 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_title/iframe.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_title/iframe.py >@@ -1,7 +1,6 @@ > import pytest > > from tests.support.asserts import assert_success >-from tests.support.inline import iframe, inline > > > """ >@@ -15,9 +14,20 @@ also enforce process isolation based on site origin. > This is known to sometimes cause problems for WebDriver implementations. > """ > >-frame_doc = inline("<title>cheese</title><p>frame") >-one_frame_doc = inline("<title>bar</title><iframe src='%s'></iframe>" % frame_doc) >-nested_frames_doc = inline("<title>foo</title><iframe src='%s'></iframe>" % one_frame_doc) >+ >+@pytest.fixture >+def frame_doc(inline): >+ return inline("<title>cheese</title><p>frame") >+ >+ >+@pytest.fixture >+def one_frame_doc(inline, frame_doc): >+ return inline("<title>bar</title><iframe src='%s'></iframe>" % frame_doc) >+ >+ >+@pytest.fixture >+def nested_frames_doc(inline, one_frame_doc): >+ return inline("<title>foo</title><iframe src='%s'></iframe>" % one_frame_doc) > > > def get_title(session): >@@ -25,14 +35,14 @@ def get_title(session): > "GET", "session/{session_id}/title".format(**vars(session))) > > >-def test_no_iframe(session): >+def test_no_iframe(session, inline): > session.url = inline("<title>Foobar</title><h2>Hello</h2>") > > result = get_title(session) > assert_success(result, "Foobar") > > >-def test_iframe(session): >+def test_iframe(session, one_frame_doc): > session.url = one_frame_doc > > frame = session.find.css("iframe", all=False) >@@ -43,7 +53,7 @@ def test_iframe(session): > assert_success(response, "bar") > > >-def test_nested_iframe(session): >+def test_nested_iframe(session, nested_frames_doc): > session.url = nested_frames_doc > > outer_frame = session.find.css("iframe", all=False) >@@ -58,7 +68,7 @@ def test_nested_iframe(session): > > > @pytest.mark.parametrize("domain", ["", "alt"], ids=["same_origin", "cross_origin"]) >-def test_origin(session, domain, url): >+def test_origin(session, inline, iframe, domain): > session.url = inline("<title>foo</title>{}".format( > iframe("<title>bar</title><p>frame", domain=domain))) > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_title/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/get_title/user_prompts.py >index 76d65c088a0b..0fd51e46f32a 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_title/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_title/user_prompts.py >@@ -3,7 +3,6 @@ > import pytest > > from tests.support.asserts import assert_dialog_handled, assert_error, assert_success >-from tests.support.inline import inline > > > def get_title(session): >@@ -12,7 +11,7 @@ def get_title(session): > > > @pytest.fixture >-def check_user_prompt_closed_without_exception(session, create_dialog): >+def check_user_prompt_closed_without_exception(session, create_dialog, inline): > def check_user_prompt_closed_without_exception(dialog_type, retval): > session.url = inline("<title>Foo</title>") > expected_title = session.title >@@ -28,7 +27,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_closed_with_exception(session, create_dialog): >+def check_user_prompt_closed_with_exception(session, create_dialog, inline): > def check_user_prompt_closed_with_exception(dialog_type, retval): > session.url = inline("<title>Foo</title>") > >@@ -43,7 +42,7 @@ def check_user_prompt_closed_with_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_not_closed_but_exception(session, create_dialog): >+def check_user_prompt_not_closed_but_exception(session, create_dialog, inline): > def check_user_prompt_not_closed_but_exception(dialog_type): > session.url = inline("<title>Foo</title>") > >@@ -117,7 +116,7 @@ def test_default(check_user_prompt_closed_with_exception, dialog_type, retval): > # trigger the creation of a dialog at all. Therefore, this test should only be > # run in contexts that support the dialog (a condition that may not be > # determined automatically). >-# def test_title_with_non_simple_dialog(session): >+# def test_title_with_non_simple_dialog(session, inline): > # document = "<title>With non-simple dialog</title><h2>Hello</h2>" > # spawn = """ > # var done = arguments[0]; >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_window_handle/get.py b/WebDriverTests/imported/w3c/webdriver/tests/get_window_handle/get.py >index 8c262033c383..68441da5ef84 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_window_handle/get.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_window_handle/get.py >@@ -1,3 +1,5 @@ >+import pytest >+ > from tests.support.asserts import assert_error, assert_success > > >@@ -19,3 +21,23 @@ def test_no_browsing_context(session, closed_frame): > def test_basic(session): > response = get_window_handle(session) > assert_success(response, session.window_handle) >+ >+ >+# Capability needed as long as no valid certificate is available: >+# https://github.com/web-platform-tests/wpt/issues/28847 >+@pytest.mark.capabilities({"acceptInsecureCerts": True}) >+def test_navigation_with_coop_headers(session, url): >+ base_path = ("/webdriver/tests/support/html/subframe.html" + >+ "?pipe=header(Cross-Origin-Opener-Policy,same-origin") >+ >+ session.url = url(base_path, protocol="https") >+ response = get_window_handle(session) >+ first_handle = assert_success(response) >+ >+ # navigating to another domain with COOP headers will force a process change >+ # in most browsers >+ session.url = url(base_path, protocol="https", domain="alt") >+ response = get_window_handle(session) >+ second_handle = assert_success(response) >+ >+ assert first_handle == second_handle >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/get_window_rect/get.py b/WebDriverTests/imported/w3c/webdriver/tests/get_window_rect/get.py >index 793650feef46..f7592a30e067 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/get_window_rect/get.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/get_window_rect/get.py >@@ -1,8 +1,4 @@ > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline >- >- >-alert_doc = inline("<script>window.alert()</script>") > > > def get_window_rect(session): >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/idlharness.html b/WebDriverTests/imported/w3c/webdriver/tests/idlharness.html >deleted file mode 100644 >index d049d835f2fc..000000000000 >--- a/WebDriverTests/imported/w3c/webdriver/tests/idlharness.html >+++ /dev/null >@@ -1,23 +0,0 @@ >-<!doctype html> >-<meta charset=utf-8> >-<link rel="help" href="https://w3c.github.io/webdriver/"> >-<script src=/resources/testharness.js></script> >-<script src=/resources/testharnessreport.js></script> >-<script src=/resources/WebIDLParser.js></script> >-<script src=/resources/idlharness.js></script> >- >-<script> >- "use strict"; >- >- promise_test(async () => { >- const idl_array = new IdlArray(); >- const idl = await fetch("/interfaces/webdriver.idl").then(r => r.text()); >- const html = await fetch("/interfaces/html.idl").then(r => r.text()); >- idl_array.add_idls(idl); >- idl_array.add_dependency_idls(html); >- idl_array.add_objects({ >- Navigator: ["navigator"] >- }); >- idl_array.test(); >- }, "Test IDL implementation of webdriver API"); >-</script> >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/idlharness.window.js b/WebDriverTests/imported/w3c/webdriver/tests/idlharness.window.js >new file mode 100644 >index 000000000000..e92e151d8983 >--- /dev/null >+++ b/WebDriverTests/imported/w3c/webdriver/tests/idlharness.window.js >@@ -0,0 +1,16 @@ >+// META: script=/resources/WebIDLParser.js >+// META: script=/resources/idlharness.js >+ >+// https://w3c.github.io/webdriver/ >+ >+"use strict"; >+ >+idl_test( >+ ["webdriver"], >+ ["html"], >+ idl_array => { >+ idl_array.add_objects({ >+ Navigator: ["navigator"] >+ }); >+ } >+); >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/is_element_enabled/enabled.py b/WebDriverTests/imported/w3c/webdriver/tests/is_element_enabled/enabled.py >index 88b1534ded1a..4a5a92ba5e91 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/is_element_enabled/enabled.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/is_element_enabled/enabled.py >@@ -1,7 +1,6 @@ > import pytest > > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline > > > def is_element_enabled(session, element_id): >@@ -15,16 +14,23 @@ def is_element_enabled(session, element_id): > > > def test_no_top_browsing_context(session, closed_window): >+ original_handle, element = closed_window >+ response = is_element_enabled(session, element.id) >+ assert_error(response, "no such window") > response = is_element_enabled(session, "foo") > assert_error(response, "no such window") > >+ session.window_handle = original_handle >+ response = is_element_enabled(session, element.id) >+ assert_error(response, "no such element") >+ > > def test_no_browsing_context(session, closed_frame): > response = is_element_enabled(session, "foo") > assert_error(response, "no such window") > > >-def test_element_stale(session): >+def test_element_stale(session, inline): > session.url = inline("<input>") > element = session.find.css("input", all=False) > session.refresh() >@@ -34,7 +40,7 @@ def test_element_stale(session): > > > @pytest.mark.parametrize("element", ["button", "input", "select", "textarea"]) >-def test_form_control_disabled(session, element): >+def test_form_control_disabled(session, inline, element): > session.url = inline("<{} disabled/>".format(element)) > element = session.find.css(element, all=False) > >@@ -43,7 +49,7 @@ def test_form_control_disabled(session, element): > > > @pytest.mark.parametrize("element", ["button", "input", "select", "textarea"]) >-def test_form_control_enabled(session, element): >+def test_form_control_enabled(session, inline, element): > session.url = inline("<{}/>".format(element)) > element = session.find.css(element, all=False) > >@@ -52,7 +58,7 @@ def test_form_control_enabled(session, element): > > > @pytest.mark.parametrize("element", ["button", "input", "select", "textarea"]) >-def test_fieldset_disabled_descendant(session, element): >+def test_fieldset_disabled_descendant(session, inline, element): > session.url = inline("<fieldset disabled><{}/></fieldset>".format(element)) > element = session.find.css(element, all=False) > >@@ -61,7 +67,7 @@ def test_fieldset_disabled_descendant(session, element): > > > @pytest.mark.parametrize("element", ["button", "input", "select", "textarea"]) >-def test_fieldset_enabled_descendant(session, element): >+def test_fieldset_enabled_descendant(session, inline, element): > session.url = inline("<fieldset><{}/></fieldset>".format(element)) > element = session.find.css(element, all=False) > >@@ -70,7 +76,7 @@ def test_fieldset_enabled_descendant(session, element): > > > @pytest.mark.parametrize("element", ["button", "input", "select", "textarea"]) >-def test_fieldset_disabled_descendant_legend(session, element): >+def test_fieldset_disabled_descendant_legend(session, inline, element): > session.url = inline("<fieldset disabled><legend><{}/></legend></fieldset>".format(element)) > element = session.find.css(element, all=False) > >@@ -79,7 +85,7 @@ def test_fieldset_disabled_descendant_legend(session, element): > > > @pytest.mark.parametrize("element", ["button", "input", "select", "textarea"]) >-def test_fieldset_enabled_descendant_legend(session, element): >+def test_fieldset_enabled_descendant_legend(session, inline, element): > session.url = inline("<fieldset><legend><{}/></legend></fieldset>".format(element)) > element = session.find.css(element, all=False) > >@@ -88,7 +94,7 @@ def test_fieldset_enabled_descendant_legend(session, element): > > > @pytest.mark.parametrize("element", ["button", "input", "select", "textarea"]) >-def test_xhtml_form_control_disabled(session, element): >+def test_xhtml_form_control_disabled(session, inline, element): > session.url = inline("""<{} disabled="disabled"/>""".format(element), > doctype="xhtml") > element = session.find.css(element, all=False) >@@ -98,7 +104,7 @@ def test_xhtml_form_control_disabled(session, element): > > > @pytest.mark.parametrize("element", ["button", "input", "select", "textarea"]) >-def test_xhtml_form_control_enabled(session, element): >+def test_xhtml_form_control_enabled(session, inline, element): > session.url = inline("""<{}/>""".format(element), doctype="xhtml") > element = session.find.css(element, all=False) > >@@ -106,7 +112,7 @@ def test_xhtml_form_control_enabled(session, element): > assert_success(result, True) > > >-def test_xml_always_not_enabled(session): >+def test_xml_always_not_enabled(session, inline): > session.url = inline("""<note></note>""", doctype="xml") > element = session.find.css("note", all=False) > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/is_element_enabled/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/is_element_enabled/user_prompts.py >index bd8bc81bdfcc..5dd7d582bd3d 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/is_element_enabled/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/is_element_enabled/user_prompts.py >@@ -3,7 +3,6 @@ > import pytest > > from tests.support.asserts import assert_error, assert_dialog_handled, assert_success >-from tests.support.inline import inline > > > def is_element_enabled(session, element_id): >@@ -17,7 +16,7 @@ def is_element_enabled(session, element_id): > > > @pytest.fixture >-def check_user_prompt_closed_without_exception(session, create_dialog): >+def check_user_prompt_closed_without_exception(session, create_dialog, inline): > def check_user_prompt_closed_without_exception(dialog_type, retval): > session.url = inline("<input id=foo disabled>") > element = session.find.css("#foo", all=False) >@@ -33,7 +32,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_closed_with_exception(session, create_dialog): >+def check_user_prompt_closed_with_exception(session, create_dialog, inline): > def check_user_prompt_closed_with_exception(dialog_type, retval): > session.url = inline("<input id=foo disabled>") > element = session.find.css("#foo", all=False) >@@ -49,7 +48,7 @@ def check_user_prompt_closed_with_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_not_closed_but_exception(session, create_dialog): >+def check_user_prompt_not_closed_but_exception(session, create_dialog, inline): > def check_user_prompt_not_closed_but_exception(dialog_type): > session.url = inline("<input id=foo disabled>") > element = session.find.css("#foo", all=False) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/is_element_selected/selected.py b/WebDriverTests/imported/w3c/webdriver/tests/is_element_selected/selected.py >index 79a1615dd9fd..6c8e8155cab1 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/is_element_selected/selected.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/is_element_selected/selected.py >@@ -1,12 +1,18 @@ >+import pytest > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline > > >-check_doc = inline(""" >+@pytest.fixture >+def check_doc(inline): >+ return inline(""" > <input id=checked type=checkbox checked> > <input id=notChecked type=checkbox> > """) >-option_doc = inline(""" >+ >+ >+@pytest.fixture >+def option_doc(inline): >+ return inline(""" > <select> > <option id=notSelected>r- > <option id=selected selected>r+ >@@ -22,16 +28,24 @@ def is_element_selected(session, element_id): > > > def test_no_top_browsing_context(session, closed_window): >+ original_handle, element = closed_window >+ >+ response = is_element_selected(session, element.id) >+ assert_error(response, "no such window") > response = is_element_selected(session, "foo") > assert_error(response, "no such window") > >+ session.window_handle = original_handle >+ response = is_element_selected(session, element.id) >+ assert_error(response, "no such element") >+ > > def test_no_browsing_context(session, closed_frame): > response = is_element_selected(session, "foo") > assert_error(response, "no such window") > > >-def test_element_stale(session): >+def test_element_stale(session, check_doc): > session.url = check_doc > element = session.find.css("#checked", all=False) > session.refresh() >@@ -40,7 +54,7 @@ def test_element_stale(session): > assert_error(result, "stale element reference") > > >-def test_element_checked(session): >+def test_element_checked(session, check_doc): > session.url = check_doc > element = session.find.css("#checked", all=False) > >@@ -48,7 +62,7 @@ def test_element_checked(session): > assert_success(result, True) > > >-def test_checkbox_not_selected(session): >+def test_checkbox_not_selected(session, check_doc): > session.url = check_doc > element = session.find.css("#notChecked", all=False) > >@@ -56,7 +70,7 @@ def test_checkbox_not_selected(session): > assert_success(result, False) > > >-def test_element_selected(session): >+def test_element_selected(session, option_doc): > session.url = option_doc > element = session.find.css("#selected", all=False) > >@@ -64,7 +78,7 @@ def test_element_selected(session): > assert_success(result, True) > > >-def test_element_not_selected(session): >+def test_element_not_selected(session, option_doc): > session.url = option_doc > element = session.find.css("#notSelected", all=False) > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/is_element_selected/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/is_element_selected/user_prompts.py >index 674f80c5c75b..96da2c08bd7e 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/is_element_selected/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/is_element_selected/user_prompts.py >@@ -3,7 +3,6 @@ > import pytest > > from tests.support.asserts import assert_error, assert_dialog_handled, assert_success >-from tests.support.inline import inline > > > def is_element_selected(session, element_id): >@@ -14,7 +13,7 @@ def is_element_selected(session, element_id): > > > @pytest.fixture >-def check_user_prompt_closed_without_exception(session, create_dialog): >+def check_user_prompt_closed_without_exception(session, create_dialog, inline): > def check_user_prompt_closed_without_exception(dialog_type, retval): > session.url = inline("<input id=foo type=checkbox checked>") > element = session.find.css("#foo", all=False) >@@ -31,7 +30,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_closed_with_exception(session, create_dialog): >+def check_user_prompt_closed_with_exception(session, create_dialog, inline): > def check_user_prompt_closed_with_exception(dialog_type, retval): > session.url = inline("<input id=foo type=checkbox checked>") > element = session.find.css("#foo", all=False) >@@ -47,7 +46,7 @@ def check_user_prompt_closed_with_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_not_closed_but_exception(session, create_dialog): >+def check_user_prompt_not_closed_but_exception(session, create_dialog, inline): > def check_user_prompt_not_closed_but_exception(dialog_type): > session.url = inline("<input id=foo type=checkbox checked>") > element = session.find.css("#foo", all=False) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/maximize_window/maximize.py b/WebDriverTests/imported/w3c/webdriver/tests/maximize_window/maximize.py >index 6903eece70d0..e233e45a10d7 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/maximize_window/maximize.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/maximize_window/maximize.py >@@ -84,6 +84,7 @@ def test_maximize_when_resized_to_max_size(session): > session.end() > session.window.maximize() > available = session.window.size >+ session.window.size = (800, 600) > session.end() > > session.window.size = available >@@ -95,4 +96,5 @@ def test_maximize_when_resized_to_max_size(session): > # its expected dimensions. > before = session.window.size > session.window.maximize() >- assert session.window.size == before >+ after = session.window.size >+ assert after == before >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/navigate_to/navigate.py b/WebDriverTests/imported/w3c/webdriver/tests/navigate_to/navigate.py >index 289e4321011f..25d768167aa0 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/navigate_to/navigate.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/navigate_to/navigate.py >@@ -1,8 +1,11 @@ >-from tests.support import platform_name >+import time >+ >+import pytest >+from webdriver import error > from webdriver.transport import Response > >+from tests.support import platform_name > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline > > > def navigate_to(session, url): >@@ -17,7 +20,7 @@ def test_null_parameter_value(session, http): > assert_error(Response.from_http(response), "invalid argument") > > >-def test_null_response_value(session): >+def test_null_response_value(session, inline): > response = navigate_to(session, inline("<div/>")) > value = assert_success(response) > assert value is None >@@ -28,7 +31,7 @@ def test_no_top_browsing_context(session, closed_window): > assert_error(response, "no such window") > > >-def test_no_browsing_context(session, closed_frame): >+def test_no_browsing_context(session, closed_frame, inline): > doc = inline("<p>foo") > > response = navigate_to(session, doc) >@@ -52,3 +55,48 @@ def test_file_protocol(session, server_config): > if session.url.endswith('/'): > url += '/' > assert session.url == url >+ >+ >+# Capability needed as long as no valid certificate is available: >+# https://github.com/web-platform-tests/wpt/issues/28847 >+@pytest.mark.capabilities({"acceptInsecureCerts": True}) >+def test_cross_origin(session, inline, url): >+ base_path = ("/webdriver/tests/support/html/subframe.html" + >+ "?pipe=header(Cross-Origin-Opener-Policy,same-origin") >+ first_page = url(base_path, protocol="https") >+ second_page = url(base_path, protocol="https", domain="alt") >+ >+ response = navigate_to(session, first_page) >+ assert_success(response) >+ >+ assert session.url == first_page >+ elem = session.find.css("#delete", all=False) >+ >+ response = navigate_to(session, second_page) >+ assert_success(response) >+ >+ assert session.url == second_page >+ with pytest.raises(error.StaleElementReferenceException): >+ elem.click() >+ >+ session.find.css("#delete", all=False) >+ >+ >+@pytest.mark.capabilities({"pageLoadStrategy": "eager"}) >+def test_utf8_meta_tag_after_1024_bytes(session, url): >+ page = url("/webdriver/tests/support/html/meta-utf8-after-1024-bytes.html") >+ >+ # Loading the page will cause a real parse commencing, and a renavigation >+ # to the same URL getting triggered subsequently. Test that the navigate >+ # command waits long enough. >+ response = navigate_to(session, page) >+ assert_success(response) >+ >+ # If the command returns too early the property will be reset due to the >+ # subsequent page load. >+ session.execute_script("window.foo = 'bar'") >+ >+ # Use delay to allow a possible missing subsequent navigation to start >+ time.sleep(1) >+ >+ assert session.execute_script("return window.foo") == "bar" >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/navigate_to/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/navigate_to/user_prompts.py >index 6a94c11bf1d3..682bc40f4fe9 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/navigate_to/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/navigate_to/user_prompts.py >@@ -3,7 +3,6 @@ > import pytest > > from tests.support.asserts import assert_error, assert_success, assert_dialog_handled >-from tests.support.inline import inline > > > def navigate_to(session, url): >@@ -13,7 +12,7 @@ def navigate_to(session, url): > > > @pytest.fixture >-def check_user_prompt_closed_without_exception(session, create_dialog): >+def check_user_prompt_closed_without_exception(session, create_dialog, inline): > def check_user_prompt_closed_without_exception(dialog_type, retval): > url = inline("<div/>") > >@@ -30,7 +29,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_closed_with_exception(session, create_dialog): >+def check_user_prompt_closed_with_exception(session, create_dialog, inline): > def check_user_prompt_closed_with_exception(dialog_type, retval): > url = inline("<div/>") > >@@ -47,7 +46,7 @@ def check_user_prompt_closed_with_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_not_closed_but_exception(session, create_dialog): >+def check_user_prompt_not_closed_but_exception(session, create_dialog, inline): > def check_user_prompt_not_closed_but_exception(dialog_type): > url = inline("<div/>") > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/new_session/response.py b/WebDriverTests/imported/w3c/webdriver/tests/new_session/response.py >index cbe9e7d7fb14..43a8d5793114 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/new_session/response.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/new_session/response.py >@@ -1,29 +1,27 @@ > import uuid > import pytest > >-from six import string_types >- > from tests.support.asserts import assert_success > > > def test_sessionid(new_session, add_browser_capabilities): > response, _ = new_session({"capabilities": {"alwaysMatch": add_browser_capabilities({})}}) > value = assert_success(response) >- assert isinstance(value["sessionId"], string_types) >+ assert isinstance(value["sessionId"], str) > uuid.UUID(hex=value["sessionId"]) > > > @pytest.mark.parametrize("capability, type", [ >- ("browserName", string_types), >- ("browserVersion", string_types), >- ("platformName", string_types), >+ ("browserName", str), >+ ("browserVersion", str), >+ ("platformName", str), > ("acceptInsecureCerts", bool), >- ("pageLoadStrategy", string_types), >+ ("pageLoadStrategy", str), > ("proxy", dict), > ("setWindowRect", bool), > ("timeouts", dict), > ("strictFileInteractability", bool), >- ("unhandledPromptBehavior", string_types), >+ ("unhandledPromptBehavior", str), > ]) > def test_capability_type(session, capability, type): > assert isinstance(session.capabilities, dict) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/new_session/websocket_url.py b/WebDriverTests/imported/w3c/webdriver/tests/new_session/websocket_url.py >new file mode 100644 >index 000000000000..452decc90aa1 >--- /dev/null >+++ b/WebDriverTests/imported/w3c/webdriver/tests/new_session/websocket_url.py >@@ -0,0 +1,7 @@ >+from tests.support.asserts import assert_success >+ >+def test_websocket_url(new_session, add_browser_capabilities): >+ response, _ = new_session({"capabilities": { >+ "alwaysMatch": add_browser_capabilities({"webSocketUrl": True})}}) >+ value = assert_success(response) >+ assert value["capabilities"]["webSocketUrl"].startswith("ws://") >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/new_window/new_tab.py b/WebDriverTests/imported/w3c/webdriver/tests/new_window/new_tab.py >index 9300b04c45fa..270e4b51b88f 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/new_window/new_tab.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/new_window/new_tab.py >@@ -1,5 +1,4 @@ > from tests.support.asserts import assert_success >-from tests.support.inline import inline > > from . import opener, window_name > >@@ -32,7 +31,7 @@ def test_keeps_current_window_handle(session): > assert session.window_handle == original_handle > > >-def test_opens_about_blank_in_new_tab(session): >+def test_opens_about_blank_in_new_tab(session, inline): > url = inline("<p>foo") > session.url = url > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/new_window/new_window.py b/WebDriverTests/imported/w3c/webdriver/tests/new_window/new_window.py >index b34854cc2f3f..d3c224502704 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/new_window/new_window.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/new_window/new_window.py >@@ -1,5 +1,4 @@ > from tests.support.asserts import assert_success >-from tests.support.inline import inline > > from . import opener, window_name > >@@ -32,7 +31,7 @@ def test_keeps_current_window_handle(session): > assert session.window_handle == original_handle > > >-def test_opens_about_blank_in_new_window(session): >+def test_opens_about_blank_in_new_window(session, inline): > url = inline("<p>foo") > session.url = url > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/conftest.py b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/conftest.py >index 970f7b7dd9be..188f8ddbb5ef 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/conftest.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/conftest.py >@@ -32,6 +32,22 @@ def mouse_chain(session): > {"pointerType": "mouse"}) > > >+@pytest.fixture >+def touch_chain(session): >+ return session.actions.sequence( >+ "pointer", >+ "pointer_id", >+ {"pointerType": "touch"}) >+ >+ >+@pytest.fixture >+def pen_chain(session): >+ return session.actions.sequence( >+ "pointer", >+ "pointer_id", >+ {"pointerType": "pen"}) >+ >+ > @pytest.fixture > def none_chain(session): > return session.actions.sequence("none", "none_id") >@@ -66,3 +82,8 @@ def test_actions_page(session, url): > @pytest.fixture > def test_actions_scroll_page(session, url): > session.url = url("/webdriver/tests/perform_actions/support/test_actions_scroll_wdspec.html") >+ >+ >+@pytest.fixture >+def test_actions_pointer_page(session, url): >+ session.url = url("/webdriver/tests/perform_actions/support/test_actions_pointer_wdspec.html") >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/key_events.py b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/key_events.py >index c3958015bfd4..8dbe024d1839 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/key_events.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/key_events.py >@@ -5,7 +5,8 @@ from collections import defaultdict > import pytest > > from tests.perform_actions.support.keys import ALL_EVENTS, Keys, ALTERNATIVE_KEY_NAMES >-from tests.perform_actions.support.refine import filter_dict, get_events, get_keys >+from tests.perform_actions.support.refine import get_events, get_keys >+from tests.support.helpers import filter_dict > > > def test_keyup_only_sends_no_events(session, key_reporter, key_chain): >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer.py b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer.py >index ad546921460e..674d46d7b610 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer.py >@@ -5,17 +5,12 @@ import pytest > from webdriver.error import NoSuchWindowException > > from tests.perform_actions.support.mouse import get_inview_center, get_viewport_rect >-from tests.perform_actions.support.refine import filter_dict, get_events >+from tests.perform_actions.support.refine import get_events > from tests.support.asserts import assert_move_to_coordinates >-from tests.support.inline import inline >+from tests.support.helpers import filter_dict > from tests.support.sync import Poll > > >-def link_doc(dest): >- content = "<a href=\"{}\" id=\"link\">destination</a>".format(dest) >- return inline(content) >- >- > def test_null_response_value(session, mouse_chain): > value = mouse_chain.click().perform() > assert value is None >@@ -95,9 +90,9 @@ def test_click_element_center(session, test_actions_page, mouse_chain): > assert e["target"] == "outer" > > >-def test_click_navigation(session, url): >+def test_click_navigation(session, url, inline): > destination = url("/webdriver/tests/actions/support/test_actions_wdspec.html") >- start = link_doc(destination) >+ start = inline("<a href=\"{}\" id=\"link\">destination</a>".format(destination)) > > def click(link): > mouse_chain = session.actions.sequence( >@@ -115,6 +110,85 @@ def test_click_navigation(session, url): > Poll(session, message=error_message).until(lambda s: s.url == destination) > > >+def test_pen_pointer_properties(session, test_actions_pointer_page, pen_chain): >+ pointerArea = session.find.css("#pointerArea", all=False) >+ center = get_inview_center(pointerArea.rect, get_viewport_rect(session)) >+ pen_chain.pointer_move(0, 0, origin=pointerArea) \ >+ .pointer_down(pressure=0.36, tilt_x=-72, tilt_y=9, twist=86) \ >+ .pointer_move(10, 10, origin=pointerArea) \ >+ .pointer_up() \ >+ .pointer_move(80, 50, origin=pointerArea) \ >+ .perform() >+ events = get_events(session) >+ assert len(events) == 10 >+ event_types = [e["type"] for e in events] >+ assert ["pointerover", "pointerenter", "pointermove", "pointerdown", >+ "pointerover", "pointerenter", "pointermove", "pointerup", >+ "pointerout", "pointerleave"] == event_types >+ assert events[3]["type"] == "pointerdown" >+ assert events[3]["pageX"] == pytest.approx(center["x"], abs=1.0) >+ assert events[3]["pageY"] == pytest.approx(center["y"], abs=1.0) >+ assert events[3]["target"] == "pointerArea" >+ assert events[3]["pointerType"] == "pen" >+ # The default value of width and height for mouse and pen inputs is 1 >+ assert round(events[3]["width"], 2) == 1 >+ assert round(events[3]["height"], 2) == 1 >+ assert round(events[3]["pressure"], 2) == 0.36 >+ assert events[3]["tiltX"] == -72 >+ assert events[3]["tiltY"] == 9 >+ assert events[3]["twist"] == 86 >+ assert events[6]["type"] == "pointermove" >+ assert events[6]["pageX"] == pytest.approx(center["x"]+10, abs=1.0) >+ assert events[6]["pageY"] == pytest.approx(center["y"]+10, abs=1.0) >+ assert events[6]["target"] == "pointerArea" >+ assert events[6]["pointerType"] == "pen" >+ assert round(events[6]["width"], 2) == 1 >+ assert round(events[6]["height"], 2) == 1 >+ # The default value of pressure for all inputs is 0.5, other properties are 0 >+ assert round(events[6]["pressure"], 2) == 0.5 >+ assert events[6]["tiltX"] == 0 >+ assert events[6]["tiltY"] == 0 >+ assert events[6]["twist"] == 0 >+ >+ >+def test_touch_pointer_properties(session, test_actions_pointer_page, touch_chain): >+ pointerArea = session.find.css("#pointerArea", all=False) >+ center = get_inview_center(pointerArea.rect, get_viewport_rect(session)) >+ touch_chain.pointer_move(0, 0, origin=pointerArea) \ >+ .pointer_down(width=23, height=31, pressure=0.78, tilt_x=21, tilt_y=-8, twist=355) \ >+ .pointer_move(10, 10, origin=pointerArea, width=39, height=35, pressure=0.91, tilt_x=-19, tilt_y=62, twist=345) \ >+ .pointer_up() \ >+ .pointer_move(80, 50, origin=pointerArea) \ >+ .perform() >+ events = get_events(session) >+ assert len(events) == 7 >+ event_types = [e["type"] for e in events] >+ assert ["pointerover", "pointerenter", "pointerdown", "pointermove", >+ "pointerup", "pointerout", "pointerleave"] == event_types >+ assert events[2]["type"] == "pointerdown" >+ assert events[2]["pageX"] == pytest.approx(center["x"], abs=1.0) >+ assert events[2]["pageY"] == pytest.approx(center["y"], abs=1.0) >+ assert events[2]["target"] == "pointerArea" >+ assert events[2]["pointerType"] == "touch" >+ assert round(events[2]["width"], 2) == 23 >+ assert round(events[2]["height"], 2) == 31 >+ assert round(events[2]["pressure"], 2) == 0.78 >+ assert events[2]["tiltX"] == 21 >+ assert events[2]["tiltY"] == -8 >+ assert events[2]["twist"] == 355 >+ assert events[3]["type"] == "pointermove" >+ assert events[3]["pageX"] == pytest.approx(center["x"]+10, abs=1.0) >+ assert events[3]["pageY"] == pytest.approx(center["y"]+10, abs=1.0) >+ assert events[3]["target"] == "pointerArea" >+ assert events[3]["pointerType"] == "touch" >+ assert round(events[3]["width"], 2) == 39 >+ assert round(events[3]["height"], 2) == 35 >+ assert round(events[3]["pressure"], 2) == 0.91 >+ assert events[3]["tiltX"] == -19 >+ assert events[3]["tiltY"] == 62 >+ assert events[3]["twist"] == 345 >+ >+ > @pytest.mark.parametrize("drag_duration", [0, 300, 800]) > @pytest.mark.parametrize("dx, dy", [ > (20, 0), (0, 15), (10, 15), (-20, 0), (10, -15), (-10, -15) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer_contextmenu.py b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer_contextmenu.py >index fda3f18bbc1c..50f684327f2a 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer_contextmenu.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer_contextmenu.py >@@ -1,7 +1,8 @@ > import pytest > > from tests.perform_actions.support.keys import Keys >-from tests.perform_actions.support.refine import filter_dict, get_events >+from tests.perform_actions.support.refine import get_events >+from tests.support.helpers import filter_dict > > > @pytest.mark.parametrize("modifier, prop", [ >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer_dblclick.py b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer_dblclick.py >index 8c89cef5f380..5be635a4d560 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer_dblclick.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer_dblclick.py >@@ -1,7 +1,8 @@ > import pytest > >-from tests.perform_actions.support.refine import filter_dict, get_events >+from tests.perform_actions.support.refine import get_events > from tests.support.asserts import assert_move_to_coordinates >+from tests.support.helpers import filter_dict > > > @pytest.mark.parametrize("click_pause", [0, 200]) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer_modifier_click.py b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer_modifier_click.py >index ba81f6d090b2..28c4086e0f8c 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer_modifier_click.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer_modifier_click.py >@@ -1,7 +1,8 @@ > import pytest > > from tests.perform_actions.support.keys import Keys >-from tests.perform_actions.support.refine import filter_dict, get_events >+from tests.perform_actions.support.refine import get_events >+from tests.support.helpers import filter_dict > > > @pytest.mark.parametrize("modifier, prop", [ >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer_origin.py b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer_origin.py >index 4c9dc879e999..c4e5db7a0017 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer_origin.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer_origin.py >@@ -3,23 +3,25 @@ import pytest > from webdriver import MoveTargetOutOfBoundsException > > from tests.perform_actions.support.mouse import get_inview_center, get_viewport_rect >-from tests.support.inline import inline > > >-def origin_doc(inner_style, outer_style=""): >- return inline(""" >- <div id="outer" style="{1}" >- onmousemove="window.coords = {{x: event.clientX, y: event.clientY}}"> >- <div id="inner" style="{0}"></div> >- </div> >- """.format(inner_style, outer_style)) >+@pytest.fixture >+def origin_doc(inline): >+ def origin_doc(inner_style, outer_style=""): >+ return inline(""" >+ <div id="outer" style="{1}" >+ onmousemove="window.coords = {{x: event.clientX, y: event.clientY}}"> >+ <div id="inner" style="{0}"></div> >+ </div> >+ """.format(inner_style, outer_style)) >+ return origin_doc > > > def get_click_coordinates(session): > return session.execute_script("return window.coords;") > > >-def test_viewport_inside(session, mouse_chain): >+def test_viewport_inside(session, mouse_chain, origin_doc): > point = {"x": 50, "y": 50} > > session.url = origin_doc("width: 100px; height: 50px; background: green;") >@@ -39,7 +41,7 @@ def test_viewport_outside(session, mouse_chain): > .perform() > > >-def test_pointer_inside(session, mouse_chain): >+def test_pointer_inside(session, mouse_chain, origin_doc): > start_point = {"x": 50, "y": 50} > offset = {"x": 10, "y": 5} > >@@ -61,7 +63,7 @@ def test_pointer_outside(session, mouse_chain): > .perform() > > >-def test_element_center_point(session, mouse_chain): >+def test_element_center_point(session, mouse_chain, origin_doc): > session.url = origin_doc("width: 100px; height: 50px; background: green;") > elem = session.find.css("#inner", all=False) > center = get_inview_center(elem.rect, get_viewport_rect(session)) >@@ -75,7 +77,7 @@ def test_element_center_point(session, mouse_chain): > assert click_coords["y"] == pytest.approx(center["y"], abs = 1.0) > > >-def test_element_center_point_with_offset(session, mouse_chain): >+def test_element_center_point_with_offset(session, mouse_chain, origin_doc): > session.url = origin_doc("width: 100px; height: 50px; background: green;") > elem = session.find.css("#inner", all=False) > center = get_inview_center(elem.rect, get_viewport_rect(session)) >@@ -89,7 +91,7 @@ def test_element_center_point_with_offset(session, mouse_chain): > assert click_coords["y"] == pytest.approx(center["y"] + 15, abs = 1.0) > > >-def test_element_in_view_center_point_partly_visible(session, mouse_chain): >+def test_element_in_view_center_point_partly_visible(session, mouse_chain, origin_doc): > session.url = origin_doc("""width: 100px; height: 50px; background: green; > position: relative; left: -50px; top: -25px;""") > elem = session.find.css("#inner", all=False) >@@ -104,7 +106,7 @@ def test_element_in_view_center_point_partly_visible(session, mouse_chain): > assert click_coords["y"] == pytest.approx(center["y"], abs = 1.0) > > >-def test_element_larger_than_viewport(session, mouse_chain): >+def test_element_larger_than_viewport(session, mouse_chain, origin_doc): > session.url = origin_doc("width: 300vw; height: 300vh; background: green;") > elem = session.find.css("#inner", all=False) > center = get_inview_center(elem.rect, get_viewport_rect(session)) >@@ -118,7 +120,7 @@ def test_element_larger_than_viewport(session, mouse_chain): > assert click_coords["y"] == pytest.approx(center["y"], abs = 1.0) > > >-def test_element_outside_of_view_port(session, mouse_chain): >+def test_element_outside_of_view_port(session, mouse_chain, origin_doc): > session.url = origin_doc("""width: 100px; height: 50px; background: green; > position: relative; left: -200px; top: -100px;""") > elem = session.find.css("#inner", all=False) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer_pause_dblclick.py b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer_pause_dblclick.py >index 85679f3614a8..d46178a1d650 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer_pause_dblclick.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer_pause_dblclick.py >@@ -1,5 +1,6 @@ > from tests.perform_actions.support.mouse import get_inview_center, get_viewport_rect >-from tests.perform_actions.support.refine import filter_dict, get_events >+from tests.perform_actions.support.refine import get_events >+from tests.support.helpers import filter_dict > > _DBLCLICK_INTERVAL = 640 > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer_tripleclick.py b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer_tripleclick.py >index 81d51be1a7e6..8239fc2a6062 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer_tripleclick.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/pointer_tripleclick.py >@@ -1,13 +1,13 @@ >-from tests.perform_actions.support.refine import filter_dict, get_events >+from tests.perform_actions.support.refine import get_events > from tests.support.asserts import assert_move_to_coordinates >-from tests.support.inline import inline >+from tests.support.helpers import filter_dict > > lots_of_text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor "\ > "incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud "\ > "exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." > > >-def test_tripleclick_at_coordinates(session, mouse_chain): >+def test_tripleclick_at_coordinates(session, mouse_chain, inline): > """ > This test does a triple click on a coordinate. On desktop platforms > this will select a paragraph. On mobile this will not have the same >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/support/keys.py b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/support/keys.py >index 2d38e1dff876..9e87ebfac1d7 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/support/keys.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/support/keys.py >@@ -21,7 +21,6 @@ import sys > > from collections import OrderedDict > from inspect import getmembers >-from six import text_type > > class Keys(object): > """ >@@ -107,7 +106,7 @@ class Keys(object): > R_DELETE = u"\uE05D" > > >-ALL_KEYS = getmembers(Keys, lambda x: type(x) == text_type) >+ALL_KEYS = getmembers(Keys, lambda x: type(x) == str) > > ALL_EVENTS = OrderedDict( > [ >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/support/refine.py b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/support/refine.py >index d5d24e934f61..35c962b9ecbf 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/support/refine.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/support/refine.py >@@ -27,12 +27,3 @@ def get_keys(input_el): > return "" > else: > return rv >- >- >-def filter_dict(source, d): >- """Filter `source` dict to only contain same keys as `d` dict. >- >- :param source: dictionary to filter. >- :param d: dictionary whose keys determine the filtering. >- """ >- return {k: source[k] for k in d.keys()} >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/support/test_actions_pointer_wdspec.html b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/support/test_actions_pointer_wdspec.html >new file mode 100644 >index 000000000000..f1fd9b2da3f8 >--- /dev/null >+++ b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/support/test_actions_pointer_wdspec.html >@@ -0,0 +1,102 @@ >+<!doctype html> >+<meta charset=utf-8> >+<html> >+<head> >+ <title>Test Actions</title> >+ <style> >+ div { padding:0px; margin: 0px; } >+ #trackPointer { position: fixed; } >+ #resultContainer { width: 600px; height: 60px; } >+ .area { width: 100px; height: 50px; background-color: #ccc; } >+ </style> >+ <script> >+ "use strict"; >+ var els = {}; >+ var allEvents = { events: [] }; >+ function displayMessage(message) { >+ document.getElementById("events").innerHTML = "<p>" + message + "</p>"; >+ } >+ >+ function appendMessage(message) { >+ document.getElementById("events").innerHTML += "<p>" + message + "</p>"; >+ } >+ >+ function recordPointerEvent(event) { >+ if (event.type === "contextmenu") { >+ event.preventDefault(); >+ } >+ allEvents.events.push({ >+ "type": event.type, >+ "button": event.button, >+ "buttons": event.buttons, >+ "pageX": event.pageX, >+ "pageY": event.pageY, >+ "ctrlKey": event.ctrlKey, >+ "metaKey": event.metaKey, >+ "altKey": event.altKey, >+ "shiftKey": event.shiftKey, >+ "target": event.target.id, >+ "pointerType": event.pointerType, >+ "width": event.width, >+ "height": event.height, >+ "pressure": event.pressure, >+ "tangentialPressure": event.tangentialPressure, >+ "tiltX": event.tiltX, >+ "tiltY": event.tiltY, >+ "twist": event.twist, >+ "altitudeAngle": event.altitudeAngle, >+ "azimuthAngle": event.azimuthAngle >+ }); >+ appendMessage(event.type + " " + >+ "button: " + event.button + ", " + >+ "pageX: " + event.pageX + ", " + >+ "pageY: " + event.pageY + ", " + >+ "button: " + event.button + ", " + >+ "buttons: " + event.buttons + ", " + >+ "ctrlKey: " + event.ctrlKey + ", " + >+ "altKey: " + event.altKey + ", " + >+ "metaKey: " + event.metaKey + ", " + >+ "shiftKey: " + event.shiftKey + ", " + >+ "target id: " + event.target.id + ", " + >+ "pointerType: " + event.pointerType + ", " + >+ "width: " + event.width + ", " + >+ "height: " + event.height + ", " + >+ "pressure: " + event.pressure + ", " + >+ "tangentialPressure: " + event.tangentialPressure + ", " + >+ "tiltX: " + event.tiltX + ", " + >+ "tiltY: " + event.tiltY + ", " + >+ "twist: " + event.twist + ", " + >+ "altitudeAngle: " + event.altitudeAngle + ", " + >+ "azimuthAngle: " + event.azimuthAngle); >+ } >+ >+ function resetEvents() { >+ allEvents.events.length = 0; >+ displayMessage(""); >+ } >+ >+ document.addEventListener("DOMContentLoaded", function() { >+ var pointerArea = document.getElementById("pointerArea"); >+ pointerArea.addEventListener("pointerdown", recordPointerEvent); >+ pointerArea.addEventListener("pointermove", recordPointerEvent); >+ pointerArea.addEventListener("pointerup", recordPointerEvent); >+ pointerArea.addEventListener("pointerover", recordPointerEvent); >+ pointerArea.addEventListener("pointerenter", recordPointerEvent); >+ pointerArea.addEventListener("pointerout", recordPointerEvent); >+ pointerArea.addEventListener("pointerleave", recordPointerEvent); >+ }); >+ </script> >+</head> >+<body> >+ <div id="trackPointer" class="block"></div> >+ <div> >+ <h2>PointerReporter</h2> >+ <div id="pointerArea" class="area"> >+ </div> >+ </div> >+ <div id="resultContainer"> >+ <h2>Events</h2> >+ <div id="events"></div> >+ </div> >+</body> >+</html> >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/wheel.py b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/wheel.py >index fc84f2f2e014..67734800b19d 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/wheel.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/perform_actions/wheel.py >@@ -2,8 +2,9 @@ import pytest > > from webdriver.error import NoSuchWindowException > >-from tests.perform_actions.support.refine import filter_dict, get_events >+from tests.perform_actions.support.refine import get_events > from tests.support.asserts import assert_move_to_coordinates >+from tests.support.helpers import filter_dict > > > def test_null_response_value(session, wheel_chain): >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/print/printcmd.py b/WebDriverTests/imported/w3c/webdriver/tests/print/printcmd.py >index 341e069f7bb6..89296d2b146b 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/print/printcmd.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/print/printcmd.py >@@ -1,11 +1,8 @@ >-import base64 >+from base64 import decodebytes > > import pytest > >-from six import ensure_binary >- > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline > > > def do_print(session, options): >@@ -27,11 +24,11 @@ def test_no_top_browsing_context(session, closed_window): > def test_no_browsing_context(session, closed_frame): > response = do_print(session, {}) > value = assert_success(response) >- pdf = base64.decodestring(ensure_binary(value)) >+ pdf = decodebytes(value.encode()) > assert_pdf(pdf) > > >-def test_html_document(session): >+def test_html_document(session, inline): > session.url = inline("Test") > > response = do_print(session, { >@@ -40,7 +37,7 @@ def test_html_document(session): > "shrinkToFit": False > }) > value = assert_success(response) >- pdf = base64.decodestring(ensure_binary(value)) >+ pdf = decodebytes(value.encode()) > # TODO: Test that the output is reasonable > assert_pdf(pdf) > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/print/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/print/user_prompts.py >index a3f7a10ac372..0a29b518ac9a 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/print/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/print/user_prompts.py >@@ -1,17 +1,14 @@ > # META: timeout=long >-import base64 >+from base64 import decodebytes > > import pytest > >-from six import ensure_binary >- > from tests.support.asserts import assert_dialog_handled, assert_error, assert_success >-from tests.support.inline import inline > from .printcmd import do_print, assert_pdf > > > @pytest.fixture >-def check_user_prompt_closed_without_exception(session, create_dialog): >+def check_user_prompt_closed_without_exception(session, create_dialog, inline): > def check_user_prompt_closed_without_exception(dialog_type, retval): > session.url = inline("<input/>") > >@@ -20,7 +17,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > response = do_print(session, {}) > value = assert_success(response) > >- pdf = base64.decodestring(ensure_binary(value)) >+ pdf = decodebytes(value.encode()) > assert_dialog_handled(session, expected_text=dialog_type, expected_retval=retval) > > assert_pdf(pdf) >@@ -29,7 +26,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_closed_with_exception(session, create_dialog): >+def check_user_prompt_closed_with_exception(session, create_dialog, inline): > def check_user_prompt_closed_with_exception(dialog_type, retval): > session.url = inline("<input/>") > >@@ -44,7 +41,7 @@ def check_user_prompt_closed_with_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_not_closed_but_exception(session, create_dialog): >+def check_user_prompt_not_closed_but_exception(session, create_dialog, inline): > def check_user_prompt_not_closed_but_exception(dialog_type): > session.url = inline("<input/>") > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/refresh/refresh.py b/WebDriverTests/imported/w3c/webdriver/tests/refresh/refresh.py >index eef413017e5e..33595d0ff71a 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/refresh/refresh.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/refresh/refresh.py >@@ -2,7 +2,6 @@ import pytest > > from webdriver.error import NoSuchElementException, StaleElementReferenceException > >-from tests.support.inline import inline > from tests.support.asserts import assert_error, assert_success > > >@@ -11,7 +10,7 @@ def refresh(session): > "POST", "session/{session_id}/refresh".format(**vars(session))) > > >-def test_null_response_value(session): >+def test_null_response_value(session, inline): > session.url = inline("<div>") > > response = refresh(session) >@@ -24,7 +23,7 @@ def test_no_top_browsing_context(session, closed_window): > assert_error(response, "no such window") > > >-def test_no_browsing_context(session, closed_frame): >+def test_no_browsing_context(session, closed_frame, inline): > url = inline("<div id=foo>") > > session.url = url >@@ -40,7 +39,7 @@ def test_no_browsing_context(session, closed_frame): > assert session.find.css("#foo", all=False) > > >-def test_basic(session): >+def test_basic(session, inline): > url = inline("<div id=foo>") > > session.url = url >@@ -56,7 +55,7 @@ def test_basic(session): > assert session.find.css("#foo", all=False) > > >-def test_dismissed_beforeunload(session): >+def test_dismissed_beforeunload(session, inline): > url_beforeunload = inline(""" > <input type="text"> > <script> >@@ -79,7 +78,7 @@ def test_dismissed_beforeunload(session): > session.find.css("input", all=False) > > >-def test_history_pushstate(session, url): >+def test_history_pushstate(session, inline): > pushstate_page = inline(""" > <script> > function pushState() { >@@ -111,7 +110,7 @@ def test_history_pushstate(session, url): > element.property("id") > > >-def test_refresh_switches_to_parent_browsing_context(session, create_frame): >+def test_refresh_switches_to_parent_browsing_context(session, create_frame, inline): > session.url = inline("<div id=foo>") > > session.switch_frame(create_frame()) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/refresh/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/refresh/user_prompts.py >index b8ef418e115f..7e944b53b096 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/refresh/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/refresh/user_prompts.py >@@ -4,7 +4,6 @@ import pytest > > from webdriver.error import StaleElementReferenceException > >-from tests.support.inline import inline > from tests.support.asserts import assert_dialog_handled, assert_error, assert_success > > >@@ -14,7 +13,7 @@ def refresh(session): > > > @pytest.fixture >-def check_user_prompt_closed_without_exception(session, create_dialog): >+def check_user_prompt_closed_without_exception(session, create_dialog, inline): > def check_user_prompt_closed_without_exception(dialog_type, retval): > session.url = inline("<div id=foo>") > element = session.find.css("#foo", all=False) >@@ -33,7 +32,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_closed_with_exception(session, create_dialog): >+def check_user_prompt_closed_with_exception(session, create_dialog, inline): > def check_user_prompt_closed_with_exception(dialog_type, retval): > session.url = inline("<div id=foo>") > element = session.find.css("#foo", all=False) >@@ -51,7 +50,7 @@ def check_user_prompt_closed_with_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_not_closed_but_exception(session, create_dialog): >+def check_user_prompt_not_closed_but_exception(session, create_dialog, inline): > def check_user_prompt_not_closed_but_exception(dialog_type): > session.url = inline("<div id=foo>") > element = session.find.css("#foo", all=False) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/release_actions/release.py b/WebDriverTests/imported/w3c/webdriver/tests/release_actions/release.py >index 1f486f7759fd..5df1ff4be902 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/release_actions/release.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/release_actions/release.py >@@ -20,4 +20,4 @@ def test_no_top_browsing_context(session, closed_window): > > def test_no_browsing_context(session, closed_frame): > response = release_actions(session) >- assert_success(response) >+ assert_error(response, "no such window") >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/release_actions/sequence.py b/WebDriverTests/imported/w3c/webdriver/tests/release_actions/sequence.py >index 2d816f8094b3..75143d85ca60 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/release_actions/sequence.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/release_actions/sequence.py >@@ -1,6 +1,7 @@ > # META: timeout=long > >-from tests.release_actions.support.refine import filter_dict, get_events, get_keys >+from tests.release_actions.support.refine import get_events, get_keys >+from tests.support.helpers import filter_dict > > > def test_release_no_actions_sends_no_events(session, key_reporter): >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/release_actions/support/refine.py b/WebDriverTests/imported/w3c/webdriver/tests/release_actions/support/refine.py >index 3a6d63e04ce9..90f722587c3b 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/release_actions/support/refine.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/release_actions/support/refine.py >@@ -22,12 +22,3 @@ def get_keys(input_el): > return "" > else: > return rv >- >- >-def filter_dict(source, d): >- """Filter `source` dict to only contain same keys as `d` dict. >- >- :param source: dictionary to filter. >- :param d: dictionary whose keys determine the filtering. >- """ >- return {k: source[k] for k in d.keys()} >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/send_alert_text/send.py b/WebDriverTests/imported/w3c/webdriver/tests/send_alert_text/send.py >index 5da67590da34..d5846ad56b3b 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/send_alert_text/send.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/send_alert_text/send.py >@@ -4,12 +4,11 @@ from webdriver.error import NoSuchAlertException > from webdriver.transport import Response > > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline > from tests.support.sync import Poll > > > @pytest.fixture >-def page(session): >+def page(session, inline): > session.url = inline(""" > <script>window.result = window.prompt('Enter Your Name: ', 'Name');</script> > """) >@@ -55,7 +54,7 @@ def test_no_user_prompt(session): > > > @pytest.mark.parametrize("dialog_type", ["alert", "confirm"]) >-def test_alert_element_not_interactable(session, dialog_type): >+def test_alert_element_not_interactable(session, inline, dialog_type): > session.url = inline("<script>window.{}('Hello');</script>".format(dialog_type)) > > response = send_alert_text(session, "Federer") >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/status/status.py b/WebDriverTests/imported/w3c/webdriver/tests/status/status.py >index 1f0f1bc4fe77..8c7ae22a67b5 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/status/status.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/status/status.py >@@ -1,7 +1,5 @@ > import json > >-from six import text_type >- > from tests.support.asserts import assert_success > > >@@ -18,7 +16,7 @@ def test_get_status_no_session(http): > value = parsed_obj["value"] > > assert value["ready"] in [True, False] >- assert isinstance(value["message"], text_type) >+ assert isinstance(value["message"], str) > > > def test_status_with_session_running_on_endpoint_node(session): >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/support/asserts.py b/WebDriverTests/imported/w3c/webdriver/tests/support/asserts.py >index 3c3d677c90ec..e60d53b6c6f0 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/support/asserts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/support/asserts.py >@@ -1,8 +1,6 @@ >-import base64 > import imghdr > import struct >- >-from six import ensure_binary, text_type >+from base64 import decodebytes > > from webdriver import Element, NoSuchAlertException, WebDriverException > >@@ -52,8 +50,8 @@ def assert_error(response, error_code): > assert response.status == errors[error_code] > assert "value" in response.body > assert response.body["value"]["error"] == error_code >- assert isinstance(response.body["value"]["message"], text_type) >- assert isinstance(response.body["value"]["stacktrace"], text_type) >+ assert isinstance(response.body["value"]["message"], str) >+ assert isinstance(response.body["value"]["stacktrace"], str) > assert_response_headers(response.headers) > > >@@ -214,6 +212,6 @@ def assert_move_to_coordinates(point, target, events): > > def assert_png(screenshot): > """Test that screenshot is a Base64 encoded PNG file.""" >- image = base64.decodestring(ensure_binary(screenshot)) >+ image = decodebytes(screenshot.encode()) > mime_type = imghdr.what("", image) > assert mime_type == "png", "Expected image to be PNG, but it was {}".format(mime_type) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/support/authentication.py b/WebDriverTests/imported/w3c/webdriver/tests/support/authentication.py >index 25a8eb95d114..9da4bb7be5ff 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/support/authentication.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/support/authentication.py >@@ -1,15 +1,12 @@ >-from six.moves.urllib.parse import urlencode >+from urllib.parse import urlencode > > >-def basic_authentication(username=None, password=None, protocol="http"): >- from .fixtures import server_config, url >- build_url = url(server_config()) >- >+def basic_authentication(url, username=None, password=None, protocol="http"): > query = {} > >- return build_url("/webdriver/tests/support/authentication.py", >- query=urlencode(query), >- protocol=protocol) >+ return url("/webdriver/tests/support/authentication.py", >+ query=urlencode(query), >+ protocol=protocol) > > > def main(request, response): >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/support/fixtures.py b/WebDriverTests/imported/w3c/webdriver/tests/support/fixtures.py >index 957c4d05868b..db32931131d6 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/support/fixtures.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/support/fixtures.py >@@ -2,15 +2,15 @@ import copy > import json > import os > >+import asyncio > import pytest > import webdriver > >-from six import string_types >- >-from six.moves.urllib.parse import urlunsplit >+from urllib.parse import urlunsplit > > from tests.support import defaults >-from tests.support.helpers import cleanup_session >+from tests.support.helpers import cleanup_session, deep_update >+from tests.support.inline import build_inline > from tests.support.http_request import HTTPRequest > from tests.support.sync import Poll > >@@ -38,6 +38,17 @@ def pytest_generate_tests(metafunc): > metafunc.parametrize("capabilities", marker.args, ids=None) > > >+# Ensure that the event loop is restarted once per session rather than the default of once per test >+# if we don't do this, tests will try to reuse a closed event loop and fail with an error that the "future >+# belongs to a different loop" >+@pytest.fixture(scope="session") >+def event_loop(): >+ """Change event_loop fixture to session level.""" >+ loop = asyncio.get_event_loop_policy().new_event_loop() >+ yield loop >+ loop.close() >+ >+ > @pytest.fixture > def add_event_listeners(session): > """Register listeners for tracked events on element.""" >@@ -94,7 +105,8 @@ def http(configuration): > > @pytest.fixture > def server_config(): >- return json.loads(os.environ.get("WD_SERVER_CONFIG")) >+ with open(os.environ.get("WD_SERVER_CONFIG_FILE"), "r") as f: >+ return json.load(f) > > > @pytest.fixture(scope="session") >@@ -110,8 +122,23 @@ def configuration(): > } > > >+async def reset_current_session_if_necessary(caps, request_bidi): >+ global _current_session >+ >+ # If there is a session with different capabilities active or the current session >+ # is of different type than the one we would like to create, end it now. >+ if _current_session is not None: >+ is_bidi = isinstance(_current_session, webdriver.BidiSession) >+ if is_bidi != request_bidi or not _current_session.match(caps): >+ if is_bidi: >+ await _current_session.end() >+ else: >+ _current_session.end() >+ _current_session = None >+ >+ > @pytest.fixture(scope="function") >-def session(capabilities, configuration, request): >+async def session(capabilities, configuration, request): > """Create and start a session for a test that does not itself test session creation. > > By default the session will stay open after each test, but we always try to start a >@@ -123,14 +150,10 @@ def session(capabilities, configuration, request): > # Update configuration capabilities with custom ones from the > # capabilities fixture, which can be set by tests > caps = copy.deepcopy(configuration["capabilities"]) >- caps.update(capabilities) >+ deep_update(caps, capabilities) > caps = {"alwaysMatch": caps} > >- # If there is a session with different capabilities active, end it now >- if _current_session is not None and ( >- caps != _current_session.requested_capabilities): >- _current_session.end() >- _current_session = None >+ await reset_current_session_if_necessary(caps, False) > > if _current_session is None: > _current_session = webdriver.Session( >@@ -139,6 +162,7 @@ def session(capabilities, configuration, request): > capabilities=caps) > try: > _current_session.start() >+ > except webdriver.error.SessionNotCreatedException: > if not _current_session.session_id: > raise >@@ -152,6 +176,48 @@ def session(capabilities, configuration, request): > cleanup_session(_current_session) > > >+@pytest.fixture(scope="function") >+async def bidi_session(capabilities, configuration, request): >+ """Create and start a bidi session for a test that does not itself test >+ bidi session creation. >+ By default the session will stay open after each test, but we always try to start a >+ new one and assume that if that fails there is already a valid session. This makes it >+ possible to recover from some errors that might leave the session in a bad state, but >+ does not demand that we start a new session per test.""" >+ global _current_session >+ >+ # Update configuration capabilities with custom ones from the >+ # capabilities fixture, which can be set by tests >+ caps = copy.deepcopy(configuration["capabilities"]) >+ deep_update(caps, capabilities) >+ caps = {"alwaysMatch": caps} >+ >+ await reset_current_session_if_necessary(caps, True) >+ >+ if _current_session is None: >+ _current_session = webdriver.Session( >+ configuration["host"], >+ configuration["port"], >+ capabilities=caps, >+ enable_bidi=True) >+ >+ try: >+ _current_session.start() >+ await _current_session.bidi_session.start() >+ except webdriver.error.SessionNotCreatedException: >+ if not _current_session.session_id: >+ raise >+ >+ # Enforce a fixed default window size and position >+ _current_session.window.size = defaults.WINDOW_SIZE >+ _current_session.window.position = defaults.WINDOW_POSITION >+ >+ yield _current_session.bidi_session >+ >+ await _current_session.bidi_session.end() >+ cleanup_session(_current_session) >+ >+ > @pytest.fixture(scope="function") > def current_session(): > return _current_session >@@ -159,14 +225,13 @@ def current_session(): > > @pytest.fixture > def url(server_config): >- def inner(path, protocol="http", domain="", subdomain="", query="", fragment=""): >+ def url(path, protocol="http", domain="", subdomain="", query="", fragment=""): > domain = server_config["domains"][domain][subdomain] > port = server_config["ports"][protocol][0] > host = "{0}:{1}".format(domain, port) > return urlunsplit((protocol, host, path, query, fragment)) > >- inner.__name__ = "url" >- return inner >+ return url > > > @pytest.fixture >@@ -182,7 +247,7 @@ def create_dialog(session): > if text is None: > text = "" > >- assert isinstance(text, string_types), "`text` parameter must be a string" >+ assert isinstance(text, str), "`text` parameter must be a string" > > # Script completes itself when the user prompt has been opened. > # For prompt() dialogs, add a value for the 'default' argument, >@@ -238,15 +303,54 @@ def closed_frame(session, url): > > > @pytest.fixture >-def closed_window(session): >+def closed_window(session, inline): > original_handle = session.window_handle > new_handle = session.new_window() > > session.window_handle = new_handle >+ session.url = inline("<input id='a' value='b'>") >+ element = session.find.css("input", all=False) > > session.window.close() > assert new_handle not in session.handles, "Unable to close window {}".format(new_handle) > >- yield new_handle >+ yield (original_handle, element) > > session.window_handle = original_handle >+ >+ >+@pytest.fixture >+def inline(url): >+ """Takes a source extract and produces well-formed documents. >+ >+ Based on the desired document type, the extract is embedded with >+ predefined boilerplate in order to produce well-formed documents. >+ The media type and character set may also be individually configured. >+ >+ This helper function originally used data URLs, but since these >+ are not universally supported (or indeed standardised!) across >+ browsers, it now delegates the serving of the document to wptserve. >+ This file also acts as a wptserve handler (see the main function >+ below) which configures the HTTP response using query parameters. >+ >+ This function returns a URL to the wptserve handler, which in turn >+ will serve an HTTP response with the requested source extract >+ inlined in a well-formed document, and the Content-Type header >+ optionally configured using the desired media type and character set. >+ >+ Any additional keyword arguments are passed on to the build_url >+ function, which comes from the url fixture. >+ """ >+ def inline(src, **kwargs): >+ return build_inline(url, src, **kwargs) >+ >+ return inline >+ >+ >+@pytest.fixture >+def iframe(inline): >+ """Inlines document extract as the source document of an <iframe>.""" >+ def iframe(src, **kwargs): >+ return "<iframe src='{}'></iframe>".format(inline(src, **kwargs)) >+ >+ return iframe >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/support/helpers.py b/WebDriverTests/imported/w3c/webdriver/tests/support/helpers.py >index f857083b4774..43bdb41cf983 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/support/helpers.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/support/helpers.py >@@ -1,5 +1,6 @@ > from __future__ import print_function > >+import collections > import math > import sys > >@@ -106,6 +107,20 @@ def clear_all_cookies(session): > session.transport.send("DELETE", "session/%s/cookie" % session.session_id) > > >+def deep_update(source, overrides): >+ """ >+ Update a nested dictionary or similar mapping. >+ Modify ``source`` in place. >+ """ >+ for key, value in overrides.items(): >+ if isinstance(value, collections.Mapping) and value: >+ returned = deep_update(source.get(key, {}), value) >+ source[key] = returned >+ else: >+ source[key] = overrides[key] >+ return source >+ >+ > def document_dimensions(session): > return tuple(session.execute_script(""" > let rect = document.documentElement.getBoundingClientRect(); >@@ -221,3 +236,27 @@ def available_screen_size(session): > screen.availHeight - screen.availTop, > ]; > """)) >+ >+def filter_dict(source, d): >+ """Filter `source` dict to only contain same keys as `d` dict. >+ >+ :param source: dictionary to filter. >+ :param d: dictionary whose keys determine the filtering. >+ """ >+ return {k: source[k] for k in d.keys()} >+ >+ >+def wait_for_new_handle(session, handles_before): >+ def find_new_handle(session): >+ new_handles = list(set(session.handles) - set(handles_before)) >+ if new_handles and len(new_handles) == 1: >+ return new_handles[0] >+ return None >+ >+ wait = Poll( >+ session, >+ timeout=5, >+ message="No new window has been opened") >+ >+ return wait.until(find_new_handle) >+ >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/support/html/frames_no_bfcache.html b/WebDriverTests/imported/w3c/webdriver/tests/support/html/frames_no_bfcache.html >new file mode 100644 >index 000000000000..1972187d21fe >--- /dev/null >+++ b/WebDriverTests/imported/w3c/webdriver/tests/support/html/frames_no_bfcache.html >@@ -0,0 +1,18 @@ >+<html> >+<head> >+ <script type="text/javascript"> >+ function remove() { >+ const frame = document.getElementById("sub-frame"); >+ const div = document.getElementById("delete"); >+ div.removeChild(frame); >+ } >+ </script> >+</head> >+ >+<!-- unload handler prevents the page from being added to the bfcache on navigation --> >+<body onunload=""> >+ <div id="delete"> >+ <iframe src="subframe.html" id="sub-frame"></iframe> >+ </div> >+</body> >+</html> >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/support/html/meta-utf8-after-1024-bytes.html b/WebDriverTests/imported/w3c/webdriver/tests/support/html/meta-utf8-after-1024-bytes.html >new file mode 100644 >index 000000000000..b5916148b558 >--- /dev/null >+++ b/WebDriverTests/imported/w3c/webdriver/tests/support/html/meta-utf8-after-1024-bytes.html >@@ -0,0 +1,17 @@ >+<!DOCTYPE html> >+<!-- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla eu iaculis >+lectus. Quisque ullamcorper est at nunc consectetur suscipit. Aliquam imperdiet >+mauris in nulla ornare, id eleifend turpis placerat. Vestibulum lorem libero, >+sollicitudin in orci suscipit, dictum vestibulum nulla. Ut ac est tincidunt, >+cursus leo vel, pellentesque orci. Sed mattis metus augue, ac tincidunt nunc >+lobortis in. Proin eu ipsum auctor lorem sagittis malesuada. Vivamus maximus, >+eros fringilla vulputate tincidunt, tellus tellus viverra augue, sed iaculis >+ipsum lacus quis tellus. Morbi et enim at ante molestie imperdiet et et nulla. >+Aliquam consequat rhoncus magna, vitae sodales urna maximus eget. Mauris eu >+laoreet turpis, eget condimentum lectus. Maecenas vel lorem vel nulla efficitur >+euismod. Sed lobortis enim ac odio bibendum, id vehicula nibh tempus. Phasellus >+sodales, ipsum feugiat aliquam vehicula, diam leo cursus est, nec varius nunc >+felis vitae est. Curabitur ac purus nisl. Mauris condimentum, magna quis >+consectetur biam. --> >+<meta charset="utf-8"> >+<div id="body"></div> >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/support/http_request.py b/WebDriverTests/imported/w3c/webdriver/tests/support/http_request.py >index 895d1319ac0a..242dc9c91887 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/support/http_request.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/support/http_request.py >@@ -1,9 +1,7 @@ > import contextlib > import json > >-from six import text_type >- >-from six.moves.http_client import HTTPConnection >+from http.client import HTTPConnection > > > class HTTPRequest(object): >@@ -31,7 +29,7 @@ class HTTPRequest(object): > raise ValueError("Failed to encode request body as JSON: {}".format( > json.dumps(body, indent=2))) > >- if isinstance(payload, text_type): >+ if isinstance(payload, str): > payload = body.encode("utf-8") > > conn = HTTPConnection(self.host, self.port) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/support/image.py b/WebDriverTests/imported/w3c/webdriver/tests/support/image.py >index 6149ccccbe32..2e7ad8c1309b 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/support/image.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/support/image.py >@@ -1,14 +1,12 @@ >-import base64 > import math > import struct >- >-from six import ensure_binary >+from base64 import decodebytes > > from tests.support.asserts import assert_png > > > def png_dimensions(screenshot): > assert_png(screenshot) >- image = base64.decodestring(ensure_binary(screenshot)) >+ image = decodebytes(screenshot.encode()) > width, height = struct.unpack(">LL", image[16:24]) > return int(width), int(height) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/support/inline.py b/WebDriverTests/imported/w3c/webdriver/tests/support/inline.py >index d23ca75d1c80..494ca74f9265 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/support/inline.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/support/inline.py >@@ -1,6 +1,6 @@ > """Helpers for inlining extracts of documents in tests.""" > >-from six.moves.urllib.parse import urlencode >+from urllib.parse import urlencode > > > BOILERPLATES = { >@@ -24,31 +24,8 @@ MIME_TYPES = { > "xml": "text/xml", > } > >-def inline(src, doctype="html", mime=None, charset=None, **kwargs): >- """ >- Takes a source extract and produces well-formed documents. >- >- Based on the desired document type, the extract is embedded with >- predefined boilerplate in order to produce well-formed documents. >- The media type and character set may also be individually configured. >- >- This helper function originally used data URLs, but since these >- are not universally supported (or indeed standardised!) across >- browsers, it now delegates the serving of the document to wptserve. >- This file also acts as a wptserve handler (see the main function >- below) which configures the HTTP response using query parameters. >- >- This function returns a URL to the wptserve handler, which in turn >- will serve an HTTP response with the requested source extract >- inlined in a well-formed document, and the Content-Type header >- optionally configured using the desired media type and character set. >- >- Any additional keyword arguments are passed on to the build_url >- function. >- """ >- from .fixtures import server_config, url >- build_url = url(server_config()) > >+def build_inline(build_url, src, doctype="html", mime=None, charset=None, **kwargs): > if mime is None: > mime = MIME_TYPES[doctype] > if charset is None: >@@ -62,11 +39,6 @@ def inline(src, doctype="html", mime=None, charset=None, **kwargs): > **kwargs) > > >-def iframe(src, **kwargs): >- """Inlines document extract as the source document of an <iframe>.""" >- return "<iframe src='{}'></iframe>".format(inline(src, **kwargs)) >- >- > def main(request, response): > doc = request.GET.first(b"doc", None) > mime = request.GET.first(b"mime", None) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/support/merge_dictionaries.py b/WebDriverTests/imported/w3c/webdriver/tests/support/merge_dictionaries.py >index cf06c9b433ce..72f1cab352de 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/support/merge_dictionaries.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/support/merge_dictionaries.py >@@ -1,10 +1,3 @@ >-def iteritems(d): >- """Create a key-value iterator for the given dict in both Python 2.x and >- Python 3.x environments""" >- if hasattr(d, "iteritems"): >- return d.iteritems() >- return d.items() >- > def merge_dictionaries(first, second): > """Given two dictionaries, create a third that defines all specified > key/value pairs. This merge_dictionaries is performed "deeply" on any nested >@@ -12,7 +5,7 @@ def merge_dictionaries(first, second): > an exception will be raised.""" > result = dict(first) > >- for key, value in iteritems(second): >+ for key, value in second.items(): > if key in result and result[key] != value: > if isinstance(result[key], dict) and isinstance(value, dict): > result[key] = merge_dictionaries(result[key], value) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/support/sync.py b/WebDriverTests/imported/w3c/webdriver/tests/support/sync.py >index fa5d18550cce..30ff872b6e15 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/support/sync.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/support/sync.py >@@ -2,8 +2,6 @@ import collections > import sys > import time > >-import six >- > from webdriver import error > > >@@ -140,6 +138,6 @@ class Poll(object): > message = "Timed out after {} seconds".format(elapsed) > if self.exc_msg is not None: > message = "{} with message: {}".format(message, self.exc_msg) >- six.reraise(self.exc_cls, self.exc_cls(message=message), tb) >+ raise self.exc_cls(message=message).with_traceback(tb) > else: > return rv >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/switch_to_frame/cross_origin.py b/WebDriverTests/imported/w3c/webdriver/tests/switch_to_frame/cross_origin.py >index 905f1eb56dce..633eba3f4248 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/switch_to_frame/cross_origin.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/switch_to_frame/cross_origin.py >@@ -1,10 +1,9 @@ >-from six.moves.urllib.parse import urlparse >+from urllib.parse import urlparse > > import webdriver.protocol as protocol > > from tests.support.asserts import assert_success > from tests.support.helpers import document_location >-from tests.support.inline import iframe, inline > > > """ >@@ -27,7 +26,7 @@ def switch_to_frame(session, frame): > session=session) > > >-def test_cross_origin_iframe(session, server_config): >+def test_cross_origin_iframe(session, server_config, inline, iframe): > session.url = inline(iframe("", domain="alt")) > frame_element = session.find.css("iframe", all=False) > >@@ -38,7 +37,7 @@ def test_cross_origin_iframe(session, server_config): > assert parse_result.netloc != server_config["browser_host"] > > >-def test_nested_cross_origin_iframe(session, server_config): >+def test_nested_cross_origin_iframe(session, server_config, inline, iframe): > frame2 = iframe("", domain="alt", subdomain="www") > frame1 = iframe(frame2) > top_doc = inline(frame1, domain="alt") >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/switch_to_frame/switch.py b/WebDriverTests/imported/w3c/webdriver/tests/switch_to_frame/switch.py >index 705fa79a5de0..9ccab2c6c96d 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/switch_to_frame/switch.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/switch_to_frame/switch.py >@@ -2,11 +2,10 @@ import pytest > > import webdriver.protocol as protocol > >-from webdriver import StaleElementReferenceException >+from webdriver import NoSuchElementException > from webdriver.transport import Response > > from tests.support.asserts import assert_error, assert_same_element, assert_success >-from tests.support.inline import inline, iframe > > > def switch_to_frame(session, frame): >@@ -17,18 +16,13 @@ def switch_to_frame(session, frame): > session=session) > > >-def frameset(*docs): >- frames = list(map(lambda doc: "<frame src='{}'></frame>".format(inline(doc)), docs)) >- return "<frameset rows='{}'>\n{}</frameset>".format(len(frames) * "*,", "\n".join(frames)) >- >- > def test_null_parameter_value(session, http): > path = "/session/{session_id}/frame".format(**vars(session)) > with http.post(path, None) as response: > assert_error(Response.from_http(response), "invalid argument") > > >-def test_null_response_value(session): >+def test_null_response_value(session, inline, iframe): > session.url = inline(iframe("<p>foo")) > frame = session.find.css("iframe", all=False) > >@@ -42,7 +36,16 @@ def test_null_response_value(session): > 0, > {"element-6066-11e4-a52e-4f735466cecf": "foo"}, > ]) >-def test_no_top_browsing_context(session, closed_window, id): >+def test_no_top_browsing_context(session, url, id): >+ session.window_handle = session.new_window() >+ >+ session.url = url("/webdriver/tests/support/html/frames.html") >+ >+ subframe = session.find.css("#sub-frame", all=False) >+ session.switch_frame(subframe) >+ >+ session.window.close() >+ > response = switch_to_frame(session, id) > assert_error(response, "no such window") > >@@ -61,13 +64,18 @@ def test_no_browsing_context(session, closed_frame, id): > assert_error(response, "no such window") > > >+def test_no_browsing_context_when_already_top_level(session, closed_window): >+ response = switch_to_frame(session, None) >+ assert_error(response, "no such window") >+ >+ > @pytest.mark.parametrize("value", ["foo", True, [], {}]) > def test_frame_id_invalid_types(session, value): > response = switch_to_frame(session, value) > assert_error(response, "invalid argument") > > >-def test_frame_id_null(session): >+def test_frame_id_null(session, inline, iframe): > session.url = inline(iframe("{}<div>foo".format(iframe("<p>bar")))) > > frame1 = session.find.css("iframe", all=False) >@@ -82,10 +90,27 @@ def test_frame_id_null(session): > response = switch_to_frame(session, None) > assert_success(response) > >- with pytest.raises(StaleElementReferenceException): >+ with pytest.raises(NoSuchElementException): > element2.text >- with pytest.raises(StaleElementReferenceException): >+ with pytest.raises(NoSuchElementException): > element1.text > > frame = session.find.css("iframe", all=False) > assert_same_element(session, frame, frame1) >+ >+ >+def test_find_element_while_frame_is_still_loading(session, url): >+ session.timeouts.implicit = 5 >+ >+ frame_url = url("/webdriver/tests/support/html/subframe.html?pipe=trickle(d2)") >+ page_url = "<html><body><iframe src='{}'></iframe></body></html>".format(frame_url) >+ >+ session.execute_script( >+ "document.documentElement.innerHTML = arguments[0];", args=[page_url]) >+ >+ frame1 = session.find.css("iframe", all=False) >+ session.switch_frame(frame1) >+ >+ # Ensure that the is always a valid browsing context, and the element >+ # can be found eventually. >+ session.find.css("#delete", all=False) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/switch_to_frame/switch_number.py b/WebDriverTests/imported/w3c/webdriver/tests/switch_to_frame/switch_number.py >index 77529544679a..c8858e77ff10 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/switch_to_frame/switch_number.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/switch_to_frame/switch_number.py >@@ -3,7 +3,6 @@ import pytest > import webdriver.protocol as protocol > > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline, iframe > > > def switch_to_frame(session, frame): >@@ -21,7 +20,7 @@ def test_frame_id_number_out_of_bounds(session, value): > > > @pytest.mark.parametrize("index", [1, 65535]) >-def test_frame_id_number_index_out_of_bounds(session, index): >+def test_frame_id_number_index_out_of_bounds(session, inline, iframe, index): > session.url = inline(iframe("<p>foo")) > > response = switch_to_frame(session, index) >@@ -29,7 +28,7 @@ def test_frame_id_number_index_out_of_bounds(session, index): > > > @pytest.mark.parametrize("index, value", [[0, "foo"], [1, "bar"]]) >-def test_frame_id_number_index(session, index, value): >+def test_frame_id_number_index(session, inline, iframe, index, value): > session.url = inline("{}{}".format(iframe("<p>foo"), iframe("<p>bar"))) > > response = switch_to_frame(session, index) >@@ -39,7 +38,7 @@ def test_frame_id_number_index(session, index, value): > assert element.text == value > > >-def test_frame_id_number_index_nested(session): >+def test_frame_id_number_index_nested(session, inline, iframe): > session.url = inline(iframe("{}<p>foo".format(iframe("<p>bar")))) > > expected_text = ["foo", "bar"] >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/switch_to_frame/switch_webelement.py b/WebDriverTests/imported/w3c/webdriver/tests/switch_to_frame/switch_webelement.py >index 89480b66b3a6..a30d2d8d339c 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/switch_to_frame/switch_webelement.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/switch_to_frame/switch_webelement.py >@@ -3,7 +3,6 @@ import pytest > import webdriver.protocol as protocol > > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline, iframe > > > def switch_to_frame(session, frame): >@@ -14,14 +13,14 @@ def switch_to_frame(session, frame): > session=session) > > >-def frameset(*docs): >+def frameset(inline, *docs): > frames = list(map(lambda doc: "<frame src='{}'></frame>".format(inline(doc)), docs)) > return "<frameset rows='{}'>\n{}</frameset>".format(len(frames) * "*,", "\n".join(frames)) > > > @pytest.mark.parametrize("index, value", [[0, "foo"], [1, "bar"]]) >-def test_frame_id_webelement_frame(session, index, value): >- session.url = inline(frameset("<p>foo", "<p>bar")) >+def test_frame_id_webelement_frame(session, inline, index, value): >+ session.url = inline(frameset(inline, "<p>foo", "<p>bar")) > frames = session.find.css("frame") > assert len(frames) == 2 > >@@ -33,7 +32,7 @@ def test_frame_id_webelement_frame(session, index, value): > > > @pytest.mark.parametrize("index, value", [[0, "foo"], [1, "bar"]]) >-def test_frame_id_webelement_iframe(session, index, value): >+def test_frame_id_webelement_iframe(session, inline, iframe, index, value): > session.url = inline("{}{}".format(iframe("<p>foo"), iframe("<p>bar"))) > frames = session.find.css("iframe") > assert len(frames) == 2 >@@ -45,7 +44,7 @@ def test_frame_id_webelement_iframe(session, index, value): > assert element.text == value > > >-def test_frame_id_webelement_nested(session): >+def test_frame_id_webelement_nested(session, inline, iframe): > session.url = inline(iframe("{}<p>foo".format(iframe("<p>bar")))) > > expected_text = ["foo", "bar"] >@@ -58,7 +57,7 @@ def test_frame_id_webelement_nested(session): > assert element.text == expected_text[i] > > >-def test_frame_id_webelement_no_element_reference(session): >+def test_frame_id_webelement_no_element_reference(session, inline, iframe): > session.url = inline(iframe("<p>foo")) > frame = session.find.css("iframe", all=False) > frame.id = "bar" >@@ -67,7 +66,7 @@ def test_frame_id_webelement_no_element_reference(session): > assert_error(response, "no such element") > > >-def test_frame_id_webelement_stale_reference(session): >+def test_frame_id_webelement_stale_reference(session, inline, iframe): > session.url = inline(iframe("<p>foo")) > frame = session.find.css("iframe", all=False) > >@@ -77,9 +76,27 @@ def test_frame_id_webelement_stale_reference(session): > assert_error(response, "stale element reference") > > >-def test_frame_id_webelement_no_frame_element(session): >+def test_frame_id_webelement_no_frame_element(session, inline): > session.url = inline("<p>foo") > no_frame = session.find.css("p", all=False) > > response = switch_to_frame(session, no_frame) > assert_error(response, "no such frame") >+ >+ >+def test_frame_id_webelement_cloned_into_iframe(session, inline, iframe): >+ session.url = inline(iframe("<body><p>hello world</p></body>")) >+ >+ session.execute_script(""" >+ const iframe = document.getElementsByTagName('iframe')[0]; >+ const div = document.createElement('div'); >+ div.innerHTML = 'I am a div created in top window and appended into the iframe'; >+ iframe.contentWindow.document.body.appendChild(div); >+ """) >+ >+ frame = session.find.css("iframe", all=False) >+ response = switch_to_frame(session, frame) >+ assert_success(response) >+ >+ element = session.find.css("div", all=False) >+ assert element.text == "I am a div created in top window and appended into the iframe" >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/switch_to_parent_frame/switch.py b/WebDriverTests/imported/w3c/webdriver/tests/switch_to_parent_frame/switch.py >index ef2da45f1625..9c6db8d2cd61 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/switch_to_parent_frame/switch.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/switch_to_parent_frame/switch.py >@@ -1,9 +1,8 @@ > import pytest > >-from webdriver import StaleElementReferenceException >+from webdriver import NoSuchElementException > > from tests.support.asserts import assert_error, assert_success >-from tests.support.inline import inline, iframe > > > def switch_to_parent_frame(session): >@@ -11,7 +10,7 @@ def switch_to_parent_frame(session): > "POST", "session/{session_id}/frame/parent".format(**vars(session))) > > >-def test_null_response_value(session): >+def test_null_response_value(session, inline, iframe): > session.url = inline(iframe("<p>foo")) > frame_element = session.find.css("iframe", all=False) > session.switch_frame(frame_element) >@@ -21,7 +20,16 @@ def test_null_response_value(session): > assert value is None > > >-def test_no_top_browsing_context(session, closed_window): >+def test_no_top_browsing_context(session, url): >+ session.window_handle = session.new_window() >+ >+ session.url = url("/webdriver/tests/support/html/frames.html") >+ >+ subframe = session.find.css("#sub-frame", all=False) >+ session.switch_frame(subframe) >+ >+ session.window.close() >+ > response = switch_to_parent_frame(session) > assert_error(response, "no such window") > >@@ -49,19 +57,29 @@ def test_no_browsing_context(session, closed_frame): > session.find.css("#delete", all=False) > > >-def test_stale_element_from_iframe(session): >+def test_no_browsing_context_when_already_top_level(session, closed_window): >+ response = switch_to_parent_frame(session) >+ assert_error(response, "no such window") >+ >+ >+def test_switch_from_iframe(session, inline, iframe): > session.url = inline(iframe("<p>foo")) > frame_element = session.find.css("iframe", all=False) > session.switch_frame(frame_element) >- stale_element = session.find.css("p", all=False) >+ element = session.find.css("p", all=False) > > result = switch_to_parent_frame(session) > assert_success(result) > >- with pytest.raises(StaleElementReferenceException): >- stale_element.text >+ with pytest.raises(NoSuchElementException): >+ element.text >+ > >+def test_switch_from_top_level(session, inline): >+ session.url = inline("<p>foo") >+ element = session.find.css("p", all=False) > >-def test_switch_from_top_level(session): > result = switch_to_parent_frame(session) > assert_success(result) >+ >+ assert element.text == "foo" >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/switch_to_window/switch.py b/WebDriverTests/imported/w3c/webdriver/tests/switch_to_window/switch.py >index 98b4bee16e2d..28d432a8b5b4 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/switch_to_window/switch.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/switch_to_window/switch.py >@@ -1,3 +1,6 @@ >+import pytest >+ >+from webdriver.error import NoSuchElementException, NoSuchAlertException > from webdriver.transport import Response > > from tests.support.asserts import assert_error, assert_success >@@ -51,3 +54,47 @@ def test_no_browsing_context(session, url): > assert_success(response) > > assert session.window_handle == new_handle >+ >+ >+def test_switch_to_window_sets_top_level_context(session, inline, iframe): >+ session.url = inline(iframe("<p>foo")) >+ >+ frame = session.find.css("iframe", all=False) >+ session.switch_frame(frame) >+ session.find.css("p", all=False) >+ >+ response = switch_to_window(session, session.window_handle) >+ assert_success(response) >+ >+ session.find.css("iframe", all=False) >+ >+ >+def test_element_not_found_after_tab_switch(session, inline): >+ session.url = inline("<p id='a'>foo") >+ paragraph = session.find.css("p", all=False) >+ >+ session.window_handle = session.new_window(type_hint="tab") >+ >+ with pytest.raises(NoSuchElementException): >+ paragraph.attribute("id") >+ >+ >+@pytest.mark.parametrize("dialog_type", ["alert", "confirm", "prompt"]) >+def test_finds_exising_user_prompt_after_tab_switch(session, dialog_type): >+ original_handle = session.window_handle >+ new_handle = session.new_window() >+ >+ session.execute_script("{}('foo');".format(dialog_type)) >+ >+ response = switch_to_window(session, new_handle) >+ assert_success(response) >+ >+ with pytest.raises(NoSuchAlertException): >+ session.alert.text >+ >+ session.window.close() >+ >+ response = switch_to_window(session, original_handle) >+ assert_success(response) >+ >+ session.alert.accept() >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/take_element_screenshot/iframe.py b/WebDriverTests/imported/w3c/webdriver/tests/take_element_screenshot/iframe.py >index 83f55deff753..e7f1b0c8053d 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/take_element_screenshot/iframe.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/take_element_screenshot/iframe.py >@@ -2,7 +2,6 @@ import pytest > > from tests.support.asserts import assert_success > from tests.support.image import png_dimensions >-from tests.support.inline import iframe, inline > > from . import element_dimensions > >@@ -68,7 +67,7 @@ def take_element_screenshot(session, element_id): > ) > > >-def test_frame_element(session): >+def test_frame_element(session, inline, iframe): > # Create a reference element which looks exactly like the frame's content > session.url = inline("{0}{1}".format(REFERENCE_STYLE, REFERENCE_CONTENT)) > >@@ -97,7 +96,7 @@ def test_frame_element(session): > > > @pytest.mark.parametrize("domain", ["", "alt"], ids=["same_origin", "cross_origin"]) >-def test_source_origin(session, url, domain): >+def test_source_origin(session, inline, iframe, domain): > # Create a reference element which looks exactly like the iframe > session.url = inline("{0}{1}".format(REFERENCE_STYLE, REFERENCE_CONTENT)) > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/take_element_screenshot/screenshot.py b/WebDriverTests/imported/w3c/webdriver/tests/take_element_screenshot/screenshot.py >index 6ff6396edb2a..4d3bfb0e5c68 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/take_element_screenshot/screenshot.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/take_element_screenshot/screenshot.py >@@ -1,6 +1,5 @@ > from tests.support.asserts import assert_error, assert_success > from tests.support.image import png_dimensions >-from tests.support.inline import inline > > from . import element_dimensions > >@@ -20,7 +19,7 @@ def test_no_top_browsing_context(session, closed_window): > assert_error(response, "no such window") > > >-def test_no_browsing_context(session, closed_frame): >+def test_no_browsing_context(session, closed_frame, inline): > session.url = inline("<input>") > element = session.find.css("input", all=False) > >@@ -30,7 +29,7 @@ def test_no_browsing_context(session, closed_frame): > assert png_dimensions(screenshot) == element_dimensions(session, element) > > >-def test_stale(session): >+def test_stale(session, inline): > session.url = inline("<input>") > element = session.find.css("input", all=False) > session.refresh() >@@ -39,7 +38,7 @@ def test_stale(session): > assert_error(result, "stale element reference") > > >-def test_format_and_dimensions(session): >+def test_format_and_dimensions(session, inline): > session.url = inline("<input>") > element = session.find.css("input", all=False) > >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/take_element_screenshot/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/take_element_screenshot/user_prompts.py >index 5d1b497ff2c4..39fefe9325a9 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/take_element_screenshot/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/take_element_screenshot/user_prompts.py >@@ -3,7 +3,6 @@ > import pytest > > from tests.support.asserts import assert_dialog_handled, assert_error, assert_png, assert_success >-from tests.support.inline import inline > > > def take_element_screenshot(session, element_id): >@@ -17,7 +16,7 @@ def take_element_screenshot(session, element_id): > > > @pytest.fixture >-def check_user_prompt_closed_without_exception(session, create_dialog): >+def check_user_prompt_closed_without_exception(session, create_dialog, inline): > def check_user_prompt_closed_without_exception(dialog_type, retval): > session.url = inline("<input/>") > element = session.find.css("input", all=False) >@@ -35,7 +34,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_closed_with_exception(session, create_dialog): >+def check_user_prompt_closed_with_exception(session, create_dialog, inline): > def check_user_prompt_closed_with_exception(dialog_type, retval): > session.url = inline("<input/>") > element = session.find.css("input", all=False) >@@ -51,7 +50,7 @@ def check_user_prompt_closed_with_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_not_closed_but_exception(session, create_dialog): >+def check_user_prompt_not_closed_but_exception(session, create_dialog, inline): > def check_user_prompt_not_closed_but_exception(dialog_type): > session.url = inline("<input/>") > element = session.find.css("input", all=False) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/take_screenshot/iframe.py b/WebDriverTests/imported/w3c/webdriver/tests/take_screenshot/iframe.py >index 601c039d56de..81881c1dae73 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/take_screenshot/iframe.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/take_screenshot/iframe.py >@@ -2,9 +2,8 @@ import pytest > > from tests.support.asserts import assert_success > from tests.support.image import png_dimensions >-from tests.support.inline import iframe, inline > >-from . import element_dimensions, viewport_dimensions >+from . import viewport_dimensions > > DEFAULT_CONTENT = "<div id='content'>Lorem ipsum dolor sit amet.</div>" > >@@ -63,7 +62,7 @@ def take_screenshot(session): > "GET", "session/{session_id}/screenshot".format(**vars(session))) > > >-def test_always_captures_top_browsing_context(session): >+def test_always_captures_top_browsing_context(session, inline, iframe): > iframe_content = "{0}{1}".format(INNER_IFRAME_STYLE, DEFAULT_CONTENT) > session.url = inline("""{0}{1}""".format(OUTER_IFRAME_STYLE, iframe(iframe_content))) > >@@ -82,7 +81,7 @@ def test_always_captures_top_browsing_context(session): > > > @pytest.mark.parametrize("domain", ["", "alt"], ids=["same_origin", "cross_origin"]) >-def test_source_origin(session, domain): >+def test_source_origin(session, inline, iframe, domain): > session.url = inline("{0}{1}".format(REFERENCE_STYLE, REFERENCE_CONTENT)) > > response = take_screenshot(session) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/take_screenshot/screenshot.py b/WebDriverTests/imported/w3c/webdriver/tests/take_screenshot/screenshot.py >index d6130919167a..9e71a633c708 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/take_screenshot/screenshot.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/take_screenshot/screenshot.py >@@ -1,6 +1,5 @@ > from tests.support.asserts import assert_error, assert_png, assert_success > from tests.support.image import png_dimensions >-from tests.support.inline import inline > > from . import viewport_dimensions > >@@ -15,7 +14,7 @@ def test_no_top_browsing_context(session, closed_window): > assert_error(response, "no such window") > > >-def test_no_browsing_context(session, closed_frame): >+def test_no_browsing_context(session, closed_frame, inline): > session.url = inline("<input>") > > response = take_screenshot(session) >@@ -25,7 +24,7 @@ def test_no_browsing_context(session, closed_frame): > assert png_dimensions(value) == viewport_dimensions(session) > > >-def test_format_and_dimensions(session): >+def test_format_and_dimensions(session, inline): > session.url = inline("<input>") > > response = take_screenshot(session) >diff --git a/WebDriverTests/imported/w3c/webdriver/tests/take_screenshot/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/take_screenshot/user_prompts.py >index ad9e3695148d..7d57f8f2716b 100644 >--- a/WebDriverTests/imported/w3c/webdriver/tests/take_screenshot/user_prompts.py >+++ b/WebDriverTests/imported/w3c/webdriver/tests/take_screenshot/user_prompts.py >@@ -3,7 +3,6 @@ > import pytest > > from tests.support.asserts import assert_dialog_handled, assert_error, assert_png, assert_success >-from tests.support.inline import inline > > > def take_screenshot(session): >@@ -12,7 +11,7 @@ def take_screenshot(session): > > > @pytest.fixture >-def check_user_prompt_closed_without_exception(session, create_dialog): >+def check_user_prompt_closed_without_exception(session, create_dialog, inline): > def check_user_prompt_closed_without_exception(dialog_type, retval): > session.url = inline("<input/>") > >@@ -29,7 +28,7 @@ def check_user_prompt_closed_without_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_closed_with_exception(session, create_dialog): >+def check_user_prompt_closed_with_exception(session, create_dialog, inline): > def check_user_prompt_closed_with_exception(dialog_type, retval): > session.url = inline("<input/>") > >@@ -44,7 +43,7 @@ def check_user_prompt_closed_with_exception(session, create_dialog): > > > @pytest.fixture >-def check_user_prompt_not_closed_but_exception(session, create_dialog): >+def check_user_prompt_not_closed_but_exception(session, create_dialog, inline): > def check_user_prompt_not_closed_but_exception(dialog_type): > session.url = inline("<input/>") >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 230551
:
438811
|
438943
|
438946
|
439024
|
439035
|
440486