diff --git a/test/fixtures/wpt/fetch/api/request/WEB_FEATURES.yml b/test/fixtures/wpt/fetch/api/request/WEB_FEATURES.yml new file mode 100644 index 00000000000..69b2ea582a2 --- /dev/null +++ b/test/fixtures/wpt/fetch/api/request/WEB_FEATURES.yml @@ -0,0 +1,4 @@ +features: +- name: fetch-priority + files: + - request-init-priority.any.js diff --git a/test/fixtures/wpt/fetch/http-cache/no-vary-search.tentative.any.js b/test/fixtures/wpt/fetch/http-cache/no-vary-search.tentative.any.js index 379da6a0594..6c5a714e287 100644 --- a/test/fixtures/wpt/fetch/http-cache/no-vary-search.tentative.any.js +++ b/test/fixtures/wpt/fetch/http-cache/no-vary-search.tentative.any.js @@ -6,12 +6,15 @@ // META: script=http-cache.js /* NOTE for testing No-Vary-Search-Header: -If `params` is set to true, `expect=("dispatch" "uuid")` must be specified. -Otherwise: -- The same HTTP Cache will be used by other tests, which are supposed - to be distinguished by uuid. -- The test utility cannot get the server's states because UA will use the HTTP - Cache instead of sending a new request to server to ask for the latest state. +- If `params` is set to true, `expect=("dispatch" "uuid")` must be specified. + Otherwise: + - The same HTTP Cache will be used by other tests, which are supposed + to be distinguished by uuid. + - The test utility cannot get the server's states because UA will use the HTTP + Cache instead of sending a new request to server to ask for the latest state. +- Do not test not_cached cases and cached cases within one test. Test infra + checks the number of requests and responses without considering if the + previous responses should be served from cache or not. */ var tests = [ { @@ -28,6 +31,61 @@ var tests = [ expected_type: "cached" } ] + }, + { + name: "Ground truth: When key-order is not set, URLs should be compared in an order-sensitive way.", + requests: [ + { + url_params: "a=1&b=2", + response_headers: [ + ["Cache-Control", "max-age=10000"], + ], + }, + { + url_params: "b=2&a=1", + expected_type: "not_cached" + } + ] + }, + { + name: "When key-order is set , URLs should be compared in an order-insensitive way. Matched cases:", + requests: [ + { + url_params: "a=1&b=2", + response_headers: [ + ["Cache-Control", "max-age=10000"], + ["No-Vary-Search", "key-order"], + ], + }, + { + url_params: "b=2&a=1", + expected_type: "cached" + } + ] + }, + { + name: "When key-order is set , URLs should be compared in an order-insensitive way. Not matched cases", + requests: [ + { + url_params: "a=1&b=2", + response_headers: [ + ["Cache-Control", "max-age=10000"], + ["No-Vary-Search", "key-order"], + ], + }, + { + url_params: "b=2", + expected_type: "not_cached" + }, + { + url_params: "a=2&b=2", + expected_type: "not_cached" + }, + { + url_params: "a=1&b=2&c=3", + expected_type: "not_cached" + } + ] } ]; run_tests(tests); diff --git a/test/fixtures/wpt/fetch/http-cache/pragma-no-cache-with-cache-control.html b/test/fixtures/wpt/fetch/http-cache/pragma-no-cache-with-cache-control.html new file mode 100644 index 00000000000..19a80fe5edc --- /dev/null +++ b/test/fixtures/wpt/fetch/http-cache/pragma-no-cache-with-cache-control.html @@ -0,0 +1,28 @@ + + +HTTP Cache: Cache-Control with Pragma: no-cache + + + + + diff --git a/test/fixtures/wpt/fetch/http-cache/resources/cached_pragma_rand.py b/test/fixtures/wpt/fetch/http-cache/resources/cached_pragma_rand.py new file mode 100644 index 00000000000..18c7d25159f --- /dev/null +++ b/test/fixtures/wpt/fetch/http-cache/resources/cached_pragma_rand.py @@ -0,0 +1,14 @@ +def main(request, response): + # Disable non-standard XSS protection + response.headers.set(b"X-XSS-Protection", b"0") + response.headers.set(b"Content-Type", b"text/html") + + # Set caching headers + # According to rfc9111 Pragma: no-cache is deprecated, so we expect + # Cache-Control to take precedence when there's a mismatch. + response.headers.set(b"Cache-Control", b"max-age=2592000, public") + response.headers.set(b"Pragma", b"no-cache") + + # Include a timestamp to verify caching behavior + import time + response.content = f"Timestamp: {time.time()}".encode('utf-8') diff --git a/test/fixtures/wpt/fetch/local-network-access/META.yml b/test/fixtures/wpt/fetch/local-network-access/META.yml new file mode 100644 index 00000000000..4c5c6983ed0 --- /dev/null +++ b/test/fixtures/wpt/fetch/local-network-access/META.yml @@ -0,0 +1,5 @@ +spec: https://wicg.github.io/local-network-access/ +suggested_reviewers: + - cthomp + - camillelamy + - hchao diff --git a/test/fixtures/wpt/fetch/local-network-access/README.md b/test/fixtures/wpt/fetch/local-network-access/README.md new file mode 100644 index 00000000000..95066cdcd0b --- /dev/null +++ b/test/fixtures/wpt/fetch/local-network-access/README.md @@ -0,0 +1,11 @@ +# Local Network Access tests + +This directory contains tests for Local Network Access' integration with +the Fetch specification. + +See also: + +* [Explainer](https://github.com/explainers-by-googlers/local-network-access) + +Local Network Access replaced [Private Network +Access](https://wicg.github.io/local-network-access/). diff --git a/test/fixtures/wpt/fetch/local-network-access/fetch.tentative.https.html b/test/fixtures/wpt/fetch/local-network-access/fetch.tentative.https.html new file mode 100644 index 00000000000..9c591f309b7 --- /dev/null +++ b/test/fixtures/wpt/fetch/local-network-access/fetch.tentative.https.html @@ -0,0 +1,51 @@ + + +LNA Fetch tests: HTTPS + + + + + + + + diff --git a/test/fixtures/wpt/fetch/local-network-access/resources/fetch-private.html b/test/fixtures/wpt/fetch/local-network-access/resources/fetch-private.html new file mode 100644 index 00000000000..b96a207ec33 --- /dev/null +++ b/test/fixtures/wpt/fetch/local-network-access/resources/fetch-private.html @@ -0,0 +1,52 @@ + + +Fetch Private resource + + + + + diff --git a/test/fixtures/wpt/fetch/local-network-access/resources/support.sub.js b/test/fixtures/wpt/fetch/local-network-access/resources/support.sub.js new file mode 100644 index 00000000000..774e34d0a6f --- /dev/null +++ b/test/fixtures/wpt/fetch/local-network-access/resources/support.sub.js @@ -0,0 +1,186 @@ +// Maps protocol (without the trailing colon) and address space to port. +const SERVER_PORTS = { + "http": { + "local": {{ports[http][0]}}, + "private": {{ports[http-private][0]}}, + "public": {{ports[http-public][0]}}, + }, + "https": { + "local": {{ports[https][0]}}, + "other-local": {{ports[https][1]}}, + "private": {{ports[https-private][0]}}, + "public": {{ports[https-public][0]}}, + }, + "ws": { + "local": {{ports[ws][0]}}, + }, + "wss": { + "local": {{ports[wss][0]}}, + }, +}; + +// A `Server` is a web server accessible by tests. It has the following shape: +// +// { +// addressSpace: the IP address space of the server ("local", "private" or +// "public"), +// name: a human-readable name for the server, +// port: the port on which the server listens for connections, +// protocol: the protocol (including trailing colon) spoken by the server, +// } +// +// Constants below define the available servers, which can also be accessed +// programmatically with `get()`. +class Server { + // Maps the given `protocol` (without a trailing colon) and `addressSpace` to + // a server. Returns null if no such server exists. + static get(protocol, addressSpace) { + const ports = SERVER_PORTS[protocol]; + if (ports === undefined) { + return null; + } + + const port = ports[addressSpace]; + if (port === undefined) { + return null; + } + + return { + addressSpace, + name: `${protocol}-${addressSpace}`, + port, + protocol: protocol + ':', + }; + } + + static HTTP_LOCAL = Server.get("http", "local"); + static HTTP_PRIVATE = Server.get("http", "private"); + static HTTP_PUBLIC = Server.get("http", "public"); + static HTTPS_LOCAL = Server.get("https", "local"); + static OTHER_HTTPS_LOCAL = Server.get("https", "other-local"); + static HTTPS_PRIVATE = Server.get("https", "private"); + static HTTPS_PUBLIC = Server.get("https", "public"); + static WS_LOCAL = Server.get("ws", "local"); + static WSS_LOCAL = Server.get("wss", "local"); +}; + +// Resolves a URL relative to the current location, returning an absolute URL. +// +// `url` specifies the relative URL, e.g. "foo.html" or "http://foo.example". +// `options`, if defined, should have the following shape: +// +// { +// // Optional. Overrides the protocol of the returned URL. +// protocol, +// +// // Optional. Overrides the port of the returned URL. +// port, +// +// // Extra headers. +// headers, +// +// // Extra search params. +// searchParams, +// } +// +function resolveUrl(url, options) { + const result = new URL(url, window.location); + if (options === undefined) { + return result; + } + + const { port, protocol, headers, searchParams } = options; + if (port !== undefined) { + result.port = port; + } + if (protocol !== undefined) { + result.protocol = protocol; + } + if (headers !== undefined) { + const pipes = []; + for (key in headers) { + pipes.push(`header(${key},${headers[key]})`); + } + result.searchParams.append("pipe", pipes.join("|")); + } + if (searchParams !== undefined) { + for (key in searchParams) { + result.searchParams.append(key, searchParams[key]); + } + } + + return result; +} + +// Computes options to pass to `resolveUrl()` for a source document's URL. +// +// `server` identifies the server from which to load the document. +// `treatAsPublic`, if set to true, specifies that the source document should +// be artificially placed in the `public` address space using CSP. +function sourceResolveOptions({ server, treatAsPublic }) { + const options = {...server}; + if (treatAsPublic) { + options.headers = { "Content-Security-Policy": "treat-as-public-address" }; + } + return options; +} + +// Computes the URL of a target handler configured with the given options. +// +// `server` identifies the server from which to load the resource. +// `behavior` specifies the behavior of the target server. It may contain: +// - `response`: The result of calling one of `ResponseBehavior`'s methods. +// - `redirect`: A URL to which the target should redirect GET requests. +function resolveTargetUrl({ server, behavior }) { + if (server === undefined) { + throw new Error("no server specified."); + } + const options = {...server}; + if (behavior) { + const { response, redirect } = behavior; + options.searchParams = { + ...response, + }; + if (redirect !== undefined) { + options.searchParams.redirect = redirect; + } + } + + return resolveUrl("target.py", options); +} + +// Methods generate behavior specifications for how `resources/target.py` +// should behave upon receiving a regular (non-preflight) request. +const ResponseBehavior = { + // The response should succeed without CORS headers. + default: () => ({}), + + // The response should succeed with CORS headers. + allowCrossOrigin: () => ({ "final-headers": "cors" }), +}; + +const FetchTestResult = { + SUCCESS: { + ok: true, + body: "success", + }, + OPAQUE: { + ok: false, + type: "opaque", + body: "", + }, + FAILURE: { + error: "TypeError: Failed to fetch", + }, +}; + +// Helper function for checking results from fetch tests. +function checkTestResult(actual, expected) { + assert_equals(actual.error, expected.error, "error mismatch"); + assert_equals(actual.ok, expected.ok, "response ok mismatch"); + assert_equals(actual.body, expected.body, "response body mismatch"); + + if (expected.type !== undefined) { + assert_equals(type, expected.type, "response type mismatch"); + } +} diff --git a/test/fixtures/wpt/fetch/local-network-access/resources/target.py b/test/fixtures/wpt/fetch/local-network-access/resources/target.py new file mode 100644 index 00000000000..eabcdd47517 --- /dev/null +++ b/test/fixtures/wpt/fetch/local-network-access/resources/target.py @@ -0,0 +1,105 @@ +# This endpoint responds to requests for a target of a (possible) LNA request. +# +# Its behavior can be configured with various search/GET parameters, all of +# which are optional: +# +# - final-headers: Valid values are: +# - cors: this endpoint responds with valid CORS headers to CORS-enabled +# non-preflight requests. These should be sufficient for non-preflighted +# CORS-enabled requests to succeed. +# - sw: this endpoint responds with a valid Service-Worker header to allow +# for the request to serve as a Service worker script resource. This is +# only valid in conjunction with the cors value above. +# - unspecified: this endpoint responds with no CORS headers to non-preflight +# requests. This should fail CORS-enabled requests, but be sufficient for +# no-CORS requests. +# +# The following parameters only affect non-preflight responses: +# +# - redirect: If set, the response code is set to 301 and the `Location` +# response header is set to this value. +# - mime-type: If set, the `Content-Type` response header is set to this value. +# - file: Specifies a path (relative to this file's directory) to a file. If +# set, the response body is copied from this file. +# - random-js-prefix: If set to any value, the response body is prefixed with +# a Javascript comment line containing a random value. This is useful in +# service worker tests, since service workers are only updated if the new +# script is not byte-for-byte identical with the old script. +# - body: If set and `file` is not, the response body is set to this value. +# + +import os +import random + +from wptserve.utils import isomorphic_encode + +_ACAO = ("Access-Control-Allow-Origin", "*") +_ACAH = ("Access-Control-Allow-Headers", "Service-Worker") + +def _get_response_headers(method, mode, origin): + acam = ("Access-Control-Allow-Methods", method) + + if mode == b"cors": + return [acam, _ACAO] + + if mode == b"cors+sw": + return [acam, _ACAO, _ACAH] + + if mode == b"navigation": + return [ + acam, + ("Access-Control-Allow-Origin", origin), + ("Access-Control-Allow-Credentials", "true"), + ] + + return [] + + +def _is_loaded_in_fenced_frame(request): + return request.GET.get(b"is-loaded-in-fenced-frame") + +def _final_response_body(request): + file_name = None + if file_name is None: + file_name = request.GET.get(b"file") + if file_name is None: + return request.GET.get(b"body") or "success" + + prefix = b"" + if request.GET.get(b"random-js-prefix"): + value = random.randint(0, 1000000000) + prefix = isomorphic_encode("// Random value: {}\n\n".format(value)) + + path = os.path.join(os.path.dirname(isomorphic_encode(__file__)), file_name) + with open(path, 'rb') as f: + contents = f.read() + + return prefix + contents + +def _handle_final_request(request, response): + mode = request.GET.get(b"final-headers") + origin = request.headers.get("Origin") + headers = _get_response_headers(request.method, mode, origin) + + redirect = request.GET.get(b"redirect") + if redirect is not None: + headers.append(("Location", redirect)) + return (301, headers, b"") + + mime_type = request.GET.get(b"mime-type") + if mime_type is not None: + headers.append(("Content-Type", mime_type),) + + if _is_loaded_in_fenced_frame(request): + headers.append(("Supports-Loading-Mode", "fenced-frame")) + + body = _final_response_body(request) + return (headers, body) + + +def main(request, response): + try: + return _handle_final_request(request, response) + except BaseException as e: + # Surface exceptions to the client, where they show up as assertion errors. + return (500, [("X-exception", str(e))], "exception: {}".format(e)) diff --git a/test/fixtures/wpt/interfaces/fedcm.idl b/test/fixtures/wpt/interfaces/fedcm.idl index 215f375432e..5ce2d260f11 100644 --- a/test/fixtures/wpt/interfaces/fedcm.idl +++ b/test/fixtures/wpt/interfaces/fedcm.idl @@ -84,8 +84,10 @@ dictionary IdentityProviderAPIConfig { dictionary IdentityProviderAccount { required USVString id; - required USVString name; - required USVString email; + USVString name; + USVString email; + USVString tel; + USVString username; USVString given_name; USVString picture; sequence approved_clients; diff --git a/test/fixtures/wpt/interfaces/image-capture.idl b/test/fixtures/wpt/interfaces/image-capture.idl index 21e03d4db82..26cc7fab5cd 100644 --- a/test/fixtures/wpt/interfaces/image-capture.idl +++ b/test/fixtures/wpt/interfaces/image-capture.idl @@ -3,7 +3,7 @@ // (https://github.com/w3c/webref) // Source: MediaStream Image Capture (https://w3c.github.io/mediacapture-image/) -[Exposed=Window] +[Exposed=Window, SecureContext] interface ImageCapture { constructor(MediaStreamTrack videoTrack); Promise takePhoto(optional PhotoSettings photoSettings = {}); diff --git a/test/fixtures/wpt/interfaces/largest-contentful-paint.idl b/test/fixtures/wpt/interfaces/largest-contentful-paint.idl index 872ba552b0d..d1630cc7daa 100644 --- a/test/fixtures/wpt/interfaces/largest-contentful-paint.idl +++ b/test/fixtures/wpt/interfaces/largest-contentful-paint.idl @@ -5,7 +5,6 @@ [Exposed=Window] interface LargestContentfulPaint : PerformanceEntry { - readonly attribute DOMHighResTimeStamp renderTime; readonly attribute DOMHighResTimeStamp loadTime; readonly attribute unsigned long size; readonly attribute DOMString id; @@ -13,3 +12,5 @@ interface LargestContentfulPaint : PerformanceEntry { readonly attribute Element? element; [Default] object toJSON(); }; + +LargestContentfulPaint includes PaintTimingMixin; diff --git a/test/fixtures/wpt/interfaces/mediastream-recording.idl b/test/fixtures/wpt/interfaces/mediastream-recording.idl index 496bfcf2e27..68c891cdc91 100644 --- a/test/fixtures/wpt/interfaces/mediastream-recording.idl +++ b/test/fixtures/wpt/interfaces/mediastream-recording.idl @@ -56,7 +56,7 @@ interface BlobEvent : Event { readonly attribute DOMHighResTimeStamp timecode; }; -dictionary BlobEventInit { +dictionary BlobEventInit : EventInit { required Blob data; DOMHighResTimeStamp timecode; }; diff --git a/test/fixtures/wpt/interfaces/service-workers.idl b/test/fixtures/wpt/interfaces/service-workers.idl index d9ff2f651f8..34af3372401 100644 --- a/test/fixtures/wpt/interfaces/service-workers.idl +++ b/test/fixtures/wpt/interfaces/service-workers.idl @@ -34,7 +34,7 @@ interface ServiceWorkerRegistration : EventTarget { readonly attribute USVString scope; readonly attribute ServiceWorkerUpdateViaCache updateViaCache; - [NewObject] Promise update(); + [NewObject] Promise update(); [NewObject] Promise unregister(); // event diff --git a/test/fixtures/wpt/interfaces/turtledove.idl b/test/fixtures/wpt/interfaces/turtledove.idl index b9f50d47885..7f11cdc3790 100644 --- a/test/fixtures/wpt/interfaces/turtledove.idl +++ b/test/fixtures/wpt/interfaces/turtledove.idl @@ -206,6 +206,7 @@ partial interface Navigator { [Exposed=InterestGroupScriptRunnerGlobalScope] interface InterestGroupScriptRunnerGlobalScope { readonly attribute PrivateAggregation? privateAggregation; + readonly attribute ProtectedAudienceUtilities protectedAudience; }; dictionary PASignalValue { @@ -220,6 +221,12 @@ dictionary PAExtendedHistogramContribution { bigint filteringId = 0; }; +[Exposed=InterestGroupScriptRunnerGlobalScope] +interface ProtectedAudienceUtilities { + Uint8Array encodeUtf8(USVString input); + USVString decodeUtf8(Uint8Array bytes); +}; + [Exposed=InterestGroupBiddingAndScoringScriptRunnerGlobalScope] interface ForDebuggingOnly { undefined reportAdAuctionWin(USVString url); diff --git a/test/fixtures/wpt/interfaces/webauthn.idl b/test/fixtures/wpt/interfaces/webauthn.idl index a33c85e7bad..7fbe55e6765 100644 --- a/test/fixtures/wpt/interfaces/webauthn.idl +++ b/test/fixtures/wpt/interfaces/webauthn.idl @@ -311,22 +311,37 @@ enum PublicKeyCredentialHint { partial dictionary AuthenticationExtensionsClientInputs { DOMString appid; }; +partial dictionary AuthenticationExtensionsClientInputsJSON { + DOMString appid; +}; partial dictionary AuthenticationExtensionsClientOutputs { boolean appid; }; +partial dictionary AuthenticationExtensionsClientOutputsJSON { + boolean appid; +}; partial dictionary AuthenticationExtensionsClientInputs { DOMString appidExclude; }; +partial dictionary AuthenticationExtensionsClientInputsJSON { + DOMString appidExclude; +}; partial dictionary AuthenticationExtensionsClientOutputs { boolean appidExclude; }; +partial dictionary AuthenticationExtensionsClientOutputsJSON { + boolean appidExclude; +}; partial dictionary AuthenticationExtensionsClientInputs { boolean credProps; }; +partial dictionary AuthenticationExtensionsClientInputsJSON { + boolean credProps; +}; dictionary CredentialPropertiesOutput { boolean rk; @@ -335,33 +350,57 @@ dictionary CredentialPropertiesOutput { partial dictionary AuthenticationExtensionsClientOutputs { CredentialPropertiesOutput credProps; }; +partial dictionary AuthenticationExtensionsClientOutputsJSON { + CredentialPropertiesOutput credProps; +}; dictionary AuthenticationExtensionsPRFValues { required BufferSource first; BufferSource second; }; +dictionary AuthenticationExtensionsPRFValuesJSON { + required Base64URLString first; + Base64URLString second; +}; dictionary AuthenticationExtensionsPRFInputs { AuthenticationExtensionsPRFValues eval; record evalByCredential; }; +dictionary AuthenticationExtensionsPRFInputsJSON { + AuthenticationExtensionsPRFValuesJSON eval; + record evalByCredential; +}; partial dictionary AuthenticationExtensionsClientInputs { AuthenticationExtensionsPRFInputs prf; }; +partial dictionary AuthenticationExtensionsClientInputsJSON { + AuthenticationExtensionsPRFInputsJSON prf; +}; dictionary AuthenticationExtensionsPRFOutputs { boolean enabled; AuthenticationExtensionsPRFValues results; }; +dictionary AuthenticationExtensionsPRFOutputsJSON { + boolean enabled; + AuthenticationExtensionsPRFValuesJSON results; +}; partial dictionary AuthenticationExtensionsClientOutputs { AuthenticationExtensionsPRFOutputs prf; }; +partial dictionary AuthenticationExtensionsClientOutputsJSON { + AuthenticationExtensionsPRFOutputsJSON prf; +}; partial dictionary AuthenticationExtensionsClientInputs { AuthenticationExtensionsLargeBlobInputs largeBlob; }; +partial dictionary AuthenticationExtensionsClientInputsJSON { + AuthenticationExtensionsLargeBlobInputsJSON largeBlob; +}; enum LargeBlobSupport { "required", @@ -373,13 +412,26 @@ dictionary AuthenticationExtensionsLargeBlobInputs { boolean read; BufferSource write; }; +dictionary AuthenticationExtensionsLargeBlobInputsJSON { + DOMString support; + boolean read; + Base64URLString write; +}; partial dictionary AuthenticationExtensionsClientOutputs { AuthenticationExtensionsLargeBlobOutputs largeBlob; }; +partial dictionary AuthenticationExtensionsClientOutputsJSON { + AuthenticationExtensionsLargeBlobOutputsJSON largeBlob; +}; dictionary AuthenticationExtensionsLargeBlobOutputs { boolean supported; ArrayBuffer blob; boolean written; }; +dictionary AuthenticationExtensionsLargeBlobOutputsJSON { + boolean supported; + Base64URLString blob; + boolean written; +}; diff --git a/test/fixtures/wpt/interfaces/webcodecs.idl b/test/fixtures/wpt/interfaces/webcodecs.idl index 274ef96578a..3d4db1ed49d 100644 --- a/test/fixtures/wpt/interfaces/webcodecs.idl +++ b/test/fixtures/wpt/interfaces/webcodecs.idl @@ -141,7 +141,7 @@ dictionary AudioDecoderConfig { required DOMString codec; [EnforceRange] required unsigned long sampleRate; [EnforceRange] required unsigned long numberOfChannels; - BufferSource description; + AllowSharedBufferSource description; }; dictionary VideoDecoderConfig { diff --git a/test/fixtures/wpt/interfaces/WebCryptoAPI.idl b/test/fixtures/wpt/interfaces/webcrypto.idl similarity index 100% rename from test/fixtures/wpt/interfaces/WebCryptoAPI.idl rename to test/fixtures/wpt/interfaces/webcrypto.idl diff --git a/test/fixtures/wpt/interfaces/webgpu.idl b/test/fixtures/wpt/interfaces/webgpu.idl index de1f7c1e52b..4fec46a2557 100644 --- a/test/fixtures/wpt/interfaces/webgpu.idl +++ b/test/fixtures/wpt/interfaces/webgpu.idl @@ -109,6 +109,7 @@ dictionary GPUDeviceDescriptor }; enum GPUFeatureName { + "core-features-and-limits", "depth-clip-control", "depth32float-stencil8", "texture-compression-bc", diff --git a/test/fixtures/wpt/interfaces/webnn.idl b/test/fixtures/wpt/interfaces/webnn.idl index 57f2a4a9890..37fcc32501e 100644 --- a/test/fixtures/wpt/interfaces/webnn.idl +++ b/test/fixtures/wpt/interfaces/webnn.idl @@ -36,6 +36,8 @@ interface MLContext { undefined dispatch(MLGraph graph, MLNamedTensors inputs, MLNamedTensors outputs); Promise createTensor(MLTensorDescriptor descriptor); + Promise createConstantTensor( + MLOperandDescriptor descriptor, AllowSharedBufferSource inputData); Promise readTensor(MLTensor tensor); Promise readTensor(MLTensor tensor, AllowSharedBufferSource outputData); @@ -133,6 +135,7 @@ interface MLTensor { readonly attribute FrozenArray shape; readonly attribute boolean readable; readonly attribute boolean writable; + readonly attribute boolean constant; undefined destroy(); }; @@ -154,6 +157,9 @@ interface MLGraphBuilder { // Create a scalar operand from the specified number of the specified type. MLOperand constant(MLOperandDataType type, MLNumber value); + // Create an operand from a specified constant tensor. + MLOperand constant(MLTensor tensor); + // Compile the graph up to the specified output operands asynchronously. Promise build(MLNamedOperands outputs); }; diff --git a/test/fixtures/wpt/interfaces/webxr-depth-sensing.idl b/test/fixtures/wpt/interfaces/webxr-depth-sensing.idl index 9befd640646..7ddbec7cc97 100644 --- a/test/fixtures/wpt/interfaces/webxr-depth-sensing.idl +++ b/test/fixtures/wpt/interfaces/webxr-depth-sensing.idl @@ -34,6 +34,10 @@ partial interface XRSession { readonly attribute XRDepthUsage depthUsage; readonly attribute XRDepthDataFormat depthDataFormat; readonly attribute XRDepthType? depthType; + readonly attribute boolean? depthActive; + + undefined pauseDepthSensing(); + undefined resumeDepthSensing(); }; [SecureContext, Exposed=Window] @@ -43,9 +47,10 @@ interface XRDepthInformation { [SameObject] readonly attribute XRRigidTransform normDepthBufferFromNormView; readonly attribute float rawValueToMeters; - readonly attribute XRView? view; }; +XRDepthInformation includes XRViewGeometry; + [Exposed=Window] interface XRCPUDepthInformation : XRDepthInformation { [SameObject] readonly attribute ArrayBuffer data; diff --git a/test/fixtures/wpt/interfaces/webxr.idl b/test/fixtures/wpt/interfaces/webxr.idl index 1098000d6c2..874994086a2 100644 --- a/test/fixtures/wpt/interfaces/webxr.idl +++ b/test/fixtures/wpt/interfaces/webxr.idl @@ -118,6 +118,11 @@ interface XRBoundedReferenceSpace : XRReferenceSpace { readonly attribute FrozenArray boundsGeometry; }; +[SecureContext, Exposed=Window] interface mixin XRViewGeometry { + readonly attribute Float32Array projectionMatrix; + [SameObject] readonly attribute XRRigidTransform transform; +}; + enum XREye { "none", "left", @@ -126,13 +131,13 @@ enum XREye { [SecureContext, Exposed=Window] interface XRView { readonly attribute XREye eye; - readonly attribute Float32Array projectionMatrix; - [SameObject] readonly attribute XRRigidTransform transform; readonly attribute double? recommendedViewportScale; undefined requestViewportScale(double? scale); }; +XRView includes XRViewGeometry; + [SecureContext, Exposed=Window] interface XRViewport { readonly attribute long x; readonly attribute long y; diff --git a/test/fixtures/wpt/interfaces/writing-assistance-apis.idl b/test/fixtures/wpt/interfaces/writing-assistance-apis.idl new file mode 100644 index 00000000000..916daee754e --- /dev/null +++ b/test/fixtures/wpt/interfaces/writing-assistance-apis.idl @@ -0,0 +1,193 @@ +// GENERATED CONTENT - DO NOT EDIT +// Content was automatically extracted by Reffy into webref +// (https://github.com/w3c/webref) +// Source: Writing Assistance APIs (https://webmachinelearning.github.io/writing-assistance-apis/) + +[Exposed=Window, SecureContext] +interface Summarizer { + static Promise create(optional SummarizerCreateOptions options = {}); + static Promise availability(optional SummarizerCreateCoreOptions options = {}); + + Promise summarize( + DOMString input, + optional SummarizerSummarizeOptions options = {} + ); + ReadableStream summarizeStreaming( + DOMString input, + optional SummarizerSummarizeOptions options = {} + ); + + readonly attribute DOMString sharedContext; + readonly attribute SummarizerType type; + readonly attribute SummarizerFormat format; + readonly attribute SummarizerLength length; + + readonly attribute FrozenArray? expectedInputLanguages; + readonly attribute FrozenArray? expectedContextLanguages; + readonly attribute DOMString? outputLanguage; + + Promise measureInputUsage( + DOMString input, + optional SummarizerSummarizeOptions options = {} + ); + readonly attribute unrestricted double inputQuota; +}; +Summarizer includes DestroyableModel; + +dictionary SummarizerCreateCoreOptions { + SummarizerType type = "key-points"; + SummarizerFormat format = "markdown"; + SummarizerLength length = "short"; + + sequence expectedInputLanguages; + sequence expectedContextLanguages; + DOMString outputLanguage; +}; + +dictionary SummarizerCreateOptions : SummarizerCreateCoreOptions { + AbortSignal signal; + CreateMonitorCallback monitor; + + DOMString sharedContext; +}; + +dictionary SummarizerSummarizeOptions { + AbortSignal signal; + DOMString context; +}; + +enum SummarizerType { "tl;dr", "teaser", "key-points", "headline" }; +enum SummarizerFormat { "plain-text", "markdown" }; +enum SummarizerLength { "short", "medium", "long" }; + +[Exposed=Window, SecureContext] +interface Writer { + static Promise create(optional WriterCreateOptions options = {}); + static Promise availability(optional WriterCreateCoreOptions options = {}); + + Promise write( + DOMString input, + optional WriterWriteOptions options = {} + ); + ReadableStream writeStreaming( + DOMString input, + optional WriterWriteOptions options = {} + ); + + readonly attribute DOMString sharedContext; + readonly attribute WriterTone tone; + readonly attribute WriterFormat format; + readonly attribute WriterLength length; + + readonly attribute FrozenArray? expectedInputLanguages; + readonly attribute FrozenArray? expectedContextLanguages; + readonly attribute DOMString? outputLanguage; + + Promise measureInputUsage( + DOMString input, + optional WriterWriteOptions options = {} + ); + readonly attribute unrestricted double inputQuota; +}; +Writer includes DestroyableModel; + +dictionary WriterCreateCoreOptions { + WriterTone tone = "neutral"; + WriterFormat format = "markdown"; + WriterLength length = "short"; + + sequence expectedInputLanguages; + sequence expectedContextLanguages; + DOMString outputLanguage; +}; + +dictionary WriterCreateOptions : WriterCreateCoreOptions { + AbortSignal signal; + CreateMonitorCallback monitor; + + DOMString sharedContext; +}; + +dictionary WriterWriteOptions { + DOMString context; + AbortSignal signal; +}; + +enum WriterTone { "formal", "neutral", "casual" }; +enum WriterFormat { "plain-text", "markdown" }; +enum WriterLength { "short", "medium", "long" }; + +[Exposed=Window, SecureContext] +interface Rewriter { + static Promise create(optional RewriterCreateOptions options = {}); + static Promise availability(optional RewriterCreateCoreOptions options = {}); + + Promise rewrite( + DOMString input, + optional RewriterRewriteOptions options = {} + ); + ReadableStream rewriteStreaming( + DOMString input, + optional RewriterRewriteOptions options = {} + ); + + readonly attribute DOMString sharedContext; + readonly attribute RewriterTone tone; + readonly attribute RewriterFormat format; + readonly attribute RewriterLength length; + + readonly attribute FrozenArray? expectedInputLanguages; + readonly attribute FrozenArray? expectedContextLanguages; + readonly attribute DOMString? outputLanguage; + + Promise measureInputUsage( + DOMString input, + optional RewriterRewriteOptions options = {} + ); + readonly attribute unrestricted double inputQuota; +}; +Rewriter includes DestroyableModel; + +dictionary RewriterCreateCoreOptions { + RewriterTone tone = "as-is"; + RewriterFormat format = "as-is"; + RewriterLength length = "as-is"; + + sequence expectedInputLanguages; + sequence expectedContextLanguages; + DOMString outputLanguage; +}; + +dictionary RewriterCreateOptions : RewriterCreateCoreOptions { + AbortSignal signal; + CreateMonitorCallback monitor; + + DOMString sharedContext; +}; + +dictionary RewriterRewriteOptions { + DOMString context; + AbortSignal signal; +}; + +enum RewriterTone { "as-is", "more-formal", "more-casual" }; +enum RewriterFormat { "as-is", "plain-text", "markdown" }; +enum RewriterLength { "as-is", "shorter", "longer" }; + +[Exposed=Window, SecureContext] +interface CreateMonitor : EventTarget { + attribute EventHandler ondownloadprogress; +}; + +callback CreateMonitorCallback = undefined (CreateMonitor monitor); + +enum Availability { + "unavailable", + "downloadable", + "downloading", + "available" +}; + +interface mixin DestroyableModel { + undefined destroy(); +}; diff --git a/test/fixtures/wpt/resources/chromium/webxr-test.js b/test/fixtures/wpt/resources/chromium/webxr-test.js index 41340aa27e0..ce2503b1bbc 100644 --- a/test/fixtures/wpt/resources/chromium/webxr-test.js +++ b/test/fixtures/wpt/resources/chromium/webxr-test.js @@ -757,30 +757,34 @@ class MockRuntime { const viewport_size = 20; return [{ eye: vrMojom.XREye.kLeft, - fieldOfView: { - upDegrees: 48.316, - downDegrees: 50.099, - leftDegrees: 50.899, - rightDegrees: 35.197 + geometry: { + fieldOfView: { + upDegrees: 48.316, + downDegrees: 50.099, + leftDegrees: 50.899, + rightDegrees: 35.197 + }, + mojoFromView: this._getMojoFromViewerWithOffset(composeGFXTransform({ + position: [-0.032, 0, 0], + orientation: [0, 0, 0, 1] + })) }, - mojoFromView: this._getMojoFromViewerWithOffset(composeGFXTransform({ - position: [-0.032, 0, 0], - orientation: [0, 0, 0, 1] - })), viewport: { x: 0, y: 0, width: viewport_size, height: viewport_size } }, { eye: vrMojom.XREye.kRight, - fieldOfView: { - upDegrees: 48.316, - downDegrees: 50.099, - leftDegrees: 50.899, - rightDegrees: 35.197 + geometry: { + fieldOfView: { + upDegrees: 48.316, + downDegrees: 50.099, + leftDegrees: 50.899, + rightDegrees: 35.197 + }, + mojoFromView: this._getMojoFromViewerWithOffset(composeGFXTransform({ + position: [0.032, 0, 0], + orientation: [0, 0, 0, 1] + })) }, - mojoFromView: this._getMojoFromViewerWithOffset(composeGFXTransform({ - position: [0.032, 0, 0], - orientation: [0, 0, 0, 1] - })), viewport: { x: viewport_size, y: 0, width: viewport_size, height: viewport_size } }]; } @@ -835,13 +839,15 @@ class MockRuntime { return { eye: viewEye, - fieldOfView: fov, - mojoFromView: this._getMojoFromViewerWithOffset(composeGFXTransform(fakeXRViewInit.viewOffset)), + geometry: { + fieldOfView: fov, + mojoFromView: this._getMojoFromViewerWithOffset(composeGFXTransform(fakeXRViewInit.viewOffset)) + }, viewport: { x: xOffset, - y: 0, - width: fakeXRViewInit.resolution.width, - height: fakeXRViewInit.resolution.height + y: 0, + width: fakeXRViewInit.resolution.width, + height: fakeXRViewInit.resolution.height }, isFirstPersonObserver: fakeXRViewInit.isFirstPersonObserver ? true : false, viewOffset: composeGFXTransform(fakeXRViewInit.viewOffset) @@ -915,12 +921,12 @@ class MockRuntime { let frame_views = this.primaryViews_; for (let i = 0; i < this.primaryViews_.length; i++) { - this.primaryViews_[i].mojoFromView = + this.primaryViews_[i].geometry.mojoFromView = this._getMojoFromViewerWithOffset(this.primaryViews_[i].viewOffset); } if (this.enabledFeatures_.includes(xrSessionMojom.XRSessionFeature.SECONDARY_VIEWS)) { for (let i = 0; i < this.secondaryViews_.length; i++) { - this.secondaryViews_[i].mojoFromView = + this.secondaryViews_[i].geometry.mojoFromView = this._getMojoFromViewerWithOffset(this.secondaryViews_[i].viewOffset); } @@ -956,7 +962,9 @@ class MockRuntime { this._calculateAnchorInformation(frameData); - this._calculateDepthInformation(frameData); + if (options.depthActive) { + this._calculateDepthInformation(frameData); + } this._injectAdditionalFrameData(options, frameData); diff --git a/test/fixtures/wpt/resources/test/conftest.py b/test/fixtures/wpt/resources/test/conftest.py index 723087d3184..1301e7a9f77 100644 --- a/test/fixtures/wpt/resources/test/conftest.py +++ b/test/fixtures/wpt/resources/test/conftest.py @@ -263,4 +263,4 @@ def _run_functional_test(self): @staticmethod def _assert_sequence(nums): if nums and len(nums) > 0: - assert nums == list(range(1, nums[-1] + 1)) + assert nums == list(range(nums[-1] + 1)) diff --git a/test/fixtures/wpt/resources/test/tests/functional/api-tests-1.html b/test/fixtures/wpt/resources/test/tests/functional/api-tests-1.html index 9de875b0f12..6ec396c221b 100644 --- a/test/fixtures/wpt/resources/test/tests/functional/api-tests-1.html +++ b/test/fixtures/wpt/resources/test/tests/functional/api-tests-1.html @@ -132,7 +132,13 @@

Sample HTML5 API Tests

{ assert_less_than(10, 11, "10 is less than 11") } - test(basicAssertApproxEquals, "basic assert_less_than test"); + test(basicAssertLessThan, "basic assert_less_than test"); + + function bigintAssertLessThan() + { + assert_less_than(10n, 11n, "10n is less than 11n") + } + test(bigintAssertLessThan, "bigint assert_less_than test"); function basicAssertGreaterThan() { @@ -140,12 +146,24 @@

Sample HTML5 API Tests

} test(basicAssertGreaterThan, "assert_greater_than expected to fail"); + function bigintAssertGreaterThan() + { + assert_greater_than(10n, 11n, "10n is not greater than 11n"); + } + test(bigintAssertGreaterThan, "bigint assert_greater_than expected to fail"); + function basicAssertGreaterThanEqual() { assert_greater_than_equal(10, 10, "10 is greater than or equal to 10") } test(basicAssertGreaterThanEqual, "basic assert_greater_than_equal test"); + function bigintAssertGreaterThanEqual() + { + assert_greater_than_equal(10n, 10n, "10n is greater than or equal to 10n") + } + test(bigintAssertGreaterThanEqual, "bigint assert_greater_than_equal test"); + function basicAssertLessThanEqual() { assert_greater_than_equal('10', 10, "'10' is not a number") @@ -462,6 +480,12 @@

Sample HTML5 API Tests

"message": "assert_greater_than: 10 is not greater than 11 expected a number greater than 11 but got 10", "properties": {} }, + { + "status_string": "FAIL", + "name": "bigint assert_greater_than expected to fail", + "message": "assert_greater_than: 10n is not greater than 11n expected a number greater than 11n but got 10n", + "properties": {} + }, { "status_string": "FAIL", "name": "assert_less_than_equal expected to fail", @@ -516,12 +540,24 @@

Sample HTML5 API Tests

"message": null, "properties": {} }, + { + "status_string": "PASS", + "name": "bigint assert_greater_than_equal test", + "message": null, + "properties": {} + }, { "status_string": "PASS", "name": "basic assert_less_than test", "message": null, "properties": {} }, + { + "status_string": "PASS", + "name": "bigint assert_less_than test", + "message": null, + "properties": {} + }, { "status_string": "PASS", "name": "body element fires the onload event set from the attribute", @@ -745,13 +781,22 @@

Sample HTML5 API Tests

"status": 0 }, { - "assert_name": "assert_approx_equals", + "assert_name": "assert_less_than", "test": "basic assert_less_than test", "args": [ "10", "11", - "1", - "\"10 is approximately (+/- 1) 11\"" + "\"10 is less than 11\"" + ], + "status": 0 + }, + { + "assert_name": "assert_less_than", + "test": "bigint assert_less_than test", + "args": [ + "10n", + "11n", + "\"10n is less than 11n\"" ], "status": 0 }, @@ -765,6 +810,16 @@

Sample HTML5 API Tests

], "status": 1 }, + { + "assert_name": "assert_greater_than", + "test": "bigint assert_greater_than expected to fail", + "args": [ + "10n", + "11n", + "\"10n is not greater than 11n\"" + ], + "status": 1 + }, { "assert_name": "assert_greater_than_equal", "test": "basic assert_greater_than_equal test", @@ -775,6 +830,16 @@

Sample HTML5 API Tests

], "status": 0 }, + { + "assert_name": "assert_greater_than_equal", + "test": "bigint assert_greater_than_equal test", + "args": [ + "10n", + "10n", + "\"10n is greater than or equal to 10n\"" + ], + "status": 0 + }, { "assert_name": "assert_greater_than_equal", "test": "assert_less_than_equal expected to fail", diff --git a/test/fixtures/wpt/resources/testdriver.js b/test/fixtures/wpt/resources/testdriver.js index f127e2b2c8d..992b9e3ab2c 100644 --- a/test/fixtures/wpt/resources/testdriver.js +++ b/test/fixtures/wpt/resources/testdriver.js @@ -254,12 +254,63 @@ } }, /** - * `log `_ module. + * `emulation `_ module. + */ + emulation: { + /** + * Overrides the geolocation coordinates for the specified + * browsing contexts. + * Matches the `emulation.setGeolocationOverride + * `_ + * WebDriver BiDi command. + * + * @example + * await test_driver.bidi.emulation.set_geolocation_override({ + * coordinates: { + * latitude: 52.51, + * longitude: 13.39, + * accuracy: 0.5, + * altitude: 34, + * altitudeAccuracy: 0.75, + * heading: 180, + * speed: 2.77 + * } + * }); + * + * @param {object} params - Parameters for the command. + * @param {null|object} params.coordinates - The optional + * geolocation coordinates to set. Matches the + * `emulation.GeolocationCoordinates `_ + * value. If null or omitted and the `params.error` is set, the + * emulation will be removed. Mutually exclusive with + * `params.error`. + * @param {object} params.error - The optional + * geolocation error to emulate. Matches the + * `emulation.GeolocationPositionError `_ + * value. Mutually exclusive with `params.coordinates`. + * @param {null|Array.<(Context)>} [params.contexts] The + * optional contexts parameter specifies which browsing contexts + * to set the geolocation override on. It should be either an + * array of Context objects (window or browsing context id), or + * null. If null or omitted, the override will be set on the + * current browsing context. + * @returns {Promise} Resolves when the geolocation + * override is successfully set. + */ + set_geolocation_override: function (params) { + // Ensure the bidi feature is enabled before calling the internal method + assertBidiIsEnabled(); + return window.test_driver_internal.bidi.emulation.set_geolocation_override( + params); + }, + }, + /** + * `log `_ module. */ log: { entry_added: { /** - * @typedef {object} LogEntryAdded `log.entryAdded `_ event. + * @typedef {object} LogEntryAdded `log.entryAdded `_ event. */ /** @@ -1576,6 +1627,12 @@ } } }, + emulation: { + set_geolocation_override: function (params) { + throw new Error( + "bidi.emulation.set_geolocation_override is not implemented by testdriver-vendor.js"); + } + }, log: { entry_added: { async subscribe() { diff --git a/test/fixtures/wpt/resources/testharness.js b/test/fixtures/wpt/resources/testharness.js index 8ef0574f1f0..6ccede34483 100644 --- a/test/fixtures/wpt/resources/testharness.js +++ b/test/fixtures/wpt/resources/testharness.js @@ -88,7 +88,7 @@ status: harness_status.structured_clone(), asserts: asserts.map(assert => assert.structured_clone())}); }] - } + }; on_event(window, 'load', function() { setTimeout(() => { @@ -203,7 +203,7 @@ } }); this.message_events = new_events; - } + }; WindowTestEnvironment.prototype.next_default_test_name = function() { var suffix = this.name_counter > 0 ? " " + this.name_counter : ""; @@ -225,8 +225,8 @@ WindowTestEnvironment.prototype.test_timeout = function() { var metas = document.getElementsByTagName("meta"); for (var i = 0; i < metas.length; i++) { - if (metas[i].name == "timeout") { - if (metas[i].content == "long") { + if (metas[i].name === "timeout") { + if (metas[i].content === "long") { return settings.harness_timeout.long; } break; @@ -487,7 +487,7 @@ this.all_loaded = false; this.on_loaded_callback = null; Promise.resolve().then(function() { - this.all_loaded = true + this.all_loaded = true; if (this.on_loaded_callback) { this.on_loaded_callback(); } @@ -563,7 +563,7 @@ // The worker object may be from another execution context, // so do not use instanceof here. return 'ServiceWorker' in global_scope && - Object.prototype.toString.call(worker) == '[object ServiceWorker]'; + Object.prototype.toString.call(worker) === '[object ServiceWorker]'; } var seen_func_name = Object.create(null); @@ -810,7 +810,7 @@ return bring_promise_to_current_realm(promise) .then(test.unreached_func("Should have rejected: " + description)) .catch(function(e) { - assert_throws_js_impl(constructor, function() { throw e }, + assert_throws_js_impl(constructor, function() { throw e; }, description, "promise_rejects_js"); }); } @@ -862,7 +862,7 @@ return bring_promise_to_current_realm(promise) .then(test.unreached_func("Should have rejected: " + description)) .catch(function(e) { - assert_throws_dom_impl(type, function() { throw e }, description, + assert_throws_dom_impl(type, function() { throw e; }, description, "promise_rejects_dom", constructor); }); } @@ -881,7 +881,7 @@ return bring_promise_to_current_realm(promise) .then(test.unreached_func("Should have rejected: " + description)) .catch(function(e) { - assert_throws_exactly_impl(exception, function() { throw e }, + assert_throws_exactly_impl(exception, function() { throw e; }, description, "promise_rejects_exactly"); }); } @@ -907,7 +907,7 @@ */ function EventWatcher(test, watchedNode, eventTypes, timeoutPromise) { - if (typeof eventTypes == 'string') { + if (typeof eventTypes === 'string') { eventTypes = [eventTypes]; } @@ -972,7 +972,7 @@ if (waitingFor) { return Promise.reject('Already waiting for an event or events'); } - if (typeof types == 'string') { + if (typeof types === 'string') { types = [types]; } if (options && options.record && options.record === 'all') { @@ -987,7 +987,7 @@ // This should always fail, otherwise we should have // resolved the promise. - assert_true(waitingFor.types.length == 0, + assert_true(waitingFor.types.length === 0, 'Timed out waiting for ' + waitingFor.types.join(', ')); var result = recordedEvents; recordedEvents = null; @@ -1400,6 +1400,8 @@ return "-0"; } return String(val); + case "bigint": + return String(val) + 'n'; case "object": if (val === null) { return "null"; @@ -1423,11 +1425,11 @@ case Node.COMMENT_NODE: return "Comment node "; case Node.DOCUMENT_NODE: - return "Document node with " + val.childNodes.length + (val.childNodes.length == 1 ? " child" : " children"); + return "Document node with " + val.childNodes.length + (val.childNodes.length === 1 ? " child" : " children"); case Node.DOCUMENT_TYPE_NODE: return "DocumentType node"; case Node.DOCUMENT_FRAGMENT_NODE: - return "DocumentFragment node with " + val.childNodes.length + (val.childNodes.length == 1 ? " child" : " children"); + return "DocumentFragment node with " + val.childNodes.length + (val.childNodes.length === 1 ? " child" : " children"); default: return "Node object of unknown type"; } @@ -1771,20 +1773,25 @@ /** * Assert that ``actual`` is a number less than ``expected``. * - * @param {number} actual - Test value. - * @param {number} expected - Number that ``actual`` must be less than. + * @param {number|bigint} actual - Test value. + * @param {number|bigint} expected - Value that ``actual`` must be less than. * @param {string} [description] - Description of the condition being tested. */ function assert_less_than(actual, expected, description) { /* - * Test if a primitive number is less than another + * Test if a primitive number (or bigint) is less than another */ - assert(typeof actual === "number", + assert(typeof actual === "number" || typeof actual === "bigint", "assert_less_than", description, "expected a number but got a ${type_actual}", {type_actual:typeof actual}); + assert(typeof actual === typeof expected, + "assert_less_than", description, + "expected a ${type_expected} but got a ${type_actual}", + {type_expected:typeof expected, type_actual:typeof actual}); + assert(actual < expected, "assert_less_than", description, "expected a number less than ${expected} but got ${actual}", @@ -1795,20 +1802,25 @@ /** * Assert that ``actual`` is a number greater than ``expected``. * - * @param {number} actual - Test value. - * @param {number} expected - Number that ``actual`` must be greater than. + * @param {number|bigint} actual - Test value. + * @param {number|bigint} expected - Value that ``actual`` must be greater than. * @param {string} [description] - Description of the condition being tested. */ function assert_greater_than(actual, expected, description) { /* - * Test if a primitive number is greater than another + * Test if a primitive number (or bigint) is greater than another */ - assert(typeof actual === "number", + assert(typeof actual === "number" || typeof actual === "bigint", "assert_greater_than", description, "expected a number but got a ${type_actual}", {type_actual:typeof actual}); + assert(typeof actual === typeof expected, + "assert_greater_than", description, + "expected a ${type_expected} but got a ${type_actual}", + {type_expected:typeof expected, type_actual:typeof actual}); + assert(actual > expected, "assert_greater_than", description, "expected a number greater than ${expected} but got ${actual}", @@ -1820,21 +1832,31 @@ * Assert that ``actual`` is a number greater than ``lower`` and less * than ``upper`` but not equal to either. * - * @param {number} actual - Test value. - * @param {number} lower - Number that ``actual`` must be greater than. - * @param {number} upper - Number that ``actual`` must be less than. + * @param {number|bigint} actual - Test value. + * @param {number|bigint} lower - Value that ``actual`` must be greater than. + * @param {number|bigint} upper - Value that ``actual`` must be less than. * @param {string} [description] - Description of the condition being tested. */ function assert_between_exclusive(actual, lower, upper, description) { /* - * Test if a primitive number is between two others + * Test if a primitive number (or bigint) is between two others */ - assert(typeof actual === "number", + assert(typeof lower === typeof upper, + "assert_between_exclusive", description, + "expected lower (${type_lower}) and upper (${type_upper}) types to match (test error)", + {type_lower:typeof lower, type_upper:typeof upper}); + + assert(typeof actual === "number" || typeof actual === "bigint", "assert_between_exclusive", description, "expected a number but got a ${type_actual}", {type_actual:typeof actual}); + assert(typeof actual === typeof lower, + "assert_between_exclusive", description, + "expected a ${type_lower} but got a ${type_actual}", + {type_lower:typeof lower, type_actual:typeof actual}); + assert(actual > lower && actual < upper, "assert_between_exclusive", description, "expected a number greater than ${lower} " + @@ -1846,21 +1868,26 @@ /** * Assert that ``actual`` is a number less than or equal to ``expected``. * - * @param {number} actual - Test value. - * @param {number} expected - Number that ``actual`` must be less + * @param {number|bigint} actual - Test value. + * @param {number|bigint} expected - Value that ``actual`` must be less * than or equal to. * @param {string} [description] - Description of the condition being tested. */ function assert_less_than_equal(actual, expected, description) { /* - * Test if a primitive number is less than or equal to another + * Test if a primitive number (or bigint) is less than or equal to another */ - assert(typeof actual === "number", + assert(typeof actual === "number" || typeof actual === "bigint", "assert_less_than_equal", description, "expected a number but got a ${type_actual}", {type_actual:typeof actual}); + assert(typeof actual === typeof expected, + "assert_less_than_equal", description, + "expected a ${type_expected} but got a ${type_actual}", + {type_expected:typeof expected, type_actual:typeof actual}); + assert(actual <= expected, "assert_less_than_equal", description, "expected a number less than or equal to ${expected} but got ${actual}", @@ -1871,21 +1898,26 @@ /** * Assert that ``actual`` is a number greater than or equal to ``expected``. * - * @param {number} actual - Test value. - * @param {number} expected - Number that ``actual`` must be greater + * @param {number|bigint} actual - Test value. + * @param {number|bigint} expected - Value that ``actual`` must be greater * than or equal to. * @param {string} [description] - Description of the condition being tested. */ function assert_greater_than_equal(actual, expected, description) { /* - * Test if a primitive number is greater than or equal to another + * Test if a primitive number (or bigint) is greater than or equal to another */ - assert(typeof actual === "number", + assert(typeof actual === "number" || typeof actual === "bigint", "assert_greater_than_equal", description, "expected a number but got a ${type_actual}", {type_actual:typeof actual}); + assert(typeof actual === typeof expected, + "assert_greater_than_equal", description, + "expected a ${type_expected} but got a ${type_actual}", + {type_expected:typeof expected, type_actual:typeof actual}); + assert(actual >= expected, "assert_greater_than_equal", description, "expected a number greater than or equal to ${expected} but got ${actual}", @@ -1897,21 +1929,31 @@ * Assert that ``actual`` is a number greater than or equal to ``lower`` and less * than or equal to ``upper``. * - * @param {number} actual - Test value. - * @param {number} lower - Number that ``actual`` must be greater than or equal to. - * @param {number} upper - Number that ``actual`` must be less than or equal to. + * @param {number|bigint} actual - Test value. + * @param {number|bigint} lower - Value that ``actual`` must be greater than or equal to. + * @param {number|bigint} upper - Value that ``actual`` must be less than or equal to. * @param {string} [description] - Description of the condition being tested. */ function assert_between_inclusive(actual, lower, upper, description) { /* - * Test if a primitive number is between to two others or equal to either of them + * Test if a primitive number (or bigint) is between to two others or equal to either of them */ - assert(typeof actual === "number", + assert(typeof lower === typeof upper, + "assert_between_inclusive", description, + "expected lower (${type_lower}) and upper (${type_upper}) types to match (test error)", + {type_lower:typeof lower, type_upper:typeof upper}); + + assert(typeof actual === "number" || typeof actual === "bigint", "assert_between_inclusive", description, "expected a number but got a ${type_actual}", {type_actual:typeof actual}); + assert(typeof actual === typeof lower, + "assert_between_inclusive", description, + "expected a ${type_lower} but got a ${type_actual}", + {type_lower:typeof lower, type_actual:typeof actual}); + assert(actual >= lower && actual <= upper, "assert_between_inclusive", description, "expected a number greater than or equal to ${lower} " + @@ -2121,7 +2163,7 @@ {func:func}); // Basic sanity-check on the passed-in constructor - assert(typeof constructor == "function", + assert(typeof constructor === "function", assertion_type, description, "${constructor} is not a constructor", {constructor:constructor}); @@ -2196,7 +2238,7 @@ assert(maybeDescription === undefined, "Too many args passed to no-constructor version of assert_throws_dom, or accidentally explicitly passed undefined"); } - assert_throws_dom_impl(type, func, description, "assert_throws_dom", constructor) + assert_throws_dom_impl(type, func, description, "assert_throws_dom", constructor); } expose_assert(assert_throws_dom, "assert_throws_dom"); @@ -2229,8 +2271,8 @@ {func:func}); // Sanity-check our type - assert(typeof type == "number" || - typeof type == "string", + assert(typeof type === "number" || + typeof type === "string", assertion_type, description, "${type} is not a number or string", {type:type}); @@ -2459,7 +2501,7 @@ function assert_implements(condition, description) { assert(!!condition, "assert_implements", description); } - expose_assert(assert_implements, "assert_implements") + expose_assert(assert_implements, "assert_implements"); /** * Assert that an optional feature is implemented, based on a 'truthy' condition. @@ -2573,11 +2615,11 @@ 2: "Timeout", 3: "Not Run", 4: "Optional Feature Unsupported", - } + }; Test.prototype.format_status = function() { return this.status_formats[this.status]; - } + }; Test.prototype.structured_clone = function() { @@ -2887,7 +2929,7 @@ return new Promise(resolve => { this.step_wait_func(cond, resolve, description, timeout, interval); }); - } + }; /* * Private method for registering cleanup functions. `testharness.js` @@ -3120,7 +3162,7 @@ throw new Error("AbortController is not supported in this browser"); } return this._abortController.signal; - } + }; /** * A RemoteTest object mirrors a Test object on a remote worker. The @@ -3196,11 +3238,11 @@ function(callback) { callback(); }); - } + }; RemoteTest.prototype.format_status = function() { return Test.prototype.status_formats[this.status]; - } + }; /* * A RemoteContext listens for test events from a remote test context, such @@ -3472,7 +3514,7 @@ this.all_done_callbacks = []; this.hide_test_state = false; - this.pending_remotes = []; + this.remotes = []; this.current_test = null; this.asserts_run = []; @@ -3514,26 +3556,26 @@ for (var p in properties) { if (properties.hasOwnProperty(p)) { var value = properties[p]; - if (p == "allow_uncaught_exception") { + if (p === "allow_uncaught_exception") { this.allow_uncaught_exception = value; - } else if (p == "explicit_done" && value) { + } else if (p === "explicit_done" && value) { this.wait_for_finish = true; - } else if (p == "explicit_timeout" && value) { + } else if (p === "explicit_timeout" && value) { this.timeout_length = null; if (this.timeout_id) { clearTimeout(this.timeout_id); } - } else if (p == "single_test" && value) { + } else if (p === "single_test" && value) { this.set_file_is_test(); - } else if (p == "timeout_multiplier") { + } else if (p === "timeout_multiplier") { this.timeout_multiplier = value; if (this.timeout_length) { this.timeout_length *= this.timeout_multiplier; } - } else if (p == "hide_test_state") { + } else if (p === "hide_test_state") { this.hide_test_state = value; - } else if (p == "output") { + } else if (p === "output") { this.output = value; } else if (p === "debug") { settings.debug = value; @@ -3626,11 +3668,14 @@ Tests.prototype.push = function(test) { + if (this.phase === this.phases.COMPLETE) { + return; + } if (this.phase < this.phases.HAVE_TESTS) { this.start(); } this.num_pending++; - test.index = this.tests.push(test); + test.index = this.tests.push(test) - 1; this.notify_test_state(test); }; @@ -3643,11 +3688,11 @@ }; Tests.prototype.all_done = function() { - return (this.tests.length > 0 || this.pending_remotes.length > 0) && + return (this.tests.length > 0 || this.remotes.length > 0) && test_environment.all_loaded && (this.num_pending === 0 || this.is_aborted) && !this.wait_for_finish && !this.processing_callbacks && - !this.pending_remotes.some(function(w) { return w.running; }); + !this.remotes.some(function(w) { return w.running; }); }; Tests.prototype.start = function() { @@ -3716,7 +3761,8 @@ function(test, testDone) { if (test.phase === test.phases.INITIAL) { - test.phase = test.phases.COMPLETE; + test.phase = test.phases.HAS_RESULT; + test.done(); testDone(); } else { add_test_done_callback(test, testDone); @@ -3727,14 +3773,14 @@ }; Tests.prototype.set_assert = function(assert_name, args) { - this.asserts_run.push(new AssertRecord(this.current_test, assert_name, args)) - } + this.asserts_run.push(new AssertRecord(this.current_test, assert_name, args)); + }; Tests.prototype.set_assert_status = function(index, status, stack) { let assert_record = this.asserts_run[index]; assert_record.status = status; assert_record.stack = stack; - } + }; /** * Update the harness status to reflect an unrecoverable harness error that @@ -3876,7 +3922,7 @@ } var remoteContext = this.create_remote_worker(worker); - this.pending_remotes.push(remoteContext); + this.remotes.push(remoteContext); return remoteContext.done; }; @@ -3899,7 +3945,7 @@ } var remoteContext = this.create_remote_window(remote); - this.pending_remotes.push(remoteContext); + this.remotes.push(remoteContext); return remoteContext.done; }; @@ -4111,8 +4157,8 @@ } else { var root = output_document.documentElement; var is_html = (root && - root.namespaceURI == "http://www.w3.org/1999/xhtml" && - root.localName == "html"); + root.namespaceURI === "http://www.w3.org/1999/xhtml" && + root.localName === "html"); var is_svg = (output_document.defaultView && "SVGSVGElement" in output_document.defaultView && root instanceof output_document.defaultView.SVGSVGElement); @@ -4557,7 +4603,7 @@ */ function AssertionError(message) { - if (typeof message == "string") { + if (typeof message === "string") { message = sanitize_unpaired_surrogates(message); } this.message = message; @@ -4603,7 +4649,7 @@ } return lines.slice(i).join("\n"); - } + }; function OptionalFeatureUnsupportedError(message) {