Skip to content

Commit a9090c0

Browse files
committed
Convert WPT test harness to TS
1 parent 5055345 commit a9090c0

File tree

7 files changed

+119
-26
lines changed

7 files changed

+119
-26
lines changed

build/wpt_test.bzl

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# generate a corresponding wd-test file. It then invokes the wd_test macro
88
# to set up the test.
99

10+
load("//:build/typescript.bzl", "js_name", "module_name")
1011
load("//:build/wd_test.bzl", "wd_test")
1112

1213
def wpt_test(name, wpt_directory, test_js):
@@ -53,7 +54,7 @@ const unitTests :Workerd.Config = (
5354
worker = (
5455
modules = [
5556
(name = "worker", esModule = embed "{test_js}"),
56-
(name = "harness", esModule = embed "../../../../../workerd/src/wpt/harness.js"),
57+
(name = "wpt:harness", esModule = embed "../../../../../workerd/src/wpt/harness.js"),
5758
{modules}
5859
],
5960
bindings = [

src/workerd/api/wpt/url-test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Licensed under the Apache 2.0 license found in the LICENSE file or at:
33
// https://opensource.org/licenses/Apache-2.0
44

5-
import { run } from 'harness';
5+
import { run } from 'wpt:harness';
66

77
export const idnaTestV2Window = run('IdnaTestV2.window.js');
88
export const historical = run('historical.any.js', {

src/workerd/api/wpt/urlpattern-test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Licensed under the Apache 2.0 license found in the LICENSE file or at:
33
// https://opensource.org/licenses/Apache-2.0
44

5-
import { run } from 'harness';
5+
import { run } from 'wpt:harness';
66

77
export const urlpatternCompareTests = run(
88
'urlpattern-compare-tests.tentative.js',

src/wpt/BUILD.bazel

+13-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
1-
filegroup(
1+
load("@aspect_rules_ts//ts:defs.bzl", "ts_project")
2+
3+
ts_project(
24
name = "wpt-test-harness",
5+
testonly = False,
36
srcs = glob(
4-
include = ["**/*"],
5-
allow_empty = True,
7+
[
8+
"*.ts",
9+
],
610
),
11+
allow_js = True,
12+
declaration = True,
13+
tsconfig = "tsconfig.json",
714
visibility = ["//visibility:public"],
15+
deps = [
16+
"//:node_modules/@types",
17+
],
818
)

src/wpt/eslint.config.mjs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { baseConfig } from '../../tools/base.eslint.config.mjs';
2+
3+
export default [...baseConfig({ tsconfigRootDir: import.meta.dirname })];

src/wpt/harness.js renamed to src/wpt/harness.ts

+71-20
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,68 @@ import {
3232
throws,
3333
} from 'node:assert';
3434

35-
function OptionalFeatureUnsupportedError(message) {
36-
AssertionError.call(this, message);
35+
type Options = {
36+
expectedFailures?: string[];
37+
verbose?: boolean;
38+
skippedTests?: string[];
39+
};
40+
41+
type TestRunnerFn = (callback: TestFn | PromiseTestFn, message: string) => void;
42+
type TestFn = () => void;
43+
type PromiseTestFn = () => Promise<void>;
44+
type ThrowingFn = () => any;
45+
46+
declare namespace globalThis {
47+
var errors: Error[];
48+
var testOptions: Options;
49+
var Window: typeof globalThis;
50+
var self: typeof globalThis;
51+
var GLOBAL: { isWindow(): boolean };
52+
function fetch(url: string): Promise<{ json(): Promise<any> }>;
53+
function test(func: TestFn, name: string): void;
54+
function done(): undefined;
55+
function subsetTestByKey(
56+
_key: any,
57+
testType: TestRunnerFn,
58+
testCallback: TestFn | PromiseTestFn,
59+
testMessage: string
60+
): void;
61+
function promise_test(
62+
func: PromiseTestFn,
63+
name: string,
64+
properties?: any
65+
): void;
66+
function assert_equals(a: any, b: any, message?: string): void;
67+
function assert_not_equals(a: any, b: any, message?: string): void;
68+
function assert_true(val: any, message?: string): void;
69+
function assert_false(val: any, message?: string): void;
70+
function assert_array_equals(a: any, b: any, message?: string): void;
71+
function assert_object_equals(a: any, b: any, message?: string): void;
72+
function assert_implements(condition: any, description?: string): void;
73+
function assert_implements_optional(
74+
condition: any,
75+
description?: string
76+
): void;
77+
function assert_unreached(description?: string): void;
78+
function assert_throws_js(
79+
constructor: any,
80+
func: ThrowingFn,
81+
description?: string
82+
): void;
83+
function assert_throws_exactly(
84+
exception: any,
85+
fn: ThrowingFn,
86+
description?: string
87+
): void;
88+
function assert_throws_dom(
89+
type: any,
90+
funcOrConstructor: any,
91+
descriptionOrFunc: any,
92+
maybeDescription: any
93+
): void;
3794
}
38-
OptionalFeatureUnsupportedError.prototype = Object.create(
39-
AssertionError.prototype
40-
);
95+
96+
class OptionalFeatureUnsupportedError extends AssertionError {}
4197

4298
globalThis.Window = Object.getPrototypeOf(globalThis).constructor;
4399

@@ -67,13 +123,7 @@ globalThis.subsetTestByKey = (_key, testType, testCallback, testMessage) => {
67123
return testType(testCallback, testMessage);
68124
};
69125

70-
globalThis.promise_test = async (func, name, properties) => {
71-
if (typeof func !== 'function') {
72-
properties = name;
73-
name = func;
74-
func = null;
75-
}
76-
126+
globalThis.promise_test = async (func, name, _properties) => {
77127
if (!shouldRunTest(name)) {
78128
return;
79129
}
@@ -140,7 +190,7 @@ globalThis.assert_implements = (condition, description) => {
140190
*/
141191
globalThis.assert_implements_optional = (condition, description) => {
142192
if (!condition) {
143-
throw new OptionalFeatureUnsupportedError(description);
193+
throw new OptionalFeatureUnsupportedError({ message: description });
144194
}
145195
};
146196

@@ -219,7 +269,7 @@ globalThis.assert_throws_exactly = (exception, fn, description) => {
219269
*
220270
*/
221271
globalThis.assert_throws_dom = (
222-
type,
272+
_type,
223273
funcOrConstructor,
224274
descriptionOrFunc,
225275
maybeDescription
@@ -230,6 +280,7 @@ globalThis.assert_throws_dom = (
230280
func = descriptionOrFunc;
231281
description = maybeDescription;
232282
} else {
283+
// @ts-ignore
233284
constructor = this.DOMException;
234285
func = funcOrConstructor;
235286
description = descriptionOrFunc;
@@ -251,7 +302,7 @@ globalThis.assert_throws_dom = (
251302
/**
252303
* Create a synchronous test
253304
*
254-
* @param {TestFunction} func - Test function. This is executed
305+
* @param {TestFn} func - Test function. This is executed
255306
* immediately. If it returns without error, the test status is
256307
* set to ``PASS``. If it throws an :js:class:`AssertionError`, or
257308
* any other exception, the test status is set to ``FAIL``
@@ -273,7 +324,7 @@ globalThis.test = (func, name) => {
273324

274325
globalThis.errors = [];
275326

276-
function shouldRunTest(message) {
327+
function shouldRunTest(message: string) {
277328
if ((globalThis.testOptions.skippedTests ?? []).includes(message)) {
278329
return false;
279330
}
@@ -285,12 +336,12 @@ function shouldRunTest(message) {
285336
return true;
286337
}
287338

288-
function prepare(options) {
339+
function prepare(options: Options) {
289340
globalThis.errors = [];
290341
globalThis.testOptions = options;
291342
}
292343

293-
function sanitizeMessage(message) {
344+
function sanitizeMessage(message: string) {
294345
// Test logs will be exported to XML, so we must escape any characters that
295346
// are forbidden in an XML CDATA section, namely "[...] the surrogate blocks,
296347
// FFFE, and FFFF".
@@ -308,7 +359,7 @@ function sanitizeMessage(message) {
308359
);
309360
}
310361

311-
function validate(testFileName, options) {
362+
function validate(testFileName: string, options: Options) {
312363
const expectedFailures = new Set(options.expectedFailures ?? []);
313364

314365
let failing = false;
@@ -332,7 +383,7 @@ function validate(testFileName, options) {
332383
}
333384
}
334385

335-
export function run(file, options = {}) {
386+
export function run(file: string, options: Options = {}) {
336387
return {
337388
async test() {
338389
prepare(options);

src/wpt/tsconfig.json

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ESNext",
4+
"module": "ESNext",
5+
"lib": ["ESNext", "dom"],
6+
"alwaysStrict": true,
7+
"strict": true,
8+
"allowJs": true,
9+
"allowUnreachableCode": false,
10+
"allowUnusedLabels": false,
11+
"exactOptionalPropertyTypes": true,
12+
"noFallthroughCasesInSwitch": true,
13+
"noImplicitOverride": true,
14+
"noImplicitReturns": true,
15+
"noPropertyAccessFromIndexSignature": false,
16+
"noUncheckedIndexedAccess": true,
17+
"noUnusedLocals": true,
18+
"noUnusedParameters": true,
19+
"strictNullChecks": true,
20+
"esModuleInterop": true,
21+
"moduleResolution": "node",
22+
"declaration": true,
23+
"paths": {
24+
"wpt:*": ["./*"]
25+
}
26+
},
27+
"exclude": []
28+
}

0 commit comments

Comments
 (0)