summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/docs/writing-tests/testharness.md
blob: e426e37f22b7afe3fda3a37755f46fb651356b5b (plain)
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# 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
[issue #11529](https://github.com/web-platform-tests/wpt/issues/11529).)

## 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
```