mirror of
https://github.com/nodejs/node.git
synced 2025-08-15 13:48:44 +02:00
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:
parent
6d8aa55a2c
commit
8e5c5b3b04
35 changed files with 2570 additions and 593 deletions
6
test/fixtures/wpt/README.md
vendored
6
test/fixtures/wpt/README.md
vendored
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
2
test/fixtures/wpt/resources/channel.sub.js
vendored
2
test/fixtures/wpt/resources/channel.sub.js
vendored
|
@ -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.
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
|
245
test/fixtures/wpt/resources/check-layout.js
vendored
245
test/fixtures/wpt/resources/check-layout.js
vendored
|
@ -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;
|
||||
}
|
||||
|
||||
})();
|
|
@ -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);
|
||||
});
|
||||
}
|
|
@ -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:
|
31
test/fixtures/wpt/resources/idlharness.js
vendored
31
test/fixtures/wpt/resources/idlharness.js
vendored
|
@ -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';
|
||||
|
|
5
test/fixtures/wpt/resources/out-of-scope-test.js
vendored
Normal file
5
test/fixtures/wpt/resources/out-of-scope-test.js
vendored
Normal 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");
|
||||
|
|
@ -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
|
||||
|
|
2
test/fixtures/wpt/resources/testdriver-actions.js.headers
vendored
Normal file
2
test/fixtures/wpt/resources/testdriver-actions.js.headers
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
Content-Type: text/javascript; charset=utf-8
|
||||
Cache-Control: max-age=3600
|
1366
test/fixtures/wpt/resources/testdriver.js
vendored
1366
test/fixtures/wpt/resources/testdriver.js
vendored
File diff suppressed because it is too large
Load diff
52
test/fixtures/wpt/resources/testharness-shadowrealm-audioworkletprocessor.js
vendored
Normal file
52
test/fixtures/wpt/resources/testharness-shadowrealm-audioworkletprocessor.js
vendored
Normal 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);
|
38
test/fixtures/wpt/resources/testharness-shadowrealm-inner.js
vendored
Normal file
38
test/fixtures/wpt/resources/testharness-shadowrealm-inner.js
vendored
Normal 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; },
|
||||
};
|
151
test/fixtures/wpt/resources/testharness-shadowrealm-outer.js
vendored
Normal file
151
test/fixtures/wpt/resources/testharness-shadowrealm-outer.js
vendored
Normal 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,
|
||||
},
|
||||
};
|
||||
};
|
652
test/fixtures/wpt/resources/testharness.js
vendored
652
test/fixtures/wpt/resources/testharness.js
vendored
File diff suppressed because it is too large
Load diff
25
test/fixtures/wpt/resources/testharnessreport.js
vendored
25
test/fixtures/wpt/resources/testharnessreport.js
vendored
|
@ -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
|
||||
|
|
408
test/fixtures/wpt/resources/web-bluetooth-bidi-test.js
vendored
Normal file
408
test/fixtures/wpt/resources/web-bluetooth-bidi-test.js
vendored
Normal 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();
|
||||
}
|
6
test/fixtures/wpt/versions.json
vendored
6
test/fixtures/wpt/versions.json
vendored
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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',
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue