Unreviewed. Update W3C WebDriver imported tests.
authorcarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 21 Nov 2017 13:35:37 +0000 (13:35 +0000)
committercarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 21 Nov 2017 13:35:37 +0000 (13:35 +0000)
* imported/w3c/importer.json:
* imported/w3c/tools/webdriver/webdriver/client.py:
* imported/w3c/tools/webdriver/webdriver/error.py:
* imported/w3c/tools/webdriver/webdriver/protocol.py: Added.
* imported/w3c/tools/webdriver/webdriver/transport.py:
* imported/w3c/tools/wptrunner/MANIFEST.in:
* imported/w3c/tools/wptrunner/requirements_firefox.txt:
* imported/w3c/tools/wptrunner/requirements_opera.txt: Added.
* imported/w3c/tools/wptrunner/wptrunner/browsers/__init__.py:
* imported/w3c/tools/wptrunner/wptrunner/browsers/edge.py:
* imported/w3c/tools/wptrunner/wptrunner/browsers/firefox.py:
* imported/w3c/tools/wptrunner/wptrunner/browsers/opera.py: Added.
* imported/w3c/tools/wptrunner/wptrunner/environment.py:
* imported/w3c/tools/wptrunner/wptrunner/executors/base.py:
* imported/w3c/tools/wptrunner/wptrunner/executors/executoredge.py: Added.
* imported/w3c/tools/wptrunner/wptrunner/executors/executormarionette.py:
* imported/w3c/tools/wptrunner/wptrunner/executors/executoropera.py: Added.
* imported/w3c/tools/wptrunner/wptrunner/executors/executorselenium.py:
* imported/w3c/tools/wptrunner/wptrunner/executors/pytestrunner/runner.py:
* imported/w3c/tools/wptrunner/wptrunner/executors/testharness_webdriver.js:
* imported/w3c/tools/wptrunner/wptrunner/executors/testharness_webdriver_resume.js: Added.
* imported/w3c/tools/wptrunner/wptrunner/stability.py: Added.
* imported/w3c/tools/wptrunner/wptrunner/testdriver-extra.js: Added.
* imported/w3c/tools/wptrunner/wptrunner/testloader.py:
* imported/w3c/tools/wptrunner/wptrunner/testrunner.py:
* imported/w3c/tools/wptrunner/wptrunner/update/state.py:
* imported/w3c/tools/wptrunner/wptrunner/update/sync.py:
* imported/w3c/tools/wptrunner/wptrunner/webdriver_server.py:
* imported/w3c/tools/wptrunner/wptrunner/wptcommandline.py:
* imported/w3c/tools/wptrunner/wptrunner/wptlogging.py:
* imported/w3c/tools/wptrunner/wptrunner/wptrunner.py:
* imported/w3c/tools/wptrunner/wptrunner/wpttest.py:
* imported/w3c/webdriver/tests/actions/conftest.py:
* imported/w3c/webdriver/tests/actions/key.py:
* imported/w3c/webdriver/tests/actions/key_shortcuts.py: Added.
* imported/w3c/webdriver/tests/actions/modifier_click.py: Added.
* imported/w3c/webdriver/tests/actions/mouse.py:
* imported/w3c/webdriver/tests/actions/sequence.py:
* imported/w3c/webdriver/tests/actions/special_keys.py:
* imported/w3c/webdriver/tests/actions/support/keys.py:
* imported/w3c/webdriver/tests/actions/support/test_actions_wdspec.html:
* imported/w3c/webdriver/tests/cookies/add_cookie.py:
* imported/w3c/webdriver/tests/cookies/get_named_cookie.py:
* imported/w3c/webdriver/tests/element_click/stale.py: Added.
* imported/w3c/webdriver/tests/element_retrieval/get_active_element.py:
* imported/w3c/webdriver/tests/interaction/element_clear.py: Added.
* imported/w3c/webdriver/tests/navigation/get_title.py:
* imported/w3c/webdriver/tests/retrieval/find_element.py:
* imported/w3c/webdriver/tests/retrieval/find_element_from_element.py:
* imported/w3c/webdriver/tests/retrieval/find_element_from_elements.py:
* imported/w3c/webdriver/tests/retrieval/find_elements.py:
* imported/w3c/webdriver/tests/sessions/new_session/support/__init__.py:
* imported/w3c/webdriver/tests/state/get_element_attribute.py:
* imported/w3c/webdriver/tests/state/get_element_property.py:
* imported/w3c/webdriver/tests/state/get_element_tag_name.py:
* imported/w3c/webdriver/tests/state/is_element_selected.py:
* imported/w3c/webdriver/tests/support/asserts.py:
* imported/w3c/webdriver/tests/user_prompts/send_alert_text.py: Added.

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

59 files changed:
WebDriverTests/ChangeLog
WebDriverTests/imported/w3c/importer.json
WebDriverTests/imported/w3c/tools/webdriver/webdriver/client.py
WebDriverTests/imported/w3c/tools/webdriver/webdriver/error.py
WebDriverTests/imported/w3c/tools/webdriver/webdriver/protocol.py [new file with mode: 0644]
WebDriverTests/imported/w3c/tools/webdriver/webdriver/transport.py
WebDriverTests/imported/w3c/tools/wptrunner/MANIFEST.in
WebDriverTests/imported/w3c/tools/wptrunner/requirements_firefox.txt
WebDriverTests/imported/w3c/tools/wptrunner/requirements_opera.txt [new file with mode: 0644]
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/__init__.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/edge.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/firefox.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/opera.py [new file with mode: 0644]
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/environment.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/base.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executoredge.py [new file with mode: 0644]
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executormarionette.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executoropera.py [new file with mode: 0644]
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executorselenium.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/pytestrunner/runner.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/testharness_webdriver.js
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/testharness_webdriver_resume.js [new file with mode: 0644]
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/stability.py [new file with mode: 0644]
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testdriver-extra.js [new file with mode: 0644]
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testloader.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testrunner.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/update/state.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/update/sync.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/webdriver_server.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptcommandline.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptlogging.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptrunner.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wpttest.py
WebDriverTests/imported/w3c/webdriver/tests/actions/conftest.py
WebDriverTests/imported/w3c/webdriver/tests/actions/key.py
WebDriverTests/imported/w3c/webdriver/tests/actions/key_shortcuts.py [new file with mode: 0644]
WebDriverTests/imported/w3c/webdriver/tests/actions/modifier_click.py [new file with mode: 0644]
WebDriverTests/imported/w3c/webdriver/tests/actions/mouse.py
WebDriverTests/imported/w3c/webdriver/tests/actions/sequence.py
WebDriverTests/imported/w3c/webdriver/tests/actions/special_keys.py
WebDriverTests/imported/w3c/webdriver/tests/actions/support/keys.py
WebDriverTests/imported/w3c/webdriver/tests/actions/support/test_actions_wdspec.html
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_click/stale.py [new file with mode: 0644]
WebDriverTests/imported/w3c/webdriver/tests/element_retrieval/get_active_element.py
WebDriverTests/imported/w3c/webdriver/tests/interaction/element_clear.py [new file with mode: 0644]
WebDriverTests/imported/w3c/webdriver/tests/navigation/get_title.py
WebDriverTests/imported/w3c/webdriver/tests/retrieval/find_element.py
WebDriverTests/imported/w3c/webdriver/tests/retrieval/find_element_from_element.py
WebDriverTests/imported/w3c/webdriver/tests/retrieval/find_element_from_elements.py
WebDriverTests/imported/w3c/webdriver/tests/retrieval/find_elements.py
WebDriverTests/imported/w3c/webdriver/tests/sessions/new_session/support/__init__.py
WebDriverTests/imported/w3c/webdriver/tests/state/get_element_attribute.py
WebDriverTests/imported/w3c/webdriver/tests/state/get_element_property.py
WebDriverTests/imported/w3c/webdriver/tests/state/get_element_tag_name.py
WebDriverTests/imported/w3c/webdriver/tests/state/is_element_selected.py
WebDriverTests/imported/w3c/webdriver/tests/support/asserts.py
WebDriverTests/imported/w3c/webdriver/tests/user_prompts/send_alert_text.py [new file with mode: 0644]

index ba81d7f..a2fd1ff 100644 (file)
@@ -1,3 +1,66 @@
+2017-11-21  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        Unreviewed. Update W3C WebDriver imported tests.
+
+        * imported/w3c/importer.json:
+        * imported/w3c/tools/webdriver/webdriver/client.py:
+        * imported/w3c/tools/webdriver/webdriver/error.py:
+        * imported/w3c/tools/webdriver/webdriver/protocol.py: Added.
+        * imported/w3c/tools/webdriver/webdriver/transport.py:
+        * imported/w3c/tools/wptrunner/MANIFEST.in:
+        * imported/w3c/tools/wptrunner/requirements_firefox.txt:
+        * imported/w3c/tools/wptrunner/requirements_opera.txt: Added.
+        * imported/w3c/tools/wptrunner/wptrunner/browsers/__init__.py:
+        * imported/w3c/tools/wptrunner/wptrunner/browsers/edge.py:
+        * imported/w3c/tools/wptrunner/wptrunner/browsers/firefox.py:
+        * imported/w3c/tools/wptrunner/wptrunner/browsers/opera.py: Added.
+        * imported/w3c/tools/wptrunner/wptrunner/environment.py:
+        * imported/w3c/tools/wptrunner/wptrunner/executors/base.py:
+        * imported/w3c/tools/wptrunner/wptrunner/executors/executoredge.py: Added.
+        * imported/w3c/tools/wptrunner/wptrunner/executors/executormarionette.py:
+        * imported/w3c/tools/wptrunner/wptrunner/executors/executoropera.py: Added.
+        * imported/w3c/tools/wptrunner/wptrunner/executors/executorselenium.py:
+        * imported/w3c/tools/wptrunner/wptrunner/executors/pytestrunner/runner.py:
+        * imported/w3c/tools/wptrunner/wptrunner/executors/testharness_webdriver.js:
+        * imported/w3c/tools/wptrunner/wptrunner/executors/testharness_webdriver_resume.js: Added.
+        * imported/w3c/tools/wptrunner/wptrunner/stability.py: Added.
+        * imported/w3c/tools/wptrunner/wptrunner/testdriver-extra.js: Added.
+        * imported/w3c/tools/wptrunner/wptrunner/testloader.py:
+        * imported/w3c/tools/wptrunner/wptrunner/testrunner.py:
+        * imported/w3c/tools/wptrunner/wptrunner/update/state.py:
+        * imported/w3c/tools/wptrunner/wptrunner/update/sync.py:
+        * imported/w3c/tools/wptrunner/wptrunner/webdriver_server.py:
+        * imported/w3c/tools/wptrunner/wptrunner/wptcommandline.py:
+        * imported/w3c/tools/wptrunner/wptrunner/wptlogging.py:
+        * imported/w3c/tools/wptrunner/wptrunner/wptrunner.py:
+        * imported/w3c/tools/wptrunner/wptrunner/wpttest.py:
+        * imported/w3c/webdriver/tests/actions/conftest.py:
+        * imported/w3c/webdriver/tests/actions/key.py:
+        * imported/w3c/webdriver/tests/actions/key_shortcuts.py: Added.
+        * imported/w3c/webdriver/tests/actions/modifier_click.py: Added.
+        * imported/w3c/webdriver/tests/actions/mouse.py:
+        * imported/w3c/webdriver/tests/actions/sequence.py:
+        * imported/w3c/webdriver/tests/actions/special_keys.py:
+        * imported/w3c/webdriver/tests/actions/support/keys.py:
+        * imported/w3c/webdriver/tests/actions/support/test_actions_wdspec.html:
+        * imported/w3c/webdriver/tests/cookies/add_cookie.py:
+        * imported/w3c/webdriver/tests/cookies/get_named_cookie.py:
+        * imported/w3c/webdriver/tests/element_click/stale.py: Added.
+        * imported/w3c/webdriver/tests/element_retrieval/get_active_element.py:
+        * imported/w3c/webdriver/tests/interaction/element_clear.py: Added.
+        * imported/w3c/webdriver/tests/navigation/get_title.py:
+        * imported/w3c/webdriver/tests/retrieval/find_element.py:
+        * imported/w3c/webdriver/tests/retrieval/find_element_from_element.py:
+        * imported/w3c/webdriver/tests/retrieval/find_element_from_elements.py:
+        * imported/w3c/webdriver/tests/retrieval/find_elements.py:
+        * imported/w3c/webdriver/tests/sessions/new_session/support/__init__.py:
+        * imported/w3c/webdriver/tests/state/get_element_attribute.py:
+        * imported/w3c/webdriver/tests/state/get_element_property.py:
+        * imported/w3c/webdriver/tests/state/get_element_tag_name.py:
+        * imported/w3c/webdriver/tests/state/is_element_selected.py:
+        * imported/w3c/webdriver/tests/support/asserts.py:
+        * imported/w3c/webdriver/tests/user_prompts/send_alert_text.py: Added.
+
 2017-09-21  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         WebDriver: Add support to import and run W3C tests
 2017-09-21  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         WebDriver: Add support to import and run W3C tests
index 78359fc..fea3874 100644 (file)
@@ -1,6 +1,6 @@
 {
     "repository": "https://github.com/w3c/web-platform-tests.git",
 {
     "repository": "https://github.com/w3c/web-platform-tests.git",
-    "revision": "09c7045152fc66f9ffe8328ba0ebda20a7e1fe90",
+    "revision": "448984b0e1c5bad4af20abeaf84eb8b5e8e81478",
     "paths_to_import": [
         "tools/pytest",
         "tools/webdriver",
     "paths_to_import": [
         "tools/pytest",
         "tools/webdriver",
index eb0734b..3f54156 100644 (file)
@@ -1,15 +1,13 @@
-import json
 import urlparse
 
 import error
 import urlparse
 
 import error
+import protocol
 import transport
 
 from mozlog import get_default_logger
 
 logger = get_default_logger()
 
 import transport
 
 from mozlog import get_default_logger
 
 logger = get_default_logger()
 
-element_key = "element-6066-11e4-a52e-4f735466cecf"
-
 
 def command(func):
     def inner(self, *args, **kwargs):
 
 def command(func):
     def inner(self, *args, **kwargs):
@@ -147,7 +145,7 @@ class ActionSequence(object):
         if duration is not None:
             action["duration"] = duration
         if origin is not None:
         if duration is not None:
             action["duration"] = duration
         if origin is not None:
-            action["origin"] = origin if isinstance(origin, basestring) else origin.json()
+            action["origin"] = origin
         self._actions.append(action)
         return self
 
         self._actions.append(action)
         return self
 
@@ -299,18 +297,9 @@ class Find(object):
 
     def _find_element(self, strategy, selector, all):
         route = "elements" if all else "element"
 
     def _find_element(self, strategy, selector, all):
         route = "elements" if all else "element"
-
         body = {"using": strategy,
                 "value": selector}
         body = {"using": strategy,
                 "value": selector}
-
-        data = self.session.send_session_command("POST", route, body)
-
-        if all:
-            rv = [self.session._element(item) for item in data]
-        else:
-            rv = self.session._element(data)
-
-        return rv
+        return self.session.send_session_command("POST", route, body)
 
 
 class Cookies(object):
 
 
 class Cookies(object):
@@ -356,8 +345,13 @@ class UserPrompt(object):
 
 
 class Session(object):
 
 
 class Session(object):
-    def __init__(self, host, port, url_prefix="/", capabilities=None,
-                 timeout=None, extension=None):
+    def __init__(self,
+                 host,
+                 port,
+                 url_prefix="/",
+                 capabilities=None,
+                 timeout=None,
+                 extension=None):
         self.transport = transport.HTTPWireProtocol(
             host, port, url_prefix, timeout=timeout)
         self.capabilities = capabilities
         self.transport = transport.HTTPWireProtocol(
             host, port, url_prefix, timeout=timeout)
         self.capabilities = capabilities
@@ -375,6 +369,10 @@ class Session(object):
         self.alert = UserPrompt(self)
         self.actions = Actions(self)
 
         self.alert = UserPrompt(self)
         self.actions = Actions(self)
 
+    def __eq__(self, other):
+        return (self.session_id is not None and isinstance(other, Session) and
+                self.session_id == other.session_id)
+
     def __enter__(self):
         self.start()
         return self
     def __enter__(self):
         self.start()
         return self
@@ -422,22 +420,36 @@ class Session(object):
         :param body: Optional body of the HTTP request.
 
         :return: `None` if the HTTP response body was empty, otherwise
         :param body: Optional body of the HTTP request.
 
         :return: `None` if the HTTP response body was empty, otherwise
-            the result of parsing the body as JSON.
+            the `value` field returned after parsing the response
+            body as JSON.
 
 
+        :raises ValueError: If the response body does not contain a
+            `value` key.
         :raises error.WebDriverException: If the remote end returns
             an error.
         """
         :raises error.WebDriverException: If the remote end returns
             an error.
         """
-        response = self.transport.send(method, url, body)
+        response = self.transport.send(
+            method, url, body,
+            encoder=protocol.Encoder, decoder=protocol.Decoder,
+            session=self)
+
+        if response.status != 200:
+            raise error.from_response(response)
 
         if "value" in response.body:
             value = response.body["value"]
 
         if "value" in response.body:
             value = response.body["value"]
+            """
+            Edge does not yet return the w3c session ID.
+            We want the tests to run in Edge anyway to help with REC.
+            In order to run the tests in Edge, we need to hack around
+            bug:
+            https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/14641972
+            """
+            if url == "session" and method == "POST" and "sessionId" in response.body and "sessionId" not in value:
+                value["sessionId"] = response.body["sessionId"]
         else:
         else:
-            raise error.UnknownErrorException("No 'value' key in response body:\n%s" %
-                                              json.dumps(response.body))
-
-        if response.status != 200:
-            cls = error.get(value.get("error"))
-            raise cls(value.get("message"))
+            raise ValueError("Expected 'value' key in response body:\n"
+                "%s" % response)
 
         return value
 
 
         return value
 
@@ -512,10 +524,7 @@ class Session(object):
             body = None
         else:
             url = "frame"
             body = None
         else:
             url = "frame"
-            if isinstance(frame, Element):
-                body = {"id": frame.json()}
-            else:
-                body = {"id": frame}
+            body = {"id": frame}
 
         return self.send_session_command("POST", url, body)
 
 
         return self.send_session_command("POST", url, body)
 
@@ -531,16 +540,7 @@ class Session(object):
     @property
     @command
     def active_element(self):
     @property
     @command
     def active_element(self):
-        data = self.send_session_command("GET", "element/active")
-        if data is not None:
-            return self._element(data)
-
-    def _element(self, data):
-        elem_id = data[element_key]
-        assert elem_id
-        if elem_id in self._element_cache:
-            return self._element_cache[elem_id]
-        return Element(self, elem_id)
+        return self.send_session_command("GET", "element/active")
 
     @command
     def cookies(self, name=None):
 
     @command
     def cookies(self, name=None):
@@ -603,26 +603,49 @@ class Session(object):
 
 
 class Element(object):
 
 
 class Element(object):
-    def __init__(self, session, id):
-        self.session = session
+    """
+    Representation of a web element.
+
+    A web element is an abstraction used to identify an element when
+    it is transported via the protocol, between remote- and local ends.
+    """
+    identifier = "element-6066-11e4-a52e-4f735466cecf"
+
+    def __init__(self, id, session):
+        """
+        Construct a new web element representation.
+
+        :param id: Web element UUID which must be unique across
+            all browsing contexts.
+        :param session: Current ``webdriver.Session``.
+        """
         self.id = id
         self.id = id
+        self.session = session
+
         assert id not in self.session._element_cache
         self.session._element_cache[self.id] = self
 
         assert id not in self.session._element_cache
         self.session._element_cache[self.id] = self
 
+    def __eq__(self, other):
+        return (isinstance(other, Element) and self.id == other.id and
+                self.session == other.session)
+
+    @classmethod
+    def from_json(cls, json, session):
+        assert Element.identifier in json
+        uuid = json[Element.identifier]
+        if uuid in session._element_cache:
+            return session._element_cache[uuid]
+        return cls(uuid, session)
+
     def send_element_command(self, method, uri, body=None):
         url = "element/%s/%s" % (self.id, uri)
         return self.session.send_session_command(method, url, body)
 
     def send_element_command(self, method, uri, body=None):
         url = "element/%s/%s" % (self.id, uri)
         return self.session.send_session_command(method, url, body)
 
-    def json(self):
-        return {element_key: self.id}
-
     @command
     def find_element(self, strategy, selector):
         body = {"using": strategy,
                 "value": selector}
     @command
     def find_element(self, strategy, selector):
         body = {"using": strategy,
                 "value": selector}
-
-        elem = self.send_element_command("POST", "element", body)
-        return self.session._element(elem)
+        return self.send_element_command("POST", "element", body)
 
     @command
     def click(self):
 
     @command
     def click(self):
index 43e9f39..69f6e45 100644 (file)
@@ -1,10 +1,25 @@
 import collections
 import collections
+import json
 
 
 class WebDriverException(Exception):
     http_status = None
     status_code = None
 
 
 
 class WebDriverException(Exception):
     http_status = None
     status_code = None
 
+    def __init__(self, message, stacktrace=None):
+        super(WebDriverException, self)
+        self.stacktrace = stacktrace
+
+    def __repr__(self):
+        return "<%s http_status=%d>" % (self.__class__.__name__, self.http_status)
+
+    def __str__(self):
+        return ("%s (%d)\n"
+            "\n"
+            "Remote-end stacktrace:\n"
+            "\n"
+            "%s" % (self.status_code, self.http_status, self.stacktrace))
+
 
 class ElementNotSelectableException(WebDriverException):
     http_status = 400
 
 class ElementNotSelectableException(WebDriverException):
     http_status = 400
@@ -38,7 +53,7 @@ class InvalidElementCoordinatesException(WebDriverException):
 
 class InvalidElementStateException(WebDriverException):
     http_status = 400
 
 class InvalidElementStateException(WebDriverException):
     http_status = 400
-    status_code = "invalid cookie domain"
+    status_code = "invalid element state"
 
 
 class InvalidSelectorException(WebDriverException):
 
 
 class InvalidSelectorException(WebDriverException):
@@ -92,7 +107,7 @@ class SessionNotCreatedException(WebDriverException):
 
 
 class StaleElementReferenceException(WebDriverException):
 
 
 class StaleElementReferenceException(WebDriverException):
-    http_status = 400
+    http_status = 404
     status_code = "stale element reference"
 
 
     status_code = "stale element reference"
 
 
@@ -131,11 +146,40 @@ class UnsupportedOperationException(WebDriverException):
     status_code = "unsupported operation"
 
 
     status_code = "unsupported operation"
 
 
-def get(status_code):
-    """Gets exception from `status_code`, falling back to
+def from_response(response):
+    """
+    Unmarshals an error from a ``Response``'s `body`, failing
+    if not all three required `error`, `message`, and `stacktrace`
+    fields are given.  Defaults to ``WebDriverException`` if `error`
+    is unknown.
+    """
+    if response.status == 200:
+        raise UnknownErrorException(
+            "Response is not an error:\n"
+            "%s" % json.dumps(response.body))
+
+    if "value" in response.body:
+        value = response.body["value"]
+    else:
+        raise UnknownErrorException(
+            "Expected 'value' key in response body:\n"
+            "%s" % json.dumps(response.body))
+
+    # all fields must exist, but stacktrace can be an empty string
+    code = value["error"]
+    message = value["message"]
+    stack = value["stacktrace"] or None
+
+    cls = get(code)
+    return cls(message, stacktrace=stack)
+
+
+def get(error_code):
+    """
+    Gets exception from `error_code`, falling back to
     ``WebDriverException`` if it is not found.
     """
     ``WebDriverException`` if it is not found.
     """
-    return _errors.get(status_code, WebDriverException)
+    return _errors.get(error_code, WebDriverException)
 
 
 _errors = collections.defaultdict()
 
 
 _errors = collections.defaultdict()
diff --git a/WebDriverTests/imported/w3c/tools/webdriver/webdriver/protocol.py b/WebDriverTests/imported/w3c/tools/webdriver/webdriver/protocol.py
new file mode 100644 (file)
index 0000000..80f358c
--- /dev/null
@@ -0,0 +1,35 @@
+import json
+
+import webdriver
+
+
+"""WebDriver wire protocol codecs."""
+
+
+class Encoder(json.JSONEncoder):
+    def __init__(self, *args, **kwargs):
+        kwargs.pop("session")
+        super(Encoder, self).__init__(*args, **kwargs)
+
+    def default(self, obj):
+        if isinstance(obj, (list, tuple)):
+            return [self.default(x) for x in obj]
+        elif isinstance(obj, webdriver.Element):
+            return {webdriver.Element.identifier: obj.id}
+        return super(ProtocolEncoder, self).default(obj)
+
+
+class Decoder(json.JSONDecoder):
+    def __init__(self, *args, **kwargs):
+        self.session = kwargs.pop("session")
+        super(Decoder, self).__init__(
+            object_hook=self.object_hook, *args, **kwargs)
+
+    def object_hook(self, payload):
+        if isinstance(payload, (list, tuple)):
+            return [self.object_hook(x) for x in payload]
+        elif isinstance(payload, dict) and webdriver.Element.identifier in payload:
+            return webdriver.Element.from_json(payload, self.session)
+        elif isinstance(payload, dict):
+            return {k: self.object_hook(v) for k, v in payload.iteritems()}
+        return payload
index cc07b91..b198b19 100644 (file)
@@ -4,58 +4,79 @@ import urlparse
 
 import error
 
 
 import error
 
+
+"""Implements HTTP transport for the WebDriver wire protocol."""
+
+
 class Response(object):
 class Response(object):
-    """Describes an HTTP response received from a remote en"Describes an HTTP
-    response received from a remote end whose body has been read and parsed as
-    appropriate."""
+    """
+    Describes an HTTP response received from a remote end whose
+    body has been read and parsed as appropriate.
+    """
+
     def __init__(self, status, body):
         self.status = status
         self.body = body
 
     def __repr__(self):
     def __init__(self, status, body):
         self.status = status
         self.body = body
 
     def __repr__(self):
-        return "wdclient.Response(status=%d, body=%s)" % (self.status, self.body)
+        cls_name = self.__class__.__name__
+        if self.error:
+            return "<%s status=%s error=%s>" % (cls_name, self.status, repr(self.error))
+        return "<% status=%s body=%s>" % (cls_name, self.status, json.dumps(self.body))
 
 
-    @classmethod
-    def from_http_response(cls, http_response):
-        status = http_response.status
-        body = http_response.read()
-
-        # SpecID: dfn-send-a-response
-        #
-        # > 3. Set the response's header with name and value with the following
-        # >    values:
-        # >
-        # >    "Content-Type"
-        # >       "application/json; charset=utf-8"
-        # >    "cache-control"
-        # >       "no-cache"
-
-        if body:
-            try:
-                body = json.loads(body)
-            except:
-                raise error.UnknownErrorException("Failed to decode body as json:\n%s" % body)
+    def __str__(self):
+        return json.dumps(self.body, indent=2)
 
 
-        return cls(status, body)
+    @property
+    def error(self):
+        if self.status != 200:
+            return error.from_response(self)
+        return None
 
 
+    @classmethod
+    def from_http(cls, http_response, decoder=json.JSONDecoder, **kwargs):
+        try:
+            body = json.load(http_response, cls=decoder, **kwargs)
+        except ValueError:
+            raise ValueError("Failed to decode response body as JSON:\n"
+                "%s" % json.dumps(body, indent=2))
 
 
-class ToJsonEncoder(json.JSONEncoder):
-    def default(self, obj):
-        return getattr(obj.__class__, "json", json.JSONEncoder().default)(obj)
+        return cls(http_response.status, body)
 
 
 class HTTPWireProtocol(object):
 
 
 class HTTPWireProtocol(object):
-    """Transports messages (commands and responses) over the WebDriver
+    """
+    Transports messages (commands and responses) over the WebDriver
     wire protocol.
     wire protocol.
+
+    Complex objects, such as ``webdriver.Element``, are by default
+    not marshaled to enable use of `session.transport.send` in WPT tests::
+
+        session = webdriver.Session("127.0.0.1", 4444)
+        response = transport.send("GET", "element/active", None)
+        print response.body["value"]
+        # => {u'element-6066-11e4-a52e-4f735466cecf': u'<uuid>'}
+
+    Automatic marshaling is provided by ``webdriver.protocol.Encoder``
+    and ``webdriver.protocol.Decoder``, which can be passed in to
+    ``HTTPWireProtocol.send`` along with a reference to the current
+    ``webdriver.Session``::
+
+        session = webdriver.Session("127.0.0.1", 4444)
+        response = transport.send("GET", "element/active", None,
+            encoder=protocol.Encoder, decoder=protocol.Decoder,
+            session=session)
+        print response.body["value"]
+        # => webdriver.Element
     """
 
     def __init__(self, host, port, url_prefix="/", timeout=None):
     """
 
     def __init__(self, host, port, url_prefix="/", timeout=None):
-        """Construct interface for communicating with the remote server.
+        """
+        Construct interface for communicating with the remote server.
 
         :param url: URL of remote WebDriver server.
         :param wait: Duration to wait for remote to appear.
         """
 
         :param url: URL of remote WebDriver server.
         :param wait: Duration to wait for remote to appear.
         """
-
         self.host = host
         self.port = port
         self.url_prefix = url_prefix
         self.host = host
         self.port = port
         self.url_prefix = url_prefix
@@ -65,43 +86,74 @@ class HTTPWireProtocol(object):
     def url(self, suffix):
         return urlparse.urljoin(self.url_prefix, suffix)
 
     def url(self, suffix):
         return urlparse.urljoin(self.url_prefix, suffix)
 
-    def send(self, method, uri, body=None, headers=None):
-        """Send a command to the remote.
+    def send(self,
+             method,
+             uri,
+             body=None,
+             headers=None,
+             encoder=json.JSONEncoder,
+             decoder=json.JSONDecoder,
+             **codec_kwargs):
+        """
+        Send a command to the remote.
+
+        The request `body` must be JSON serialisable unless a
+        custom `encoder` has been provided.  This means complex
+        objects such as ``webdriver.Element`` are not automatically
+        made into JSON.  This behaviour is, however, provided by
+        ``webdriver.protocol.Encoder``, should you want it.
+
+        Similarly, the response body is returned au natural
+        as plain JSON unless a `decoder` that converts web
+        element references to ``webdriver.Element`` is provided.
+        Use ``webdriver.protocol.Decoder`` to achieve this behaviour.
 
         :param method: `GET`, `POST`, or `DELETE`.
         :param uri: Relative endpoint of the requests URL path.
         :param body: Body of the request.  Defaults to an empty
             dictionary if ``method`` is `POST`.
 
         :param method: `GET`, `POST`, or `DELETE`.
         :param uri: Relative endpoint of the requests URL path.
         :param body: Body of the request.  Defaults to an empty
             dictionary if ``method`` is `POST`.
-        :param headers: Additional headers to include in the request.
-
-        :return: Instance of ``wdclient.Response`` describing the
-            HTTP response received from the remote end.
-
+        :param headers: Additional dictionary of headers to include
+            in the request.
+        :param encoder: JSON encoder class, which defaults to
+            ``json.JSONEncoder`` unless specified.
+        :param decoder: JSON decoder class, which defaults to
+            ``json.JSONDecoder`` unless specified.
+        :param codec_kwargs: Surplus arguments passed on to `encoder`
+            and `decoder` on construction.
+
+        :return: Instance of ``webdriver.transport.Response``
+            describing the HTTP response received from the remote end.
+
+        :raises ValueError: If `body` or the response body are not
+            JSON serialisable.
         """
         if body is None and method == "POST":
             body = {}
 
         """
         if body is None and method == "POST":
             body = {}
 
-        if isinstance(body, dict):
-            body = json.dumps(body, cls=ToJsonEncoder)
-
-        if isinstance(body, unicode):
-            body = body.encode("utf-8")
+        try:
+            payload = json.dumps(body, cls=encoder, **codec_kwargs)
+        except ValueError:
+            raise ValueError("Failed to encode request body as JSON:\n"
+                "%s" % json.dumps(body, indent=2))
+        if isinstance(payload, unicode):
+            payload = body.encode("utf-8")
 
         if headers is None:
             headers = {}
 
         url = self.url(uri)
 
 
         if headers is None:
             headers = {}
 
         url = self.url(uri)
 
-        kwargs = {}
+        conn_kwargs = {}
         if self._timeout is not None:
         if self._timeout is not None:
-            kwargs["timeout"] = self._timeout
+            conn_kwargs["timeout"] = self._timeout
 
         conn = httplib.HTTPConnection(
 
         conn = httplib.HTTPConnection(
-            self.host, self.port, strict=True, **kwargs)
-        conn.request(method, url, body, headers)
+            self.host, self.port, strict=True, **conn_kwargs)
+        conn.request(method, url, payload, headers)
 
         try:
             response = conn.getresponse()
 
         try:
             response = conn.getresponse()
-            return Response.from_http_response(response)
+            return Response.from_http(
+                response, decoder=decoder, **codec_kwargs)
         finally:
             conn.close()
         finally:
             conn.close()
index 0c5e38b..2be4d91 100644 (file)
@@ -3,15 +3,7 @@ include requirements.txt
 include wptrunner/browsers/b2g_setup/*
 include wptrunner.default.ini
 include wptrunner/testharness_runner.html
 include wptrunner/browsers/b2g_setup/*
 include wptrunner.default.ini
 include wptrunner/testharness_runner.html
-include wptrunner/testharnessreport.js
-include wptrunner/testharnessreport-servo.js
-include wptrunner/testharnessreport-servodriver.js
-include wptrunner/executors/testharness_marionette.js
-include wptrunner/executors/testharness_servodriver.js
-include wptrunner/executors/testharness_webdriver.js
-include wptrunner/executors/reftest.js
-include wptrunner/executors/reftest-wait.js
-include wptrunner/executors/reftest-wait_servodriver.js
-include wptrunner/executors/reftest-wait_webdriver.js
+include wptrunner/*.js
+include wptrunner/executors/*.js
 include wptrunner/config.json
 include wptrunner/browsers/server-locations.txt
\ No newline at end of file
 include wptrunner/config.json
 include wptrunner/browsers/server-locations.txt
\ No newline at end of file
index d8d268f..44b7f5a 100644 (file)
@@ -1,4 +1,4 @@
-marionette_driver >= 0.4
+marionette_driver >= 2.4
 mozprofile >= 0.21
 mozprocess >= 0.19
 mozcrash >= 0.13
 mozprofile >= 0.21
 mozprocess >= 0.19
 mozcrash >= 0.13
diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/requirements_opera.txt b/WebDriverTests/imported/w3c/tools/wptrunner/requirements_opera.txt
new file mode 100644 (file)
index 0000000..a2f5442
--- /dev/null
@@ -0,0 +1,2 @@
+mozprocess >= 0.19
+selenium >= 2.41.0
index c4ea971..a5c2533 100644 (file)
@@ -28,4 +28,5 @@ product_list = ["chrome",
                 "ie",
                 "sauce",
                 "servo",
                 "ie",
                 "sauce",
                 "servo",
-                "servodriver"]
+                "servodriver",
+                "opera"]
index fdc7a77..db4ae00 100644 (file)
@@ -3,12 +3,14 @@ from ..webdriver_server import EdgeDriverServer
 from ..executors import executor_kwargs as base_executor_kwargs
 from ..executors.executorselenium import (SeleniumTestharnessExecutor,
                                           SeleniumRefTestExecutor)
 from ..executors import executor_kwargs as base_executor_kwargs
 from ..executors.executorselenium import (SeleniumTestharnessExecutor,
                                           SeleniumRefTestExecutor)
+from ..executors.executoredge import EdgeDriverWdspecExecutor
 
 __wptrunner__ = {"product": "edge",
                  "check_args": "check_args",
                  "browser": "EdgeBrowser",
                  "executor": {"testharness": "SeleniumTestharnessExecutor",
 
 __wptrunner__ = {"product": "edge",
                  "check_args": "check_args",
                  "browser": "EdgeBrowser",
                  "executor": {"testharness": "SeleniumTestharnessExecutor",
-                              "reftest": "SeleniumRefTestExecutor"},
+                              "reftest": "SeleniumRefTestExecutor",
+                              "wdspec": "EdgeDriverWdspecExecutor"},
                  "browser_kwargs": "browser_kwargs",
                  "executor_kwargs": "executor_kwargs",
                  "env_extras": "env_extras",
                  "browser_kwargs": "browser_kwargs",
                  "executor_kwargs": "executor_kwargs",
                  "env_extras": "env_extras",
index 43da63c..552cd15 100644 (file)
@@ -57,8 +57,6 @@ def get_timeout_multiplier(test_type, run_info_data, **kwargs):
 
 def check_args(**kwargs):
     require_arg(kwargs, "binary")
 
 def check_args(**kwargs):
     require_arg(kwargs, "binary")
-    if kwargs["ssl_type"] != "none":
-        require_arg(kwargs, "certutil_binary")
 
 
 def browser_kwargs(test_type, run_info_data, **kwargs):
 
 
 def browser_kwargs(test_type, run_info_data, **kwargs):
@@ -78,7 +76,8 @@ def browser_kwargs(test_type, run_info_data, **kwargs):
                                                          run_info_data,
                                                          **kwargs),
             "leak_check": kwargs["leak_check"],
                                                          run_info_data,
                                                          **kwargs),
             "leak_check": kwargs["leak_check"],
-            "stylo_threads": kwargs["stylo_threads"]}
+            "stylo_threads": kwargs["stylo_threads"],
+            "chaos_mode_flags": kwargs["chaos_mode_flags"]}
 
 
 def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
 
 
 def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
@@ -89,6 +88,7 @@ def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
     executor_kwargs["timeout_multiplier"] = get_timeout_multiplier(test_type,
                                                                    run_info_data,
                                                                    **kwargs)
     executor_kwargs["timeout_multiplier"] = get_timeout_multiplier(test_type,
                                                                    run_info_data,
                                                                    **kwargs)
+    capabilities = {}
     if test_type == "reftest":
         executor_kwargs["reftest_internal"] = kwargs["reftest_internal"]
         executor_kwargs["reftest_screenshot"] = kwargs["reftest_screenshot"]
     if test_type == "reftest":
         executor_kwargs["reftest_internal"] = kwargs["reftest_internal"]
         executor_kwargs["reftest_screenshot"] = kwargs["reftest_screenshot"]
@@ -101,7 +101,10 @@ def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
         fxOptions["prefs"] = {
             "network.dns.localDomains": ",".join(hostnames)
         }
         fxOptions["prefs"] = {
             "network.dns.localDomains": ",".join(hostnames)
         }
-        capabilities = {"moz:firefoxOptions": fxOptions}
+        capabilities["moz:firefoxOptions"] = fxOptions
+    if kwargs["certutil_binary"] is None:
+        capabilities["acceptInsecureCerts"] = True
+    if capabilities:
         executor_kwargs["capabilities"] = capabilities
     return executor_kwargs
 
         executor_kwargs["capabilities"] = capabilities
     return executor_kwargs
 
@@ -136,7 +139,8 @@ class FirefoxBrowser(Browser):
     def __init__(self, logger, binary, prefs_root, test_type, extra_prefs=None, debug_info=None,
                  symbols_path=None, stackwalk_binary=None, certutil_binary=None,
                  ca_certificate_path=None, e10s=False, stackfix_dir=None,
     def __init__(self, logger, binary, prefs_root, test_type, extra_prefs=None, debug_info=None,
                  symbols_path=None, stackwalk_binary=None, certutil_binary=None,
                  ca_certificate_path=None, e10s=False, stackfix_dir=None,
-                 binary_args=None, timeout_multiplier=None, leak_check=False, stylo_threads=1):
+                 binary_args=None, timeout_multiplier=None, leak_check=False, stylo_threads=1,
+                 chaos_mode_flags=None):
         Browser.__init__(self, logger)
         self.binary = binary
         self.prefs_root = prefs_root
         Browser.__init__(self, logger)
         self.binary = binary
         self.prefs_root = prefs_root
@@ -164,6 +168,7 @@ class FirefoxBrowser(Browser):
         self.leak_report_file = None
         self.leak_check = leak_check
         self.stylo_threads = stylo_threads
         self.leak_report_file = None
         self.leak_check = leak_check
         self.stylo_threads = stylo_threads
+        self.chaos_mode_flags = chaos_mode_flags
 
     def settings(self, test):
         return {"check_leaks": self.leak_check and not test.leaks}
 
     def settings(self, test):
         return {"check_leaks": self.leak_check and not test.leaks}
@@ -178,6 +183,8 @@ class FirefoxBrowser(Browser):
         env["MOZ_CRASHREPORTER_SHUTDOWN"] = "1"
         env["MOZ_DISABLE_NONLOCAL_CONNECTIONS"] = "1"
         env["STYLO_THREADS"] = str(self.stylo_threads)
         env["MOZ_CRASHREPORTER_SHUTDOWN"] = "1"
         env["MOZ_DISABLE_NONLOCAL_CONNECTIONS"] = "1"
         env["STYLO_THREADS"] = str(self.stylo_threads)
+        if self.chaos_mode_flags is not None:
+            env["MOZ_CHAOSMODE"] = str(self.chaos_mode_flags)
 
         locations = ServerLocations(filename=os.path.join(here, "server-locations.txt"))
 
 
         locations = ServerLocations(filename=os.path.join(here, "server-locations.txt"))
 
@@ -333,6 +340,9 @@ class FirefoxBrowser(Browser):
         """Create a certificate database to use in the test profile. This is configured
         to trust the CA Certificate that has signed the web-platform.test server
         certificate."""
         """Create a certificate database to use in the test profile. This is configured
         to trust the CA Certificate that has signed the web-platform.test server
         certificate."""
+        if self.certutil_binary is None:
+            self.logger.info("--certutil-binary not supplied; Firefox will not check certificates")
+            return
 
         self.logger.info("Setting up ssl")
 
 
         self.logger.info("Setting up ssl")
 
diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/opera.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/opera.py
new file mode 100644 (file)
index 0000000..57edfa0
--- /dev/null
@@ -0,0 +1,101 @@
+from .base import Browser, ExecutorBrowser, require_arg
+from ..webdriver_server import OperaDriverServer
+from ..executors import executor_kwargs as base_executor_kwargs
+from ..executors.executorselenium import (SeleniumTestharnessExecutor,
+                                          SeleniumRefTestExecutor)
+from ..executors.executoropera import OperaDriverWdspecExecutor
+
+
+__wptrunner__ = {"product": "opera",
+                 "check_args": "check_args",
+                 "browser": "OperaBrowser",
+                 "executor": {"testharness": "SeleniumTestharnessExecutor",
+                              "reftest": "SeleniumRefTestExecutor",
+                              "wdspec": "OperaDriverWdspecExecutor"},
+                 "browser_kwargs": "browser_kwargs",
+                 "executor_kwargs": "executor_kwargs",
+                 "env_extras": "env_extras",
+                 "env_options": "env_options"}
+
+
+def check_args(**kwargs):
+    require_arg(kwargs, "webdriver_binary")
+
+
+def browser_kwargs(test_type, run_info_data, **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,
+                    **kwargs):
+    from selenium.webdriver import DesiredCapabilities
+
+    executor_kwargs = base_executor_kwargs(test_type, server_config,
+                                           cache_manager, **kwargs)
+    executor_kwargs["close_after_done"] = True
+    capabilities = dict(DesiredCapabilities.OPERA.items())
+    capabilities.setdefault("operaOptions", {})["prefs"] = {
+        "profile": {
+            "default_content_setting_values": {
+                "popups": 1
+            }
+        }
+    }
+    for (kwarg, capability) in [("binary", "binary"), ("binary_args", "args")]:
+        if kwargs[kwarg] is not None:
+            capabilities["operaOptions"][capability] = kwargs[kwarg]
+    if test_type == "testharness":
+        capabilities["operaOptions"]["useAutomationExtension"] = False
+        capabilities["operaOptions"]["excludeSwitches"] = ["enable-automation"]
+    if test_type == "wdspec":
+        capabilities["operaOptions"]["w3c"] = True
+    executor_kwargs["capabilities"] = capabilities
+    return executor_kwargs
+
+
+def env_extras(**kwargs):
+    return []
+
+
+def env_options():
+    return {"host": "web-platform.test",
+            "bind_hostname": "true"}
+
+
+class OperaBrowser(Browser):
+    """Opera is backed by operadriver, which is supplied through
+    ``wptrunner.webdriver.OperaDriverServer``.
+    """
+
+    def __init__(self, logger, binary, webdriver_binary="operadriver",
+                 webdriver_args=None):
+        """Creates a new representation of Opera.  The `binary` argument gives
+        the browser binary to use for testing."""
+        Browser.__init__(self, logger)
+        self.binary = binary
+        self.server = OperaDriverServer(self.logger,
+                                        binary=webdriver_binary,
+                                        args=webdriver_args)
+
+    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()
+
+    def executor_browser(self):
+        return ExecutorBrowser, {"webdriver_url": self.server.url}
index 73b54ce..e873440 100644 (file)
@@ -9,8 +9,10 @@ import time
 from mozlog import get_default_logger, handlers, proxy
 
 from wptlogging import LogLevelRewriter
 from mozlog import get_default_logger, handlers, proxy
 
 from wptlogging import LogLevelRewriter
+from wptserve.handlers import StringHandler
 
 here = os.path.split(__file__)[0]
 
 here = os.path.split(__file__)[0]
+repo_root = os.path.abspath(os.path.join(here, os.pardir, os.pardir, os.pardir))
 
 serve = None
 sslutils = None
 
 serve = None
 sslutils = None
@@ -189,6 +191,14 @@ class TestEnvironment(object):
             path = os.path.normpath(os.path.join(here, path))
             route_builder.add_static(path, format_args, content_type, route)
 
             path = os.path.normpath(os.path.join(here, path))
             route_builder.add_static(path, format_args, content_type, route)
 
+        data = b""
+        with open(os.path.join(repo_root, "resources", "testdriver.js"), "rb") as fp:
+            data += fp.read()
+        with open(os.path.join(here, "testdriver-extra.js"), "rb") as fp:
+            data += fp.read()
+        route_builder.add_handler(b"GET", b"/resources/testdriver.js",
+                                  StringHandler(data, "text/javascript"))
+
         for url_base, paths in self.test_paths.iteritems():
             if url_base == "/":
                 continue
         for url_base, paths in self.test_paths.iteritems():
             if url_base == "/":
                 continue
index 27be0c5..b33bc42 100644 (file)
@@ -102,6 +102,7 @@ class TestExecutor(object):
 
     test_type = None
     convert_result = None
 
     test_type = None
     convert_result = None
+    supports_testdriver = False
 
     def __init__(self, browser, server_config, timeout_multiplier=1,
                  debug_info=None, **kwargs):
 
     def __init__(self, browser, server_config, timeout_multiplier=1,
                  debug_info=None, **kwargs):
diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executoredge.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executoredge.py
new file mode 100644 (file)
index 0000000..bd6553f
--- /dev/null
@@ -0,0 +1,10 @@
+from ..webdriver_server import EdgeDriverServer
+from .base import WdspecExecutor, WebDriverProtocol
+
+
+class EdgeDriverProtocol(WebDriverProtocol):
+    server_cls = EdgeDriverServer
+
+
+class EdgeDriverWdspecExecutor(WdspecExecutor):
+    protocol_cls = EdgeDriverProtocol
index fa1c6d5..edea3f1 100644 (file)
@@ -43,12 +43,13 @@ def do_delayed_imports():
 
 
 class MarionetteProtocol(Protocol):
 
 
 class MarionetteProtocol(Protocol):
-    def __init__(self, executor, browser, timeout_multiplier=1):
+    def __init__(self, executor, browser, capabilities=None, timeout_multiplier=1):
         do_delayed_imports()
 
         Protocol.__init__(self, executor, browser)
         self.marionette = None
         self.marionette_port = browser.marionette_port
         do_delayed_imports()
 
         Protocol.__init__(self, executor, browser)
         self.marionette = None
         self.marionette_port = browser.marionette_port
+        self.capabilities = capabilities
         self.timeout_multiplier = timeout_multiplier
         self.timeout = None
         self.runner_handle = None
         self.timeout_multiplier = timeout_multiplier
         self.timeout = None
         self.runner_handle = None
@@ -62,30 +63,26 @@ class MarionetteProtocol(Protocol):
         self.marionette = marionette.Marionette(host='localhost',
                                                 port=self.marionette_port,
                                                 socket_timeout=None,
         self.marionette = marionette.Marionette(host='localhost',
                                                 port=self.marionette_port,
                                                 socket_timeout=None,
-                                                startup_timeout=None)
-
-        # XXX Move this timeout somewhere
-        self.logger.debug("Waiting for Marionette connection")
-        while True:
-            success = self.marionette.wait_for_port(startup_timeout)
-            #When running in a debugger wait indefinitely for firefox to start
-            if success or self.executor.debug_info is None:
-                break
-
-        session_started = False
-        if success:
-            try:
-                self.logger.debug("Starting Marionette session")
-                self.marionette.start_session()
-            except Exception as e:
-                self.logger.warning("Starting marionette session failed: %s" % e)
-            else:
-                self.logger.debug("Marionette session started")
-                session_started = True
+                                                startup_timeout=startup_timeout)
+        try:
+            self.logger.debug("Waiting for Marionette connection")
+            while True:
+                try:
+                    self.marionette.raise_for_port()
+                    break
+                except IOError:
+                    # When running in a debugger wait indefinitely for Firefox to start
+                    if self.executor.debug_info is None:
+                        raise
+
+            self.logger.debug("Starting Marionette session")
+            self.marionette.start_session()
+            self.logger.debug("Marionette session started")
 
 
-        if not success or not session_started:
-            self.logger.warning("Failed to connect to Marionette")
+        except Exception as e:
+            self.logger.warning("Failed to start a Marionette session: %s" % e)
             self.executor.runner.send_message("init_failed")
             self.executor.runner.send_message("init_failed")
+
         else:
             try:
                 self.after_connect()
         else:
             try:
                 self.after_connect()
@@ -159,15 +156,24 @@ class MarionetteProtocol(Protocol):
             runner_handle = handles.pop(0)
 
         for handle in handles:
             runner_handle = handles.pop(0)
 
         for handle in handles:
-            self.marionette.switch_to_window(handle)
-            self.marionette.close()
+            try:
+                self.marionette.switch_to_window(handle)
+                self.marionette.close()
+            except errors.NoSuchWindowException:
+                # We might have raced with the previous test to close this
+                # window, skip it.
+                pass
 
         self.marionette.switch_to_window(runner_handle)
         if runner_handle != self.runner_handle:
             self.load_runner(protocol)
 
     def wait(self):
 
         self.marionette.switch_to_window(runner_handle)
         if runner_handle != self.runner_handle:
             self.load_runner(protocol)
 
     def wait(self):
-        socket_timeout = self.marionette.client.sock.gettimeout()
+        try:
+            socket_timeout = self.marionette.client.socket_timeout
+        except AttributeError:
+            # This can happen if there was a crash
+            return
         if socket_timeout:
             self.marionette.timeout.script = socket_timeout / 2
 
         if socket_timeout:
             self.marionette.timeout.script = socket_timeout / 2
 
@@ -360,13 +366,14 @@ class ExecuteAsyncScriptRun(object):
 
 class MarionetteTestharnessExecutor(TestharnessExecutor):
     def __init__(self, browser, server_config, timeout_multiplier=1,
 
 class MarionetteTestharnessExecutor(TestharnessExecutor):
     def __init__(self, browser, server_config, timeout_multiplier=1,
-                 close_after_done=True, debug_info=None, **kwargs):
+                 close_after_done=True, debug_info=None, capabilities=None,
+                 **kwargs):
         """Marionette-based executor for testharness.js tests"""
         TestharnessExecutor.__init__(self, browser, server_config,
                                      timeout_multiplier=timeout_multiplier,
                                      debug_info=debug_info)
 
         """Marionette-based executor for testharness.js tests"""
         TestharnessExecutor.__init__(self, browser, server_config,
                                      timeout_multiplier=timeout_multiplier,
                                      debug_info=debug_info)
 
-        self.protocol = MarionetteProtocol(self, browser, timeout_multiplier)
+        self.protocol = MarionetteProtocol(self, browser, capabilities, timeout_multiplier)
         self.script = open(os.path.join(here, "testharness_marionette.js")).read()
         self.close_after_done = close_after_done
         self.window_id = str(uuid.uuid4())
         self.script = open(os.path.join(here, "testharness_marionette.js")).read()
         self.close_after_done = close_after_done
         self.window_id = str(uuid.uuid4())
@@ -425,7 +432,7 @@ class MarionetteRefTestExecutor(RefTestExecutor):
                  screenshot_cache=None, close_after_done=True,
                  debug_info=None, reftest_internal=False,
                  reftest_screenshot="unexpected",
                  screenshot_cache=None, close_after_done=True,
                  debug_info=None, reftest_internal=False,
                  reftest_screenshot="unexpected",
-                 group_metadata=None, **kwargs):
+                 group_metadata=None, capabilities=None, **kwargs):
         """Marionette-based executor for reftests"""
         RefTestExecutor.__init__(self,
                                  browser,
         """Marionette-based executor for reftests"""
         RefTestExecutor.__init__(self,
                                  browser,
@@ -433,7 +440,8 @@ class MarionetteRefTestExecutor(RefTestExecutor):
                                  screenshot_cache=screenshot_cache,
                                  timeout_multiplier=timeout_multiplier,
                                  debug_info=debug_info)
                                  screenshot_cache=screenshot_cache,
                                  timeout_multiplier=timeout_multiplier,
                                  debug_info=debug_info)
-        self.protocol = MarionetteProtocol(self, browser)
+        self.protocol = MarionetteProtocol(self, browser, capabilities,
+                                           timeout_multiplier)
         self.implementation = (InternalRefTestImplementation
                                if reftest_internal
                                else RefTestImplementation)(self)
         self.implementation = (InternalRefTestImplementation
                                if reftest_internal
                                else RefTestImplementation)(self)
diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executoropera.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/executoropera.py
new file mode 100644 (file)
index 0000000..55f1f0d
--- /dev/null
@@ -0,0 +1,10 @@
+from ..webdriver_server import OperaDriverServer
+from .base import WdspecExecutor, WebDriverProtocol
+
+
+class OperaDriverProtocol(WebDriverProtocol):
+    server_cls = OperaDriverServer
+
+
+class OperaDriverWdspecExecutor(WdspecExecutor):
+    protocol_cls = OperaDriverProtocol
index 728d3c6..2e84ab6 100644 (file)
@@ -1,3 +1,4 @@
+import json
 import os
 import socket
 import sys
 import os
 import socket
 import sys
@@ -159,6 +160,8 @@ class SeleniumRun(object):
 
 
 class SeleniumTestharnessExecutor(TestharnessExecutor):
 
 
 class SeleniumTestharnessExecutor(TestharnessExecutor):
+    supports_testdriver = True
+
     def __init__(self, browser, server_config, timeout_multiplier=1,
                  close_after_done=True, capabilities=None, debug_info=None,
                  **kwargs):
     def __init__(self, browser, server_config, timeout_multiplier=1,
                  close_after_done=True, capabilities=None, debug_info=None,
                  **kwargs):
@@ -169,6 +172,8 @@ class SeleniumTestharnessExecutor(TestharnessExecutor):
         self.protocol = SeleniumProtocol(self, browser, capabilities)
         with open(os.path.join(here, "testharness_webdriver.js")) as f:
             self.script = f.read()
         self.protocol = SeleniumProtocol(self, browser, capabilities)
         with open(os.path.join(here, "testharness_webdriver.js")) as f:
             self.script = f.read()
+        with open(os.path.join(here, "testharness_webdriver_resume.js")) as f:
+            self.script_resume = f.read()
         self.close_after_done = close_after_done
         self.window_id = str(uuid.uuid4())
 
         self.close_after_done = close_after_done
         self.window_id = str(uuid.uuid4())
 
@@ -193,12 +198,108 @@ class SeleniumTestharnessExecutor(TestharnessExecutor):
         return (test.result_cls(*data), [])
 
     def do_testharness(self, webdriver, url, timeout):
         return (test.result_cls(*data), [])
 
     def do_testharness(self, webdriver, url, timeout):
-        return webdriver.execute_async_script(
-            self.script % {"abs_url": url,
-                           "url": strip_server(url),
-                           "window_id": self.window_id,
-                           "timeout_multiplier": self.timeout_multiplier,
-                           "timeout": timeout * 1000})
+        format_map = {"abs_url": url,
+                      "url": strip_server(url),
+                      "window_id": self.window_id,
+                      "timeout_multiplier": self.timeout_multiplier,
+                      "timeout": timeout * 1000}
+
+        parent = webdriver.current_window_handle
+        handles = [item for item in webdriver.window_handles if item != parent]
+        for handle in handles:
+            try:
+                webdriver.switch_to_window(handle)
+                webdriver.close()
+            except exceptions.NoSuchWindowException:
+                pass
+        webdriver.switch_to_window(parent)
+
+        webdriver.execute_script(self.script % format_map)
+        try:
+            # Try this, it's in Level 1 but nothing supports it yet
+            win_s = webdriver.execute_script("return window['%s'];" % self.window_id)
+            win_obj = json.loads(win_s)
+            test_window = win_obj["window-fcc6-11e5-b4f8-330a88ab9d7f"]
+        except:
+            after = webdriver.window_handles
+            if len(after) == 2:
+                test_window = next(iter(set(after) - set([parent])))
+            elif after[0] == parent and len(after) > 2:
+                # Hope the first one here is the test window
+                test_window = after[1]
+            else:
+                raise Exception("unable to find test window")
+        assert test_window != parent
+
+        handler = CallbackHandler(webdriver, test_window, self.logger)
+        while True:
+            result = webdriver.execute_async_script(
+                self.script_resume % format_map)
+            done, rv = handler(result)
+            if done:
+                break
+        return rv
+
+
+class CallbackHandler(object):
+    def __init__(self, webdriver, test_window, logger):
+        self.webdriver = webdriver
+        self.test_window = test_window
+        self.logger = logger
+
+    def __call__(self, result):
+        self.logger.debug("Got async callback: %s" % result[1])
+        try:
+            attr = getattr(self, "process_%s" % result[1])
+        except AttributeError:
+            raise ValueError("Unknown callback type %r" % result[1])
+        else:
+            return attr(result)
+
+    def process_complete(self, result):
+        rv = [result[0]] + result[2]
+        return True, rv
+
+    def process_action(self, result):
+        parent = self.webdriver.current_window_handle
+        try:
+            self.webdriver.switch_to.window(self.test_window)
+            action = result[2]["action"]
+            self.logger.debug("Got action: %s" % action)
+            if action == "click":
+                selector = result[2]["selector"]
+                elements = self.webdriver.find_elements_by_css_selector(selector)
+                if len(elements) == 0:
+                    raise ValueError("Selector matches no elements")
+                elif len(elements) > 1:
+                    raise ValueError("Selector matches multiple elements")
+                self.logger.debug("Clicking element: %s" % selector)
+                try:
+                    elements[0].click()
+                except (exceptions.ElementNotInteractableException,
+                        exceptions.ElementNotVisibleException) as e:
+                    self._send_message("complete",
+                                       "failure",
+                                       e)
+                    self.logger.debug("Clicking element failed: %s" % str(e))
+                else:
+                    self._send_message("complete",
+                                       "success")
+                    self.logger.debug("Clicking element succeeded")
+        finally:
+            self.webdriver.switch_to.window(parent)
+
+        return False, None
+
+    def _send_message(self, message_type, status, message=None):
+        obj = {
+            "type": "testdriver-%s" % str(message_type),
+            "status": str(status)
+        }
+        if message:
+            obj["message"] = str(message)
+        self.webdriver.execute_script("window.postMessage(%s, '*')" % json.dumps(obj))
+
 
 
 class SeleniumRefTestExecutor(RefTestExecutor):
 
 
 class SeleniumRefTestExecutor(RefTestExecutor):
index 043fccb..611a498 100644 (file)
@@ -56,6 +56,7 @@ def run(path, server_config, session_config, timeout=0):
                      "--verbose",  # show each individual subtest
                      "--capture", "no",  # enable stdout/stderr from tests
                      "--basetemp", cache,  # temporary directory
                      "--verbose",  # show each individual subtest
                      "--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
                      path],
                      "-p", "no:mozlog",  # use the WPT result recorder
                      "-p", "no:cacheprovider",  # disable state preservation across invocations
                      path],
index f5cbff9..22675f0 100644 (file)
@@ -1,29 +1,23 @@
-var callback = arguments[arguments.length - 1];
 window.timeout_multiplier = %(timeout_multiplier)d;
 
 window.timeout_multiplier = %(timeout_multiplier)d;
 
-window.addEventListener("message", function f(event) {
-  if (event.data.type != "complete") {
-    return;
-  }
-  window.removeEventListener("message", f);
+window.message_queue = [];
 
 
-  var tests = event.data.tests;
-  var status = event.data.status;
+window.setMessageListener = function(func) {
+  window.current_listener = func;
+  window.addEventListener(
+    "message",
+    func,
+    false
+  );
+};
 
 
-  var subtest_results = tests.map(function(x) {
-    return [x.name, x.status, x.message, x.stack]
-  });
-  clearTimeout(timer);
-  callback(["%(url)s",
-            status.status,
-            status.message,
-            status.stack,
-            subtest_results]);
-}, false);
+window.setMessageListener(function(event) {
+  window.message_queue.push(event);
+});
 
 window.win = window.open("%(abs_url)s", "%(window_id)s");
 
 
 window.win = window.open("%(abs_url)s", "%(window_id)s");
 
-var timer = setTimeout(function() {
+window.timer = setTimeout(function() {
   window.win.timeout();
   window.win.close();
 }, %(timeout)s);
   window.win.timeout();
   window.win.close();
 }, %(timeout)s);
diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/testharness_webdriver_resume.js b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/testharness_webdriver_resume.js
new file mode 100644 (file)
index 0000000..7229777
--- /dev/null
@@ -0,0 +1,44 @@
+var callback = arguments[arguments.length - 1];
+
+function process_event(event) {
+  var data = event.data;
+
+  var payload = undefined;
+
+  switch(data.type) {
+  case "complete":
+    var tests = event.data.tests;
+    var status = event.data.status;
+
+    var subtest_results = tests.map(function(x) {
+      return [x.name, x.status, x.message, x.stack];
+    });
+    payload = [status.status,
+               status.message,
+               status.stack,
+               subtest_results];
+    clearTimeout(window.timer);
+    break;
+
+  case "action":
+    window.setMessageListener(function(event) {
+      window.message_queue.push(event);
+    });
+    payload = data;
+    break;
+  }
+
+  callback(["%(url)s", data.type, payload]);
+}
+
+window.removeEventListener("message", window.current_listener);
+if (window.message_queue.length) {
+  var next = window.message_queue.shift();
+  process_event(next);
+} else {
+  window.addEventListener(
+    "message", function f(event) {
+      window.removeEventListener("message", f);
+      process_event(event);
+    }, false);
+}
diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/stability.py b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/stability.py
new file mode 100644 (file)
index 0000000..dc1a1d0
--- /dev/null
@@ -0,0 +1,279 @@
+import copy
+import functools
+import imp
+import os
+import sys
+from collections import OrderedDict, defaultdict
+from datetime import datetime
+
+from mozlog import reader
+from mozlog.formatters import JSONFormatter, TbplFormatter
+from mozlog.handlers import BaseHandler, LogLevelFilter, StreamHandler
+
+here = os.path.dirname(__file__)
+localpaths = imp.load_source("localpaths", os.path.abspath(os.path.join(here, os.pardir, os.pardir, "localpaths.py")))
+from wpt.markdown import markdown_adjust, table
+
+
+class LogActionFilter(BaseHandler):
+
+    """Handler that filters out messages not of a given set of actions.
+
+    Subclasses BaseHandler.
+
+    :param inner: Handler to use for messages that pass this filter
+    :param actions: List of actions for which to fire the handler
+    """
+
+    def __init__(self, inner, actions):
+        """Extend BaseHandler and set inner and actions props on self."""
+        BaseHandler.__init__(self, inner)
+        self.inner = inner
+        self.actions = actions
+
+    def __call__(self, item):
+        """Invoke handler if action is in list passed as constructor param."""
+        if item["action"] in self.actions:
+            return self.inner(item)
+
+
+class LogHandler(reader.LogHandler):
+
+    """Handle updating test and subtest status in log.
+
+    Subclasses reader.LogHandler.
+    """
+    def __init__(self):
+        self.results = OrderedDict()
+
+    def find_or_create_test(self, data):
+        test_name = data["test"]
+        if self.results.get(test_name):
+            return self.results[test_name]
+
+        test = {
+            "subtests": OrderedDict(),
+            "status": defaultdict(int)
+        }
+        self.results[test_name] = test
+        return test
+
+    def find_or_create_subtest(self, data):
+        test = self.find_or_create_test(data)
+        subtest_name = data["subtest"]
+
+        if test["subtests"].get(subtest_name):
+            return test["subtests"][subtest_name]
+
+        subtest = {
+            "status": defaultdict(int),
+            "messages": set()
+        }
+        test["subtests"][subtest_name] = subtest
+
+        return subtest
+
+    def test_status(self, data):
+        subtest = self.find_or_create_subtest(data)
+        subtest["status"][data["status"]] += 1
+        if data.get("message"):
+            subtest["messages"].add(data["message"])
+
+    def test_end(self, data):
+        test = self.find_or_create_test(data)
+        test["status"][data["status"]] += 1
+
+
+def is_inconsistent(results_dict, iterations):
+    """Return whether or not a single test is inconsistent."""
+    return len(results_dict) > 1 or sum(results_dict.values()) != iterations
+
+
+def process_results(log, iterations):
+    """Process test log and return overall results and list of inconsistent tests."""
+    inconsistent = []
+    handler = LogHandler()
+    reader.handle_log(reader.read(log), handler)
+    results = handler.results
+    for test_name, test in results.iteritems():
+        if is_inconsistent(test["status"], iterations):
+            inconsistent.append((test_name, None, test["status"], []))
+        for subtest_name, subtest in test["subtests"].iteritems():
+            if is_inconsistent(subtest["status"], iterations):
+                inconsistent.append((test_name, subtest_name, subtest["status"], subtest["messages"]))
+    return results, inconsistent
+
+
+def err_string(results_dict, iterations):
+    """Create and return string with errors from test run."""
+    rv = []
+    total_results = sum(results_dict.values())
+    for key, value in sorted(results_dict.items()):
+        rv.append("%s%s" %
+                  (key, ": %s/%s" % (value, iterations) if value != iterations else ""))
+    if total_results < iterations:
+        rv.append("MISSING: %s/%s" % (iterations - total_results, iterations))
+    rv = ", ".join(rv)
+    if is_inconsistent(results_dict, iterations):
+        rv = "**%s**" % rv
+    return rv
+
+
+def write_inconsistent(log, inconsistent, iterations):
+    """Output inconsistent tests to logger.error."""
+    log("## Unstable results ##\n")
+    strings = [(
+        "`%s`" % markdown_adjust(test),
+        ("`%s`" % markdown_adjust(subtest)) if subtest else "",
+        err_string(results, iterations),
+        ("`%s`" % markdown_adjust(";".join(messages))) if len(messages) else "")
+        for test, subtest, results, messages in inconsistent]
+    table(["Test", "Subtest", "Results", "Messages"], strings, log)
+
+
+def write_results(log, results, iterations, pr_number=None, use_details=False):
+    log("## All results ##\n")
+    if use_details:
+        log("<details>\n")
+        log("<summary>%i %s ran</summary>\n\n" % (len(results),
+                                                  "tests" if len(results) > 1
+                                                  else "test"))
+
+    for test_name, test in results.iteritems():
+        baseurl = "http://w3c-test.org/submissions"
+        if "https" in os.path.splitext(test_name)[0].split(".")[1:]:
+            baseurl = "https://w3c-test.org/submissions"
+        title = test_name
+        if use_details:
+            log("<details>\n")
+            if pr_number:
+                title = "<a href=\"%s/%s%s\">%s</a>" % (baseurl, pr_number, test_name, title)
+            log('<summary>%s</summary>\n\n' % title)
+        else:
+            log("### %s ###" % title)
+        strings = [("", err_string(test["status"], iterations), "")]
+
+        strings.extend(((
+            ("`%s`" % markdown_adjust(subtest_name)) if subtest else "",
+            err_string(subtest["status"], iterations),
+            ("`%s`" % markdown_adjust(';'.join(subtest["messages"]))) if len(subtest["messages"]) else "")
+            for subtest_name, subtest in test["subtests"].items()))
+        table(["Subtest", "Results", "Messages"], strings, log)
+        if use_details:
+            log("</details>\n")
+
+    if use_details:
+        log("</details>\n")
+
+
+def run_step(logger, iterations, restart_after_iteration, kwargs_extras, **kwargs):
+    import wptrunner
+    kwargs = copy.deepcopy(kwargs)
+
+    if restart_after_iteration:
+        kwargs["repeat"] = iterations
+    else:
+        kwargs["rerun"] = iterations
+
+    kwargs["pause_after_test"] = False
+    kwargs.update(kwargs_extras)
+
+    handler = LogActionFilter(
+        LogLevelFilter(
+            StreamHandler(
+                sys.stdout,
+                TbplFormatter()
+            ),
+            "WARNING"),
+        ["log", "process_output"])
+
+    # There is a public API for this in the next mozlog
+    initial_handlers = logger._state.handlers
+    logger._state.handlers = []
+
+    with open("raw.log", "wb") as log:
+        # Setup logging for wptrunner that keeps process output and
+        # warning+ level logs only
+        logger.add_handler(handler)
+        logger.add_handler(StreamHandler(log, JSONFormatter()))
+
+        wptrunner.run_tests(**kwargs)
+
+    logger._state.handlers = initial_handlers
+
+    with open("raw.log", "rb") as log:
+        results, inconsistent = process_results(log, iterations)
+    return results, inconsistent, iterations
+
+
+def get_steps(logger, repeat_loop, repeat_restart, kwargs_extras):
+    steps = []
+    for kwargs_extra in kwargs_extras:
+        if kwargs_extra:
+            flags_string = " with flags %s" % " ".join(
+                "%s=%s" % item for item in kwargs_extra.iteritems())
+        else:
+            flags_string = ""
+
+        if repeat_loop:
+            desc = "Running tests in a loop %d times%s" % (repeat_loop,
+                                                           flags_string)
+            steps.append((desc, functools.partial(run_step, logger, repeat_loop, False, kwargs_extra)))
+
+        if repeat_restart:
+            desc = "Running tests in a loop with restarts %s times%s" % (repeat_restart,
+                                                                         flags_string)
+            steps.append((desc, functools.partial(run_step, logger, repeat_restart, True, kwargs_extra)))
+
+    return steps
+
+
+def write_summary(logger, step_results, final_result):
+    for desc, result in step_results:
+        logger.info('::: %s : %s' % (desc, result))
+    logger.info(':::')
+    if final_result == "PASS":
+        log = logger.info
+    elif final_result == "TIMEOUT":
+        log = logger.warning
+    else:
+        log = logger.error
+    log('::: Test verification %s' % final_result)
+
+    logger.info(':::')
+
+
+def check_stability(logger, repeat_loop=10, repeat_restart=5, chaos_mode=True, max_time=None,
+                    output_results=True, **kwargs):
+    kwargs_extras = [{}]
+    if chaos_mode and kwargs["product"] == "firefox":
+        kwargs_extras.append({"chaos_mode_flags": 3})
+
+    steps = get_steps(logger, repeat_loop, repeat_restart, kwargs_extras)
+
+    start_time = datetime.now()
+    step_results = []
+
+    for desc, step_func in steps:
+        if max_time and datetime.now() - start_time > max_time:
+            logger.info("::: Test verification is taking too long: Giving up!")
+            logger.info("::: So far, all checks passed, but not all checks were run.")
+            write_summary(logger, step_results, "TIMEOUT")
+            return 2
+
+        logger.info(':::')
+        logger.info('::: Running test verification step "%s"...' % desc)
+        logger.info(':::')
+        results, inconsistent, iterations = step_func(**kwargs)
+        if output_results:
+            write_results(logger.info, results, iterations)
+
+        if inconsistent:
+            step_results.append((desc, "FAIL"))
+            write_inconsistent(logger.info, inconsistent, iterations)
+            write_summary(logger, step_results, "FAIL")
+            return 1
+
+        step_results.append((desc,  "PASS"))
+
+    write_summary(logger, step_results, "PASS")
diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testdriver-extra.js b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testdriver-extra.js
new file mode 100644 (file)
index 0000000..856a33e
--- /dev/null
@@ -0,0 +1,63 @@
+"use strict";
+
+(function(){
+    let pending_resolve = null;
+    let pending_reject = null;
+    window.addEventListener("message", function(event) {
+        const data = event.data;
+
+        if (typeof data !== "object" && data !== null) {
+            return;
+        }
+
+        if (data.type !== "testdriver-complete") {
+            return;
+        }
+
+        if (data.status === "success") {
+            pending_resolve();
+        } else {
+            pending_reject();
+        }
+    });
+
+    const get_selector = function(element) {
+        let selector;
+
+        if (element.id && document.getElementById(element.id) === element) {
+            const id = element.id;
+
+            selector = "#";
+            // escape everything, because it's easy to implement
+            for (let i = 0, len = id.length; i < len; i++) {
+                selector += '\\' + id.charCodeAt(i).toString(16) + ' ';
+            }
+        } else {
+            // push and then reverse to avoid O(n) unshift in the loop
+            let segments = [];
+            for (let node = element;
+                 node.parentElement;
+                 node = node.parentElement) {
+                let segment = "*|" + node.localName;
+                let nth = Array.prototype.indexOf.call(node.parentElement.children, node) + 1;
+                segments.push(segment + ":nth-child(" + nth + ")");
+            }
+            segments.push(":root");
+            segments.reverse();
+
+            selector = segments.join(" > ");
+        }
+
+        return selector;
+    };
+
+    window.test_driver_internal.click = function(element) {
+        const selector = get_selector(element);
+        const pending_promise = new Promise(function(resolve, reject) {
+            pending_resolve = resolve;
+            pending_reject = reject;
+        });
+        window.opener.postMessage({"type": "action", "action": "click", "selector": selector}, "*");
+        return pending_promise;
+    };
+})();
index 10f3586..0eb78bb 100644 (file)
@@ -14,12 +14,17 @@ from mozlog import structured
 
 manifest = None
 manifest_update = None
 
 manifest = None
 manifest_update = None
+download_from_github = None
+manifest_log = None
 
 def do_delayed_imports():
     # This relies on an already loaded module having set the sys.path correctly :(
 
 def do_delayed_imports():
     # This relies on an already loaded module having set the sys.path correctly :(
-    global manifest, manifest_update
+    global manifest, manifest_update, download_from_github, manifest_log
     from manifest import manifest
     from manifest import update as manifest_update
     from manifest import manifest
     from manifest import update as manifest_update
+    from manifest.download import download_from_github
+    from manifest import log as manifest_log
+
 
 class TestChunker(object):
     def __init__(self, total_chunks, chunk_number):
 
 class TestChunker(object):
     def __init__(self, total_chunks, chunk_number):
@@ -372,10 +377,11 @@ class TagFilter(object):
                 yield test
 
 class ManifestLoader(object):
                 yield test
 
 class ManifestLoader(object):
-    def __init__(self, test_paths, force_manifest_update=False):
+    def __init__(self, test_paths, force_manifest_update=False, manifest_download=False):
         do_delayed_imports()
         self.test_paths = test_paths
         self.force_manifest_update = force_manifest_update
         do_delayed_imports()
         self.test_paths = test_paths
         self.force_manifest_update = force_manifest_update
+        self.manifest_download = manifest_download
         self.logger = structured.get_default_logger()
         if self.logger is None:
             self.logger = structured.structuredlog.StructuredLogger("ManifestLoader")
         self.logger = structured.get_default_logger()
         if self.logger is None:
             self.logger = structured.structuredlog.StructuredLogger("ManifestLoader")
@@ -391,13 +397,19 @@ class ManifestLoader(object):
         return rv
 
     def create_manifest(self, manifest_path, tests_path, url_base="/"):
         return rv
 
     def create_manifest(self, manifest_path, tests_path, url_base="/"):
-        self.update_manifest(manifest_path, tests_path, url_base, recreate=True)
+        self.update_manifest(manifest_path, tests_path, url_base, recreate=True,
+                             download=self.manifest_download)
 
     def update_manifest(self, manifest_path, tests_path, url_base="/",
 
     def update_manifest(self, manifest_path, tests_path, url_base="/",
-                        recreate=False):
+                        recreate=False, download=False):
         self.logger.info("Updating test manifest %s" % manifest_path)
         self.logger.info("Updating test manifest %s" % manifest_path)
+        manifest_log.setup()
 
         json_data = None
 
         json_data = None
+        if download:
+            # TODO: make this not github-specific
+            download_from_github(manifest_path, tests_path)
+
         if not recreate:
             try:
                 with open(manifest_path) as f:
         if not recreate:
             try:
                 with open(manifest_path) as f:
@@ -422,7 +434,7 @@ class ManifestLoader(object):
         manifest_path = os.path.join(metadata_path, "MANIFEST.json")
         if (not os.path.exists(manifest_path) or
             self.force_manifest_update):
         manifest_path = os.path.join(metadata_path, "MANIFEST.json")
         if (not os.path.exists(manifest_path) or
             self.force_manifest_update):
-            self.update_manifest(manifest_path, tests_path, url_base)
+            self.update_manifest(manifest_path, tests_path, url_base, download=self.manifest_download)
         manifest_file = manifest.load(tests_path, manifest_path)
         if manifest_file.url_base != url_base:
             self.logger.info("Updating url_base in manifest from %s to %s" % (manifest_file.url_base,
         manifest_file = manifest.load(tests_path, manifest_path)
         if manifest_file.url_base != url_base:
             self.logger.info("Updating url_base in manifest from %s to %s" % (manifest_file.url_base,
@@ -448,7 +460,8 @@ class TestLoader(object):
                  chunk_type="none",
                  total_chunks=1,
                  chunk_number=1,
                  chunk_type="none",
                  total_chunks=1,
                  chunk_number=1,
-                 include_https=True):
+                 include_https=True,
+                 skip_timeout=False):
 
         self.test_types = test_types
         self.run_info = run_info
 
         self.test_types = test_types
         self.run_info = run_info
@@ -460,6 +473,7 @@ class TestLoader(object):
         self.tests = None
         self.disabled_tests = None
         self.include_https = include_https
         self.tests = None
         self.disabled_tests = None
         self.include_https = include_https
+        self.skip_timeout = skip_timeout
 
         self.chunk_type = chunk_type
         self.total_chunks = total_chunks
 
         self.chunk_type = chunk_type
         self.total_chunks = total_chunks
@@ -545,6 +559,8 @@ class TestLoader(object):
             enabled = not test.disabled()
             if not self.include_https and test.environment["protocol"] == "https":
                 enabled = False
             enabled = not test.disabled()
             if not self.include_https and test.environment["protocol"] == "https":
                 enabled = False
+            if self.skip_timeout and test.expected() == "TIMEOUT":
+                enabled = False
             key = "enabled" if enabled else "disabled"
             tests[key][test_type].append(test)
 
             key = "enabled" if enabled else "disabled"
             tests[key][test_type].append(test)
 
index 882ac20..6441828 100644 (file)
@@ -249,7 +249,7 @@ RunnerManagerState = _RunnerManagerState()
 
 class TestRunnerManager(threading.Thread):
     def __init__(self, suite_name, test_queue, test_source_cls, browser_cls, browser_kwargs,
 
 class TestRunnerManager(threading.Thread):
     def __init__(self, suite_name, test_queue, test_source_cls, browser_cls, browser_kwargs,
-                 executor_cls, executor_kwargs, stop_flag, pause_after_test=False,
+                 executor_cls, executor_kwargs, stop_flag, rerun=1, pause_after_test=False,
                  pause_on_unexpected=False, restart_on_unexpected=True, debug_info=None):
         """Thread that owns a single TestRunner process and any processes required
         by the TestRunner (e.g. the Firefox binary).
                  pause_on_unexpected=False, restart_on_unexpected=True, debug_info=None):
         """Thread that owns a single TestRunner process and any processes required
         by the TestRunner (e.g. the Firefox binary).
@@ -280,6 +280,8 @@ class TestRunnerManager(threading.Thread):
         self.parent_stop_flag = stop_flag
         self.child_stop_flag = multiprocessing.Event()
 
         self.parent_stop_flag = stop_flag
         self.child_stop_flag = multiprocessing.Event()
 
+        self.rerun = rerun
+        self.run_count = 0
         self.pause_after_test = pause_after_test
         self.pause_on_unexpected = pause_on_unexpected
         self.restart_on_unexpected = restart_on_unexpected
         self.pause_after_test = pause_after_test
         self.pause_on_unexpected = pause_on_unexpected
         self.restart_on_unexpected = restart_on_unexpected
@@ -503,9 +505,9 @@ class TestRunnerManager(threading.Thread):
                     self.logger.info("No more tests")
                     return None, None, None
             test = test_group.popleft()
                     self.logger.info("No more tests")
                     return None, None, None
             test = test_group.popleft()
+        self.run_count = 0
         return test, test_group, group_metadata
 
         return test, test_group, group_metadata
 
-
     def run_test(self):
         assert isinstance(self.state, RunnerManagerState.running)
         assert self.state.test is not None
     def run_test(self):
         assert isinstance(self.state, RunnerManagerState.running)
         assert self.state.test is not None
@@ -517,6 +519,9 @@ class TestRunnerManager(threading.Thread):
                                                  self.state.group_metadata)
 
         self.logger.test_start(self.state.test.id)
                                                  self.state.group_metadata)
 
         self.logger.test_start(self.state.test.id)
+        if self.rerun > 1:
+            self.logger.info("Run %d/%d" % (self.run_count, self.rerun))
+        self.run_count += 1
         self.send_message("run_test", self.state.test)
 
     def test_ended(self, test, results):
         self.send_message("run_test", self.state.test)
 
     def test_ended(self, test, results):
@@ -580,7 +585,7 @@ class TestRunnerManager(threading.Thread):
             self.logger.info("Pausing until the browser exits")
             self.send_message("wait")
         else:
             self.logger.info("Pausing until the browser exits")
             self.send_message("wait")
         else:
-            return self.after_test_end(restart_before_next)
+            return self.after_test_end(test, restart_before_next)
 
     def wait_finished(self):
         assert isinstance(self.state, RunnerManagerState.running)
 
     def wait_finished(self):
         assert isinstance(self.state, RunnerManagerState.running)
@@ -588,16 +593,21 @@ class TestRunnerManager(threading.Thread):
         # processing
         self.logger.debug("Wait finished")
 
         # processing
         self.logger.debug("Wait finished")
 
-        return self.after_test_end(True)
+        return self.after_test_end(self.state.test, True)
 
 
-    def after_test_end(self, restart):
+    def after_test_end(self, test, restart):
         assert isinstance(self.state, RunnerManagerState.running)
         assert isinstance(self.state, RunnerManagerState.running)
-        test, test_group, group_metadata = self.get_next_test()
-        if test is None:
-            return RunnerManagerState.stop()
-        if test_group != self.state.test_group:
-            # We are starting a new group of tests, so force a restart
-            restart = True
+        if self.run_count == self.rerun:
+            test, test_group, group_metadata = self.get_next_test()
+            if test is None:
+                return RunnerManagerState.stop()
+            if test_group != self.state.test_group:
+                # We are starting a new group of tests, so force a restart
+                restart = True
+        else:
+            test = test
+            test_group = self.state.test_group
+            group_metadata = self.state.group_metadata
         if restart:
             return RunnerManagerState.restarting(test, test_group, group_metadata)
         else:
         if restart:
             return RunnerManagerState.restarting(test, test_group, group_metadata)
         else:
@@ -686,6 +696,7 @@ class ManagerGroup(object):
     def __init__(self, suite_name, size, test_source_cls, test_source_kwargs,
                  browser_cls, browser_kwargs,
                  executor_cls, executor_kwargs,
     def __init__(self, suite_name, size, test_source_cls, test_source_kwargs,
                  browser_cls, browser_kwargs,
                  executor_cls, executor_kwargs,
+                 rerun=1,
                  pause_after_test=False,
                  pause_on_unexpected=False,
                  restart_on_unexpected=True,
                  pause_after_test=False,
                  pause_on_unexpected=False,
                  restart_on_unexpected=True,
@@ -703,6 +714,7 @@ class ManagerGroup(object):
         self.pause_on_unexpected = pause_on_unexpected
         self.restart_on_unexpected = restart_on_unexpected
         self.debug_info = debug_info
         self.pause_on_unexpected = pause_on_unexpected
         self.restart_on_unexpected = restart_on_unexpected
         self.debug_info = debug_info
+        self.rerun = rerun
 
         self.pool = set()
         # Event that is polled by threads so that they can gracefully exit in the face
 
         self.pool = set()
         # Event that is polled by threads so that they can gracefully exit in the face
@@ -735,6 +747,7 @@ class ManagerGroup(object):
                                         self.executor_cls,
                                         self.executor_kwargs,
                                         self.stop_flag,
                                         self.executor_cls,
                                         self.executor_kwargs,
                                         self.stop_flag,
+                                        self.rerun,
                                         self.pause_after_test,
                                         self.pause_on_unexpected,
                                         self.restart_on_unexpected,
                                         self.pause_after_test,
                                         self.pause_on_unexpected,
                                         self.restart_on_unexpected,
index 9454414..dc459da 100644 (file)
@@ -44,6 +44,8 @@ class State(object):
     def load(cls, logger):
         """Load saved state from a file"""
         try:
     def load(cls, logger):
         """Load saved state from a file"""
         try:
+            if not os.path.isfile(cls.filename):
+                return None
             with open(cls.filename) as f:
                 try:
                     rv = pickle.load(f)
             with open(cls.filename) as f:
                 try:
                     rv = pickle.load(f)
index b201fe7..40bd1d7 100644 (file)
@@ -81,7 +81,8 @@ def copy_wpt_tree(tree, dest, excludes=None, includes=None):
             shutil.copy2(source_path, dest_path)
 
     for source, destination in [("testharness_runner.html", ""),
             shutil.copy2(source_path, dest_path)
 
     for source, destination in [("testharness_runner.html", ""),
-                                ("testharnessreport.js", "resources/")]:
+                                ("testharnessreport.js", "resources/"),
+                                ("testdriver-vendor.js", "resources/")]:
         source_path = os.path.join(here, os.pardir, source)
         dest_path = os.path.join(dest, destination, os.path.split(source)[1])
         shutil.copy2(source_path, dest_path)
         source_path = os.path.join(here, os.pardir, source)
         dest_path = os.path.join(dest, destination, os.path.split(source)[1])
         shutil.copy2(source_path, dest_path)
index f78593e..4a3c48c 100644 (file)
@@ -11,8 +11,8 @@ import urlparse
 import mozprocess
 
 
 import mozprocess
 
 
-__all__ = ["SeleniumServer", "ChromeDriverServer",
-           "GeckoDriverServer", "InternetExplorerDriverServer",
+__all__ = ["SeleniumServer", "ChromeDriverServer", "OperaDriverServer",
+           "GeckoDriverServer", "InternetExplorerDriverServer", "EdgeDriverServer",
            "ServoDriverServer", "WebDriverServer"]
 
 
            "ServoDriverServer", "WebDriverServer"]
 
 
@@ -137,6 +137,25 @@ class ChromeDriverServer(WebDriverServer):
                 cmd_arg("port", str(self.port)),
                 cmd_arg("url-base", self.base_path) if self.base_path else ""] + self._args
 
                 cmd_arg("port", str(self.port)),
                 cmd_arg("url-base", self.base_path) if self.base_path else ""] + self._args
 
+class EdgeDriverServer(WebDriverServer):
+    default_base_path = "/"
+
+    def __init__(self, logger, binary="microsoftwebdriver.exe", 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 ""] + 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 EdgeDriverServer(WebDriverServer):
     def __init__(self, logger, binary="MicrosoftWebDriver.exe", port=None,
 
 class EdgeDriverServer(WebDriverServer):
     def __init__(self, logger, binary="MicrosoftWebDriver.exe", port=None,
index a9a6264..b0591d5 100644 (file)
@@ -51,6 +51,8 @@ scheme host and port.""")
                         help="Regenerate the test manifest.")
     parser.add_argument("--no-manifest-update", action="store_false", dest="manifest_update",
                         help="Prevent regeneration of the test manifest.")
                         help="Regenerate the test manifest.")
     parser.add_argument("--no-manifest-update", action="store_false", dest="manifest_update",
                         help="Prevent regeneration of the test manifest.")
+    parser.add_argument("--manifest-download", action="store_true", default=None,
+                        help="Attempt to download a preexisting manifest when updating.")
 
     parser.add_argument("--timeout-multiplier", action="store", type=float, default=None,
                         help="Multiplier relative to standard test timeout to use")
 
     parser.add_argument("--timeout-multiplier", action="store", type=float, default=None,
                         help="Multiplier relative to standard test timeout to use")
@@ -74,6 +76,9 @@ scheme host and port.""")
     mode_group.add_argument("--list-tests", action="store_true",
                             default=False,
                             help="List all tests that will run")
     mode_group.add_argument("--list-tests", action="store_true",
                             default=False,
                             help="List all tests that will run")
+    mode_group.add_argument("--verify", action="store_true",
+                            default=False,
+                            help="Run a stability check on the selected tests")
 
     test_selection_group = parser.add_argument_group("Test Selection")
     test_selection_group.add_argument("--test-types", action="store",
 
     test_selection_group = parser.add_argument_group("Test Selection")
     test_selection_group.add_argument("--test-types", action="store",
@@ -86,6 +91,8 @@ scheme host and port.""")
                                       help="URL prefix to exclude")
     test_selection_group.add_argument("--include-manifest", type=abs_path,
                                       help="Path to manifest listing tests to include")
                                       help="URL prefix to exclude")
     test_selection_group.add_argument("--include-manifest", type=abs_path,
                                       help="Path to manifest listing tests to include")
+    test_selection_group.add_argument("--skip-timeout", action="store_true",
+                                      help="Skip tests that are expected to time out")
     test_selection_group.add_argument("--tag", action="append", dest="tags",
                                       help="Labels applied to tests to include in the run. Labels starting dir: are equivalent to top-level directories.")
 
     test_selection_group.add_argument("--tag", action="append", dest="tags",
                                       help="Labels applied to tests to include in the run. Labels starting dir: are equivalent to top-level directories.")
 
@@ -93,8 +100,10 @@ scheme host and port.""")
     debugging_group.add_argument('--debugger', const="__default__", nargs="?",
                                  help="run under a debugger, e.g. gdb or valgrind")
     debugging_group.add_argument('--debugger-args', help="arguments to the debugger")
     debugging_group.add_argument('--debugger', const="__default__", nargs="?",
                                  help="run under a debugger, e.g. gdb or valgrind")
     debugging_group.add_argument('--debugger-args', help="arguments to the debugger")
+    debugging_group.add_argument("--rerun", action="store", type=int, default=1,
+                                 help="Number of times to re run each test without restarts")
     debugging_group.add_argument("--repeat", action="store", type=int, default=1,
     debugging_group.add_argument("--repeat", action="store", type=int, default=1,
-                                 help="Number of times to run the tests")
+                                 help="Number of times to run the tests, restarting between each run")
     debugging_group.add_argument("--repeat-until-unexpected", action="store_true", default=None,
                                  help="Run tests in a loop until one returns an unexpected result")
     debugging_group.add_argument('--pause-after-test', action="store_true", default=None,
     debugging_group.add_argument("--repeat-until-unexpected", action="store_true", default=None,
                                  help="Run tests in a loop until one returns an unexpected result")
     debugging_group.add_argument('--pause-after-test', action="store_true", default=None,
@@ -199,6 +208,11 @@ scheme host and port.""")
     gecko_group.add_argument("--reftest-screenshot", dest="reftest_screenshot", action="store",
                              choices=["always", "fail", "unexpected"], default="unexpected",
                              help="With --reftest-internal, when to take a screenshot")
     gecko_group.add_argument("--reftest-screenshot", dest="reftest_screenshot", action="store",
                              choices=["always", "fail", "unexpected"], default="unexpected",
                              help="With --reftest-internal, when to take a screenshot")
+    gecko_group.add_argument("--chaos", dest="chaos_mode_flags", action="store",
+                             nargs="?", const=0xFFFFFFFF, type=int,
+                             help="Enable chaos mode with the specified feature flag "
+                             "(see http://searchfox.org/mozilla-central/source/mfbt/ChaosMode.h for "
+                             "details). If no value is supplied, all features are activated")
 
     servo_group = parser.add_argument_group("Servo-specific")
     servo_group.add_argument("--user-stylesheet",
 
     servo_group = parser.add_argument_group("Servo-specific")
     servo_group.add_argument("--user-stylesheet",
@@ -393,7 +407,7 @@ def check_args(kwargs):
             sys.exit(1)
         kwargs["openssl_binary"] = path
 
             sys.exit(1)
         kwargs["openssl_binary"] = path
 
-    if kwargs["ssl_type"] != "none" and kwargs["product"] == "firefox":
+    if kwargs["ssl_type"] != "none" and kwargs["product"] == "firefox" and kwargs["certutil_binary"]:
         path = exe_path(kwargs["certutil_binary"])
         if path is None:
             print >> sys.stderr, "certutil-binary argument missing or not a valid executable"
         path = exe_path(kwargs["certutil_binary"])
         if path is None:
             print >> sys.stderr, "certutil-binary argument missing or not a valid executable"
index 1ab6755..4d32061 100644 (file)
@@ -76,7 +76,10 @@ class LoggingWrapper(StringIO):
 
     def write(self, data):
         if isinstance(data, str):
 
     def write(self, data):
         if isinstance(data, str):
-            data = data.decode("utf8")
+            try:
+                data = data.decode("utf8")
+            except UnicodeDecodeError:
+                data = data.encode("string_escape").decode("ascii")
 
         if data.endswith("\n"):
             data = data[:-1]
 
         if data.endswith("\n"):
             data = data[:-1]
index 9855ecc..be81eb9 100644 (file)
@@ -38,6 +38,7 @@ def setup_logging(*args, **kwargs):
     global logger
     logger = wptlogging.setup(*args, **kwargs)
 
     global logger
     logger = wptlogging.setup(*args, **kwargs)
 
+
 def get_loader(test_paths, product, ssl_env, debug=None, run_info_extras=None, **kwargs):
     if run_info_extras is None:
         run_info_extras = {}
 def get_loader(test_paths, product, ssl_env, debug=None, run_info_extras=None, **kwargs):
     if run_info_extras is None:
         run_info_extras = {}
@@ -45,7 +46,8 @@ def get_loader(test_paths, product, ssl_env, debug=None, run_info_extras=None, *
     run_info = wpttest.get_run_info(kwargs["run_info"], product, debug=debug,
                                     extras=run_info_extras)
 
     run_info = wpttest.get_run_info(kwargs["run_info"], product, debug=debug,
                                     extras=run_info_extras)
 
-    test_manifests = testloader.ManifestLoader(test_paths, force_manifest_update=kwargs["manifest_update"]).load()
+    test_manifests = testloader.ManifestLoader(test_paths, force_manifest_update=kwargs["manifest_update"],
+                                               manifest_download=kwargs["manifest_download"]).load()
 
     manifest_filters = []
     meta_filters = []
 
     manifest_filters = []
     meta_filters = []
@@ -66,9 +68,11 @@ def get_loader(test_paths, product, ssl_env, debug=None, run_info_extras=None, *
                                         chunk_type=kwargs["chunk_type"],
                                         total_chunks=kwargs["total_chunks"],
                                         chunk_number=kwargs["this_chunk"],
                                         chunk_type=kwargs["chunk_type"],
                                         total_chunks=kwargs["total_chunks"],
                                         chunk_number=kwargs["this_chunk"],
-                                        include_https=ssl_env.ssl_enabled)
+                                        include_https=ssl_env.ssl_enabled,
+                                        skip_timeout=kwargs["skip_timeout"])
     return run_info, test_loader
 
     return run_info, test_loader
 
+
 def list_test_groups(test_paths, product, **kwargs):
     env.do_delayed_imports(logger, test_paths)
 
 def list_test_groups(test_paths, product, **kwargs):
     env.do_delayed_imports(logger, test_paths)
 
@@ -122,7 +126,7 @@ def get_pause_after_test(test_loader, **kwargs):
     if kwargs["pause_after_test"] is None:
         if kwargs["repeat_until_unexpected"]:
             return False
     if kwargs["pause_after_test"] is None:
         if kwargs["repeat_until_unexpected"]:
             return False
-        if kwargs["repeat"] == 1 and total_tests == 1:
+        if kwargs["repeat"] == 1 and kwargs["rerun"] == 1 and total_tests == 1:
             return True
         return False
     return kwargs["pause_after_test"]
             return True
         return False
     return kwargs["pause_after_test"]
@@ -216,7 +220,6 @@ def run_tests(config, test_paths, product, **kwargs):
                                                         ssl_env=ssl_env,
                                                         **kwargs)
 
                                                         ssl_env=ssl_env,
                                                         **kwargs)
 
-
                     executor_cls = executor_classes.get(test_type)
                     executor_kwargs = get_executor_kwargs(test_type,
                                                           test_environment.external_config,
                     executor_cls = executor_classes.get(test_type)
                     executor_kwargs = get_executor_kwargs(test_type,
                                                           test_environment.external_config,
@@ -233,6 +236,17 @@ def run_tests(config, test_paths, product, **kwargs):
                         logger.test_start(test.id)
                         logger.test_end(test.id, status="SKIP")
 
                         logger.test_start(test.id)
                         logger.test_end(test.id, status="SKIP")
 
+                    if test_type == "testharness":
+                        run_tests = {"testharness": []}
+                        for test in test_loader.tests["testharness"]:
+                            if test.testdriver and not executor_cls.supports_testdriver:
+                                logger.test_start(test.id)
+                                logger.test_end(test.id, status="SKIP")
+                            else:
+                                run_tests["testharness"].append(test)
+                    else:
+                        run_tests = test_loader.tests
+
                     with ManagerGroup("web-platform-tests",
                                       kwargs["processes"],
                                       test_source_cls,
                     with ManagerGroup("web-platform-tests",
                                       kwargs["processes"],
                                       test_source_cls,
@@ -241,12 +255,13 @@ def run_tests(config, test_paths, product, **kwargs):
                                       browser_kwargs,
                                       executor_cls,
                                       executor_kwargs,
                                       browser_kwargs,
                                       executor_cls,
                                       executor_kwargs,
+                                      kwargs["rerun"],
                                       kwargs["pause_after_test"],
                                       kwargs["pause_on_unexpected"],
                                       kwargs["restart_on_unexpected"],
                                       kwargs["debug_info"]) as manager_group:
                         try:
                                       kwargs["pause_after_test"],
                                       kwargs["pause_on_unexpected"],
                                       kwargs["restart_on_unexpected"],
                                       kwargs["debug_info"]) as manager_group:
                         try:
-                            manager_group.run(test_type, test_loader.tests)
+                            manager_group.run(test_type, run_tests)
                         except KeyboardInterrupt:
                             logger.critical("Main thread got signal")
                             manager_group.stop()
                         except KeyboardInterrupt:
                             logger.critical("Main thread got signal")
                             manager_group.stop()
@@ -260,6 +275,12 @@ def run_tests(config, test_paths, product, **kwargs):
                 logger.suite_end()
     return unexpected_total == 0
 
                 logger.suite_end()
     return unexpected_total == 0
 
+
+def check_stability(**kwargs):
+    import stability
+    return stability.check_stability(logger, **kwargs)
+
+
 def start(**kwargs):
     if kwargs["list_test_groups"]:
         list_test_groups(**kwargs)
 def start(**kwargs):
     if kwargs["list_test_groups"]:
         list_test_groups(**kwargs)
@@ -267,9 +288,12 @@ def start(**kwargs):
         list_disabled(**kwargs)
     elif kwargs["list_tests"]:
         list_tests(**kwargs)
         list_disabled(**kwargs)
     elif kwargs["list_tests"]:
         list_tests(**kwargs)
+    elif kwargs["verify"]:
+        check_stability(**kwargs)
     else:
         return not run_tests(**kwargs)
 
     else:
         return not run_tests(**kwargs)
 
+
 def main():
     """Main entry point when calling from the command line"""
     kwargs = wptcommandline.parse_args()
 def main():
     """Main entry point when calling from the command line"""
     kwargs = wptcommandline.parse_args()
index 86320f8..d283e85 100644 (file)
@@ -242,6 +242,27 @@ class TestharnessTest(Test):
     subtest_result_cls = TestharnessSubtestResult
     test_type = "testharness"
 
     subtest_result_cls = TestharnessSubtestResult
     test_type = "testharness"
 
+    def __init__(self, tests_root, url, inherit_metadata, test_metadata,
+                 timeout=None, path=None, protocol="http", testdriver=False):
+        Test.__init__(self, tests_root, url, inherit_metadata, test_metadata, timeout,
+                      path, protocol)
+
+        self.testdriver = testdriver
+
+    @classmethod
+    def from_manifest(cls, manifest_item, inherit_metadata, test_metadata):
+        timeout = cls.long_timeout if manifest_item.timeout == "long" else cls.default_timeout
+        protocol = "https" if hasattr(manifest_item, "https") and manifest_item.https else "http"
+        testdriver = manifest_item.testdriver if hasattr(manifest_item, "testdriver") else False
+        return cls(manifest_item.source_file.tests_root,
+                   manifest_item.url,
+                   inherit_metadata,
+                   test_metadata,
+                   timeout=timeout,
+                   path=manifest_item.source_file.path,
+                   protocol=protocol,
+                   testdriver=testdriver)
+
     @property
     def id(self):
         return self.url
     @property
     def id(self):
         return self.url
index 7520938..47aad72 100644 (file)
@@ -26,6 +26,7 @@ def key_reporter(session, test_actions_page, request):
     """Represents focused input element from `test_keys_page` fixture."""
     input_el = session.find.css("#keys", all=False)
     input_el.click()
     """Represents focused input element from `test_keys_page` fixture."""
     input_el = session.find.css("#keys", all=False)
     input_el.click()
+    session.execute_script("resetEvents();")
     return input_el
 
 
     return input_el
 
 
index 43887ed..9678f8f 100644 (file)
@@ -38,7 +38,12 @@ def test_single_printable_key_sends_correct_events(session,
         {"code": code, "key": value, "type": "keypress"},
         {"code": code, "key": value, "type": "keyup"},
     ]
         {"code": code, "key": value, "type": "keypress"},
         {"code": code, "key": value, "type": "keyup"},
     ]
-    events = [filter_dict(e, expected[0]) for e in get_events(session)]
+    all_events = get_events(session)
+    events = [filter_dict(e, expected[0]) for e in all_events]
+    if len(events) > 0 and events[0]["code"] == None:
+        # Remove 'code' entry if browser doesn't support it
+        expected = [filter_dict(e, {"key": "", "type": ""}) for e in expected]
+        events = [filter_dict(e, expected[0]) for e in events]
     assert events == expected
     assert get_keys(key_reporter) == value
 
     assert events == expected
     assert get_keys(key_reporter) == value
 
@@ -80,6 +85,10 @@ def test_single_modifier_key_sends_correct_events(session,
         {"code": code, "key": key, "type": "keyup"},
     ]
     events = [filter_dict(e, expected[0]) for e in all_events]
         {"code": code, "key": key, "type": "keyup"},
     ]
     events = [filter_dict(e, expected[0]) for e in all_events]
+    if len(events) > 0 and events[0]["code"] == None:
+        # Remove 'code' entry if browser doesn't support it
+        expected = [filter_dict(e, {"key": "", "type": ""}) for e in expected]
+        events = [filter_dict(e, expected[0]) for e in events]
     assert events == expected
     assert len(get_keys(key_reporter)) == 0
 
     assert events == expected
     assert len(get_keys(key_reporter)) == 0
 
@@ -103,7 +112,12 @@ def test_single_nonprintable_key_sends_events(session,
         {"code": code, "key": key, "type": "keypress"},
         {"code": code, "key": key, "type": "keyup"},
     ]
         {"code": code, "key": key, "type": "keypress"},
         {"code": code, "key": key, "type": "keyup"},
     ]
-    events = [filter_dict(e, expected[0]) for e in get_events(session)]
+    all_events = get_events(session)
+    events = [filter_dict(e, expected[0]) for e in all_events]
+    if len(events) > 0 and events[0]["code"] == None:
+        # Remove 'code' entry if browser doesn't support it
+        expected = [filter_dict(e, {"key": "", "type": ""}) for e in expected]
+        events = [filter_dict(e, expected[0]) for e in events]
     if len(events) == 2:
         # most browsers don't send a keypress for non-printable keys
         assert events == [expected[0], expected[2]]
     if len(events) == 2:
         # most browsers don't send a keypress for non-printable keys
         assert events == [expected[0], expected[2]]
@@ -125,7 +139,12 @@ def test_sequence_of_keydown_printable_keys_sends_events(session,
         {"code": "KeyB", "key": "b", "type": "keydown"},
         {"code": "KeyB", "key": "b", "type": "keypress"},
     ]
         {"code": "KeyB", "key": "b", "type": "keydown"},
         {"code": "KeyB", "key": "b", "type": "keypress"},
     ]
-    events = [filter_dict(e, expected[0]) for e in get_events(session)]
+    all_events = get_events(session)
+    events = [filter_dict(e, expected[0]) for e in all_events]
+    if len(events) > 0 and events[0]["code"] == None:
+        # Remove 'code' entry if browser doesn't support it
+        expected = [filter_dict(e, {"key": "", "type": ""}) for e in expected]
+        events = [filter_dict(e, expected[0]) for e in events]
     assert events == expected
     assert get_keys(key_reporter) == "ab"
 
     assert events == expected
     assert get_keys(key_reporter) == "ab"
 
@@ -140,7 +159,12 @@ def test_sequence_of_keydown_character_keys(session, key_reporter, key_chain):
         {"code": "KeyF", "key": "f", "type": "keypress"},
         {"code": "KeyF", "key": "f", "type": "keyup"},
     ]
         {"code": "KeyF", "key": "f", "type": "keypress"},
         {"code": "KeyF", "key": "f", "type": "keyup"},
     ]
-    events = [filter_dict(e, expected[0]) for e in get_events(session)]
+    all_events = get_events(session)
+    events = [filter_dict(e, expected[0]) for e in all_events]
+    if len(events) > 0 and events[0]["code"] == None:
+        # Remove 'code' entry if browser doesn't support it
+        expected = [filter_dict(e, {"key": "", "type": ""}) for e in expected]
+        events = [filter_dict(e, expected[0]) for e in events]
     assert events == expected
     assert get_keys(key_reporter) == "ef"
 
     assert events == expected
     assert get_keys(key_reporter) == "ef"
 
diff --git a/WebDriverTests/imported/w3c/webdriver/tests/actions/key_shortcuts.py b/WebDriverTests/imported/w3c/webdriver/tests/actions/key_shortcuts.py
new file mode 100644 (file)
index 0000000..ec062f7
--- /dev/null
@@ -0,0 +1,49 @@
+from tests.actions.support.keys import Keys, MODIFIER_KEY
+from tests.actions.support.refine import get_keys
+
+
+def test_mod_a_and_backspace_deletes_all_text(session, key_reporter, key_chain):
+    key_chain.send_keys("abc d") \
+             .key_down(MODIFIER_KEY) \
+             .key_down("a") \
+             .key_up(MODIFIER_KEY) \
+             .key_up("a") \
+             .key_down(Keys.BACKSPACE) \
+             .perform()
+    assert get_keys(key_reporter) == ""
+
+
+def test_mod_a_mod_c_right_mod_v_pastes_text(session, key_reporter, key_chain):
+    initial = "abc d"
+    key_chain.send_keys(initial) \
+             .key_down(MODIFIER_KEY) \
+             .key_down("a") \
+             .key_up(MODIFIER_KEY) \
+             .key_up("a") \
+             .key_down(MODIFIER_KEY) \
+             .key_down("c") \
+             .key_up(MODIFIER_KEY) \
+             .key_up("c") \
+             .send_keys([Keys.RIGHT]) \
+             .key_down(MODIFIER_KEY) \
+             .key_down("v") \
+             .key_up(MODIFIER_KEY) \
+             .key_up("v") \
+             .perform()
+    assert get_keys(key_reporter) == initial * 2
+
+
+def test_mod_a_mod_x_deletes_all_text(session, key_reporter, key_chain):
+    key_chain.send_keys("abc d") \
+             .key_down(MODIFIER_KEY) \
+             .key_down("a") \
+             .key_up(MODIFIER_KEY) \
+             .key_up("a") \
+             .key_down(MODIFIER_KEY) \
+             .key_down("x") \
+             .key_up(MODIFIER_KEY) \
+             .key_up("x") \
+             .perform()
+    assert get_keys(key_reporter) == ""
+
+
diff --git a/WebDriverTests/imported/w3c/webdriver/tests/actions/modifier_click.py b/WebDriverTests/imported/w3c/webdriver/tests/actions/modifier_click.py
new file mode 100644 (file)
index 0000000..73de161
--- /dev/null
@@ -0,0 +1,74 @@
+# META: timeout=long
+
+import pytest
+
+from tests.actions.support.refine import filter_dict, get_events
+from tests.actions.support.keys import Keys
+
+
+@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"),
+])
+def test_modifier_click(session,
+                       test_actions_page,
+                       key_chain,
+                       mouse_chain,
+                       modifier,
+                       prop):
+    key_chain \
+        .pause(0) \
+        .key_down(modifier) \
+        .pause(200) \
+        .key_up(modifier)
+    outer = session.find.css("#outer", all=False)
+    mouse_chain.click(element=outer)
+    session.actions.perform([key_chain.dict, mouse_chain.dict])
+    expected = [
+        {"type": "mousemove"},
+        {"type": "mousedown"},
+        {"type": "mouseup"},
+        {"type": "click"},
+    ]
+    defaults = {
+        "altKey": False,
+        "metaKey": False,
+        "shiftKey": False,
+        "ctrlKey": False
+    }
+    for e in expected:
+        e.update(defaults)
+        if e["type"] != "mousemove":
+            e[prop] = True
+    filtered_events = [filter_dict(e, expected[0]) for e in get_events(session)]
+    assert expected == filtered_events
+
+
+def test_release_control_click(session, key_reporter, key_chain, mouse_chain):
+    key_chain \
+        .pause(0) \
+        .key_down(Keys.CONTROL)
+    mouse_chain \
+        .pointer_move(0, 0, origin=key_reporter) \
+        .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()
+    expected = [
+        {"type": "mouseup"},
+        {"type": "keyup"},
+    ]
+    events = [filter_dict(e, expected[0]) for e in get_events(session)]
+    assert events == expected
index 3ff1e3e..07f7809 100644 (file)
@@ -19,7 +19,7 @@ def get_center(rect):
 
 # TODO use pytest.approx once we upgrade to pytest > 3.0
 def approx(n, m, tolerance=1):
 
 # TODO use pytest.approx once we upgrade to pytest > 3.0
 def approx(n, m, tolerance=1):
-    return abs(n - m) < tolerance
+    return abs(n - m) <= tolerance
 
 
 def test_click_at_coordinates(session, test_actions_page, mouse_chain):
 
 
 def test_click_at_coordinates(session, test_actions_page, mouse_chain):
index 3e83b15..3092034 100644 (file)
@@ -1,7 +1,5 @@
 # META: timeout=long
 
 # META: timeout=long
 
-import pytest
-
 from tests.actions.support.refine import get_keys, filter_dict, get_events
 from tests.actions.support.keys import Keys
 
 from tests.actions.support.refine import get_keys, filter_dict, get_events
 from tests.actions.support.keys import Keys
 
@@ -26,7 +24,12 @@ def test_release_char_sequence_sends_keyup_events_in_reverse(session,
         {"code": "KeyB", "key": "b", "type": "keyup"},
         {"code": "KeyA", "key": "a", "type": "keyup"},
     ]
         {"code": "KeyB", "key": "b", "type": "keyup"},
         {"code": "KeyA", "key": "a", "type": "keyup"},
     ]
-    events = [filter_dict(e, expected[0]) for e in get_events(session)]
+    all_events = get_events(session)
+    events = [filter_dict(e, expected[0]) for e in all_events]
+    if len(events) > 0 and events[0]["code"] == None:
+        # Remove 'code' entry if browser doesn't support it
+        expected = [filter_dict(e, {"key": "", "type": ""}) for e in expected]
+        events = [filter_dict(e, expected[0]) for e in events]
     assert events == expected
 
 
     assert events == expected
 
 
@@ -36,74 +39,6 @@ def test_release_no_actions_sends_no_events(session, key_reporter):
     assert len(get_events(session)) == 0
 
 
     assert len(get_events(session)) == 0
 
 
-@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"),
-])
-def test_control_click(session,
-                       test_actions_page,
-                       key_chain,
-                       mouse_chain,
-                       modifier,
-                       prop):
-    key_chain \
-        .pause(0) \
-        .key_down(modifier) \
-        .pause(200) \
-        .key_up(modifier)
-    outer = session.find.css("#outer", all=False)
-    mouse_chain.click(element=outer)
-    session.actions.perform([key_chain.dict, mouse_chain.dict])
-    expected = [
-        {"type": "mousemove"},
-        {"type": "mousedown"},
-        {"type": "mouseup"},
-        {"type": "click"},
-    ]
-    defaults = {
-        "altKey": False,
-        "metaKey": False,
-        "shiftKey": False,
-        "ctrlKey": False
-    }
-    for e in expected:
-        e.update(defaults)
-        if e["type"] != "mousemove":
-            e[prop] = True
-    filtered_events = [filter_dict(e, expected[0]) for e in get_events(session)]
-    assert expected == filtered_events
-
-
-def test_release_control_click(session, key_reporter, key_chain, mouse_chain):
-    key_chain \
-        .pause(0) \
-        .key_down(Keys.CONTROL)
-    mouse_chain \
-        .pointer_move(0, 0, origin=key_reporter) \
-        .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()
-    expected = [
-        {"type": "mouseup"},
-        {"type": "keyup"},
-    ]
-    events = [filter_dict(e, expected[0]) for e in get_events(session)]
-    assert events == expected
-
-
 def test_many_modifiers_click(session, test_actions_page, key_chain, mouse_chain):
     outer = session.find.css("#outer", all=False)
     key_chain \
 def test_many_modifiers_click(session, test_actions_page, key_chain, mouse_chain):
     outer = session.find.css("#outer", all=False)
     key_chain \
index ee2d13f..f50bbc6 100644 (file)
@@ -1,6 +1,7 @@
 # META: timeout=long
 
 import pytest
 # META: timeout=long
 
 import pytest
+import time
 from tests.actions.support.keys import ALL_EVENTS, Keys
 from tests.actions.support.refine import filter_dict, get_keys, get_events
 
 from tests.actions.support.keys import ALL_EVENTS, Keys
 from tests.actions.support.refine import filter_dict, get_keys, get_events
 
@@ -17,19 +18,24 @@ def test_webdriver_special_key_sends_keydown(session,
         # may interfere with subsequent tests.)
         session.execute_script("""
             document.body.addEventListener("keydown",
         # may interfere with subsequent tests.)
         session.execute_script("""
             document.body.addEventListener("keydown",
-                    (e) => e.preventDefault());
+                    function(e) { e.preventDefault() });
         """)
     key_chain.key_down(getattr(Keys, name)).perform()
         """)
     key_chain.key_down(getattr(Keys, name)).perform()
+
     # only interested in keydown
     first_event = get_events(session)[0]
     # make a copy so we can throw out irrelevant keys and compare to events
     expected = dict(expected)
 
     del expected["value"]
     # only interested in keydown
     first_event = get_events(session)[0]
     # make a copy so we can throw out irrelevant keys and compare to events
     expected = dict(expected)
 
     del expected["value"]
+
     # check and remove keys that aren't in expected
     assert first_event["type"] == "keydown"
     assert first_event["repeat"] == False
     first_event = filter_dict(first_event, expected)
     # check and remove keys that aren't in expected
     assert first_event["type"] == "keydown"
     assert first_event["repeat"] == False
     first_event = filter_dict(first_event, expected)
+    if first_event["code"] == None:
+        del first_event["code"]
+        del expected["code"]
     assert first_event == expected
     # only printable characters should be recorded in input field
     entered_keys = get_keys(key_reporter)
     assert first_event == expected
     # only printable characters should be recorded in input field
     entered_keys = get_keys(key_reporter)
index 8551791..7c71a87 100644 (file)
@@ -20,6 +20,7 @@ The Keys implementation.
 """
 
 from inspect import getmembers
 """
 
 from inspect import getmembers
+import sys
 
 
 class Keys(object):
 
 
 class Keys(object):
@@ -740,3 +741,8 @@ ALL_EVENTS = {
         "value": u"\ue040",
     }
 }
         "value": u"\ue040",
     }
 }
+
+if sys.platform == 'darwin':
+    MODIFIER_KEY = Keys.META
+else:
+    MODIFIER_KEY = Keys.CONTROL
index be27322..6f844cd 100644 (file)
@@ -1,5 +1,6 @@
-<!doctype html>
+<!doctype html>
 <meta charset=utf-8>
 <meta charset=utf-8>
+<html>
 <head>
     <title>Test Actions</title>
     <style>
 <head>
     <title>Test Actions</title>
     <style>
@@ -14,7 +15,7 @@
     <script>
         "use strict";
         var els = {};
     <script>
         "use strict";
         var els = {};
-        var allEvents = {events: []};
+        var allEvents = { events: [] };
         function displayMessage(message) {
             document.getElementById("events").innerHTML = "<p>" + message + "</p>";
         }
         function displayMessage(message) {
             document.getElementById("events").innerHTML = "<p>" + message + "</p>";
         }
             "repeat": event.repeat,
             "type": event.type
           });
             "repeat": event.repeat,
             "type": event.type
           });
-          appendMessage(`${event.type}(` +
-              `code: ${event.code}, ` +
-              `key: ${key}, ` +
-              `which: ${event.which}, ` +
-              `keyCode: ${event.keyCode})`);
+          appendMessage(event.type + " " +
+              "code: " + event.code + ", " +
+              "key: " + key + ", " +
+              "which: " + event.which + ", " +
+              "keyCode: " + event.keyCode);
         }
 
         function recordPointerEvent(event) {
         }
 
         function recordPointerEvent(event) {
             "shiftKey": event.shiftKey,
             "target": event.target.id
           });
             "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})`);
+          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) {
         }
 
         function recordFirstPointerMove(event) {
           window.removeEventListener("mousemove", recordFirstPointerMove);
         }
 
           window.removeEventListener("mousemove", recordFirstPointerMove);
         }
 
+        function grabOnce(event) {
+          grab(event);
+          els.dragTarget.removeEventListener("mousedown", grabOnce);
+        }
+
+        function dropOnce(moveHandler) {
+          return function (event) {
+            moveHandler(event);
+            els.dragArea.removeEventListener("mouseup", dropOnce);
+          }
+        }
+
         function resetEvents() {
         function resetEvents() {
-            allEvents.events.length = 0;
-            displayMessage("");
+          allEvents.events.length = 0;
+          displayMessage("");
         }
 
         function drop(moveHandler) {
         }
 
         function drop(moveHandler) {
         }
 
         function move(el, offsetX, offsetY, timeout) {
         }
 
         function move(el, offsetX, offsetY, timeout) {
-          return (event) => {
-            setTimeout(() => {
+          return function(event) {
+            setTimeout(function() {
               el.style.top = event.clientY + offsetY + "px";
               el.style.left = event.clientX + offsetX + "px";
             }, timeout);
               el.style.top = event.clientY + offsetY + "px";
               el.style.left = event.clientX + offsetX + "px";
             }, timeout);
               -(areaRect.top + (event.clientY - boxRect.top)),
               20);
           els.dragArea.addEventListener("mousemove", moveHandler);
               -(areaRect.top + (event.clientY - boxRect.top)),
               20);
           els.dragArea.addEventListener("mousemove", moveHandler);
-          els.dragArea.addEventListener("mouseup", drop(moveHandler), {once: true});
+          els.dragArea.addEventListener("mouseup", dropOnce(drop(moveHandler)));
         }
 
         }
 
-        document.addEventListener("DOMContentLoaded", () => {
+        document.addEventListener("DOMContentLoaded", function() {
           var keyReporter = document.getElementById("keys");
           var keyReporter = document.getElementById("keys");
-          ["keyup", "keypress", "keydown"].forEach((e) => {
-            keyReporter.addEventListener(e, recordKeyboardEvent);
-          });
+          keyReporter.addEventListener("keyup", recordKeyboardEvent);
+          keyReporter.addEventListener("keypress", recordKeyboardEvent);
+          keyReporter.addEventListener("keydown", recordKeyboardEvent);
+
           var outer = document.getElementById("outer");
           var outer = document.getElementById("outer");
-          ["click", "dblclick", "mousedown",
-              "mouseup", "contextmenu"].forEach((e) => {
-            outer.addEventListener(e, recordPointerEvent);
-          });
-          window.addEventListener("mousemove", recordPointerEvent, {once: true});
+          outer.addEventListener("click", recordPointerEvent);
+          outer.addEventListener("dblclick", recordPointerEvent);
+          outer.addEventListener("mousedown", recordPointerEvent);
+          outer.addEventListener("mouseup", recordPointerEvent);
+          outer.addEventListener("contextmenu", recordPointerEvent);
+
+          window.addEventListener("mousemove", recordFirstPointerMove);
           //visual cue for mousemove
           var pointer = document.getElementById("trackPointer");
           window.addEventListener("mousemove", move(pointer, 15, 15, 30));
           // drag and drop
           els.dragArea = document.getElementById("dragArea");
           els.dragTarget = document.getElementById("dragTarget");
           //visual cue for mousemove
           var pointer = document.getElementById("trackPointer");
           window.addEventListener("mousemove", move(pointer, 15, 15, 30));
           // drag and drop
           els.dragArea = document.getElementById("dragArea");
           els.dragTarget = document.getElementById("dragTarget");
-          els.dragTarget.addEventListener("mousedown", grab, {once: true});
+          els.dragTarget.addEventListener("mousedown", grabOnce);
         });
     </script>
 </head>
         });
     </script>
 </head>
index df45722..86a5f86 100644 (file)
@@ -1,5 +1,6 @@
 from tests.support.fixtures import clear_all_cookies
 from tests.support.fixtures import server_config
 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):
     session.url = url("/common/blank.html")
 
 def test_add_domain_cookie(session, url):
     session.url = url("/common/blank.html")
@@ -74,3 +75,70 @@ def test_add_cookie_for_ip(session, url, server_config):
     assert cookie["name"] == "hello"
     assert cookie["value"] == "world"
     assert cookie["domain"] == "127.0.0.1"
     assert cookie["name"] == "hello"
     assert cookie["value"] == "world"
     assert cookie["domain"] == "127.0.0.1"
+
+def test_add_non_session_cookie(session, url):
+    session.url = url("/common/blank.html")
+    clear_all_cookies(session)
+    a_year_from_now = int((datetime.utcnow() + timedelta(days=365)).strftime("%s"))
+    create_cookie_request = {
+        "cookie": {
+            "name": "hello",
+            "value": "world",
+            "expiry": a_year_from_now
+        }
+    }
+    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 "expiry" in cookie
+    assert isinstance(cookie["expiry"], int)
+
+    assert cookie["name"] == "hello"
+    assert cookie["value"] == "world"
+    assert cookie["expiry"] == a_year_from_now
+
+def test_add_session_cookie(session, url):
+    session.url = url("/common/blank.html")
+    clear_all_cookies(session)
+    create_cookie_request = {
+        "cookie": {
+            "name": "hello",
+            "value": "world"
+        }
+    }
+    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 "expiry" in cookie
+    assert cookie.get("expiry") is None
+
+    assert cookie["name"] == "hello"
+    assert cookie["value"] == "world"
index 654568f..027859d 100644 (file)
@@ -1,8 +1,10 @@
 from tests.support.inline import inline
 from tests.support.fixtures import clear_all_cookies
 from tests.support.inline import inline
 from tests.support.fixtures import clear_all_cookies
+from datetime import datetime, timedelta
 
 
-def test_get_named_cookie(session, url):
+def test_get_named_session_cookie(session, url):
     session.url = url("/common/blank.html")
     session.url = url("/common/blank.html")
+    clear_all_cookies(session)
     session.execute_script("document.cookie = 'foo=bar'")
 
     result = session.transport.send("GET", "session/%s/cookie/foo" % session.session_id)
     session.execute_script("document.cookie = 'foo=bar'")
 
     result = session.transport.send("GET", "session/%s/cookie/foo" % session.session_id)
@@ -25,10 +27,39 @@ def test_get_named_cookie(session, url):
     assert "httpOnly" in cookie
     assert isinstance(cookie["httpOnly"], bool)
     assert "expiry" in cookie
     assert "httpOnly" in cookie
     assert isinstance(cookie["httpOnly"], bool)
     assert "expiry" in cookie
+    assert cookie.get("expiry") is None
+
+    assert cookie["name"] == "foo"
+    assert cookie["value"] == "bar"
+
+def test_get_named_cookie(session, url):
+    session.url = url("/common/blank.html")
+    clear_all_cookies(session)
+
+    # same formatting as Date.toUTCString() in javascript
+    utc_string_format = "%a, %d %b %Y %H:%M:%S"
+    a_year_from_now = (datetime.utcnow() + timedelta(days=365)).strftime(utc_string_format)
+    session.execute_script("document.cookie = 'foo=bar;expires=%s'" % a_year_from_now)
+
+    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 "expiry" in cookie
     assert isinstance(cookie["expiry"], (int, long))
 
     assert cookie["name"] == "foo"
     assert cookie["value"] == "bar"
     assert isinstance(cookie["expiry"], (int, long))
 
     assert cookie["name"] == "foo"
     assert cookie["value"] == "bar"
+    # convert from seconds since epoch
+    assert datetime.utcfromtimestamp(cookie["expiry"]).strftime(utc_string_format) == a_year_from_now
 
 def test_duplicated_cookie(session, url):
     session.url = url("/common/blank.html")
 
 def test_duplicated_cookie(session, url):
     session.url = url("/common/blank.html")
@@ -64,4 +95,3 @@ def test_duplicated_cookie(session, url):
 
     assert cookie["name"] == "hello"
     assert cookie["value"] == "newworld"
 
     assert cookie["name"] == "hello"
     assert cookie["value"] == "newworld"
-
diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_click/stale.py b/WebDriverTests/imported/w3c/webdriver/tests/element_click/stale.py
new file mode 100644 (file)
index 0000000..d39e3a3
--- /dev/null
@@ -0,0 +1,22 @@
+import pytest
+import webdriver
+
+from tests.support.asserts import assert_error
+from tests.support.inline import inline
+
+
+def click_element(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_is_stale(session):
+    session.url = inline("<button>foo</button>")
+    button = session.find.css("button", all=False)
+    session.url = inline("<button>bar</button>")
+
+    response = click_element(session, button)
+    assert_error(response, "stale element reference")
index 900e3fd..9bc6f73 100644 (file)
@@ -1,22 +1,31 @@
-import pytest
-
-from tests.support.asserts import assert_error, assert_success, assert_dialog_handled, assert_same_element
+from tests.support.asserts import assert_error, assert_dialog_handled, assert_same_element
 from tests.support.fixtures import create_dialog
 from tests.support.inline import inline
 
 from tests.support.fixtures import create_dialog
 from tests.support.inline import inline
 
-def assert_result_is_active_element(session, result):
-    """Ensure that the provided object is a successful WebDriver response
-    describing an element reference and that the referenced element matches the
-    element returned by the `activeElement` attribute of the current browsing
-    context's active document."""
-    assert result.status == 200
+def read_global(session, name):
+    return session.execute_script("return %s;" % name)
+
+def get_active_element(session):
+    return session.transport.send("GET", "session/%s/element/active" % session.session_id)
+
 
 
-    from_js = session.execute_script("return document.activeElement;")
+def assert_is_active_element(session, response):
+    """Ensure that the provided object is a successful WebDriver
+    response describing an element reference and that the referenced
+    element matches the element returned by the `activeElement`
+    attribute of the current browsing context's active document.
 
 
-    if result.body["value"] is None:
-        assert from_js == None
+    """
+    assert response.status == 200
+    assert "value" in response.body
+
+    from_js = session.execute_script("return document.activeElement")
+
+    if response.body["value"] is None:
+        assert from_js is None
     else:
     else:
-        assert_same_element(session, result.body["value"], from_js)
+        assert_same_element(session, response.body["value"], from_js)
+
 
 # > 1. If the current browsing context is no longer open, return error with
 # >    error code no such window.
 
 # > 1. If the current browsing context is no longer open, return error with
 # >    error code no such window.
@@ -25,10 +34,9 @@ def test_closed_context(session, create_window):
     session.window_handle = new_window
     session.close()
 
     session.window_handle = new_window
     session.close()
 
-    result = session.transport.send("GET",
-                                    "session/%s/element/active" % session.session_id)
+    response = get_active_element(session)
+    assert_error(response, "no such window")
 
 
-    assert_error(result, "no such window")
 
 # [...]
 # 2. Handle any user prompts and return its value if it is an error.
 
 # [...]
 # 2. Handle any user prompts and return its value if it is an error.
@@ -49,30 +57,25 @@ def test_handle_prompt_dismiss(new_session, add_browser_capabilites):
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
-    result = session.transport.send("GET",
-                                    "session/%s/element/active" % session.session_id)
-
-    assert_result_is_active_element(session, result)
+    response = get_active_element(session)
+    assert_is_active_element(session, response)
     assert_dialog_handled(session, "dismiss #1")
     assert_dialog_handled(session, "dismiss #1")
-    assert session.execute_script("return dismiss1;") == None
+    assert session.execute_script("return dismiss1") is None
 
     create_dialog(session)("confirm", text="dismiss #2", result_var="dismiss2")
 
 
     create_dialog(session)("confirm", text="dismiss #2", result_var="dismiss2")
 
-    result = session.transport.send("GET",
-                                    "session/%s/element/active" % session.session_id)
-
-    assert_result_is_active_element(session, result)
+    response = get_active_element(session)
+    assert_is_active_element(session, response)
     assert_dialog_handled(session, "dismiss #2")
     assert_dialog_handled(session, "dismiss #2")
-    assert read_global(session, "dismiss2") == None
+    assert read_global(session, "dismiss2") is None
 
     create_dialog(session)("prompt", text="dismiss #3", result_var="dismiss3")
 
 
     create_dialog(session)("prompt", text="dismiss #3", result_var="dismiss3")
 
-    result = session.transport.send("GET",
-                                    "session/%s/element/active" % session.session_id)
-
-    assert_result_is_active_element(session, result)
+    response = get_active_element(session)
+    assert_is_active_element(session, response)
     assert_dialog_handled(session, "dismiss #3")
     assert_dialog_handled(session, "dismiss #3")
-    assert read_global(session, "dismiss3") == None
+    assert read_global(session, "dismiss3") is None
+
 
 # [...]
 # 2. Handle any user prompts and return its value if it is an error.
 
 # [...]
 # 2. Handle any user prompts and return its value if it is an error.
@@ -92,30 +95,25 @@ def test_handle_prompt_accept(new_session, add_browser_capabilites):
     session.url = inline("<body><p>Hello, World!</p></body>")
     create_dialog(session)("alert", text="accept #1", result_var="accept1")
 
     session.url = inline("<body><p>Hello, World!</p></body>")
     create_dialog(session)("alert", text="accept #1", result_var="accept1")
 
-    result = session.transport.send("GET",
-                                    "session/%s/element/active" % session.session_id)
-
-    assert_result_is_active_element(session, result)
+    response = get_active_element(session)
+    assert_is_active_element(session, response)
     assert_dialog_handled(session, "accept #1")
     assert_dialog_handled(session, "accept #1")
-    assert read_global(session, "accept1") == None
+    assert read_global(session, "accept1") is None
 
     create_dialog(session)("confirm", text="accept #2", result_var="accept2")
 
 
     create_dialog(session)("confirm", text="accept #2", result_var="accept2")
 
-    result = session.transport.send("GET",
-                                    "session/%s/element/active" % session.session_id)
-
-    assert_result_is_active_element(session, result)
+    response = get_active_element(session)
+    assert_is_active_element(session, response)
     assert_dialog_handled(session, "accept #2")
     assert_dialog_handled(session, "accept #2")
-    assert read_global(session, "accept2"), True
+    assert read_global(session, "accept2") is True
 
     create_dialog(session)("prompt", text="accept #3", result_var="accept3")
 
 
     create_dialog(session)("prompt", text="accept #3", result_var="accept3")
 
-    result = session.transport.send("GET",
-                                    "session/%s/element/active" % session.session_id)
-
-    assert_result_is_active_element(session, result)
+    response = get_active_element(session)
+    assert_is_active_element(session, response)
     assert_dialog_handled(session, "accept #3")
     assert_dialog_handled(session, "accept #3")
-    assert read_global(session, "accept3") == ""
+    assert read_global(session, "accept3") == "" or read_global(session, "accept3") == "undefined"
+
 
 # [...]
 # 2. Handle any user prompts and return its value if it is an error.
 
 # [...]
 # 2. Handle any user prompts and return its value if it is an error.
@@ -134,30 +132,25 @@ def test_handle_prompt_missing_value(session, create_dialog):
 
     create_dialog("alert", text="dismiss #1", result_var="dismiss1")
 
 
     create_dialog("alert", text="dismiss #1", result_var="dismiss1")
 
-    result = session.transport.send("GET",
-                                    "session/%s/element/active" % session.session_id)
-
-    assert_error(result, "unexpected alert open")
+    response = get_active_element(session)
+    assert_error(response, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #1")
     assert_dialog_handled(session, "dismiss #1")
-    assert session.execute_script("return accept1;") == None
+    assert session.execute_script("return dismiss1") is None
 
     create_dialog("confirm", text="dismiss #2", result_var="dismiss2")
 
 
     create_dialog("confirm", text="dismiss #2", result_var="dismiss2")
 
-    result = session.transport.send("GET",
-                                    "session/%s/element/active" % session.session_id)
-
-    assert_error(result, "unexpected alert open")
+    response = get_active_element(session)
+    assert_error(response, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #2")
     assert_dialog_handled(session, "dismiss #2")
-    assert session.execute_script("return dismiss2;") == False
+    assert session.execute_script("return dismiss2") is False
 
     create_dialog("prompt", text="dismiss #3", result_var="dismiss3")
 
 
     create_dialog("prompt", text="dismiss #3", result_var="dismiss3")
 
-    result = session.transport.send("GET",
-                                    "session/%s/element/active" % session.session_id)
-
-    assert_error(result, "unexpected alert open")
+    response = get_active_element(session)
+    assert_error(response, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #3")
     assert_dialog_handled(session, "dismiss #3")
-    assert session.execute_script("return dismiss3;") == None
+    assert session.execute_script("return dismiss3") is None
+
 
 # > [...]
 # > 3. Let active element be the active element of the current browsing
 
 # > [...]
 # > 3. Let active element be the active element of the current browsing
@@ -170,36 +163,36 @@ def test_success_document(session):
             <h1>Heading</h1>
             <input />
             <input />
             <h1>Heading</h1>
             <input />
             <input />
-            <input style="opacity: 0;" />
+            <input style="opacity: 0" />
             <p>Another element</p>
         </body>""")
             <p>Another element</p>
         </body>""")
-    result = session.transport.send("GET", "session/%s/element/active" % session.session_id)
+    response = get_active_element(session)
+    assert_is_active_element(session, response)
 
 
-    assert_result_is_active_element(session, result)
 
 def test_sucess_input(session):
     session.url = inline("""
         <body>
             <h1>Heading</h1>
             <input autofocus />
 
 def test_sucess_input(session):
     session.url = inline("""
         <body>
             <h1>Heading</h1>
             <input autofocus />
-            <input style="opacity: 0;" />
+            <input style="opacity: 0" />
             <p>Another element</p>
         </body>""")
             <p>Another element</p>
         </body>""")
-    result = session.transport.send("GET", "session/%s/element/active" % session.session_id)
+    response = get_active_element(session)
+    assert_is_active_element(session, response)
 
 
-    assert_result_is_active_element(session, result)
 
 def test_sucess_input_non_interactable(session):
     session.url = inline("""
         <body>
             <h1>Heading</h1>
             <input />
 
 def test_sucess_input_non_interactable(session):
     session.url = inline("""
         <body>
             <h1>Heading</h1>
             <input />
-            <input style="opacity: 0;" autofocus />
+            <input style="opacity: 0" autofocus />
             <p>Another element</p>
         </body>""")
             <p>Another element</p>
         </body>""")
-    result = session.transport.send("GET", "session/%s/element/active" % session.session_id)
+    response = get_active_element(session)
+    assert_is_active_element(session, response)
 
 
-    assert_result_is_active_element(session, result)
 
 def test_success_explicit_focus(session):
     session.url = inline("""
 
 def test_success_explicit_focus(session):
     session.url = inline("""
@@ -209,44 +202,56 @@ def test_success_explicit_focus(session):
             <iframe></iframe>
         </body>""")
 
             <iframe></iframe>
         </body>""")
 
-    session.execute_script("document.body.getElementsByTagName('h1')[0].focus();")
-    result = session.transport.send("GET", "session/%s/element/active" % session.session_id)
-    assert_result_is_active_element(session, result)
+    session.execute_script("document.body.getElementsByTagName('h1')[0].focus()")
+    response = get_active_element(session)
+    assert_is_active_element(session, response)
 
 
-    session.execute_script("document.body.getElementsByTagName('input')[0].focus();")
-    result = session.transport.send("GET", "session/%s/element/active" % session.session_id)
-    assert_result_is_active_element(session, result)
+    session.execute_script("document.body.getElementsByTagName('input')[0].focus()")
+    response = get_active_element(session)
+    assert_is_active_element(session, response)
 
 
-    session.execute_script("document.body.getElementsByTagName('iframe')[0].focus();")
-    result = session.transport.send("GET", "session/%s/element/active" % session.session_id)
-    assert_result_is_active_element(session, result)
+    session.execute_script("document.body.getElementsByTagName('iframe')[0].focus()")
+    response = get_active_element(session)
+    assert_is_active_element(session, response)
 
     session.execute_script("document.body.getElementsByTagName('iframe')[0].focus();")
 
     session.execute_script("document.body.getElementsByTagName('iframe')[0].focus();")
-    session.execute_script("document.body.getElementsByTagName('iframe')[0].remove();")
-    result = session.transport.send("GET", "session/%s/element/active" % session.session_id)
-    assert_result_is_active_element(session, result)
+    session.execute_script("""
+        var iframe = document.body.getElementsByTagName('iframe')[0];
+        if (iframe.remove) {
+          iframe.remove();
+        } else {
+          iframe.removeNode(true);
+        }""")
+    response = get_active_element(session)
+    assert_is_active_element(session, response)
+
+    session.execute_script("document.body.appendChild(document.createElement('textarea'))")
+    response = get_active_element(session)
+    assert_is_active_element(session, response)
 
 
-    session.execute_script("document.body.appendChild(document.createElement('textarea'));")
-    result = session.transport.send("GET", "session/%s/element/active" % session.session_id)
-    assert_result_is_active_element(session, result)
 
 def test_success_iframe_content(session):
     session.url = inline("<body></body>")
     session.execute_script("""
 
 def test_success_iframe_content(session):
     session.url = inline("<body></body>")
     session.execute_script("""
-        var iframe = document.createElement('iframe');
+        let iframe = document.createElement('iframe');
         document.body.appendChild(iframe);
         document.body.appendChild(iframe);
-        var input = iframe.contentDocument.createElement('input');
+        let input = iframe.contentDocument.createElement('input');
         iframe.contentDocument.body.appendChild(input);
         iframe.contentDocument.body.appendChild(input);
-        input.focus();""")
+        input.focus();
+        """)
 
 
-    result = session.transport.send("GET", "session/%s/element/active" % session.session_id)
+    response = get_active_element(session)
+    assert_is_active_element(session, response)
 
 
-    assert_result_is_active_element(session, result)
 
 def test_sucess_without_body(session):
     session.url = inline("<body></body>")
 
 def test_sucess_without_body(session):
     session.url = inline("<body></body>")
-    session.execute_script("document.body.remove();")
-
-    result = session.transport.send("GET", "session/%s/element/active"% session.session_id)
-
-    assert_result_is_active_element(session, result)
+    session.execute_script("""
+        if (document.body.remove) {
+          document.body.remove();
+        } else {
+          document.body.removeNode(true);
+        }""")
+
+    response = get_active_element(session)
+    assert_is_active_element(session, response)
diff --git a/WebDriverTests/imported/w3c/webdriver/tests/interaction/element_clear.py b/WebDriverTests/imported/w3c/webdriver/tests/interaction/element_clear.py
new file mode 100644 (file)
index 0000000..0712290
--- /dev/null
@@ -0,0 +1,185 @@
+import pytest
+from tests.support.asserts import assert_error, assert_success
+from tests.support.inline import inline
+
+
+def clear(session, element):
+    return session.transport.send("POST", "session/{session_id}/element/{element_id}/clear"
+                                  .format(session_id=session.session_id,
+                                          element_id=element.id))
+
+
+# 14.2 Element Clear
+
+def test_no_browsing_context(session, create_window):
+    # 14.2 step 1
+    session.url = inline("<p>This is not an editable paragraph.")
+    element = session.find.css("p", all=False)
+
+    session.window_handle = create_window()
+    session.close()
+
+    response = clear(session, element)
+    assert_error(response, "no such window")
+
+
+def test_element_not_found(session):
+    # 14.2 Step 2
+    response = session.transport.send("POST", "session/{session_id}/element/{element_id}/clear"
+                                      .format(session_id=session.session_id,
+                                              element_id="box1"))
+
+    assert_error(response, "no such element")
+
+
+def test_element_not_editable(session):
+    # 14.2 Step 3
+    session.url = inline("<p>This is not an editable paragraph.")
+
+    element = session.find.css("p", all=False)
+    response = clear(session, element)
+    assert_error(response, "invalid element state")
+
+
+def test_button_element_not_resettable(session):
+    # 14.2 Step 3
+    session.url = inline("<input type=button value=Federer>")
+
+    element = session.find.css("input", all=False)
+    response = clear(session, element)
+    assert_error(response, "invalid element state")
+
+
+def test_disabled_element_not_resettable(session):
+    # 14.2 Step 3
+    session.url = inline("<input type=text value=Federer disabled>")
+
+    element = session.find.css("input", all=False)
+    response = clear(session, element)
+    assert_error(response, "invalid element state")
+
+
+def test_scroll_into_element_view(session):
+    # 14.2 Step 4
+    session.url = inline("<input type=text value=Federer><div style= \"height: 200vh; width: 5000vh\">")
+
+    # Scroll to the bottom right of the page
+    session.execute_script("window.scrollTo(document.body.scrollWidth, document.body.scrollHeight);")
+    element = session.find.css("input", all=False)
+    # Clear and scroll back to the top of the page
+    response = clear(session, element)
+    assert_success(response)
+
+    # Check if element cleared is scrolled into view
+    rect = session.execute_script("return document.getElementsByTagName(\"input\")[0].getBoundingClientRect()")
+
+    pageDict = {}
+
+    pageDict["innerHeight"] = session.execute_script("return window.innerHeight")
+    pageDict["innerWidth"] = session.execute_script("return window.innerWidth")
+    pageDict["pageXOffset"] = session.execute_script("return window.pageXOffset")
+    pageDict["pageYOffset"] = session.execute_script("return window.pageYOffset")
+
+    assert rect["top"] < (pageDict["innerHeight"] + pageDict["pageYOffset"]) and \
+           rect["left"] < (pageDict["innerWidth"] + pageDict["pageXOffset"]) and \
+           (rect["top"] + element.rect["height"]) > pageDict["pageYOffset"] and \
+           (rect["left"] + element.rect["width"]) > pageDict["pageXOffset"]
+
+
+# TODO
+# Any suggestions on implementation?
+# def test_session_implicit_wait_timeout(session):
+    # 14.2 Step 5
+
+# TODO
+# Any suggestions on implementation?
+# def test_element_not_interactable(session):
+#     # 14.2 Step 6
+#     assert_error(response, "element not interactable")
+
+
+def test_element_readonly(session):
+    # 14.2 Step 7
+    session.url = inline("<input type=text readonly value=Federer>")
+
+    element = session.find.css("input", all=False)
+    response = clear(session, element)
+    assert_error(response, "invalid element state")
+
+
+def test_element_disabled(session):
+    # 14.2 Step 7
+    session.url = inline("<input type=text disabled value=Federer>")
+
+    element = session.find.css("input", all=False)
+    response = clear(session, element)
+    assert_error(response, "invalid element state")
+
+
+def test_element_pointer_events_disabled(session):
+    # 14.2 Step 7
+    session.url = inline("<input type=text value=Federer style=\"pointer-events: none\">")
+
+    element = session.find.css("input", all=False)
+    response = clear(session, element)
+    assert_error(response, "invalid element state")
+
+
+@pytest.mark.parametrize("element", [["text", "<input id=text type=text value=\"Federer\"><input id=empty type=text value=\"\">"],
+                                    ["search", "<input id=search type=search value=\"Federer\"><input id=empty type=search value=\"\">"],
+                                    ["url", "<input id=url type=url value=\"www.hello.com\"><input id=empty type=url value=\"\">"],
+                                    ["tele", "<input id=tele type=telephone value=\"2061234567\"><input id=empty type=telephone value=\"\">"],
+                                    ["email", "<input id=email type=email value=\"hello@world.com\"><input id=empty type=email value=\"\">"],
+                                    ["password", "<input id=password type=password value=\"pass123\"><input id=empty type=password value=\"\">"],
+                                    ["date", "<input id=date type=date value=\"2017-12-25\"><input id=empty type=date value=\"\">"],
+                                    ["time", "<input id=time type=time value=\"11:11\"><input id=empty type=time value=\"\">"],
+                                    ["number", "<input id=number type=number value=\"19\"><input id=empty type=number value=\"\">"],
+                                    ["range", "<input id=range type=range min=\"0\" max=\"10\"><input id=empty type=range value=\"\">"],
+                                    ["color", "<input id=color type=color value=\"#ff0000\"><input id=empty type=color value=\"\">"],
+                                    ["file", "<input id=file type=file value=\"C:\\helloworld.txt\"><input id=empty type=file value=\"\">"],
+                                    ["textarea", "<textarea id=textarea>Hello World</textarea><textarea id=empty></textarea>"],
+                                    ["sel", "<select id=sel><option></option><option>a</option><option>b</option></select><select id=empty><option></option></select>"],
+                                    ["out", "<output id=out value=100></output><output id=empty></output>"],
+                                    ["para", "<p id=para contenteditable=true>This is an editable paragraph.</p><p id=empty contenteditable=true></p>"]])
+
+def test_clear_content_editable_resettable_element(session, element):
+    # 14.2 Step 8
+    url = element[1] + """<input id=focusCheck type=checkbox>
+                    <input id=blurCheck type=checkbox>
+                    <script>
+                    var id = %s
+                    document.getElementById("id").addEventListener("focus", checkFocus);
+                    document.getElementById("id").addEventListener("blur", checkBlur);
+                    document.getElementById("empty").addEventListener("focus", checkFocus);
+                    document.getElementById("empty").addEventListener("blur", checkBlur);
+
+                    function checkFocus() {
+                        document.getElementById("focusCheck").checked = true;
+                    }
+                    function checkBlur() {
+                        document.getElementById("blurCheck").checked = true;
+                    }
+                    </script>""" % element[0]
+    session.url = inline(url)
+    # Step 1
+    empty_element = session.find.css("#empty", all=False)
+    test_clear_element_helper(session, empty_element, False)
+    session.execute_script("document.getElementById(\"focusCheck\").checked = false;")
+    session.execute_script("document.getElementById(\"blurCheck\").checked = false;")
+    # Step 2 - 4
+    test_element = session.find.css("#" + element[0], all=False)
+    test_clear_element_helper(session, test_element, True)
+
+
+def test_clear_element_helper(session, element, value):
+    response = clear(session, element)
+    assert_success(response)
+    response = session.execute_script("return document.getElementById(\"focusCheck\").checked;")
+    assert response is value
+    response = session.execute_script("return document.getElementById(\"blurCheck\").checked;")
+    assert response is value
+    if element.name == "p":
+        response = session.execute_script("return document.getElementById(\"para\").innerHTML;")
+        assert response == ""
+    else:
+        assert element.property("value") == ""
index 7416136..3edee84 100644 (file)
@@ -55,7 +55,7 @@ def test_title_handle_prompt_dismiss(new_session, add_browser_capabilites):
 
     assert_success(result, expected_title)
     assert_dialog_handled(session, "dismiss #2")
 
     assert_success(result, expected_title)
     assert_dialog_handled(session, "dismiss #2")
-    assert read_global(session, "dismiss2") == None
+    assert read_global(session, "dismiss2") == False
 
     expected_title = read_global(session, "document.title")
     create_dialog(session)("prompt", text="dismiss #3", result_var="dismiss3")
 
     expected_title = read_global(session, "document.title")
     create_dialog(session)("prompt", text="dismiss #3", result_var="dismiss3")
@@ -101,7 +101,7 @@ def test_title_handle_prompt_accept(new_session, add_browser_capabilites):
 
     assert_success(result, expected_title)
     assert_dialog_handled(session, "accept #2")
 
     assert_success(result, expected_title)
     assert_dialog_handled(session, "accept #2")
-    assert read_global(session, "accept2"), True
+    assert read_global(session, "accept2") == True
 
     expected_title = read_global(session, "document.title")
     create_dialog(session)("prompt", text="accept #3", result_var="accept3")
 
     expected_title = read_global(session, "document.title")
     create_dialog(session)("prompt", text="accept #3", result_var="accept3")
@@ -111,7 +111,7 @@ def test_title_handle_prompt_accept(new_session, add_browser_capabilites):
 
     assert_success(result, expected_title)
     assert_dialog_handled(session, "accept #3")
 
     assert_success(result, expected_title)
     assert_dialog_handled(session, "accept #3")
-    assert read_global(session, "accept3") == ""
+    assert read_global(session, "accept3") == "" or read_global(session, "accept3") == "undefined"
 
 # [...]
 # 2. Handle any user prompts and return its value if it is an error.
 
 # [...]
 # 2. Handle any user prompts and return its value if it is an error.
@@ -279,4 +279,4 @@ def test_title_from_frame(session, create_frame):
     result = session.transport.send("GET",
                                     "session/%s/title" % session.session_id)
 
     result = session.transport.send("GET",
                                     "session/%s/title" % session.session_id)
 
-    assert_success(result, read_global(session, "document.title"))
+    assert_success(result, "Parent")
index d59c782..a0047eb 100644 (file)
@@ -1,7 +1,6 @@
 import pytest
 
 import pytest
 
-
-from tests.support.asserts import assert_error, assert_success
+from tests.support.asserts import assert_error, assert_same_element, assert_success
 from tests.support.inline import inline
 
 
 from tests.support.inline import inline
 
 
@@ -57,3 +56,18 @@ def test_no_element(session, using, value):
     # Step 8 - 9
     response = find_element(session, using, value)
     assert_error(response, "no such element")
     # Step 8 - 9
     response = find_element(session, using, value)
     assert_error(response, "no such element")
+
+
+@pytest.mark.parametrize("using,value",
+                         [("css selector", "#linkText"),
+                          ("link text", "full link text"),
+                          ("partial link text", "link text"),
+                          ("tag name", "a"),
+                          ("xpath", "//*[name()='a']")])
+def test_xhtml_namespace(session, using, value):
+    session.url = inline("""<a href="#" id="linkText">full link text</a>""", doctype="xhtml")
+    expected = session.execute_script("return document.links[0]")
+
+    response = find_element(session, using, value)
+    value = assert_success(response)
+    assert_same_element(session, value, expected)
index d1d8dd5..3bf1d86 100644 (file)
@@ -1,7 +1,6 @@
 import pytest
 
 import pytest
 
-
-from tests.support.asserts import assert_error, assert_success
+from tests.support.asserts import assert_error, assert_same_element, assert_success
 from tests.support.inline import inline
 
 
 from tests.support.inline import inline
 
 
@@ -57,3 +56,19 @@ def test_no_element(session, using, value):
     element = session.find.css("div", all=False)
     response = find_element(session, element.id, using, value)
     assert_error(response, "no such element")
     element = session.find.css("div", all=False)
     response = find_element(session, element.id, using, value)
     assert_error(response, "no such element")
+
+
+@pytest.mark.parametrize("using,value",
+                         [("css selector", "#linkText"),
+                          ("link text", "full link text"),
+                          ("partial link text", "link text"),
+                          ("tag name", "a"),
+                          ("xpath", "//*[name()='a']")])
+def test_xhtml_namespace(session, 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")""")
+    expected = session.execute_script("return document.links[0]")
+
+    response = find_element(session, from_element.id, using, value)
+    value = assert_success(response)
+    assert_same_element(session, value, expected)
index 116f5dd..933d69d 100644 (file)
@@ -1,7 +1,6 @@
 import pytest
 
 import pytest
 
-
-from tests.support.asserts import assert_error, assert_success
+from tests.support.asserts import assert_error, assert_same_element, assert_success
 from tests.support.inline import inline
 
 
 from tests.support.inline import inline
 
 
@@ -56,3 +55,23 @@ def test_no_element(session, using, value):
     element = session.find.css("div", all=False)
     response = find_elements(session, element.id, using, value)
     assert response.body["value"] == []
     element = session.find.css("div", all=False)
     response = find_elements(session, element.id, using, value)
     assert response.body["value"] == []
+
+
+@pytest.mark.parametrize("using,value",
+                         [("css selector", "#linkText"),
+                          ("link text", "full link text"),
+                          ("partial link text", "link text"),
+                          ("tag name", "a"),
+                          ("xpath", "//*[name()='a']")])
+def test_xhtml_namespace(session, 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")""")
+    expected = session.execute_script("return document.links[0]")
+
+    response = find_elements(session, from_element.id, using, value)
+    value = assert_success(response)
+    assert isinstance(value, list)
+    assert len(value) == 1
+
+    found_element = value[0]
+    assert_same_element(session, found_element, expected)
index 3812694..8bc2113 100644 (file)
@@ -1,6 +1,6 @@
 import pytest
 
 import pytest
 
-from tests.support.asserts import assert_error, assert_success
+from tests.support.asserts import assert_error, assert_same_element, assert_success
 from tests.support.inline import inline
 
 
 from tests.support.inline import inline
 
 
@@ -58,3 +58,22 @@ def test_no_element(session, using, value):
     response = find_elements(session, using, value)
     assert_success(response)
     assert response.body["value"] == []
     response = find_elements(session, using, value)
     assert_success(response)
     assert response.body["value"] == []
+
+
+@pytest.mark.parametrize("using,value",
+                         [("css selector", "#linkText"),
+                          ("link text", "full link text"),
+                          ("partial link text", "link text"),
+                          ("tag name", "a"),
+                          ("xpath", "//*[name()='a']")])
+def test_xhtml_namespace(session, using, value):
+    session.url = inline("""<p><a href="#" id="linkText">full link text</a></p>""", doctype="xhtml")
+    expected = session.execute_script("return document.links[0]")
+
+    response = find_elements(session, using, value)
+    value = assert_success(response)
+    assert isinstance(value, list)
+    assert len(value) == 1
+
+    found_element = value[0]
+    assert_same_element(session, found_element, expected)
index d08eda1..1301a45 100644 (file)
@@ -28,24 +28,25 @@ def test_handle_prompt_dismiss(new_session, add_browser_capabilites):
     # 13.2 step 2
     _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "dismiss"})}})
     session.url = inline("<input id=foo>")
     # 13.2 step 2
     _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "dismiss"})}})
     session.url = inline("<input id=foo>")
+    element = session.find.css("#foo", all=False)
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
-    result = get_attribute(session, "foo", "id")
+    result = get_attribute(session, element.id, "id")
 
     assert_success(result, "foo")
     assert_dialog_handled(session, "dismiss #1")
 
     create_dialog(session)("confirm", text="dismiss #2", result_var="dismiss2")
 
 
     assert_success(result, "foo")
     assert_dialog_handled(session, "dismiss #1")
 
     create_dialog(session)("confirm", text="dismiss #2", result_var="dismiss2")
 
-    result = get_attribute(session, "foo", "id")
+    result = get_attribute(session, element.id, "id")
 
     assert_success(result, "foo")
     assert_dialog_handled(session, "dismiss #2")
 
     create_dialog(session)("prompt", text="dismiss #3", result_var="dismiss3")
 
 
     assert_success(result, "foo")
     assert_dialog_handled(session, "dismiss #2")
 
     create_dialog(session)("prompt", text="dismiss #3", result_var="dismiss3")
 
-    result = get_attribute(session, "foo", "id")
+    result = get_attribute(session, element.id, "id")
 
     assert_success(result, "foo")
     assert_dialog_handled(session, "dismiss #3")
 
     assert_success(result, "foo")
     assert_dialog_handled(session, "dismiss #3")
@@ -55,50 +56,52 @@ def test_handle_prompt_accept(new_session, add_browser_capabilites):
     # 13.2 step 2
     _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "accept"})}})
     session.url = inline("<input id=foo>")
     # 13.2 step 2
     _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "accept"})}})
     session.url = inline("<input id=foo>")
+    element = session.find.css("#foo", all=False)
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
-    result = get_attribute(session, "foo", "id")
+    result = get_attribute(session, element.id, "id")
 
     assert_success(result, "foo")
     assert_dialog_handled(session, "dismiss #1")
 
     create_dialog(session)("confirm", text="dismiss #2", result_var="dismiss2")
 
 
     assert_success(result, "foo")
     assert_dialog_handled(session, "dismiss #1")
 
     create_dialog(session)("confirm", text="dismiss #2", result_var="dismiss2")
 
-    result = get_attribute(session, "foo", "id")
+    result = get_attribute(session, element.id, "id")
 
     assert_success(result, "foo")
     assert_dialog_handled(session, "dismiss #2")
 
     create_dialog(session)("prompt", text="dismiss #3", result_var="dismiss3")
 
 
     assert_success(result, "foo")
     assert_dialog_handled(session, "dismiss #2")
 
     create_dialog(session)("prompt", text="dismiss #3", result_var="dismiss3")
 
-    result = get_attribute(session, "foo", "id")
+    result = get_attribute(session, element.id, "id")
 
     assert_success(result, "foo")
     assert_dialog_handled(session, "dismiss #3")
 
 
 
     assert_success(result, "foo")
     assert_dialog_handled(session, "dismiss #3")
 
 
-def test_handle_prompt_missing_value(session, create_dialog):
+def test_handle_prompt_missing_value(session):
     # 13.2 step 2
     session.url = inline("<input id=foo>")
     # 13.2 step 2
     session.url = inline("<input id=foo>")
+    element = session.find.css("#foo", all=False)
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
-    result = get_attribute(session, "foo", "id")
+    result = get_attribute(session, element.id, "id")
 
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #1")
 
     create_dialog(session)("confirm", text="dismiss #2", result_var="dismiss2")
 
 
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #1")
 
     create_dialog(session)("confirm", text="dismiss #2", result_var="dismiss2")
 
-    result = get_attribute(session, "foo", "id")
+    result = get_attribute(session, element.id, "id")
 
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #2")
 
     create_dialog(session)("prompt", text="dismiss #3", result_var="dismiss3")
 
 
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #2")
 
     create_dialog(session)("prompt", text="dismiss #3", result_var="dismiss3")
 
-    result = get_attribute(session, "foo", "id")
+    result = get_attribute(session, element.id, "id")
 
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #3")
 
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #3")
index d84339d..c76c561 100644 (file)
@@ -23,12 +23,13 @@ def test_handle_prompt_dismiss(new_session, add_browser_capabilites):
     # 13.3 step 2
     _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "dismiss"})}})
     session.url = inline("<input id=foo>")
     # 13.3 step 2
     _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "dismiss"})}})
     session.url = inline("<input id=foo>")
+    element = session.find.css("#foo", all=False)
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/property/id"
                                     .format(session_id=session.session_id,
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/property/id"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
     assert_success(result, "foo")
     assert_dialog_handled(session, "dismiss #1")
 
     assert_success(result, "foo")
     assert_dialog_handled(session, "dismiss #1")
@@ -37,7 +38,7 @@ def test_handle_prompt_dismiss(new_session, add_browser_capabilites):
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/property/id"
                                     .format(session_id=session.session_id,
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/property/id"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
     assert_success(result, "foo")
     assert_dialog_handled(session, "dismiss #2")
 
     assert_success(result, "foo")
     assert_dialog_handled(session, "dismiss #2")
@@ -46,7 +47,7 @@ def test_handle_prompt_dismiss(new_session, add_browser_capabilites):
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/property/id"
                                     .format(session_id=session.session_id,
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/property/id"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
     assert_success(result, "foo")
     assert_dialog_handled(session, "dismiss #3")
 
     assert_success(result, "foo")
     assert_dialog_handled(session, "dismiss #3")
@@ -57,12 +58,13 @@ def test_handle_prompt_accept(new_session, add_browser_capabilites):
     # 13.3 step 2
     _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "accept"})}})
     session.url = inline("<input id=foo>")
     # 13.3 step 2
     _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "accept"})}})
     session.url = inline("<input id=foo>")
+    element = session.find.css("#foo", all=False)
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/property/id"
                                     .format(session_id=session.session_id,
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/property/id"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
     assert_success(result, "foo")
     assert_dialog_handled(session, "dismiss #1")
 
     assert_success(result, "foo")
     assert_dialog_handled(session, "dismiss #1")
@@ -71,7 +73,7 @@ def test_handle_prompt_accept(new_session, add_browser_capabilites):
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/property/id"
                                     .format(session_id=session.session_id,
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/property/id"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
     assert_success(result, "foo")
     assert_dialog_handled(session, "dismiss #2")
 
     assert_success(result, "foo")
     assert_dialog_handled(session, "dismiss #2")
@@ -80,21 +82,22 @@ def test_handle_prompt_accept(new_session, add_browser_capabilites):
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/property/id"
                                     .format(session_id=session.session_id,
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/property/id"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
     assert_success(result, "foo")
     assert_dialog_handled(session, "dismiss #3")
 
 
 
     assert_success(result, "foo")
     assert_dialog_handled(session, "dismiss #3")
 
 
-def test_handle_prompt_missing_value(session, create_dialog):
+def test_handle_prompt_missing_value(session):
     # 13.3 step 2
     session.url = inline("<input id=foo>")
     # 13.3 step 2
     session.url = inline("<input id=foo>")
+    element = session.find.css("#foo", all=False)
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/property/id"
                                     .format(session_id=session.session_id,
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/property/id"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #1")
 
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #1")
@@ -103,7 +106,7 @@ def test_handle_prompt_missing_value(session, create_dialog):
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/property/id"
                                     .format(session_id=session.session_id,
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/property/id"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #2")
 
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #2")
@@ -112,7 +115,7 @@ def test_handle_prompt_missing_value(session, create_dialog):
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/property/id"
                                     .format(session_id=session.session_id,
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/property/id"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #3")
 
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #3")
index df519f4..2dd7528 100644 (file)
@@ -21,32 +21,33 @@ def test_handle_prompt_dismiss(new_session, add_browser_capabilites):
     # 13.6 step 2
     _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "dismiss"})}})
     session.url = inline("<input id=foo>")
     # 13.6 step 2
     _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "dismiss"})}})
     session.url = inline("<input id=foo>")
+    element = session.find.css("#foo", all=False)
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/name"
                                     .format(session_id=session.session_id,
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/name"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
 
-    assert_success(result, "foo")
+    assert_success(result, "input")
     assert_dialog_handled(session, "dismiss #1")
 
     create_dialog(session)("confirm", text="dismiss #2", result_var="dismiss2")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/name"
                                     .format(session_id=session.session_id,
     assert_dialog_handled(session, "dismiss #1")
 
     create_dialog(session)("confirm", text="dismiss #2", result_var="dismiss2")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/name"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
 
-    assert_success(result, "foo")
+    assert_success(result, "input")
     assert_dialog_handled(session, "dismiss #2")
 
     create_dialog(session)("prompt", text="dismiss #3", result_var="dismiss3")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/name"
                                     .format(session_id=session.session_id,
     assert_dialog_handled(session, "dismiss #2")
 
     create_dialog(session)("prompt", text="dismiss #3", result_var="dismiss3")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/name"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
 
-    assert_success(result, "foo")
+    assert_success(result, "input")
     assert_dialog_handled(session, "dismiss #3")
 
 
     assert_dialog_handled(session, "dismiss #3")
 
 
@@ -54,44 +55,46 @@ def test_handle_prompt_accept(new_session, add_browser_capabilites):
     # 13.6 step 2
     _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "accept"})}})
     session.url = inline("<input id=foo>")
     # 13.6 step 2
     _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "accept"})}})
     session.url = inline("<input id=foo>")
+    element = session.find.css("#foo", all=False)
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/name"
                                     .format(session_id=session.session_id,
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/name"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
 
-    assert_success(result, "foo")
+    assert_success(result, "input")
     assert_dialog_handled(session, "dismiss #1")
 
     create_dialog(session)("confirm", text="dismiss #2", result_var="dismiss2")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/name"
                                     .format(session_id=session.session_id,
     assert_dialog_handled(session, "dismiss #1")
 
     create_dialog(session)("confirm", text="dismiss #2", result_var="dismiss2")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/name"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
 
-    assert_success(result, "foo")
+    assert_success(result, "input")
     assert_dialog_handled(session, "dismiss #2")
 
     create_dialog(session)("prompt", text="dismiss #3", result_var="dismiss3")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/name"
                                     .format(session_id=session.session_id,
     assert_dialog_handled(session, "dismiss #2")
 
     create_dialog(session)("prompt", text="dismiss #3", result_var="dismiss3")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/name"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
 
-    assert_success(result, "foo")
+    assert_success(result, "input")
     assert_dialog_handled(session, "dismiss #3")
 
 
     assert_dialog_handled(session, "dismiss #3")
 
 
-def test_handle_prompt_missing_value(session, create_dialog):
+def test_handle_prompt_missing_value(session):
     # 13.6 step 2
     session.url = inline("<input id=foo>")
     # 13.6 step 2
     session.url = inline("<input id=foo>")
+    element = session.find.css("#foo", all=False)
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/name"
                                     .format(session_id=session.session_id,
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/name"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #1")
 
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #1")
@@ -100,7 +103,7 @@ def test_handle_prompt_missing_value(session, create_dialog):
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/name"
                                     .format(session_id=session.session_id,
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/name"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #2")
 
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #2")
@@ -109,7 +112,7 @@ def test_handle_prompt_missing_value(session, create_dialog):
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/name"
                                     .format(session_id=session.session_id,
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/name"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #3")
 
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #3")
index 8027d5e..392929a 100644 (file)
@@ -30,32 +30,33 @@ def test_handle_prompt_dismiss(new_session, add_browser_capabilites):
     # 13.1 step 2
     _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "dismiss"})}})
     session.url = inline("<input id=foo>")
     # 13.1 step 2
     _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "dismiss"})}})
     session.url = inline("<input id=foo>")
+    element = session.find.css("#foo", all=False)
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/selected"
                                     .format(session_id=session.session_id,
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/selected"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
 
-    assert_success(result, "foo")
+    assert_success(result, False)
     assert_dialog_handled(session, "dismiss #1")
 
     create_dialog(session)("confirm", text="dismiss #2", result_var="dismiss2")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/selected"
                                     .format(session_id=session.session_id,
     assert_dialog_handled(session, "dismiss #1")
 
     create_dialog(session)("confirm", text="dismiss #2", result_var="dismiss2")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/selected"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
 
-    assert_success(result, "foo")
+    assert_success(result, False)
     assert_dialog_handled(session, "dismiss #2")
 
     create_dialog(session)("prompt", text="dismiss #3", result_var="dismiss3")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/selected"
                                     .format(session_id=session.session_id,
     assert_dialog_handled(session, "dismiss #2")
 
     create_dialog(session)("prompt", text="dismiss #3", result_var="dismiss3")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/selected"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
 
-    assert_success(result, "foo")
+    assert_success(result, False)
     assert_dialog_handled(session, "dismiss #3")
 
 
     assert_dialog_handled(session, "dismiss #3")
 
 
@@ -63,44 +64,46 @@ def test_handle_prompt_accept(new_session, add_browser_capabilites):
     # 13.1 step 2
     _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "accept"})}})
     session.url = inline("<input id=foo>")
     # 13.1 step 2
     _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "accept"})}})
     session.url = inline("<input id=foo>")
+    element = session.find.css("#foo", all=False)
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/selected"
                                     .format(session_id=session.session_id,
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/selected"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
 
-    assert_success(result, "foo")
+    assert_success(result, False)
     assert_dialog_handled(session, "dismiss #1")
 
     create_dialog(session)("confirm", text="dismiss #2", result_var="dismiss2")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/selected"
                                     .format(session_id=session.session_id,
     assert_dialog_handled(session, "dismiss #1")
 
     create_dialog(session)("confirm", text="dismiss #2", result_var="dismiss2")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/selected"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
 
-    assert_success(result, "foo")
+    assert_success(result, False)
     assert_dialog_handled(session, "dismiss #2")
 
     create_dialog(session)("prompt", text="dismiss #3", result_var="dismiss3")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/selected"
                                     .format(session_id=session.session_id,
     assert_dialog_handled(session, "dismiss #2")
 
     create_dialog(session)("prompt", text="dismiss #3", result_var="dismiss3")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/selected"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
 
-    assert_success(result, "foo")
+    assert_success(result, False)
     assert_dialog_handled(session, "dismiss #3")
 
 
     assert_dialog_handled(session, "dismiss #3")
 
 
-def test_handle_prompt_missing_value(session, create_dialog):
+def test_handle_prompt_missing_value(session):
     # 13.1 step 2
     session.url = inline("<input id=foo>")
     # 13.1 step 2
     session.url = inline("<input id=foo>")
+    element = session.find.css("#foo", all=False)
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/selected"
                                     .format(session_id=session.session_id,
 
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/selected"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #1")
 
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #1")
@@ -109,7 +112,7 @@ def test_handle_prompt_missing_value(session, create_dialog):
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/selected"
                                     .format(session_id=session.session_id,
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/selected"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #2")
 
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #2")
@@ -118,7 +121,7 @@ def test_handle_prompt_missing_value(session, create_dialog):
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/selected"
                                     .format(session_id=session.session_id,
 
     result = session.transport.send("GET", "session/{session_id}/element/{element_id}/selected"
                                     .format(session_id=session.session_id,
-                                            element_id="foo"))
+                                            element_id=element.id))
 
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #3")
 
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #3")
index 21cc07b..4174791 100644 (file)
@@ -1,5 +1,4 @@
-from webdriver.client import Element, element_key
-from webdriver.error import WebDriverException
+from webdriver import Element, WebDriverException
 
 # WebDriver specification ID: dfn-error-response-data
 errors = {
 
 # WebDriver specification ID: dfn-error-response-data
 errors = {
@@ -22,7 +21,7 @@ errors = {
     "no such window": 404,
     "script timeout": 408,
     "session not created": 500,
     "no such window": 404,
     "script timeout": 408,
     "session not created": 500,
-    "stale element reference": 400,
+    "stale element reference": 404,
     "timeout": 408,
     "unable to set cookie": 500,
     "unable to capture screen": 500,
     "timeout": 408,
     "unable to set cookie": 500,
     "unable to capture screen": 500,
@@ -55,12 +54,13 @@ errors = {
 # >
 # > 5. Send a response with status and data as arguments.
 def assert_error(response, error_code):
 # >
 # > 5. Send a response with status and data as arguments.
 def assert_error(response, error_code):
-    """Verify that the provided wdclient.Response instance described a valid
-    error response as defined by `dfn-send-an-error` and the provided error
-    code.
+    """
+    Verify that the provided webdriver.Response instance described
+    a valid error response as defined by `dfn-send-an-error` and
+    the provided error code.
 
 
-    :param response: wdclient.Response instance
-    :param error_code: string value of the expected "error code"
+    :param response: ``webdriver.Response`` instance.
+    :param error_code: String value of the expected error code
     """
     assert response.status == errors[error_code]
     assert "value" in response.body
     """
     assert response.status == errors[error_code]
     assert "value" in response.body
@@ -68,20 +68,23 @@ def assert_error(response, error_code):
     assert isinstance(response.body["value"]["message"], basestring)
     assert isinstance(response.body["value"]["stacktrace"], basestring)
 
     assert isinstance(response.body["value"]["message"], basestring)
     assert isinstance(response.body["value"]["stacktrace"], basestring)
 
+
 def assert_success(response, value=None):
 def assert_success(response, value=None):
-    """Verify that the provided wdclient.Response instance described a valid
-    error response as defined by `dfn-send-an-error` and the provided error
-    code.
+    """
+    Verify that the provided webdriver.Response instance described
+    a valid error response as defined by `dfn-send-an-error` and
+    the provided error code.
 
 
-    :param response: wdclient.Response instance.
+    :param response: ``webdriver.Response`` instance.
     :param value: Expected value of the response body, if any.
     :param value: Expected value of the response body, if any.
-
     """
     """
-    assert response.status == 200
+    assert response.status == 200, str(response.error)
+
     if value is not None:
         assert response.body["value"] == value
     return response.body.get("value")
 
     if value is not None:
         assert response.body["value"] == value
     return response.body.get("value")
 
+
 def assert_dialog_handled(session, expected_text):
     result = session.transport.send("GET",
                                     "session/%s/alert/text" % session.session_id)
 def assert_dialog_handled(session, expected_text):
     result = session.transport.send("GET",
                                     "session/%s/alert/text" % session.session_id)
@@ -97,14 +100,26 @@ def assert_dialog_handled(session, expected_text):
                 result.body["value"] != expected_text), (
                "Dialog with text '%s' was not handled." % expected_text)
 
                 result.body["value"] != expected_text), (
                "Dialog with text '%s' was not handled." % expected_text)
 
+
 def assert_same_element(session, a, b):
     """Verify that two element references describe the same element."""
 def assert_same_element(session, a, b):
     """Verify that two element references describe the same element."""
-    assert isinstance(a, dict), "Actual value is not a dictionary"
-    assert isinstance(b, dict), "Expected value is not a dictionary"
-    assert element_key in a, "Actual value does not describe an element"
-    assert element_key in b, "Expected value does not describe an element"
+    if isinstance(a, dict):
+        assert Element.identifier in a, "Actual value does not describe an element"
+        a_id = a[Element.identifier]
+    elif isinstance(a, Element):
+        a_id = a.id
+    else:
+        raise AssertionError("Actual value is not a dictionary or web element")
+
+    if isinstance(b, dict):
+        assert Element.identifier in b, "Expected value does not describe an element"
+        b_id = b[Element.identifier]
+    elif isinstance(b, Element):
+        b_id = b.id
+    else:
+        raise AssertionError("Expected value is not a dictionary or web element")
 
 
-    if a[element_key] == b[element_key]:
+    if a_id == b_id:
         return
 
     message = ("Expected element references to describe the same element, " +
         return
 
     message = ("Expected element references to describe the same element, " +
@@ -113,8 +128,8 @@ def assert_same_element(session, a, b):
     # Attempt to provide more information, accounting for possible errors such
     # as stale element references or not visible elements.
     try:
     # Attempt to provide more information, accounting for possible errors such
     # as stale element references or not visible elements.
     try:
-        a_markup = session.execute_script("return arguments[0].outerHTML;", args=[a])
-        b_markup = session.execute_script("return arguments[0].outerHTML;", args=[b])
+        a_markup = session.execute_script("return arguments[0].outerHTML;", args=(a,))
+        b_markup = session.execute_script("return arguments[0].outerHTML;", args=(b,))
         message += " Actual: `%s`. Expected: `%s`." % (a_markup, b_markup)
     except WebDriverException:
         pass
         message += " Actual: `%s`. Expected: `%s`." % (a_markup, b_markup)
     except WebDriverException:
         pass
diff --git a/WebDriverTests/imported/w3c/webdriver/tests/user_prompts/send_alert_text.py b/WebDriverTests/imported/w3c/webdriver/tests/user_prompts/send_alert_text.py
new file mode 100644 (file)
index 0000000..8e07b85
--- /dev/null
@@ -0,0 +1,73 @@
+import pytest
+
+from tests.support.asserts import assert_error, assert_success
+
+def send_alert_text(session, body=None):
+    return session.transport.send("POST", "session/{session_id}/alert/text"
+                                  .format(session_id=session.session_id), body)
+
+
+# 18.4 Send Alert Text
+
+@pytest.mark.parametrize("text", [None, {}, [], 42, True])
+def test_invalid_input(session, text):
+    # 18.4 step 2
+    session.execute_script("window.result = window.prompt(\"Enter Your Name: \", \"Name\");")
+    response = send_alert_text(session, {"text": text})
+    assert_error(response, "invalid argument")
+
+
+def test_no_browsing_context(session, create_window):
+    # 18.4 step 3
+    session.window_handle = create_window()
+    session.close()
+    body = {"text": "Federer"}
+    response = send_alert_text(session, body)
+    assert_error(response, "no such window")
+
+
+def test_no_user_prompt(session):
+    # 18.4 step 4
+    body = {"text": "Federer"}
+    response = send_alert_text(session, body)
+    assert_error(response, "no such alert")
+
+
+def test_alert_element_not_interactable(session):
+    # 18.4 step 5
+    session.execute_script("window.alert(\"Hello\");")
+    body = {"text": "Federer"}
+    response = send_alert_text(session, body)
+    assert_error(response, "element not interactable")
+
+
+def test_confirm_element_not_interactable(session):
+    # 18.4 step 5
+    session.execute_script("window.confirm(\"Hello\");")
+    body = {"text": "Federer"}
+    response = send_alert_text(session, body)
+    assert_error(response, "element not interactable")
+
+
+def test_send_alert_text(session):
+    # 18.4 step 6
+    session.execute_script("window.result = window.prompt(\"Enter Your Name: \", \"Name\");")
+    body = {"text": "Federer"}
+    send_response = send_alert_text(session, body)
+    assert_success(send_response)
+    accept_response = session.transport.send("POST", "session/{session_id}/alert/accept"
+                                             .format(session_id=session.session_id))
+    assert_success(accept_response)
+    assert session.execute_script("return window.result") == "Federer"
+
+
+def test_send_alert_text_with_whitespace(session):
+    # 18.4 step 6
+    session.execute_script("window.result = window.prompt(\"Enter Your Name: \", \"Name\");")
+    body = {"text": " Fed erer "}
+    send_response = send_alert_text(session, body)
+    assert_success(send_response)
+    accept_response = session.transport.send("POST", "session/{session_id}/alert/accept"
+                                             .format(session_id=session.session_id))
+    assert_success(accept_response)
+    assert session.execute_script("return window.result") == " Fed erer "