Unreviewed. Update W3C WebDriver imported tests.
authorcarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 10 Jan 2018 11:25:51 +0000 (11:25 +0000)
committercarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 10 Jan 2018 11:25:51 +0000 (11:25 +0000)
* imported/w3c/importer.json:
* imported/w3c/tools/wptrunner/wptrunner/browsers/firefox.py:
* imported/w3c/tools/wptrunner/wptrunner/browsers/servo.py:
* imported/w3c/tools/wptrunner/wptrunner/environment.py:
* imported/w3c/tools/wptrunner/wptrunner/executors/base.py:
* imported/w3c/tools/wptrunner/wptrunner/executors/executormarionette.py:
* imported/w3c/tools/wptrunner/wptrunner/executors/executorservo.py:
* imported/w3c/tools/wptrunner/wptrunner/executors/pytestrunner/runner.py:
* imported/w3c/tools/wptrunner/wptrunner/testrunner.py:
* imported/w3c/tools/wptrunner/wptrunner/update/update.py:
* imported/w3c/tools/wptrunner/wptrunner/webdriver_server.py:
* imported/w3c/tools/wptrunner/wptrunner/wptlogging.py:
* imported/w3c/tools/wptrunner/wptrunner/wptrunner.py:
* imported/w3c/webdriver/OWNERS:
* imported/w3c/webdriver/interface/interface.html: Removed.
* imported/w3c/webdriver/tests/actions/modifier_click.py:
* imported/w3c/webdriver/tests/actions/mouse.py:
* imported/w3c/webdriver/tests/actions/mouse_dblclick.py: Added.
* imported/w3c/webdriver/tests/actions/sequence.py:
* imported/w3c/webdriver/tests/actions/support/mouse.py: Added.
* imported/w3c/webdriver/tests/cookies/add_cookie.py:
* imported/w3c/webdriver/tests/cookies/get_named_cookie.py:
* imported/w3c/webdriver/tests/element_retrieval/__init__.py: Copied from WebDriverTests/imported/w3c/webdriver/tests/retrieval/__init__.py.
* imported/w3c/webdriver/tests/element_retrieval/find_element.py: Renamed from WebDriverTests/imported/w3c/webdriver/tests/retrieval/find_element.py.
* imported/w3c/webdriver/tests/element_retrieval/find_element_from_element.py: Renamed from WebDriverTests/imported/w3c/webdriver/tests/retrieval/find_element_from_element.py.
* imported/w3c/webdriver/tests/element_retrieval/find_elements.py: Renamed from WebDriverTests/imported/w3c/webdriver/tests/retrieval/find_elements.py.
* imported/w3c/webdriver/tests/element_retrieval/find_elements_from_element.py: Renamed from WebDriverTests/imported/w3c/webdriver/tests/retrieval/find_element_from_elements.py.
* imported/w3c/webdriver/tests/element_send_keys/__init__.py: Renamed from WebDriverTests/imported/w3c/webdriver/tests/retrieval/__init__.py.
* imported/w3c/webdriver/tests/element_send_keys/interactability.py: Added.
* imported/w3c/webdriver/tests/element_send_keys/scroll_into_view.py: Added.
* imported/w3c/webdriver/tests/execute_script/cyclic.py: Added.
* imported/w3c/webdriver/tests/get_window_rect.py:
* imported/w3c/webdriver/tests/interface.html:
* imported/w3c/webdriver/tests/navigation/current_url.py:
* imported/w3c/webdriver/tests/support/asserts.py:
* imported/w3c/webdriver/tests/support/fixtures.py:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@226708 268f45cc-cd09-0410-ab3c-d52691b4dbfc

37 files changed:
WebDriverTests/ChangeLog
WebDriverTests/imported/w3c/importer.json
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/firefox.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/servo.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/environment.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/base.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executormarionette.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executorservo.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/pytestrunner/runner.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testrunner.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/update/update.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/webdriver_server.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptlogging.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptrunner.py
WebDriverTests/imported/w3c/webdriver/OWNERS
WebDriverTests/imported/w3c/webdriver/interface/interface.html [deleted file]
WebDriverTests/imported/w3c/webdriver/tests/actions/modifier_click.py
WebDriverTests/imported/w3c/webdriver/tests/actions/mouse.py
WebDriverTests/imported/w3c/webdriver/tests/actions/mouse_dblclick.py [new file with mode: 0644]
WebDriverTests/imported/w3c/webdriver/tests/actions/sequence.py
WebDriverTests/imported/w3c/webdriver/tests/actions/support/mouse.py [new file with mode: 0644]
WebDriverTests/imported/w3c/webdriver/tests/cookies/add_cookie.py
WebDriverTests/imported/w3c/webdriver/tests/cookies/get_named_cookie.py
WebDriverTests/imported/w3c/webdriver/tests/element_retrieval/__init__.py [moved from WebDriverTests/imported/w3c/webdriver/tests/retrieval/__init__.py with 100% similarity]
WebDriverTests/imported/w3c/webdriver/tests/element_retrieval/find_element.py [moved from WebDriverTests/imported/w3c/webdriver/tests/retrieval/find_element.py with 88% similarity]
WebDriverTests/imported/w3c/webdriver/tests/element_retrieval/find_element_from_element.py [moved from WebDriverTests/imported/w3c/webdriver/tests/retrieval/find_element_from_element.py with 92% similarity]
WebDriverTests/imported/w3c/webdriver/tests/element_retrieval/find_elements.py [moved from WebDriverTests/imported/w3c/webdriver/tests/retrieval/find_elements.py with 87% similarity]
WebDriverTests/imported/w3c/webdriver/tests/element_retrieval/find_elements_from_element.py [moved from WebDriverTests/imported/w3c/webdriver/tests/retrieval/find_element_from_elements.py with 90% similarity]
WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/__init__.py [new file with mode: 0644]
WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/interactability.py [new file with mode: 0644]
WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/scroll_into_view.py [new file with mode: 0644]
WebDriverTests/imported/w3c/webdriver/tests/execute_script/cyclic.py [new file with mode: 0644]
WebDriverTests/imported/w3c/webdriver/tests/get_window_rect.py
WebDriverTests/imported/w3c/webdriver/tests/interface.html
WebDriverTests/imported/w3c/webdriver/tests/navigation/current_url.py
WebDriverTests/imported/w3c/webdriver/tests/support/asserts.py
WebDriverTests/imported/w3c/webdriver/tests/support/fixtures.py

index 1067ef9..ab24b21 100644 (file)
@@ -1,3 +1,44 @@
+2018-01-10  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        Unreviewed. Update W3C WebDriver imported tests.
+
+        * imported/w3c/importer.json:
+        * imported/w3c/tools/wptrunner/wptrunner/browsers/firefox.py:
+        * imported/w3c/tools/wptrunner/wptrunner/browsers/servo.py:
+        * imported/w3c/tools/wptrunner/wptrunner/environment.py:
+        * imported/w3c/tools/wptrunner/wptrunner/executors/base.py:
+        * imported/w3c/tools/wptrunner/wptrunner/executors/executormarionette.py:
+        * imported/w3c/tools/wptrunner/wptrunner/executors/executorservo.py:
+        * imported/w3c/tools/wptrunner/wptrunner/executors/pytestrunner/runner.py:
+        * imported/w3c/tools/wptrunner/wptrunner/testrunner.py:
+        * imported/w3c/tools/wptrunner/wptrunner/update/update.py:
+        * imported/w3c/tools/wptrunner/wptrunner/webdriver_server.py:
+        * imported/w3c/tools/wptrunner/wptrunner/wptlogging.py:
+        * imported/w3c/tools/wptrunner/wptrunner/wptrunner.py:
+        * imported/w3c/webdriver/OWNERS:
+        * imported/w3c/webdriver/interface/interface.html: Removed.
+        * imported/w3c/webdriver/tests/actions/modifier_click.py:
+        * imported/w3c/webdriver/tests/actions/mouse.py:
+        * imported/w3c/webdriver/tests/actions/mouse_dblclick.py: Added.
+        * imported/w3c/webdriver/tests/actions/sequence.py:
+        * imported/w3c/webdriver/tests/actions/support/mouse.py: Added.
+        * imported/w3c/webdriver/tests/cookies/add_cookie.py:
+        * imported/w3c/webdriver/tests/cookies/get_named_cookie.py:
+        * imported/w3c/webdriver/tests/element_retrieval/__init__.py: Copied from WebDriverTests/imported/w3c/webdriver/tests/retrieval/__init__.py.
+        * imported/w3c/webdriver/tests/element_retrieval/find_element.py: Renamed from WebDriverTests/imported/w3c/webdriver/tests/retrieval/find_element.py.
+        * imported/w3c/webdriver/tests/element_retrieval/find_element_from_element.py: Renamed from WebDriverTests/imported/w3c/webdriver/tests/retrieval/find_element_from_element.py.
+        * imported/w3c/webdriver/tests/element_retrieval/find_elements.py: Renamed from WebDriverTests/imported/w3c/webdriver/tests/retrieval/find_elements.py.
+        * imported/w3c/webdriver/tests/element_retrieval/find_elements_from_element.py: Renamed from WebDriverTests/imported/w3c/webdriver/tests/retrieval/find_element_from_elements.py.
+        * imported/w3c/webdriver/tests/element_send_keys/__init__.py: Renamed from WebDriverTests/imported/w3c/webdriver/tests/retrieval/__init__.py.
+        * imported/w3c/webdriver/tests/element_send_keys/interactability.py: Added.
+        * imported/w3c/webdriver/tests/element_send_keys/scroll_into_view.py: Added.
+        * imported/w3c/webdriver/tests/execute_script/cyclic.py: Added.
+        * imported/w3c/webdriver/tests/get_window_rect.py:
+        * imported/w3c/webdriver/tests/interface.html:
+        * imported/w3c/webdriver/tests/navigation/current_url.py:
+        * imported/w3c/webdriver/tests/support/asserts.py:
+        * imported/w3c/webdriver/tests/support/fixtures.py:
+
 2017-12-14  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         WebDriver: add a common way to run tests with pytest
index af46c58..6778b25 100644 (file)
@@ -1,6 +1,6 @@
 {
     "repository": "https://github.com/w3c/web-platform-tests.git",
-    "revision": "267d6b27ffd416577702653c0987b30239daae3f",
+    "revision": "941f6aa2949e24475a7dcd53fd1cb70142059d37",
     "paths_to_import": [
         "tools/webdriver",
         "tools/wptrunner",
index 552cd15..b324bcc 100644 (file)
@@ -51,7 +51,10 @@ def get_timeout_multiplier(test_type, run_info_data, **kwargs):
         else:
             return 2
     elif run_info_data["debug"] or run_info_data.get("asan"):
-        return 3
+        if run_info_data.get("ccov"):
+            return 4
+        else:
+            return 3
     return 1
 
 
index 89d69ab..3738ce4 100644 (file)
@@ -42,6 +42,8 @@ def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
     rv = base_executor_kwargs(test_type, server_config,
                               cache_manager, **kwargs)
     rv["pause_after_test"] = kwargs["pause_after_test"]
+    if test_type == "wdspec":
+        rv["capabilities"] = {}
     return rv
 
 
index e873440..874595c 100644 (file)
@@ -37,8 +37,6 @@ def do_delayed_imports(logger, test_paths):
     try:
         from tools.serve import serve
     except ImportError:
-        from wpt_tools.serve import serve
-    except ImportError:
         failed.append("serve")
 
     try:
@@ -86,7 +84,6 @@ class TestEnvironment(object):
         self.ssl_env = ssl_env
         self.server = None
         self.config = None
-        self.external_config = None
         self.pause_after_test = pause_after_test
         self.test_server_port = options.pop("test_server_port", True)
         self.debug_info = debug_info
@@ -105,9 +102,10 @@ class TestEnvironment(object):
             cm.__enter__(self.options)
         self.setup_server_logging()
         self.config = self.load_config()
-        serve.set_computed_defaults(self.config)
-        self.external_config, self.servers = serve.start(self.config, self.ssl_env,
-                                                         self.get_routes())
+        ports = serve.get_ports(self.config, self.ssl_env)
+        self.config = serve.normalise_config(self.config, ports)
+        self.servers = serve.start(self.config, self.ssl_env,
+                                   self.get_routes())
         if self.options.get("supports_debugger") and self.debug_info and self.debug_info.interactive:
             self.ignore_interrupts()
         return self
@@ -160,6 +158,8 @@ class TestEnvironment(object):
         config["key_file"] = key_file
         config["certificate"] = certificate
 
+        serve.set_computed_defaults(config)
+
         return config
 
     def setup_server_logging(self):
index cc04d76..d87e4ab 100644 (file)
@@ -113,7 +113,7 @@ class TestExecutor(object):
         :param browser: ExecutorBrowser instance providing properties of the
                         browser that will be tested.
         :param server_config: Dictionary of wptserve server configuration of the
-                              form stored in TestEnvironment.external_config
+                              form stored in TestEnvironment.config
         :param timeout_multiplier: Multiplier relative to base timeout to use
                                    when setting test timeout.
         """
index edea3f1..2d0dc91 100644 (file)
@@ -175,7 +175,11 @@ class MarionetteProtocol(Protocol):
             # This can happen if there was a crash
             return
         if socket_timeout:
-            self.marionette.timeout.script = socket_timeout / 2
+            try:
+                self.marionette.timeout.script = socket_timeout / 2
+            except (socket.error, IOError):
+                self.logger.debug("Socket closed")
+                return
 
         self.marionette.switch_to_window(self.runner_handle)
         while True:
@@ -564,7 +568,7 @@ class InternalRefTestImplementation(object):
             self.executor.protocol.marionette.set_context(self.executor.protocol.marionette.CONTEXT_CONTENT)
         except Exception as e:
             # Ignore errors during teardown
-            self.logger.warning(traceback.traceback.format_exc(e))
+            self.logger.warning(traceback.format_exc(e))
 
 
 
index 97fe304..33ff10d 100644 (file)
@@ -18,7 +18,7 @@ from .base import (ExecutorException,
                    RefTestImplementation,
                    testharness_result_converter,
                    reftest_result_converter,
-                   WdspecExecutor)
+                   WdspecExecutor, WebDriverProtocol)
 from .process import ProcessTestExecutor
 from ..browsers.base import browser_command
 from ..wpttest import WdspecResult, WdspecSubtestResult
@@ -286,82 +286,9 @@ class ServoRefTestExecutor(ProcessTestExecutor):
                                        line,
                                        " ".join(self.command))
 
-class ServoWdspecProtocol(Protocol):
-    def __init__(self, executor, browser):
-        self.do_delayed_imports()
-        Protocol.__init__(self, executor, browser)
-        self.session = None
-        self.server = None
-
-    def setup(self, runner):
-        try:
-            self.server = ServoDriverServer(self.logger, binary=self.browser.binary, binary_args=self.browser.binary_args)
-            self.server.start(block=False)
-            self.logger.info(
-                "WebDriver HTTP server listening at %s" % self.server.url)
-
-            self.logger.info(
-                "Establishing new WebDriver session with %s" % self.server.url)
-            self.session = webdriver.Session(
-                self.server.host, self.server.port, self.server.base_path)
-        except Exception:
-            self.logger.error(traceback.format_exc())
-            self.executor.runner.send_message("init_failed")
-        else:
-            self.executor.runner.send_message("init_succeeded")
-
-    def teardown(self):
-        if self.server is not None:
-            try:
-                if self.session.session_id is not None:
-                    self.session.end()
-            except Exception:
-                pass
-            if self.server.is_alive:
-                self.server.stop()
-
-    @property
-    def is_alive(self):
-        conn = httplib.HTTPConnection(self.server.host, self.server.port)
-        conn.request("HEAD", self.server.base_path + "invalid")
-        res = conn.getresponse()
-        return res.status == 404
-
-    def do_delayed_imports(self):
-        global pytestrunner, webdriver
-        from . import pytestrunner
-        import webdriver
 
+class ServoDriverProtocol(WebDriverProtocol):
+    server_cls = ServoDriverServer
 
 class ServoWdspecExecutor(WdspecExecutor):
-    def __init__(self, browser, server_config,
-                 timeout_multiplier=1, close_after_done=True, debug_info=None,
-                 **kwargs):
-        WdspecExecutor.__init__(self, browser, server_config,
-                                timeout_multiplier=timeout_multiplier,
-                                debug_info=debug_info)
-        self.protocol = ServoWdspecProtocol(self, browser)
-
-    def is_alive(self):
-        return self.protocol.is_alive
-
-    def on_environment_change(self, new_environment):
-        pass
-
-    def do_test(self, test):
-        timeout = test.timeout * self.timeout_multiplier + extra_timeout
-
-        success, data = WdspecRun(self.do_wdspec,
-                                  self.protocol.session,
-                                  test.path,
-                                  timeout).run()
-
-        if success:
-            return self.convert_result(test, data)
-
-        return (test.result_cls(*data), [])
-
-    def do_wdspec(self, session, path, timeout):
-        harness_result = ("OK", None)
-        subtest_results = pytestrunner.run(path, session, timeout=timeout)
-        return (harness_result, subtest_results)
+    protocol_cls = ServoDriverProtocol
index a2b603d..0870b12 100644 (file)
@@ -117,7 +117,7 @@ class SubtestResultRecorder(object):
     def record(self, test, status, message=None, stack=None):
         if stack is not None:
             stack = str(stack)
-        new_result = (test, status, message, stack)
+        new_result = (test.split("::")[-1], status, message, stack)
         self.results.append(new_result)
 
 
index 6441828..75f4825 100644 (file)
@@ -120,6 +120,13 @@ def start_runner(runner_command_queue, runner_result_queue,
                  executor_browser_cls, executor_browser_kwargs,
                  stop_flag):
     """Launch a TestRunner in a new process"""
+    def log(level, msg):
+        runner_result_queue.put(("log", (level, {"message": msg})))
+
+    def handle_error(e):
+        log("critical", traceback.format_exc())
+        stop_flag.set()
+
     try:
         browser = executor_browser_cls(**executor_browser_kwargs)
         executor = executor_cls(browser, **executor_kwargs)
@@ -128,10 +135,10 @@ def start_runner(runner_command_queue, runner_result_queue,
                 runner.run()
             except KeyboardInterrupt:
                 stop_flag.set()
-    except Exception:
-        runner_result_queue.put(("log", ("critical", {"message": traceback.format_exc()})))
-        print >> sys.stderr, traceback.format_exc()
-        stop_flag.set()
+            except Exception as e:
+                handle_error(e)
+    except Exception as e:
+        handle_error(e)
     finally:
         runner_command_queue = None
         runner_result_queue = None
@@ -389,6 +396,7 @@ class TestRunnerManager(threading.Thread):
         }
         try:
             command, data = self.command_queue.get(True, 1)
+            self.logger.debug("Got command: %r" % command)
         except IOError:
             self.logger.error("Got IOError from poll")
             return RunnerManagerState.restarting(0)
@@ -676,7 +684,18 @@ class TestRunnerManager(threading.Thread):
             self.browser.cleanup()
         while True:
             try:
-                self.logger.warning(" ".join(map(repr, self.command_queue.get_nowait())))
+                cmd, data = self.command_queue.get_nowait()
+            except Empty:
+                break
+            else:
+                if cmd == "log":
+                     self.log(*data)
+                else:
+                    self.logger.warning("%r: %r" % (cmd, data))
+        while True:
+            try:
+                cmd, data = self.remote_queue.get_nowait()
+                self.logger.warning("%r: %r" % (cmd, data))
             except Empty:
                 break
 
index d9ef40f..c985d35 100644 (file)
@@ -11,10 +11,7 @@ from state import State
 
 def setup_paths(sync_path):
     sys.path.insert(0, os.path.abspath(sync_path))
-    try:
-        from tools import localpaths
-    except ImportError:
-        from wpt_tools import localpaths
+    from tools import localpaths
 
 class LoadConfig(Step):
     """Step for loading configuration from the ini file and kwargs."""
index 4a3c48c..fc853bf 100644 (file)
@@ -195,10 +195,11 @@ class GeckoDriverServer(WebDriverServer):
 
 
 class ServoDriverServer(WebDriverServer):
-    def __init__(self, logger, binary="servo", binary_args=None, host="127.0.0.1", port=None):
+    def __init__(self, logger, binary="servo", binary_args=None, 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=env)
+        WebDriverServer.__init__(self, logger, binary, host=host, port=port, env=env, args=args)
         self.binary_args = binary_args
 
     def make_command(self):
index 4d32061..26d174c 100644 (file)
@@ -1,13 +1,20 @@
 import logging
 import sys
 import threading
+from Queue import Empty
 from StringIO import StringIO
 from multiprocessing import Queue
 
-from mozlog import commandline, stdadapter
+from mozlog import commandline, stdadapter, set_default_logger
+from mozlog.structuredlog import StructuredLogger
 
 def setup(args, defaults):
-    logger = commandline.setup_logging("web-platform-tests", args, defaults)
+    logger = args.pop('log', None)
+    if logger:
+        set_default_logger(logger)
+        StructuredLogger._logger_states["web-platform-tests"] = logger._state
+    else:
+        logger = commandline.setup_logging("web-platform-tests", args, defaults)
     setup_stdlib_logger()
 
     for name in args.keys():
@@ -45,7 +52,6 @@ class LogLevelRewriter(object):
         return self.inner(data)
 
 
-
 class LogThread(threading.Thread):
     def __init__(self, queue, logger, level):
         self.queue = queue
@@ -120,5 +126,10 @@ class CaptureIO(object):
                 self.logging_queue.put(None)
                 if self.logging_thread is not None:
                     self.logging_thread.join(10)
+                while not self.logging_queue.empty():
+                    try:
+                        self.logger.warning("Dropping log message: %r", self.logging_queue.get())
+                    except Exception:
+                        pass
                 self.logging_queue.close()
                 self.logger.info("queue closed")
index be81eb9..637cbe5 100644 (file)
@@ -201,7 +201,7 @@ def run_tests(config, test_paths, product, **kwargs):
                     logger.info("Repetition %i / %i" % (repeat_count, repeat))
 
                 unexpected_count = 0
-                logger.suite_start(test_loader.test_ids, run_info)
+                logger.suite_start(test_loader.test_ids, name='web-platform-test', run_info=run_info)
                 for test_type in kwargs["test_types"]:
                     logger.info("Running %s tests" % test_type)
 
@@ -222,7 +222,7 @@ def run_tests(config, test_paths, product, **kwargs):
 
                     executor_cls = executor_classes.get(test_type)
                     executor_kwargs = get_executor_kwargs(test_type,
-                                                          test_environment.external_config,
+                                                          test_environment.config,
                                                           test_environment.cache_manager,
                                                           run_info,
                                                           **kwargs)
diff --git a/WebDriverTests/imported/w3c/webdriver/interface/interface.html b/WebDriverTests/imported/w3c/webdriver/interface/interface.html
deleted file mode 100644 (file)
index f3c149b..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-<!DOCTYPE html>
-<body>
-<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 type=text/plain class=untested>
-[Exposed=Window]
-interface Navigator {
-  // objects implementing this interface also implement the interfaces given below
-};
-</script>
-<script type=text/plain>
-Navigator implements NavigatorAutomationInformation;
-
-[NoInterfaceObject,
- Exposed=(Window)]
-interface NavigatorAutomationInformation {
-    readonly attribute boolean webdriver;
-    // always returns true
-};
-</script>
-<script>
-"use strict";
-
-if ("webdriver" in navigator) {
-  test(() => assert_true(navigator.webdriver), "navigator.webdriver is always true");
-  var idlArray = new IdlArray();
-  [].forEach.call(document.querySelectorAll("script[type=text\\/plain]"), function(node) {
-    if (node.className == "untested") {
-      idlArray.add_untested_idls(node.textContent);
-    } else {
-      idlArray.add_idls(node.textContent);
-    }
-  });
-  idlArray.test();
-} else {
-  done();
-}
-</script>
index 73de161..fdd43e2 100644 (file)
@@ -6,30 +6,55 @@ from tests.actions.support.refine import filter_dict, get_events
 from tests.actions.support.keys import Keys
 
 
+# Using local fixtures because we want to start a new session between
+# each test, otherwise the clicks in each test interfere with each other.
+@pytest.fixture(autouse=True)
+def release_actions(mod_click_session, request):
+    request.addfinalizer(mod_click_session.actions.release)
+
+
+@pytest.fixture
+def mod_click_session(new_session, url, add_browser_capabilites):
+    _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({})}})
+    session.url = url("/webdriver/tests/actions/support/test_actions_wdspec.html")
+
+    return session
+
+
+@pytest.fixture
+def key_chain(mod_click_session):
+    return mod_click_session.actions.sequence("key", "keyboard_id")
+
+
+@pytest.fixture
+def mouse_chain(mod_click_session):
+    return mod_click_session.actions.sequence(
+        "pointer",
+        "pointer_id",
+        {"pointerType": "mouse"})
+
+
 @pytest.mark.parametrize("modifier, prop", [
-    (Keys.CONTROL, "ctrlKey"),
-    (Keys.ALT, "altKey"),
-    (Keys.META, "metaKey"),
-    (Keys.SHIFT, "shiftKey"),
-    (Keys.R_CONTROL, "ctrlKey"),
-    (Keys.R_ALT, "altKey"),
-    (Keys.R_META, "metaKey"),
-    (Keys.R_SHIFT, "shiftKey"),
+   (Keys.ALT, "altKey"),
+   (Keys.R_ALT, "altKey"),
+   (Keys.META, "metaKey"),
+   (Keys.R_META, "metaKey"),
+   (Keys.SHIFT, "shiftKey"),
+   (Keys.R_SHIFT, "shiftKey"),
 ])
-def test_modifier_click(session,
-                       test_actions_page,
+def test_modifier_click(mod_click_session,
                        key_chain,
                        mouse_chain,
                        modifier,
                        prop):
     key_chain \
-        .pause(0) \
+        .pause(200) \
         .key_down(modifier) \
         .pause(200) \
         .key_up(modifier)
-    outer = session.find.css("#outer", all=False)
+    outer = mod_click_session.find.css("#outer", all=False)
     mouse_chain.click(element=outer)
-    session.actions.perform([key_chain.dict, mouse_chain.dict])
+    mod_click_session.actions.perform([key_chain.dict, mouse_chain.dict])
     expected = [
         {"type": "mousemove"},
         {"type": "mousedown"},
@@ -46,29 +71,47 @@ def test_modifier_click(session,
         e.update(defaults)
         if e["type"] != "mousemove":
             e[prop] = True
-    filtered_events = [filter_dict(e, expected[0]) for e in get_events(session)]
+    filtered_events = [filter_dict(e, expected[0]) for e in get_events(mod_click_session)]
     assert expected == filtered_events
 
 
-def test_release_control_click(session, key_reporter, key_chain, mouse_chain):
+def test_many_modifiers_click(mod_click_session, key_chain, mouse_chain):
+    outer = mod_click_session.find.css("#outer", all=False)
     key_chain \
         .pause(0) \
-        .key_down(Keys.CONTROL)
+        .key_down(Keys.CONTROL) \
+        .key_down(Keys.SHIFT) \
+        .pause(0) \
+        .key_up(Keys.CONTROL) \
+        .key_up(Keys.SHIFT)
     mouse_chain \
-        .pointer_move(0, 0, origin=key_reporter) \
+        .pointer_move(0, 0, origin=outer) \
+        .pause(0) \
+        .pointer_down() \
+        .pointer_up() \
+        .pause(0) \
+        .pause(0) \
         .pointer_down()
-    session.actions.perform([key_chain.dict, mouse_chain.dict])
-    session.execute_script("""
-        var keyReporter = document.getElementById("keys");
-        ["mousedown", "mouseup"].forEach((e) => {
-            keyReporter.addEventListener(e, recordPointerEvent);
-          });
-        resetEvents();
-    """)
-    session.actions.release()
+    mod_click_session.actions.perform([key_chain.dict, mouse_chain.dict])
     expected = [
+        {"type": "mousemove"},
+        # shift and ctrl presses
+        {"type": "mousedown"},
         {"type": "mouseup"},
-        {"type": "keyup"},
+        {"type": "click"},
+        # no modifiers pressed
+        {"type": "mousedown"},
     ]
-    events = [filter_dict(e, expected[0]) for e in get_events(session)]
+    defaults = {
+        "altKey": False,
+        "metaKey": False,
+        "shiftKey": False,
+        "ctrlKey": False
+    }
+    for e in expected:
+        e.update(defaults)
+    for e in expected[1:4]:
+        e["shiftKey"] = True
+        e["ctrlKey"] = True
+    events = [filter_dict(e, expected[0]) for e in get_events(mod_click_session)]
     assert events == expected
index 07f7809..95d284c 100644 (file)
@@ -1,7 +1,8 @@
 import pytest
 
-from tests.support.inline import inline
+from tests.actions.support.mouse import assert_move_to_coordinates, get_center
 from tests.actions.support.refine import get_events, filter_dict
+from tests.support.inline import inline
 from tests.support.wait import wait
 
 
@@ -10,13 +11,6 @@ def link_doc(dest):
     return inline(content)
 
 
-def get_center(rect):
-    return {
-        "x": rect["width"] / 2 + rect["x"],
-        "y": rect["height"] / 2 + rect["y"],
-    }
-
-
 # TODO use pytest.approx once we upgrade to pytest > 3.0
 def approx(n, m, tolerance=1):
     return abs(n - m) <= tolerance
@@ -33,11 +27,8 @@ def test_click_at_coordinates(session, test_actions_page, mouse_chain):
         .perform()
     events = get_events(session)
     assert len(events) == 4
+    assert_move_to_coordinates(div_point, "outer", events)
     for e in events:
-        if e["type"] != "mousemove":
-            assert e["pageX"] == div_point["x"]
-            assert e["pageY"] == div_point["y"]
-            assert e["target"] == "outer"
         if e["type"] != "mousedown":
             assert e["buttons"] == 0
         assert e["button"] == 0
@@ -89,7 +80,7 @@ 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, release_actions):
     destination = url("/webdriver/tests/actions/support/test_actions_wdspec.html")
     start = link_doc(destination)
 
@@ -112,7 +103,12 @@ def test_click_navigation(session, url):
 @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)])
-def test_drag_and_drop(session, test_actions_page, mouse_chain, dx, dy, drag_duration):
+def test_drag_and_drop(session,
+                       test_actions_page,
+                       mouse_chain,
+                       dx,
+                       dy,
+                       drag_duration):
     drag_target = session.find.css("#dragTarget", all=False)
     initial_rect = drag_target.rect
     initial_center = get_center(initial_rect)
diff --git a/WebDriverTests/imported/w3c/webdriver/tests/actions/mouse_dblclick.py b/WebDriverTests/imported/w3c/webdriver/tests/actions/mouse_dblclick.py
new file mode 100644 (file)
index 0000000..f73f780
--- /dev/null
@@ -0,0 +1,108 @@
+import pytest
+
+from tests.actions.support.mouse import assert_move_to_coordinates, get_center
+from tests.actions.support.refine import get_events, filter_dict
+
+
+_DBLCLICK_INTERVAL = 640
+
+
+# Using local fixtures because we want to start a new session between
+# each test, otherwise the clicks in each test interfere with each other.
+@pytest.fixture(autouse=True)
+def release_actions(dblclick_session, request):
+    # release all actions after each test
+    # equivalent to a teardown_function, but with access to session fixture
+    request.addfinalizer(dblclick_session.actions.release)
+
+
+@pytest.fixture
+def dblclick_session(new_session, url, add_browser_capabilites):
+    _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({})}})
+    session.url = url("/webdriver/tests/actions/support/test_actions_wdspec.html")
+
+    return session
+
+
+@pytest.fixture
+def mouse_chain(dblclick_session):
+    return dblclick_session.actions.sequence(
+        "pointer",
+        "pointer_id",
+        {"pointerType": "mouse"})
+
+
+@pytest.mark.parametrize("click_pause", [0, 200])
+def test_dblclick_at_coordinates(dblclick_session, mouse_chain, click_pause):
+    div_point = {
+        "x": 82,
+        "y": 187,
+    }
+    mouse_chain \
+        .pointer_move(div_point["x"], div_point["y"]) \
+        .click() \
+        .pause(click_pause) \
+        .click() \
+        .perform()
+    events = get_events(dblclick_session)
+    assert_move_to_coordinates(div_point, "outer", events)
+    expected = [
+        {"type": "mousedown", "button": 0},
+        {"type": "mouseup", "button": 0},
+        {"type": "click", "button": 0},
+        {"type": "mousedown", "button": 0},
+        {"type": "mouseup", "button": 0},
+        {"type": "click", "button": 0},
+        {"type": "dblclick", "button": 0},
+    ]
+    assert len(events) == 8
+    filtered_events = [filter_dict(e, expected[0]) for e in events]
+    assert expected == filtered_events[1:]
+
+
+def test_dblclick_with_pause_after_second_pointerdown(dblclick_session, mouse_chain):
+        outer = dblclick_session.find.css("#outer", all=False)
+        center = get_center(outer.rect)
+        mouse_chain \
+            .pointer_move(int(center["x"]), int(center["y"])) \
+            .click() \
+            .pointer_down() \
+            .pause(_DBLCLICK_INTERVAL + 10) \
+            .pointer_up() \
+            .perform()
+        events = get_events(dblclick_session)
+        expected = [
+            {"type": "mousedown", "button": 0},
+            {"type": "mouseup", "button": 0},
+            {"type": "click", "button": 0},
+            {"type": "mousedown", "button": 0},
+            {"type": "mouseup", "button": 0},
+            {"type": "click", "button": 0},
+            {"type": "dblclick", "button": 0},
+        ]
+        assert len(events) == 8
+        filtered_events = [filter_dict(e, expected[0]) for e in events]
+        assert expected == filtered_events[1:]
+
+
+def test_no_dblclick(dblclick_session, mouse_chain):
+        outer = dblclick_session.find.css("#outer", all=False)
+        center = get_center(outer.rect)
+        mouse_chain \
+            .pointer_move(int(center["x"]), int(center["y"])) \
+            .click() \
+            .pause(_DBLCLICK_INTERVAL + 10) \
+            .click() \
+            .perform()
+        events = get_events(dblclick_session)
+        expected = [
+            {"type": "mousedown", "button": 0},
+            {"type": "mouseup", "button": 0},
+            {"type": "click", "button": 0},
+            {"type": "mousedown", "button": 0},
+            {"type": "mouseup", "button": 0},
+            {"type": "click", "button": 0},
+        ]
+        assert len(events) == 7
+        filtered_events = [filter_dict(e, expected[0]) for e in events]
+        assert expected == filtered_events[1:]
index 3092034..426dbe8 100644 (file)
@@ -37,45 +37,3 @@ def test_release_no_actions_sends_no_events(session, key_reporter):
     session.actions.release()
     assert len(get_keys(key_reporter)) == 0
     assert len(get_events(session)) == 0
-
-
-def test_many_modifiers_click(session, test_actions_page, key_chain, mouse_chain):
-    outer = session.find.css("#outer", all=False)
-    key_chain \
-        .pause(0) \
-        .key_down(Keys.CONTROL) \
-        .key_down(Keys.SHIFT) \
-        .pause(0) \
-        .key_up(Keys.CONTROL) \
-        .key_up(Keys.SHIFT)
-    mouse_chain \
-        .pointer_move(0, 0, origin=outer) \
-        .pause(0) \
-        .pointer_down() \
-        .pointer_up() \
-        .pause(0) \
-        .pause(0) \
-        .pointer_down()
-    session.actions.perform([key_chain.dict, mouse_chain.dict])
-    expected = [
-        {"type": "mousemove"},
-        # shift and ctrl presses
-        {"type": "mousedown"},
-        {"type": "mouseup"},
-        {"type": "click"},
-        # no modifiers pressed
-        {"type": "mousedown"},
-    ]
-    defaults = {
-        "altKey": False,
-        "metaKey": False,
-        "shiftKey": False,
-        "ctrlKey": False
-    }
-    for e in expected:
-        e.update(defaults)
-    for e in expected[1:4]:
-        e["shiftKey"] = True
-        e["ctrlKey"] = True
-    events = [filter_dict(e, expected[0]) for e in get_events(session)]
-    assert events == expected
diff --git a/WebDriverTests/imported/w3c/webdriver/tests/actions/support/mouse.py b/WebDriverTests/imported/w3c/webdriver/tests/actions/support/mouse.py
new file mode 100644 (file)
index 0000000..63a771d
--- /dev/null
@@ -0,0 +1,13 @@
+def assert_move_to_coordinates(point, target, events):
+    for e in events:
+        if e["type"] != "mousemove":
+            assert e["pageX"] == point["x"]
+            assert e["pageY"] == point["y"]
+            assert e["target"] == target
+
+
+def get_center(rect):
+    return {
+        "x": rect["width"] / 2 + rect["x"],
+        "y": rect["height"] / 2 + rect["y"],
+    }
index 86a5f86..1ebbcb4 100644 (file)
@@ -1,15 +1,14 @@
 from tests.support.fixtures import clear_all_cookies
-from tests.support.fixtures import server_config
 from datetime import datetime, timedelta
 
-def test_add_domain_cookie(session, url):
+def test_add_domain_cookie(session, url, server_config):
     session.url = url("/common/blank.html")
     clear_all_cookies(session)
     create_cookie_request = {
         "cookie": {
             "name": "hello",
             "value": "world",
-            "domain": "web-platform.test",
+            "domain": server_config["domains"][""],
             "path": "/",
             "httpOnly": False,
             "secure": False
@@ -37,16 +36,16 @@ def test_add_domain_cookie(session, url):
 
     assert cookie["name"] == "hello"
     assert cookie["value"] == "world"
-    assert cookie["domain"] == ".web-platform.test"
+    assert cookie["domain"] == ".%s" % server_config["domains"][""]
 
-def test_add_cookie_for_ip(session, url, server_config):
+def test_add_cookie_for_ip(session, url, server_config, configuration):
     session.url = "http://127.0.0.1:%s/404" % (server_config["ports"]["http"][0])
     clear_all_cookies(session)
     create_cookie_request = {
         "cookie": {
             "name": "hello",
             "value": "world",
-            "domain": "127.0.0.1",
+            "domain": configuration["host"],
             "path": "/",
             "httpOnly": False,
             "secure": False
@@ -142,3 +141,37 @@ def test_add_session_cookie(session, url):
 
     assert cookie["name"] == "hello"
     assert cookie["value"] == "world"
+
+def test_add_session_cookie_with_leading_dot_character_in_domain(session, url, server_config):
+    session.url = url("/common/blank.html")
+    clear_all_cookies(session)
+    create_cookie_request = {
+        "cookie": {
+            "name": "hello",
+            "value": "world",
+            "domain": ".%s" % server_config["domains"][""]
+        }
+    }
+    result = session.transport.send("POST", "session/%s/cookie" % session.session_id, create_cookie_request)
+    assert result.status == 200
+    assert "value" in result.body
+    assert isinstance(result.body["value"], dict)
+
+    result = session.transport.send("GET", "session/%s/cookie" % session.session_id)
+    assert result.status == 200
+    assert "value" in result.body
+    assert isinstance(result.body["value"], list)
+    assert len(result.body["value"]) == 1
+    assert isinstance(result.body["value"][0], dict)
+
+    cookie = result.body["value"][0]
+    assert "name" in cookie
+    assert isinstance(cookie["name"], basestring)
+    assert "value" in cookie
+    assert isinstance(cookie["value"], basestring)
+    assert "domain" in cookie
+    assert isinstance(cookie["domain"], basestring)
+
+    assert cookie["name"] == "hello"
+    assert cookie["value"] == "world"
+    assert cookie["domain"] == ".%s" % server_config["domains"][""]
index 027859d..f367ecc 100644 (file)
@@ -61,14 +61,14 @@ def test_get_named_cookie(session, url):
     # convert from seconds since epoch
     assert datetime.utcfromtimestamp(cookie["expiry"]).strftime(utc_string_format) == a_year_from_now
 
-def test_duplicated_cookie(session, url):
+def test_duplicated_cookie(session, url, server_config):
     session.url = url("/common/blank.html")
     clear_all_cookies(session)
     create_cookie_request = {
         "cookie": {
             "name": "hello",
             "value": "world",
-            "domain": "web-platform.test",
+            "domain": server_config["domains"][""],
             "path": "/",
             "httpOnly": False,
             "secure": False
@@ -79,7 +79,7 @@ def test_duplicated_cookie(session, url):
     assert "value" in result.body
     assert isinstance(result.body["value"], dict)
 
-    session.url = inline("<script>document.cookie = 'hello=newworld; domain=web-platform.test; path=/';</script>")
+    session.url = inline("<script>document.cookie = 'hello=newworld; domain=%s; path=/';</script>" % server_config["domains"][""])
     result = session.transport.send("GET", "session/%s/cookie" % session.session_id)
     assert result.status == 200
     assert "value" in result.body
@@ -71,3 +71,13 @@ def test_xhtml_namespace(session, using, value):
     response = find_element(session, using, value)
     value = assert_success(response)
     assert_same_element(session, value, expected)
+
+
+@pytest.mark.parametrize("using,value",
+                         [("css selector", ":root"),
+                          ("tag name", "html"),
+                          ("xpath", "/html")])
+def test_htmldocument(session, using, value):
+    session.url = inline("")
+    response = find_element(session, using, value)
+    assert_success(response)
@@ -72,3 +72,11 @@ def test_xhtml_namespace(session, using, value):
     response = find_element(session, from_element.id, using, value)
     value = assert_success(response)
     assert_same_element(session, value, expected)
+
+
+def test_parent_htmldocument(session):
+    session.url = inline("")
+    from_element = session.execute_script("return document.documentElement")
+
+    response = find_element(session, from_element.id, "xpath", "..")
+    assert_success(response)
@@ -77,3 +77,15 @@ def test_xhtml_namespace(session, using, value):
 
     found_element = value[0]
     assert_same_element(session, found_element, expected)
+
+
+@pytest.mark.parametrize("using,value",
+                         [("css selector", ":root"),
+                          ("tag name", "html"),
+                          ("xpath", "/html")])
+def test_htmldocument(session, using, value):
+    session.url = inline("")
+    response = find_elements(session, using, value)
+    value = assert_success(response)
+    assert isinstance(value, list)
+    assert len(value) == 1
@@ -75,3 +75,13 @@ def test_xhtml_namespace(session, using, value):
 
     found_element = value[0]
     assert_same_element(session, found_element, expected)
+
+
+def test_parent_htmldocument(session):
+    session.url = inline("")
+    from_element = session.execute_script("return document.documentElement")
+
+    response = find_elements(session, from_element.id, "xpath", "..")
+    value = assert_success(response)
+    assert isinstance(value, list)
+    assert len(value) == 1
diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/__init__.py b/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/interactability.py b/WebDriverTests/imported/w3c/webdriver/tests/element_send_keys/interactability.py
new file mode 100644 (file)
index 0000000..5812f2b
--- /dev/null
@@ -0,0 +1,136 @@
+from tests.support.asserts import assert_error, assert_same_element, assert_success
+from tests.support.inline import iframe, inline
+
+
+def send_keys_to_element(session, element, text):
+    return session.transport.send(
+        "POST",
+        "/session/{session_id}/element/{element_id}/value".format(
+            session_id=session.session_id,
+            element_id=element.id),
+        {"text": text})
+
+
+def test_body_is_interactable(session):
+    session.url = inline("""
+        <body onkeypress="document.getElementById('result').value += event.key">
+          <input type="text" id="result"/>
+        </body>
+    """)
+
+    element = session.find.css("body", all=False)
+    result = session.find.css("input", all=False)
+
+    # By default body is the active element
+    assert_same_element(session, element, session.active_element)
+
+    response = send_keys_to_element(session, element, "foo")
+    assert_success(response)
+    assert_same_element(session, element, session.active_element)
+    assert result.property("value") == "foo"
+
+
+def test_document_element_is_interactable(session):
+    session.url = inline("""
+        <html onkeypress="document.getElementById('result').value += event.key">
+          <input type="text" id="result"/>
+        </html>
+    """)
+
+    body = session.find.css("body", all=False)
+    element = session.find.css(":root", all=False)
+    result = session.find.css("input", all=False)
+
+    # By default body is the active element
+    assert_same_element(session, body, session.active_element)
+
+    response = send_keys_to_element(session, element, "foo")
+    assert_success(response)
+    assert_same_element(session, element, session.active_element)
+    assert result.property("value") == "foo"
+
+
+def test_iframe_is_interactable(session):
+    session.url = inline(iframe("""
+        <body onkeypress="document.getElementById('result').value += event.key">
+          <input type="text" id="result"/>
+        </body>
+    """))
+
+    body = session.find.css("body", all=False)
+    frame = session.find.css("iframe", all=False)
+
+    # By default the body has the focus
+    assert_same_element(session, body, session.active_element)
+
+    response = send_keys_to_element(session, frame, "foo")
+    assert_success(response)
+    assert_same_element(session, frame, session.active_element)
+
+    # Any key events are immediately routed to the nested
+    # browsing context's active document.
+    session.switch_frame(frame)
+    result = session.find.css("input", all=False)
+    assert result.property("value") == "foo"
+
+
+def test_transparent_element(session):
+    session.url = inline("<input style=\"opacity: 0;\">")
+    element = session.find.css("input", all=False)
+
+    response = send_keys_to_element(session, element, "foo")
+    assert_success(response)
+    assert element.property("value") == "foo"
+
+
+def test_readonly_element(session):
+    session.url = inline("<input readonly>")
+    element = session.find.css("input", all=False)
+
+    response = send_keys_to_element(session, element, "foo")
+    assert_success(response)
+    assert element.property("value") == ""
+
+
+def test_obscured_element(session):
+    session.url = inline("""
+      <input type="text" />
+      <div style="position: relative; top: -3em; height: 5em; background-color: blue"></div>
+    """)
+    element = session.find.css("input", all=False)
+
+    response = send_keys_to_element(session, element, "foo")
+    assert_success(response)
+    assert element.property("value") == "foo"
+
+
+def test_not_a_focusable_element(session):
+    session.url = inline("<div>foo</div>")
+    element = session.find.css("div", all=False)
+
+    response = send_keys_to_element(session, element, "foo")
+    assert_error(response, "element not interactable")
+
+
+def test_not_displayed_element(session):
+    session.url = inline("<input style=\"display: none\">")
+    element = session.find.css("input", all=False)
+
+    response = send_keys_to_element(session, element, "foo")
+    assert_error(response, "element not interactable")
+
+
+def test_hidden_element(session):
+    session.url = inline("<input style=\"visibility: hidden\">")
+    element = session.find.css("input", all=False)
+
+    response = send_keys_to_element(session, element, "foo")
+    assert_error(response, "element not interactable")
+
+
+def test_disabled_element(session):
+    session.url = inline("<input disabled=\"false\">")
+    element = session.find.css("input", all=False)
+
+    response = send_keys_to_element(session, element, "foo")
+    assert_error(response, "element not interactable")
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
new file mode 100644 (file)
index 0000000..a1d454d
--- /dev/null
@@ -0,0 +1,78 @@
+from tests.support.asserts import assert_success
+from tests.support.fixtures import is_element_in_viewport
+from tests.support.inline import inline
+
+
+def send_keys_to_element(session, element, text):
+    return session.transport.send(
+        "POST",
+        "/session/{session_id}/element/{element_id}/value".format(
+            session_id=session.session_id,
+            element_id=element.id),
+        {"text": text})
+
+
+def test_element_outside_of_not_scrollable_viewport(session):
+    session.url = inline("<input style=\"position: relative; left: -9999px;\">")
+    element = session.find.css("input", all=False)
+
+    response = send_keys_to_element(session, element, "foo")
+    assert_success(response)
+
+    assert not is_element_in_viewport(session, element)
+
+
+def test_element_outside_of_scrollable_viewport(session):
+    session.url = inline("<input style=\"margin-top: 102vh;\">")
+    element = session.find.css("input", all=False)
+
+    response = send_keys_to_element(session, element, "foo")
+    assert_success(response)
+
+    assert is_element_in_viewport(session, element)
+
+
+def test_option_select_container_outside_of_scrollable_viewport(session):
+    session.url = inline("""
+        <select style="margin-top: 102vh;">
+          <option value="foo">foo</option>
+          <option value="bar" id="bar">bar</option>
+        </select>
+    """)
+    element = session.find.css("option#bar", all=False)
+    select = session.find.css("select", all=False)
+
+    response = send_keys_to_element(session, element, "bar")
+    assert_success(response)
+
+    assert is_element_in_viewport(session, select)
+    assert is_element_in_viewport(session, element)
+
+
+def test_option_stays_outside_of_scrollable_viewport(session):
+    session.url = inline("""
+        <select multiple style="height: 105vh; margin-top: 100vh;">
+          <option value="foo" id="foo" style="height: 100vh;">foo</option>
+          <option value="bar" id="bar" style="background-color: yellow;">bar</option>
+        </select>
+    """)
+    select = session.find.css("select", all=False)
+    option_foo = session.find.css("option#foo", all=False)
+    option_bar = session.find.css("option#bar", all=False)
+
+    response = send_keys_to_element(session, option_bar, "bar")
+    assert_success(response)
+
+    assert is_element_in_viewport(session, select)
+    assert is_element_in_viewport(session, option_foo)
+    assert not is_element_in_viewport(session, option_bar)
+
+
+def test_contenteditable_element_outside_of_scrollable_viewport(session):
+    session.url = inline("<div contenteditable style=\"margin-top: 102vh;\"></div>")
+    element = session.find.css("div", all=False)
+
+    response = send_keys_to_element(session, element, "foo")
+    assert_success(response)
+
+    assert is_element_in_viewport(session, element)
diff --git a/WebDriverTests/imported/w3c/webdriver/tests/execute_script/cyclic.py b/WebDriverTests/imported/w3c/webdriver/tests/execute_script/cyclic.py
new file mode 100644 (file)
index 0000000..ae9d2f5
--- /dev/null
@@ -0,0 +1,48 @@
+from tests.support.asserts import assert_error
+
+
+def execute_script(session, script, args=None):
+    if args is None:
+        args = []
+    body = {"script": script, "args": args}
+    return session.transport.send(
+        "POST",
+        "/session/{session_id}/execute/sync".format(
+            session_id=session.session_id),
+        body)
+
+
+def test_array(session):
+    response = execute_script(session, """
+        let arr = [];
+        arr.push(arr);
+        return arr;
+        """)
+    assert_error(response, "javascript error")
+
+
+def test_object(session):
+    response = execute_script(session, """
+        let obj = {};
+        obj.reference = obj;
+        return obj;
+        """)
+    assert_error(response, "javascript error")
+
+
+def test_array_in_object(session):
+    response = execute_script(session, """
+        let arr = [];
+        arr.push(arr);
+        return {arr};
+        """)
+    assert_error(response, "javascript error")
+
+
+def test_object_in_array(session):
+    response = execute_script(session, """
+        let obj = {};
+        obj.reference = obj;
+        return [obj];
+        """)
+    assert_error(response, "javascript error")
index b25ca28..cb3398e 100644 (file)
@@ -151,11 +151,10 @@ def test_payload(session):
     assert response.status == 200
     assert isinstance(response.body["value"], dict)
     value = response.body["value"]
-    assert "width" in value
-    assert "height" in value
-    assert "x" in value
-    assert "y" in value
-    assert isinstance(value["width"], int)
-    assert isinstance(value["height"], int)
-    assert isinstance(value["x"], int)
-    assert isinstance(value["y"], int)
+    expected = session.execute_script("""return {
+         x: window.screenX,
+         y: window.screenY,
+         width: window.outerWidth,
+         height: window.outerHeight
+    }""")
+    assert expected == value
index 143a864..3163728 100644 (file)
@@ -1,15 +1,38 @@
-<!doctype html>
-<meta charset=utf-8>
-<title>WebDriver interface test</title>
-<script src=/resources/testharness.js></script>
-<script src=/resources/testharnessreport.js></script>
+<!DOCTYPE html>
+<body>
+<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 type=text/plain class=untested>
+[Exposed=Window]
+interface Navigator {
+  // objects implementing this interface also implement the interfaces given below
+};
+</script>
+<script type=text/plain>
+Navigator includes NavigatorAutomationInformation;
 
+interface mixin NavigatorAutomationInformation {
+    readonly attribute boolean webdriver;
+    // always returns true
+};
+</script>
 <script>
-var t = new IdlArray();
-t.add_untested_idls("interface Navigator {};");
-t.add_idls("partial interface Navigator { readonly attribute boolean webdriver; };");
-t.add_objects({Navigator: ["navigator"]});
-t.test();
+"use strict";
+
+if ("webdriver" in navigator) {
+  test(() => assert_true(navigator.webdriver), "navigator.webdriver is always true");
+  var idlArray = new IdlArray();
+  [].forEach.call(document.querySelectorAll("script[type=text\\/plain]"), function(node) {
+    if (node.className == "untested") {
+      idlArray.add_untested_idls(node.textContent);
+    } else {
+      idlArray.add_idls(node.textContent);
+    }
+  });
+  idlArray.test();
+} else {
+  done();
+}
 </script>
index 45e462f..b3dfe1f 100644 (file)
@@ -4,6 +4,7 @@ import types
 
 from tests.support.inline import inline
 from tests.support.asserts import assert_error, assert_success
+from tests.support.wait import wait
 
 alert_doc = inline("<script>window.alert()</script>")
 frame_doc = inline("<p>frame")
@@ -75,8 +76,11 @@ def test_set_malformed_url(session):
     assert_error(result, "invalid argument")
 
 def test_get_current_url_after_modified_location(session):
+    start = session.transport.send("GET", "session/%s/url" % session.session_id)
     session.execute_script("window.location.href = 'about:blank#wd_test_modification'")
-
+    wait(session,
+         lambda s: s.transport.send("GET", "session/%s/url" % session.session_id) != start.body["value"],
+         "URL did not change")
     result = session.transport.send("GET", "session/%s/url" % session.session_id)
 
     assert_success(result, "about:blank#wd_test_modification")
index 4174791..95859ac 100644 (file)
@@ -1,5 +1,6 @@
 from webdriver import Element, WebDriverException
 
+
 # WebDriver specification ID: dfn-error-response-data
 errors = {
     "element click intercepted": 400,
@@ -32,6 +33,7 @@ errors = {
     "unsupported operation": 500,
 }
 
+
 # WebDriver specification ID: dfn-send-an-error
 #
 # > When required to send an error, with error code, a remote end must run the
@@ -98,7 +100,7 @@ def assert_dialog_handled(session, expected_text):
     except:
         assert (result.status == 200 and
                 result.body["value"] != expected_text), (
-               "Dialog with text '%s' was not handled." % expected_text)
+            "Dialog with text '%s' was not handled." % expected_text)
 
 
 def assert_same_element(session, a, b):
@@ -123,7 +125,7 @@ def assert_same_element(session, a, b):
         return
 
     message = ("Expected element references to describe the same element, " +
-        "but they did not.")
+               "but they did not.")
 
     # Attempt to provide more information, accounting for possible errors such
     # as stale element references or not visible elements.
index 8da0d5c..844ae03 100644 (file)
@@ -267,3 +267,19 @@ def create_dialog(session):
 def clear_all_cookies(session):
     """Removes all cookies associated with the current active document"""
     session.transport.send("DELETE", "session/%s/cookie" % session.session_id)
+
+
+def is_element_in_viewport(session, element):
+    """Check if element is outside of the viewport"""
+    return session.execute_script("""
+        let el = arguments[0];
+
+        let rect = el.getBoundingClientRect();
+        let viewport = {
+          height: window.innerHeight || document.documentElement.clientHeight,
+          width: window.innerWidth || document.documentElement.clientWidth,
+        };
+
+        return !(rect.right < 0 || rect.bottom < 0 ||
+            rect.left > viewport.width || rect.top > viewport.height)
+    """, args=(element,))