Unreviewed. Update W3C WebDriver imported tests.
authorcarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 28 Nov 2017 13:15:05 +0000 (13:15 +0000)
committercarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 28 Nov 2017 13:15:05 +0000 (13:15 +0000)
* imported/w3c/importer.json:
* imported/w3c/tools/webdriver/webdriver/client.py:
* imported/w3c/tools/webdriver/webdriver/error.py:
* imported/w3c/tools/wptrunner/docs/conf.py:
* imported/w3c/tools/wptrunner/wptrunner/browsers/ie.py:
* imported/w3c/tools/wptrunner/wptrunner/executors/base.py:
* imported/w3c/tools/wptrunner/wptrunner/executors/pytestrunner/runner.py:
* imported/w3c/tools/wptrunner/wptrunner/stability.py:
* imported/w3c/tools/wptrunner/wptrunner/testdriver-vendor.js: Added.
* imported/w3c/tools/wptrunner/wptrunner/update/sync.py:
* imported/w3c/tools/wptrunner/wptrunner/wptcommandline.py:
* imported/w3c/webdriver/interface/interface.html:
* imported/w3c/webdriver/tests/document_handling/page_source.py: Added.
* imported/w3c/webdriver/tests/element_click/bubbling.py: Added.
* imported/w3c/webdriver/tests/element_retrieval/get_active_element.py:
* imported/w3c/webdriver/tests/execute_async_script/user_prompts.py: Added.
* imported/w3c/webdriver/tests/execute_script/user_prompts.py: Added.
* imported/w3c/webdriver/tests/sessions/status.py: Added.
* imported/w3c/webdriver/tests/status.py: Removed.
* imported/w3c/webdriver/tests/support/fixtures.py:
* imported/w3c/webdriver/tests/support/wait.py:
* imported/w3c/webdriver/tests/user_prompts/accept_alert.py:
* imported/w3c/webdriver/tests/user_prompts/dismiss_alert.py:
* imported/w3c/webdriver/tests/user_prompts/get_alert_text.py:
* imported/w3c/webdriver/tests/user_prompts/send_alert_text.py:

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

26 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/wptrunner/docs/conf.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/browsers/ie.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/base.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/executors/pytestrunner/runner.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/stability.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testdriver-vendor.js [new file with mode: 0644]
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/update/sync.py
WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/wptcommandline.py
WebDriverTests/imported/w3c/webdriver/interface/interface.html
WebDriverTests/imported/w3c/webdriver/tests/document_handling/page_source.py [new file with mode: 0644]
WebDriverTests/imported/w3c/webdriver/tests/element_click/bubbling.py [new file with mode: 0644]
WebDriverTests/imported/w3c/webdriver/tests/element_retrieval/get_active_element.py
WebDriverTests/imported/w3c/webdriver/tests/execute_async_script/user_prompts.py [new file with mode: 0644]
WebDriverTests/imported/w3c/webdriver/tests/execute_script/user_prompts.py [new file with mode: 0644]
WebDriverTests/imported/w3c/webdriver/tests/sessions/status.py [new file with mode: 0644]
WebDriverTests/imported/w3c/webdriver/tests/status.py [deleted file]
WebDriverTests/imported/w3c/webdriver/tests/support/fixtures.py
WebDriverTests/imported/w3c/webdriver/tests/support/wait.py
WebDriverTests/imported/w3c/webdriver/tests/user_prompts/accept_alert.py
WebDriverTests/imported/w3c/webdriver/tests/user_prompts/dismiss_alert.py
WebDriverTests/imported/w3c/webdriver/tests/user_prompts/get_alert_text.py
WebDriverTests/imported/w3c/webdriver/tests/user_prompts/send_alert_text.py

index 77f16a2..e08514b 100644 (file)
@@ -1,5 +1,35 @@
 2017-11-28  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/wptrunner/docs/conf.py:
+        * imported/w3c/tools/wptrunner/wptrunner/browsers/ie.py:
+        * imported/w3c/tools/wptrunner/wptrunner/executors/base.py:
+        * imported/w3c/tools/wptrunner/wptrunner/executors/pytestrunner/runner.py:
+        * imported/w3c/tools/wptrunner/wptrunner/stability.py:
+        * imported/w3c/tools/wptrunner/wptrunner/testdriver-vendor.js: Added.
+        * imported/w3c/tools/wptrunner/wptrunner/update/sync.py:
+        * imported/w3c/tools/wptrunner/wptrunner/wptcommandline.py:
+        * imported/w3c/webdriver/interface/interface.html:
+        * imported/w3c/webdriver/tests/document_handling/page_source.py: Added.
+        * imported/w3c/webdriver/tests/element_click/bubbling.py: Added.
+        * imported/w3c/webdriver/tests/element_retrieval/get_active_element.py:
+        * imported/w3c/webdriver/tests/execute_async_script/user_prompts.py: Added.
+        * imported/w3c/webdriver/tests/execute_script/user_prompts.py: Added.
+        * imported/w3c/webdriver/tests/sessions/status.py: Added.
+        * imported/w3c/webdriver/tests/status.py: Removed.
+        * imported/w3c/webdriver/tests/support/fixtures.py:
+        * imported/w3c/webdriver/tests/support/wait.py:
+        * imported/w3c/webdriver/tests/user_prompts/accept_alert.py:
+        * imported/w3c/webdriver/tests/user_prompts/dismiss_alert.py:
+        * imported/w3c/webdriver/tests/user_prompts/get_alert_text.py:
+        * imported/w3c/webdriver/tests/user_prompts/send_alert_text.py:
+
+2017-11-28  Carlos Garcia Campos  <cgarcia@igalia.com>
+
         WebDriver: add timeout option to run-webdriver-tests script
         https://bugs.webkit.org/show_bug.cgi?id=179940
 
index fea3874..dba6dc3 100644 (file)
@@ -1,6 +1,6 @@
 {
     "repository": "https://github.com/w3c/web-platform-tests.git",
-    "revision": "448984b0e1c5bad4af20abeaf84eb8b5e8e81478",
+    "revision": "2b50389ee72d89dd0be12bc6ca54a6e95c98d163",
     "paths_to_import": [
         "tools/pytest",
         "tools/webdriver",
index 3f54156..4defb75 100644 (file)
@@ -509,6 +509,11 @@ class Session(object):
 
     @property
     @command
+    def source(self):
+        return self.send_session_command("GET", "source")
+
+    @property
+    @command
     def window_handle(self):
         return self.send_session_command("GET", "window")
 
index 69f6e45..2fb748d 100644 (file)
@@ -11,16 +11,21 @@ class WebDriverException(Exception):
         self.stacktrace = stacktrace
 
     def __repr__(self):
-        return "<%s http_status=%d>" % (self.__class__.__name__, self.http_status)
+        return "<%s http_status=%s>" % (self.__class__.__name__, self.http_status)
 
     def __str__(self):
-        return ("%s (%d)\n"
+        return ("%s (%s)\n"
             "\n"
             "Remote-end stacktrace:\n"
             "\n"
             "%s" % (self.status_code, self.http_status, self.stacktrace))
 
 
+class ElementClickInterceptedException(WebDriverException):
+    http_status = 400
+    status_code = "element click intercepted"
+
+
 class ElementNotSelectableException(WebDriverException):
     http_status = 400
     status_code = "element not selectable"
index 39e5cc4..0c717f5 100644 (file)
@@ -264,4 +264,4 @@ texinfo_documents = [
 
 # Example configuration for intersphinx: refer to the Python standard library.
 intersphinx_mapping = {'python': ('http://docs.python.org/', None),
-                       'mozlog': ('http://mozbase.readthedocs.org/en/latest/', None)}
+                       'mozlog': ('https://firefox-source-docs.mozilla.org/', None)}
index c981024..553372f 100644 (file)
@@ -31,8 +31,6 @@ def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
     ieOptions = {}
     ieOptions["requireWindowFocus"] = True
     capabilities = {}
-    capabilities["browserName"] = "internet explorer"
-    capabilities["platformName"] = "windows"
     capabilities["se:ieOptions"] = ieOptions
     executor_kwargs = base_executor_kwargs(test_type, server_config,
                                            cache_manager, **kwargs)
index b33bc42..cc04d76 100644 (file)
@@ -357,12 +357,10 @@ class WdspecExecutor(TestExecutor):
         return (test.result_cls(*data), [])
 
     def do_wdspec(self, session_config, path, timeout):
-        harness_result = ("OK", None)
-        subtest_results = pytestrunner.run(path,
-                                           self.server_config,
-                                           session_config,
-                                           timeout=timeout)
-        return (harness_result, subtest_results)
+        return pytestrunner.run(path,
+                                self.server_config,
+                                session_config,
+                                timeout=timeout)
 
     def do_delayed_imports(self):
         global pytestrunner
index 611a498..a2b603d 100644 (file)
@@ -1,4 +1,5 @@
-"""Provides interface to deal with pytest.
+"""
+Provides interface to deal with pytest.
 
 Usage::
 
@@ -24,7 +25,8 @@ def do_delayed_imports():
 
 
 def run(path, server_config, session_config, timeout=0):
-    """Run Python test at ``path`` in pytest.  The provided ``session``
+    """
+    Run Python test at ``path`` in pytest.  The provided ``session``
     is exposed as a fixture available in the scope of the test functions.
 
     :param path: Path to the test file.
@@ -33,36 +35,51 @@ def run(path, server_config, session_config, timeout=0):
     :param timeout: Duration before interrupting potentially hanging
         tests.  If 0, there is no timeout.
 
-    :returns: List of subtest results, which are tuples of (test id,
-        status, message, stacktrace).
+    :returns: (<harness result>, [<subtest result>, ...]),
+        where <subtest result> is (test id, status, message, stacktrace).
     """
-
     if pytest is None:
         do_delayed_imports()
 
-    recorder = SubtestResultRecorder()
-
     os.environ["WD_HOST"] = session_config["host"]
     os.environ["WD_PORT"] = str(session_config["port"])
     os.environ["WD_CAPABILITIES"] = json.dumps(session_config["capabilities"])
     os.environ["WD_SERVER_CONFIG"] = json.dumps(server_config)
 
-    plugins = [recorder]
-
-    # TODO(ato): Deal with timeouts
+    harness = HarnessResultRecorder()
+    subtests = SubtestResultRecorder()
 
     with TemporaryDirectory() as cache:
-        pytest.main(["--strict",  # turn warnings into errors
-                     "--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],
-                    plugins=plugins)
-
-    return recorder.results
+        try:
+            pytest.main(["--strict",  # turn warnings into errors
+                         "--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],
+                        plugins=[harness, subtests])
+        except Exception as e:
+            harness.outcome = ("ERROR", str(e))
+
+    return (harness.outcome, subtests.results)
+
+
+class HarnessResultRecorder(object):
+    outcomes = {
+        "failed": "ERROR",
+        "passed": "OK",
+        "skipped": "SKIP",
+    }
+
+    def __init__(self):
+        # we are ok unless told otherwise
+        self.outcome = ("OK", None)
+
+    def pytest_collectreport(self, report):
+        harness_result = self.outcomes[report.outcome]
+        self.outcome = (harness_result, None)
 
 
 class SubtestResultRecorder(object):
index dc1a1d0..6eb0604 100644 (file)
@@ -1,14 +1,14 @@
 import copy
 import functools
 import imp
+import io
 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
+from mozlog.formatters import JSONFormatter
+from mozlog.handlers import BaseHandler, StreamHandler, LogLevelFilter
 
 here = os.path.dirname(__file__)
 localpaths = imp.load_source("localpaths", os.path.abspath(os.path.join(here, os.pardir, os.pardir, "localpaths.py")))
@@ -86,6 +86,8 @@ class LogHandler(reader.LogHandler):
 
 def is_inconsistent(results_dict, iterations):
     """Return whether or not a single test is inconsistent."""
+    if 'SKIP' in results_dict:
+        return False
     return len(results_dict) > 1 or sum(results_dict.values()) != iterations
 
 
@@ -178,31 +180,29 @@ def run_step(logger, iterations, restart_after_iteration, kwargs_extras, **kwarg
     kwargs["pause_after_test"] = False
     kwargs.update(kwargs_extras)
 
-    handler = LogActionFilter(
-        LogLevelFilter(
-            StreamHandler(
-                sys.stdout,
-                TbplFormatter()
-            ),
-            "WARNING"),
-        ["log", "process_output"])
+    def wrap_handler(x):
+        x = LogLevelFilter(x, "WARNING")
+        if not kwargs["verify_log_full"]:
+            x = LogActionFilter(x, ["log", "process_output"])
+        return x
 
-    # There is a public API for this in the next mozlog
     initial_handlers = logger._state.handlers
-    logger._state.handlers = []
+    logger._state.handlers = [wrap_handler(handler)
+                              for handler in initial_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()))
+    log = io.BytesIO()
+    # Setup logging for wptrunner that keeps process output and
+    # warning+ level logs only
+    logger.add_handler(StreamHandler(log, JSONFormatter()))
 
-        wptrunner.run_tests(**kwargs)
+    wptrunner.run_tests(**kwargs)
 
     logger._state.handlers = initial_handlers
+    logger._state.running_tests = set()
+    logger._state.suite_started = False
 
-    with open("raw.log", "rb") as log:
-        results, inconsistent = process_results(log, iterations)
+    log.seek(0)
+    results, inconsistent = process_results(log, iterations)
     return results, inconsistent, iterations
 
 
diff --git a/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testdriver-vendor.js b/WebDriverTests/imported/w3c/tools/wptrunner/wptrunner/testdriver-vendor.js
new file mode 100644 (file)
index 0000000..3e88403
--- /dev/null
@@ -0,0 +1 @@
+// This file intentionally left blank
index 40bd1d7..c1bff85 100644 (file)
@@ -81,7 +81,6 @@ def copy_wpt_tree(tree, dest, excludes=None, includes=None):
             shutil.copy2(source_path, dest_path)
 
     for source, destination in [("testharness_runner.html", ""),
-                                ("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])
index b0591d5..b232462 100644 (file)
@@ -79,6 +79,9 @@ scheme host and port.""")
     mode_group.add_argument("--verify", action="store_true",
                             default=False,
                             help="Run a stability check on the selected tests")
+    mode_group.add_argument("--verify-log-full", action="store_true",
+                            default=False,
+                            help="Output per-iteration test results when running verify")
 
     test_selection_group = parser.add_argument_group("Test Selection")
     test_selection_group.add_argument("--test-types", action="store",
index e29fa63..f3c149b 100644 (file)
@@ -2,20 +2,39 @@
 <body>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src=/resources/WebIDLParser.js></script>
+<script src=/resources/idlharness.js></script>
+<script type=text/plain class=untested>
+[Exposed=Window]
+interface Navigator {
+  // objects implementing this interface also implement the interfaces given below
+};
+</script>
+<script type=text/plain>
+Navigator implements NavigatorAutomationInformation;
+
+[NoInterfaceObject,
+ Exposed=(Window)]
+interface NavigatorAutomationInformation {
+    readonly attribute boolean webdriver;
+    // always returns true
+};
+</script>
 <script>
+"use strict";
 
-test(function() {
-  if ("webdriver" in navigator) {
-    var descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(navigator), "webdriver");
-    assert_true(descriptor !== undefined);
-    assert_true(descriptor.configurable);
-    assert_true(descriptor.enumerable);
-    assert_true(descriptor.set === undefined);
-    assert_true(navigator.webdriver);
-  } else {
-    assert_true(navigator.webdriver === undefined);
-  }
-}, "Test that the navigator.webdriver descriptor has expected properties or doesn't exist at all");
+if ("webdriver" in navigator) {
+  test(() => assert_true(navigator.webdriver), "navigator.webdriver is always true");
+  var idlArray = new IdlArray();
+  [].forEach.call(document.querySelectorAll("script[type=text\\/plain]"), function(node) {
+    if (node.className == "untested") {
+      idlArray.add_untested_idls(node.textContent);
+    } else {
+      idlArray.add_idls(node.textContent);
+    }
+  });
+  idlArray.test();
+} else {
+  done();
+}
 </script>
-</body>
-</html>
diff --git a/WebDriverTests/imported/w3c/webdriver/tests/document_handling/page_source.py b/WebDriverTests/imported/w3c/webdriver/tests/document_handling/page_source.py
new file mode 100644 (file)
index 0000000..04eae6f
--- /dev/null
@@ -0,0 +1,14 @@
+import pytest
+
+from tests.support.inline import inline
+
+
+# 15.1.3 "Let source be the result returned from the outerHTML IDL attribute
+#         of the document element"
+def test_source_matches_outer_html(session):
+    session.url = inline("<html><head><title>Cheese</title><body>Peas")
+    expected_source = session.execute_script(
+        "return document.documentElement.outerHTML")
+
+    assert session.source == expected_source
+
diff --git a/WebDriverTests/imported/w3c/webdriver/tests/element_click/bubbling.py b/WebDriverTests/imported/w3c/webdriver/tests/element_click/bubbling.py
new file mode 100644 (file)
index 0000000..1cfb2ef
--- /dev/null
@@ -0,0 +1,152 @@
+from tests.support.asserts import assert_success
+from tests.support.inline import inline
+
+
+def click(session, element):
+    return session.transport.send(
+        "POST", "/session/{session_id}/element/{element_id}/click".format(
+            session_id=session.session_id,
+            element_id=element.id))
+
+
+def test_click_event_bubbles_to_parents(session):
+    session.url = inline("""
+        <style>
+        body * {
+          margin: 10px;
+          padding: 10px;
+          border: 1px solid blue;
+        }
+        </style>
+
+        <div id=three>THREE
+          <div id=two>TWO
+            <div id=one>ONE</div>
+          </div>
+        </div>
+
+        <script>
+        window.clicks = [];
+
+        for (let level of document.querySelectorAll("div")) {
+          level.addEventListener("click", ({currentTarget}) => {
+            window.clicks.push(currentTarget);
+          });
+        }
+        </script>
+        """)
+    three, two, one = session.find.css("div")
+    one.click()
+
+    clicks = session.execute_script("return window.clicks")
+    assert one in clicks
+    assert two in clicks
+    assert three in clicks
+
+
+def test_spin_event_loop(session):
+    """
+    Wait until the user agent event loop has spun enough times to
+    process the DOM events generated by clicking.
+    """
+    session.url = inline("""
+        <style>
+        body * {
+          margin: 10px;
+          padding: 10px;
+          border: 1px solid blue;
+        }
+        </style>
+
+        <div id=three>THREE
+          <div id=two>TWO
+            <div id=one>ONE</div>
+          </div>
+        </div>
+
+        <script>
+        window.delayedClicks = [];
+
+        for (let level of document.querySelectorAll("div")) {
+          level.addEventListener("click", ({currentTarget}) => {
+            setTimeout(() => window.delayedClicks.push(currentTarget), 100);
+          });
+        }
+        </script>
+        """)
+    three, two, one = session.find.css("div")
+    one.click()
+
+    delayed_clicks = session.execute_script("return window.delayedClicks")
+    assert one in delayed_clicks
+    assert two in delayed_clicks
+    assert three in delayed_clicks
+
+
+def test_element_disappears_during_click(session):
+    """
+    When an element in the event bubbling order disappears (its CSS
+    display style is set to "none") during a click, Gecko and Blink
+    exhibit different behaviour.  Whilst Chrome fires a "click"
+    DOM event on <body>, Firefox does not.
+
+    A WebDriver implementation may choose to wait for this event to let
+    the event loops spin enough times to let click events propagate,
+    so this is a corner case test that Firefox does not hang indefinitely.
+    """
+    session.url = inline("""
+        <style>
+        #over,
+        #under {
+          position: absolute;
+          top: 8px;
+          left: 8px;
+          width: 100px;
+          height: 100px;
+        }
+
+        #over {
+          background: blue;
+          opacity: .5;
+        }
+        #under {
+          background: yellow;
+        }
+
+        #log {
+          margin-top: 120px;
+        }
+        </style>
+
+        <body id="body">
+          <div id=under></div>
+          <div id=over></div>
+
+           <div id=log></div>
+        </body>
+
+        <script>
+        let under = document.querySelector("#under");
+        let over = document.querySelector("#over");
+        let body = document.querySelector("body");
+        let log = document.querySelector("#log");
+
+        function logEvent({type, target, currentTarget}) {
+          log.innerHTML += "<p></p>";
+          log.lastElementChild.textContent = `${type} in ${target.id} (handled by ${currentTarget.id})`;
+        }
+
+        for (let ev of ["click", "mousedown", "mouseup"]) {
+          under.addEventListener(ev, logEvent);
+          over.addEventListener(ev, logEvent);
+          body.addEventListener(ev, logEvent);
+        }
+
+        over.addEventListener("mousedown", () => over.style.display = "none");
+        </script>
+        """)
+    over = session.find.css("#over", all=False)
+
+    # should not time out
+    response = click(session, over)
+    assert_success(response)
index 9bc6f73..5c2b52a 100644 (file)
@@ -2,9 +2,11 @@ from tests.support.asserts import assert_error, assert_dialog_handled, assert_sa
 from tests.support.fixtures import create_dialog
 from tests.support.inline import inline
 
+
 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)
 
@@ -244,7 +246,7 @@ def test_success_iframe_content(session):
     assert_is_active_element(session, response)
 
 
-def test_sucess_without_body(session):
+def test_missing_document_element(session):
     session.url = inline("<body></body>")
     session.execute_script("""
         if (document.body.remove) {
@@ -254,4 +256,4 @@ def test_sucess_without_body(session):
         }""")
 
     response = get_active_element(session)
-    assert_is_active_element(session, response)
+    assert_error(response, "no such element")
diff --git a/WebDriverTests/imported/w3c/webdriver/tests/execute_async_script/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/execute_async_script/user_prompts.py
new file mode 100644 (file)
index 0000000..03e1762
--- /dev/null
@@ -0,0 +1,60 @@
+import pytest
+
+from webdriver import error
+
+
+# 15.2 Executing Script
+
+def test_handle_prompt_accept(new_session, add_browser_capabilites):
+    _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "accept"})}})
+    session.execute_async_script("window.alert('Hello');")
+    with pytest.raises(error.NoSuchAlertException):
+        session.alert.accept()
+
+
+def test_handle_prompt_dismiss(new_session, add_browser_capabilites):
+    _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "dismiss"})}})
+    session.execute_async_script("window.alert('Hello');")
+    with pytest.raises(error.NoSuchAlertException):
+        session.alert.dismiss()
+
+
+def test_handle_prompt_dismiss_and_notify(new_session, add_browser_capabilites):
+    _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "dismiss and notify"})}})
+    with pytest.raises(error.UnexpectedAlertOpenException):
+        session.execute_async_script("window.alert('Hello');")
+    with pytest.raises(error.NoSuchAlertException):
+        session.alert.dismiss()
+
+
+def test_handle_prompt_accept_and_notify(new_session, add_browser_capabilites):
+    _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "accept and notify"})}})
+    with pytest.raises(error.UnexpectedAlertOpenException):
+        session.execute_async_script("window.alert('Hello');")
+    with pytest.raises(error.NoSuchAlertException):
+        session.alert.accept()
+
+
+def test_handle_prompt_ignore(new_session, add_browser_capabilites):
+    _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "ignore"})}})
+    with pytest.raises(error.UnexpectedAlertOpenException):
+        session.execute_async_script("window.alert('Hello');")
+    session.alert.dismiss()
+
+
+def test_handle_prompt_default(new_session, add_browser_capabilites):
+    _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({})}})
+    with pytest.raises(error.UnexpectedAlertOpenException):
+        session.execute_async_script("window.alert('Hello');")
+    with pytest.raises(error.NoSuchAlertException):
+        session.alert.dismiss()
+
+
+def test_handle_prompt_twice(new_session, add_browser_capabilites):
+    _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "accept"})}})
+    session.execute_async_script("window.alert('Hello');window.alert('Bye');")
+    # The first alert has been accepted by the user prompt handler, the second one remains.
+    # FIXME: this is how browsers currently work, but the spec should clarify if this is the
+    #        expected behavior, see https://github.com/w3c/webdriver/issues/1153.
+    assert session.alert.text == "Bye"
+    session.alert.dismiss()
diff --git a/WebDriverTests/imported/w3c/webdriver/tests/execute_script/user_prompts.py b/WebDriverTests/imported/w3c/webdriver/tests/execute_script/user_prompts.py
new file mode 100644 (file)
index 0000000..8d91bdd
--- /dev/null
@@ -0,0 +1,60 @@
+import pytest
+
+from webdriver import error
+
+
+# 15.2 Executing Script
+
+def test_handle_prompt_accept(new_session, add_browser_capabilites):
+    _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "accept"})}})
+    session.execute_script("window.alert('Hello');")
+    with pytest.raises(error.NoSuchAlertException):
+        session.alert.accept()
+
+
+def test_handle_prompt_dismiss(new_session, add_browser_capabilites):
+    _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "dismiss"})}})
+    session.execute_script("window.alert('Hello');")
+    with pytest.raises(error.NoSuchAlertException):
+        session.alert.dismiss()
+
+
+def test_handle_prompt_dismiss_and_notify(new_session, add_browser_capabilites):
+    _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "dismiss and notify"})}})
+    with pytest.raises(error.UnexpectedAlertOpenException):
+        session.execute_script("window.alert('Hello');")
+    with pytest.raises(error.NoSuchAlertException):
+        session.alert.dismiss()
+
+
+def test_handle_prompt_accept_and_notify(new_session, add_browser_capabilites):
+    _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "accept and notify"})}})
+    with pytest.raises(error.UnexpectedAlertOpenException):
+        session.execute_script("window.alert('Hello');")
+    with pytest.raises(error.NoSuchAlertException):
+        session.alert.accept()
+
+
+def test_handle_prompt_ignore(new_session, add_browser_capabilites):
+    _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "ignore"})}})
+    with pytest.raises(error.UnexpectedAlertOpenException):
+        session.execute_script("window.alert('Hello');")
+    session.alert.dismiss()
+
+
+def test_handle_prompt_default(new_session, add_browser_capabilites):
+    _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({})}})
+    with pytest.raises(error.UnexpectedAlertOpenException):
+        session.execute_script("window.alert('Hello');")
+    with pytest.raises(error.NoSuchAlertException):
+        session.alert.dismiss()
+
+
+def test_handle_prompt_twice(new_session, add_browser_capabilites):
+    _, session = new_session({"capabilities": {"alwaysMatch": add_browser_capabilites({"unhandledPromptBehavior": "accept"})}})
+    session.execute_script("window.alert('Hello');window.alert('Bye');")
+    # The first alert has been accepted by the user prompt handler, the second one remains.
+    # FIXME: this is how browsers currently work, but the spec should clarify if this is the
+    #        expected behavior, see https://github.com/w3c/webdriver/issues/1153.
+    assert session.alert.text == "Bye"
+    session.alert.dismiss()
diff --git a/WebDriverTests/imported/w3c/webdriver/tests/sessions/status.py b/WebDriverTests/imported/w3c/webdriver/tests/sessions/status.py
new file mode 100644 (file)
index 0000000..e4b3e93
--- /dev/null
@@ -0,0 +1,45 @@
+import pytest
+import json
+
+
+def test_get_status_no_session(http):
+    with http.get("/status") as response:
+        # GET /status should never return an error
+        assert response.status == 200
+
+        # parse JSON response and unwrap 'value' property
+        parsed_obj = json.loads(response.read().decode('utf-8'))
+        value = parsed_obj["value"]
+
+        # Let body be a new JSON Object with the following properties:
+        # "ready"
+        #       The remote end's readiness state.
+        assert value["ready"] in [True, False]
+        # "message"
+        #       An implementation-defined string explaining the remote end's
+        #       readiness state.
+        assert isinstance(value["message"], basestring)
+
+
+def test_status_with_session_running_on_endpoint_node(new_session):
+    # For an endpoint node, the maximum number of active
+    # sessions is 1: https://www.w3.org/TR/webdriver/#dfn-maximum-active-sessions
+    # A session is open, so we expect `ready` to be False
+    # 8.3 step 1.
+
+    _, session = new_session("{}")  # we don't care what we're using
+    value = session.send_command("GET", "status")
+
+    assert value["ready"] == False
+    assert "message" in value
+
+    session.end()
+
+    # Active session count is 0, meaning that the
+    # readiness state of the server should be True
+    # 8.3 step 1. Again
+    value = session.send_command("GET", "status")
+
+    assert value["ready"] == True
+    assert "message" in value
+
diff --git a/WebDriverTests/imported/w3c/webdriver/tests/status.py b/WebDriverTests/imported/w3c/webdriver/tests/status.py
deleted file mode 100644 (file)
index 9472f8a..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-import pytest
-import json
-
-
-def test_get_status_no_session(http):
-    with http.get("/status") as response:
-        # GET /status should never return an error
-        assert response.status == 200
-
-        # parse JSON response and unwrap 'value' property
-        parsed_obj = json.loads(response.read().decode('utf-8'))
-        value = parsed_obj["value"]
-
-        # Let body be a new JSON Object with the following properties:
-        # "ready"
-        #       The remote end's readiness state.
-        assert value["ready"] in [True, False]
-        # "message"
-        #       An implementation-defined string explaining the remote end's
-        #       readiness state.
-        assert isinstance(value["message"], basestring)
index 778acd3..8da0d5c 100644 (file)
@@ -8,6 +8,7 @@ import mozlog
 
 from tests.support.asserts import assert_error
 from tests.support.http_request import HTTPRequest
+from tests.support.wait import wait
 from tests.support import merge_dictionaries
 
 default_host = "http://127.0.0.1"
@@ -254,10 +255,15 @@ def create_dialog(session):
         session.send_session_command("POST",
                                      "execute/async",
                                      {"script": spawn, "args": []})
+        wait(session,
+             lambda s: s.send_session_command("GET", "alert/text") == text,
+             "modal has not appeared",
+             timeout=15,
+             ignored_exceptions=webdriver.NoSuchAlertException)
 
     return create_dialog
 
+
 def clear_all_cookies(session):
     """Removes all cookies associated with the current active document"""
     session.transport.send("DELETE", "session/%s/cookie" % session.session_id)
-
index f645abe..ab85e38 100644 (file)
@@ -1,10 +1,13 @@
+import sys
 import time
 
+
 class TimeoutException(Exception):
     pass
 
 
-def wait(session, condition, message, interval=0.1, timeout=5):
+def wait(session, condition, message,
+         interval=0.1, timeout=5, ignored_exceptions=Exception):
     """ Poll a condition until it's true or the timeout ellapses.
 
     :param session: WebDriver session to use with `condition`
@@ -12,6 +15,8 @@ def wait(session, condition, message, interval=0.1, timeout=5):
     :param message: failure description to display in case the timeout is reached
     :param interval: seconds between each call to `condition`. Default: 0.1
     :param timeout: seconds until we stop polling. Default: 5
+    :param ignored_exceptions: Exceptions that are expected and can be ignored.
+        Default: Exception
     """
 
     start = time.time()
@@ -19,10 +24,16 @@ def wait(session, condition, message, interval=0.1, timeout=5):
 
     while not (time.time() >= end):
         next_step = time.time() + interval
-        success = condition(session)
+        try:
+            success = condition(session)
+        except ignored_exceptions:
+            last_exc = sys.exc_info()[0]
+            success = False
         next_interval = max(next_step - time.time(), 0)
         if not success:
             time.sleep(next_interval)
             continue
         return success
+
+    print "Last exception encountered was {}".format(last_exc)
     raise TimeoutException("Timed out after %d seconds: %s" % (timeout, message))
index d97aa73..d47ed1c 100644 (file)
@@ -1,4 +1,5 @@
 from tests.support.asserts import assert_error, assert_success
+from tests.support.inline import inline
 
 
 def accept_alert(session):
@@ -25,14 +26,14 @@ def test_no_user_prompt(session):
 
 def test_accept_alert(session):
     # 18.2 step 3
-    session.execute_script("window.alert(\"Hello\");")
+    session.url = inline("<script>window.alert('Hello');</script>")
     response = accept_alert(session)
     assert_success(response)
 
 
 def test_accept_confirm(session):
     # 18.2 step 3
-    session.execute_script("window.result = window.confirm(\"Hello\");")
+    session.url = inline("<script>window.result = window.confirm('Hello');</script>")
     response = accept_alert(session)
     assert_success(response)
     assert session.execute_script("return window.result") is True
@@ -40,7 +41,7 @@ def test_accept_confirm(session):
 
 def test_accept_prompt(session):
     # 18.2 step 3
-    session.execute_script("window.result = window.prompt(\"Enter Your Name: \", \"Federer\");")
+    session.url = inline("<script>window.result = window.prompt('Enter Your Name: ', 'Federer');</script>")
     response = accept_alert(session)
     assert_success(response)
     assert session.execute_script("return window.result") == "Federer"
index 73f9822..6a20496 100644 (file)
@@ -1,4 +1,5 @@
 from tests.support.asserts import assert_error, assert_success
+from tests.support.inline import inline
 
 
 def dismiss_alert(session):
@@ -25,14 +26,14 @@ def test_no_user_prompt(session):
 
 def test_dismiss_alert(session):
     # 18.1 step 3
-    session.execute_script("window.alert(\"Hello\");")
+    session.url = inline("<script>window.alert('Hello');</script>")
     response = dismiss_alert(session)
     assert_success(response)
 
 
 def test_dismiss_confirm(session):
     # 18.1 step 3
-    session.execute_script("window.result = window.confirm(\"Hello\");")
+    session.url = inline("<script>window.result = window.confirm('Hello');</script>")
     response = dismiss_alert(session)
     assert_success(response)
     assert session.execute_script("return window.result;") is False
@@ -40,7 +41,7 @@ def test_dismiss_confirm(session):
 
 def test_dismiss_prompt(session):
     # 18.1 step 3
-    session.execute_script("window.result = window.prompt(\"Enter Your Name: \", \"Federer\");")
+    session.url = inline("<script>window.result = window.prompt('Enter Your Name: ', 'Federer');</script>")
     response = dismiss_alert(session)
     assert_success(response)
     assert session.execute_script("return window.result") is None
index 85506ee..7b71f7f 100644 (file)
@@ -1,4 +1,5 @@
 from tests.support.asserts import assert_error, assert_success
+from tests.support.inline import inline
 
 
 def get_dialog_text(session):
@@ -25,7 +26,7 @@ def test_no_user_prompt(session):
 
 def test_get_alert_text(session):
     # 18.3 step 3
-    session.execute_script("window.alert(\"Hello\");")
+    session.url = inline("<script>window.alert('Hello');</script>")
     response = get_dialog_text(session)
     assert_success(response)
     assert isinstance(response.body, dict)
@@ -37,7 +38,7 @@ def test_get_alert_text(session):
 
 def test_get_confirm_text(session):
     # 18.3 step 3
-    session.execute_script("window.confirm(\"Hello\");")
+    session.url = inline("<script>window.confirm('Hello');</script>")
     response = get_dialog_text(session)
     assert_success(response)
     assert isinstance(response.body, dict)
@@ -49,7 +50,7 @@ def test_get_confirm_text(session):
 
 def test_get_prompt_text(session):
     # 18.3 step 3
-    session.execute_script("window.prompt(\"Enter Your Name: \", \"Federer\");")
+    session.url = inline("<script>window.prompt('Enter Your Name: ', 'Federer');</script>")
     response = get_dialog_text(session)
     assert_success(response)
     assert isinstance(response.body, dict)
index 8e07b85..6769b55 100644 (file)
@@ -1,6 +1,7 @@
 import pytest
 
 from tests.support.asserts import assert_error, assert_success
+from tests.support.inline import inline
 
 def send_alert_text(session, body=None):
     return session.transport.send("POST", "session/{session_id}/alert/text"
@@ -12,7 +13,7 @@ def send_alert_text(session, body=None):
 @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\");")
+    session.url = inline("<script>window.result = window.prompt('Enter Your Name: ', 'Name');</script>")
     response = send_alert_text(session, {"text": text})
     assert_error(response, "invalid argument")
 
@@ -35,7 +36,7 @@ def test_no_user_prompt(session):
 
 def test_alert_element_not_interactable(session):
     # 18.4 step 5
-    session.execute_script("window.alert(\"Hello\");")
+    session.url = inline("<script>window.alert('Hello');</script>")
     body = {"text": "Federer"}
     response = send_alert_text(session, body)
     assert_error(response, "element not interactable")
@@ -43,7 +44,7 @@ def test_alert_element_not_interactable(session):
 
 def test_confirm_element_not_interactable(session):
     # 18.4 step 5
-    session.execute_script("window.confirm(\"Hello\");")
+    session.url = inline("<script>window.confirm('Hello');</script>")
     body = {"text": "Federer"}
     response = send_alert_text(session, body)
     assert_error(response, "element not interactable")
@@ -51,7 +52,7 @@ def test_confirm_element_not_interactable(session):
 
 def test_send_alert_text(session):
     # 18.4 step 6
-    session.execute_script("window.result = window.prompt(\"Enter Your Name: \", \"Name\");")
+    session.url = inline("<script>window.result = window.prompt('Enter Your Name: ', 'Name');</script>")
     body = {"text": "Federer"}
     send_response = send_alert_text(session, body)
     assert_success(send_response)
@@ -63,7 +64,7 @@ def test_send_alert_text(session):
 
 def test_send_alert_text_with_whitespace(session):
     # 18.4 step 6
-    session.execute_script("window.result = window.prompt(\"Enter Your Name: \", \"Name\");")
+    session.url = inline("<script>window.result = window.prompt('Enter Your Name: ', 'Name');</script>")
     body = {"text": " Fed erer "}
     send_response = send_alert_text(session, body)
     assert_success(send_response)