test: update WPT resources,WebCryptoAPI,webstorage

PR-URL: https://github.com/nodejs/node/pull/59311
Refs: https://github.com/nodejs/node/issues/58987
Refs: https://github.com/nodejs/node/issues/59310
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
Filip Skokan 2025-08-03 13:31:39 +02:00 committed by GitHub
parent 6d8aa55a2c
commit 8e5c5b3b04
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 2570 additions and 593 deletions

View file

@ -26,7 +26,7 @@ Last update:
- interfaces: https://github.com/web-platform-tests/wpt/tree/e1b27be06b/interfaces
- performance-timeline: https://github.com/web-platform-tests/wpt/tree/94caab7038/performance-timeline
- resource-timing: https://github.com/web-platform-tests/wpt/tree/22d38586d0/resource-timing
- resources: https://github.com/web-platform-tests/wpt/tree/1e140d63ec/resources
- resources: https://github.com/web-platform-tests/wpt/tree/1d2c5fb36a/resources
- streams: https://github.com/web-platform-tests/wpt/tree/bc9dcbbf1a/streams
- url: https://github.com/web-platform-tests/wpt/tree/9504a83e01/url
- urlpattern: https://github.com/web-platform-tests/wpt/tree/84b75f0880/urlpattern
@ -34,10 +34,10 @@ Last update:
- wasm/jsapi: https://github.com/web-platform-tests/wpt/tree/cde25e7e3c/wasm/jsapi
- wasm/webapi: https://github.com/web-platform-tests/wpt/tree/fd1b23eeaa/wasm/webapi
- web-locks: https://github.com/web-platform-tests/wpt/tree/10a122a6bc/web-locks
- WebCryptoAPI: https://github.com/web-platform-tests/wpt/tree/ab08796857/WebCryptoAPI
- WebCryptoAPI: https://github.com/web-platform-tests/wpt/tree/1d2c5fb36a/WebCryptoAPI
- webidl/ecmascript-binding/es-exceptions: https://github.com/web-platform-tests/wpt/tree/2f96fa1996/webidl/ecmascript-binding/es-exceptions
- webmessaging/broadcastchannel: https://github.com/web-platform-tests/wpt/tree/6495c91853/webmessaging/broadcastchannel
- webstorage: https://github.com/web-platform-tests/wpt/tree/1291340aaa/webstorage
- webstorage: https://github.com/web-platform-tests/wpt/tree/1d2c5fb36a/webstorage
[Web Platform Tests]: https://github.com/web-platform-tests/wpt
[`git node wpt`]: https://github.com/nodejs/node-core-utils/blob/main/docs/git-node.md#git-node-wpt

View file

@ -60,9 +60,9 @@ for (const array of arrays) {
test(function() {
const maxlength = 65536 / ctor.BYTES_PER_ELEMENT;
assert_throws_dom("QuotaExceededError", function() {
self.crypto.getRandomValues(new ctor(maxlength + 1))
}, "crypto.getRandomValues length over 65536")
assert_throws_quotaexceedederror(() => {
self.crypto.getRandomValues(new ctor(maxlength + 1));
}, null, null, "crypto.getRandomValues length over 65536");
}, "Large length: " + array);
test(function() {

View file

@ -511,7 +511,7 @@
*
* @param {SendChannel|string} [dest] - Either a SendChannel
* to the destination, or the UUID of the destination. If
* omitted, a new UUID is generated, which can be used when
* ommitted, a new UUID is generated, which can be used when
* constructing the URL for the global.
*
*/

View file

@ -218,6 +218,7 @@ window.checkLayout = function(selectorList, callDone = true)
nodes = Array.prototype.slice.call(nodes);
var checkedLayout = false;
Array.prototype.forEach.call(nodes, function(node) {
const title = node.title ? `: ${node.title}` : '';
test(function(t) {
var container = node.parentNode.className == 'container' ? node.parentNode : node;
var prefix =
@ -240,7 +241,7 @@ window.checkLayout = function(selectorList, callDone = true)
}
checkedLayout |= !passed;
}
}, selectorList + ' ' + String(++testNumber));
}, `${selectorList} ${++testNumber}${title}`);
});
if (!checkedLayout) {
console.error("No valid data-* attributes found in selector list : " + selectorList);

View file

@ -1,245 +0,0 @@
(function() {
function insertAfter(nodeToAdd, referenceNode)
{
if (referenceNode == document.body) {
document.body.appendChild(nodeToAdd);
return;
}
if (referenceNode.nextSibling)
referenceNode.parentNode.insertBefore(nodeToAdd, referenceNode.nextSibling);
else
referenceNode.parentNode.appendChild(nodeToAdd);
}
function positionedAncestor(node)
{
var ancestor = node.parentNode;
while (getComputedStyle(ancestor).position == 'static')
ancestor = ancestor.parentNode;
return ancestor;
}
function checkSubtreeExpectedValues(parent, failures)
{
var checkedLayout = checkExpectedValues(parent, failures);
Array.prototype.forEach.call(parent.childNodes, function(node) {
checkedLayout |= checkSubtreeExpectedValues(node, failures);
});
return checkedLayout;
}
function checkAttribute(output, node, attribute)
{
var result = node.getAttribute && node.getAttribute(attribute);
output.checked |= !!result;
return result;
}
function checkExpectedValues(node, failures)
{
var output = { checked: false };
var expectedWidth = checkAttribute(output, node, "data-expected-width");
if (expectedWidth) {
if (isNaN(expectedWidth) || Math.abs(node.offsetWidth - expectedWidth) >= 1)
failures.push("Expected " + expectedWidth + " for width, but got " + node.offsetWidth + ". ");
}
var expectedHeight = checkAttribute(output, node, "data-expected-height");
if (expectedHeight) {
if (isNaN(expectedHeight) || Math.abs(node.offsetHeight - expectedHeight) >= 1)
failures.push("Expected " + expectedHeight + " for height, but got " + node.offsetHeight + ". ");
}
var expectedOffset = checkAttribute(output, node, "data-offset-x");
if (expectedOffset) {
if (isNaN(expectedOffset) || Math.abs(node.offsetLeft - expectedOffset) >= 1)
failures.push("Expected " + expectedOffset + " for offsetLeft, but got " + node.offsetLeft + ". ");
}
var expectedOffset = checkAttribute(output, node, "data-offset-y");
if (expectedOffset) {
if (isNaN(expectedOffset) || Math.abs(node.offsetTop - expectedOffset) >= 1)
failures.push("Expected " + expectedOffset + " for offsetTop, but got " + node.offsetTop + ". ");
}
var expectedOffset = checkAttribute(output, node, "data-positioned-offset-x");
if (expectedOffset) {
var actualOffset = node.getBoundingClientRect().left - positionedAncestor(node).getBoundingClientRect().left;
if (isNaN(expectedOffset) || Math.abs(actualOffset - expectedOffset) >= 1)
failures.push("Expected " + expectedOffset + " for getBoundingClientRect().left offset, but got " + actualOffset + ". ");
}
var expectedOffset = checkAttribute(output, node, "data-positioned-offset-y");
if (expectedOffset) {
var actualOffset = node.getBoundingClientRect().top - positionedAncestor(node).getBoundingClientRect().top;
if (isNaN(expectedOffset) || Math.abs(actualOffset - expectedOffset) >= 1)
failures.push("Expected " + expectedOffset + " for getBoundingClientRect().top offset, but got " + actualOffset + ". ");
}
var expectedWidth = checkAttribute(output, node, "data-expected-client-width");
if (expectedWidth) {
if (isNaN(expectedWidth) || Math.abs(node.clientWidth - expectedWidth) >= 1)
failures.push("Expected " + expectedWidth + " for clientWidth, but got " + node.clientWidth + ". ");
}
var expectedHeight = checkAttribute(output, node, "data-expected-client-height");
if (expectedHeight) {
if (isNaN(expectedHeight) || Math.abs(node.clientHeight - expectedHeight) >= 1)
failures.push("Expected " + expectedHeight + " for clientHeight, but got " + node.clientHeight + ". ");
}
var expectedWidth = checkAttribute(output, node, "data-expected-scroll-width");
if (expectedWidth) {
if (isNaN(expectedWidth) || Math.abs(node.scrollWidth - expectedWidth) >= 1)
failures.push("Expected " + expectedWidth + " for scrollWidth, but got " + node.scrollWidth + ". ");
}
var expectedHeight = checkAttribute(output, node, "data-expected-scroll-height");
if (expectedHeight) {
if (isNaN(expectedHeight) || Math.abs(node.scrollHeight - expectedHeight) >= 1)
failures.push("Expected " + expectedHeight + " for scrollHeight, but got " + node.scrollHeight + ". ");
}
var expectedOffset = checkAttribute(output, node, "data-total-x");
if (expectedOffset) {
var totalLeft = node.clientLeft + node.offsetLeft;
if (isNaN(expectedOffset) || Math.abs(totalLeft - expectedOffset) >= 1)
failures.push("Expected " + expectedOffset + " for clientLeft+offsetLeft, but got " + totalLeft + ", clientLeft: " + node.clientLeft + ", offsetLeft: " + node.offsetLeft + ". ");
}
var expectedOffset = checkAttribute(output, node, "data-total-y");
if (expectedOffset) {
var totalTop = node.clientTop + node.offsetTop;
if (isNaN(expectedOffset) || Math.abs(totalTop - expectedOffset) >= 1)
failures.push("Expected " + expectedOffset + " for clientTop+offsetTop, but got " + totalTop + ", clientTop: " + node.clientTop + ", + offsetTop: " + node.offsetTop + ". ");
}
var expectedDisplay = checkAttribute(output, node, "data-expected-display");
if (expectedDisplay) {
var actualDisplay = getComputedStyle(node).display;
if (actualDisplay != expectedDisplay)
failures.push("Expected " + expectedDisplay + " for display, but got " + actualDisplay + ". ");
}
var expectedPaddingTop = checkAttribute(output, node, "data-expected-padding-top");
if (expectedPaddingTop) {
var actualPaddingTop = getComputedStyle(node).paddingTop;
// Trim the unit "px" from the output.
actualPaddingTop = actualPaddingTop.substring(0, actualPaddingTop.length - 2);
if (actualPaddingTop != expectedPaddingTop)
failures.push("Expected " + expectedPaddingTop + " for padding-top, but got " + actualPaddingTop + ". ");
}
var expectedPaddingBottom = checkAttribute(output, node, "data-expected-padding-bottom");
if (expectedPaddingBottom) {
var actualPaddingBottom = getComputedStyle(node).paddingBottom;
// Trim the unit "px" from the output.
actualPaddingBottom = actualPaddingBottom.substring(0, actualPaddingBottom.length - 2);
if (actualPaddingBottom != expectedPaddingBottom)
failures.push("Expected " + expectedPaddingBottom + " for padding-bottom, but got " + actualPaddingBottom + ". ");
}
var expectedPaddingLeft = checkAttribute(output, node, "data-expected-padding-left");
if (expectedPaddingLeft) {
var actualPaddingLeft = getComputedStyle(node).paddingLeft;
// Trim the unit "px" from the output.
actualPaddingLeft = actualPaddingLeft.substring(0, actualPaddingLeft.length - 2);
if (actualPaddingLeft != expectedPaddingLeft)
failures.push("Expected " + expectedPaddingLeft + " for padding-left, but got " + actualPaddingLeft + ". ");
}
var expectedPaddingRight = checkAttribute(output, node, "data-expected-padding-right");
if (expectedPaddingRight) {
var actualPaddingRight = getComputedStyle(node).paddingRight;
// Trim the unit "px" from the output.
actualPaddingRight = actualPaddingRight.substring(0, actualPaddingRight.length - 2);
if (actualPaddingRight != expectedPaddingRight)
failures.push("Expected " + expectedPaddingRight + " for padding-right, but got " + actualPaddingRight + ". ");
}
var expectedMarginTop = checkAttribute(output, node, "data-expected-margin-top");
if (expectedMarginTop) {
var actualMarginTop = getComputedStyle(node).marginTop;
// Trim the unit "px" from the output.
actualMarginTop = actualMarginTop.substring(0, actualMarginTop.length - 2);
if (actualMarginTop != expectedMarginTop)
failures.push("Expected " + expectedMarginTop + " for margin-top, but got " + actualMarginTop + ". ");
}
var expectedMarginBottom = checkAttribute(output, node, "data-expected-margin-bottom");
if (expectedMarginBottom) {
var actualMarginBottom = getComputedStyle(node).marginBottom;
// Trim the unit "px" from the output.
actualMarginBottom = actualMarginBottom.substring(0, actualMarginBottom.length - 2);
if (actualMarginBottom != expectedMarginBottom)
failures.push("Expected " + expectedMarginBottom + " for margin-bottom, but got " + actualMarginBottom + ". ");
}
var expectedMarginLeft = checkAttribute(output, node, "data-expected-margin-left");
if (expectedMarginLeft) {
var actualMarginLeft = getComputedStyle(node).marginLeft;
// Trim the unit "px" from the output.
actualMarginLeft = actualMarginLeft.substring(0, actualMarginLeft.length - 2);
if (actualMarginLeft != expectedMarginLeft)
failures.push("Expected " + expectedMarginLeft + " for margin-left, but got " + actualMarginLeft + ". ");
}
var expectedMarginRight = checkAttribute(output, node, "data-expected-margin-right");
if (expectedMarginRight) {
var actualMarginRight = getComputedStyle(node).marginRight;
// Trim the unit "px" from the output.
actualMarginRight = actualMarginRight.substring(0, actualMarginRight.length - 2);
if (actualMarginRight != expectedMarginRight)
failures.push("Expected " + expectedMarginRight + " for margin-right, but got " + actualMarginRight + ". ");
}
return output.checked;
}
window.checkLayout = function(selectorList, outputContainer)
{
var result = true;
if (!selectorList) {
document.body.appendChild(document.createTextNode("You must provide a CSS selector of nodes to check."));
return;
}
var nodes = document.querySelectorAll(selectorList);
nodes = Array.prototype.slice.call(nodes);
nodes.reverse();
var checkedLayout = false;
Array.prototype.forEach.call(nodes, function(node) {
var failures = [];
checkedLayout |= checkExpectedValues(node.parentNode, failures);
checkedLayout |= checkSubtreeExpectedValues(node, failures);
var container = node.parentNode.className == 'container' ? node.parentNode : node;
var pre = document.createElement('pre');
if (failures.length) {
pre.className = 'FAIL';
result = false;
}
pre.appendChild(document.createTextNode(failures.length ? "FAIL:\n" + failures.join('\n') + '\n\n' + container.outerHTML : "PASS"));
var referenceNode = container;
if (outputContainer) {
if (!outputContainer.lastChild) {
// Inserting a text node so we have something to insertAfter.
outputContainer.textContent = " ";
}
referenceNode = outputContainer.lastChild;
}
insertAfter(pre, referenceNode);
});
if (!checkedLayout) {
document.body.appendChild(document.createTextNode("FAIL: No valid data-* attributes found in selector list : " + selectorList));
return false;
}
return result;
}
})();

View file

@ -1,25 +0,0 @@
/*
* Polyfill for attaching shadow trees for declarative Shadow DOM for
* implementations that do not support declarative Shadow DOM.
*
* Note: this polyfill will feature-detect the native feature, and do nothing
* if supported.
*
* See: https://github.com/whatwg/html/pull/5465
*
* root: The root of the subtree in which to upgrade shadow roots
*
*/
function polyfill_declarative_shadow_dom(root) {
if (HTMLTemplateElement.prototype.hasOwnProperty('shadowRootMode'))
return;
root.querySelectorAll("template[shadowrootmode]").forEach(template => {
const mode = template.getAttribute("shadowrootmode");
const delegatesFocus = template.hasAttribute("shadowrootdelegatesfocus");
const shadowRoot = template.parentNode.attachShadow({ mode, delegatesFocus });
shadowRoot.appendChild(template.content);
template.remove();
polyfill_declarative_shadow_dom(shadowRoot);
});
}

View file

@ -1,61 +0,0 @@
// TODO: it would be nice to support `idl_array.add_objects`
function fetch_text(url) {
return fetch(url).then(function (r) {
if (!r.ok) {
throw new Error("Error fetching " + url + ".");
}
return r.text();
});
}
/**
* idl_test_shadowrealm is a promise_test wrapper that handles the fetching of the IDL, and
* running the code in a `ShadowRealm`, avoiding repetitive boilerplate.
*
* @see https://github.com/tc39/proposal-shadowrealm
* @param {String[]} srcs Spec name(s) for source idl files (fetched from
* /interfaces/{name}.idl).
* @param {String[]} deps Spec name(s) for dependency idl files (fetched
* from /interfaces/{name}.idl). Order is important - dependencies from
* each source will only be included if they're already know to be a
* dependency (i.e. have already been seen).
*/
function idl_test_shadowrealm(srcs, deps) {
promise_setup(async t => {
const realm = new ShadowRealm();
// https://github.com/web-platform-tests/wpt/issues/31996
realm.evaluate("globalThis.self = globalThis; undefined;");
realm.evaluate(`
globalThis.self.GLOBAL = {
isWindow: function() { return false; },
isWorker: function() { return false; },
isShadowRealm: function() { return true; },
}; undefined;
`);
const specs = await Promise.all(srcs.concat(deps).map(spec => {
return fetch_text("/interfaces/" + spec + ".idl");
}));
const idls = JSON.stringify(specs);
await new Promise(
realm.evaluate(`(resolve,reject) => {
(async () => {
await import("/resources/testharness.js");
await import("/resources/WebIDLParser.js");
await import("/resources/idlharness.js");
const idls = ${idls};
const idl_array = new IdlArray();
for (let i = 0; i < ${srcs.length}; i++) {
idl_array.add_idls(idls[i]);
}
for (let i = ${srcs.length}; i < ${srcs.length + deps.length}; i++) {
idl_array.add_dependency_idls(idls[i]);
}
idl_array.test();
})().then(resolve, (e) => reject(e.toString()));
}`)
);
await fetch_tests_from_shadow_realm(realm);
});
}
// vim: set expandtab shiftwidth=4 tabstop=4 foldmarker=@{,@} foldmethod=marker:

View file

@ -566,6 +566,7 @@ IdlArray.prototype.is_json_type = function(type)
case "Uint8ClampedArray":
case "BigInt64Array":
case "BigUint64Array":
case "Float16Array":
case "Float32Array":
case "Float64Array":
case "ArrayBuffer":
@ -733,7 +734,7 @@ IdlArray.prototype.test = function()
Object.getOwnPropertyNames(this.members).forEach(function(memberName) {
var member = this.members[memberName];
if (!(member instanceof IdlInterface)) {
if (!(member instanceof IdlInterface || member instanceof IdlNamespace)) {
return;
}
@ -782,6 +783,10 @@ IdlArray.prototype.merge_partials = function()
}
testedPartials.set(parsed_idl.name, partialTestCount);
if (!self.shouldRunSubTest(partialTestName)) {
return;
}
if (!parsed_idl.untested) {
test(function () {
assert_true(originalExists, `Original ${parsed_idl.type} should be defined`);
@ -809,7 +814,7 @@ IdlArray.prototype.merge_partials = function()
{
// Special-case "Exposed". Must be a subset of original interface's exposure.
// Exposed on a partial is the equivalent of having the same Exposed on all nested members.
// See https://github.com/heycam/webidl/issues/154 for discrepancy between Exposed and
// See https://github.com/heycam/webidl/issues/154 for discrepency between Exposed and
// other extended attributes on partial interfaces.
const exposureAttr = parsed_idl.extAttrs.find(a => a.name === "Exposed");
if (exposureAttr) {
@ -870,6 +875,7 @@ IdlArray.prototype.merge_mixins = function()
{
const lhs = parsed_idl.target;
const rhs = parsed_idl.includes;
const testName = lhs + " includes " + rhs + ": member names are unique";
var errStr = lhs + " includes " + rhs + ", but ";
if (!(lhs in this.members)) throw errStr + lhs + " is undefined.";
@ -877,7 +883,7 @@ IdlArray.prototype.merge_mixins = function()
if (!(rhs in this.members)) throw errStr + rhs + " is undefined.";
if (!(this.members[rhs] instanceof IdlInterface)) throw errStr + rhs + " is not an interface.";
if (this.members[rhs].members.length) {
if (this.members[rhs].members.length && self.shouldRunSubTest(testName)) {
test(function () {
var clash = this.members[rhs].members.find(function(member) {
return this.members[lhs].members.find(function(m) {
@ -891,7 +897,7 @@ IdlArray.prototype.merge_mixins = function()
this.members[lhs].members.push(new IdlInterfaceMember(member));
}.bind(this));
assert_true(!clash, "member " + (clash && clash.name) + " is unique");
}.bind(this), lhs + " includes " + rhs + ": member names are unique");
}.bind(this), testName);
}
}
this.includes = [];
@ -1420,7 +1426,7 @@ IdlInterface.prototype.test = function()
if (!this.untested)
{
subsetTestByKey(this.name, test, function() {
assert_false(this.name in self);
assert_false(this.name in self, this.name + " interface should not exist");
}.bind(this), this.name + " interface: existence and properties of interface object");
}
return;
@ -3450,6 +3456,17 @@ IdlNamespace.prototype.test_self = function ()
IdlNamespace.prototype.test = function ()
{
// If the namespace object is not exposed, only test that. Members can't be
// tested either
if (!this.exposed) {
if (!this.untested) {
subsetTestByKey(this.name, test, function() {
assert_false(this.name in self, this.name + " namespace should not exist");
}.bind(this), this.name + " namespace: existence and properties of namespace object");
}
return;
}
if (!this.untested) {
this.test_self();
}
@ -3497,7 +3514,7 @@ function idl_test(srcs, deps, idl_setup_func) {
"require-exposed"
];
return Promise.all(
srcs.concat(deps).map(fetch_spec))
srcs.concat(deps).map(globalThis.fetch_spec))
.then(function(results) {
const astArray = results.map(result =>
WebIDL2.parse(result.idl, { sourceName: result.spec })
@ -3538,9 +3555,11 @@ function idl_test(srcs, deps, idl_setup_func) {
});
}, 'idl_test setup');
}
globalThis.idl_test = idl_test;
/**
* fetch_spec is a shorthand for a Promise that fetches the spec's content.
* Note: ShadowRealm-specific implementation in testharness-shadowrealm-inner.js
*/
function fetch_spec(spec) {
var url = '/interfaces/' + spec + '.idl';

View file

@ -0,0 +1,5 @@
// Testing that the resolution is correct using `resolve`, as you can't import
// the same module twice.
window.outscope_test_result = import.meta.resolve("a");
window.outscope_test_result2 = import.meta.resolve("../resources/log.sub.js?name=E");

View file

@ -32,7 +32,7 @@
* await actions.send();
*
* @param {number} [defaultTickDuration] - The default duration of a
* tick. Be default this is set to 16ms, which is one frame time
* tick. Be default this is set ot 16ms, which is one frame time
* based on 60Hz display.
*/
function Actions(defaultTickDuration=16) {
@ -290,7 +290,7 @@
},
/**
* Create a keyDown event for the current default key source
* Create a keyUp event for the current default key source
*
* @param {String} key - Key to release
* @param {String?} sourceName - Named key source to use or null for the default key source

View file

@ -0,0 +1,2 @@
Content-Type: text/javascript; charset=utf-8
Cache-Control: max-age=3600

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,52 @@
/**
* AudioWorkletProcessor intended for hosting a ShadowRealm and running a test
* inside of that ShadowRealm.
*/
globalThis.TestRunner = class TestRunner extends AudioWorkletProcessor {
constructor() {
super();
this.createShadowRealmAndStartTests();
}
/**
* Fetch adaptor function intended as a drop-in replacement for fetchAdaptor()
* (see testharness-shadowrealm-outer.js), but it does not assume fetch() is
* present in the realm. Instead, it relies on setupFakeFetchOverMessagePort()
* having been called on the port on the other side of this.port's channel.
*/
fetchOverPortExecutor(resource) {
return (resolve, reject) => {
const listener = (event) => {
if (typeof event.data !== "string" || !event.data.startsWith("fetchResult::")) {
return;
}
const result = event.data.slice("fetchResult::".length);
if (result.startsWith("success::")) {
resolve(result.slice("success::".length));
} else {
reject(result.slice("fail::".length));
}
this.port.removeEventListener("message", listener);
}
this.port.addEventListener("message", listener);
this.port.start();
this.port.postMessage(`fetchRequest::${resource}`);
}
}
/**
* Async method, which is patched over in
* (test).any.audioworklet-shadowrealm.js; see serve.py
*/
async createShadowRealmAndStartTests() {
throw new Error("Forgot to overwrite this method!");
}
/** Overrides AudioWorkletProcessor.prototype.process() */
process() {
return false;
}
};
registerProcessor("test-runner", TestRunner);

View file

@ -0,0 +1,38 @@
// testharness file with ShadowRealm utilities to be imported inside ShadowRealm
/**
* Set up all properties on the ShadowRealm's global object that tests will
* expect to be present.
*
* @param {string} queryString - string to use as value for location.search,
* used for subsetting some tests
* @param {function} fetchAdaptor - a function that takes a resource URI and
* returns a function which itself takes a (resolve, reject) pair from the
* hosting realm, and calls resolve with the text result of fetching the
* resource, or reject with a string indicating the error that occurred
*/
globalThis.setShadowRealmGlobalProperties = function (queryString, fetchAdaptor) {
globalThis.fetch_json = (resource) => {
const executor = fetchAdaptor(resource);
return new Promise(executor).then((s) => JSON.parse(s));
};
// Used only by idlharness.js
globalThis.fetch_spec = (spec) => {
const resource = `/interfaces/${spec}.idl`;
const executor = fetchAdaptor(resource);
return new Promise(executor).then(
idl => ({ spec, idl }),
() => {
throw new IdlHarnessError(`Error fetching ${resource}.`);
});
}
globalThis.location = { search: queryString };
};
globalThis.GLOBAL = {
isWindow: function() { return false; },
isWorker: function() { return false; },
isShadowRealm: function() { return true; },
};

View file

@ -0,0 +1,151 @@
// testharness file with ShadowRealm utilities to be imported in the realm
// hosting the ShadowRealm
/**
* Convenience function for evaluating some async code in the ShadowRealm and
* waiting for the result.
*
* In case of error, this function intentionally exposes the stack trace (if it
* is available) to the hosting realm, for debugging purposes.
*
* @param {ShadowRealm} realm - the ShadowRealm to evaluate the code in
* @param {string} asyncBody - the code to evaluate; will be put in the body of
* an async function, and must return a value explicitly if a value is to be
* returned to the hosting realm.
*/
globalThis.shadowRealmEvalAsync = function (realm, asyncBody) {
return new Promise(realm.evaluate(`
(resolve, reject) => {
(async () => {
${asyncBody}
})().then(resolve, (e) => reject(e.toString() + "\\n" + (e.stack || "")));
}
`));
};
/**
* Convenience adaptor function for fetch() that can be passed to
* setShadowRealmGlobalProperties() (see testharness-shadowrealm-inner.js).
* Used to adapt the hosting realm's fetch(), if present, to fetch a resource
* and pass its text through the callable boundary to the ShadowRealm.
*/
globalThis.fetchAdaptor = (resource) => (resolve, reject) => {
fetch(resource)
.then(res => res.text())
.then(resolve, (e) => reject(e.toString()));
};
let workerMessagePortPromise;
/**
* Used when the hosting realm is a worker. This value is a Promise that
* resolves to a function that posts a message to the worker's message port,
* just like postMessage(). The message port is only available asynchronously in
* SharedWorkers and ServiceWorkers.
*/
globalThis.getPostMessageFunc = async function () {
if (typeof postMessage === "function") {
return postMessage; // postMessage available directly in dedicated worker
}
if (workerMessagePortPromise) {
return await workerMessagePortPromise;
}
throw new Error("getPostMessageFunc is intended for Worker scopes");
}
// Port available asynchronously in shared worker, but not via an async func
let savedResolver;
if (globalThis.constructor.name === "SharedWorkerGlobalScope") {
workerMessagePortPromise = new Promise((resolve) => {
savedResolver = resolve;
});
addEventListener("connect", function (event) {
const port = event.ports[0];
savedResolver(port.postMessage.bind(port));
});
} else if (globalThis.constructor.name === "ServiceWorkerGlobalScope") {
workerMessagePortPromise = new Promise((resolve) => {
savedResolver = resolve;
});
addEventListener("message", (e) => {
if (typeof e.data === "object" && e.data !== null && e.data.type === "connect") {
const client = e.source;
savedResolver(client.postMessage.bind(client));
}
});
}
/**
* Used when the hosting realm does not permit dynamic import, e.g. in
* ServiceWorkers or AudioWorklets. Requires an adaptor function such as
* fetchAdaptor() above, or an equivalent if fetch() is not present in the
* hosting realm.
*
* @param {ShadowRealm} realm - the ShadowRealm in which to setup a
* fakeDynamicImport() global function.
* @param {function} adaptor - an adaptor function that does what fetchAdaptor()
* does.
*/
globalThis.setupFakeDynamicImportInShadowRealm = function(realm, adaptor) {
function fetchModuleTextExecutor(url) {
return (resolve, reject) => {
new Promise(adaptor(url))
.then(text => realm.evaluate(text + ";\nundefined"))
.then(resolve, (e) => reject(e.toString()));
}
}
realm.evaluate(`
(fetchModuleTextExecutor) => {
globalThis.fakeDynamicImport = function (url) {
return new Promise(fetchModuleTextExecutor(url));
}
}
`)(fetchModuleTextExecutor);
};
/**
* Used when the hosting realm does not expose fetch(), i.e. in worklets. The
* port on the other side of the channel needs to send messages starting with
* 'fetchRequest::' and listen for messages starting with 'fetchResult::'. See
* testharness-shadowrealm-audioworkletprocessor.js.
*
* @param {port} MessagePort - the message port on which to listen for fetch
* requests
*/
globalThis.setupFakeFetchOverMessagePort = function (port) {
port.addEventListener("message", (event) => {
if (typeof event.data !== "string" || !event.data.startsWith("fetchRequest::")) {
return;
}
fetch(event.data.slice("fetchRequest::".length))
.then(res => res.text())
.then(
text => port.postMessage(`fetchResult::success::${text}`),
error => port.postMessage(`fetchResult::fail::${error}`),
);
});
port.start();
}
/**
* Returns a message suitable for posting with postMessage() that will signal to
* the test harness that the tests are finished and there was an error in the
* setup code.
*
* @param {message} any - error
*/
globalThis.createSetupErrorResult = function (message) {
return {
type: "complete",
tests: [],
asserts: [],
status: {
status: 1, // TestsStatus.ERROR,
message: String(message),
stack: typeof message === "object" && message !== null && "stack" in message ? message.stack : undefined,
},
};
};

File diff suppressed because it is too large Load diff

View file

@ -14,31 +14,6 @@
* parameters they are called with see testharness.js
*/
function dump_test_results(tests, status) {
var results_element = document.createElement("script");
results_element.type = "text/json";
results_element.id = "__testharness__results__";
var test_results = tests.map(function(x) {
return {name:x.name, status:x.status, message:x.message, stack:x.stack}
});
var data = {test:window.location.href,
tests:test_results,
status: status.status,
message: status.message,
stack: status.stack};
results_element.textContent = JSON.stringify(data);
// To avoid a HierarchyRequestError with XML documents, ensure that 'results_element'
// is inserted at a location that results in a valid document.
var parent = document.body
? document.body // <body> is required in XHTML documents
: document.documentElement; // fallback for optional <body> in HTML5, SVG, etc.
parent.appendChild(results_element);
}
add_completion_callback(dump_test_results);
/* If the parent window has a testharness_properties object,
* we use this to provide the test settings. This is used by the
* default in-browser runner to configure the timeout and the

View file

@ -0,0 +1,408 @@
'use strict'
// Convert `manufacturerData` to an array of bluetooth.BluetoothManufacturerData
// defined in
// https://webbluetoothcg.github.io/web-bluetooth/#bluetooth-bidi-definitions.
function convertToBidiManufacturerData(manufacturerData) {
const bidiManufacturerData = [];
for (const key in manufacturerData) {
bidiManufacturerData.push({
key: parseInt(key),
data: btoa(String.fromCharCode(...manufacturerData[key]))
})
}
return bidiManufacturerData;
}
function ArrayToMojoCharacteristicProperties(arr) {
const struct = {};
arr.forEach(property => {
struct[property] = true;
});
return struct;
}
class FakeBluetooth {
constructor() {
this.fake_central_ = null;
}
// Returns a promise that resolves with a FakeCentral that clients can use
// to simulate events that a device in the Central/Observer role would
// receive as well as monitor the operations performed by the device in the
// Central/Observer role.
//
// A "Central" object would allow its clients to receive advertising events
// and initiate connections to peripherals i.e. operations of two roles
// defined by the Bluetooth Spec: Observer and Central.
// See Bluetooth 4.2 Vol 3 Part C 2.2.2 "Roles when Operating over an
// LE Physical Transport".
async simulateCentral({state}) {
if (this.fake_central_) {
throw 'simulateCentral() should only be called once';
}
await test_driver.bidi.bluetooth.simulate_adapter({state: state});
this.fake_central_ = new FakeCentral();
return this.fake_central_;
}
}
// FakeCentral allows clients to simulate events that a device in the
// Central/Observer role would receive as well as monitor the operations
// performed by the device in the Central/Observer role.
class FakeCentral {
constructor() {
this.peripherals_ = new Map();
}
// Simulates a peripheral with |address|, |name|, |manufacturerData| and
// |known_service_uuids| that has already been connected to the system. If the
// peripheral existed already it updates its name, manufacturer data, and
// known UUIDs. |known_service_uuids| should be an array of
// BluetoothServiceUUIDs
// https://webbluetoothcg.github.io/web-bluetooth/#typedefdef-bluetoothserviceuuid
//
// Platforms offer methods to retrieve devices that have already been
// connected to the system or weren't connected through the UA e.g. a user
// connected a peripheral through the system's settings. This method is
// intended to simulate peripherals that those methods would return.
async simulatePreconnectedPeripheral(
{address, name, manufacturerData = {}, knownServiceUUIDs = []}) {
await test_driver.bidi.bluetooth.simulate_preconnected_peripheral({
address: address,
name: name,
manufacturerData: convertToBidiManufacturerData(manufacturerData),
knownServiceUuids:
knownServiceUUIDs.map(uuid => BluetoothUUID.getService(uuid))
});
return this.fetchOrCreatePeripheral_(address);
}
// Create a fake_peripheral object from the given address.
fetchOrCreatePeripheral_(address) {
let peripheral = this.peripherals_.get(address);
if (peripheral === undefined) {
peripheral = new FakePeripheral(address);
this.peripherals_.set(address, peripheral);
}
return peripheral;
}
}
class FakePeripheral {
constructor(address) {
this.address = address;
}
// Adds a fake GATT Service with |uuid| to be discovered when discovering
// the peripheral's GATT Attributes. Returns a FakeRemoteGATTService
// corresponding to this service. |uuid| should be a BluetoothServiceUUIDs
// https://webbluetoothcg.github.io/web-bluetooth/#typedefdef-bluetoothserviceuuid
async addFakeService({uuid}) {
const service_uuid = BluetoothUUID.getService(uuid);
await test_driver.bidi.bluetooth.simulate_service({
address: this.address,
uuid: service_uuid,
type: 'add',
});
return new FakeRemoteGATTService(service_uuid, this.address);
}
// Sets the next GATT Connection request response to |code|. |code| could be
// an HCI Error Code from BT 4.2 Vol 2 Part D 1.3 List Of Error Codes or a
// number outside that range returned by specific platforms e.g. Android
// returns 0x101 to signal a GATT failure
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#GATT_FAILURE
async setNextGATTConnectionResponse({code}) {
const remove_handler =
test_driver.bidi.bluetooth.gatt_connection_attempted.on((event) => {
if (event.address != this.address) {
return;
}
remove_handler();
test_driver.bidi.bluetooth.simulate_gatt_connection_response({
address: event.address,
code,
});
});
}
async setNextGATTDiscoveryResponse({code}) {
// No-op for Web Bluetooth Bidi test, it will be removed when migration
// completes.
return Promise.resolve();
}
// Simulates a GATT connection response with |code| from the peripheral.
async simulateGATTConnectionResponse(code) {
await test_driver.bidi.bluetooth.simulate_gatt_connection_response(
{address: this.address, code});
}
// Simulates a GATT disconnection in the peripheral.
async simulateGATTDisconnection() {
await test_driver.bidi.bluetooth.simulate_gatt_disconnection(
{address: this.address});
}
}
class FakeRemoteGATTService {
constructor(service_uuid, peripheral_address) {
this.service_uuid_ = service_uuid;
this.peripheral_address_ = peripheral_address;
}
// Adds a fake GATT Characteristic with |uuid| and |properties|
// to this fake service. The characteristic will be found when discovering
// the peripheral's GATT Attributes. Returns a FakeRemoteGATTCharacteristic
// corresponding to the added characteristic.
async addFakeCharacteristic({uuid, properties}) {
const characteristic_uuid = BluetoothUUID.getCharacteristic(uuid);
await test_driver.bidi.bluetooth.simulate_characteristic({
address: this.peripheral_address_,
serviceUuid: this.service_uuid_,
characteristicUuid: characteristic_uuid,
characteristicProperties: ArrayToMojoCharacteristicProperties(properties),
type: 'add'
});
return new FakeRemoteGATTCharacteristic(
characteristic_uuid, this.service_uuid_, this.peripheral_address_);
}
// Removes the fake GATT service from its fake peripheral.
async remove() {
await test_driver.bidi.bluetooth.simulate_service({
address: this.peripheral_address_,
uuid: this.service_uuid_,
type: 'remove'
});
}
}
class FakeRemoteGATTCharacteristic {
constructor(characteristic_uuid, service_uuid, peripheral_address) {
this.characteristic_uuid_ = characteristic_uuid;
this.service_uuid_ = service_uuid;
this.peripheral_address_ = peripheral_address;
this.last_written_value_ = {lastValue: null, lastWriteType: 'none'};
}
// Adds a fake GATT Descriptor with |uuid| to be discovered when
// discovering the peripheral's GATT Attributes. Returns a
// FakeRemoteGATTDescriptor corresponding to this descriptor. |uuid| should
// be a BluetoothDescriptorUUID
// https://webbluetoothcg.github.io/web-bluetooth/#typedefdef-bluetoothdescriptoruuid
async addFakeDescriptor({uuid}) {
const descriptor_uuid = BluetoothUUID.getDescriptor(uuid);
await test_driver.bidi.bluetooth.simulate_descriptor({
address: this.peripheral_address_,
serviceUuid: this.service_uuid_,
characteristicUuid: this.characteristic_uuid_,
descriptorUuid: descriptor_uuid,
type: 'add'
});
return new FakeRemoteGATTDescriptor(
descriptor_uuid, this.characteristic_uuid_, this.service_uuid_,
this.peripheral_address_);
}
// Simulate a characteristic for operation |type| with response |code| and
// |data|.
async simulateResponse(type, code, data) {
await test_driver.bidi.bluetooth.simulate_characteristic_response({
address: this.peripheral_address_,
serviceUuid: this.service_uuid_,
characteristicUuid: this.characteristic_uuid_,
type,
code,
data,
});
}
// Simulate a characteristic response for read operation with response |code|
// and |data|.
async simulateReadResponse(code, data) {
await this.simulateResponse('read', code, data);
}
// Simulate a characteristic response for write operation with response
// |code|.
async simulateWriteResponse(code) {
await this.simulateResponse('write', code);
}
// Sets the next read response for characteristic to |code| and |value|.
// |code| could be a GATT Error Response from
// BT 4.2 Vol 3 Part F 3.4.1.1 Error Response or a number outside that range
// returned by specific platforms e.g. Android returns 0x101 to signal a GATT
// failure.
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#GATT_FAILURE
async setNextReadResponse(gatt_code, value = null) {
if (gatt_code === 0 && value === null) {
throw '|value| can\'t be null if read should success.';
}
if (gatt_code !== 0 && value !== null) {
throw '|value| must be null if read should fail.';
}
const remove_handler =
test_driver.bidi.bluetooth.characteristic_event_generated.on(
(event) => {
if (event.address != this.peripheral_address_) {
return;
}
remove_handler();
this.simulateReadResponse(gatt_code, value);
});
}
// Sets the next write response for this characteristic to |code|. If
// writing to a characteristic that only supports 'write-without-response'
// the set response will be ignored.
// |code| could be a GATT Error Response from
// BT 4.2 Vol 3 Part F 3.4.1.1 Error Response or a number outside that range
// returned by specific platforms e.g. Android returns 0x101 to signal a GATT
// failure.
async setNextWriteResponse(gatt_code) {
const remove_handler =
test_driver.bidi.bluetooth.characteristic_event_generated.on(
(event) => {
if (event.address != this.peripheral_address_) {
return;
}
this.last_written_value_ = {
lastValue: event.data,
lastWriteType: event.type
};
remove_handler();
if (event.type == 'write-with-response') {
this.simulateWriteResponse(gatt_code);
}
});
}
// Gets the last successfully written value to the characteristic and its
// write type. Write type is one of 'none', 'default-deprecated',
// 'with-response', 'without-response'. Returns {lastValue: null,
// lastWriteType: 'none'} if no value has yet been written to the
// characteristic.
async getLastWrittenValue() {
return this.last_written_value_;
}
// Removes the fake GATT Characteristic from its fake service.
async remove() {
await test_driver.bidi.bluetooth.simulate_characteristic({
address: this.peripheral_address_,
serviceUuid: this.service_uuid_,
characteristicUuid: this.characteristic_uuid_,
characteristicProperties: undefined,
type: 'remove'
});
}
}
class FakeRemoteGATTDescriptor {
constructor(
descriptor_uuid, characteristic_uuid, service_uuid, peripheral_address) {
this.descriptor_uuid_ = descriptor_uuid;
this.characteristic_uuid_ = characteristic_uuid;
this.service_uuid_ = service_uuid;
this.peripheral_address_ = peripheral_address;
this.last_written_value_ = null;
}
// Simulate a descriptor for operation |type| with response |code| and
// |data|.
async simulateResponse(type, code, data) {
await test_driver.bidi.bluetooth.simulate_descriptor_response({
address: this.peripheral_address_,
serviceUuid: this.service_uuid_,
characteristicUuid: this.characteristic_uuid_,
descriptorUuid: this.descriptor_uuid_,
type,
code,
data,
});
}
// Simulate a descriptor response for read operation with response |code| and
// |data|.
async simulateReadResponse(code, data) {
await this.simulateResponse('read', code, data);
}
// Simulate a descriptor response for write operation with response |code|.
async simulateWriteResponse(code) {
await this.simulateResponse('write', code);
}
// Sets the next read response for descriptor to |code| and |value|.
// |code| could be a GATT Error Response from
// BT 4.2 Vol 3 Part F 3.4.1.1 Error Response or a number outside that range
// returned by specific platforms e.g. Android returns 0x101 to signal a GATT
// failure.
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#GATT_FAILURE
async setNextReadResponse(gatt_code, value = null) {
if (gatt_code === 0 && value === null) {
throw '|value| can\'t be null if read should success.';
}
if (gatt_code !== 0 && value !== null) {
throw '|value| must be null if read should fail.';
}
const remove_handler =
test_driver.bidi.bluetooth.descriptor_event_generated.on((event) => {
if (event.address != this.peripheral_address_) {
return;
}
remove_handler();
this.simulateReadResponse(gatt_code, value);
});
}
// Sets the next write response for this descriptor to |code|.
// |code| could be a GATT Error Response from
// BT 4.2 Vol 3 Part F 3.4.1.1 Error Response or a number outside that range
// returned by specific platforms e.g. Android returns 0x101 to signal a GATT
// failure.
async setNextWriteResponse(gatt_code) {
const remove_handler =
test_driver.bidi.bluetooth.descriptor_event_generated.on((event) => {
if (event.address != this.peripheral_address_) {
return;
}
this.last_written_value_ = {
lastValue: event.data,
lastWriteType: event.type
};
remove_handler();
if (event.type == 'write-with-response') {
this.simulateWriteResponse(gatt_code);
}
});
}
// Gets the last successfully written value to the descriptor.
// Returns null if no value has yet been written to the descriptor.
async getLastWrittenValue() {
return this.last_written_value_;
}
// Removes the fake GATT Descriptor from its fake characteristic.
async remove() {
await test_driver.bidi.bluetooth.simulate_descriptor({
address: this.peripheral_address_,
serviceUuid: this.service_uuid_,
characteristicUuid: this.characteristic_uuid_,
descriptorUuid: this.descriptor_uuid_,
type: 'remove'
});
}
}
function initializeBluetoothBidiResources() {
navigator.bluetooth.test = new FakeBluetooth();
}

View file

@ -64,7 +64,7 @@
"path": "resource-timing"
},
"resources": {
"commit": "1e140d63ec885703ce24b3798abd81912696bb85",
"commit": "1d2c5fb36a6e477c8f915bde7eca027be6abe792",
"path": "resources"
},
"streams": {
@ -96,7 +96,7 @@
"path": "web-locks"
},
"WebCryptoAPI": {
"commit": "ab08796857072c5a93e27e0a25effba2e07dad11",
"commit": "1d2c5fb36a6e477c8f915bde7eca027be6abe792",
"path": "WebCryptoAPI"
},
"webidl/ecmascript-binding/es-exceptions": {
@ -108,7 +108,7 @@
"path": "webmessaging/broadcastchannel"
},
"webstorage": {
"commit": "1291340aaaa6e73db43b412e47401eca3830c556",
"commit": "1d2c5fb36a6e477c8f915bde7eca027be6abe792",
"path": "webstorage"
}
}

View file

@ -1,16 +1,18 @@
test(function() {
test(t => {
localStorage.clear();
var index = 0;
var key = "name";
var val = "x".repeat(1024);
assert_throws_dom("QUOTA_EXCEEDED_ERR", function() {
t.add_cleanup(() => {
localStorage.clear();
});
assert_throws_quotaexceedederror(() => {
while (true) {
index++;
localStorage.setItem("" + key + index, "" + val + index);
}
});
localStorage.clear();
}, null, null);
}, "Throws QuotaExceededError when the quota has been exceeded");

View file

@ -1,16 +1,18 @@
test(function() {
test(t => {
sessionStorage.clear();
var index = 0;
var key = "name";
var val = "x".repeat(1024);
assert_throws_dom("QUOTA_EXCEEDED_ERR", function() {
t.add_cleanup(() => {
sessionStorage.clear();
});
assert_throws_quotaexceedederror(() => {
while (true) {
index++;
sessionStorage.setItem("" + key + index, "" + val + index);
}
});
sessionStorage.clear();
}, null, null);
}, "Throws QuotaExceededError when the quota has been exceeded");

View file

@ -39,10 +39,10 @@
Object.defineProperty(storage, key, { "value": "test", "configurable": false });
assert_equals(storage[key], "test");
var desc = Object.getOwnPropertyDescriptor(storage, key);
assert_true(desc.configurable, "configurable");
assert_false(desc.configurable, "configurable");
assert_true(delete storage[key]);
assert_equals(storage[key], undefined);
assert_false(delete storage[key]);
assert_equals(storage[key], "test");
}, name + ": defineProperty not configurable");
test(function() {

View file

@ -26,4 +26,20 @@ module.exports = {
],
},
},
'getRandomValues.any.js': {
'fail': {
'note': 'https://github.com/nodejs/node/issues/58987',
'expected': [
'Large length: Int8Array',
'Large length: Int16Array',
'Large length: Int32Array',
'Large length: BigInt64Array',
'Large length: Uint8Array',
'Large length: Uint8ClampedArray',
'Large length: Uint16Array',
'Large length: Uint32Array',
'Large length: BigUint64Array',
],
},
},
};

View file

@ -22,5 +22,30 @@
},
"storage_session_window_reopen.window.js": {
"skip": "window.open() is not supported in Node.js."
},
"storage_session_setitem_quotaexceedederr.window.js": {
"fail": {
"note": "https://github.com/nodejs/node/issues/58987",
"expected": [
"Throws QuotaExceededError when the quota has been exceeded"
]
}
},
"storage_local_setitem_quotaexceedederr.window.js": {
"fail": {
"note": "https://github.com/nodejs/node/issues/58987",
"expected": [
"Throws QuotaExceededError when the quota has been exceeded"
]
}
},
"symbol-props.window.js": {
"fail": {
"note": "https://github.com/nodejs/node/issues/59310",
"expected": [
"localStorage: defineProperty not configurable",
"sessionStorage: defineProperty not configurable"
]
}
}
}