diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/resources/test | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/resources/test')
102 files changed, 9869 insertions, 0 deletions
diff --git a/testing/web-platform/tests/resources/test/README.md b/testing/web-platform/tests/resources/test/README.md new file mode 100644 index 0000000000..edc03ef214 --- /dev/null +++ b/testing/web-platform/tests/resources/test/README.md @@ -0,0 +1,83 @@ +# `testharness.js` test suite + +The test suite for the `testharness.js` testing framework. + +## Executing Tests + +Install the following dependencies: + +- [Python 2.7.9+](https://www.python.org/) +- [the tox Python package](https://tox.readthedocs.io/en/latest/) +- [the Mozilla Firefox web browser](https://mozilla.org/firefox) +- [the GeckoDriver server](https://github.com/mozilla/geckodriver) + +Make sure `geckodriver` can be found in your `PATH`. + +Currently, the tests should be run with the latest *Firefox Nightly*. In order to +specify the path to Firefox Nightly, use the following command-line option: + + tox -- --binary=/path/to/FirefoxNightly + +### Automated Script + +Alternatively, you may run `tools/ci/ci_resources_unittest.sh`, which only depends on +Python 2. The script will install other dependencies automatically and start `tox` with +the correct arguments. + +## Authoring Tests + +Test cases are expressed as `.html` files located within the `tests/unit/` and +`tests/functional/` sub-directories. Each test should include the +`testharness.js` library with the following markup: + + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + +This should be followed by one or more `<script>` tags that interface with the +`testharness.js` API in some way. For example: + + <script> + test(function() { + 1 = 1; + }, 'This test is expected to fail.'); + </script> + +### Unit tests + +The "unit test" type allows for concisely testing the expected behavior of +assertion methods. These tests may define any number of sub-tests; the +acceptance criteria is simply that all tests executed pass. + +### Functional tests + +Thoroughly testing the behavior of the harness itself requires ensuring a +number of considerations which cannot be verified with the "unit testing" +strategy. These include: + +- Ensuring that some tests are not run +- Ensuring conditions that cause test failures +- Ensuring conditions that cause harness errors + +Functional tests allow for these details to be verified. Every functional test +must include a summary of the expected results as a JSON string within a +`<script>` tag with an `id` of `"expected"`, e.g.: + + <script type="text/json" id="expected"> + { + "summarized_status": { + "message": null, + "stack": null, + "status_string": "OK" + }, + "summarized_tests": [ + { + "message": "ReferenceError: invalid assignment left-hand side", + "name": "Sample HTML5 API Tests", + "properties": {}, + "stack": "(implementation-defined)", + "status_string": "FAIL" + } + ], + "type": "complete" + } + </script> diff --git a/testing/web-platform/tests/resources/test/conftest.py b/testing/web-platform/tests/resources/test/conftest.py new file mode 100644 index 0000000000..7253cac9ac --- /dev/null +++ b/testing/web-platform/tests/resources/test/conftest.py @@ -0,0 +1,269 @@ +import copy +import json +import os +import ssl +import sys +import subprocess +import urllib + +import html5lib +import py +import pytest + +from wptserver import WPTServer + +HERE = os.path.dirname(os.path.abspath(__file__)) +WPT_ROOT = os.path.normpath(os.path.join(HERE, '..', '..')) +HARNESS = os.path.join(HERE, 'harness.html') +TEST_TYPES = ('functional', 'unit') + +sys.path.insert(0, os.path.normpath(os.path.join(WPT_ROOT, "tools"))) +import localpaths + +sys.path.insert(0, os.path.normpath(os.path.join(WPT_ROOT, "tools", "webdriver"))) +import webdriver + + +def pytest_addoption(parser): + parser.addoption("--binary", action="store", default=None, help="path to browser binary") + parser.addoption("--headless", action="store_true", default=False, help="run browser in headless mode") + + +def pytest_collect_file(file_path, path, parent): + if file_path.suffix.lower() != '.html': + return + + # Tests are organized in directories by type + test_type = os.path.relpath(str(file_path), HERE) + if os.path.sep not in test_type or ".." in test_type: + # HTML files in this directory are not tests + return + test_type = test_type.split(os.path.sep)[1] + + # Handle the deprecation of Node construction in pytest6 + # https://docs.pytest.org/en/stable/deprecations.html#node-construction-changed-to-node-from-parent + if hasattr(HTMLItem, "from_parent"): + return HTMLItem.from_parent(parent, filename=str(file_path), test_type=test_type) + return HTMLItem(parent, str(file_path), test_type) + + +def pytest_configure(config): + config.proc = subprocess.Popen(["geckodriver"]) + config.add_cleanup(config.proc.kill) + + capabilities = {"alwaysMatch": {"acceptInsecureCerts": True, "moz:firefoxOptions": {}}} + if config.getoption("--binary"): + capabilities["alwaysMatch"]["moz:firefoxOptions"]["binary"] = config.getoption("--binary") + if config.getoption("--headless"): + capabilities["alwaysMatch"]["moz:firefoxOptions"]["args"] = ["--headless"] + + config.driver = webdriver.Session("localhost", 4444, + capabilities=capabilities) + config.add_cleanup(config.driver.end) + + # Although the name of the `_create_unverified_context` method suggests + # that it is not intended for external consumption, the standard library's + # documentation explicitly endorses its use: + # + # > To revert to the previous, unverified, behavior + # > ssl._create_unverified_context() can be passed to the context + # > parameter. + # + # https://docs.python.org/2/library/httplib.html#httplib.HTTPSConnection + config.ssl_context = ssl._create_unverified_context() + + config.server = WPTServer(WPT_ROOT) + config.server.start(config.ssl_context) + config.add_cleanup(config.server.stop) + + +def resolve_uri(context, uri): + if uri.startswith('/'): + base = WPT_ROOT + path = uri[1:] + else: + base = os.path.dirname(context) + path = uri + + return os.path.exists(os.path.join(base, path)) + + +def _summarize(actual): + def _scrub_stack(test_obj): + copy = dict(test_obj) + del copy['stack'] + return copy + + def _expand_status(status_obj): + for key, value in [item for item in status_obj.items()]: + # In "status" and "test" objects, the "status" value enum + # definitions are interspersed with properties for unrelated + # metadata. The following condition is a best-effort attempt to + # ignore non-enum properties. + if key != key.upper() or not isinstance(value, int): + continue + + del status_obj[key] + + if status_obj['status'] == value: + status_obj[u'status_string'] = key + + del status_obj['status'] + + return status_obj + + def _summarize_test(test_obj): + del test_obj['index'] + + assert 'phase' in test_obj + assert 'phases' in test_obj + assert 'COMPLETE' in test_obj['phases'] + assert test_obj['phase'] == test_obj['phases']['COMPLETE'] + del test_obj['phases'] + del test_obj['phase'] + + return _expand_status(_scrub_stack(test_obj)) + + def _summarize_status(status_obj): + return _expand_status(_scrub_stack(status_obj)) + + + summarized = {} + + summarized[u'summarized_status'] = _summarize_status(actual['status']) + summarized[u'summarized_tests'] = [ + _summarize_test(test) for test in actual['tests']] + summarized[u'summarized_tests'].sort(key=lambda test_obj: test_obj.get('name')) + summarized[u'summarized_asserts'] = [ + {"assert_name": assert_item["assert_name"], + "test": assert_item["test"]["name"] if assert_item["test"] else None, + "args": assert_item["args"], + "status": assert_item["status"]} for assert_item in actual["asserts"]] + summarized[u'type'] = actual['type'] + + return summarized + + +class HTMLItem(pytest.Item, pytest.Collector): + def __init__(self, parent, filename, test_type): + self.url = parent.session.config.server.url(filename) + self.type = test_type + # Some tests are reliant on the WPT servers substitution functionality, + # so tests must be retrieved from the server rather than read from the + # file system directly. + handle = urllib.request.urlopen(self.url, + context=parent.session.config.ssl_context) + try: + markup = handle.read() + finally: + handle.close() + + if test_type not in TEST_TYPES: + raise ValueError('Unrecognized test type: "%s"' % test_type) + + parsed = html5lib.parse(markup, namespaceHTMLElements=False) + name = None + self.expected = None + + for element in parsed.iter(): + if not name and element.tag == 'title': + name = element.text + continue + if element.tag == 'script': + if element.attrib.get('id') == 'expected': + try: + self.expected = json.loads(element.text) + except ValueError: + print("Failed parsing JSON in %s" % filename) + raise + + if not name: + raise ValueError('No name found in %s add a <title> element' % filename) + elif self.type == 'functional': + if not self.expected: + raise ValueError('Functional tests must specify expected report data') + elif self.type == 'unit' and self.expected: + raise ValueError('Unit tests must not specify expected report data') + + # Ensure that distinct items have distinct fspath attributes. + # This is necessary because pytest has an internal cache keyed on it, + # and only the first test with any given fspath will be run. + # + # This cannot use super(HTMLItem, self).__init__(..) because only the + # Collector constructor takes the fspath argument. + pytest.Item.__init__(self, name, parent) + pytest.Collector.__init__(self, name, parent, fspath=py.path.local(filename)) + + + def reportinfo(self): + return self.fspath, None, self.url + + def repr_failure(self, excinfo): + return pytest.Collector.repr_failure(self, excinfo) + + def runtest(self): + if self.type == 'unit': + self._run_unit_test() + elif self.type == 'functional': + self._run_functional_test() + else: + raise NotImplementedError + + def _run_unit_test(self): + driver = self.session.config.driver + server = self.session.config.server + + driver.url = server.url(HARNESS) + + actual = driver.execute_async_script( + 'runTest("%s", "foo", arguments[0])' % self.url + ) + + summarized = _summarize(copy.deepcopy(actual)) + + print(json.dumps(summarized, indent=2)) + + assert summarized[u'summarized_status'][u'status_string'] == u'OK', summarized[u'summarized_status'][u'message'] + for test in summarized[u'summarized_tests']: + msg = "%s\n%s" % (test[u'name'], test[u'message']) + assert test[u'status_string'] == u'PASS', msg + + def _run_functional_test(self): + driver = self.session.config.driver + server = self.session.config.server + + driver.url = server.url(HARNESS) + + test_url = self.url + actual = driver.execute_async_script('runTest("%s", "foo", arguments[0])' % test_url) + + print(json.dumps(actual, indent=2)) + + summarized = _summarize(copy.deepcopy(actual)) + + print(json.dumps(summarized, indent=2)) + + # Test object ordering is not guaranteed. This weak assertion verifies + # that the indices are unique and sequential + indices = [test_obj.get('index') for test_obj in actual['tests']] + self._assert_sequence(indices) + + self.expected[u'summarized_tests'].sort(key=lambda test_obj: test_obj.get('name')) + + # Make asserts opt-in for now + if "summarized_asserts" not in self.expected: + del summarized["summarized_asserts"] + else: + # We can't be sure of the order of asserts even within the same test + # although we could also check for the failing assert being the final + # one + for obj in [summarized, self.expected]: + obj["summarized_asserts"].sort( + key=lambda x: (x["test"] or "", x["status"], x["assert_name"], tuple(x["args"]))) + + assert summarized == self.expected + + @staticmethod + def _assert_sequence(nums): + if nums and len(nums) > 0: + assert nums == list(range(1, nums[-1] + 1)) diff --git a/testing/web-platform/tests/resources/test/harness.html b/testing/web-platform/tests/resources/test/harness.html new file mode 100644 index 0000000000..5ee0f285e8 --- /dev/null +++ b/testing/web-platform/tests/resources/test/harness.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + </head> + <body> + <script> +function runTest(url, id, done) { + var child; + + function onMessage(event) { + if (!event.data || event.data.type !== 'complete') { + return; + } + + window.removeEventListener('message', onMessage); + child.close(); + done(event.data); + } + window.addEventListener('message', onMessage); + + window.child = child = window.open(url, id); +} + </script> + </body> +</html> diff --git a/testing/web-platform/tests/resources/test/idl-helper.js b/testing/web-platform/tests/resources/test/idl-helper.js new file mode 100644 index 0000000000..2b73527ff2 --- /dev/null +++ b/testing/web-platform/tests/resources/test/idl-helper.js @@ -0,0 +1,24 @@ +"use strict"; + +var typedefFrom = interfaceFrom; +var dictionaryFrom = interfaceFrom; +function interfaceFrom(i) { + var idl = new IdlArray(); + idl.add_idls(i); + for (var prop in idl.members) { + return idl.members[prop]; + } +} + +function memberFrom(m) { + var idl = new IdlArray(); + idl.add_idls('interface A { ' + m + '; };'); + return idl.members["A"].members[0]; +} + +function typeFrom(type) { + var ast = WebIDL2.parse('interface Foo { ' + type + ' a(); };'); + ast = ast[0]; // get the first fragment + ast = ast.members[0]; // get the first member + return ast.idlType; // get the type of the first field +} diff --git a/testing/web-platform/tests/resources/test/nested-testharness.js b/testing/web-platform/tests/resources/test/nested-testharness.js new file mode 100644 index 0000000000..d97c1568c7 --- /dev/null +++ b/testing/web-platform/tests/resources/test/nested-testharness.js @@ -0,0 +1,80 @@ +'use strict'; + +/** + * Execute testharness.js and one or more scripts in an iframe. Report the + * results of the execution. + * + * @param {...function|...string} bodies - a function body. If specified as a + * function object, it will be + * serialized to a string using the + * built-in + * `Function.prototype.toString` prior + * to inclusion in the generated + * iframe. + * + * @returns {Promise} eventual value describing the result of the test + * execution; the summary object has two properties: + * `harness` (a string describing the harness status) and + * `tests` (an object whose "own" property names are the + * titles of the defined sub-tests and whose associated + * values are the subtest statuses). + */ +function makeTest(...bodies) { + const closeScript = '<' + '/script>'; + let src = ` +<!DOCTYPE HTML> +<html> +<head> +<title>Document title</title> +<script src="/resources/testharness.js?${Math.random()}">${closeScript} +</head> + +<body> +<div id="log"></div>`; + bodies.forEach((body) => { + src += '<script>(' + body + ')();' + closeScript; + }); + + const iframe = document.createElement('iframe'); + + document.body.appendChild(iframe); + iframe.contentDocument.write(src); + + return new Promise((resolve) => { + window.addEventListener('message', function onMessage(e) { + if (e.source !== iframe.contentWindow) { + return; + } + if (!e.data || e.data.type !=='complete') { + return; + } + window.removeEventListener('message', onMessage); + resolve(e.data); + }); + + iframe.contentDocument.close(); + }).then(({ tests, status }) => { + const summary = { + harness: getEnumProp(status, status.status), + tests: {} + }; + + tests.forEach((test) => { + summary.tests[test.name] = getEnumProp(test, test.status); + }); + + return summary; + }); +} + +function getEnumProp(object, value) { + for (let property in object) { + if (!/^[A-Z]+$/.test(property)) { + continue; + } + + if (object[property] === value) { + return property; + } + } +} diff --git a/testing/web-platform/tests/resources/test/requirements.txt b/testing/web-platform/tests/resources/test/requirements.txt new file mode 100644 index 0000000000..95d87c3875 --- /dev/null +++ b/testing/web-platform/tests/resources/test/requirements.txt @@ -0,0 +1 @@ +html5lib==1.1 diff --git a/testing/web-platform/tests/resources/test/tests/functional/abortsignal.html b/testing/web-platform/tests/resources/test/tests/functional/abortsignal.html new file mode 100644 index 0000000000..e6080e96ec --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/abortsignal.html @@ -0,0 +1,49 @@ +<!DOCTYPE html> +<title>Test#get_signal</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + "use strict"; + + setup(() => { + assert_implements_optional(window.AbortController, "No AbortController"); + }); + + let signal; + let observed = false; + + test(t => { + signal = t.get_signal(); + assert_true(signal instanceof AbortSignal, "Returns an abort signal"); + assert_false(signal.aborted, "Signal should not be aborted before test end"); + signal.onabort = () => observed = true; + }, "t.signal existence"); + + test(t => { + assert_true(signal.aborted, "Signal should be aborted after test end"); + assert_true(observed, "onabort should have been called"); + }, "t.signal.aborted"); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "message": null, + "status_string": "OK" + }, + "summarized_tests": [ + { + "message": null, + "name": "t.signal existence", + "properties": {}, + "status_string": "PASS" + }, + { + "message": null, + "name": "t.signal.aborted", + "properties": {}, + "status_string": "PASS" + } + ], + "type": "complete" +} +</script> diff --git a/testing/web-platform/tests/resources/test/tests/functional/add_cleanup.html b/testing/web-platform/tests/resources/test/tests/functional/add_cleanup.html new file mode 100644 index 0000000000..468319fdbe --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/add_cleanup.html @@ -0,0 +1,91 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Test#add_cleanup</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> +<div id="log"></div> +<script> +"use strict"; + +var log_sync; +test(function(t) { + log_sync = ""; + t.add_cleanup(function() { log_sync += "1"; }); + t.add_cleanup(function() { log_sync += "2"; }); + t.add_cleanup(function() { log_sync += "3"; }); + t.add_cleanup(function() { log_sync += "4"; }); + t.add_cleanup(function() { log_sync += "5"; }); + log_sync += "0"; +}, "probe synchronous"); + +test(function() { + if (log_sync !== "012345") { + throw new Error("Expected: '012345'. Actual: '" + log_sync + "'."); + } +}, "Cleanup methods are invoked exactly once and in the expected sequence."); + +var complete, log_async; +async_test(function(t) { + complete = t.step_func(function() { + if (log_async !== "012") { + throw new Error("Expected: '012'. Actual: '" + log_async + "'."); + } + + t.done(); + }); +}, "Cleanup methods are invoked following the completion of asynchronous tests"); + +async_test(function(t) { + log_async = ""; + t.add_cleanup(function() { log_async += "1"; }); + + setTimeout(t.step_func(function() { + t.add_cleanup(function() { + log_async += "2"; + complete(); + }); + log_async += "0"; + t.done(); + }), 0); +}, "probe asynchronous"); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "Cleanup methods are invoked exactly once and in the expected sequence.", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Cleanup methods are invoked following the completion of asynchronous tests", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "probe asynchronous", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "probe synchronous", + "properties": {}, + "message": null + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_async.html b/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_async.html new file mode 100644 index 0000000000..07ade4b93b --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_async.html @@ -0,0 +1,85 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Test#add_cleanup with Promise-returning functions</title> +<script src="../../../testharness.js"></script> +<script src="../../../testharnessreport.js"></script> +</head> +<body> +<div id="log"></div> + +<script> +"use strict"; +var completeCount = 0; +var counts = { + afterTick: null, + afterFirst: null +}; + +add_result_callback(function(result_t) { + completeCount += 1; +}); + +promise_test(function(t) { + t.add_cleanup(function() { + return new Promise(function(resolve) { + setTimeout(function() { + counts.afterTick = completeCount; + resolve(); + }, 0); + }); + }); + t.add_cleanup(function() { + return new Promise(function(resolve) { + + setTimeout(function() { + counts.afterFirst = completeCount; + resolve(); + }, 0); + }); + }); + + return Promise.resolve(); +}, 'promise_test with asynchronous cleanup'); + +promise_test(function() { + assert_equals( + counts.afterTick, + 0, + "test is not asynchronously considered 'complete'" + ); + assert_equals( + counts.afterFirst, + 0, + "test is not considered 'complete' following fulfillment of first promise" + ); + assert_equals(completeCount, 1); + + return Promise.resolve(); +}, "synchronously-defined promise_test"); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "promise_test with asynchronous cleanup", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "synchronously-defined promise_test", + "message": null, + "properties": {} + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_async_bad_return.html b/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_async_bad_return.html new file mode 100644 index 0000000000..867bde2c39 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_async_bad_return.html @@ -0,0 +1,50 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Test#add_cleanup with non-thenable-returning function</title> +<script src="../../../testharness.js"></script> +<script src="../../../testharnessreport.js"></script> +</head> +<body> +<div id="log"></div> + +<script> +"use strict"; + +promise_test(function(t) { + t.add_cleanup(function() {}); + t.add_cleanup(function() { + return { then: 9 }; + }); + t.add_cleanup(function() { return Promise.resolve(); }); + + return Promise.resolve(); +}, "promise_test that returns a non-thenable object in one \"cleanup\" callback"); + +promise_test(function() {}, "The test runner is in an unpredictable state ('NOT RUN')"); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "ERROR", + "message": "Test named 'promise_test that returns a non-thenable object in one \"cleanup\" callback' specified 3 'cleanup' functions, and 1 returned a non-thenable value." + }, + "summarized_tests": [ + { + "status_string": "NOTRUN", + "name": "The test runner is in an unpredictable state ('NOT RUN')", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "promise_test that returns a non-thenable object in one \"cleanup\" callback", + "message": null, + "properties": {} + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_async_rejection.html b/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_async_rejection.html new file mode 100644 index 0000000000..e51465e7eb --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_async_rejection.html @@ -0,0 +1,94 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Test#add_cleanup with Promise-returning functions (rejection handling)</title> +<script src="../../../testharness.js"></script> +<script src="../../../testharnessreport.js"></script> +</head> +<body> +<div id="log"></div> + +<script> +"use strict"; +var resolve, reject; +var completeCount = 0; +add_result_callback(function(result_t) { + completeCount += 1; +}); +promise_test(function(t) { + t.add_cleanup(function() { + return new Promise(function(_, _reject) { reject = _reject; }); + }); + t.add_cleanup(function() { + return new Promise(function(_resolve) { resolve = _resolve; }); + }); + + // The following cleanup function defines empty tests so that the reported + // data demonstrates the intended run-time behavior without relying on the + // test harness's handling of errors during test cleanup (which is tested + // elsewhere). + t.add_cleanup(function() { + if (completeCount === 0) { + promise_test( + function() {}, + "test is not asynchronously considered 'complete' ('NOT RUN')" + ); + } + + reject(); + + setTimeout(function() { + if (completeCount === 0) { + promise_test( + function() {}, + "test is not considered 'complete' following rejection of first " + + "promise ('NOT RUN')" + ); + } + + resolve(); + }, 0); + }); + + return Promise.resolve(); +}, "promise_test with asynchronous cleanup including rejection"); + +promise_test(function() {}, "synchronously-defined test ('NOT RUN')"); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "ERROR", + "message": "Test named 'promise_test with asynchronous cleanup including rejection' specified 3 'cleanup' functions, and 1 failed." + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "promise_test with asynchronous cleanup including rejection", + "message": null, + "properties": {} + }, + { + "status_string": "NOTRUN", + "name": "synchronously-defined test ('NOT RUN')", + "message": null, + "properties": {} + }, + { + "status_string": "NOTRUN", + "name": "test is not asynchronously considered 'complete' ('NOT RUN')", + "message": null, + "properties": {} + }, + { + "status_string": "NOTRUN", + "name": "test is not considered 'complete' following rejection of first promise ('NOT RUN')", + "message": null, + "properties": {} + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_async_rejection_after_load.html b/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_async_rejection_after_load.html new file mode 100644 index 0000000000..f9b2846100 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_async_rejection_after_load.html @@ -0,0 +1,52 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Test#add_cleanup with Promise-returning functions (rejection handling following "load" event)</title> +</head> +<body> +<h1>Promise Tests</h1> +<p>This test demonstrates the use of <tt>promise_test</tt>. Assumes ECMAScript 6 +Promise support. Some failures are expected.</p> +<div id="log"></div> +<script src="../../../testharness.js"></script> +<script src="../../../testharnessreport.js"></script> +<script> +promise_test(function(t) { + t.add_cleanup(function() { + return Promise.reject(new Error("foo")); + }); + + return new Promise((resolve) => { + document.addEventListener("DOMContentLoaded", function() { + setTimeout(resolve, 0) + }); + }); +}, "Test with failing cleanup that completes after DOMContentLoaded event"); + +promise_test(function(t) { + return Promise.resolve(); +}, "Test that should not be run due to invalid harness state ('NOT RUN')"); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "ERROR", + "message": "Test named 'Test with failing cleanup that completes after DOMContentLoaded event' specified 1 'cleanup' function, and 1 failed." + }, + "summarized_tests": [ + { + "status_string": "NOTRUN", + "name": "Test that should not be run due to invalid harness state ('NOT RUN')", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "Test with failing cleanup that completes after DOMContentLoaded event", + "message": null, + "properties": {} + } + ], + "type": "complete" +} +</script> diff --git a/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_async_timeout.html b/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_async_timeout.html new file mode 100644 index 0000000000..429536ce6e --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_async_timeout.html @@ -0,0 +1,57 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Test#add_cleanup with Promise-returning functions (timeout handling)</title> +<script src="../../../testharness.js"></script> +<script src="../../../testharnessreport.js"></script> +</head> +<body> +<div id="log"></div> +<script> +"use strict"; + +promise_test(function(t) { + t.add_cleanup(function() { + return Promise.resolve(); + }); + + t.add_cleanup(function() { + return new Promise(function() {}); + }); + + t.add_cleanup(function() {}); + + t.add_cleanup(function() { + return new Promise(function() {}); + }); + + return Promise.resolve(); +}, "promise_test with asynchronous cleanup"); + +promise_test(function() {}, "promise_test following timed out cleanup ('NOT RUN')"); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "ERROR", + "message": "Timeout while running cleanup for test named \"promise_test with asynchronous cleanup\"." + }, + "summarized_tests": [ + { + "status_string": "NOTRUN", + "name": "promise_test following timed out cleanup ('NOT RUN')", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "promise_test with asynchronous cleanup", + "message": null, + "properties": {} + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_bad_return.html b/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_bad_return.html new file mode 100644 index 0000000000..3cfb28adf2 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_bad_return.html @@ -0,0 +1,61 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Test#add_cleanup with value-returning function</title> +<script src="../../../testharness.js"></script> +<script src="../../../testharnessreport.js"></script> +</head> +<body> +<div id="log"></div> +<script> +"use strict"; + +test(function(t) { + t.add_cleanup(function() {}); + t.add_cleanup(function() { return null; }); + t.add_cleanup(function() { + test( + function() {}, + "The test runner is in an unpredictable state #1 ('NOT RUN')" + ); + + throw new Error(); + }); + t.add_cleanup(function() { return 4; }); + t.add_cleanup(function() { return { then: function() {} }; }); + t.add_cleanup(function() {}); +}, "Test that returns a value in three \"cleanup\" functions"); + +test(function() {}, "The test runner is in an unpredictable state #2 ('NOT RUN')"); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "ERROR", + "message": "Test named 'Test that returns a value in three \"cleanup\" functions' specified 6 'cleanup' functions, and 1 failed, and 3 returned a non-undefined value." + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "Test that returns a value in three \"cleanup\" functions", + "properties": {}, + "message": null + }, + { + "status_string": "NOTRUN", + "name": "The test runner is in an unpredictable state #1 ('NOT RUN')", + "message": null, + "properties": {} + }, + { + "status_string": "NOTRUN", + "name": "The test runner is in an unpredictable state #2 ('NOT RUN')", + "message": null, + "properties": {} + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_count.html b/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_count.html new file mode 100644 index 0000000000..2c9b51c6d0 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_count.html @@ -0,0 +1,39 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Test#add_cleanup reported count</title> +<script src="../../../testharness.js"></script> +<script src="../../../testharnessreport.js"></script> +</head> +<body> +<div id="log"></div> + +<script> +promise_test(function(t) { + t.add_cleanup(function() {}); + t.add_cleanup(function() {}); + t.add_cleanup(function() { throw new Error(); }); + new EventWatcher(t, document.body, []); + + return Promise.resolve(); +}, 'test with 3 user-defined cleanup functions'); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "ERROR", + "message": "Test named 'test with 3 user-defined cleanup functions' specified 3 'cleanup' functions, and 1 failed." + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "test with 3 user-defined cleanup functions", + "message": null, + "properties": {} + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_err.html b/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_err.html new file mode 100644 index 0000000000..60357c66ee --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_err.html @@ -0,0 +1,45 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Test#add_cleanup: error</title> +<script src="../../../testharness.js"></script> +<script src="../../../testharnessreport.js"></script> +</head> +<body> +<div id="log"></div> +<script> +"use strict"; + +test(function(t) { + t.add_cleanup(function() { + throw new Error('exception in cleanup function'); + }); +}, "Exception in cleanup function causes harness failure."); + +test(function() {}, "This test should not be run."); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "ERROR", + "message": "Test named 'Exception in cleanup function causes harness failure.' specified 1 'cleanup' function, and 1 failed." + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "Exception in cleanup function causes harness failure.", + "properties": {}, + "message": null + }, + { + "status_string": "NOTRUN", + "name": "This test should not be run.", + "properties": {}, + "message": null + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_err_multi.html b/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_err_multi.html new file mode 100644 index 0000000000..80ba1b4959 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_err_multi.html @@ -0,0 +1,52 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Test#add_cleanup: multiple functions with one in error</title> +<script src="../../../testharness.js"></script> +<script src="../../../testharnessreport.js"></script> +</head> +<body> +<div id="log"></div> + +<script> +"use strict"; + +test(function(t) { + t.add_cleanup(function() { + throw new Error("exception in cleanup function"); + }); + + // The following cleanup function defines a test so that the reported + // data demonstrates the intended run-time behavior, i.e. that + // `testharness.js` invokes all cleanup functions even when one or more + // throw errors. + t.add_cleanup(function() { + test(function() {}, "Verification test"); + }); + }, "Test with multiple cleanup functions"); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "ERROR", + "message": "Test named 'Test with multiple cleanup functions' specified 2 'cleanup' functions, and 1 failed." + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "Test with multiple cleanup functions", + "properties": {}, + "message": null + }, + { + "status_string": "NOTRUN", + "name": "Verification test", + "properties": {}, + "message": null + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_sync_queue.html b/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_sync_queue.html new file mode 100644 index 0000000000..0a61503cc8 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/add_cleanup_sync_queue.html @@ -0,0 +1,55 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Test#add_cleanup: queuing tests</title> +<script src="../../../testharness.js"></script> +<script src="../../../testharnessreport.js"></script> +</head> +<body> +<div id="log"></div> + +<script> +"use strict"; +var firstCleaned = false; + +promise_test(function(t) { + promise_test(function() { + assert_true( + firstCleaned, "should not execute until first test is complete" + ); + + return Promise.resolve(); + }, "test defined when no tests are queued, but one test is executing"); + + t.add_cleanup(function() { + firstCleaned = true; + }); + + return Promise.resolve(); +}, "Test with a 'cleanup' function"); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "message": null, + "status_string": "OK" + }, + "summarized_tests": [ + { + "message": null, + "name": "Test with a 'cleanup' function", + "status_string": "PASS", + "properties": {} + }, + { + "message": null, + "name": "test defined when no tests are queued, but one test is executing", + "status_string": "PASS", + "properties": {} + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/api-tests-1.html b/testing/web-platform/tests/resources/test/tests/functional/api-tests-1.html new file mode 100644 index 0000000000..9de875b0f1 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/api-tests-1.html @@ -0,0 +1,991 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Sample HTML5 API Tests</title> +<meta name="timeout" content="6000"> +</head> +<body onload="load_test_attr.done()"> +<h1>Sample HTML5 API Tests</h1> +<div id="log"></div> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + setup_run = false; + setup(function() { + setup_run = true; + }); + test(function() {assert_true(setup_run)}, "Setup function ran"); + + // Two examples for testing events from handler and attributes + var load_test_event = async_test("window onload event fires when set from the handler"); + + function windowLoad() + { + load_test_event.done(); + } + on_event(window, "load", windowLoad); + + test(function() { + var sequence = []; + var outer = document.createElement("div"); + var inner = document.createElement("div"); + outer.appendChild(inner); + document.body.appendChild(outer); + inner.addEventListener("click", function() { + sequence.push("inner"); + }, false); + + on_event(outer, "click", function() { + sequence.push("outer"); + }); + inner.click(); + + assert_array_equals(sequence, ["inner", "outer"]); + }, "on_event does not use event capture"); + + // see the body onload below + var load_test_attr = async_test("body element fires the onload event set from the attribute"); +</script> +<script> + function bodyElement() + { + assert_equals(document.body, document.getElementsByTagName("body")[0]); + } + test(bodyElement, "document.body should be the first body element in the document"); + + test(function() { + assert_equals(1,1); + assert_equals(NaN, NaN, "NaN case"); + assert_equals(0, 0, "Zero case"); + }, "assert_equals tests") + + test(function() { + assert_equals(-0, 0, "Zero case"); + }, "assert_equals tests expected to fail") + + test(function() { + assert_not_equals({}, {}, "object case"); + assert_not_equals(-0, 0, "Zero case"); + }, "assert_not_equals tests") + + function testAssertPass() + { + assert_true(true); + } + test(testAssertPass, "assert_true expected to pass"); + + function testAssertFalse() + { + assert_true(false, "false should not be true"); + } + test(testAssertFalse, "assert_true expected to fail"); + + function basicAssertArrayEquals() + { + assert_array_equals([1, NaN], [1, NaN], "[1, NaN] is equal to [1, NaN]"); + } + test(basicAssertArrayEquals, "basic assert_array_equals test"); + + function assertArrayEqualsUndefined() + { + assert_array_equals(undefined, [1], "undefined equals [1]?"); + } + test(assertArrayEqualsUndefined, "assert_array_equals with first param undefined"); + + function assertArrayEqualsTrue() + { + assert_array_equals(true, [1], "true equals [1]?"); + } + test(assertArrayEqualsTrue, "assert_array_equals with first param true"); + + function assertArrayEqualsFalse() + { + assert_array_equals(false, [1], "false equals [1]?"); + } + test(assertArrayEqualsFalse, "assert_array_equals with first param false"); + + function assertArrayEqualsNull() + { + assert_array_equals(null, [1], "null equals [1]?"); + } + test(assertArrayEqualsNull, "assert_array_equals with first param null"); + + function assertArrayEqualsNumeric() + { + assert_array_equals(1, [1], "1 equals [1]?"); + } + test(assertArrayEqualsNumeric, "assert_array_equals with first param 1"); + + function basicAssertArrayApproxEquals() + { + assert_array_approx_equals([10, 11], [11, 10], 1, "[10, 11] is approximately (+/- 1) [11, 10]") + } + test(basicAssertArrayApproxEquals, "basic assert_array_approx_equals test"); + + function basicAssertApproxEquals() + { + assert_approx_equals(10, 11, 1, "10 is approximately (+/- 1) 11") + } + test(basicAssertApproxEquals, "basic assert_approx_equals test"); + + function basicAssertLessThan() + { + assert_less_than(10, 11, "10 is less than 11") + } + test(basicAssertApproxEquals, "basic assert_less_than test"); + + function basicAssertGreaterThan() + { + assert_greater_than(10, 11, "10 is not greater than 11"); + } + test(basicAssertGreaterThan, "assert_greater_than expected to fail"); + + function basicAssertGreaterThanEqual() + { + assert_greater_than_equal(10, 10, "10 is greater than or equal to 10") + } + test(basicAssertGreaterThanEqual, "basic assert_greater_than_equal test"); + + function basicAssertLessThanEqual() + { + assert_greater_than_equal('10', 10, "'10' is not a number") + } + test(basicAssertLessThanEqual, "assert_less_than_equal expected to fail"); + + function testAssertInherits() { + var A = function(){this.a = "a"} + A.prototype = {b:"b"} + var a = new A(); + assert_own_property(a, "a"); + assert_not_own_property(a, "b", "unexpected property found: \"b\""); + assert_inherits(a, "b"); + } + test(testAssertInherits, "test for assert[_not]_own_property and insert_inherits") + + test(function() + { + var a = document.createElement("a") + var b = document.createElement("b") + assert_throws_dom("NOT_FOUND_ERR", function () {a.removeChild(b)}); + }, "Test throw DOM exception") + + test(function() + { + var a = document.createElement("a") + var b = document.createElement("b") + assert_throws_js(DOMException, function () {a.removeChild(b)}); + }, "Test throw DOMException as JS exception expected to fail") + + test(function() + { + assert_throws_js(SyntaxError, function () {document.querySelector("")}); + }, "Test throw SyntaxError DOMException where JS SyntaxError expected; expected to fail") + + test(function() + { + assert_throws_js(SyntaxError, function () {JSON.parse("{")}); + }, "Test throw JS SyntaxError") + + test(function() + { + assert_throws_dom("SyntaxError", function () {document.querySelector("")}); + }, "Test throw DOM SyntaxError") + + test(function() + { + var ifr = document.createElement("iframe"); + document.body.appendChild(ifr); + this.add_cleanup(() => ifr.remove()); + assert_throws_dom("SyntaxError", ifr.contentWindow.DOMException, + function () {ifr.contentDocument.querySelector("")}); + }, "Test throw DOM SyntaxError from subframe"); + + test(function() + { + var ifr = document.createElement("iframe"); + document.body.appendChild(ifr); + this.add_cleanup(() => ifr.remove()); + assert_throws_dom("SyntaxError", + function () {ifr.contentDocument.querySelector("")}); + }, "Test throw DOM SyntaxError from subframe with incorrect global expectation; expected to fail"); + + test(function() + { + var ifr = document.createElement("iframe"); + document.body.appendChild(ifr); + this.add_cleanup(() => ifr.remove()); + assert_throws_dom("SyntaxError", ifr.contentWindow.DOMException, + function () {document.querySelector("")}); + }, "Test throw DOM SyntaxError with incorrect expectation; expected to fail"); + + test(function() + { + assert_throws_dom("SyntaxError", function () {JSON.parse("{")}); + }, "Test throw JS SyntaxError where SyntaxError DOMException expected; expected to fail") + + test(function() + { + var a = document.createTextNode("a") + var b = document.createElement("b") + assert_throws_dom("NOT_FOUND_ERR", function () {a.appendChild(b)}); + }, "Test throw DOM exception expected to fail") + + test(function() + { + var e = new DOMException("I am not known", "TEST_ERROR_NO_SUCH_THING"); + assert_throws_dom(0, function() {throw e}); + }, "Test assert_throws_dom with ambiguous DOM-exception expected to Fail"); + + test(function() + { + var e = {code:0, name:"TEST_ERR", TEST_ERR:0}; + e.constructor = DOMException; + assert_throws_dom("TEST_ERR", function() {throw e}); + }, "Test assert_throws_dom with non-DOM-exception expected to Fail"); + + test(function() + { + var e = {code: DOMException.SYNTAX_ERR, name:"SyntaxError"}; + e.constructor = DOMException; + assert_throws_dom(DOMException.SYNTAX_ERR, function() {throw e}); + }, "Test assert_throws_dom with number code value expected to Pass"); + + test(function() + { + var e = new DOMException("Some message", "SyntaxError"); + assert_throws_dom(DOMException.SYNTAX_ERR, function() {throw e}); + }, "Test assert_throws_dom with number code value and real DOMException expected to Pass"); + + var t = async_test("Test step_func") + setTimeout( + t.step_func( + function () { + assert_true(true); t.done(); + }), 0); + + async_test(function(t) { + setTimeout(t.step_func(function (){assert_true(true); t.done();}), 0); + }, "Test async test with callback"); + + async_test(function() { + setTimeout(this.step_func(function (){assert_true(true); this.done();}), 0); + }, "Test async test with callback and `this` obj."); + + async_test("test should timeout (fail) with the default of 2 seconds").step(function(){}); + + async_test("async test that is never started, should have status Not Run"); + + + test(function(t) { + window.global = 1; + t.add_cleanup(function() {delete window.global}); + assert_equals(window.global, 1); + }, + "Test that defines a global and cleans it up"); + + test(function() {assert_equals(window.global, undefined)}, + "Test that cleanup handlers from previous test ran"); + +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "TIMEOUT", + "message": null + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "Setup function ran", + "message": null, + "properties": {} + }, + { + "status_string": "FAIL", + "name": "Test assert_throws_dom with ambiguous DOM-exception expected to Fail", + "message": "Test bug: ambiguous DOMException code 0 passed to assert_throws_dom()", + "properties": {} + }, + { + "status_string": "FAIL", + "name": "Test assert_throws_dom with non-DOM-exception expected to Fail", + "message": "Test bug: unrecognized DOMException code name or name \"TEST_ERR\" passed to assert_throws_dom()", + "properties": {} + }, + { + "status_string": "PASS", + "name": "Test assert_throws_dom with number code value expected to Pass", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "Test assert_throws_dom with number code value and real DOMException expected to Pass", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "Test async test with callback", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "Test async test with callback and `this` obj.", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "Test step_func", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "Test that cleanup handlers from previous test ran", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "Test that defines a global and cleans it up", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "Test throw DOM exception", + "message": null, + "properties": {} + }, + { + "status_string": "FAIL", + "name": "Test throw DOMException as JS exception expected to fail", + "message": "assert_throws_js: function \"function DOMException() {\n [native code]\n}\" is not an Error subtype", + "properties": {} + }, + { + "status_string": "FAIL", + "name": "Test throw SyntaxError DOMException where JS SyntaxError expected; expected to fail", + "message": "assert_throws_js: function \"function () {document.querySelector(\"\")}\" threw object \"SyntaxError: Document.querySelector: '' is not a valid selector\" (\"SyntaxError\") expected instance of function \"function SyntaxError() {\n [native code]\n}\" (\"SyntaxError\")", + "properties": {} + }, + { + "status_string": "PASS", + "name": "Test throw JS SyntaxError", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "Test throw DOM SyntaxError", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "Test throw DOM SyntaxError from subframe", + "message": null, + "properties": {} + }, + { + "status_string": "FAIL", + "name": "Test throw DOM SyntaxError from subframe with incorrect global expectation; expected to fail", + "message": "assert_throws_dom: function \"function () {ifr.contentDocument.querySelector(\"\")}\" threw an exception from the wrong global", + "properties": {} + }, + { + "status_string": "FAIL", + "name": "Test throw DOM SyntaxError with incorrect expectation; expected to fail", + "message": "assert_throws_dom: function \"function () {document.querySelector(\"\")}\" threw an exception from the wrong global", + "properties": {} + }, + { + "status_string": "FAIL", + "name": "Test throw JS SyntaxError where SyntaxError DOMException expected; expected to fail", + "message": "assert_throws_dom: function \"function () {JSON.parse(\"{\")}\" threw object \"SyntaxError: JSON.parse: end of data while reading object contents at line 1 column 2 of the JSON data\" that is not a DOMException SyntaxError: property \"code\" is equal to undefined, expected 12", + "properties": {} + }, + { + "status_string": "FAIL", + "name": "Test throw DOM exception expected to fail", + "message": "assert_throws_dom: function \"function () {a.appendChild(b)}\" threw object \"HierarchyRequestError: Node.appendChild: Cannot add children to a Text\" that is not a DOMException NOT_FOUND_ERR: property \"code\" is equal to 3, expected 8", + "properties": {} + }, + { + "status_string": "FAIL", + "name": "assert_array_equals with first param 1", + "message": "assert_array_equals: 1 equals [1]? value is 1, expected array", + "properties": {} + }, + { + "status_string": "FAIL", + "name": "assert_array_equals with first param false", + "message": "assert_array_equals: false equals [1]? value is false, expected array", + "properties": {} + }, + { + "status_string": "FAIL", + "name": "assert_array_equals with first param null", + "message": "assert_array_equals: null equals [1]? value is null, expected array", + "properties": {} + }, + { + "status_string": "FAIL", + "name": "assert_array_equals with first param true", + "message": "assert_array_equals: true equals [1]? value is true, expected array", + "properties": {} + }, + { + "status_string": "FAIL", + "name": "assert_array_equals with first param undefined", + "message": "assert_array_equals: undefined equals [1]? value is undefined, expected array", + "properties": {} + }, + { + "status_string": "PASS", + "name": "assert_equals tests", + "message": null, + "properties": {} + }, + { + "status_string": "FAIL", + "name": "assert_equals tests expected to fail", + "message": "assert_equals: Zero case expected 0 but got -0", + "properties": {} + }, + { + "status_string": "FAIL", + "name": "assert_greater_than expected to fail", + "message": "assert_greater_than: 10 is not greater than 11 expected a number greater than 11 but got 10", + "properties": {} + }, + { + "status_string": "FAIL", + "name": "assert_less_than_equal expected to fail", + "message": "assert_greater_than_equal: '10' is not a number expected a number but got a \"string\"", + "properties": {} + }, + { + "status_string": "PASS", + "name": "assert_not_equals tests", + "message": null, + "properties": {} + }, + { + "status_string": "FAIL", + "name": "assert_true expected to fail", + "message": "assert_true: false should not be true expected true got false", + "properties": {} + }, + { + "status_string": "PASS", + "name": "assert_true expected to pass", + "message": null, + "properties": {} + }, + { + "status_string": "NOTRUN", + "name": "async test that is never started, should have status Not Run", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "basic assert_approx_equals test", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "basic assert_array_approx_equals test", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "basic assert_array_equals test", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "basic assert_greater_than_equal test", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "basic assert_less_than test", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "body element fires the onload event set from the attribute", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "document.body should be the first body element in the document", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "test for assert[_not]_own_property and insert_inherits", + "message": null, + "properties": {} + }, + { + "status_string": "TIMEOUT", + "name": "test should timeout (fail) with the default of 2 seconds", + "message": "Test timed out", + "properties": {} + }, + { + "status_string": "PASS", + "name": "window onload event fires when set from the handler", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "on_event does not use event capture", + "message": null, + "properties": {} + } + ], + "summarized_asserts": [ + { + "assert_name": "assert_true", + "test": "Setup function ran", + "args": [ + "true" + ], + "status": 0 + }, + { + "assert_name": "assert_array_equals", + "test": "on_event does not use event capture", + "args": [ + "[\"inner\", \"outer\"]", + "[\"inner\", \"outer\"]" + ], + "status": 0 + }, + { + "assert_name": "assert_equals", + "test": "document.body should be the first body element in the document", + "args": [ + "Element node <body onload=\"load_test_attr.done()\"> <h1>Sample HTML5 AP...", + "Element node <body onload=\"load_test_attr.done()\"> <h1>Sample HTML5 AP..." + ], + "status": 0 + }, + { + "assert_name": "assert_equals", + "test": "assert_equals tests", + "args": [ + "1", + "1" + ], + "status": 0 + }, + { + "assert_name": "assert_equals", + "test": "assert_equals tests", + "args": [ + "NaN", + "NaN", + "\"NaN case\"" + ], + "status": 0 + }, + { + "assert_name": "assert_equals", + "test": "assert_equals tests", + "args": [ + "0", + "0", + "\"Zero case\"" + ], + "status": 0 + }, + { + "assert_name": "assert_equals", + "test": "assert_equals tests expected to fail", + "args": [ + "-0", + "0", + "\"Zero case\"" + ], + "status": 1 + }, + { + "assert_name": "assert_not_equals", + "test": "assert_not_equals tests", + "args": [ + "object \"[object Object]\"", + "object \"[object Object]\"", + "\"object case\"" + ], + "status": 0 + }, + { + "assert_name": "assert_not_equals", + "test": "assert_not_equals tests", + "args": [ + "-0", + "0", + "\"Zero case\"" + ], + "status": 0 + }, + { + "assert_name": "assert_true", + "test": "assert_true expected to pass", + "args": [ + "true" + ], + "status": 0 + }, + { + "assert_name": "assert_true", + "test": "assert_true expected to fail", + "args": [ + "false", + "\"false should not be true\"" + ], + "status": 1 + }, + { + "assert_name": "assert_array_equals", + "test": "basic assert_array_equals test", + "args": [ + "[1, NaN]", + "[1, NaN]", + "\"[1, NaN] is equal to [1, NaN]\"" + ], + "status": 0 + }, + { + "assert_name": "assert_array_equals", + "test": "assert_array_equals with first param undefined", + "args": [ + "undefined", + "[1]", + "\"undefined equals [1]?\"" + ], + "status": 1 + }, + { + "assert_name": "assert_array_equals", + "test": "assert_array_equals with first param true", + "args": [ + "true", + "[1]", + "\"true equals [1]?\"" + ], + "status": 1 + }, + { + "assert_name": "assert_array_equals", + "test": "assert_array_equals with first param false", + "args": [ + "false", + "[1]", + "\"false equals [1]?\"" + ], + "status": 1 + }, + { + "assert_name": "assert_array_equals", + "test": "assert_array_equals with first param null", + "args": [ + "null", + "[1]", + "\"null equals [1]?\"" + ], + "status": 1 + }, + { + "assert_name": "assert_array_equals", + "test": "assert_array_equals with first param 1", + "args": [ + "1", + "[1]", + "\"1 equals [1]?\"" + ], + "status": 1 + }, + { + "assert_name": "assert_array_approx_equals", + "test": "basic assert_array_approx_equals test", + "args": [ + "[10, 11]", + "[11, 10]", + "1", + "\"[10, 11] is approximately (+/- 1) [11, 10]\"" + ], + "status": 0 + }, + { + "assert_name": "assert_approx_equals", + "test": "basic assert_approx_equals test", + "args": [ + "10", + "11", + "1", + "\"10 is approximately (+/- 1) 11\"" + ], + "status": 0 + }, + { + "assert_name": "assert_approx_equals", + "test": "basic assert_less_than test", + "args": [ + "10", + "11", + "1", + "\"10 is approximately (+/- 1) 11\"" + ], + "status": 0 + }, + { + "assert_name": "assert_greater_than", + "test": "assert_greater_than expected to fail", + "args": [ + "10", + "11", + "\"10 is not greater than 11\"" + ], + "status": 1 + }, + { + "assert_name": "assert_greater_than_equal", + "test": "basic assert_greater_than_equal test", + "args": [ + "10", + "10", + "\"10 is greater than or equal to 10\"" + ], + "status": 0 + }, + { + "assert_name": "assert_greater_than_equal", + "test": "assert_less_than_equal expected to fail", + "args": [ + "\"10\"", + "10", + "\"'10' is not a number\"" + ], + "status": 1 + }, + { + "assert_name": "assert_own_property", + "test": "test for assert[_not]_own_property and insert_inherits", + "args": [ + "object \"[object Object]\"", + "\"a\"" + ], + "status": 0 + }, + { + "assert_name": "assert_not_own_property", + "test": "test for assert[_not]_own_property and insert_inherits", + "args": [ + "object \"[object Object]\"", + "\"b\"", + "\"unexpected property found: \\\"b\\\"\"" + ], + "status": 0 + }, + { + "assert_name": "assert_inherits", + "test": "test for assert[_not]_own_property and insert_inherits", + "args": [ + "object \"[object Object]\"", + "\"b\"" + ], + "status": 0 + }, + { + "assert_name": "assert_throws_dom", + "test": "Test throw DOM exception", + "args": [ + "\"NOT_FOUND_ERR\"", + "function \"function () {a.removeChild(b)}\"" + ], + "status": 0 + }, + { + "assert_name": "assert_throws_js", + "test": "Test throw DOMException as JS exception expected to fail", + "args": [ + "function \"function DOMException() { [native code] }\"", + "function \"function () {a.removeChild(b)}\"" + ], + "status": 1 + }, + { + "assert_name": "assert_throws_js", + "test": "Test throw SyntaxError DOMException where JS SyntaxError expected; expected to fail", + "args": [ + "function \"function SyntaxError() { [native code] }\"", + "function \"function () {document.querySelector(\"\")}\"" + ], + "status": 1 + }, + { + "assert_name": "assert_throws_js", + "test": "Test throw JS SyntaxError", + "args": [ + "function \"function SyntaxError() { [native code] }\"", + "function \"function () {JSON.parse(\"{\")}\"" + ], + "status": 0 + }, + { + "assert_name": "assert_throws_dom", + "test": "Test throw DOM SyntaxError", + "args": [ + "\"SyntaxError\"", + "function \"function () {document.querySelector(\"\")}\"" + ], + "status": 0 + }, + { + "assert_name": "assert_throws_dom", + "test": "Test throw DOM SyntaxError from subframe", + "args": [ + "\"SyntaxError\"", + "function \"function DOMException() { [native code] }\"", + "function \"function () {ifr.contentDocument.querySelector(\"\")}\"" + ], + "status": 0 + }, + { + "assert_name": "assert_throws_dom", + "test": "Test throw DOM SyntaxError from subframe with incorrect global expectation; expected to fail", + "args": [ + "\"SyntaxError\"", + "function \"function () {ifr.contentDocument.querySelector(\"\")}\"" + ], + "status": 1 + }, + { + "assert_name": "assert_throws_dom", + "test": "Test throw DOM SyntaxError with incorrect expectation; expected to fail", + "args": [ + "\"SyntaxError\"", + "function \"function DOMException() { [native code] }\"", + "function \"function () {document.querySelector(\"\")}\"" + ], + "status": 1 + }, + { + "assert_name": "assert_throws_dom", + "test": "Test throw JS SyntaxError where SyntaxError DOMException expected; expected to fail", + "args": [ + "\"SyntaxError\"", + "function \"function () {JSON.parse(\"{\")}\"" + ], + "status": 1 + }, + { + "assert_name": "assert_throws_dom", + "test": "Test throw DOM exception expected to fail", + "args": [ + "\"NOT_FOUND_ERR\"", + "function \"function () {a.appendChild(b)}\"" + ], + "status": 1 + }, + { + "assert_name": "assert_throws_dom", + "test": "Test assert_throws_dom with ambiguous DOM-exception expected to Fail", + "args": [ + "0", + "function \"function() {throw e}\"" + ], + "status": 1 + }, + { + "assert_name": "assert_throws_dom", + "test": "Test assert_throws_dom with non-DOM-exception expected to Fail", + "args": [ + "\"TEST_ERR\"", + "function \"function() {throw e}\"" + ], + "status": 1 + }, + { + "assert_name": "assert_throws_dom", + "test": "Test assert_throws_dom with number code value expected to Pass", + "args": [ + "12", + "function \"function() {throw e}\"" + ], + "status": 0 + }, + { + "assert_name": "assert_throws_dom", + "test": "Test assert_throws_dom with number code value and real DOMException expected to Pass", + "args": [ + "12", + "function \"function() {throw e}\"" + ], + "status": 0 + }, + { + "assert_name": "assert_equals", + "test": "Test that defines a global and cleans it up", + "args": [ + "1", + "1" + ], + "status": 0 + }, + { + "assert_name": "assert_equals", + "test": "Test that cleanup handlers from previous test ran", + "args": [ + "undefined", + "undefined" + ], + "status": 0 + }, + { + "assert_name": "assert_true", + "test": "Test step_func", + "args": [ + "true" + ], + "status": 0 + }, + { + "assert_name": "assert_true", + "test": "Test async test with callback", + "args": [ + "true" + ], + "status": 0 + }, + { + "assert_name": "assert_true", + "test": "Test async test with callback and `this` obj.", + "args": [ + "true" + ], + "status": 0 + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/api-tests-2.html b/testing/web-platform/tests/resources/test/tests/functional/api-tests-2.html new file mode 100644 index 0000000000..9af94f61ac --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/api-tests-2.html @@ -0,0 +1,62 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Sample HTML5 API Tests</title> +</head> +<body> +<h1>Sample HTML5 API Tests</h1> +<p>There should be two results</p> +<div id="log"></div> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +setup({explicit_done:true}) +test(function() {assert_true(true)}, "Test defined before onload"); + +onload = function() {test(function (){assert_true(true)}, "Test defined after onload"); +done(); +} +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "Test defined after onload", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Test defined before onload", + "properties": {}, + "message": null + } + ], + "summarized_asserts": [ + { + "assert_name": "assert_true", + "test": "Test defined before onload", + "args": [ + "true" + ], + "status": 0 + }, + { + "assert_name": "assert_true", + "test": "Test defined after onload", + "args": [ + "true" + ], + "status": 0 + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/api-tests-3.html b/testing/web-platform/tests/resources/test/tests/functional/api-tests-3.html new file mode 100644 index 0000000000..991fc6da67 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/api-tests-3.html @@ -0,0 +1,34 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Sample HTML5 API Tests</title> +</head> +<script src="/resources/testharness.js"></script> + +<body> +<h1>Sample HTML5 API Tests</h1> +<div id="log"></div> +<script> +setup({explicit_timeout:true}); +var t = async_test("This test should give a status of 'Not Run' without a delay"); +timeout(); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "TIMEOUT", + "message": null + }, + "summarized_tests": [ + { + "status_string": "NOTRUN", + "name": "This test should give a status of 'Not Run' without a delay", + "properties": {}, + "message": null + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/assert-array-equals.html b/testing/web-platform/tests/resources/test/tests/functional/assert-array-equals.html new file mode 100644 index 0000000000..b6460a4868 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/assert-array-equals.html @@ -0,0 +1,162 @@ +<!DOCTYPE HTML> +<title>assert_array_equals</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<script> +test(() => { + assert_array_equals([], []); +}, "empty and equal"); +test(() => { + assert_array_equals([1], [1]); +}, "non-empty and equal"); +test(() => { + assert_array_equals([], [1]); +}, "length differs"); +test(() => { + assert_array_equals([1], [,]); +}, "property is present"); +test(() => { + assert_array_equals([,], [1]); +}, "property is missing"); +test(() => { + assert_array_equals([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20], ["x",1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]); +}, "property 0 differs"); +test(() => { + assert_array_equals([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20], [0,1,2,3,4,"x",6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]); +}, "property 5 differs"); +test(() => { + assert_array_equals([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21], [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21]); +}, "lengths differ and input array beyond display limit"); +test(() => { + assert_array_equals([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21], [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21]); +}, "lengths differ and expected array beyond display limit"); +test(() => { + assert_array_equals([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21], [,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21]); +}, "property 0 is present and arrays are beyond display limit"); +test(() => { + assert_array_equals([,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21], [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21]); +}, "property 0 is missing and arrays are beyond display limit"); +test(() => { + assert_array_equals([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21], [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,,19,20,21]); +}, "property 18 is present and arrays are beyond display limit"); +test(() => { + assert_array_equals([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,,19,20,21], [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21]); +}, "property 18 is missing and arrays are beyond display limit"); +test(() => { + assert_array_equals([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21], ["x",1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21]); +}, "property 0 differs and arrays are beyond display limit"); +test(() => { + assert_array_equals([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21], [0,1,2,3,4,"x",6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21]); +}, "property 5 differs and arrays are beyond display limit"); +test(() => { + assert_array_equals([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26], [0,1,2,3,4,5,6,7,8,9,10,11,"x",13,14,15,16,17,18,19,20,21,22,23,24,25,26]); +}, "property 5 differs and arrays are beyond display limit on both sides"); +</script> +<script type="text/json" id="expected"> +{ + "type": "complete", + "summarized_status": { + "message": null, + "status_string": "OK" + }, + "summarized_tests": [ + { + "name": "empty and equal", + "message": null, + "properties": {}, + "status_string": "PASS" + }, + { + "name": "length differs", + "message": "assert_array_equals: lengths differ, expected array [1] length 1, got [] length 0", + "properties": {}, + "status_string": "FAIL" + }, + { + "name": "lengths differ and expected array beyond display limit", + "message": "assert_array_equals: lengths differ, expected array [\u2026, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] length 22, got [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] length 21", + "properties": {}, + "status_string": "FAIL" + }, + { + "name": "lengths differ and input array beyond display limit", + "message": "assert_array_equals: lengths differ, expected array [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] length 21, got [\u2026, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] length 22", + "properties": {}, + "status_string": "FAIL" + }, + { + "name": "non-empty and equal", + "message": null, + "properties": {}, + "status_string": "PASS" + }, + { + "name": "property 0 differs", + "message": "assert_array_equals: expected property 0 to be \"x\" but got 0 (expected array [\"x\", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] got [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20])", + "properties": {}, + "status_string": "FAIL" + }, + { + "name": "property 0 differs and arrays are beyond display limit", + "message": "assert_array_equals: expected property 0 to be \"x\" but got 0 (expected array [\"x\", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, \u2026] got [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, \u2026])", + "properties": {}, + "status_string": "FAIL" + }, + { + "name": "property 0 is missing and arrays are beyond display limit", + "message": "assert_array_equals: expected property 0 to be \"present\" but was \"missing\" (expected array [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, \u2026] got [, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, \u2026])", + "properties": {}, + "status_string": "FAIL" + }, + { + "name": "property 0 is present and arrays are beyond display limit", + "message": "assert_array_equals: expected property 0 to be \"missing\" but was \"present\" (expected array [, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, \u2026] got [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, \u2026])", + "properties": {}, + "status_string": "FAIL" + }, + { + "name": "property 18 is missing and arrays are beyond display limit", + "message": "assert_array_equals: expected property 18 to be \"present\" but was \"missing\" (expected array [\u2026, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21] got [\u2026, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, , 19, 20, 21])", + "properties": {}, + "status_string": "FAIL" + }, + { + "name": "property 18 is present and arrays are beyond display limit", + "message": "assert_array_equals: expected property 18 to be \"missing\" but was \"present\" (expected array [\u2026, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, , 19, 20, 21] got [\u2026, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21])", + "properties": {}, + "status_string": "FAIL" + }, + { + "name": "property 5 differs", + "message": "assert_array_equals: expected property 5 to be \"x\" but got 5 (expected array [0, 1, 2, 3, 4, \"x\", 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] got [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20])", + "properties": {}, + "status_string": "FAIL" + }, + { + "name": "property 5 differs and arrays are beyond display limit", + "message": "assert_array_equals: expected property 5 to be \"x\" but got 5 (expected array [0, 1, 2, 3, 4, \"x\", 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, \u2026] got [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, \u2026])", + "properties": {}, + "status_string": "FAIL" + }, + { + "name": "property 5 differs and arrays are beyond display limit on both sides", + "message": "assert_array_equals: expected property 12 to be \"x\" but got 12 (expected array [\u2026, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, \"x\", 13, 14, 15, 16, 17, 18, 19, 20, 21, \u2026] got [\u2026, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, \u2026])", + "properties": {}, + "status_string": "FAIL" + }, + { + "name": "property is missing", + "message": "assert_array_equals: expected property 0 to be \"present\" but was \"missing\" (expected array [1] got [])", + "properties": {}, + "status_string": "FAIL" + }, + { + "name": "property is present", + "message": "assert_array_equals: expected property 0 to be \"missing\" but was \"present\" (expected array [] got [1])", + "properties": {}, + "status_string": "FAIL" + } + ] +} +</script> diff --git a/testing/web-platform/tests/resources/test/tests/functional/assert-throws-dom.html b/testing/web-platform/tests/resources/test/tests/functional/assert-throws-dom.html new file mode 100644 index 0000000000..4dd66b2372 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/assert-throws-dom.html @@ -0,0 +1,55 @@ +<!DOCTYPE HTML> +<title>assert_throws_dom</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<script> +test(() => { + function f() { + assert_true(false, "Trivial assertion."); + + // Would lead to throwing a SyntaxError. + document.createElement("div").contentEditable = "invalid"; + } + + assert_throws_dom("SyntaxError", () => { f(); }); +}, "Violated assertion nested in `assert_throws_dom`."); +</script> +<script type="text/json" id="expected"> +{ + "type": "complete", + "summarized_status": { + "message": null, + "status_string": "OK" + }, + "summarized_tests": [ + { + "message": "assert_true: Trivial assertion. expected true got false", + "name": "Violated assertion nested in `assert_throws_dom`.", + "properties": {}, + "status_string": "FAIL" + } + ], + "summarized_asserts": [ + { + "assert_name": "assert_throws_dom", + "test": "Violated assertion nested in `assert_throws_dom`.", + "args": [ + "\"SyntaxError\"", + "function \"() => { f(); }\"" + ], + "status": 1 + }, + { + "assert_name": "assert_true", + "test": "Violated assertion nested in `assert_throws_dom`.", + "args": [ + "false", + "\"Trivial assertion.\"" + ], + "status": 1 + } + ] +} +</script> + diff --git a/testing/web-platform/tests/resources/test/tests/functional/force_timeout.html b/testing/web-platform/tests/resources/test/tests/functional/force_timeout.html new file mode 100644 index 0000000000..2058fdb862 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/force_timeout.html @@ -0,0 +1,60 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Test#force_timeout</title> +</head> +<body> +<h1>Test#force_timeout</h1> +<div id="log"></div> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +setup({ explicit_timeout: true }); + +test(function(t) { + t.force_timeout(); + }, 'test (synchronous)'); + +async_test(function(t) { + t.step_timeout(function() { + t.force_timeout(); + }, 0); + }, 'async_test'); + +promise_test(function(t) { + t.force_timeout(); + + return new Promise(function() {}); + }, 'promise_test'); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "status_string": "TIMEOUT", + "name": "async_test", + "message": "Test timed out", + "properties": {} + }, + { + "status_string": "TIMEOUT", + "name": "promise_test", + "message": "Test timed out", + "properties": {} + }, + { + "status_string": "TIMEOUT", + "name": "test (synchronous)", + "message": "Test timed out", + "properties": {} + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/generate-callback.html b/testing/web-platform/tests/resources/test/tests/functional/generate-callback.html new file mode 100644 index 0000000000..11d41743b3 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/generate-callback.html @@ -0,0 +1,153 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Sample for using generate_tests to create a series of tests that share the same callback.</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> +<script> +// generate_tests takes an array of arrays that define tests +// but lets pass it an empty array and verify it does nothing. +function null_callback() { + throw "null_callback should not be called."; +} +generate_tests(null_callback, []); + +// Generate 3 tests specifying the name and one parameter +function validate_arguments(arg1) { + assert_equals(arg1, 1, "Ensure that we get our expected argument"); +} +generate_tests(validate_arguments, [ + ["first test", 1], + ["second test", 1], + ["third test", 1], +]); + +// Generate a test passing in a properties object that is shared across tests. +function validate_properties() { + assert_true(this.properties.sentinel, "Ensure that we got the right properties object."); +} +generate_tests(validate_properties, [["sentinel check 1"], ["sentinel check 2"]], {sentinel: true}); + +// Generate a test passing in a properties object that is shared across tests. +function validate_separate_properties() { + if (this.name === "sentinel check 1 unique properties") { + assert_true(this.properties.sentinel, "Ensure that we got the right properties object. Expect sentinel: true."); + } + else { + assert_false(this.properties.sentinel, "Ensure that we got the right properties object. Expect sentinel: false."); + } +} +generate_tests(validate_separate_properties, [["sentinel check 1 unique properties"], ["sentinel check 2 unique properties"]], [{sentinel: true}, {sentinel: false}]); + +// Finally generate a complicated set of tests from another data source +var letters = ["a", "b", "c", "d", "e", "f"]; +var numbers = [0, 1, 2, 3, 4, 5]; +function validate_related_arguments(arg1, arg2) { + assert_equals(arg1.charCodeAt(0) - "a".charCodeAt(0), arg2, "Ensure that we can map letters to numbers."); +} +function format_as_test(letter, index, letters) { + return ["Test to map " + letter + " to " + numbers[index], letter, numbers[index]]; +} +generate_tests(validate_related_arguments, letters.map(format_as_test)); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "Test to map a to 0", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Test to map b to 1", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Test to map c to 2", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Test to map d to 3", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Test to map e to 4", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Test to map f to 5", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "first test", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "second test", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "sentinel check 1", + "properties": { + "sentinel": true + }, + "message": null + }, + { + "status_string": "PASS", + "name": "sentinel check 1 unique properties", + "properties": { + "sentinel": true + }, + "message": null + }, + { + "status_string": "PASS", + "name": "sentinel check 2", + "properties": { + "sentinel": true + }, + "message": null + }, + { + "status_string": "PASS", + "name": "sentinel check 2 unique properties", + "properties": { + "sentinel": false + }, + "message": null + }, + { + "status_string": "PASS", + "name": "third test", + "properties": {}, + "message": null + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlDictionary/test_partial_interface_of.html b/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlDictionary/test_partial_interface_of.html new file mode 100644 index 0000000000..f635768c69 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlDictionary/test_partial_interface_of.html @@ -0,0 +1,89 @@ +<!DOCTYPE HTML> +<html> + +<head> + <meta charset="utf-8"> + <title>idlharness: Partial dictionary</title> + <script src="/resources/test/variants.js"></script> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/WebIDLParser.js"></script> + <script src="/resources/idlharness.js"></script> +</head> + +<body> + <p>Verify the series of sub-tests that are executed for "partial" dictionary objects.</p> + <script> + "use strict"; + + // No original existence + (() => { + const idlArray = new IdlArray(); + idlArray.add_idls('partial dictionary A {};'); + idlArray.test(); + })(); + + // Multiple partials existence + (() => { + const idlArray = new IdlArray(); + idlArray.add_untested_idls('partial dictionary B {};'); + idlArray.add_idls('partial dictionary B {};'); + idlArray.add_idls('partial dictionary B {};'); + idlArray.add_idls('partial dictionary B {};'); + idlArray.add_idls('dictionary B {};'); + idlArray.test(); + })(); + + // Original is a namespace, not a dictionary. + (() => { + const idlArray = new IdlArray(); + idlArray.add_idls(` + partial dictionary C {}; + namespace C {};`); + idlArray.merge_partials(); + })(); + </script> + <script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "name": "Partial dictionary A: original dictionary defined", + "status_string": "FAIL", + "properties": {}, + "message": "assert_true: Original dictionary should be defined expected true got false" + }, + { + "name": "Partial dictionary B[2]: original dictionary defined", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Partial dictionary B[3]: original dictionary defined", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Partial dictionary B[4]: original dictionary defined", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Partial dictionary C: original dictionary defined", + "status_string": "FAIL", + "properties": {}, + "message": "assert_true: Original C definition should have type dictionary expected true got false" + } + ], + "type": "complete" +} +</script> +</body> + +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlInterface/test_exposed_wildcard.html b/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlInterface/test_exposed_wildcard.html new file mode 100644 index 0000000000..addc0eb4fc --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlInterface/test_exposed_wildcard.html @@ -0,0 +1,233 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>idlharness: Exposed=*</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/WebIDLParser.js"></script> + <script src="/resources/idlharness.js"></script> +</head> +<body> +<script> +"use strict"; + +Object.defineProperty(window, "A", { + enumerable: false, + writable: true, + configurable: true, + value: function A() {} + }); +Object.defineProperty(window.A, "prototype", { + writable: false, + value: window.A.prototype + }); +A.prototype[Symbol.toStringTag] = "A"; + +Object.defineProperty(window, "C", { + enumerable: false, + writable: true, + configurable: true, + value: function C() {} + }); +Object.defineProperty(window.C, "prototype", { + writable: false, + value: window.C.prototype + }); +C.prototype[Symbol.toStringTag] = "C"; + +Object.defineProperty(window, "D", { + enumerable: false, + writable: true, + configurable: true, + value: function D() {} + }); +Object.defineProperty(window.D, "prototype", { + writable: false, + value: window.D.prototype + }); +C.prototype[Symbol.toStringTag] = "D"; +Object.defineProperty(window, "B", { + enumerable: false, + writable: true, + configurable: true, + value: window.A + }); + +var idlArray = new IdlArray(); +idlArray.add_idls(` +[Exposed=*, LegacyWindowAlias=B] interface A {}; +[Exposed=*] partial interface A {}; +[Exposed=Window] interface C {}; +[Exposed=*] partial interface C {}; +[Exposed=*] interface D {}; +[Exposed=Window] partial interface D {}; +`); +idlArray.add_objects({ + Window: ["window"] +}); +idlArray.test(); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "message": null, + "status_string": "OK" + }, + "summarized_tests": [ + { + "name": "A interface object length", + "properties": {}, + "message": null, + "status_string": "PASS" + }, + { + "name": "A interface object name", + "properties": {}, + "message": null, + "status_string": "PASS" + }, + { + "name": "A interface: existence and properties of interface object", + "properties": {}, + "message": "assert_throws_js: interface object didn't throw TypeError when called as a function function \"function() {\n interface_object();\n }\" did not throw", + "status_string": "FAIL" + }, + { + "name": "A interface: existence and properties of interface prototype object", + "properties": {}, + "message": null, + "status_string": "PASS" + }, + { + "name": "A interface: existence and properties of interface prototype object's \"constructor\" property", + "properties": {}, + "message": null, + "status_string": "PASS" + }, + { + "name": "A interface: existence and properties of interface prototype object's @@unscopables property", + "properties": {}, + "message": null, + "status_string": "PASS" + }, + { + "name": "A interface: legacy window alias", + "properties": {}, + "message": null, + "status_string": "PASS" + }, + { + "name": "C interface object length", + "properties": {}, + "message": null, + "status_string": "PASS" + }, + { + "name": "C interface object name", + "properties": {}, + "message": null, + "status_string": "PASS" + }, + { + "name": "C interface: existence and properties of interface object", + "properties": {}, + "message": "assert_throws_js: interface object didn't throw TypeError when called as a function function \"function() {\n interface_object();\n }\" did not throw", + "status_string": "FAIL" + }, + { + "name": "C interface: existence and properties of interface prototype object", + "properties": {}, + "message": null, + "status_string": "PASS" + }, + { + "name": "C interface: existence and properties of interface prototype object's \"constructor\" property", + "properties": {}, + "message": null, + "status_string": "PASS" + }, + { + "name": "C interface: existence and properties of interface prototype object's @@unscopables property", + "properties": {}, + "message": null, + "status_string": "PASS" + }, + { + "name": "D interface object length", + "properties": {}, + "message": null, + "status_string": "PASS" + }, + { + "name": "D interface object name", + "properties": {}, + "message": null, + "status_string": "PASS" + }, + { + "name": "D interface: existence and properties of interface object", + "properties": {}, + "message": "assert_throws_js: interface object didn't throw TypeError when called as a function function \"function() {\n interface_object();\n }\" did not throw", + "status_string": "FAIL" + }, + { + "name": "D interface: existence and properties of interface prototype object", + "properties": {}, + "message": null, + "status_string": "PASS" + }, + { + "name": "D interface: existence and properties of interface prototype object's \"constructor\" property", + "properties": {}, + "message": null, + "status_string": "PASS" + }, + { + "name": "D interface: existence and properties of interface prototype object's @@unscopables property", + "properties": {}, + "message": null, + "status_string": "PASS" + }, + { + "name": "Partial interface A: original interface defined", + "properties": {}, + "message": null, + "status_string": "PASS" + }, + { + "name": "Partial interface A: valid exposure set", + "properties": {}, + "message": null, + "status_string": "PASS" + }, + { + "name": "Partial interface C: original interface defined", + "properties": {}, + "message": null, + "status_string": "PASS" + }, + { + "name": "Partial interface C: valid exposure set", + "properties": {}, + "message": "Partial C interface is exposed everywhere, the original interface is not.", + "status_string": "FAIL" + }, + { + "name": "Partial interface D: original interface defined", + "properties": {}, + "message": null, + "status_string": "PASS" + }, + { + "name": "Partial interface D: valid exposure set", + "properties": {}, + "message": null, + "status_string": "PASS" + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlInterface/test_immutable_prototype.html b/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlInterface/test_immutable_prototype.html new file mode 100644 index 0000000000..5fe05915b0 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlInterface/test_immutable_prototype.html @@ -0,0 +1,298 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>idlharness: Immutable prototypes</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/WebIDLParser.js"></script> + <script src="/resources/idlharness.js"></script> +</head> +<body> +<script> +"use strict"; + +Object.defineProperty(window, "Foo", { + enumerable: false, + writable: true, + configurable: true, + value: function Foo() { + if (!new.target) { + throw new TypeError('Foo() must be called with new'); + } + } + }); +Object.defineProperty(window.Foo, "prototype", { + writable: false, + value: window.Foo.prototype + }); +Foo.prototype[Symbol.toStringTag] = "Foo"; + +var idlArray = new IdlArray(); +idlArray.add_untested_idls("interface EventTarget {};"); +idlArray.add_idls( + "[Global=Window, Exposed=Window]\n" + + "interface Window : EventTarget {};\n" + + + "[Global=Window, Exposed=Window]\n" + + "interface Foo { constructor(); };" + ); +idlArray.add_objects({ + Foo: ["new Foo()"], + Window: ["window"] +}); +idlArray.test(); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "message": null, + "status_string": "OK" + }, + "summarized_tests": [ + { + "name": "Foo interface object length", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Foo interface object name", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Foo interface: existence and properties of interface object", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Foo interface: existence and properties of interface prototype object", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Foo interface: existence and properties of interface prototype object's \"constructor\" property", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Foo interface: existence and properties of interface prototype object's @@unscopables property", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Foo interface: internal [[SetPrototypeOf]] method of global platform object - setting to a new value via Object.setPrototypeOf should throw a TypeError", + "status_string": "FAIL", + "properties": {}, + "message": "assert_throws_js: function \"function() {\n Object.setPrototypeOf(obj, newValue);\n }\" did not throw" + }, + { + "name": "Foo interface: internal [[SetPrototypeOf]] method of global platform object - setting to a new value via Reflect.setPrototypeOf should return false", + "status_string": "FAIL", + "properties": {}, + "message": "assert_false: expected false got true" + }, + { + "name": "Foo interface: internal [[SetPrototypeOf]] method of global platform object - setting to a new value via __proto__ should throw a TypeError", + "status_string": "FAIL", + "properties": {}, + "message": "assert_throws_js: function \"function() {\n obj.__proto__ = newValue;\n }\" did not throw" + }, + { + "name": "Foo interface: internal [[SetPrototypeOf]] method of global platform object - setting to its original value via Object.setPrototypeOf should not throw", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Foo interface: internal [[SetPrototypeOf]] method of global platform object - setting to its original value via Reflect.setPrototypeOf should return true", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Foo interface: internal [[SetPrototypeOf]] method of global platform object - setting to its original value via __proto__ should not throw", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Foo interface: internal [[SetPrototypeOf]] method of interface prototype object - setting to a new value via Object.setPrototypeOf should throw a TypeError", + "status_string": "FAIL", + "properties": {}, + "message": "assert_throws_js: function \"function() {\n Object.setPrototypeOf(obj, newValue);\n }\" did not throw" + }, + { + "name": "Foo interface: internal [[SetPrototypeOf]] method of interface prototype object - setting to a new value via Reflect.setPrototypeOf should return false", + "status_string": "FAIL", + "properties": {}, + "message": "assert_false: expected false got true" + }, + { + "name": "Foo interface: internal [[SetPrototypeOf]] method of interface prototype object - setting to a new value via __proto__ should throw a TypeError", + "status_string": "FAIL", + "properties": {}, + "message": "assert_throws_js: function \"function() {\n obj.__proto__ = newValue;\n }\" did not throw" + }, + { + "name": "Foo interface: internal [[SetPrototypeOf]] method of interface prototype object - setting to its original value via Object.setPrototypeOf should not throw", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Foo interface: internal [[SetPrototypeOf]] method of interface prototype object - setting to its original value via Reflect.setPrototypeOf should return true", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Foo interface: internal [[SetPrototypeOf]] method of interface prototype object - setting to its original value via __proto__ should not throw", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Foo must be primary interface of new Foo()", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Stringification of new Foo()", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Stringification of window", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Window interface object length", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Window interface object name", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Window interface: existence and properties of interface object", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Window interface: existence and properties of interface prototype object", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Window interface: existence and properties of interface prototype object's \"constructor\" property", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Window interface: existence and properties of interface prototype object's @@unscopables property", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Window interface: internal [[SetPrototypeOf]] method of global platform object - setting to a new value via Object.setPrototypeOf should throw a TypeError", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Window interface: internal [[SetPrototypeOf]] method of global platform object - setting to a new value via Reflect.setPrototypeOf should return false", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Window interface: internal [[SetPrototypeOf]] method of global platform object - setting to a new value via __proto__ should throw a TypeError", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Window interface: internal [[SetPrototypeOf]] method of global platform object - setting to its original value via Object.setPrototypeOf should not throw", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Window interface: internal [[SetPrototypeOf]] method of global platform object - setting to its original value via Reflect.setPrototypeOf should return true", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Window interface: internal [[SetPrototypeOf]] method of global platform object - setting to its original value via __proto__ should not throw", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Window interface: internal [[SetPrototypeOf]] method of interface prototype object - setting to a new value via Object.setPrototypeOf should throw a TypeError", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Window interface: internal [[SetPrototypeOf]] method of interface prototype object - setting to a new value via Reflect.setPrototypeOf should return false", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Window interface: internal [[SetPrototypeOf]] method of interface prototype object - setting to a new value via __proto__ should throw a TypeError", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Window interface: internal [[SetPrototypeOf]] method of interface prototype object - setting to its original value via Object.setPrototypeOf should not throw", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Window interface: internal [[SetPrototypeOf]] method of interface prototype object - setting to its original value via Reflect.setPrototypeOf should return true", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Window interface: internal [[SetPrototypeOf]] method of interface prototype object - setting to its original value via __proto__ should not throw", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Window must be primary interface of window", + "status_string": "PASS", + "properties": {}, + "message": null + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlInterface/test_interface_mixin.html b/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlInterface/test_interface_mixin.html new file mode 100644 index 0000000000..be2844e698 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlInterface/test_interface_mixin.html @@ -0,0 +1,131 @@ +<!DOCTYPE HTML> +<html> + +<head> + <meta charset="utf-8"> + <title>idlharness: interface mixins</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/WebIDLParser.js"></script> + <script src="/resources/idlharness.js"></script> +</head> + +<body> + <p>Verify the series of sub-tests that are executed for "interface mixin" objects.</p> + <script> + "use strict"; + + // Simple includes statement (valid) + (() => { + const idlArray = new IdlArray(); + idlArray.add_idls(` + [Exposed=Window] interface I1 {}; + interface mixin M1 { attribute any a1; }; + I1 includes M1;`); + idlArray.merge_partials(); + idlArray.merge_mixins(); + })(); + + // Partial interface mixin (valid) + (() => { + const idlArray = new IdlArray(); + idlArray.add_idls(` + [Exposed=Window] interface I2 {}; + interface mixin M2 {}; + partial interface mixin M2 { attribute any a2; }; + I2 includes M2;`); + idlArray.merge_partials(); + idlArray.merge_mixins(); + })(); + + // Partial interface mixin without original mixin + (() => { + const idlArray = new IdlArray(); + idlArray.add_idls('partial interface mixin M3 {};'); + idlArray.merge_partials(); + idlArray.merge_mixins(); + })(); + + // Name clash between mixin and partial mixin + (() => { + const idlArray = new IdlArray(); + idlArray.add_idls(` + interface mixin M4 { attribute any a4; }; + partial interface mixin M4 { attribute any a4; };`); + idlArray.merge_partials(); + idlArray.merge_mixins(); + })(); + + // Name clash between interface and mixin + (() => { + const idlArray = new IdlArray(); + idlArray.add_untested_idls(` + interface mixin M5 { attribute any a5; }; + interface I5 { attribute any a5; }; + I5 includes M5;`); + idlArray.merge_partials(); + idlArray.merge_mixins(); + })(); + </script> + <script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "name": "I1 includes M1: member names are unique", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "I2 includes M2: member names are unique", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "I5 includes M5: member names are unique", + "status_string": "FAIL", + "properties": {}, + "message": "assert_true: member a5 is unique expected true got false" + }, + { + "name": "Partial interface mixin M2: member names are unique", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Partial interface mixin M2: original interface mixin defined", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Partial interface mixin M3: original interface mixin defined", + "status_string": "FAIL", + "properties": {}, + "message": "assert_true: Original interface mixin should be defined expected true got false" + }, + { + "name": "Partial interface mixin M4: member names are unique", + "status_string": "FAIL", + "properties": {}, + "message": "assert_true: member a4 is unique expected true got false" + }, + { + "name": "Partial interface mixin M4: original interface mixin defined", + "status_string": "PASS", + "properties": {}, + "message": null + } + ], + "type": "complete" +} +</script> +</body> + +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlInterface/test_partial_interface_of.html b/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlInterface/test_partial_interface_of.html new file mode 100644 index 0000000000..7dd9e676af --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlInterface/test_partial_interface_of.html @@ -0,0 +1,187 @@ +<!DOCTYPE HTML> +<html> + +<head> + <meta charset="utf-8"> + <title>idlharness: Partial interface</title> + <script src="/resources/test/variants.js"></script> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/WebIDLParser.js"></script> + <script src="/resources/idlharness.js"></script> +</head> + +<body> + <p>Verify the series of sub-tests that are executed for "partial" interface objects.</p> + <script> + "use strict"; + + // No original existence + (() => { + const idlArray = new IdlArray(); + idlArray.add_idls('partial interface A {};'); + idlArray.test(); + })(); + + // Valid exposure (Note: Worker -> {Shared,Dedicated,Service}Worker) + (() => { + const idlArray = new IdlArray(); + idlArray.add_untested_idls(` + [Exposed=(Worker)] + interface B {}; + + [Exposed=(DedicatedWorker, ServiceWorker, SharedWorker)] + interface C {};`); + idlArray.add_idls(` + [Exposed=(DedicatedWorker, ServiceWorker, SharedWorker)] + partial interface B {}; + + [Exposed=(Worker)] + partial interface C {};`); + idlArray.merge_partials(); + })(); + + // Invalid exposure + (() => { + const idlArray = new IdlArray(); + idlArray.add_untested_idls(` + [Exposed=(Window, ServiceWorker)] + interface D {};`); + idlArray.add_idls(` + [Exposed=(DedicatedWorker)] + partial interface D {};`); + idlArray.merge_partials(); + })(); + + // Original is a namespace, not an interface. + (() => { + const idlArray = new IdlArray(); + idlArray.add_idls(` + partial interface E {}; + namespace E {};`); + idlArray.merge_partials(); + })(); + + (() => { + const idlArray = new IdlArray(); + idlArray.add_idls(` + partial interface F {}; + partial interface mixin G {}; + `); + idlArray.add_dependency_idls(` + interface F {}; + interface mixin G {}; + interface mixin H {}; + F includes H; + I includes H; + J includes G; + interface K : J {}; + interface L : F {}; + `); + test(() => { + // Convert idlArray.includes into a Map from name of target interface to + // name of included mixin. (This assumes each interface includes at most + // one mixin, otherwise later includes would clobber earlier ones.) + const includes = new Map(idlArray.includes.map(i => [i.target, i.includes])); + // F is tested, so H is a dep. + assert_equals(includes.get('F'), 'H', 'F should be picked up'); + // H is not tested, so I is not a dep. + assert_false(includes.has('I'), 'I should be ignored'); + // G is a dep, so J is a dep. + assert_equals(includes.get('J'), 'G', 'J should be picked up'); + // K isn't a dep because J isn't defined. + assert_false('K' in idlArray.members, 'K should be ignored'); + // L isn't a dep because F is untested. + assert_false('L' in idlArray.members, 'L should be ignored'); + }, 'partial mixin dep implications'); + })(); + + // Name clash (partials) + (() => { + const idlArray = new IdlArray(); + idlArray.add_idls(` + interface M { attribute any A; }; + partial interface M { attribute any A; };`); + idlArray.merge_partials(); + })(); + </script> + <script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "name": "Partial interface A: original interface defined", + "status_string": "FAIL", + "properties": {}, + "message": "assert_true: Original interface should be defined expected true got false" + }, + { + "name": "Partial interface B: original interface defined", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Partial interface B: valid exposure set", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Partial interface C: original interface defined", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Partial interface C: valid exposure set", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Partial interface D: original interface defined", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Partial interface D: valid exposure set", + "status_string": "FAIL", + "properties": {}, + "message": "Partial D interface is exposed to 'DedicatedWorker', the original interface is not." + }, + { + "name": "Partial interface E: original interface defined", + "status_string": "FAIL", + "properties": {}, + "message": "assert_true: Original E definition should have type interface expected true got false" + }, + { + "name": "partial mixin dep implications", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Partial interface M: original interface defined", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Partial interface M: member names are unique", + "status_string": "FAIL", + "properties": {}, + "message": "assert_true: member A is unique expected true got false" + } + ], + "type": "complete" +} +</script> +</body> + +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlInterface/test_primary_interface_of.html b/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlInterface/test_primary_interface_of.html new file mode 100644 index 0000000000..309de60bb7 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlInterface/test_primary_interface_of.html @@ -0,0 +1,116 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>idlharness: Primary interface</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/WebIDLParser.js"></script> + <script src="/resources/idlharness.js"></script> +</head> +<body> +<p>Verify the series of sub-tests that are executed for "tested" interface +objects but skipped for "untested" interface objects.</p> +<script> +"use strict"; + +function FooParent() { + if (!new.target) { + throw new TypeError('FooParent() must be called with new'); + } +} +Object.defineProperty(window, "Foo", { + enumerable: false, + writable: true, + configurable: true, + value: function Foo() { + if (!new.target) { + throw new TypeError('Foo() must be called with new'); + } + } + }); +Object.defineProperty(window.Foo, "prototype", { + writable: false, + value: new FooParent() + }); +Object.defineProperty(window.Foo.prototype, "constructor", { + enumerable: false, + writable: true, + configurable: true, + value: window.Foo + }); +Object.setPrototypeOf(Foo, FooParent); +Foo.prototype[Symbol.toStringTag] = "Foo"; + +var idlArray = new IdlArray(); +idlArray.add_untested_idls("interface FooParent {};"); +idlArray.add_idls( + "interface Foo : FooParent { constructor(); };" + ); +idlArray.add_objects({ + Foo: ["new Foo()"], + FooParent: ["new FooParent()"] +}); +idlArray.test(); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "name": "Foo interface object length", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Foo interface object name", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Foo interface: existence and properties of interface object", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Foo interface: existence and properties of interface prototype object", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Foo interface: existence and properties of interface prototype object's \"constructor\" property", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Foo interface: existence and properties of interface prototype object's @@unscopables property", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Foo must be primary interface of new Foo()", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Stringification of new Foo()", + "status_string": "PASS", + "properties": {}, + "message": null + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlInterface/test_to_json_operation.html b/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlInterface/test_to_json_operation.html new file mode 100644 index 0000000000..bbc502a313 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlInterface/test_to_json_operation.html @@ -0,0 +1,177 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>IdlInterface.prototype.test_to_json_operation()</title> + <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 src="../../../../idl-helper.js"></script> +</head> +<body> +<script> + "use strict"; + function wrap(member, obj) { + function F(obj) { + this._obj = obj; + } + + F.prototype.toJSON = function() { + return this._obj; + } + Object.defineProperty(F, 'name', { value: member.name }); + return new F(obj); + } + + var i, obj; + i = interfaceFrom("interface A { [Default] object toJSON(); attribute long foo; };"); + i.test_to_json_operation("object", wrap(i, { foo: 123 }), i.members[0]); + + // should fail (wrong type) + i = interfaceFrom("interface B { [Default] object toJSON(); attribute long foo; };"); + i.test_to_json_operation("object", wrap(i, { foo: "a value" }), i.members[0]); + + // should handle extraneous attributes (e.g. from an extension specification) + i = interfaceFrom("interface C { [Default] object toJSON(); attribute long foo; };"); + i.test_to_json_operation("object", wrap(i, { foo: 123, bar: 456 }), i.members[0]); + + // should fail (missing property) + i = interfaceFrom("interface D { [Default] object toJSON(); attribute long foo; };"); + i.test_to_json_operation("object", wrap(i, { }), i.members[0]); + + // should fail (should be writable) + obj = Object.defineProperties({}, { foo: { + writable: false, + enumerable: true, + configurable: true, + value: 123 + }}); + i = interfaceFrom("interface F { [Default] object toJSON(); attribute long foo; };"); + i.test_to_json_operation("object", wrap(i, obj), i.members[0]); + + // should fail (should be enumerable) + obj = Object.defineProperties({}, { foo: { + writable: true, + enumerable: false, + configurable: true, + value: 123 + }}); + i = interfaceFrom("interface G { [Default] object toJSON(); attribute long foo; };"); + i.test_to_json_operation("object", wrap(i, obj), i.members[0]); + + // should fail (should be configurable) + obj = Object.defineProperties({}, { foo: { + writable: true, + enumerable: true, + configurable: false, + value: 123 + }}); + i = interfaceFrom("interface H { [Default] object toJSON(); attribute long foo; };"); + i.test_to_json_operation("object", wrap(i, obj), i.members[0]); + + var idl = new IdlArray(); + idl.add_idls("interface I : J { [Default] object toJSON(); attribute long foo; };"); + idl.add_idls("interface J { [Default] object toJSON(); attribute DOMString foo;};"); + var i = idl.members.I; + i.test_to_json_operation("object", wrap(i, { foo: 123 }), i.members[0]); + + i = interfaceFrom("interface K { [Default] object toJSON(); };"); + i.test_to_json_operation("object", wrap(i, {}), i.members[0]); + + i = interfaceFrom("interface L { DOMString toJSON(); };"); + i.test_to_json_operation("object", wrap(i, "a string"), i.members[0]); + + // should fail (wrong output type) + i = interfaceFrom("interface M { DOMString toJSON(); };"); + i.test_to_json_operation("object", wrap(i, {}), i.members[0]); + + // should fail (not an IDL type) + i = interfaceFrom("interface N { DOMException toJSON(); };"); + i.test_to_json_operation("object", wrap(i, {}), i.members[0]); +</script> +<script type="text/json" id="expected"> + { + "summarized_status": { + "message": null, + "status_string": "OK" + }, + "summarized_tests": [ + { + "message": null, + "name": "A interface: default toJSON operation on object", + "properties": {}, + "status_string": "PASS" + }, + { + "message": "assert_equals: expected \"number\" but got \"string\"", + "name": "B interface: default toJSON operation on object", + "properties": {}, + "status_string": "FAIL" + }, + { + "message": null, + "name": "C interface: default toJSON operation on object", + "properties": {}, + "status_string": "PASS" + }, + { + "message": "assert_true: property \"foo\" should be present in the output of D.prototype.toJSON() expected true got false", + "name": "D interface: default toJSON operation on object", + "properties": {}, + "status_string": "FAIL" + }, + { + "message": "assert_true: property foo should be writable expected true got false", + "name": "F interface: default toJSON operation on object", + "properties": {}, + "status_string": "FAIL" + }, + { + "message": "assert_true: property foo should be enumerable expected true got false", + "name": "G interface: default toJSON operation on object", + "properties": {}, + "status_string": "FAIL" + }, + { + "message": "assert_true: property foo should be configurable expected true got false", + "name": "H interface: default toJSON operation on object", + "properties": {}, + "status_string": "FAIL" + }, + { + "message": null, + "name": "I interface: default toJSON operation on object", + "properties": {}, + "status_string": "PASS" + }, + { + "message": null, + "name": "K interface: default toJSON operation on object", + "properties": {}, + "status_string": "PASS" + }, + { + "message": null, + "name": "L interface: toJSON operation on object", + "properties": {}, + "status_string": "PASS" + }, + { + "message": "assert_equals: expected \"string\" but got \"object\"", + "name": "M interface: toJSON operation on object", + "properties": {}, + "status_string": "FAIL" + }, + { + "message": "assert_true: {\"type\":\"return-type\",\"extAttrs\":[],\"generic\":\"\",\"nullable\":false,\"union\":false,\"idlType\":\"DOMException\"} is not an appropriate return value for the toJSON operation of N expected true got false", + "name": "N interface: toJSON operation on object", + "properties": {}, + "status_string": "FAIL" + } + ], + "type": "complete" + } +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlNamespace/test_attribute.html b/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlNamespace/test_attribute.html new file mode 100644 index 0000000000..2c94061fc1 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlNamespace/test_attribute.html @@ -0,0 +1,100 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>idlharness: namespace attribute</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/WebIDLParser.js"></script> + <script src="/resources/idlharness.js"></script> +</head> +<body> +<p>Verify the series of sub-tests that are executed for namespace attributes.</p> +<script> +"use strict"; + +Object.defineProperty(self, "foo", { + value: { + truth: true, + }, + writable: true, + enumerable: false, + configurable: true, +}); + +var idlArray = new IdlArray(); +idlArray.add_idls(` +[Exposed=Window] +namespace foo { + readonly attribute bool truth; + readonly attribute bool lies; +};`); +idlArray.test(); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "name": "foo namespace: extended attributes", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "foo namespace: property descriptor", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "foo namespace: [[Extensible]] is true", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "foo namespace: [[Prototype]] is Object.prototype", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "foo namespace: typeof is \"object\"", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "foo namespace: has no length property", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "foo namespace: has no name property", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "foo namespace: attribute truth", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "foo namespace: attribute lies", + "status_string": "FAIL", + "properties": {}, + "message": "assert_own_property: foo does not have property \"lies\" expected property \"lies\" missing" + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlNamespace/test_operation.html b/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlNamespace/test_operation.html new file mode 100644 index 0000000000..da70c8fa31 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlNamespace/test_operation.html @@ -0,0 +1,242 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>idlharness: namespace operation</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/WebIDLParser.js"></script> + <script src="/resources/idlharness.js"></script> +</head> +<body> +<p>Verify the series of sub-tests that are executed for namespace operations.</p> +<script> +"use strict"; + +Object.defineProperty(self, "foo", { + value: Object.defineProperty({}, "Truth", { + value: function() {}, + writable: true, + enumerable: true, + configurable: true, + }), + writable: true, + enumerable: false, + configurable: true, +}); + +Object.defineProperty(self, "bar", { + value: Object.defineProperty({}, "Truth", { + value: function() {}, + writable: false, + enumerable: true, + configurable: false, + }), + writable: true, + enumerable: false, + configurable: true, +}); + +Object.defineProperty(self, "baz", { + value: { + LongStory: function(hero, ...details) { + return `${hero} went and ${details.join(', then')}` + }, + ShortStory: function(...details) { + return `${details.join('. ')}`; + }, + }, + writable: true, + enumerable: false, + configurable: true, +}); + +var idlArray = new IdlArray(); +idlArray.add_idls(` +[Exposed=Window] +namespace foo { + undefined Truth(); + undefined Lies(); +}; +[Exposed=Window] +namespace bar { + [LegacyUnforgeable] + undefined Truth(); +}; +[Exposed=Window] +namespace baz { + DOMString LongStory(any hero, DOMString... details); + DOMString ShortStory(DOMString... details); +};`); +idlArray.test(); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "name": "foo namespace: extended attributes", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "foo namespace: property descriptor", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "foo namespace: [[Extensible]] is true", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "foo namespace: [[Prototype]] is Object.prototype", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "foo namespace: typeof is \"object\"", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "foo namespace: has no length property", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "foo namespace: has no name property", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "foo namespace: operation Truth()", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "foo namespace: operation Lies()", + "status_string": "FAIL", + "properties": {}, + "message": "assert_own_property: namespace object missing operation \"Lies\" expected property \"Lies\" missing" + }, + { + "name": "bar namespace: extended attributes", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "bar namespace: property descriptor", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "bar namespace: [[Extensible]] is true", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "bar namespace: [[Prototype]] is Object.prototype", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "bar namespace: typeof is \"object\"", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "bar namespace: has no length property", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "bar namespace: has no name property", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "bar namespace: operation Truth()", + "status_string": "PASS", + "properties": {}, + "message": null + }, + + { + "name": "baz namespace: extended attributes", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "baz namespace: property descriptor", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "baz namespace: [[Extensible]] is true", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "baz namespace: [[Prototype]] is Object.prototype", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "baz namespace: typeof is \"object\"", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "baz namespace: has no length property", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "baz namespace: has no name property", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "baz namespace: operation LongStory(any, DOMString...)", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "baz namespace: operation ShortStory(DOMString...)", + "status_string": "PASS", + "properties": {}, + "message": null + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlNamespace/test_partial_namespace.html b/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlNamespace/test_partial_namespace.html new file mode 100644 index 0000000000..eabdcd10a9 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/idlharness/IdlNamespace/test_partial_namespace.html @@ -0,0 +1,125 @@ +<!DOCTYPE HTML> +<html> + +<head> + <meta charset="utf-8"> + <title>idlharness: Partial namespace</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/WebIDLParser.js"></script> + <script src="/resources/idlharness.js"></script> +</head> + +<body> + <p>Verify the series of sub-tests that are executed for "partial" namespace objects.</p> + <script> + "use strict"; + + // No original existence + (() => { + const idlArray = new IdlArray(); + idlArray.add_idls('partial namespace A {};'); + idlArray.test(); + })(); + + // Valid exposure (Note: Worker -> {Shared,Dedicated,Service}Worker) + (() => { + const idlArray = new IdlArray(); + idlArray.add_untested_idls(` + [Exposed=(Worker)] + namespace B {}; + + [Exposed=(DedicatedWorker, ServiceWorker, SharedWorker)] + namespace C {};`); + idlArray.add_idls(` + [Exposed=(DedicatedWorker, ServiceWorker, SharedWorker)] + partial namespace B {}; + + [Exposed=(Worker)] + partial namespace C {};`); + idlArray.merge_partials(); + })(); + + // Invalid exposure + (() => { + const idlArray = new IdlArray(); + idlArray.add_untested_idls(` + [Exposed=(Window, ServiceWorker)] + namespace D {};`); + idlArray.add_idls(` + [Exposed=(DedicatedWorker)] + partial namespace D {};`); + idlArray.merge_partials(); + })(); + + // Original is an interface, not a namespace. + (() => { + const idlArray = new IdlArray(); + idlArray.add_idls(` + partial namespace E {}; + interface E {};`); + idlArray.merge_partials(); + })(); + </script> + <script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "name": "Partial namespace A: original namespace defined", + "status_string": "FAIL", + "properties": {}, + "message": "assert_true: Original namespace should be defined expected true got false" + }, + { + "name": "Partial namespace B: original namespace defined", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Partial namespace B: valid exposure set", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Partial namespace C: original namespace defined", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Partial namespace C: valid exposure set", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Partial namespace D: original namespace defined", + "status_string": "PASS", + "properties": {}, + "message": null + }, + { + "name": "Partial namespace D: valid exposure set", + "status_string": "FAIL", + "properties": {}, + "message": "Partial D namespace is exposed to 'DedicatedWorker', the original namespace is not." + }, + { + "name": "Partial namespace E: original namespace defined", + "status_string": "FAIL", + "properties": {}, + "message": "assert_true: Original E definition should have type namespace expected true got false" + } + ], + "type": "complete" +} +</script> +</body> + +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/iframe-callback.html b/testing/web-platform/tests/resources/test/tests/functional/iframe-callback.html new file mode 100644 index 0000000000..f49d0aa6b8 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/iframe-callback.html @@ -0,0 +1,116 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Example with iframe that notifies containing document via callbacks</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body onload="start_test_in_iframe()"> +<h1>Callbacks From Tests Running In An IFRAME</h1> +<p>A test is run inside an <tt>iframe</tt> with a same origin document. The +containing document should receive callbacks as the tests progress inside the +<tt>iframe</tt>. A single passing test is expected in the summary below. +<div id="log"></div> + +<script> +var callbacks = []; +var START = 1 +var TEST_STATE = 2 +var RESULT = 3 +var COMPLETION = 4 +var test_complete = false; + +setup({explicit_done: true}); + +// The following callbacks are called for tests in this document as well as the +// tests in the IFRAME. Currently, callbacks invoked from this document and any +// child document are indistinguishable from each other. + +function start_callback(properties) { + callbacks.push(START); +} + +function test_state_callback(test) { + callbacks.push(TEST_STATE); +} + +function result_callback(test) { + callbacks.push(RESULT); +} + +function completion_callback(tests, status) { + if (test_complete) { + return; + } + test_complete = true; + callbacks.push(COMPLETION); + verify_received_callbacks(); + done(); +} + +function verify_received_callbacks() { + var copy_of_callbacks = callbacks.slice(0); + + // Note that you can't run test assertions directly in a callback even if + // this is a file test. When the callback is invoked from a same-origin child + // page, the callstack reaches into the calling child document. Any + // exception thrown in a callback will be handled by the child rather than + // this document. + test( + function() { + // callbacks list should look like: + // START 1*(TEST_STATE) RESULT COMPLETION + assert_equals(copy_of_callbacks.shift(), START, + "The first received callback should be 'start_callback'."); + assert_equals(copy_of_callbacks.shift(), TEST_STATE, + "'test_state_callback' should be received before any " + + "result or completion callbacks."); + while(copy_of_callbacks.length > 0) { + var callback = copy_of_callbacks.shift(); + if (callback != TEST_STATE) { + copy_of_callbacks.unshift(callback); + break; + } + } + assert_equals(copy_of_callbacks.shift(), RESULT, + "'test_state_callback' should be followed by 'result_callback'."); + assert_equals(copy_of_callbacks.shift(), COMPLETION, + "Final 'result_callback' should be followed by 'completion_callback'."); + assert_equals(copy_of_callbacks.length, 0, + "'completion_callback' should be the last callback."); + }); +} + +function start_test_in_iframe() { + // This document is going to clear any received callbacks and maintain + // radio silence until the test in the iframe runs to completion. The + // completion_callback() will then complete the testing on this document. + callbacks.length = 0; + var iframe = document.createElement("iframe"); + // single-page-test-pass.html has a single test. + iframe.src = "single-page-test-pass.html"; + iframe.style.setProperty("display", "none"); + document.getElementById("target").appendChild(iframe); +} +</script> + +<div id="target"> +</div> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "Example with iframe that notifies containing document via callbacks", + "properties": {}, + "message": null + } + ], + "type": "complete" +} +</script> +</body> diff --git a/testing/web-platform/tests/resources/test/tests/functional/iframe-consolidate-errors.html b/testing/web-platform/tests/resources/test/tests/functional/iframe-consolidate-errors.html new file mode 100644 index 0000000000..ef9b8702ec --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/iframe-consolidate-errors.html @@ -0,0 +1,50 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Example with iframe that consolidates errors via fetch_tests_from_window</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +var parent_test = async_test("Test executing in parent context"); +</script> +</head> +<body onload="parent_test.done()"> +<h1>Fetching Tests From a Child Context</h1> +<p>This test demonstrates the use of <tt>fetch_tests_from_window</tt> to pull +tests from an <tt>iframe</tt> into the primary document.</p> +<p>The test suite is expected to fail due to an unhandled exception in the +child context.</p> +<div id="log"></div> + +<iframe id="childContext" src="uncaught-exception-handle.html" style="display:none"></iframe> +<!-- apisample4.html is a failing suite due to an unhandled Error. --> + +<script> + var childContext = document.getElementById("childContext"); + fetch_tests_from_window(childContext.contentWindow); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "ERROR", + "message": "Error in remote: Error: Example Error" + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "Test executing in parent context", + "properties": {}, + "message": null + }, + { + "status_string": "NOTRUN", + "name": "This should show a harness status of 'Error' and a test status of 'Not Run'", + "properties": {}, + "message": null + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/iframe-consolidate-tests.html b/testing/web-platform/tests/resources/test/tests/functional/iframe-consolidate-tests.html new file mode 100644 index 0000000000..246dddee11 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/iframe-consolidate-tests.html @@ -0,0 +1,85 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Example with iframe that consolidates tests via fetch_tests_from_window</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +var parent_test = async_test("Test executing in parent context"); +</script> +</head> +<body onload="parent_test.done()"> +<h1>Fetching Tests From a Child Context</h1> +<p>This test demonstrates the use of <tt>fetch_tests_from_window</tt> to pull +tests from an <tt>iframe</tt> into the primary document.</p> +<p>The test suite will not complete until tests in the child context have finished +executing</p> +<div id="log"></div> + +<iframe id="childContext" src="promise-async.html" style="display:none"></iframe> + +<script> + var childContext = document.getElementById("childContext"); + fetch_tests_from_window(childContext.contentWindow); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "Promise rejection", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Promise resolution", + "properties": {}, + "message": null + }, + { + "status_string": "FAIL", + "name": "Promises and test assertion failures (should fail)", + "properties": {}, + "message": "assert_true: This failure is expected expected true got false" + }, + { + "status_string": "PASS", + "name": "Promises are supported in your browser", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Promises resolution chaining", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Test executing in parent context", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Use of step_func with Promises", + "properties": {}, + "message": null + }, + { + "status_string": "FAIL", + "name": "Use of unreached_func with Promises (should fail)", + "properties": {}, + "message": "assert_unreached: This failure is expected Reached unreachable code" + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/iframe-msg.html b/testing/web-platform/tests/resources/test/tests/functional/iframe-msg.html new file mode 100644 index 0000000000..283a5d98cc --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/iframe-msg.html @@ -0,0 +1,84 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Example with iframe that notifies containing document via cross document messaging</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> +<h1>Notifications From Tests Running In An IFRAME</h1> +<p>A test is run inside an <tt>iframe</tt> with a same origin document. The +containing document should receive messages via <tt>postMessage</tt>/ +<tt>onmessage</tt> as the tests progress inside the <tt>iframe</tt>. A single +passing test is expected in the summary below. +</p> +<div id="log"></div> + +<script> +var t = async_test("Containing document receives messages"); +var start_received = false; +var result_received = false; +var completion_received = false; + +// These are the messages that are expected to be seen while running the tests +// in the IFRAME. +var expected_messages = [ + t.step_func( + function(message) { + assert_equals(message.data.type, "start"); + assert_own_property(message.data, "properties"); + }), + + t.step_func( + function(message) { + assert_equals(message.data.type, "test_state"); + assert_equals(message.data.test.status, message.data.test.NOTRUN); + }), + + t.step_func( + function(message) { + assert_equals(message.data.type, "result"); + assert_equals(message.data.test.status, message.data.test.PASS); + }), + + t.step_func( + function(message) { + assert_equals(message.data.type, "complete"); + assert_equals(message.data.tests.length, 1); + assert_equals(message.data.tests[0].status, + message.data.tests[0].PASS); + assert_equals(message.data.status.status, message.data.status.OK); + t.done(); + }), + + t.unreached_func("Too many messages received") +]; + +on_event(window, + "message", + function(message) { + var handler = expected_messages.shift(); + handler(message); + }); +</script> +<iframe src="single-page-test-pass.html" style="display:none"> + <!-- single-page-test-pass.html implements a file_is_test test. --> +</iframe> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "Containing document receives messages", + "properties": {}, + "message": null + } + ], + "type": "complete" +} +</script> +</body> diff --git a/testing/web-platform/tests/resources/test/tests/functional/log-insertion.html b/testing/web-platform/tests/resources/test/tests/functional/log-insertion.html new file mode 100644 index 0000000000..9a63c3dbde --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/log-insertion.html @@ -0,0 +1,46 @@ +<!DOCTYPE HTML> +<title>Log insertion</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +test(function(t) { + assert_equals(document.body, null); +}, "Log insertion before load"); +test(function(t) { + assert_equals(document.body, null); +}, "Log insertion before load (again)"); +async_test(function(t) { + window.onload = t.step_func_done(function() { + var body = document.body; + assert_not_equals(body, null); + + var log = document.getElementById("log"); + assert_equals(log.parentNode, body); + }); +}, "Log insertion after load"); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [{ + "status_string": "PASS", + "name": "Log insertion before load", + "message": null, + "properties": {} + }, { + "status_string": "PASS", + "name": "Log insertion before load (again)", + "message": null, + "properties": {} + }, { + "status_string": "PASS", + "name": "Log insertion after load", + "message": null, + "properties": {} + }], + "type": "complete" +} +</script> diff --git a/testing/web-platform/tests/resources/test/tests/functional/no-title.html b/testing/web-platform/tests/resources/test/tests/functional/no-title.html new file mode 100644 index 0000000000..a337e4e5f5 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/no-title.html @@ -0,0 +1,146 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Tests with no title</title> +</head> +<script src="/resources/testharness.js"></script> + +<body> +<h1>Tests with no title</h1> +<div id="log"></div> +<script> + test(function(){assert_true(true, '1')}); + test(()=>assert_true(true, '2')); + test(() => assert_true(true, '3')); + test(() => assert_true(true, '3')); // test duplicate behaviour + test(() => assert_true(true, '3')); // test duplicate behaviour + test(() => { + assert_true(true, '4'); + }); + test(() => { assert_true(true, '5') }); + test(() => { assert_true(true, '6') } ); + test(() => { assert_true(true, '7'); }); + test(() => {}); + test(() => { }); + test(() => {;;;;}); + test(() => { ; ; ; ; }); + test(()=>{}); + test(()=>{ }); + test(()=>{;;;;}); + test(()=>{ ; ; ; ; }); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "Tests with no title", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "assert_true(true, '2')", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "assert_true(true, '3')", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "assert_true(true, '3') 1", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "assert_true(true, '3') 2", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Tests with no title 1", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "assert_true(true, '5')", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "assert_true(true, '6')", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "assert_true(true, '7')", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Tests with no title 2", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Tests with no title 3", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Tests with no title 4", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Tests with no title 5", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Tests with no title 6", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Tests with no title 7", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Tests with no title 8", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Tests with no title 9", + "properties": {}, + "message": null + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/order.html b/testing/web-platform/tests/resources/test/tests/functional/order.html new file mode 100644 index 0000000000..686383861a --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/order.html @@ -0,0 +1,36 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Ordering</title> +<meta name="timeout" content="6000"> +</head> +<body> +<div id="log"></div> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +test(function() {}, 'second'); +test(function() {}, 'first'); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [{ + "status_string": "PASS", + "name": "first", + "message": null, + "properties": {} + }, { + "status_string": "PASS", + "name": "second", + "message": null, + "properties": {} + }], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/promise-async.html b/testing/web-platform/tests/resources/test/tests/functional/promise-async.html new file mode 100644 index 0000000000..fa82665cf0 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/promise-async.html @@ -0,0 +1,172 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Async Tests and Promises</title> +</head> +<body> +<h1>Async Tests and Promises</h1> +<p>This test assumes ECMAScript 6 Promise support. Some failures are expected.</p> +<div id="log"></div> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + +test(function() { + var p = new Promise(function(resolve, reject) {}); + assert_true('then' in p); + assert_equals(typeof Promise.resolve, 'function'); + assert_equals(typeof Promise.reject, 'function'); +}, "Promises are supported in your browser"); + +(function() { + var t = async_test("Promise resolution"); + t.step(function() { + Promise.resolve('x').then( + t.step_func(function(value) { + assert_equals(value, 'x'); + t.done(); + }), + t.unreached_func('Promise should not reject') + ); + }); +}()); + +(function() { + var t = async_test("Promise rejection"); + t.step(function() { + Promise.reject(Error('fail')).then( + t.unreached_func('Promise should reject'), + t.step_func(function(reason) { + assert_true(reason instanceof Error); + assert_equals(reason.message, 'fail'); + t.done(); + }) + ); + }); +}()); + +(function() { + var t = async_test("Promises resolution chaining"); + t.step(function() { + var resolutions = []; + Promise.resolve('a').then( + t.step_func(function(value) { + resolutions.push(value); + return 'b'; + }) + ).then( + t.step_func(function(value) { + resolutions.push(value); + return 'c'; + }) + ).then( + t.step_func(function(value) { + resolutions.push(value); + + assert_array_equals(resolutions, ['a', 'b', 'c']); + t.done(); + }) + ).catch( + t.unreached_func('promise should not have rejected') + ); + }); +}()); + +(function() { + var t = async_test("Use of step_func with Promises"); + t.step(function() { + var resolutions = []; + Promise.resolve('x').then( + t.step_func_done(), + t.unreached_func('Promise should not have rejected') + ); + }); +}()); + +(function() { + var t = async_test("Promises and test assertion failures (should fail)"); + t.step(function() { + var resolutions = []; + Promise.resolve('x').then( + t.step_func(function(value) { + assert_true(false, 'This failure is expected'); + }) + ).then( + t.unreached_func('Promise should not have resolved') + ).catch( + t.unreached_func('Promise should not have rejected') + ); + }); +}()); + +(function() { + var t = async_test("Use of unreached_func with Promises (should fail)"); + t.step(function() { + var resolutions = []; + var r; + var p = new Promise(function(resolve, reject) { + // Reject instead of resolve, to demonstrate failure. + reject(123); + }); + p.then( + function(value) { + assert_equals(value, 123, 'This should not actually happen'); + }, + t.unreached_func('This failure is expected') + ); + }); +}()); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "Promise rejection", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Promise resolution", + "properties": {}, + "message": null + }, + { + "status_string": "FAIL", + "name": "Promises and test assertion failures (should fail)", + "properties": {}, + "message": "assert_true: This failure is expected expected true got false" + }, + { + "status_string": "PASS", + "name": "Promises are supported in your browser", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Promises resolution chaining", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Use of step_func with Promises", + "properties": {}, + "message": null + }, + { + "status_string": "FAIL", + "name": "Use of unreached_func with Promises (should fail)", + "properties": {}, + "message": "assert_unreached: This failure is expected Reached unreachable code" + } + ], + "type": "complete" +} +</script> diff --git a/testing/web-platform/tests/resources/test/tests/functional/promise-with-sync.html b/testing/web-platform/tests/resources/test/tests/functional/promise-with-sync.html new file mode 100644 index 0000000000..e8e680a9c7 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/promise-with-sync.html @@ -0,0 +1,79 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Promise Tests and Synchronous Tests</title> +</head> +<body> +<h1>Promise Tests</h1> +<p>This test demonstrates the use of <tt>promise_test</tt> alongside synchronous tests.</p> +<div id="log"></div> +<script src="../../../testharness.js"></script> +<script src="../../../testharnessreport.js"></script> +<script> +"use strict"; +var sequence = []; + +test(function(t) { + assert_array_equals(sequence, []); + sequence.push(1); +}, "first synchronous test"); + +promise_test(function() { + assert_array_equals(sequence, [1, 2]); + + return Promise.resolve() + .then(function() { + assert_array_equals(sequence, [1, 2]); + sequence.push(3); + }); +}, "first promise_test");; + +test(function(t) { + assert_array_equals(sequence, [1]); + sequence.push(2); +}, "second synchronous test"); + +promise_test(function() { + assert_array_equals(sequence, [1, 2, 3]); + + return Promise.resolve() + .then(function() { + assert_array_equals(sequence, [1, 2, 3]); + }); +}, "second promise_test");; +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "message": null, + "status_string": "OK" + }, + "summarized_tests": [ + { + "message": null, + "properties": {}, + "name": "first promise_test", + "status_string": "PASS" + }, + { + "message": null, + "properties": {}, + "name": "first synchronous test", + "status_string": "PASS" + }, + { + "message": null, + "properties": {}, + "name": "second promise_test", + "status_string": "PASS" + }, + { + "message": null, + "properties": {}, + "name": "second synchronous test", + "status_string": "PASS" + } + ], + "type": "complete" +} +</script> diff --git a/testing/web-platform/tests/resources/test/tests/functional/promise.html b/testing/web-platform/tests/resources/test/tests/functional/promise.html new file mode 100644 index 0000000000..f35feb0e21 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/promise.html @@ -0,0 +1,219 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Promise Tests</title> +</head> +<body> +<h1>Promise Tests</h1> +<p>This test demonstrates the use of <tt>promise_test</tt>. Assumes ECMAScript 6 +Promise support. Some failures are expected.</p> +<div id="log"></div> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +test( + function() { + var p = new Promise(function(resolve, reject){}); + assert_true("then" in p); + assert_equals(typeof Promise.resolve, "function"); + assert_equals(typeof Promise.reject, "function"); + }, + "Promises are supported in your browser"); + +promise_test( + function() { + return Promise.resolve("x") + .then( + function(value) { + assert_equals(value, + "x", + "Fulfilled promise should pass result to " + + "fulfill reaction."); + }); + }, + "Promise fulfillment with result"); + +promise_test( + function(t) { + return Promise.reject(new Error("fail")) + .then(t.unreached_func("Promise should reject"), + function(reason) { + assert_true( + reason instanceof Error, + "Rejected promise should pass reason to fulfill reaction."); + assert_equals( + reason.message, + "fail", + "Rejected promise should pass reason to reject reaction."); + }); + }, + "Promise rejection with result"); + +promise_test( + function() { + var resolutions = []; + return Promise.resolve("a") + .then( + function(value) { + resolutions.push(value); + return "b"; + }) + .then( + function(value) { + resolutions.push(value); + return "c"; + }) + .then( + function(value) { + resolutions.push(value); + assert_array_equals(resolutions, ["a", "b", "c"]); + }); + }, + "Chain of promise resolutions"); + +promise_test( + function(t) { + var resolutions = []; + return Promise.resolve("x") + .then( + function(value) { + assert_true(false, "Expected failure."); + }) + .then(t.unreached_func("UNEXPECTED FAILURE: Promise should not have resolved.")); + }, + "Assertion failure in a fulfill reaction (should FAIL with an expected failure)"); + +promise_test( + function(t) { + return new Promise( + function(resolve, reject) { + reject(123); + }) + .then(t.unreached_func("UNEXPECTED FAILURE: Fulfill reaction reached after rejection."), + t.unreached_func("Expected failure.")); + }, + "unreached_func as reactor (should FAIL with an expected failure)"); + +promise_test( + function() { + return true; + }, + "promise_test with function that doesn't return a Promise (should FAIL)"); + +promise_test(function(){}, + "promise_test with function that doesn't return anything"); + +promise_test( + function() { return { then: 23 }; }, + "promise_test that returns a non-thenable (should FAIL)"); + +promise_test( + function() { + return Promise.reject("Expected rejection"); + }, + "promise_test with unhandled rejection (should FAIL)"); + +promise_test( + function() { + return Promise.resolve(10) + .then( + function(value) { + throw Error("Expected exception."); + }); + }, + "promise_test with unhandled exception in fulfill reaction (should FAIL)"); + +promise_test( + function(t) { + return Promise.reject(10) + .then( + t.unreached_func("UNEXPECTED FAILURE: Fulfill reaction reached after rejection."), + function(value) { + throw Error("Expected exception."); + }); + }, + "promise_test with unhandled exception in reject reaction (should FAIL)"); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "status_string": "FAIL", + "name": "Assertion failure in a fulfill reaction (should FAIL with an expected failure)", + "message": "assert_true: Expected failure. expected true got false", + "properties": {} + }, + { + "status_string": "PASS", + "name": "Chain of promise resolutions", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "Promise fulfillment with result", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "Promise rejection with result", + "message": null, + "properties": {} + }, + { + "status_string": "PASS", + "name": "Promises are supported in your browser", + "message": null, + "properties": {} + }, + { + "status_string": "FAIL", + "name": "promise_test with function that doesn't return a Promise (should FAIL)", + "message": "promise_test: test body must return a 'thenable' object (received an object with no `then` method)", + "properties": {} + }, + { + "status_string": "FAIL", + "name": "promise_test with function that doesn't return anything", + "message": "promise_test: test body must return a 'thenable' object (received undefined)", + "properties": {} + }, + { + "status_string": "FAIL", + "name": "promise_test that returns a non-thenable (should FAIL)", + "message": "promise_test: test body must return a 'thenable' object (received an object with no `then` method)", + "properties": {} + }, + { + "status_string": "FAIL", + "name": "promise_test with unhandled exception in fulfill reaction (should FAIL)", + "message": "promise_test: Unhandled rejection with value: object \"Error: Expected exception.\"", + "properties": {} + }, + { + "status_string": "FAIL", + "name": "promise_test with unhandled exception in reject reaction (should FAIL)", + "message": "promise_test: Unhandled rejection with value: object \"Error: Expected exception.\"", + "properties": {} + }, + { + "status_string": "FAIL", + "name": "promise_test with unhandled rejection (should FAIL)", + "message": "promise_test: Unhandled rejection with value: \"Expected rejection\"", + "properties": {} + }, + { + "status_string": "FAIL", + "name": "unreached_func as reactor (should FAIL with an expected failure)", + "message": "assert_unreached: Expected failure. Reached unreachable code", + "properties": {} + } + ], + "type": "complete" +} +</script> diff --git a/testing/web-platform/tests/resources/test/tests/functional/queue.html b/testing/web-platform/tests/resources/test/tests/functional/queue.html new file mode 100644 index 0000000000..0c721286ec --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/queue.html @@ -0,0 +1,130 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Test queuing synchronous tests</title> +<script src="../../../testharness.js"></script> +<script src="../../../testharnessreport.js"></script> +</head> +<body> +<div id="log"></div> + +<script> +"use strict"; +var inInitialTurn = true; + +test(function(t) { + assert_true( + inInitialTurn, "should execute in the initial turn of the event loop" + ); +}, "First synchronous test"); + +test(function(t) { + assert_true( + inInitialTurn, "should execute in the initial turn of the event loop" + ); +}, "Second synchronous test"); + +async_test(function(t) { + assert_true( + inInitialTurn, "should execute in the initial turn of the event loop" + ); + t.done(); +}, "First async_test (run in parallel)"); + +async_test(function(t) { + assert_true( + inInitialTurn, "should execute in the initial turn of the event loop" + ); + t.done(); +}, "Second async_test (run in parallel)"); + +test(function(t) { + assert_true( + inInitialTurn, "should execute in the initial turn of the event loop" + ); +}, "Third synchronous test"); + +promise_test(function(t) { + assert_false( + inInitialTurn, "should not execute in the initial turn of the event loop" + ); + + return Promise.resolve(); +}, "promise_test"); + +async_test(function(t) { + assert_true( + inInitialTurn, "should execute in the initial turn of the event loop" + ); + t.done(); +}, "Third async_test (run in parallel)"); + +test(function(t) { + assert_true( + inInitialTurn, "should execute in the initial turn of the event loop" + ); +}, "Fourth synchronous test"); + +inInitialTurn = false; +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "properties": {}, + "name": "First async_test (run in parallel)", + "status_string": "PASS", + "message": null + }, + { + "properties": {}, + "name": "First synchronous test", + "status_string": "PASS", + "message": null + }, + { + "properties": {}, + "name": "Fourth synchronous test", + "status_string": "PASS", + "message": null + }, + { + "properties": {}, + "name": "Second async_test (run in parallel)", + "status_string": "PASS", + "message": null + }, + { + "properties": {}, + "name": "Second synchronous test", + "status_string": "PASS", + "message": null + }, + { + "properties": {}, + "name": "Third async_test (run in parallel)", + "status_string": "PASS", + "message": null + }, + { + "properties": {}, + "name": "Third synchronous test", + "status_string": "PASS", + "message": null + }, + { + "properties": {}, + "name": "promise_test", + "status_string": "PASS", + "message": null + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/setup-function-worker.js b/testing/web-platform/tests/resources/test/tests/functional/setup-function-worker.js new file mode 100644 index 0000000000..82c1456aa6 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/setup-function-worker.js @@ -0,0 +1,14 @@ +importScripts("/resources/testharness.js"); + +// Regression test for https://github.com/web-platform-tests/wpt/issues/27299, +// where we broke the ability for a setup function in a worker to contain an +// assertion (even a passing one). +setup(function() { + assert_true(true, "True is true"); +}); + +// We must define at least one test for the harness, though it is not what we +// are testing here. +test(function() { + assert_false(false, "False is false"); +}, 'Worker test'); diff --git a/testing/web-platform/tests/resources/test/tests/functional/setup-worker-service.html b/testing/web-platform/tests/resources/test/tests/functional/setup-worker-service.html new file mode 100644 index 0000000000..9f24adac2d --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/setup-worker-service.html @@ -0,0 +1,86 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Setup function in a service worker</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> +<h1>Setup function in a service worker</h1> +<p>This test assumes that the browser supports <a href="http://www.w3.org/TR/service-workers/">ServiceWorkers</a>. +<div id="log"></div> + +<script> +test(function(t) { + assert_true("serviceWorker" in navigator, + "navigator.serviceWorker exists"); +}, "Browser supports ServiceWorker"); + +promise_test(function() { + // Since the service worker registration could be in an indeterminate + // state (due to, for example, a previous test run failing), we start by + // unregstering our service worker and then registering it again. + var scope = "service-worker-scope"; + var worker_url = "setup-function-worker.js"; + + return navigator.serviceWorker.register(worker_url, {scope: scope}) + .then(function(registration) { + return registration.unregister(); + }).then(function() { + return navigator.serviceWorker.register(worker_url, {scope: scope}); + }).then(function(registration) { + add_completion_callback(function() { + registration.unregister(); + }); + + return new Promise(function(resolve) { + registration.addEventListener("updatefound", function() { + resolve(registration.installing); + }); + }); + }).then(function(worker) { + fetch_tests_from_worker(worker); + }); +}, "Register ServiceWorker"); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "Browser supports ServiceWorker", + "properties": {}, + "message": null + }, + { + "message": null, + "name": "Register ServiceWorker", + "properties": {}, + "status_string": "PASS" + }, + { + "message": null, + "name": "Worker test", + "properties": {}, + "status_string": "PASS" + } + ], + "summarized_asserts": [ + { + "assert_name": "assert_true", + "test": "Browser supports ServiceWorker", + "args": [ + "true", + "\"navigator.serviceWorker exists\"" + ], + "status": 0 + } + ], + "type": "complete" +} +</script> +</body> diff --git a/testing/web-platform/tests/resources/test/tests/functional/single-page-test-fail.html b/testing/web-platform/tests/resources/test/tests/functional/single-page-test-fail.html new file mode 100644 index 0000000000..8bbd530c48 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/single-page-test-fail.html @@ -0,0 +1,28 @@ +<!DOCTYPE HTML> +<title>Example with file_is_test (should fail)</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +setup({ single_test: true }); +onload = function() { + assert_true(false); + done(); +} +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "status_string": "FAIL", + "name": "Example with file_is_test (should fail)", + "properties": {}, + "message": "uncaught exception: Error: assert_true: expected true got false" + } + ], + "type": "complete" +} +</script> diff --git a/testing/web-platform/tests/resources/test/tests/functional/single-page-test-no-assertions.html b/testing/web-platform/tests/resources/test/tests/functional/single-page-test-no-assertions.html new file mode 100644 index 0000000000..9b39d2a02c --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/single-page-test-no-assertions.html @@ -0,0 +1,25 @@ +<!DOCTYPE HTML> +<title>Example single page test with no asserts</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +setup({ single_test: true }); +done(); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "Example single page test with no asserts", + "properties": {}, + "message": null + } + ], + "type": "complete" +} +</script> diff --git a/testing/web-platform/tests/resources/test/tests/functional/single-page-test-no-body.html b/testing/web-platform/tests/resources/test/tests/functional/single-page-test-no-body.html new file mode 100644 index 0000000000..cb018f4dae --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/single-page-test-no-body.html @@ -0,0 +1,26 @@ +<!DOCTYPE HTML> +<title>Example single page test with no body</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +setup({ single_test: true }); +assert_true(true); +done(); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "Example single page test with no body", + "properties": {}, + "message": null + } + ], + "type": "complete" +} +</script> diff --git a/testing/web-platform/tests/resources/test/tests/functional/single-page-test-pass.html b/testing/web-platform/tests/resources/test/tests/functional/single-page-test-pass.html new file mode 100644 index 0000000000..e143e22f3c --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/single-page-test-pass.html @@ -0,0 +1,28 @@ +<!DOCTYPE HTML> +<title>Example with file_is_test</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +setup({ single_test: true }); +onload = function() { + assert_true(true); + done(); +} +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "Example with file_is_test", + "properties": {}, + "message": null + } + ], + "type": "complete" +} +</script> diff --git a/testing/web-platform/tests/resources/test/tests/functional/step_wait.html b/testing/web-platform/tests/resources/test/tests/functional/step_wait.html new file mode 100644 index 0000000000..8235d9d48a --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/step_wait.html @@ -0,0 +1,79 @@ +<!doctype html> +<title>Tests for step_wait</title> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<div id=log></div> +<script> +promise_test(async t => { + let x = 1; + Promise.resolve().then(() => ++x); + await t.step_wait(() => x === 1); + assert_equals(x, 2); +}, "Basic step_wait() test"); + +promise_test(async t => { + let cond = false; + let x = 0; + setTimeout(() => cond = true, 100); + await t.step_wait(() => { + ++x; + return cond; + }); + assert_equals(x, 2); +}, "step_wait() isn't invoked too often"); + +promise_test(async t => { + await t.step_wait(); // Throws +}, "step_wait() takes an argument"); + +promise_test(async t => { + function wait(ms) { + return new Promise(resolve => step_timeout(resolve, ms)); + } + let x = 1; + step_timeout(() => { + ++x; + }, 100); + + await t.step_wait(async () => { + await wait(1); + return x === 2; + }); + assert_equals(x, 2); +}, "async step_wait()"); +</script> +<script type="text/json" id="expected"> +{ + "summarized_tests": [ + { + "name": "Basic step_wait() test", + "message": null, + "properties": {}, + "status_string": "PASS" + }, + { + "name": "step_wait() isn't invoked too often", + "message": null, + "properties": {}, + "status_string": "PASS" + }, + { + "name": "step_wait() takes an argument", + "message": "cond is not a function", + "properties": {}, + "status_string": "FAIL" + }, + { + "name": "async step_wait()", + "message": null, + "properties": {}, + "status_string": "PASS" + } + ], + "type": "complete", + "summarized_status": { + "status_string": "OK", + "message": null + } +} +</script> diff --git a/testing/web-platform/tests/resources/test/tests/functional/step_wait_func.html b/testing/web-platform/tests/resources/test/tests/functional/step_wait_func.html new file mode 100644 index 0000000000..9fed18a3e2 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/step_wait_func.html @@ -0,0 +1,49 @@ +<!doctype html> +<title>Tests for step_wait_func and step_wait_func_done</title> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<div id=log></div> +<script> +async_test(t => { + let x = 0; + let step_x = 0; + setTimeout(() => ++x, 100); + t.step_wait_func(() => { + ++step_x; + return x === 1; + }, () => { + assert_equals(step_x, 2); + t.done(); + }); +}, "Basic step_wait_func() test"); + +async_test(t => { + let x = 0; + setTimeout(() => ++x, 100); + t.step_wait_func_done(() => true, () => assert_equals(x, 0)); +}, "Basic step_wait_func_done() test"); + +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "message": null, + "status_string": "OK" + }, + "summarized_tests": [ + { + "properties": {}, + "message": null, + "name": "Basic step_wait_func() test", + "status_string": "PASS" + }, + { + "properties": {}, + "message": null, + "name": "Basic step_wait_func_done() test", + "status_string": "PASS" + } + ], + "type": "complete" +} +</script> diff --git a/testing/web-platform/tests/resources/test/tests/functional/task-scheduling-promise-test.html b/testing/web-platform/tests/resources/test/tests/functional/task-scheduling-promise-test.html new file mode 100644 index 0000000000..9d8e5c11cc --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/task-scheduling-promise-test.html @@ -0,0 +1,241 @@ +<!doctype html> +<title>testharness.js - task scheduling</title> +<script src="../../../testharness.js"></script> +<script src="../../../testharnessreport.js"></script> +<script> +var sameTask = null; +var sameMicrotask = null; +var expectedError = new Error('This error is expected'); + +promise_test(function() { + return Promise.resolve() + .then(function() { + sameMirotask = true; + Promise.resolve().then(() => sameMicrotask = false); + }); +}, 'promise test without cleanup #1'); + +promise_test(function() { + assert_false(sameMicrotask); + + return Promise.resolve(); +}, 'sub-test with 0 cleanup functions executes in distinct microtask from a passing sub-test'); + +promise_test(function() { + return Promise.resolve() + .then(function() { + sameMirotask = true; + Promise.resolve().then(() => sameMicrotask = false); + throw expectedError; + }); +}, 'failing promise test without cleanup #1'); + +promise_test(function() { + assert_false(sameMicrotask); + + return Promise.resolve(); +}, 'sub-test with 0 cleanup functions executes in distinct microtask from a failing sub-test'); + +promise_test(function(t) { + t.add_cleanup(function() {}); + + return Promise.resolve() + .then(function() { + sameMirotask = true; + Promise.resolve().then(() => sameMicrotask = false); + }); +}, 'promise test with cleanup #1'); + +promise_test(function() { + assert_false(sameMicrotask); + + return Promise.resolve(); +}, 'sub-test with some cleanup functions executes in distinct microtask from a passing sub-test'); + +promise_test(function(t) { + t.add_cleanup(function() {}); + + return Promise.resolve() + .then(function() { + sameMirotask = true; + Promise.resolve().then(() => sameMicrotask = false); + throw expectedError; + }); +}, 'failing promise test with cleanup #1'); + +promise_test(function() { + assert_false(sameMicrotask); + + return Promise.resolve(); +}, 'sub-test with some cleanup functions executes in distinct microtask from a failing sub-test'); + +promise_test(function(t) { + return Promise.resolve() + .then(function() { + sameTask = true; + t.step_timeout(() => sameTask = false, 0); + }); +}, 'promise test without cleanup #2'); + +promise_test(function() { + assert_true(sameTask); + + return Promise.resolve(); +}, 'sub-test with 0 cleanup functions executes in the same task as a passing sub-test'); + +promise_test(function(t) { + return Promise.resolve() + .then(function() { + sameTask = true; + t.step_timeout(() => sameTask = false, 0); + throw expectedError; + }); +}, 'failing promise test without cleanup #2'); + +promise_test(function() { + assert_true(sameTask); + + return Promise.resolve(); +}, 'sub-test with 0 cleanup functions executes in the same task as a failing sub-test'); + +promise_test(function(t) { + t.add_cleanup(function() {}); + + return Promise.resolve() + .then(function() { + sameTask = true; + t.step_timeout(() => sameTask = false, 0); + }); +}, 'promise test with cleanup #2'); + +promise_test(function() { + assert_true(sameTask); + + return Promise.resolve(); +}, 'sub-test with some cleanup functions executes in the same task as a passing sub-test'); + +promise_test(function(t) { + t.add_cleanup(function() {}); + + return Promise.resolve() + .then(function() { + sameTask = true; + t.step_timeout(() => sameTask = false, 0); + throw expectedError; + }); +}, 'failing promise test with cleanup #2'); + +promise_test(function() { + assert_true(sameTask); + + return Promise.resolve(); +}, 'sub-test with some cleanup functions executes in the same task as a failing sub-test'); +</script> + +<script type="text/json" id="expected"> +{ + "summarized_status": { + "message": null, + "status_string": "OK" + }, + "summarized_tests": [ + { + "message": "promise_test: Unhandled rejection with value: object \"Error: This error is expected\"", + "name": "failing promise test with cleanup #1", + "properties": {}, + "status_string": "FAIL" + }, + { + "message": "promise_test: Unhandled rejection with value: object \"Error: This error is expected\"", + "name": "failing promise test with cleanup #2", + "properties": {}, + "status_string": "FAIL" + }, + { + "message": "promise_test: Unhandled rejection with value: object \"Error: This error is expected\"", + "name": "failing promise test without cleanup #1", + "properties": {}, + "status_string": "FAIL" + }, + { + "message": "promise_test: Unhandled rejection with value: object \"Error: This error is expected\"", + "name": "failing promise test without cleanup #2", + "properties": {}, + "status_string": "FAIL" + }, + { + "message": null, + "name": "promise test with cleanup #1", + "properties": {}, + "status_string": "PASS" + }, + { + "message": null, + "name": "promise test with cleanup #2", + "properties": {}, + "status_string": "PASS" + }, + { + "message": null, + "name": "promise test without cleanup #1", + "properties": {}, + "status_string": "PASS" + }, + { + "message": null, + "name": "promise test without cleanup #2", + "properties": {}, + "status_string": "PASS" + }, + { + "message": null, + "name": "sub-test with 0 cleanup functions executes in distinct microtask from a failing sub-test", + "properties": {}, + "status_string": "PASS" + }, + { + "message": null, + "name": "sub-test with 0 cleanup functions executes in distinct microtask from a passing sub-test", + "properties": {}, + "status_string": "PASS" + }, + { + "message": null, + "name": "sub-test with 0 cleanup functions executes in the same task as a failing sub-test", + "properties": {}, + "status_string": "PASS" + }, + { + "message": null, + "name": "sub-test with 0 cleanup functions executes in the same task as a passing sub-test", + "properties": {}, + "status_string": "PASS" + }, + { + "message": null, + "name": "sub-test with some cleanup functions executes in distinct microtask from a failing sub-test", + "properties": {}, + "status_string": "PASS" + }, + { + "message": null, + "name": "sub-test with some cleanup functions executes in distinct microtask from a passing sub-test", + "properties": {}, + "status_string": "PASS" + }, + { + "message": null, + "name": "sub-test with some cleanup functions executes in the same task as a failing sub-test", + "properties": {}, + "status_string": "PASS" + }, + { + "message": null, + "name": "sub-test with some cleanup functions executes in the same task as a passing sub-test", + "properties": {}, + "status_string": "PASS" + } + ], + "type": "complete" +} +</script> diff --git a/testing/web-platform/tests/resources/test/tests/functional/task-scheduling-test.html b/testing/web-platform/tests/resources/test/tests/functional/task-scheduling-test.html new file mode 100644 index 0000000000..035844448d --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/task-scheduling-test.html @@ -0,0 +1,141 @@ +<!doctype html> +<title>testharness.js - task scheduling</title> +<script src="../../../testharness.js"></script> +<script src="../../../testharnessreport.js"></script> +<script> +var sameMicrotask = null; +var expectedError = new Error('This error is expected'); + +// Derived from `immediate` +// https://github.com/calvinmetcalf/immediate/blob/c353bd2106648cee1d525bfda22cfc4456e69c0e/lib/mutation.js +function microTask(callback) { + var observer = new MutationObserver(callback); + var element = document.createTextNode(''); + observer.observe(element, { + characterData: true + }); + + element.data = true; +}; + +async_test(function(t) { + var microtask_ran = false; + + t.step_timeout(t.step_func(function() { + assert_true(microtask_ran, 'function registered as a microtask was executed before task'); + t.done(); + }), 0); + + microTask(function() { + microtask_ran = true; + }); +}, 'precondition: microtask creation logic functions as expected'); + +test(function() { + sameMicrotask = true; + microTask(function() { sameMicrotask = false; }); +}, 'synchronous test without cleanup'); + +test(function() { + assert_true(sameMicrotask); +}, 'sub-test with 0 cleanup functions executes in the same microtask as a passing sub-test'); + +test(function() { + sameMicrotask = true; + microTask(function() { sameMicrotask = false; }); + throw expectedError; +}, 'failing synchronous test without cleanup'); + +test(function() { + assert_true(sameMicrotask); +}, 'sub-test with 0 cleanup functions executes in the same microtask as a failing sub-test'); + +test(function(t) { + t.add_cleanup(function() {}); + + sameMicrotask = true; + microTask(function() { sameMicrotask = false; }); +}, 'synchronous test with cleanup'); + +test(function() { + assert_true(sameMicrotask); +}, 'sub-test with some cleanup functions executes in the same microtask as a passing sub-test'); + +test(function(t) { + t.add_cleanup(function() {}); + + sameMicrotask = true; + microTask(function() { sameMicrotask = false; }); + throw expectedError; +}, 'failing synchronous test with cleanup'); + +test(function() { + assert_true(sameMicrotask); +}, 'sub-test with some cleanup functions executes in the same microtask as a failing sub-test'); +</script> + +<script type="text/json" id="expected"> +{ + "summarized_status": { + "message": null, + "status_string": "OK" + }, + "summarized_tests": [ + { + "message": "This error is expected", + "name": "failing synchronous test with cleanup", + "properties": {}, + "status_string": "FAIL" + }, + { + "message": "This error is expected", + "name": "failing synchronous test without cleanup", + "properties": {}, + "status_string": "FAIL" + }, + { + "message": null, + "name": "precondition: microtask creation logic functions as expected", + "properties": {}, + "status_string": "PASS" + }, + { + "message": null, + "name": "sub-test with 0 cleanup functions executes in the same microtask as a failing sub-test", + "properties": {}, + "status_string": "PASS" + }, + { + "message": null, + "name": "sub-test with 0 cleanup functions executes in the same microtask as a passing sub-test", + "properties": {}, + "status_string": "PASS" + }, + { + "message": null, + "name": "sub-test with some cleanup functions executes in the same microtask as a failing sub-test", + "properties": {}, + "status_string": "PASS" + }, + { + "message": null, + "name": "sub-test with some cleanup functions executes in the same microtask as a passing sub-test", + "properties": {}, + "status_string": "PASS" + }, + { + "message": null, + "name": "synchronous test with cleanup", + "properties": {}, + "status_string": "PASS" + }, + { + "message": null, + "name": "synchronous test without cleanup", + "properties": {}, + "status_string": "PASS" + } + ], + "type": "complete" +} +</script> diff --git a/testing/web-platform/tests/resources/test/tests/functional/uncaught-exception-handle.html b/testing/web-platform/tests/resources/test/tests/functional/uncaught-exception-handle.html new file mode 100644 index 0000000000..764b0c4055 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/uncaught-exception-handle.html @@ -0,0 +1,33 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Harness Handling Uncaught Exception</title> +</head> +<script src="/resources/testharness.js"></script> + +<body> +<h1>Harness Handling Uncaught Exception</h1> +<div id="log"></div> +<script> +var t = async_test("This should show a harness status of 'Error' and a test status of 'Not Run'"); +throw new Error("Example Error"); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "ERROR", + "message": "Error: Example Error" + }, + "summarized_tests": [ + { + "status_string": "NOTRUN", + "name": "This should show a harness status of 'Error' and a test status of 'Not Run'", + "properties": {}, + "message": null + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/uncaught-exception-ignore.html b/testing/web-platform/tests/resources/test/tests/functional/uncaught-exception-ignore.html new file mode 100644 index 0000000000..6bd0ddbb0d --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/uncaught-exception-ignore.html @@ -0,0 +1,35 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Harness Ignoring Uncaught Exception</title> +</head> +<script src="/resources/testharness.js"></script> + +<body> +<h1>Harness Ignoring Uncaught Exception</h1> +<div id="log"></div> +<script> +setup({allow_uncaught_exception:true}); +var t = async_test("setup({allow_uncaught_exception:true}) should allow tests to pass even if there is an exception"); +onerror = t.step_func(function() {t.done()}); +throw new Error("Example Error"); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "setup({allow_uncaught_exception:true}) should allow tests to pass even if there is an exception", + "properties": {}, + "message": null + } + ], + "type": "complete" +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/functional/worker-dedicated-uncaught-allow.html b/testing/web-platform/tests/resources/test/tests/functional/worker-dedicated-uncaught-allow.html new file mode 100644 index 0000000000..ba28d4914f --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/worker-dedicated-uncaught-allow.html @@ -0,0 +1,45 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Dedicated Worker Tests - Allowed Uncaught Exception</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> +<h1>Dedicated Web Worker Tests - Allowed Uncaught Exception</h1> +<p>Demonstrates running <tt>testharness</tt> based tests inside a dedicated web worker. +<p>The test harness is expected to pass despite an uncaught exception in a worker because that worker is configured to allow uncaught exceptions.</p> +<div id="log"></div> + +<script> +test(function(t) { + assert_true("Worker" in self, "Browser should support Workers"); + }, + "Browser supports Workers"); + +fetch_tests_from_worker(new Worker("worker-uncaught-allow.js")); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "Browser supports Workers", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "onerror event is triggered", + "properties": {}, + "message": null + } + ], + "type": "complete" +} +</script> +</body> diff --git a/testing/web-platform/tests/resources/test/tests/functional/worker-dedicated-uncaught-single.html b/testing/web-platform/tests/resources/test/tests/functional/worker-dedicated-uncaught-single.html new file mode 100644 index 0000000000..486e067114 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/worker-dedicated-uncaught-single.html @@ -0,0 +1,56 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Dedicated Worker Tests - Uncaught Exception in Single-Page Test</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> +<h1>Dedicated Web Worker Tests - Uncaught Exception in Single-Page Test</h1> +<p>Demonstrates running <tt>testharness</tt> based tests inside a dedicated web worker. +<p>The test harness is expected to pass despite an uncaught exception in a worker because that worker is a single-page test.</p> +<div id="log"></div> + +<script> +test(function(t) { + assert_true("Worker" in self, "Browser should support Workers"); + }, + "Browser supports Workers"); + +fetch_tests_from_worker(new Worker("worker-uncaught-single.js")); + +test(function(t) { + assert_false(false, "False is false"); + }, + "Test running on main document."); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "OK", + "message": null + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "Browser supports Workers", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Test running on main document.", + "properties": {}, + "message": null + }, + { + "status_string": "FAIL", + "name": "worker-uncaught-single", + "properties": {}, + "message": "Error: This failure is expected." + } + ], + "type": "complete" +} +</script> +</body> diff --git a/testing/web-platform/tests/resources/test/tests/functional/worker-dedicated.sub.html b/testing/web-platform/tests/resources/test/tests/functional/worker-dedicated.sub.html new file mode 100644 index 0000000000..efd703c760 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/worker-dedicated.sub.html @@ -0,0 +1,88 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Dedicated Worker Tests</title> +<script src="../../../testharness.js"></script> +<script src="../../../testharnessreport.js"></script> +</head> +<body> +<h1>Dedicated Web Worker Tests</h1> +<p>Demonstrates running <tt>testharness</tt> based tests inside a dedicated web worker. +<p>The test harness is expected to fail due to an uncaught exception in one worker.</p> +<div id="log"></div> + +<script> +test(function(t) { + assert_true("Worker" in self, "Browser should support Workers"); + }, + "Browser supports Workers"); + +fetch_tests_from_worker(new Worker("worker.js")); + +fetch_tests_from_worker(new Worker("worker-error.js")); + +test(function(t) { + assert_false(false, "False is false"); + }, + "Test running on main document."); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "ERROR", + "message": "Error: This failure is expected." + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "Browser supports Workers", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Test running on main document.", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Worker async_test that completes successfully", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Worker test that completes successfully", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "worker test that completes successfully before exception", + "properties": {}, + "message": null + }, + { + "status_string": "NOTRUN", + "name": "Worker test that doesn't run ('NOT RUN')", + "properties": {}, + "message": null + }, + { + "status_string": "FAIL", + "name": "Worker test that fails ('FAIL')", + "properties": {}, + "message": "assert_true: Failing test expected true got false" + }, + { + "status_string": "TIMEOUT", + "name": "Worker test that times out ('TIMEOUT')", + "properties": {}, + "message": "Test timed out" + } + ], + "type": "complete" +} +</script> +</body> diff --git a/testing/web-platform/tests/resources/test/tests/functional/worker-error.js b/testing/web-platform/tests/resources/test/tests/functional/worker-error.js new file mode 100644 index 0000000000..7b89602f04 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/worker-error.js @@ -0,0 +1,8 @@ +importScripts("/resources/testharness.js"); + +// The following sub-test ensures that the worker is not interpreted as a +// single-page test. The subsequent uncaught exception should therefore be +// interpreted as a harness error rather than a single-page test failure. +test(function() {}, "worker test that completes successfully before exception"); + +throw new Error("This failure is expected."); diff --git a/testing/web-platform/tests/resources/test/tests/functional/worker-service.html b/testing/web-platform/tests/resources/test/tests/functional/worker-service.html new file mode 100644 index 0000000000..2e07746e62 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/worker-service.html @@ -0,0 +1,115 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Example with a service worker</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> +<h1>Service Worker Tests</h1> +<p>Demonstrates running <tt>testharness</tt> based tests inside a service worker. +<p>The test harness should time out due to one of the tests inside the worker timing out. +<p>This test assumes that the browser supports <a href="http://www.w3.org/TR/service-workers/">ServiceWorkers</a>. +<div id="log"></div> + +<script> +test( + function(t) { + assert_true("serviceWorker" in navigator, + "navigator.serviceWorker exists"); + }, + "Browser supports ServiceWorker"); + +promise_test( + function() { + // Since the service worker registration could be in an indeterminate + // state (due to, for example, a previous test run failing), we start by + // unregstering our service worker and then registering it again. + var scope = "service-worker-scope"; + var worker_url = "worker.js"; + + return navigator.serviceWorker.register(worker_url, {scope: scope}) + .then( + function(registration) { + return registration.unregister(); + }) + .then( + function() { + return navigator.serviceWorker.register(worker_url, {scope: scope}); + }) + .then( + function(registration) { + add_completion_callback( + function() { + registration.unregister(); + }); + + return new Promise( + function(resolve) { + registration.addEventListener("updatefound", + function() { + resolve(registration.installing); + }); + }); + }) + .then( + function(worker) { + fetch_tests_from_worker(worker); + }); + }, + "Register ServiceWorker"); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "TIMEOUT", + "message": null + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "Browser supports ServiceWorker", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Register ServiceWorker", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Worker async_test that completes successfully", + "properties": {}, + "message": null + }, + { + "status_string": "PASS", + "name": "Worker test that completes successfully", + "properties": {}, + "message": null + }, + { + "status_string": "NOTRUN", + "name": "Worker test that doesn't run ('NOT RUN')", + "properties": {}, + "message": null + }, + { + "status_string": "FAIL", + "name": "Worker test that fails ('FAIL')", + "properties": {}, + "message": "assert_true: Failing test expected true got false" + }, + { + "status_string": "TIMEOUT", + "name": "Worker test that times out ('TIMEOUT')", + "properties": {}, + "message": "Test timed out" + } + ], + "type": "complete" +} +</script> +</body> diff --git a/testing/web-platform/tests/resources/test/tests/functional/worker-shared.html b/testing/web-platform/tests/resources/test/tests/functional/worker-shared.html new file mode 100644 index 0000000000..e26f17dec2 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/worker-shared.html @@ -0,0 +1,73 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Example with a shared worker</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +</head> +<body> +<h1>Shared Web Worker Tests</h1> +<p>Demonstrates running <tt>testharness</tt> based tests inside a shared worker. +<p>The test harness should time out due to one of the tests in the worker timing out. +<p>This test assumes that the browser supports <a href="http://www.w3.org/TR/workers/#shared-workers-and-the-sharedworker-interface">shared web workers</a>. +<div id="log"></div> + +<script> +test( + function(t) { + assert_true("SharedWorker" in self, + "Browser should support SharedWorkers"); + }, + "Browser supports SharedWorkers"); + +fetch_tests_from_worker(new SharedWorker("worker.js", + "My shared worker")); +</script> +<script type="text/json" id="expected"> +{ + "summarized_status": { + "status_string": "TIMEOUT", + "message": null + }, + "summarized_tests": [ + { + "status_string": "PASS", + "name": "Browser supports SharedWorkers", + "properties": {}, + "message": null + }, + { + "message": null, + "name": "Worker async_test that completes successfully", + "properties": {}, + "status_string": "PASS" + }, + { + "message": null, + "name": "Worker test that completes successfully", + "properties": {}, + "status_string": "PASS" + }, + { + "message": null, + "name": "Worker test that doesn't run ('NOT RUN')", + "properties": {}, + "status_string": "NOTRUN" + }, + { + "message": "assert_true: Failing test expected true got false", + "name": "Worker test that fails ('FAIL')", + "properties": {}, + "status_string": "FAIL" + }, + { + "message": "Test timed out", + "name": "Worker test that times out ('TIMEOUT')", + "properties": {}, + "status_string": "TIMEOUT" + } + ], + "type": "complete" +} +</script> +</body> diff --git a/testing/web-platform/tests/resources/test/tests/functional/worker-uncaught-allow.js b/testing/web-platform/tests/resources/test/tests/functional/worker-uncaught-allow.js new file mode 100644 index 0000000000..6925d59349 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/worker-uncaught-allow.js @@ -0,0 +1,19 @@ +importScripts("/resources/testharness.js"); + +setup({allow_uncaught_exception:true}); + +async_test(function(t) { + onerror = function() { + // Further delay the test's completion to ensure that the worker's + // `onerror` handler does not influence results in the parent context. + setTimeout(function() { + t.done(); + }, 0); + }; + + setTimeout(function() { + throw new Error("This error is expected."); + }, 0); +}, 'onerror event is triggered'); + +done(); diff --git a/testing/web-platform/tests/resources/test/tests/functional/worker-uncaught-single.js b/testing/web-platform/tests/resources/test/tests/functional/worker-uncaught-single.js new file mode 100644 index 0000000000..c04542b2f5 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/worker-uncaught-single.js @@ -0,0 +1,8 @@ +importScripts("/resources/testharness.js"); + +setup({ single_test: true }); + +// Because this script enables the `single_test` configuration option, it +// should be interpreted as a single-page test, and the uncaught exception +// should be reported as a test failure (harness status: OK). +throw new Error("This failure is expected."); diff --git a/testing/web-platform/tests/resources/test/tests/functional/worker.js b/testing/web-platform/tests/resources/test/tests/functional/worker.js new file mode 100644 index 0000000000..a923bc2d89 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/functional/worker.js @@ -0,0 +1,34 @@ +importScripts("/resources/testharness.js"); + +test( + function(test) { + assert_true(true, "True is true"); + }, + "Worker test that completes successfully"); + +test( + function(test) { + assert_true(false, "Failing test"); + }, + "Worker test that fails ('FAIL')"); + +async_test( + function(test) { + assert_true(true, "True is true"); + }, + "Worker test that times out ('TIMEOUT')"); + +async_test("Worker test that doesn't run ('NOT RUN')"); + +async_test( + function(test) { + self.setTimeout( + function() { + test.done(); + }, + 0); + }, + "Worker async_test that completes successfully"); + +// An explicit done() is required for dedicated and shared web workers. +done(); diff --git a/testing/web-platform/tests/resources/test/tests/unit/IdlArray/is_json_type.html b/testing/web-platform/tests/resources/test/tests/unit/IdlArray/is_json_type.html new file mode 100644 index 0000000000..18e83a8e89 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/IdlArray/is_json_type.html @@ -0,0 +1,192 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>IdlArray.prototype.is_json_type()</title> +</head> +<body> +<div id="log"></div> +<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 src="../../../idl-helper.js"></script> +<script> + "use strict"; + + test(function() { + var idl = new IdlArray(); + assert_true(idl.is_json_type(typeFrom("DOMString"))); + assert_true(idl.is_json_type(typeFrom("ByteString"))); + assert_true(idl.is_json_type(typeFrom("USVString"))); + idl.add_untested_idls('enum BarEnum { "a", "b", "c" };'); + assert_true(idl.is_json_type(typeFrom("BarEnum"))); + }, 'should return true for all string types'); + + test(function() { + var idl = new IdlArray(); + assert_false(idl.is_json_type(typeFrom("Error"))); + assert_false(idl.is_json_type(typeFrom("DOMException"))); + }, 'should return false for all exception types'); + + test(function() { + var idl = new IdlArray(); + assert_false(idl.is_json_type(typeFrom("Int8Array"))); + assert_false(idl.is_json_type(typeFrom("Int16Array"))); + assert_false(idl.is_json_type(typeFrom("Int32Array"))); + assert_false(idl.is_json_type(typeFrom("Uint8Array"))); + assert_false(idl.is_json_type(typeFrom("Uint16Array"))); + assert_false(idl.is_json_type(typeFrom("Uint32Array"))); + assert_false(idl.is_json_type(typeFrom("Uint8ClampedArray"))); + assert_false(idl.is_json_type(typeFrom("BigInt64Array"))); + assert_false(idl.is_json_type(typeFrom("BigUint64Array"))); + assert_false(idl.is_json_type(typeFrom("Float32Array"))); + assert_false(idl.is_json_type(typeFrom("Float64Array"))); + assert_false(idl.is_json_type(typeFrom("ArrayBuffer"))); + assert_false(idl.is_json_type(typeFrom("DataView"))); + }, 'should return false for all buffer source types'); + + test(function() { + var idl = new IdlArray(); + assert_true(idl.is_json_type(typeFrom("boolean"))); + }, 'should return true for boolean'); + + test(function() { + var idl = new IdlArray(); + assert_true(idl.is_json_type(typeFrom("byte"))); + assert_true(idl.is_json_type(typeFrom("octet"))); + assert_true(idl.is_json_type(typeFrom("short"))); + assert_true(idl.is_json_type(typeFrom("unsigned short"))); + assert_true(idl.is_json_type(typeFrom("long"))); + assert_true(idl.is_json_type(typeFrom("unsigned long"))); + assert_true(idl.is_json_type(typeFrom("long long"))); + assert_true(idl.is_json_type(typeFrom("unsigned long long"))); + assert_true(idl.is_json_type(typeFrom("float"))); + assert_true(idl.is_json_type(typeFrom("unrestricted float"))); + assert_true(idl.is_json_type(typeFrom("double"))); + assert_true(idl.is_json_type(typeFrom("unrestricted double"))); + }, 'should return true for all numeric types'); + + test(function() { + var idl = new IdlArray(); + assert_false(idl.is_json_type(typeFrom("Promise<DOMString>"))); + }, 'should return false for promises'); + + test(function() { + var idl = new IdlArray(); + assert_false(idl.is_json_type(typeFrom("sequence<DOMException>"))); + assert_true(idl.is_json_type(typeFrom("sequence<DOMString>"))); + }, 'should handle sequences according to their inner types'); + + test(function() { + var idl = new IdlArray(); + assert_false(idl.is_json_type(typeFrom("FrozenArray<DOMException>"))); + assert_true(idl.is_json_type(typeFrom("FrozenArray<DOMString>"))); + }, 'should handle frozen arrays according to their inner types'); + + test(function() { + var idl = new IdlArray(); + assert_true(idl.is_json_type(typeFrom("record<DOMString, DOMString>"))); + assert_false(idl.is_json_type(typeFrom("record<DOMString, Error>"))); + }, 'should handle records according to their inner types'); + + test(function() { + var idl = new IdlArray(); + assert_true(idl.is_json_type(typeFrom("object"))); + }, 'should return true for object type'); + + test(function() { + var idl = new IdlArray(); + assert_false(idl.is_json_type(typeFrom("any"))); + }, 'should return false for any type'); + + test(function() { + var idl = new IdlArray(); + idl.add_untested_idls('dictionary Foo { DOMString foo; }; dictionary Bar : Foo { DOMString bar; };'); + assert_true(idl.is_json_type(typeFrom("Foo"))); + assert_true(idl.is_json_type(typeFrom("Bar"))); + }, 'should return true for dictionaries whose members are all JSON types'); + + test(function() { + var idl = new IdlArray(); + idl.add_untested_idls('dictionary Foo { };'); + assert_true(idl.is_json_type(typeFrom("Foo"))); + }, 'should return true for dictionaries which have no members'); + + test(function() { + var idl = new IdlArray(); + idl.add_untested_idls('dictionary FooBar { DOMString a; Error b; }; dictionary Baz : FooBar {};'); + assert_false(idl.is_json_type(typeFrom("FooBar"))); + assert_false(idl.is_json_type(typeFrom("Baz"))); + }, 'should return false for dictionaries whose members are not all JSON types'); + + test(function() { + var idl = new IdlArray(); + idl.add_untested_idls('interface Foo { DOMString toJSON(); };'); + assert_true(idl.is_json_type(typeFrom("Foo"))); + }, 'should return true for interfaces which declare a toJSON operation'); + + test(function() { + var idl = new IdlArray(); + idl.add_untested_idls('interface Foo { DOMString toJSON(); }; interface Bar : Foo { };'); + assert_true(idl.is_json_type(typeFrom("Bar"))); + }, 'should return true for interfaces which inherit from an interface which declares a toJSON operation'); + + test(function() { + var idl = new IdlArray(); + idl.add_untested_idls('interface Foo { }; interface mixin Bar { DOMString toJSON(); }; Foo includes Bar;'); + idl.merge_mixins(); + assert_true(idl.is_json_type(typeFrom("Foo"))); + }, 'should return true for interfaces which mixin an interface which declare a toJSON operation'); + + test(function() { + var idl = new IdlArray(); + idl.add_untested_idls('interface Foo { };'); + assert_false(idl.is_json_type(typeFrom("Foo"))); + }, 'should return false for interfaces which do not declare a toJSON operation'); + + test(function() { + var idl = new IdlArray(); + idl.add_untested_idls('interface Foo { object toJSON(); };'); + assert_true(idl.is_json_type(typeFrom("(Foo or DOMString)"))); + }, 'should return true for union types whose member types are JSON types'); + + test(function() { + var idl = new IdlArray(); + assert_false(idl.is_json_type(typeFrom("(DataView or DOMString)"))); + }, 'should return false for union types whose member types are not all JSON types'); + + test(function() { + var idl = new IdlArray(); + assert_true(idl.is_json_type(typeFrom("DOMString?"))); + assert_false(idl.is_json_type(typeFrom("DataView?"))); + }, 'should consider the inner types of nullable types'); + + test(function() { + var idl = new IdlArray(); + assert_true(idl.is_json_type(typeFrom("[XAttr] long"))); + assert_false(idl.is_json_type(typeFrom("[XAttr] DataView"))); + }, 'should consider the inner types of annotated types.'); + + test(function() { + var idl = new IdlArray(); + assert_throws_js(Error, _ => idl.is_json_type(typeFrom("Foo"))); + }, "should throw if it references a dictionary, enum or interface which wasn't added to the IdlArray"); + + test(function() { + var idl = new IdlArray(); + idl.add_untested_idls('interface Foo : Bar { };'); + assert_throws_js(Error, _ => idl.is_json_type(typeFrom("Foo"))); + }, "should throw for interfaces which inherit from another interface which wasn't added to the IdlArray"); + + test(function() { + var idl = new IdlArray(); + assert_true(idl.is_json_type(typedefFrom("typedef double DOMHighResTimeStamp;").idlType)); + }, 'should return true for typedefs whose source type is a JSON type'); + + test(function() { + var idl = new IdlArray(); + assert_false(idl.is_json_type(typedefFrom("typedef DataView DOMHighResTimeStamp;").idlType)); + }, 'should return false for typedefs whose source type is not a JSON type'); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/unit/IdlDictionary/get_reverse_inheritance_stack.html b/testing/web-platform/tests/resources/test/tests/unit/IdlDictionary/get_reverse_inheritance_stack.html new file mode 100644 index 0000000000..418bcdec92 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/IdlDictionary/get_reverse_inheritance_stack.html @@ -0,0 +1,47 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>IdlDictionary.prototype.get_reverse_inheritance_stack()</title> +</head> +<body> +<div id="log"></div> +<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 src="../../../idl-helper.js"></script> +<script> + "use strict"; + test(function() { + var stack = dictionaryFrom('dictionary A { };').get_reverse_inheritance_stack(); + assert_array_equals(stack.map(d => d.name), ["A"]); + }, 'should return an array that includes itself.'); + + test(function() { + var d = dictionaryFrom('dictionary A : B { };'); + assert_throws_js(Error, _ => d.get_reverse_inheritance_stack()); + }, "should throw for dictionaries which inherit from another dictionary which wasn't added to the IdlArray"); + + test(function() { + var idl = new IdlArray(); + idl.add_idls('dictionary A : B { };'); + idl.add_untested_idls('dictionary B : C { }; dictionary C { };'); + var A = idl.members["A"]; + assert_array_equals(A.get_reverse_inheritance_stack().map(d => d.name), ["C", "B", "A"]); + }, 'should return an array of dictionaries in order of inheritance, starting with the base dictionary'); + + test(function () { + let i = new IdlArray(); + i.add_untested_idls('dictionary A : B {};'); + i.assert_throws(new IdlHarnessError('A inherits B, but B is undefined.'), i => i.test()); + }, 'A : B with B undeclared should throw IdlHarnessError'); + + test(function () { + let i = new IdlArray(); + i.add_untested_idls('dictionary A : B {};'); + i.add_untested_idls('interface B {};'); + i.assert_throws(new IdlHarnessError('A inherits B, but A is not an interface.'), i => i.test()); + }, 'dictionary A : B with B interface should throw IdlHarnessError'); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/unit/IdlDictionary/test_partial_dictionary.html b/testing/web-platform/tests/resources/test/tests/unit/IdlDictionary/test_partial_dictionary.html new file mode 100644 index 0000000000..d6137f6895 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/IdlDictionary/test_partial_dictionary.html @@ -0,0 +1,39 @@ +<!DOCTYPE HTML> +<html> + +<head> + <meta charset="utf-8"> + <title>idlharness: partial dictionaries</title> + <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 src="../../../idl-helper.js"></script> +</head> + +<body> +<pre id='idl'> +dictionary A {}; +partial dictionary A { + boolean B; +}; +partial dictionary A { + boolean C; +}; +</pre> + +<script> +'use strict'; + +test(() => { + let idlArray = new IdlArray(); + idlArray.add_idls(document.getElementById('idl').textContent); + idlArray.test(); + + let members = idlArray.members["A"].members.map(m => m.name); + assert_array_equals(members, ["B", "C"], 'A should contain B, C'); +}, 'Partial dictionaries'); +</script> + +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/constructors.html b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/constructors.html new file mode 100644 index 0000000000..e9ee3f8680 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/constructors.html @@ -0,0 +1,26 @@ +<!DOCTYPE HTML> +<title>IdlInterface.prototype.constructors()</title> +<div id="log"></div> +<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 src="../../../idl-helper.js"></script> +<script> +"use strict"; +// [Constructor] extended attribute should not be supported: +test(function() { + var i = interfaceFrom('[Constructor] interface A { };'); + assert_equals(i.constructors().length, 0); +}, 'Interface with Constructor extended attribute.'); + +test(function() { + var i = interfaceFrom('interface A { constructor(); };'); + assert_equals(i.constructors().length, 1); +}, 'Interface with constructor method'); + +test(function() { + var i = interfaceFrom('interface A { constructor(); constructor(any value); };'); + assert_equals(i.constructors().length, 2); +}, 'Interface with constructor overloads'); +</script> diff --git a/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/default_to_json_operation.html b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/default_to_json_operation.html new file mode 100644 index 0000000000..5ade7d0d28 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/default_to_json_operation.html @@ -0,0 +1,114 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>IdlDictionary.prototype.default_to_json_operation()</title> +</head> +<body> +<div id="log"></div> +<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 src="../../../idl-helper.js"></script> +<script> + "use strict"; + test(function() { + var map = interfaceFrom('interface A { [Default] object toJSON(); };').default_to_json_operation(); + assert_equals(map.size, 0); + }, 'should return an empty map when there are no attributes'); + + test(function() { + var r = interfaceFrom('interface A { };').default_to_json_operation(); + assert_equals(r, null); + }, 'should return null when there is no toJSON method'); + + test(function() { + var r = interfaceFrom('interface A { DOMString toJSON(); };').default_to_json_operation(); + assert_equals(r, null); + }, 'should return null when there is a toJSON method but it does not have the [Default] extended attribute'); + + test(function() { + var context = new IdlArray(); + context.add_idls("interface A : B { DOMString toJSON(); };"); + context.add_idls("interface B { [Default] object toJSON(); };"); + var r = context.members.A.default_to_json_operation(); + assert_equals(r, null); + }, 'should return null when there is a toJSON method but it does not have the [Default] extended attribute even if this extended attribute exists on inherited interfaces'); + + test(function() { + var map = interfaceFrom('interface A { [Default] object toJSON(); static attribute DOMString foo; };').default_to_json_operation(); + assert_equals(map.size, 0); + }, 'should not include static attributes'); + + test(function() { + var map = interfaceFrom('interface A { [Default] object toJSON(); attribute Promise<DOMString> bar; };').default_to_json_operation(); + assert_equals(map.size, 0); + }, 'should not include attributes which are not JSON types'); + + test(function() { + var map = interfaceFrom('interface A { [Default] object toJSON(); DOMString bar(); };').default_to_json_operation(); + assert_equals(map.size, 0); + }, 'should not include operations'); + + test(function() { + var map = interfaceFrom('interface A { [Default] object toJSON(); attribute DOMString bar; };').default_to_json_operation(); + assert_equals(map.size, 1); + assert_true(map.has("bar")); + assert_equals(map.get("bar").idlType, "DOMString"); + }, 'should return a map whose key/value pair represent the identifier and IDL type of valid attributes'); + + test(function() { + var context = new IdlArray(); + context.add_idls("interface A : B { [Default] object toJSON(); attribute DOMString a; };"); + context.add_idls("interface B { [Default] object toJSON(); attribute long b; };"); + var map = context.members.A.default_to_json_operation(); + assert_array_equals([...map.keys()], ["b", "a"]); + assert_array_equals([...map.values()].map(v => v.idlType), ["long", "DOMString"]); + }, 'should return a properly ordered map that contains IDL types of valid attributes for inherited interfaces'); + + test(function() { + var context = new IdlArray(); + context.add_idls("interface A : B { attribute DOMString a; };"); + context.add_idls("interface B { [Default] object toJSON(); attribute long b; };"); + var map = context.members.A.default_to_json_operation(); + assert_equals(map.size, 1); + assert_true(map.has("b")); + assert_equals(map.get("b").idlType, "long"); + assert_array_equals([...map.keys()], ["b"]); + }, 'should not include attributes of the current interface when the [Default] toJSON method in inherited'); + + test(function() { + var context = new IdlArray(); + context.add_idls("interface A : B { [Default] object toJSON(); };"); + context.add_idls("interface B : C { [Default] object toJSON(); attribute DOMString foo; };"); + context.add_idls("interface C { [Default] object toJSON(); attribute long foo; };"); + var map = context.members.A.default_to_json_operation(); + assert_equals(map.size, 1); + assert_true(map.has("foo")); + assert_equals(map.get("foo").idlType, "DOMString"); + }, 'attributes declared further away in the inheritance hierarchy should be masked by attributes declared closer'); + + test(function() { + var context = new IdlArray(); + context.add_idls("interface A { [Default] object toJSON(); attribute DOMString a; };"); + context.add_idls("interface B : A { attribute any b; };"); + context.add_idls("interface C : B { [Default] object toJSON(); attribute long c; };"); + var map = context.members.C.default_to_json_operation(); + assert_array_equals([...map.keys()], ["a", "c"]); + assert_array_equals([...map.values()].map(v => v.idlType), ["DOMString", "long"]); + }, 'should return an ordered map that ignores attributes of inherited interfaces which do not declare a [Default] toJSON operation.'); + + test(function() { + var context = new IdlArray(); + context.add_idls("interface D { attribute DOMString d; };"); + context.add_idls("interface mixin M { [Default] object toJSON(); attribute long m; };"); + context.add_idls("D includes M;"); + context.merge_mixins(); + var map = context.members.D.default_to_json_operation(); + assert_array_equals([...map.keys()], ["d", "m"]); + assert_array_equals([...map.values()].map(v => v.idlType), ["DOMString", "long"]); + }, 'should return a properly ordered map that accounts for mixed-in interfaces which declare a [Default] toJSON operation.'); +</script> +</body> +</html> + diff --git a/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/do_member_unscopable_asserts.html b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/do_member_unscopable_asserts.html new file mode 100644 index 0000000000..90142efe6b --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/do_member_unscopable_asserts.html @@ -0,0 +1,56 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>IdlDictionary.prototype.do_member_unscopable_asserts()</title> +</head> +<body> +<div id="log"></div> +<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 src="../../../idl-helper.js"></script> +<script> + 'use strict'; + function mock_interface_A(unscopables) { + self.A = function A() {}; + A.prototype[Symbol.unscopables] = unscopables; + } + + test(function() { + const i = interfaceFrom('interface A { [Unscopable] attribute any x; };'); + const member = i.members[0]; + assert_true(member.isUnscopable); + mock_interface_A({ x: true }); + i.do_member_unscopable_asserts(member); + }, 'should not throw for [Unscopable] with property in @@unscopables'); + + test(function() { + const i = interfaceFrom('interface A { [Unscopable] attribute any x; };'); + const member = i.members[0]; + assert_true(member.isUnscopable); + mock_interface_A({}); + // assert_throws_* can't be used because they rethrow AssertionErrors. + try { + i.do_member_unscopable_asserts(member); + } catch(e) { + assert_true(e.message.includes('Symbol.unscopables')); + return; + } + assert_unreached('did not throw'); + }, 'should throw for [Unscopable] with property missing from @@unscopables'); + + // This test checks that for attributes/methods which aren't [Unscopable] + // in the IDL, we don't assert that @@unscopables is missing the property. + // This could miss implementation bugs, but [Unscopable] is so rarely used + // that it's fairly unlikely to ever happen. + test(function() { + const i = interfaceFrom('interface A { attribute any x; };'); + const member = i.members[0]; + assert_false(member.isUnscopable); + mock_interface_A({ x: true }); + i.do_member_unscopable_asserts(member); + }, 'should not throw if [Unscopable] is used but property is in @@unscopables'); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/get_interface_object.html b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/get_interface_object.html new file mode 100644 index 0000000000..a3d901a752 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/get_interface_object.html @@ -0,0 +1,22 @@ +<!DOCTYPE HTML> +<title>IdlInterface.prototype.get_interface_object()</title> +<div id="log"></div> +<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 src="../../../idl-helper.js"></script> +<script> +"use strict"; +test(function() { + window.A = {}; + var i = interfaceFrom('interface A { };'); + assert_equals(i.get_interface_object(), window.A); +}, 'Interface does not have LegacyNamespace.'); + +test(function() { + window.Foo = { A: {} }; + var i = interfaceFrom('[LegacyNamespace=Foo] interface A { }; namespace Foo { };'); + assert_equals(i.get_interface_object(), window.Foo.A); +}, 'Interface has LegacyNamespace'); +</script> diff --git a/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/get_interface_object_owner.html b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/get_interface_object_owner.html new file mode 100644 index 0000000000..51ab2067bc --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/get_interface_object_owner.html @@ -0,0 +1,21 @@ +<!DOCTYPE HTML> +<title>IdlInterface.prototype.get_interface_object_owner()</title> +<div id="log"></div> +<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 src="../../../idl-helper.js"></script> +<script> +"use strict"; +test(function() { + var i = interfaceFrom('interface A { };'); + assert_equals(i.get_interface_object_owner(), window); +}, 'Interface does not have LegacyNamespace.'); + +test(function() { + window.Foo = {}; + var i = interfaceFrom('[LegacyNamespace=Foo] interface A { }; namespace Foo { };'); + assert_equals(i.get_interface_object_owner(), window.Foo); +}, 'Interface has LegacyNamespace'); +</script> diff --git a/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/get_legacy_namespace.html b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/get_legacy_namespace.html new file mode 100644 index 0000000000..e2d42bb09e --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/get_legacy_namespace.html @@ -0,0 +1,20 @@ +<!DOCTYPE HTML> +<title>IdlInterface.prototype.get_legacy_namespace()</title> +<div id="log"></div> +<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 src="../../../idl-helper.js"></script> +<script> +"use strict"; +test(function() { + var i = interfaceFrom('interface A { };'); + assert_equals(i.get_legacy_namespace(), undefined); +}, 'Interface does not have LegacyNamespace.'); + +test(function() { + var i = interfaceFrom('[LegacyNamespace=Foo] interface A { }; namespace Foo { };'); + assert_equals(i.get_legacy_namespace(), "Foo"); +}, 'Interface has LegacyNamespace'); +</script> diff --git a/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/get_qualified_name.html b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/get_qualified_name.html new file mode 100644 index 0000000000..677a31b5e7 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/get_qualified_name.html @@ -0,0 +1,20 @@ +<!DOCTYPE HTML> +<title>IdlInterface.prototype.get_qualified_name()</title> +<div id="log"></div> +<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 src="../../../idl-helper.js"></script> +<script> +"use strict"; +test(function() { + var i = interfaceFrom('interface A { };'); + assert_equals(i.get_qualified_name(), "A"); +}, 'Interface does not have LegacyNamespace.'); + +test(function() { + var i = interfaceFrom('[LegacyNamespace=Foo] interface A { }; namespace Foo { };'); + assert_equals(i.get_qualified_name(), "Foo.A"); +}, 'Interface has LegacyNamespace'); +</script> diff --git a/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/get_reverse_inheritance_stack.html b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/get_reverse_inheritance_stack.html new file mode 100644 index 0000000000..0c066baabb --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/get_reverse_inheritance_stack.html @@ -0,0 +1,49 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>IdlInterface.prototype.get_reverse_inheritance_stack()</title> +</head> +<body> +<div id="log"></div> +<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 src="../../../idl-helper.js"></script> +<script> + "use strict"; + test(function() { + var stack = interfaceFrom('interface A { };').get_reverse_inheritance_stack(); + assert_array_equals(stack.map(i => i.name), ["A"]); + }, 'should return an array that includes itself.'); + + test(function() { + var i = interfaceFrom('interface A : B { };'); + assert_throws_js(Error, _ => i.get_reverse_inheritance_stack()); + }, "should throw for interfaces which inherit from another interface which wasn't added to the IdlArray"); + + test(function() { + var idl = new IdlArray(); + idl.add_idls('interface A : B { };'); + idl.add_untested_idls('interface B : C { }; interface C { };'); + var A = idl.members["A"]; + assert_array_equals(A.get_reverse_inheritance_stack().map(i => i.name), ["C", "B", "A"]); + }, 'should return an array of interfaces in order of inheritance, starting with the base interface'); + + test(function () { + var idl = new IdlArray(); + idl.add_untested_idls('interface A : B { };'); + idl.add_untested_idls('interface B : A { };'); + idl.assert_throws('A has a circular dependency: A,B,A', i => i.test()); + }, 'should throw when inheritance is circular'); + + test(function () { + var idl = new IdlArray(); + idl.add_untested_idls('interface A : B { };'); + idl.assert_throws( + 'Duplicate identifier A', + i => i.add_untested_idls('interface A : C { };')); + }, 'should throw when multiple inheritances defined'); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/has_default_to_json_regular_operation.html b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/has_default_to_json_regular_operation.html new file mode 100644 index 0000000000..b47262b72b --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/has_default_to_json_regular_operation.html @@ -0,0 +1,47 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>IdlInterface.prototype.has_default_to_json_regular_operation()</title> +</head> +<body> +<div id="log"></div> +<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 src="../../../idl-helper.js"></script> +<script> + "use strict"; + test(function() { + var i = interfaceFrom('interface A { };'); + assert_false(i.has_default_to_json_regular_operation()); + }, 'should return false when the interface declares no toJSON operation.'); + + test(function() { + var i = interfaceFrom('interface A { static object toJSON(); };'); + assert_false(i.has_default_to_json_regular_operation()); + }, 'should return false when the interface declares a static toJSON operation.'); + + test(function() { + var i = interfaceFrom('interface A { object toJSON(); };'); + assert_false(i.has_default_to_json_regular_operation()); + }, 'should return false when the interface declares a regular toJSON operation with no extended attribute.'); + + test(function() { + var i = interfaceFrom('interface A { [x] object toJSON(); };'); + assert_false(i.has_default_to_json_regular_operation()); + }, 'should return false when the interface declares a regular toJSON operation with another extented attribute.'); + + test(function() { + var i = interfaceFrom('interface A { [Default] object toJSON(); };'); + assert_true(i.has_default_to_json_regular_operation()); + }, 'should return true when the interface declares a regular toJSON operation with the [Default] extented attribute.'); + + test(function() { + var i = interfaceFrom('interface A { [Attr, AnotherAttr, Default] object toJSON(); };'); + assert_true(i.has_default_to_json_regular_operation()); + }, 'should return true when the interface declares a regular toJSON operation with multiple extended attributes, including [Default].'); +</script> +</body> +</html> + diff --git a/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/has_to_json_regular_operation.html b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/has_to_json_regular_operation.html new file mode 100644 index 0000000000..a1a641bd97 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/has_to_json_regular_operation.html @@ -0,0 +1,31 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>IdlInterface.prototype.has_to_json_regular_operation()</title> +</head> +<body> +<div id="log"></div> +<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 src="../../../idl-helper.js"></script> +<script> + "use strict"; + test(function() { + var i = interfaceFrom('interface A { };'); + assert_false(i.has_to_json_regular_operation()); + }, 'should return false when the interface declares no toJSON operation.'); + + test(function() { + var i = interfaceFrom('interface A { static object toJSON(); };'); + assert_false(i.has_to_json_regular_operation()); + }, 'should return false when the interface declares a static toJSON operation.'); + + test(function() { + var i = interfaceFrom('interface A { object toJSON(); };'); + assert_true(i.has_to_json_regular_operation()); + }, 'should return true when the interface declares a regular toJSON operation.'); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/should_have_interface_object.html b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/should_have_interface_object.html new file mode 100644 index 0000000000..3ce945751d --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/should_have_interface_object.html @@ -0,0 +1,30 @@ +<!DOCTYPE HTML> +<title>IdlInterface.prototype.should_have_interface_object()</title> +<div id="log"></div> +<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 src="../../../idl-helper.js"></script> +<script> +"use strict"; +test(function() { + var i = interfaceFrom("callback interface A { const unsigned short B = 0; };"); + assert_true(i.should_have_interface_object()); +}, "callback interface with a constant"); + +test(function() { + var i = interfaceFrom("callback interface A { undefined b(); sequence<any> c(); };"); + assert_false(i.should_have_interface_object()); +}, "callback interface without a constant"); + +test(function() { + var i = interfaceFrom("[LegacyNoInterfaceObject] interface A { };"); + assert_false(i.should_have_interface_object()); +}, "non-callback interface with [LegacyNoInterfaceObject]"); + +test(function() { + var i = interfaceFrom("interface A { };"); + assert_true(i.should_have_interface_object()); +}, "non-callback interface without [LegacyNoInterfaceObject]"); +</script> diff --git a/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/test_primary_interface_of_undefined.html b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/test_primary_interface_of_undefined.html new file mode 100644 index 0000000000..0031558ad4 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/IdlInterface/test_primary_interface_of_undefined.html @@ -0,0 +1,29 @@ +<!DOCTYPE HTML> +<html> + +<head> + <title>idlharness test_primary_interface_of_undefined</title> +</head> + +<body> + <script src="/resources/testharness.js"></script> + <script src="/resources/WebIDLParser.js"></script> + <script src="/resources/idlharness.js"></script> + <script> + 'use strict'; + test(function () { + let i = new IdlArray(); + i.add_untested_idls('interface A : B {};'); + i.assert_throws(new IdlHarnessError('A inherits B, but B is undefined.'), i => i.test()); + }, 'A : B with B undeclared should throw IdlHarnessError'); + + test(function () { + let i = new IdlArray(); + i.add_untested_idls('interface A : B {};'); + i.add_untested_idls('dictionary B {};'); + i.assert_throws(new IdlHarnessError('A inherits B, but B is not an interface.'), i => i.test()); + }, 'interface A : B with B dictionary should throw IdlHarnessError'); + </script> +</body> + +</html> diff --git a/testing/web-platform/tests/resources/test/tests/unit/IdlInterfaceMember/is_to_json_regular_operation.html b/testing/web-platform/tests/resources/test/tests/unit/IdlInterfaceMember/is_to_json_regular_operation.html new file mode 100644 index 0000000000..b3f402dd08 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/IdlInterfaceMember/is_to_json_regular_operation.html @@ -0,0 +1,42 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>IdlInterfaceMember.prototype.is_to_json_regular_operation()</title> +</head> +<body> +<div id="log"></div> +<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 src="../../../idl-helper.js"></script> +<script> + "use strict"; + test(function() { + var m = memberFrom("readonly attribute DOMString foo"); + assert_false(m.is_to_json_regular_operation()); + }, 'should return false when member is an attribute.'); + + test(function() { + var m = memberFrom("static undefined foo()"); + assert_false(m.is_to_json_regular_operation()); + }, 'should return false when member is a static operation.'); + + test(function() { + var m = memberFrom("static object toJSON()"); + assert_false(m.is_to_json_regular_operation()); + }, 'should return false when member is a static toJSON operation.'); + + test(function() { + var m = memberFrom("object toJSON()"); + assert_true(m.is_to_json_regular_operation()); + }, 'should return true when member is a regular toJSON operation.'); + + test(function() { + var m = memberFrom("[Foo] object toJSON()"); + assert_true(m.is_to_json_regular_operation()); + }, 'should return true when member is a regular toJSON operation with extensible attributes.'); +</script> +</body> +</html> + diff --git a/testing/web-platform/tests/resources/test/tests/unit/IdlInterfaceMember/toString.html b/testing/web-platform/tests/resources/test/tests/unit/IdlInterfaceMember/toString.html new file mode 100644 index 0000000000..054dbb1ccb --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/IdlInterfaceMember/toString.html @@ -0,0 +1,36 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>IdlInterfaceMember.prototype.toString()</title> +</head> +<body> +<div id="log"></div> +<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 src="../../../idl-helper.js"></script> +<script> +"use strict"; +const tests = [ + ["long x", "long"], + ["long? x", "long?"], + ["Promise<long> x", "Promise<long>"], + ["Promise<long?> x", "Promise<long?>"], + ["sequence<long> x", "sequence<long>"], + ["(long or DOMString) x", "(long or DOMString)"], + ["long x, boolean y", "long, boolean"], + ["long x, optional boolean y", "long, optional boolean"], + ["long... args", "long..."], + ["sequence<long>... args", "sequence<long>..."], + ["(long or DOMString)... args", "(long or DOMString)..."], +]; +for (const [input, output] of tests) { + test(function() { + var m = memberFrom(`undefined foo(${input})`); + assert_equals(m.toString(), `foo(${output})`); + }, `toString for ${input}`); +} +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/unit/assert_implements.html b/testing/web-platform/tests/resources/test/tests/unit/assert_implements.html new file mode 100644 index 0000000000..6e35f38502 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/assert_implements.html @@ -0,0 +1,43 @@ +<!DOCTYPE HTML> +<html lang="en"> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/test/tests/unit/helpers.js"></script> +<title>assert_implements unittests</title> +<script> +'use strict'; + +test(() => { + // All values in JS that are not falsy are truthy, so we just check some + // common cases here. + assert_implements(true, 'true is a truthy value'); + assert_implements(5, 'positive integeter is a truthy value'); + assert_implements(-5, 'negative integeter is a truthy value'); + assert_implements('foo', 'non-empty string is a truthy value'); +}, 'truthy values'); + +test_failure(() => { + assert_implements(false); +}, 'false is a falsy value'); + +test_failure(() => { + assert_implements(0); +}, '0 is a falsy value'); + +test_failure(() => { + assert_implements(''); +}, 'empty string is a falsy value'); + +test_failure(() => { + assert_implements(null); +}, 'null is a falsy value'); + +test_failure(() => { + assert_implements(undefined); +}, 'undefined is a falsy value'); + +test_failure(() => { + assert_implements(NaN); +}, 'NaN is a falsy value'); +</script> diff --git a/testing/web-platform/tests/resources/test/tests/unit/assert_implements_optional.html b/testing/web-platform/tests/resources/test/tests/unit/assert_implements_optional.html new file mode 100644 index 0000000000..4f23e203c5 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/assert_implements_optional.html @@ -0,0 +1,43 @@ +<!DOCTYPE HTML> +<html lang="en"> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/test/tests/unit/helpers.js"></script> +<title>assert_implements_optional unittests</title> +<script> +'use strict'; + +test(() => { + // All values in JS that are not falsy are truthy, so we just check some + // common cases here. + assert_implements_optional(true, 'true is a truthy value'); + assert_implements_optional(5, 'positive integeter is a truthy value'); + assert_implements_optional(-5, 'negative integeter is a truthy value'); + assert_implements_optional('foo', 'non-empty string is a truthy value'); +}, 'truthy values'); + +test_failure(() => { + assert_implements_optional(false); +}, 'false is a falsy value'); + +test_failure(() => { + assert_implements_optional(0); +}, '0 is a falsy value'); + +test_failure(() => { + assert_implements_optional(''); +}, 'empty string is a falsy value'); + +test_failure(() => { + assert_implements_optional(null); +}, 'null is a falsy value'); + +test_failure(() => { + assert_implements_optional(undefined); +}, 'undefined is a falsy value'); + +test_failure(() => { + assert_implements_optional(NaN); +}, 'NaN is a falsy value'); +</script> diff --git a/testing/web-platform/tests/resources/test/tests/unit/assert_object_equals.html b/testing/web-platform/tests/resources/test/tests/unit/assert_object_equals.html new file mode 100644 index 0000000000..313d77b977 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/assert_object_equals.html @@ -0,0 +1,152 @@ +<!DOCTYPE HTML> +<html lang="en"> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/test/tests/unit/helpers.js"></script> +<title>Assertion functions</title> +<script> +'use strict'; + +test(function() { + assert_object_equals({}, {}); +}, 'empty objects'); + +test(function() { + var actual = {}; + var expected = {}; + actual.a = actual; + expected.a = actual; + + assert_object_equals(actual, expected); +}, 'tolerates cycles in actual value'); + +test(function() { + var actual = {}; + var expected = {}; + actual.a = expected; + expected.a = expected; + + assert_object_equals(actual, expected); +}, 'tolerates cycles in expected value'); + +test(function() { + assert_object_equals({2: 99, 0: 23, 1: 45}, [23, 45, 99]); +}, 'recognizes equivalence of actual object and expected array'); + +test(function() { + assert_object_equals([23, 45, 99], {2: 99, 0: 23, 1: 45}); +}, 'recognizes equivalence of actual array and expected object'); + +test(function() { + var actual = {}; + var expected = {}; + Object.defineProperty(actual, 'a', { value: 1, enumerable: false }); + + assert_not_equals(actual.a, expected.a); + assert_object_equals(actual, expected); +}, 'non-enumerable properties in actual value ignored'); + +test(function() { + var actual = {}; + var expected = {}; + Object.defineProperty(expected, 'a', { value: 1, enumerable: false }); + + assert_not_equals(actual.a, expected.a); + assert_object_equals(actual, expected); +}, 'non-enumerable properties in expected value ignored'); + +test(function() { + assert_object_equals({c: 3, a: 1, b: 2}, {a: 1, b: 2, c: 3}); +}, 'equivalent objects - "flat" object'); + +test(function() { + assert_object_equals( + {c: {e: 5, d: 4}, b: 2, a: 1}, + {a: 1, b: 2, c: {d: 4, e: 5}} + ); +}, 'equivalent objects - nested object'); + +test(function() { + assert_object_equals( + {c: [4, 5], b: 2, a: 1}, + {a: 1, b: 2, c: [4, 5]} + ); +}, 'equivalent objects - nested array'); + +test(function() { + assert_object_equals({a: NaN}, {a: NaN}); +}, 'equivalent objects - NaN value'); + +test(function() { + assert_object_equals({a: -0}, {a: -0}); +}, 'equivalent objects - negative zero value'); + +test_failure(function() { + assert_object_equals(undefined, {}); +}, 'invalid actual value: undefined'); + +test_failure(function() { + assert_object_equals(null, {}); +}, 'invalid actual value: null'); + +test_failure(function() { + assert_object_equals(34, {}); +}, 'invalid actual value: number'); + +test_failure(function() { + assert_object_equals({c: 3, a: 1, b: 2}, {a: 1, b: 1, c: 3}); +}, 'unequal property value - "flat" object'); + +test_failure(function() { + var actual = Object.create({a: undefined}); + var expected = {}; + + assert_object_equals(actual, expected); +}, 'non-own properties in actual value verified'); + +test_failure(function() { + var actual = {}; + var expected = Object.create({a: undefined}); + + assert_object_equals(actual, expected); +}, 'non-own properties in expected value verified'); + +test_failure(function() { + assert_object_equals( + {a: 1, b: 2, c: {d: 5, e: 5, f: 6}}, + {a: 1, b: 2, c: {d: 5, e: 7, f: 6}} + ); +}, 'unequal property value - nested object'); + +test_failure(function() { + assert_object_equals( + {a: 1, b: 2, c: [4, 5, 6]}, + {a: 1, b: 2, c: [4, 7, 6]} + ); +}, 'unequal property value - nested array'); + +test_failure(function() { + assert_object_equals({a: NaN}, {a: 0}); +}, 'equivalent objects - NaN actual value'); + +test_failure(function() { + assert_object_equals({a: 0}, {a: NaN}); +}, 'equivalent objects - NaN expected value'); + +test_failure(function() { + assert_object_equals({a: -0}, {a: 0}); +}, 'equivalent objects - negative zero actual value'); + +test_failure(function() { + assert_object_equals({a: 0}, {a: -0}); +}, 'equivalent objects - negative zero expected value'); + +test_failure(function() { + assert_object_equals({a: 1}, {}); +}, 'actual contains additional property'); + +test_failure(function() { + assert_object_equals({}, {a: 1}); +}, 'expected contains additional property'); +</script> diff --git a/testing/web-platform/tests/resources/test/tests/unit/async-test-return-restrictions.html b/testing/web-platform/tests/resources/test/tests/unit/async-test-return-restrictions.html new file mode 100644 index 0000000000..0fde2e2422 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/async-test-return-restrictions.html @@ -0,0 +1,135 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <script src="/resources/testharness.js"></script> + <title>Restrictions on return value from `async_test`</title> +</head> +<body> +<script> +function makeTest(...bodies) { + const closeScript = '<' + '/script>'; + let src = ` +<!DOCTYPE HTML> +<html> +<head> +<title>Document title</title> +<script src="/resources/testharness.js?${Math.random()}">${closeScript} +</head> + +<body> +<div id="log"></div>`; + bodies.forEach((body) => { + src += '<script>(' + body + ')();' + closeScript; + }); + + const iframe = document.createElement('iframe'); + + document.body.appendChild(iframe); + iframe.contentDocument.write(src); + + return new Promise((resolve) => { + window.addEventListener('message', function onMessage(e) { + if (e.source !== iframe.contentWindow) { + return; + } + if (!e.data || e.data.type !=='complete') { + return; + } + window.removeEventListener('message', onMessage); + resolve(e.data); + }); + + iframe.contentDocument.close(); + }).then(({ tests, status }) => { + const summary = { + harness: { + status: getEnumProp(status, status.status), + message: status.message + }, + tests: {} + }; + + tests.forEach((test) => { + summary.tests[test.name] = getEnumProp(test, test.status); + }); + + return summary; + }); +} + +function getEnumProp(object, value) { + for (let property in object) { + if (!/^[A-Z]+$/.test(property)) { + continue; + } + + if (object[property] === value) { + return property; + } + } +} + +promise_test(() => { + return makeTest( + () => { + async_test((t) => {t.done(); return undefined;}, 'before'); + async_test((t) => {t.done(); return null;}, 'null'); + async_test((t) => {t.done(); return undefined;}, 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness.status, 'ERROR'); + assert_equals( + harness.message, + 'Test named "null" passed a function to `async_test` that returned a value.' + ); + assert_equals(tests.before, 'PASS'); + assert_equals(tests.null, 'PASS'); + // This test did not get the chance to start. + assert_equals(tests.after, undefined); + }); +}, 'test returning `null`'); + +promise_test(() => { + return makeTest( + () => { + async_test((t) => {t.done(); return undefined;}, 'before'); + async_test((t) => {t.done(); return {};}, 'object'); + async_test((t) => {t.done(); return undefined;}, 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness.status, 'ERROR'); + assert_equals( + harness.message, + 'Test named "object" passed a function to `async_test` that returned a value.' + ); + assert_equals(tests.before, 'PASS'); + assert_equals(tests.object, 'PASS'); + // This test did not get the chance to start. + assert_equals(tests.after, undefined); + }); +}, 'test returning an ordinary object'); + +promise_test(() => { + return makeTest( + () => { + async_test((t) => {t.done(); return undefined;}, 'before'); + async_test((t) => {t.done(); return Promise.resolve(5);}, 'thenable'); + async_test((t) => {t.done(); return undefined;}, 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness.status, 'ERROR'); + assert_equals( + harness.message, + 'Test named "thenable" passed a function to `async_test` that returned a value. ' + + 'Consider using `promise_test` instead when using Promises or async/await.' + ); + assert_equals(tests.before, 'PASS'); + assert_equals(tests.thenable, 'PASS'); + // This test did not get a chance to start. + assert_equals(tests.after, undefined); + }); +}, 'test returning a thenable object'); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/unit/basic.html b/testing/web-platform/tests/resources/test/tests/unit/basic.html new file mode 100644 index 0000000000..d52082f2e0 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/basic.html @@ -0,0 +1,48 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>idlharness basic</title> +</head> +<body> +<div id="log"></div> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/WebIDLParser.js"></script> +<script src="/resources/idlharness.js"></script> +<script> + "use strict"; + test(function() { + assert_true("IdlArray" in window); + }, 'IdlArray constructor should be a global object'); + test(function() { + assert_true(new IdlArray() instanceof IdlArray); + }, 'IdlArray constructor should be constructible'); + test(function() { + assert_true("WebIDL2" in window); + }, 'WebIDL2 namespace should be a global object'); + test(function() { + assert_equals(typeof WebIDL2.parse, "function"); + }, 'WebIDL2 namespace should have a parse method'); + test(function() { + try { + WebIDL2.parse("I'm a syntax error"); + throw new Error("Web IDL didn't throw"); + } catch (e) { + assert_equals(e.name, "WebIDLParseError"); + } + }, 'WebIDL2 parse method should bail on incorrect WebIDL'); + test(function() { + assert_equals(typeof WebIDL2.parse("interface Foo {};"), "object"); + }, 'WebIDL2 parse method should produce an AST for correct WebIDL'); + test(function () { + try { + let i = new IdlArray(); + i.add_untested_idls(`interface C {};`); + i.assert_throws('Anything', i => i.test()); + } catch (e) { + assert_true(e instanceof IdlHarnessError); + } + }, `assert_throws should throw if no IdlHarnessError thrown`); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/unit/exceptional-cases-timeouts.html b/testing/web-platform/tests/resources/test/tests/unit/exceptional-cases-timeouts.html new file mode 100644 index 0000000000..760ac7154f --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/exceptional-cases-timeouts.html @@ -0,0 +1,120 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <meta name="timeout" content="long"> + <script src="/resources/testharness.js"></script> + <title>Exceptional cases - timeouts</title> +</head> +<body> +<p> + The tests in this file are executed in parallel to avoid exceeding the "long" + timeout duration. +</p> +<script> +function makeTest(...bodies) { + const closeScript = '<' + '/script>'; + let src = ` +<!DOCTYPE HTML> +<html> +<head> +<title>Document title</title> +<script src="/resources/testharness.js?${Math.random()}">${closeScript} +</head> + +<body> +<div id="log"></div>`; + bodies.forEach((body) => { + src += '<script>(' + body + ')();' + closeScript; + }); + + const iframe = document.createElement('iframe'); + + document.body.appendChild(iframe); + iframe.contentDocument.write(src); + + return new Promise((resolve) => { + window.addEventListener('message', function onMessage(e) { + if (e.source !== iframe.contentWindow) { + return; + } + if (!e.data || e.data.type !=='complete') { + return; + } + window.removeEventListener('message', onMessage); + resolve(e.data); + }); + + iframe.contentDocument.close(); + }).then(({ tests, status }) => { + const summary = { + harness: getEnumProp(status, status.status), + tests: {} + }; + + tests.forEach((test) => { + summary.tests[test.name] = getEnumProp(test, test.status); + }); + + return summary; + }); +} + +function getEnumProp(object, value) { + for (let property in object) { + if (!/^[A-Z]+$/.test(property)) { + continue; + } + + if (object[property] === value) { + return property; + } + } +} + +(() => { + window.asyncTestCleanupCount1 = 0; + const nestedTest = makeTest( + () => { + async_test((t) => { + t.add_cleanup(() => window.parent.asyncTestCleanupCount1 += 1); + setTimeout(() => { + throw new Error('this error is expected'); + }); + }, 'test'); + } + ); + promise_test(() => { + return nestedTest.then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.test, 'TIMEOUT'); + assert_equals(window.asyncTestCleanupCount1, 1); + }); + }, 'uncaught exception during async_test which times out'); +})(); + +(() => { + window.promiseTestCleanupCount2 = 0; + const nestedTest = makeTest( + () => { + promise_test((t) => { + t.add_cleanup(() => window.parent.promiseTestCleanupCount2 += 1); + setTimeout(() => { + throw new Error('this error is expected'); + }); + + return new Promise(() => {}); + }, 'test'); + } + ); + promise_test(() => { + return nestedTest.then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.test, 'TIMEOUT'); + assert_equals(window.promiseTestCleanupCount2, 1); + }); + }, 'uncaught exception during promise_test which times out'); +})(); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/unit/exceptional-cases.html b/testing/web-platform/tests/resources/test/tests/unit/exceptional-cases.html new file mode 100644 index 0000000000..4054d0311d --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/exceptional-cases.html @@ -0,0 +1,392 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <meta name="timeout" content="long"> + <script src="/resources/testharness.js"></script> + <title>Exceptional cases</title> +</head> +<body> +<script> +function makeTest(...bodies) { + const closeScript = '<' + '/script>'; + let src = ` +<!DOCTYPE HTML> +<html> +<head> +<title>Document title</title> +<script src="/resources/testharness.js?${Math.random()}">${closeScript} +</head> + +<body> +<div id="log"></div>`; + bodies.forEach((body) => { + src += '<script>(' + body + ')();' + closeScript; + }); + + const iframe = document.createElement('iframe'); + + document.body.appendChild(iframe); + iframe.contentDocument.write(src); + + return new Promise((resolve) => { + window.addEventListener('message', function onMessage(e) { + if (e.source !== iframe.contentWindow) { + return; + } + if (!e.data || e.data.type !=='complete') { + return; + } + window.removeEventListener('message', onMessage); + resolve(e.data); + }); + + iframe.contentDocument.close(); + }).then(({ tests, status }) => { + const summary = { + harness: getEnumProp(status, status.status), + tests: {} + }; + + tests.forEach((test) => { + summary.tests[test.name] = getEnumProp(test, test.status); + }); + + return summary; + }); +} + +function getEnumProp(object, value) { + for (let property in object) { + if (!/^[A-Z]+$/.test(property)) { + continue; + } + + if (object[property] === value) { + return property; + } + } +} + +promise_test(() => { + return makeTest( + () => { done(); } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_array_equals(Object.keys(tests), []); + }); +}, 'completion signaled before testing begins'); + +promise_test(() => { + return makeTest( + () => { assert_true(true); done(); } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_array_equals(Object.keys(tests), []); + }); +}, 'passing assertion before testing begins'); + +promise_test(() => { + return makeTest( + () => { assert_false(true); } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_array_equals(Object.keys(tests), []); + }); +}, 'failing assertion before testing begins'); + +promise_test(() => { + return makeTest( + () => { throw new Error('this error is expected'); } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_array_equals(Object.keys(tests), []); + }); +}, 'uncaught exception before testing begins'); + +promise_test(() => { + return makeTest( + () => { + setup({ allow_uncaught_exception: true }); + throw new Error('this error is expected'); + }, + () => { + test(function() {}, 'a'); + test(function() {}, 'b'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests.a, 'PASS'); + assert_equals(tests.b, 'PASS'); + }); +}, 'uncaught exception with subsequent subtest'); + +promise_test(() => { + return makeTest( + () => { + async_test((t) => { + setTimeout(() => { + setTimeout(() => t.done(), 0); + async_test((t) => { setTimeout(t.done.bind(t), 0); }, 'after'); + throw new Error('this error is expected'); + }, 0); + }, 'during'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.during, 'PASS'); + assert_equals(tests.after, 'PASS'); + }); +}, 'uncaught exception during async_test'); + +promise_test(() => { + return makeTest( + () => { + promise_test(() => { + return new Promise((resolve) => { + setTimeout(() => { + resolve(); + promise_test(() => Promise.resolve(), 'after'); + throw new Error('this error is expected'); + }, 0); + }); + }, 'during'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.during, 'PASS'); + assert_equals(tests.after, 'PASS'); + }); +}, 'uncaught exception during promise_test'); + +promise_test(() => { + return makeTest( + () => { test(() => {}, 'before'); }, + () => { throw new Error('this error is expected'); }, + () => { test(() => {}, 'after'); } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.before, 'PASS'); + assert_equals(tests.after, 'PASS'); + }); +}, 'uncaught exception between tests'); + +promise_test(() => { + return makeTest( + () => { promise_test(() => Promise.resolve(), 'before'); }, + () => { throw new Error('this error is expected'); }, + () => { promise_test(() => Promise.resolve(), 'after'); } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.before, 'PASS'); + assert_equals(tests.after, 'PASS'); + }); +}, 'uncaught exception between promise_tests'); + + +// This feature of testharness.js is only observable in browsers which +// implement the `unhandledrejection` event. +if ('onunhandledrejection' in window) { + + promise_test(() => { + return makeTest( + () => { Promise.reject(new Error('this error is expected')); } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_array_equals(Object.keys(tests), []); + }); + }, 'unhandled rejection before testing begins'); + + promise_test(() => { + return makeTest( + () => { + async_test((t) => { + Promise.reject(new Error('this error is expected')); + + window.addEventListener('unhandledrejection', () => { + setTimeout(() => t.done(), 0); + async_test((t) => { setTimeout(t.done.bind(t), 0); }, 'after'); + t.done(); + }); + }, 'during'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.during, 'PASS'); + assert_equals(tests.after, 'PASS'); + }); + }, 'unhandled rejection during async_test'); + + promise_test(() => { + return makeTest( + () => { + promise_test(() => { + return new Promise((resolve) => { + Promise.reject(new Error('this error is expected')); + + window.addEventListener('unhandledrejection', () => { + resolve(); + promise_test(() => Promise.resolve(), 'after'); + throw new Error('this error is expected'); + }, 0); + }); + }, 'during'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.during, 'PASS'); + assert_equals(tests.after, 'PASS'); + }); + }, 'unhandled rejection during promise_test'); + + promise_test(() => { + return makeTest( + () => { + setup({ explicit_done: true }); + test(() => {}, 'before'); + Promise.reject(new Error('this error is expected')); + window.addEventListener('unhandledrejection', () => { + test(() => {}, 'after'); + done(); + }); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.before, 'PASS'); + assert_true('after' in tests); + }); + }, 'unhandled rejection between tests'); + + promise_test(() => { + return makeTest( + () => { + setup({ explicit_done: true }); + async_test((t) => { setTimeout(t.done.bind(t), 0); }, 'before'); + Promise.reject(new Error('this error is expected')); + window.addEventListener('unhandledrejection', () => { + async_test((t) => { setTimeout(t.done.bind(t), 0); }, 'after'); + done(); + }); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.before, 'PASS'); + assert_equals(tests.after, 'PASS'); + }); + }, 'unhandled rejection between async_tests'); + + promise_test(() => { + return makeTest( + () => { + setup({ explicit_done: true }); + promise_test(() => Promise.resolve(), 'before'); + Promise.reject(new Error('this error is expected')); + window.addEventListener('unhandledrejection', () => { + promise_test(() => Promise.resolve(), 'after'); + done(); + }); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.before, 'PASS'); + assert_true('after' in tests); + }); + }, 'unhandled rejection between promise_tests'); + + promise_test(() => { + return makeTest( + () => { + test((t) => { + t.add_cleanup(() => { throw new Error('this error is expected'); }); + }, 'during'); + test((t) => {}, 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.during, 'PASS'); + assert_equals(tests.after, 'NOTRUN'); + }); + }, 'exception in `add_cleanup` of a test'); + +} + + +promise_test(() => { + return makeTest( + () => { + setup({explicit_done: true}); + window.addEventListener('DOMContentLoaded', () => { + async_test((t) => { + t.add_cleanup(() => { + setTimeout(() => { + async_test((t) => t.done(), 'after'); + done(); + }, 0); + throw new Error('this error is expected'); + }); + setTimeout(t.done.bind(t), 0); + }, 'during'); + }); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.during, 'PASS'); + assert_equals(tests.after, 'NOTRUN'); + }); +}, 'exception in `add_cleanup` of an async_test'); + +promise_test(() => { + return makeTest( + () => { + promise_test((t) => { + t.add_cleanup(() => { throw new Error('this error is expected'); }); + return Promise.resolve(); + }, 'test'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.test, 'PASS'); + }); +}, 'exception in `add_cleanup` of a promise_test'); + +promise_test(() => { + return makeTest( + () => { + promise_test((t) => { + t.step(() => { + throw new Error('this error is expected'); + }); + }, 'test'); + async_test((t) => t.done(), 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests.test, 'FAIL'); + assert_equals(tests.after, 'PASS'); + }); +}, 'exception in `step` of an async_test'); + +promise_test(() => { + return makeTest( + () => { + promise_test((t) => { + t.step(() => { + throw new Error('this error is expected'); + }); + + return new Promise(() => {}); + }, 'test'); + + // This following test should be run to completion despite the fact + // that the promise returned by the previous test never resolves. + promise_test((t) => Promise.resolve(), 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests.test, 'FAIL'); + assert_equals(tests.after, 'PASS'); + }); +}, 'exception in `step` of a promise_test'); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/unit/format-value.html b/testing/web-platform/tests/resources/test/tests/unit/format-value.html new file mode 100644 index 0000000000..13d01b81f3 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/format-value.html @@ -0,0 +1,123 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>format_value utility function</title> + <meta charset="utf-8"> +</head> +<body> +<div id="log"></div> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +"use strict"; + +test(function() { + assert_equals(format_value(null), "null"); +}, "null"); + +test(function() { + assert_equals(format_value(undefined), "undefined"); +}, "undefined"); + +test(function() { + assert_equals(format_value(true), "true"); + assert_equals(format_value(false), "false"); +}, "boolean values"); + +test(function() { + assert_equals(format_value(0.4), "0.4"); + assert_equals(format_value(0), "0"); + assert_equals(format_value(-0), "-0"); +}, "number values"); + +test(function() { + assert_equals(format_value("a string"), "\"a string\""); + assert_equals(format_value("new\nline"), "\"new\\nline\""); +}, "string values"); + +test(function() { + var node = document.createElement("span"); + node.setAttribute("data-foo", "bar"); + assert_true( + /<span\b/i.test(format_value(node)), "element includes tag name" + ); + assert_true( + /data-foo=["']?bar["']?/i.test(format_value(node)), + "element includes attributes" + ); +}, "node value: element node"); + +test(function() { + var text = document.createTextNode("wpt"); + assert_equals(format_value(text), "Text node \"wpt\""); +}, "node value: text node"); + +test(function() { + var node = document.createProcessingInstruction("wpt1", "wpt2"); + assert_equals( + format_value(node), + "ProcessingInstruction node with target \"wpt1\" and data \"wpt2\"" + ); +}, "node value: ProcessingInstruction node"); + +test(function() { + var node = document.createComment("wpt"); + assert_equals(format_value(node), "Comment node <!--wpt-->"); +}, "node value: comment node"); + +test(function() { + var node = document.implementation.createDocument( + "application/xhtml+xml", "", null + ); + + assert_equals(format_value(node), "Document node with 0 children"); + + node.appendChild(document.createElement('html')); + + assert_equals(format_value(node), "Document node with 1 child"); +}, "node value: document node"); + +test(function() { + var node = document.implementation.createDocumentType("foo", "baz", "baz"); + + assert_equals(format_value(node), "DocumentType node"); +}, "node value: DocumentType node"); + +test(function() { + var node = document.createDocumentFragment(); + + assert_equals(format_value(node), "DocumentFragment node with 0 children"); + + node.appendChild(document.createElement("span")); + + assert_equals(format_value(node), "DocumentFragment node with 1 child"); + + node.appendChild(document.createElement("span")); + + assert_equals(format_value(node), "DocumentFragment node with 2 children"); +}, "node value: DocumentFragment node"); + +test(function() { + assert_equals(format_value(Symbol("wpt")), "symbol \"Symbol(wpt)\""); +}, "symbol value"); + +test(function() { + assert_equals(format_value([]), "[]"); + assert_equals(format_value(["one"]), "[\"one\"]"); + assert_equals(format_value(["one", "two"]), "[\"one\", \"two\"]"); +}, "array values"); + +test(function() { + var obj = { + toString: function() { + throw "wpt"; + } + }; + + assert_equals( + format_value(obj), "[stringifying object threw wpt with type string]" + ); +}, "object value with faulty `toString`"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/unit/helpers.js b/testing/web-platform/tests/resources/test/tests/unit/helpers.js new file mode 100644 index 0000000000..ca378a27c9 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/helpers.js @@ -0,0 +1,21 @@ +// Helper for testing assertion failure cases for a testharness.js API +// +// The `assert_throws_*` functions cannot be used for this purpose because they +// always fail in response to AssertionError exceptions, even when this is +// expressed as the expected error. +function test_failure(fn, name) { + test(function() { + try { + fn(); + } catch (err) { + if (err instanceof AssertionError) { + return; + } + throw new AssertionError('Expected an AssertionError, but' + err); + } + throw new AssertionError( + 'Expected an AssertionError, but no error was thrown' + ); + }, name); +} + diff --git a/testing/web-platform/tests/resources/test/tests/unit/late-test.html b/testing/web-platform/tests/resources/test/tests/unit/late-test.html new file mode 100644 index 0000000000..c9f8ec61fe --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/late-test.html @@ -0,0 +1,56 @@ +<!DOCTYPE HTML> +<html> +<head> +<title>Test declared after harness completion</title> +</head> +<body> +<div id="log"></div> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<p>This test simulates an automated test running scenario, where the test +results emitted by testharness.js may be interpreted after some delay. It is +intended to demonstrate that in such cases, any additional tests which are +executed during that delay are included in the dataset.</p> + +<p>Although these "late tests" are likely an indication of a mistake in test +design, they are also recorded. Previously, "late tests" were ignored. +This test changed to assert "late tests" were no longer ignored after +https://github.com/web-platform-tests/wpt/pull/38806 was introduced.</p> +<script> +async_test(function(t) { + var source = [ + "<div id='log'></div>", + "<script src='/resources/testharness.js'></" + "script>", + "<script src='/resources/testharnessreport.js'></" + "script>", + "<script>", + "parent.childReady(window);", + "setup({ explicit_done: true });", + "test(function() {}, 'acceptable test');", + "onload = function() {", + " done();", + " test(function() {}, 'test registered in onload handler');", + "};", + "</" + "script>" + ].join("\n"); + var iframe = document.createElement("iframe"); + + document.body.appendChild(iframe); + window.childReady = t.step_func(function(childWindow) { + childWindow.add_completion_callback(t.step_func(function(tests, status) { + t.step_timeout(t.step_func(function() { + assert_equals(tests.length, 2); + assert_equals(tests[0].name, "acceptable test"); + assert_equals(tests[1].name, "test registered in onload handler"); + assert_equals(status.status, status.OK); + t.done(); + }), 0); + })); + }); + + iframe.contentDocument.open(); + iframe.contentDocument.write(source); + iframe.contentDocument.close(); +}); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/unit/promise_setup-timeout.html b/testing/web-platform/tests/resources/test/tests/unit/promise_setup-timeout.html new file mode 100644 index 0000000000..c4947feef4 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/promise_setup-timeout.html @@ -0,0 +1,28 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <meta name="timeout" content="long"> + <script src="/resources/testharness.js"></script> + <script src="../../nested-testharness.js"></script> + <title>promise_setup - timeout</title> +</head> +<body> +<script> +'use strict'; +promise_test(() => { + return makeTest( + () => { + test(() => {}, 'before'); + promise_setup(() => new Promise(() => {})); + promise_test(() => Promise.resolve(), 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'TIMEOUT'); + assert_equals(tests.before, 'PASS'); + assert_equals(tests.after, 'NOTRUN'); + }); +}, 'timeout when returned promise does not settle'); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/unit/promise_setup.html b/testing/web-platform/tests/resources/test/tests/unit/promise_setup.html new file mode 100644 index 0000000000..2abb10a476 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/promise_setup.html @@ -0,0 +1,333 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <script src="/resources/testharness.js"></script> + <script src="../../nested-testharness.js"></script> + <title>promise_setup</title> +</head> +<body> +<script> +'use strict'; +promise_test(() => { + return makeTest( + () => { + // Ensure that the harness error is the result of explicit error + // handling + setup({ allow_uncaught_exception: true }); + + test(() => {}, 'before'); + promise_setup({}); + promise_test(() => Promise.resolve(), 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.before, 'PASS'); + assert_equals(tests.after, undefined); + }); +}, 'Error when no function provided'); + +promise_test(() => { + return makeTest( + () => { + test(() => {}, 'before'); + promise_setup(() => Promise.resolve(), {}); + promise_test(() => Promise.resolve(), 'after'); + throw new Error('this error is expected'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.before, 'PASS'); + assert_equals(tests.after, 'PASS'); + }); +}, 'Does not apply unspecified configuration properties'); + +promise_test(() => { + return makeTest( + () => { + var properties = { + allow_uncaught_exception: true + }; + test(() => {}, 'before'); + promise_setup(() => Promise.resolve(), properties); + promise_test(() => Promise.resolve(), 'after'); + throw new Error('this error is expected'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.before, 'PASS'); + assert_equals(tests.after, 'PASS'); + }); +}, 'Ignores configuration properties when some tests have already run'); + +promise_test(() => { + return makeTest( + () => { + var properties = { + allow_uncaught_exception: true + }; + promise_setup(() => Promise.resolve(), properties); + promise_test(() => { + return new Promise((resolve) => { + setTimeout(() => { + resolve(); + throw new Error('this error is expected'); + }); + }); + }, 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests.after, 'PASS'); + }); +}, 'Honors configuration properties'); + +promise_test(() => { + return makeTest( + () => { + // Ensure that the harness error is the result of explicit error + // handling + setup({ allow_uncaught_exception: true }); + + test(() => {}, 'before'); + promise_setup(() => { throw new Error('this error is expected'); }); + promise_test(() => Promise.resolve(), 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.before, 'PASS'); + assert_equals(tests.after, 'NOTRUN'); + }); +}, 'Error for synchronous exceptions'); + +promise_test(() => { + return makeTest( + () => { + // Ensure that the harness error is the result of explicit error + // handling + setup({ allow_uncaught_exception: true }); + + test(() => {}, 'before'); + promise_setup(() => undefined); + promise_test(() => Promise.resolve(), 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.before, 'PASS'); + assert_equals(tests.after, 'NOTRUN'); + }); +}, 'Error for missing return value'); + +promise_test(() => { + return makeTest( + () => { + // Ensure that the harness error is the result of explicit error + // handling + setup({ allow_uncaught_exception: true }); + + test(() => {}, 'before'); + var noThen = Promise.resolve(); + noThen.then = undefined; + promise_setup(() => noThen); + promise_test(() => Promise.resolve(), 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.before, 'PASS'); + assert_equals(tests.after, 'NOTRUN'); + }); +}, 'Error for non-thenable return value'); + +promise_test(() => { + return makeTest( + () => { + // Ensure that the harness error is the result of explicit error + // handling + setup({ allow_uncaught_exception: true }); + + test(() => {}, 'before'); + var poisonedThen = { + get then() { + throw new Error('this error is expected'); + } + }; + promise_setup(() => poisonedThen); + promise_test(() => Promise.resolve(), 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.before, 'PASS'); + assert_equals(tests.after, 'NOTRUN'); + }); +}, 'Error for "poisoned" `then` property'); + +promise_test(() => { + return makeTest( + () => { + // Ensure that the harness error is the result of explicit error + // handling + setup({ allow_uncaught_exception: true }); + + test(() => {}, 'before'); + var badThen = { + then() { + throw new Error('this error is expected'); + } + }; + promise_setup(() => badThen); + promise_test(() => Promise.resolve(), 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.before, 'PASS'); + assert_equals(tests.after, 'NOTRUN'); + }); +}, 'Error for synchronous error from `then` method'); + +promise_test(() => { + return makeTest( + () => { + // Ensure that the harness error is the result of explicit error + // handling + setup({ allow_uncaught_exception: true }); + + test(() => {}, 'before'); + promise_setup(() => Promise.resolve()); + test(() => {}, 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.before, 'PASS'); + assert_equals(tests.after, undefined); + }); +}, 'Error for subsequent invocation of `test`'); + +promise_test(() => { + return makeTest( + () => { + // Ensure that the harness error is the result of explicit error + // handling + setup({ allow_uncaught_exception: true }); + + test(() => {}, 'before'); + promise_setup(() => Promise.resolve()); + async_test((t) => t.done(), 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.before, 'PASS'); + assert_equals(tests.after, undefined); + }); +}, 'Error for subsequent invocation of `async_test`'); + +promise_test(() => { + return makeTest( + () => { + // Ensure that the harness error is the result of explicit error + // handling + setup({ allow_uncaught_exception: true }); + + test(() => {}, 'before'); + promise_setup(() => Promise.reject()); + promise_test(() => Promise.resolve(), 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.before, 'PASS'); + assert_equals(tests.after, 'NOTRUN'); + }); +}, 'Error for rejected promise'); + +promise_test(() => { + var expected_sequence = [ + 'test body', + 'promise_setup begin', + 'promise_setup end', + 'promise_test body' + ]; + var actual_sequence = window.actual_sequence = []; + + return makeTest( + () => { + test(() => { parent.actual_sequence.push('test body'); }, 'before'); + promise_setup(() => { + parent.actual_sequence.push('promise_setup begin'); + + return Promise.resolve() + .then(() => new Promise((resolve) => setTimeout(resolve, 300))) + .then(() => parent.actual_sequence.push('promise_setup end')); + }); + promise_test(() => { + parent.actual_sequence.push('promise_test body'); + return Promise.resolve(); + }, 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests.before, 'PASS'); + assert_equals(tests.after, 'PASS'); + assert_array_equals(actual_sequence, expected_sequence); + }); +}, 'Waits for promise to settle'); + +promise_test(() => { + var expected_sequence = [ + 'promise_test 1 begin', + 'promise_test 1 end', + 'promise_setup begin', + 'promise_setup end', + 'promise_test 2 body' + ]; + var actual_sequence = window.actual_sequence = []; + + return makeTest( + () => { + promise_test((t) => { + parent.actual_sequence.push('promise_test 1 begin'); + + return Promise.resolve() + .then(() => new Promise((resolve) => t.step_timeout(resolve, 300))) + .then(() => parent.actual_sequence.push('promise_test 1 end')); + }, 'before'); + promise_setup(() => { + parent.actual_sequence.push('promise_setup begin'); + + return Promise.resolve() + .then(() => new Promise((resolve) => setTimeout(resolve, 300))) + .then(() => parent.actual_sequence.push('promise_setup end')); + }); + promise_test(() => { + parent.actual_sequence.push('promise_test 2 body'); + return Promise.resolve(); + }, 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests.before, 'PASS'); + assert_equals(tests.after, 'PASS'); + assert_array_equals(actual_sequence, expected_sequence); + }); +}, 'Waits for existing promise_test to complete'); + +promise_test(() => { + return makeTest( + () => { + var properties = { allow_uncaught_exception: true }; + promise_test(() => { + return new Promise((resolve) => { + setTimeout(() => { + resolve(); + throw new Error('this error is expected'); + }); + }); + }, 'before'); + promise_setup(() => Promise.resolve(), properties); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'ERROR'); + assert_equals(tests.before, 'PASS'); + }); +}, 'Defers application of setup properties'); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/unit/single_test.html b/testing/web-platform/tests/resources/test/tests/unit/single_test.html new file mode 100644 index 0000000000..ff766e66ce --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/single_test.html @@ -0,0 +1,94 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <meta name="timeout" content="long"> + <script src="/resources/testharness.js"></script> + <script src="../../nested-testharness.js"></script> + <title>single_test</title> +</head> +<body> +<script> +promise_test(() => { + return makeTest( + () => { + setup({ single_test: true }); + done(); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests['Document title'], 'PASS'); + }); +}, 'Expected usage'); + +promise_test(() => { + return makeTest( + () => { + setup({ single_test: true }); + throw new Error('this error is expected'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests['Document title'], 'FAIL'); + }); +}, 'Uncaught exception'); + +promise_test(() => { + return makeTest( + () => { + setup({ single_test: true }); + Promise.reject(new Error('this error is expected')); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests['Document title'], 'FAIL'); + }); +}, 'Unhandled rejection'); + +promise_test(() => { + return makeTest( + () => { + setup({ single_test: true }); + test(function() {}, 'sync test'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests['Document title'], 'FAIL'); + assert_equals( + Object.keys(tests).length, 1, 'no additional subtests created' + ); + }); +}, 'Erroneous usage: subtest declaration (synchronous test)'); + +promise_test(() => { + return makeTest( + () => { + setup({ single_test: true }); + async_test(function(t) { t.done(); }, 'async test'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests['Document title'], 'FAIL'); + assert_equals( + Object.keys(tests).length, 1, 'no additional subtests created' + ); + }); +}, 'Erroneous usage: subtest declaration (asynchronous test)'); + +promise_test(() => { + return makeTest( + () => { + setup({ single_test: true }); + promise_test(function() { return Promise.resolve(); }, 'promise test'); + } + ).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests['Document title'], 'FAIL'); + assert_equals( + Object.keys(tests).length, 1, 'no additional subtests created' + ); + }); +}, 'Erroneous usage: subtest declaration (promise test)'); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/unit/test-return-restrictions.html b/testing/web-platform/tests/resources/test/tests/unit/test-return-restrictions.html new file mode 100644 index 0000000000..0295c5214d --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/test-return-restrictions.html @@ -0,0 +1,156 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <script src="/resources/testharness.js"></script> + <title>Restrictions on return value from `test`</title> +</head> +<body> +<script> +function makeTest(...bodies) { + const closeScript = '<' + '/script>'; + let src = ` +<!DOCTYPE HTML> +<html> +<head> +<title>Document title</title> +<script src="/resources/testharness.js?${Math.random()}">${closeScript} +</head> + +<body> +<div id="log"></div>`; + bodies.forEach((body) => { + src += '<script>(' + body + ')();' + closeScript; + }); + + const iframe = document.createElement('iframe'); + + document.body.appendChild(iframe); + iframe.contentDocument.write(src); + + return new Promise((resolve) => { + window.addEventListener('message', function onMessage(e) { + if (e.source !== iframe.contentWindow) { + return; + } + if (!e.data || e.data.type !=='complete') { + return; + } + window.removeEventListener('message', onMessage); + resolve(e.data); + }); + + iframe.contentDocument.close(); + }).then(({ tests, status }) => { + const summary = { + harness: { + status: getEnumProp(status, status.status), + message: status.message + }, + tests: {} + }; + + tests.forEach((test) => { + summary.tests[test.name] = getEnumProp(test, test.status); + }); + + return summary; + }); +} + +function getEnumProp(object, value) { + for (let property in object) { + if (!/^[A-Z]+$/.test(property)) { + continue; + } + + if (object[property] === value) { + return property; + } + } +} + +promise_test(() => { + return makeTest( + () => { + test(() => undefined, 'before'); + test(() => null, 'null'); + test(() => undefined, 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness.status, 'ERROR'); + assert_equals( + harness.message, + 'Test named "null" passed a function to `test` that returned a value.' + ); + assert_equals(tests.before, 'PASS'); + assert_equals(tests.null, 'PASS'); + assert_equals(tests.after, 'PASS'); + }); +}, 'test returning `null`'); + +promise_test(() => { + return makeTest( + () => { + test(() => undefined, 'before'); + test(() => ({}), 'object'); + test(() => undefined, 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness.status, 'ERROR'); + assert_equals( + harness.message, + 'Test named "object" passed a function to `test` that returned a value.' + ); + assert_equals(tests.before, 'PASS'); + assert_equals(tests.object, 'PASS'); + assert_equals(tests.after, 'PASS'); + }); +}, 'test returning an ordinary object'); + +promise_test(() => { + return makeTest( + () => { + test(() => undefined, 'before'); + test(() => Promise.resolve(5), 'thenable'); + test(() => undefined, 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness.status, 'ERROR'); + assert_equals( + harness.message, + 'Test named "thenable" passed a function to `test` that returned a value. ' + + 'Consider using `promise_test` instead when using Promises or async/await.' + ); + assert_equals(tests.before, 'PASS'); + assert_equals(tests.thenable, 'PASS'); + assert_equals(tests.after, 'PASS'); + }); +}, 'test returning a thenable object'); + +promise_test(() => { + return makeTest( + () => { + test(() => undefined, 'before'); + test(() => { + const iframe = document.createElement('iframe'); + iframe.setAttribute('sandbox', ''); + document.body.appendChild(iframe); + return iframe.contentWindow; + }, 'restricted'); + test(() => undefined, 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness.status, 'ERROR'); + assert_equals( + harness.message, + 'Test named "restricted" passed a function to `test` that returned a value.' + ); + assert_equals(tests.before, 'PASS'); + assert_equals(tests.restricted, 'PASS'); + assert_equals(tests.after, 'PASS'); + }); +}, 'test returning a restricted object'); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/unit/throwing-assertions.html b/testing/web-platform/tests/resources/test/tests/unit/throwing-assertions.html new file mode 100644 index 0000000000..a36a56043c --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/throwing-assertions.html @@ -0,0 +1,268 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <meta name="timeout" content="long"> + <script src="/resources/testharness.js"></script> + <title>Test the methods that make assertions about exceptions</title> +</head> +<body> +<script> +function makeTest(...bodies) { + const closeScript = '<' + '/script>'; + let src = ` +<!DOCTYPE HTML> +<html> +<head> +<title>Document title</title> +<script src="/resources/testharness.js?${Math.random()}">${closeScript} +</head> + +<body> +<div id="log"></div>`; + bodies.forEach((body) => { + src += '<script>(' + body + ')();' + closeScript; + }); + + const iframe = document.createElement('iframe'); + + document.body.appendChild(iframe); + iframe.contentDocument.write(src); + + return new Promise((resolve) => { + window.addEventListener('message', function onMessage(e) { + if (e.source !== iframe.contentWindow) { + return; + } + if (!e.data || e.data.type !=='complete') { + return; + } + window.removeEventListener('message', onMessage); + resolve(e.data); + }); + + iframe.contentDocument.close(); + }).then(({ tests, status }) => { + const summary = { + harness: getEnumProp(status, status.status), + tests: {} + }; + + tests.forEach((test) => { + summary.tests[test.name] = getEnumProp(test, test.status); + }); + + return summary; + }); +} + +function getEnumProp(object, value) { + for (let property in object) { + if (!/^[A-Z]+$/.test(property)) { + continue; + } + + if (object[property] === value) { + return property; + } + } +} + +promise_test(() => { + return makeTest(() => { + test(() => { + assert_throws_js(TypeError, () => { throw new TypeError(); }); + }); + }).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests['Document title'], 'PASS'); + }); +}, 'assert_throws_js on a TypeError'); + +promise_test(() => { + return makeTest(() => { + test(() => { + assert_throws_js(RangeError, () => { throw new RangeError(); }); + }); + }).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests['Document title'], 'PASS'); + }); +}, 'assert_throws_js on a RangeError'); + +promise_test(() => { + return makeTest(() => { + test(() => { + assert_throws_js(TypeError, () => { throw new RangeError(); }); + }); + }).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests['Document title'], 'FAIL'); + }); +}, 'assert_throws_js on a TypeError when RangeError is thrown'); + +promise_test(() => { + return makeTest(() => { + test(() => { + assert_throws_js(Error, () => { throw new TypeError(); }); + }); + }).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests['Document title'], 'FAIL'); + }); +}, 'assert_throws_js on an Error when TypeError is thrown'); + +promise_test(() => { + return makeTest(() => { + test(() => { + assert_throws_js(Error, + () => { throw new DOMException("hello", "Error"); }); + }); + }).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests['Document title'], 'FAIL'); + }); +}, 'assert_throws_js on an Error when a DOMException is thrown'); + +promise_test(() => { + return makeTest(() => { + test(() => { + assert_throws_js(SyntaxError, + () => { throw new DOMException("hey", "SyntaxError"); }); + }); + }).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests['Document title'], 'FAIL'); + }); +}, 'assert_throws_js on a SyntaxError when a DOMException is thrown'); + +promise_test(() => { + return makeTest(() => { + test(() => { + assert_throws_dom("SyntaxError", + () => { throw new DOMException("x", "SyntaxError"); }); + }); + }).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests['Document title'], 'PASS'); + }); +}, 'assert_throws_dom basic sanity'); + +promise_test(() => { + return makeTest(() => { + test(() => { + assert_throws_dom(12, + () => { throw new DOMException("x", "SyntaxError"); }); + }); + }).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests['Document title'], 'PASS'); + }); +}, 'assert_throws_dom with numeric code'); + +promise_test(() => { + return makeTest(() => { + test(() => { + assert_throws_dom("SYNTAX_ERR", + () => { throw new DOMException("x", "SyntaxError"); }); + }); + }).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests['Document title'], 'PASS'); + }); +}, 'assert_throws_dom with string name for code'); + +promise_test(() => { + return makeTest(() => { + test(() => { + assert_throws_dom("DataError", + () => { throw new DOMException("x", "DataError"); }); + }); + }).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests['Document title'], 'PASS'); + }); +}, 'assert_throws_dom for a code-less DOMException type'); + +promise_test(() => { + return makeTest(() => { + test(() => { + assert_throws_dom("NoSuchError", + () => { throw new DOMException("x", "NoSuchError"); }); + }); + }).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests['Document title'], 'FAIL'); + }); +}, 'assert_throws_dom for a nonexistent DOMException type'); + +promise_test(() => { + return makeTest(() => { + test(() => { + assert_throws_dom("SyntaxError", () => { throw new SyntaxError(); }); + }); + }).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests['Document title'], 'FAIL'); + }); +}, 'assert_throws_dom when a non-DOM exception is thrown'); + +promise_test(() => { + return makeTest(() => { + test(() => { + assert_throws_exactly(5, () => { throw 5; }); + }); + }).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests['Document title'], 'PASS'); + }); +}, 'assert_throws_exactly with number'); + +promise_test(() => { + return makeTest(() => { + test(() => { + assert_throws_exactly("foo", () => { throw "foo"; }); + }); + }).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests['Document title'], 'PASS'); + }); +}, 'assert_throws_exactly with string'); + +promise_test(() => { + return makeTest(() => { + test(() => { + assert_throws_exactly({}, () => { throw {}; }); + }); + }).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests['Document title'], 'FAIL'); + }); +}, 'assert_throws_exactly with different objects'); + +promise_test(() => { + return makeTest(() => { + test(() => { + var obj = {}; + assert_throws_exactly(obj, () => { throw obj; }); + }); + }).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests['Document title'], 'PASS'); + }); +}, 'assert_throws_exactly with same object'); + +promise_test(() => { + return makeTest(() => { + test(() => { + assert_throws_exactly(TypeError, () => { throw new TypeError; }); + }); + }).then(({harness, tests}) => { + assert_equals(harness, 'OK'); + assert_equals(tests['Document title'], 'FAIL'); + }); +}, 'assert_throws_exactly with bogus TypeError bits '); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tests/unit/unpaired-surrogates.html b/testing/web-platform/tests/resources/test/tests/unit/unpaired-surrogates.html new file mode 100644 index 0000000000..b232111326 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tests/unit/unpaired-surrogates.html @@ -0,0 +1,143 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <script src="/resources/testharness.js"></script> + <title>Restrictions on return value from `test`</title> +</head> +<body> +<script> +function makeTest(...bodies) { + const closeScript = '<' + '/script>'; + let src = ` +<!DOCTYPE HTML> +<html> +<head> +<title>Document title</title> +<script src="/resources/testharness.js?${Math.random()}">${closeScript} +</head> + +<body> +<div id="log"></div>`; + bodies.forEach((body) => { + src += '<script>(' + body + ')();' + closeScript; + }); + + const iframe = document.createElement('iframe'); + + document.body.appendChild(iframe); + iframe.contentDocument.write(src); + + return new Promise((resolve) => { + window.addEventListener('message', function onMessage(e) { + if (e.source !== iframe.contentWindow) { + return; + } + if (!e.data || e.data.type !=='complete') { + return; + } + window.removeEventListener('message', onMessage); + resolve(e.data); + }); + + iframe.contentDocument.close(); + }).then(({ tests, status }) => { + const summary = { + harness: { + status: getEnumProp(status, status.status), + message: status.message + }, + tests: {} + }; + + tests.forEach((test) => { + summary.tests[test.name] = getEnumProp(test, test.status); + }); + + return summary; + }); +} + +function getEnumProp(object, value) { + for (let property in object) { + if (!/^[A-Z]+$/.test(property)) { + continue; + } + + if (object[property] === value) { + return property; + } + } +} + +promise_test(() => { + return makeTest( + () => { + test(() => {}, 'before'); + test(() => {}, 'U+d7ff is not modified: \ud7ff'); + test(() => {}, 'U+e000 is not modified: \ue000'); + test(() => {}, 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness.status, 'OK'); + assert_equals(harness.message, null); + assert_equals(tests.before, 'PASS'); + assert_equals(tests['U+d7ff is not modified: \ud7ff'], 'PASS'); + assert_equals(tests['U+e000 is not modified: \ue000'], 'PASS'); + assert_equals(tests.after, 'PASS'); + }); +}, 'sub-test names which include valid code units'); + +promise_test(() => { + return makeTest( + () => { + test(() => {}, 'before'); + test(() => {}, 'U+d800U+dfff is not modified: \ud800\udfff'); + test(() => {}, 'U+dbffU+dc00 is not modified: \udbff\udc00'); + test(() => {}, 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness.status, 'OK'); + assert_equals(harness.message, null); + assert_equals(tests.before, 'PASS'); + assert_equals(tests['U+d800U+dfff is not modified: \ud800\udfff'], 'PASS'); + assert_equals(tests['U+dbffU+dc00 is not modified: \udbff\udc00'], 'PASS'); + assert_equals(tests.after, 'PASS'); + }); +}, 'sub-test names which include paired surrogates'); + +promise_test(() => { + return makeTest( + () => { + test(() => {}, 'before'); + test(() => {}, 'U+d800 must be sanitized: \ud800'); + test(() => {}, 'U+d800U+d801 must be sanitized: \ud800\ud801'); + test(() => {}, 'U+dfff must be sanitized: \udfff'); + test(() => {}, 'U+dc00U+d800U+dc00U+d800 must be sanitized: \udc00\ud800\udc00\ud800'); + test(() => {}, 'U+dbffU+dfffU+dfff must be sanitized: \udbff\udfff\udfff'); + test(() => {}, 'after'); + } + ).then(({harness, tests}) => { + assert_equals(harness.status, 'OK'); + assert_equals(harness.message, null); + assert_equals(tests.before, 'PASS'); + assert_equals(tests['U+d800 must be sanitized: U+d800'], 'PASS'); + assert_equals(tests['U+dfff must be sanitized: U+dfff'], 'PASS'); + assert_equals( + tests['U+d800U+d801 must be sanitized: U+d800U+d801'], + 'PASS' + ); + assert_equals( + tests['U+dc00U+d800U+dc00U+d800 must be sanitized: U+dc00\ud800\udc00U+d800'], + 'PASS' + ); + assert_equals( + tests['U+dbffU+dfffU+dfff must be sanitized: \udbff\udfffU+dfff'], + 'PASS' + ); + assert_equals(tests.after, 'PASS'); + }); +}, 'sub-test names which include unpaired surrogates'); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/resources/test/tox.ini b/testing/web-platform/tests/resources/test/tox.ini new file mode 100644 index 0000000000..12013a1a70 --- /dev/null +++ b/testing/web-platform/tests/resources/test/tox.ini @@ -0,0 +1,13 @@ +[tox] +envlist = py37,py38,py39,py310,py311 +skipsdist=True + +[testenv] +passenv=DISPLAY # Necessary for the spawned GeckoDriver process to connect to + # the appropriate display. + +deps = + -r{toxinidir}/../../tools/requirements_pytest.txt + -r{toxinidir}/requirements.txt + +commands = pytest -vv {posargs} diff --git a/testing/web-platform/tests/resources/test/wptserver.py b/testing/web-platform/tests/resources/test/wptserver.py new file mode 100644 index 0000000000..1f913dd96d --- /dev/null +++ b/testing/web-platform/tests/resources/test/wptserver.py @@ -0,0 +1,58 @@ +import logging +import os +import subprocess +import time +import sys +import urllib + + +class WPTServer(object): + def __init__(self, wpt_root): + self.logger = logging.getLogger() + self.wpt_root = wpt_root + + # This is a terrible hack to get the default config of wptserve. + sys.path.insert(0, os.path.join(wpt_root, "tools")) + from serve.serve import build_config + with build_config(self.logger) as config: + self.host = config["browser_host"] + self.http_port = config["ports"]["http"][0] + self.https_port = config["ports"]["https"][0] + + self.base_url = 'http://%s:%s' % (self.host, self.http_port) + self.https_base_url = 'https://%s:%s' % (self.host, self.https_port) + + def start(self, ssl_context): + self.devnull = open(os.devnull, 'w') + wptserve_cmd = [os.path.join(self.wpt_root, 'wpt'), 'serve'] + if sys.executable: + wptserve_cmd[0:0] = [sys.executable] + self.logger.info('Executing %s' % ' '.join(wptserve_cmd)) + self.proc = subprocess.Popen( + wptserve_cmd, + stderr=self.devnull, + cwd=self.wpt_root) + + for retry in range(5): + # Exponential backoff. + time.sleep(2 ** retry) + exit_code = self.proc.poll() + if exit_code != None: + logging.warning('Command "%s" exited with %s', ' '.join(wptserve_cmd), exit_code) + break + try: + urllib.request.urlopen(self.base_url, timeout=1) + urllib.request.urlopen(self.https_base_url, timeout=1, context=ssl_context) + return + except urllib.error.URLError: + pass + + raise Exception('Could not start wptserve on %s' % self.base_url) + + def stop(self): + self.proc.terminate() + self.proc.wait() + self.devnull.close() + + def url(self, abs_path): + return self.https_base_url + '/' + os.path.relpath(abs_path, self.wpt_root) |