Skip to content
File

Blob: src/wpt/harness/globals.ts

typescript164 lines
1// Copyright (c) 2017-2022 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4// Copyright © web-platform-tests contributors. BSD license
5// Adapted from Node.js. Copyright Joyent, Inc. and other Node contributors.
6//
7// Permission is hereby granted, free of charge, to any person obtaining a
8// copy of this software and associated documentation files (the
9// "Software"), to deal in the Software without restriction, including
10// without limitation the rights to use, copy, modify, merge, publish,
11// distribute, sublicense, and/or sell copies of the Software, and to permit
12// persons to whom the Software is furnished to do so, subject to the
13// following conditions:
14//
15// The above copyright notice and this permission notice shall be included
16// in all copies or substantial portions of the Software.
17//
18// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
21// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
22// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24// USE OR OTHER DEALINGS IN THE SOFTWARE.
25 
26import path from 'node:path';
27import { getBindingPath } from './common';
28 
29// @ts-expect-error We're just exposing enough stuff for the tests to pass; it's not a perfect match
30globalThis.self = globalThis;
31 
32// Some WPT tests reference addEventListener at the top level during file evaluation.
33// workerd doesn't have addEventListener on the global (it uses module exports instead),
34// so we provide a no-op stub to allow test files to load without throwing.
35// Tests that actually need addEventListener functionality should be marked as omitted.
36globalThis.addEventListener = (): void => {};
37 
38// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access -- We're just exposing enough stuff for the tests to pass; it's not a perfect match
39globalThis.Window = Object.getPrototypeOf(globalThis).constructor;
40 
41const realFetch = globalThis.fetch;
42const realRequest = globalThis.Request;
43 
44function relativizeUrl(input: URL | string): string {
45 return new URL(input, globalThis.state.testUrl).href;
46}
47 
48function relativizeRequest(
49 input: RequestInfo | URL,
50 init?: RequestInit
51): Request {
52 if (input instanceof Request) {
53 return new realRequest(
54 relativizeRequest(input.url),
55 new realRequest(input, init)
56 );
57 } else if (input instanceof URL) {
58 return new realRequest(relativizeUrl(input), init);
59 } else {
60 return new realRequest(relativizeUrl(input), init);
61 }
62}
63 
64globalThis.Request = class _Request extends Request {
65 constructor(input: RequestInfo | URL, init?: RequestInit) {
66 super(relativizeRequest(input, init));
67 }
68};
69 
70globalThis.Response = class _Response extends Response {
71 static override redirect(url: string | URL, status?: number): Response {
72 return super.redirect(relativizeUrl(url), status);
73 }
74};
75 
76globalThis.fetch = async (
77 input: RequestInfo | URL,
78 init?: RequestInit
79): Promise<Response> => {
80 if (typeof input === 'string' && input.endsWith('.json')) {
81 const relativePath = getBindingPath(
82 path.dirname(globalThis.state.testFileName),
83 input
84 );
85 // WPT sometimes uses fetch to load a resource file, we "serve" this from the bindings
86 const exports: unknown = globalThis.state.env[relativePath];
87 if (exports === undefined) {
88 throw new Error(`Unable to load resources file ${input} from bindings`);
89 }
90 
91 return new Response(JSON.stringify(exports));
92 }
93 return realFetch(relativizeRequest(input, init));
94};
95 
96class _Location {
97 get ancestorOrigins(): DOMStringList {
98 return {
99 length: 0,
100 item(_index: number): string | null {
101 return null;
102 },
103 contains(_string: string): boolean {
104 return false;
105 },
106 [Symbol.iterator](): ArrayIterator<string> {
107 return ([] as string[])[Symbol.iterator]();
108 },
109 };
110 }
111 
112 get hash(): string {
113 return globalThis.state.testUrl.hash;
114 }
115 
116 get host(): string {
117 return globalThis.state.testUrl.host;
118 }
119 
120 get hostname(): string {
121 return globalThis.state.testUrl.hostname;
122 }
123 
124 get href(): string {
125 return globalThis.state.testUrl.href;
126 }
127 
128 get origin(): string {
129 return globalThis.state.testUrl.origin;
130 }
131 
132 get pathname(): string {
133 return globalThis.state.testUrl.pathname;
134 }
135 
136 get port(): string {
137 return globalThis.state.testUrl.port;
138 }
139 
140 get protocol(): string {
141 return globalThis.state.testUrl.protocol;
142 }
143 
144 get search(): string {
145 return globalThis.state.testUrl.search;
146 }
147 
148 assign(url: string): void {
149 globalThis.state.testUrl = new URL(url);
150 }
151 
152 reload(): void {}
153 
154 replace(url: string): void {
155 globalThis.state.testUrl = new URL(url);
156 }
157 
158 toString(): string {
159 return globalThis.state.testUrl.href;
160 }
161}
162 
163globalThis.location = new _Location();