Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update WPT usage #1032

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
336 changes: 174 additions & 162 deletions README.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@
"jest-junit": "^16.0.0",
"js-yaml": "^4.1.0",
"jsdom": "^20.0.0",
"minimatch": "^9.0.0",
"mocha": "^10.0.0",
"minimatch": "^9.0.3",
"mocha": "^10.3.0",
"mocha-sugar-free": "^1.4.0",
"prettier": "^3.0.0",
"q": "^1.5.1",
Expand Down
321 changes: 162 additions & 159 deletions tests/cypress/integration/web-platform-test.cy.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion tests/wpt
Submodule wpt updated 19688 files
4 changes: 2 additions & 2 deletions tests/wpt-jsdom/ATTAcomm.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ class ATTAcomm {

if (matcher === "property") {
if (name === namePropertyName[implementation]) {
const actual = computeAccessibleName(element);
const actual = computeAccessibleName(element) ?? "";
if (equality === "is") {
assert_equals(actual, expected);
continue;
}
} else if (name === descriptionPropertyName[implementation]) {
const actual = computeAccessibleDescription(element);
const actual = computeAccessibleDescription(element) ?? "";
if (equality === "is") {
assert_equals(actual, expected);
continue;
Expand Down
2 changes: 1 addition & 1 deletion tests/wpt-jsdom/README.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
See https://github.com/jsdom/jsdom/blob/67eb73efc22d85b2aa3c7b0237be58ac5129b584/test/README.md#L1
See https://github.com/jsdom/jsdom/blob/2f8a730/test/README.md
234 changes: 200 additions & 34 deletions tests/wpt-jsdom/run-single-wpt.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,31 @@ const { specify } = require("mocha-sugar-free");
const { inBrowserContext } = require("./util.js");
const { JSDOM, VirtualConsole } = require("jsdom");
const ResourceLoader = require("jsdom/lib/jsdom/browser/resources/resource-loader");
const { resolveReason } = require("./utils.js");
const {
computeAccessibleName,
computeAccessibleDescription,
getRole,
} = require("../../dist/");

const reporterPathname = "/resources/testharnessreport.js";
const testdriverPathname = "/resources/testdriver.js";
const ATTAcommPathname = "/wai-aria/scripts/ATTAcomm.js";

const unexpectedPassingTestMessage = `
Hey, did you fix a bug? This test used to be failing, but during
this run there were no errors. If you have fixed the issue covered
by this test, you can edit the "to-run.yaml" file and remove the line
containing this test. Thanks!
`;

module.exports = (urlPrefixFactory) => {
if (inBrowserContext()) {
return () => {
// TODO: browser support for running WPT
};
}

return (testPath, title = testPath, expectFail) => {
return (testPath, title = testPath, expectFail = false) => {
specify({
title,
expectPromise: true,
Expand All @@ -42,13 +52,23 @@ class CustomResourceLoader extends ResourceLoader {
fetch(urlString, options) {
const url = new URL(urlString);

if (url.pathname === reporterPathname) {
if (url.pathname === ATTAcommPathname) {
const filePath = path.resolve(__dirname, "./ATTAcomm.js");

return super.fetch(`file://${filePath}`, options);
} else if (url.pathname === testdriverPathname) {
const filePath = path.resolve(__dirname, "./testdriver.js");

return super.fetch(`file://${filePath}`, options);
} else if (url.pathname.startsWith("/resources/testdriver")) {
return Promise.resolve(Buffer.from("", "utf-8"));
} else if (url.pathname === reporterPathname) {
return Promise.resolve(Buffer.from("window.shimTest();", "utf-8"));
} else if (url.pathname.startsWith("/resources/")) {
// When running to-upstream tests, the server doesn't have a /resources/ directory.
// So, always go to the one in ./tests.
// So, always go to the one in ../wpt.
// The path replacement accounts for a rewrite performed by the WPT server:
// https://github.com/w3c/web-platform-tests/blob/master/tools/serve/serve.py#L271
// https://github.com/web-platform-tests/wpt/blob/master/tools/serve/serve.py#L271
const filePath = path
.resolve(__dirname, "../wpt" + url.pathname)
.replace(
Expand All @@ -57,18 +77,42 @@ class CustomResourceLoader extends ResourceLoader {
);

return super.fetch(`file://${filePath}`, options);
} else if (url.pathname === "/wai-aria/scripts/ATTAcomm.js") {
const filePath = path.resolve(__dirname, "./ATTAcomm.js");
return super.fetch(`file://${filePath}`, options);
}

return super.fetch(urlString, options);
}
}

function formatFailedTest(test) {
switch (test.status) {
case test.PASS:
return `Unexpected passing test: ${JSON.stringify(
test.name,
)}${unexpectedPassingTestMessage}`;
case test.FAIL:
case test.PRECONDITION_FAILED:
return `Failed in ${JSON.stringify(test.name)}:\n${test.message}\n\n${
test.stack
}`;
case test.TIMEOUT:
return `Timeout in ${JSON.stringify(test.name)}:\n${test.message}\n\n${
test.stack
}`;
case test.NOTRUN:
return `Uncompleted test ${JSON.stringify(test.name)}:\n${
test.message
}\n\n${test.stack}`;
default:
throw new RangeError(
`Unexpected test status: ${test.status} (test: ${JSON.stringify(
test.name,
)})`,
);
}
}

function createJSDOM(urlPrefix, testPath, expectFail) {
const unhandledExceptions = [];
const doneErrors = [];

let allowUnhandledExceptions = false;

Expand Down Expand Up @@ -102,27 +146,108 @@ function createJSDOM(urlPrefix, testPath, expectFail) {

window.computeAccessibleName = computeAccessibleName;
window.computeAccessibleDescription = computeAccessibleDescription;
window.getRole = getRole;

window.shimTest = () => {
const oldSetup = window.setup;
window.setup = () => {
// noop, otherwise failing tests just slowly timeout
window.setup = (options) => {
if (options.allow_uncaught_exception) {
allowUnhandledExceptions = true;
}
oldSetup(options);
};

window.add_result_callback((test) => {
if (test.status === 1) {
errors.push(
`Failed in "${test.name}": \n${test.message}\n\n${test.stack}`,
// Overriding assert_throws_js and friends in order to allow us to throw exceptions from another realm. See
// https://github.com/jsdom/jsdom/issues/2727 for more information.

function assertThrowsJSImpl(
constructor,
func,
description,
assertionType,
) {
try {
func.call(this);
window.assert_true(
false,
`${assertionType}: ${description}: ${func} did not throw`,
);
} else if (test.status === 2) {
errors.push(
`Timeout in "${test.name}": \n${test.message}\n\n${test.stack}`,
} catch (e) {
if (e instanceof window.AssertionError) {
throw e;
}

// Basic sanity-checks on the thrown exception.
window.assert_true(
typeof e === "object",
`${assertionType}: ${description}: ${func} threw ${e} with type ${typeof e}, not an object`,
);
} else if (test.status === 3) {
errors.push(
`Uncompleted test "${test.name}": \n${test.message}\n\n${test.stack}`,

window.assert_true(
e !== null,
`${assertionType}: ${description}: ${func} threw null, not an object`,
);

// Basic sanity-check on the passed-in constructor
window.assert_true(
typeof constructor === "function",
`${assertionType}: ${description}: ${constructor} is not a constructor`,
);
let obj = constructor;
while (obj) {
if (typeof obj === "function" && obj.name === "Error") {
break;
}
obj = Object.getPrototypeOf(obj);
}
window.assert_true(
obj !== null && obj !== undefined,
`${assertionType}: ${description}: ${constructor} is not an Error subtype`,
);

// And checking that our exception is reasonable
window.assert_equals(
e.name,
constructor.name,
`${assertionType}: ${description}: ${func} threw ${e} (${e.name}) ` +
`expected instance of ${constructor.name}`,
);
}
}

// eslint-disable-next-line camelcase
window.assert_throws_js = (constructor, func, description) => {
assertThrowsJSImpl(
constructor,
func,
description,
"assert_throws_js",
);
};
// eslint-disable-next-line camelcase
window.promise_rejects_js = (test, expected, promise, description) => {
return promise
.then(test.unreached_func("Should have rejected: " + description))
.catch((e) => {
assertThrowsJSImpl(
expected,
() => {
throw e;
},
description,
"promise_reject_js",
);
});
};

window.add_result_callback((test) => {
if (
test.status === test.FAIL ||
test.status === test.TIMEOUT ||
test.status === test.NOTRUN
) {
errors.push(formatFailedTest(test));
}
});

window.add_completion_callback((tests, harnessStatus) => {
Expand All @@ -131,34 +256,75 @@ function createJSDOM(urlPrefix, testPath, expectFail) {
window.close();
});

if (harnessStatus.status === 2) {
let harnessFail = false;
if (harnessStatus.status === harnessStatus.ERROR) {
harnessFail = true;
errors.push(
new Error(
`test harness should not error: ${testPath}\n${harnessStatus.message}`,
),
);
} else if (harnessStatus.status === harnessStatus.TIMEOUT) {
harnessFail = true;
errors.push(
new Error(`test harness should not timeout: ${testPath}`),
);
}

errors.push(...doneErrors);
errors.push(...unhandledExceptions);

if (
typeof expectFail === "object" &&
(harnessFail || unhandledExceptions.length)
) {
expectFail = false;
}

if (errors.length === 0 && expectFail) {
reject(
new Error(`
Hey, did you fix a bug? This test used to be failing, but during
this run there were no errors. If you have fixed the issue covered
by this test, you can edit the "to-run.yaml" file and remove the line
containing this test. Thanks!
`),
);
} else if (errors.length === 1 && !expectFail) {
reject(new Error(unexpectedPassingTestMessage));
} else if (
errors.length === 1 &&
(tests.length === 1 || harnessFail) &&
!expectFail
) {
reject(new Error(errors[0]));
} else if (errors.length && !expectFail) {
reject(
new Error(
`${errors.length} errors in test:\n\n${errors.join("\n")}`,
`${errors.length}/${
tests.length
} errors in test:\n\n${errors.join("\n\n")}`,
),
);
} else {
} else if (typeof expectFail !== "object") {
resolve();
} else {
const unexpectedErrors = [];
for (const test of tests) {
const data = expectFail[test.name];
const reason = data && data[0];

const innerExpectFail = resolveReason(reason) === "expect-fail";
if (
innerExpectFail
? test.status === test.PASS
: test.status !== test.PASS
) {
unexpectedErrors.push(formatFailedTest(test));
}
}

if (unexpectedErrors.length) {
reject(
new Error(
`${unexpectedErrors.length}/${
tests.length
} errors in test:\n\n${unexpectedErrors.join("\n\n")}`,
),
);
} else {
resolve();
}
}
});
};
Expand Down
Loading
Loading