◐ Shell
clean mode source ↗

doc: update WPT test runner README.md · nodejs/node@8126d1c

11

# Web Platform Tests

223-

The tests here are drivers for running the [Web Platform Tests][].

4-5-

See [`test/fixtures/wpt/README.md`][] for a hash of the last

6-

updated WPT commit for each module being covered here.

7-8-

See the json files in [the `status` folder](./status) for prerequisites,

9-

expected failures, and support status for specific tests in each module.

10-11-

Currently there are still some Web Platform Tests titled `test-whatwg-*`

12-

under `test/parallel` that have not been migrated to be run with the

13-

WPT harness and have automatic updates. There are also a few

14-

`test-whatwg-*-custom-*` tests that may need to be upstreamed.

15-

This folder covers the tests that have been migrated.

3+

This directory contains test runners that execute upstream

4+

[Web Platform Tests][] against Node.js using the WPT harness.

5+

The actual test files live in `test/fixtures/wpt`, a subset of the

6+

upstream WPT repository containing only the modules relevant to Node.js.

7+

Each module is updated independently using [git node wpt][], so

8+

different modules may be pinned to different upstream commits.

9+10+

Each module has a status file in the [`status` folder](./status) that

11+

declares build requirements, expected failures, and tests to skip.

12+

See [`test/fixtures/wpt/README.md`][] for the pinned WPT commit

13+

hashes for each module.

16141715

<a id="add-tests"></a>

18161917

## How to add tests for a new module

20182119

### 1. Create a status file

222023-

For example, to add the URL tests, add a `test/wpt/status/url.json` file.

21+

For example, to add the URL tests, add a `test/wpt/status/url.cjs` file.

242225-

In the beginning, it's fine to leave an empty object `{}` in the file if

26-

it's not yet clear how compliant the implementation is,

23+

In the beginning, it's fine to leave an empty object `module.exports = {}`

24+

in the file if it's not yet clear how compliant the implementation is,

2725

the requirements and expected failures can be figured out in a later step

2826

when the tests are run for the first time.

2927

@@ -39,7 +37,7 @@ cd /path/to/node/project

3937

git node wpt url

4038

```

413942-

### 3. Create the test driver

40+

### 3. Create the test runner

43414442

For example, for the URL tests, add a file `test/wpt/test-url.js`:

4543

@@ -50,21 +48,49 @@ const { WPTRunner } = require('../common/wpt');

50485149

const runner = new WPTRunner('url');

525053-

// Set Node.js flags required for the tests.

54-

runner.setFlags(['--expose-internals']);

55-56-

// Set a script that will be executed in the worker before running the tests.

57-

runner.setInitScript(`

58-

const { internalBinding } = require('internal/test/binding');

59-

const { DOMException } = internalBinding('messaging');

60-

global.DOMException = DOMException;

61-

`);

62-51+

runner.pretendGlobalThisAs('Window');

6352

runner.runJsTests();

6453

```

655466-

This driver is capable of running the tests located in `test/fixtures/wpt/url`

67-

with the WPT harness while taking the status file into account.

55+

The runner loads the tests from `test/fixtures/wpt/url`, applies the

56+

status rules from `test/wpt/status/url.cjs`, and runs them using

57+

worker threads.

58+59+

#### `new WPTRunner(path[, options])`

60+61+

* `path` {string} Relative path of the WPT module

62+

(e.g. `'url'`, `'html/webappapis/timers'`).

63+

* `options` {Object}

64+

* `concurrency` {number} Number of tests to run in parallel.

65+

Defaults to `os.availableParallelism() - 1`. Set to `1` for tests

66+

that require sequential execution (e.g. web-locks, webstorage).

67+68+

#### `runner.setFlags(flags)`

69+70+

* `flags` {string\[]} Node.js CLI flags passed to each worker thread

71+

(e.g. `['--expose-internals']`).

72+73+

#### `runner.setInitScript(script)`

74+75+

* `script` {string} JavaScript code executed in the worker before

76+

the tests run. Useful for setting up globals needed by the tests.

77+78+

#### `runner.setScriptModifier(modifier)`

79+80+

* `modifier` {Function} A callback `(meta) => void` invoked for each

81+

script before it is run in the worker. `meta` is an object with

82+

`code` {string} and `filename` {string} properties that can be

83+

mutated.

84+85+

#### `runner.pretendGlobalThisAs(name)`

86+87+

* `name` {string} Currently only `'Window'` is supported. Sets up

88+

`globalThis.Window` so that WPT tests checking the global scope

89+

type work correctly.

90+91+

#### `runner.runJsTests()`

92+93+

Starts running the tests. Must be called last, after all configuration.

68946995

### 4. Run the tests

7096

@@ -76,26 +102,28 @@ tools/test.py wpt/test-url

76102

```

7710378104

To run a specific test in WPT, for example, `url/url-searchparams.any.js`,

79-

pass the file name as argument to the corresponding test driver:

105+

pass the file name as argument to the corresponding test runner:

8010681107

```bash

82108

node test/wpt/test-url.js url-searchparams.any.js

83109

```

8411085111

If there are any failures, update the corresponding status file

86-

(in this case, `test/wpt/status/url.json`) to make the test pass.

112+

(in this case, `test/wpt/status/url.cjs`) to make the test pass.

8711388114

For example, to mark `url/url-searchparams.any.js` as expected to fail,

89-

add this to `test/wpt/status/url.json`:

90-91-

```json

92-

"url-searchparams.any.js": {

93-

"fail": {

94-

"expected": [

95-

"test name in the WPT test case, e.g. second argument passed to test()"

96-

]

97-

}

98-

}

115+

add this to `test/wpt/status/url.cjs`:

116+117+

```js

118+

module.exports = {

119+

'url-searchparams.any.js': {

120+

fail: {

121+

expected: [

122+

'test name in the WPT test case, e.g. second argument passed to test()',

123+

],

124+

},

125+

},

126+

};

99127

```

100128101129

See [Format of a status file](#status-format) for details.

@@ -110,90 +138,78 @@ The tests can be updated in a way similar to how they are added.

110138

Run Step 2 and Step 4 of [adding tests for a new module](#add-tests).

111139112140

The [git node wpt][] command maintains the status of the local

113-

WPT subset, if no files are updated after running it for a module,

114-

the local subset is up to date and there is no need to update them

115-

until they are changed in the upstream.

141+

WPT subset. If no files are updated after running it for a module,

142+

the local subset is up to date and there is no need to create a PR.

143+

When files are updated, run the tests and update the status file to

144+

account for any new failures or passes before submitting.

116145117-

## How it works

146+

## Daily WPT report

118147119-

Note: currently this test suite only supports `.js` tests. There is

120-

ongoing work in the upstream to properly split out the tests into files

121-

that can be run in a shell environment like Node.js.

148+

A [GitHub Actions workflow][] runs every night and uploads results to

149+

[wpt.fyi][]. It tests all active Node.js release lines and the latest

150+

nightly build against the WPT `epochs/daily` branch, which is a daily

151+

snapshot of the upstream WPT repository.

122152123-

### Getting the original test files and harness from WPT

124-125-

The original files and harness from WPT are downloaded and stored in

126-

`test/fixtures/wpt`.

127-128-

The [git node wpt][] command automate this process while maintaining a map

129-

containing the hash of the last updated commit for each module in

130-

`test/fixtures/wpt/versions.json` and [`test/fixtures/wpt/README.md`][].

131-

It also maintains the LICENSE file in `test/fixtures/wpt`.

132-133-

### Loading and running the tests

134-135-

Given a module, the `WPTRunner` class in [`test/common/wpt`](../common/wpt.js)

136-

loads:

137-138-

* `.js` test files (for example, `test/common/wpt/url/*.js` for `url`)

139-

* Status file (for example, `test/wpt/status/url.json` for `url`)

140-

* The WPT harness

141-142-

Then, for each test, it creates a worker thread with the globals and mocks,

143-

sets up the harness result hooks, loads the metadata in the test (including

144-

loading extra resources), and runs all the tests in that worker thread,

145-

skipping tests that cannot be run because of lack of dependency or

146-

expected failures.

153+

Unlike the pinned fixtures used in CI, this workflow replaces

154+

`test/fixtures/wpt` with the full `epochs/daily` checkout so that

155+

results reflect the latest upstream tests. Results can be viewed on

156+

the [wpt.fyi dashboard][].

147157148158

<a id="status-format"></a>

149159150160

## Format of a status file

151161152-

```json

153-

{

154-

"something.scope.js": { // the file name

155-

// Optional: If the requirement is not met, this test will be skipped

156-

"requires": ["small-icu"], // supports: "small-icu", "full-icu", "crypto"

157-158-

// Optional: the entire file will be skipped with the reason printed

159-

"skip": "explain why we cannot run a test that's supposed to pass",

160-161-

// Optional: failing tests

162-

"fail": {

163-

"note": "You may leave an optional arbitrary note e.g. with TODOs",

164-

"expected": [

165-

"test name in the WPT test case, e.g. second argument passed to test()",

166-

"another test name"

162+

The status file can be either a `.json` file or a `.cjs` module that exports

163+

the same object. Using CJS allows for conditional logic and regular

164+

expressions, which JSON does not support.

165+166+

```js

167+

module.exports = {

168+

'something.scope.js': { // the file name

169+

// Optional: If the requirement is not met, this test will be skipped.

170+

// Supported values:

171+

// 'small-icu' - requires at least small-icu intl support

172+

// 'full-icu' - requires full-icu intl support

173+

// 'crypto' - requires crypto (OpenSSL) support

174+

// 'inspector' - requires the inspector to be available

175+

requires: ['small-icu'],

176+177+

// Optional: the entire file will be skipped with the reason printed.

178+

skip: 'explain why we cannot run a test that is supposed to pass',

179+180+

// Optional: failing tests.

181+

fail: {

182+

// Tests that are expected to fail consistently.

183+

expected: [

184+

'test name in the WPT test case, e.g. second argument passed to test()',

185+

'another test name',

167186

],

168-

"flaky": [

169-

"flaky test name"

170-

]

171-

}

172-

}

173-

}

187+

// Tests that fail intermittently. These are treated as expected

188+

// failures but are not flagged as unexpected passes when they

189+

// succeed.

190+

flaky: [

191+

'flaky test name',

192+

],

193+

},

194+

},

195+

};

174196

```

175197198+

A test should be marked with `skip` when it cannot be run at all, for

199+

example, because it depends on a browser-only Web API or a harness feature

200+

that has not been ported to the Node.js runner. Use `fail` instead when

201+

the test can run but produces incorrect results due to an implementation

202+

bug or missing feature.

203+176204

### Skipping individual subtests

177205178206

To skip specific subtests within a file (rather than skipping the entire file),

179-

use `skipTests` with an array of exact test names:

180-181-

```json

182-

{

183-

"something.scope.js": {

184-

"skipTests": [

185-

"exact test name to skip"

186-

]

187-

}

188-

}

189-

```

190-191-

When the status file is a CJS module, regular expressions can also be used:

207+

use `skipTests` with an array of exact test names or regular expressions:

192208193209

```js

194210

module.exports = {

195211

'something.scope.js': {

196-

'skipTests': [

212+

skipTests: [

197213

'exact test name to skip',

198214

/regexp pattern to match/,

199215

],

@@ -205,18 +221,31 @@ Skipped subtests are reported as `[SKIP]` in the output, recorded as `NOTRUN`

205221

in the WPT report, and counted separately in the summary line.

206222207223

This is useful for skipping a particular subtest that crashes the runner,

208-

which would otherwise prevent the rest of the file from being run. When using

209-

CJS status files, this also enables conditionally skipping slow or

210-

resource-heavy subtests in CI on specific architectures.

224+

which would otherwise prevent the rest of the file from being run. Using CJS

225+

status files also enables conditionally skipping slow or resource-heavy

226+

subtests in CI on specific architectures.

227+228+

### Wildcard patterns in file names

229+230+

File name keys can include a `*` character to match multiple test files

231+

with a single entry. For example, to skip all `.window.js` tests:

211232212-

A test may have to be skipped because it depends on another irrelevant

213-

Web API, or certain harness has not been ported in our test runner yet.

214-

In that case it needs to be marked with `skip` instead of `fail`.

233+

```js

234+

module.exports = {

235+

'*.window.js': {

236+

skip: 'window tests are not relevant for Node.js',

237+

},

238+

};

239+

```

215240216-

The status file may optionally also be a CJS module that exports the object.

217-

This allows for more complex logic to be used to determine the expected status

218-

of a test.

241+

The `*` is converted to a `.*` regular expression, so `"subdir/*.any.js"`

242+

would match all `.any.js` files under the `subdir` directory. A test file

243+

can match multiple rules (both an exact match and one or more wildcard

244+

patterns); all matched rules are merged.

219245246+

[GitHub Actions workflow]: ../../.github/workflows/daily-wpt-fyi.yml

220247

[Web Platform Tests]: https://github.com/web-platform-tests/wpt

221248

[`test/fixtures/wpt/README.md`]: ../fixtures/wpt/README.md

222249

[git node wpt]: https://github.com/nodejs/node-core-utils/blob/HEAD/docs/git-node.md#git-node-wpt

250+

[wpt.fyi]: https://wpt.fyi

251+

[wpt.fyi dashboard]: https://wpt.fyi/results/?label=master&label=experimental&product=node.js&product=chrome&product=firefox&product=safari&product=ladybird&product=servo&q=node.js%3A%21missing