Source code

Revision control

Copy as Markdown

Other Tools

# JavaScript Tests (testharness.js)
JavaScript tests are the correct type of test to write in any
situation where you are not specifically interested in the rendering
of a page, and where human interaction isn't required; these tests are
written in JavaScript using a framework called `testharness.js`.
A high-level overview is provided below and more information can be found here:
* [testharness.js Documentation](testharness-api.md) — An introduction
to the library and a detailed API reference. [The tutorial on writing a
testharness.js test](testharness-tutorial) provides a concise guide to writing
a test — a good place to start for newcomers to the project.
* [testdriver.js Automation](testdriver.md) — Automating end user actions, such as moving or
clicking a mouse. See also the
[testdriver.js extension tutorial](testdriver-extension-tutorial.md) for adding new commands.
* [idlharness.js](idlharness.md) — A library for testing
IDL interfaces using `testharness.js`.
* [Message Channels](channels.md) - A way to communicate between
different globals, including window globals not in the same
browsing context group.
* [Server features](server-features.md) - Advanced testing features
that are commonly used with JavaScript tests.
See also the [general guidelines](general-guidelines.md) for all test types.
## Window tests
### Without HTML boilerplate (`.window.js`)
Create a JavaScript file whose filename ends in `.window.js` to have the necessary HTML boilerplate
generated for you at `.window.html`. I.e., for `test.window.js` the server will ensure
`test.window.html` is available.
In this JavaScript file you can place one or more tests, as follows:
```js
test(() => {
// Place assertions and logic here
assert_equals(document.characterSet, "UTF-8");
}, "Ensure HTML boilerplate uses UTF-8"); // This is the title of the test
```
If you only need to test a [single thing](testharness-api.html#single-page-tests), you could also use:
```js
// META: title=Ensure HTML boilerplate uses UTF-8
setup({ single_test: true });
assert_equals(document.characterSet, "UTF-8");
done();
```
See [asynchronous (`async_test()`)](testharness-api.html#asynchronous-tests) and
[promise tests (`promise_test()`)](testharness-api.html#promise-tests) for more involved setups.
### With HTML boilerplate
You need to be a bit more explicit and include the `testharness.js` framework directly as well as an
additional file used by implementations:
```html
<!doctype html>
<meta charset=utf-8>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body>
<script>
test(() => {
assert_equals(document.characterSet, "UTF-8");
}, "Ensure UTF-8 declaration is observed");
</script>
```
Here too you could avoid the wrapper `test()` function:
```html
<!doctype html>
<meta charset=utf-8>
<title>Ensure UTF-8 declaration is observed</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body>
<script>
setup({ single_test: true });
assert_equals(document.characterSet, "UTF-8");
done();
</script>
```
In this case the test title is taken from the `title` element.
## Dedicated worker tests (`.worker.js`)
Create a JavaScript file that imports `testharness.js` and whose filename ends in `.worker.js` to
have the necessary HTML boilerplate generated for you at `.worker.html`.
For example, one could write a test for the `FileReaderSync` API by
creating a `FileAPI/FileReaderSync.worker.js` as follows:
```js
importScripts("/resources/testharness.js");
test(function () {
const blob = new Blob(["Hello"]);
const fr = new FileReaderSync();
assert_equals(fr.readAsText(blob), "Hello");
}, "FileReaderSync#readAsText.");
done();
```
This test could then be run from `FileAPI/FileReaderSync.worker.html`.
(Removing the need for `importScripts()` and `done()` is tracked in
## Tests for other or multiple globals (`.any.js`)
Tests for features that exist in multiple global scopes can be written in a way
that they are automatically run in several scopes. In this case, the test is a
JavaScript file with extension `.any.js`, which can use all the usual APIs.
By default, the test runs in a window scope and a dedicated worker scope.
For example, one could write a test for the `Blob` constructor by
creating a `FileAPI/Blob-constructor.any.js` as follows:
```js
test(function () {
const blob = new Blob();
assert_equals(blob.size, 0);
assert_equals(blob.type, "");
assert_false(blob.isClosed);
}, "The Blob constructor.");
```
This test could then be run from `FileAPI/Blob-constructor.any.worker.html` as well
as `FileAPI/Blob-constructor.any.html`.
It is possible to customize the set of scopes with a metadata comment, such as
```
// META: global=sharedworker
// ==> would run in the shared worker scope
// META: global=window,serviceworker
// ==> would only run in the window and service worker scope
// META: global=dedicatedworker
// ==> would run in the default dedicated worker scope
// META: global=dedicatedworker-module
// ==> would run in the dedicated worker scope as a module
// META: global=worker
// ==> would run in the dedicated, shared, and service worker scopes
```
For a test file <code><var>x</var>.any.js</code>, the available scope keywords
are:
* `window` (default): to be run at <code><var>x</var>.any.html</code>
* `dedicatedworker` (default): to be run at <code><var>x</var>.any.worker.html</code>
* `dedicatedworker-module` to be run at <code><var>x</var>.any.worker-module.html</code>
* `serviceworker`: to be run at <code><var>x</var>.any.serviceworker.html</code> (`.https` is
implied)
* `serviceworker-module`: to be run at <code><var>x</var>.any.serviceworker-module.html</code>
(`.https` is implied)
* `sharedworker`: to be run at <code><var>x</var>.any.sharedworker.html</code>
* `sharedworker-module`: to be run at <code><var>x</var>.any.sharedworker-module.html</code>
* `jsshell`: to be run in a JavaScript shell, without access to the DOM
(currently only supported in SpiderMonkey, and skipped in wptrunner)
* `worker`: shorthand for the dedicated, shared, and service worker scopes
* `shadowrealm`: runs the test code in a
[ShadowRealm](https://github.com/tc39/proposal-shadowrealm) context hosted in
an ordinary Window context; to be run at <code><var>x</var>.any.shadowrealm.html</code>
To check what scope your test is run from, you can use the following methods that will
be made available by the framework:
self.GLOBAL.isWindow()
self.GLOBAL.isWorker()
self.GLOBAL.isShadowRealm()
Although [the global `done()` function must be explicitly invoked for most
dedicated worker tests and shared worker
tests](testharness-api.html#determining-when-all-tests-are-complete), it is
automatically invoked for tests defined using the "multi-global" pattern.
## Other features of `.window.js`, `.worker.js` and `.any.js`
### Specifying a test title
Use `// META: title=This is the title of the test` at the beginning of the resource.
### Including other JavaScript files
Use `// META: script=link/to/resource.js` at the beginning of the resource. For example,
```
// META: script=/common/utils.js
// META: script=resources/utils.js
```
can be used to include both the global and a local `utils.js` in a test.
In window environments, the script will be included using a classic `<script>` tag. In classic
worker environments, the script will be imported using `importScripts()`. In module worker
environments, the script will be imported using a static `import`.
wptserve generates markup with `/resources/testharness.js` and `/resources/testharnessreport.js`
included automatically, so there's no need to include those scripts from the `.js` test file.
### Specifying a timeout of long
Use `// META: timeout=long` at the beginning of the resource.
### Specifying test [variants](#variants)
Use `// META: variant=url-suffix` at the beginning of the resource. For example,
```
// META: variant=?default
// META: variant=?wss
```
## Variants
A test file can have multiple variants by including `meta` elements,
for example:
```html
<meta name="variant" content="?default">
<meta name="variant" content="?wss">
```
Test runners will execute the test for each variant specified, appending the corresponding content
attribute value to the URL of the test as they do so.
`/common/subset-tests.js` and `/common/subset-tests-by-key.js` are two utility scripts that work
well together with variants, allowing a test to be split up into subtests in cases when there are
otherwise too many tests to complete inside the timeout. For example:
```html
<!doctype html>
<title>Testing variants</title>
<meta name="variant" content="?1-1000">
<meta name="variant" content="?1001-2000">
<meta name="variant" content="?2001-last">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/subset-tests.js"></script>
<script>
const tests = [
{ fn: t => { ... }, name: "..." },
... lots of tests ...
];
for (const test of tests) {
subsetTest(async_test, test.fn, test.name);
}
</script>
```
With `subsetTestByKey`, the key is given as the first argument, and the
query string can include or exclude a key (which will be matched as a regular
expression).
```html
<!doctype html>
<title>Testing variants by key</title>
<meta name="variant" content="?include=Foo">
<meta name="variant" content="?include=Bar">
<meta name="variant" content="?exclude=(Foo|Bar)">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/subset-tests-by-key.js"></script>
<script>
subsetTestByKey("Foo", async_test, () => { ... }, "Testing foo");
...
</script>
```
## Table of Contents
```eval_rst
.. toctree::
:maxdepth: 1
testharness-api
testdriver
testdriver-extension-tutorial
idlharness
```