Tests/LibWeb: Import the WPT URL test suite

This commit is contained in:
Shannon Booth 2025-03-09 13:14:39 +13:00 committed by Jelle Raaijmakers
commit 2022c9e679
Notes: github-actions[bot] 2025-03-18 09:01:32 +00:00
59 changed files with 31488 additions and 0 deletions

View file

@ -0,0 +1,83 @@
(function() {
var subTestKeyPattern = null;
var match;
var collectKeys = false;
var collectCounts = false;
var keys = {};
var exclude = false;
if (location.search) {
match = /(?:^\?|&)(include|exclude)=([^&]+)?/.exec(location.search);
if (match) {
subTestKeyPattern = new RegExp(`^${match[2]}$`);
if (match[1] === 'exclude') {
exclude = true;
}
}
// Below is utility code to generate <meta> for copy/paste into tests.
// Sample usage:
// test.html?get-keys
match = /(?:^\?|&)get-keys(&get-counts)?(?:&|$)/.exec(location.search);
if (match) {
collectKeys = true;
if (match[1]) {
collectCounts = true;
}
add_completion_callback(() => {
var metas = [];
var template = '<meta name="variant" content="?include=%s">';
if (collectCounts) {
template += ' <!--%s-->';
}
for (var key in keys) {
var meta = template.replace("%s", key);
if (collectCounts) {
meta = meta.replace("%s", keys[key]);
}
metas.push(meta);
}
var pre = document.createElement('pre');
pre.textContent = metas.join('\n') + '\n';
document.body.insertBefore(pre, document.body.firstChild);
document.getSelection().selectAllChildren(pre);
});
}
}
/**
* Check if `key` is in the subset specified in the URL.
* @param {string} key
* @returns {boolean}
*/
function shouldRunSubTest(key) {
if (key && subTestKeyPattern) {
var found = subTestKeyPattern.test(key);
if (exclude) {
return !found;
}
return found;
}
return true;
}
/**
* Only test a subset of tests with `?include=Foo` or `?exclude=Foo` in the URL.
* Can be used together with `<meta name="variant" content="...">`
* Sample usage:
* for (const test of tests) {
* subsetTestByKey("Foo", async_test, test.fn, test.name);
* }
*/
function subsetTestByKey(key, testFunc, ...args) {
if (collectKeys) {
if (collectCounts && key in keys) {
keys[key]++;
} else {
keys[key] = 1;
}
}
if (shouldRunSubTest(key)) {
return testFunc(...args);
}
return null;
}
self.shouldRunSubTest = shouldRunSubTest;
self.subsetTestByKey = subsetTestByKey;
})();

View file

@ -0,0 +1,8 @@
<!doctype html>
<meta charset=utf-8>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<div id=log></div>
<script src="../url/IdnaTestV2.window.js"></script>

View file

@ -0,0 +1,24 @@
promise_test(() => fetch("resources/IdnaTestV2.json").then(res => res.json()).then(runTests), "Loading data…");
function runTests(idnaTests) {
for (const idnaTest of idnaTests) {
if (typeof idnaTest === "string") {
continue // skip comments
}
if (idnaTest.input === "") {
continue // cannot test empty string input through new URL()
}
test(() => {
if (idnaTest.output === null) {
assert_throws_js(TypeError, () => new URL(`https://${idnaTest.input}/x`));
} else {
const url = new URL(`https://${idnaTest.input}/x`);
assert_equals(url.host, idnaTest.output);
assert_equals(url.hostname, idnaTest.output);
assert_equals(url.pathname, "/x");
assert_equals(url.href, `https://${idnaTest.output}/x`);
}
}, `ToASCII("${idnaTest.input}")${idnaTest.comment ? " " + idnaTest.comment : ""}`);
}
}

View file

@ -0,0 +1,12 @@
<!doctype html>
<meta charset=utf-8>
<script src=../resources/testharness.js></script>
<script src=../resources/testharnessreport.js></script>
<base id=base>
<div id=log></div>
<script src=resources/a-element-origin.js></script>
<!--
Other dependencies:
* resources/urltestdata.json,
* resources/urltestdata-javascript-only.json,
-->

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,39 @@
promise_test(() => Promise.all([
fetch("resources/urltestdata.json").then(res => res.json()),
fetch("resources/urltestdata-javascript-only.json").then(res => res.json()),
]).then((tests) => tests.flat()).then(runURLTests), "Loading data…");
function setBase(base) {
document.getElementById("base").href = base
}
function bURL(url, base) {
setBase(base);
const a = document.createElement("a");
a.setAttribute("href", url);
return a;
}
function runURLTests(urlTests) {
for (const expected of urlTests) {
// Skip comments and tests without "origin" expectation
if (typeof expected === "string" || !("origin" in expected))
continue;
// Fragments are relative against "about:blank" (this might always be redundant due to requiring "origin" in expected)
if (expected.base === null && expected.input.startsWith("#"))
continue;
// HTML special cases data: and javascript: URLs in <base>
if (expected.base !== null && (expected.base.startsWith("data:") || expected.base.startsWith("javascript:")))
continue;
// We cannot use a null base for HTML tests
const base = expected.base === null ? "about:blank" : expected.base;
test(function() {
var url = bURL(expected.input, base)
assert_equals(url.origin, expected.origin, "origin")
}, "Parsing origin: <" + expected.input + "> against <" + base + ">")
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,18 @@
[
"See ../README.md for a description of the format.",
{
"input": "http://example.com/\uD800\uD801\uDFFE\uDFFF\uFDD0\uFDCF\uFDEF\uFDF0\uFFFE\uFFFF?\uD800\uD801\uDFFE\uDFFF\uFDD0\uFDCF\uFDEF\uFDF0\uFFFE\uFFFF",
"base": null,
"href": "http://example.com/%EF%BF%BD%F0%90%9F%BE%EF%BF%BD%EF%B7%90%EF%B7%8F%EF%B7%AF%EF%B7%B0%EF%BF%BE%EF%BF%BF?%EF%BF%BD%F0%90%9F%BE%EF%BF%BD%EF%B7%90%EF%B7%8F%EF%B7%AF%EF%B7%B0%EF%BF%BE%EF%BF%BF",
"origin": "http://example.com",
"protocol": "http:",
"username": "",
"password": "",
"host": "example.com",
"hostname": "example.com",
"port": "",
"pathname": "/%EF%BF%BD%F0%90%9F%BE%EF%BF%BD%EF%B7%90%EF%B7%8F%EF%B7%AF%EF%B7%B0%EF%BF%BE%EF%BF%BF",
"search": "?%EF%BF%BD%F0%90%9F%BE%EF%BF%BD%EF%B7%90%EF%B7%8F%EF%B7%AF%EF%B7%B0%EF%BF%BE%EF%BF%BF",
"hash": ""
}
]

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,15 @@
<!doctype html>
<meta charset=utf-8>
<meta name="timeout" content="long">
<script>
self.GLOBAL = {
isWindow: function() { return true; },
isWorker: function() { return false; },
isShadowRealm: function() { return false; },
};
</script>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<script src="../common/subset-tests-by-key.js"></script>
<div id=log></div>
<script src="../url/url-constructor.any.js"></script>

View file

@ -0,0 +1,56 @@
// META: script=/common/subset-tests-by-key.js
// META: timeout=long
// META: variant=?include=file
// META: variant=?include=javascript
// META: variant=?include=mailto
// META: variant=?exclude=(file|javascript|mailto)
function runURLTests(urlTests) {
for (const expected of urlTests) {
// Skip comments
if (typeof expected === "string")
continue;
const base = expected.base !== null ? expected.base : undefined;
function getKey(expected) {
if (expected.protocol) {
return expected.protocol.replace(":", "");
}
if (expected.failure) {
return expected.input.split(":")[0];
}
return "other";
}
subsetTestByKey(getKey(expected), test, function() {
if (expected.failure) {
assert_throws_js(TypeError, function() {
new URL(expected.input, base);
});
return;
}
const url = new URL(expected.input, base);
assert_equals(url.href, expected.href, "href")
assert_equals(url.protocol, expected.protocol, "protocol")
assert_equals(url.username, expected.username, "username")
assert_equals(url.password, expected.password, "password")
assert_equals(url.host, expected.host, "host")
assert_equals(url.hostname, expected.hostname, "hostname")
assert_equals(url.port, expected.port, "port")
assert_equals(url.pathname, expected.pathname, "pathname")
assert_equals(url.search, expected.search, "search")
if ("searchParams" in expected) {
assert_true("searchParams" in url)
assert_equals(url.searchParams.toString(), expected.searchParams, "searchParams")
}
assert_equals(url.hash, expected.hash, "hash")
}, `Parsing: <${expected.input}> ${base ? "against <" + base + ">" : "without base"}`)
}
}
promise_test(() => Promise.all([
fetch("resources/urltestdata.json").then(res => res.json()),
fetch("resources/urltestdata-javascript-only.json").then(res => res.json()),
]).then((tests) => tests.flat()).then(runURLTests), "Loading data…");

View file

@ -0,0 +1,15 @@
<!doctype html>
<meta charset=utf-8>
<script>
self.GLOBAL = {
isWindow: function() { return true; },
isWorker: function() { return false; },
isShadowRealm: function() { return false; },
};
</script>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<div id=log></div>
<script src="../url/url-origin.any.js"></script>

View file

@ -0,0 +1,19 @@
promise_test(() => Promise.all([
fetch("resources/urltestdata.json").then(res => res.json()),
fetch("resources/urltestdata-javascript-only.json").then(res => res.json()),
]).then((tests) => tests.flat()).then(runURLTests), "Loading data…");
function runURLTests(urlTests) {
for (const expected of urlTests) {
// Skip comments and tests without "origin" expectation
if (typeof expected === "string" || !("origin" in expected))
continue;
const base = expected.base !== null ? expected.base : undefined;
test(() => {
const url = new URL(expected.input, base);
assert_equals(url.origin, expected.origin, "origin");
}, `Origin parsing: <${expected.input}> ${base ? "against <" + base + ">" : "without base"}`);
}
}

View file

@ -0,0 +1,8 @@
<!doctype html>
<meta charset=utf-8>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<script src="../common/subset-tests-by-key.js"></script>
<div id=log></div>
<script src="../url/url-setters-a-area.window.js"></script>

View file

@ -0,0 +1,43 @@
// META: script=/common/subset-tests-by-key.js
// META: variant=?include=file
// META: variant=?include=javascript
// META: variant=?include=mailto
// META: variant=?exclude=(file|javascript|mailto)
// Keep this file in sync with url-setters.any.js.
promise_test(() => fetch("resources/setters_tests.json").then(res => res.json()).then(runURLSettersTests), "Loading data…");
function runURLSettersTests(all_test_cases) {
for (var attribute_to_be_set in all_test_cases) {
if (attribute_to_be_set == "comment") {
continue;
}
var test_cases = all_test_cases[attribute_to_be_set];
for(var i = 0, l = test_cases.length; i < l; i++) {
var test_case = test_cases[i];
var name = "Setting <" + test_case.href + ">." + attribute_to_be_set +
" = '" + test_case.new_value + "'";
if ("comment" in test_case) {
name += " " + test_case.comment;
}
const key = test_case.href.split(":")[0];
subsetTestByKey(key, test, function() {
var url = document.createElement("a");
url.href = test_case.href;
url[attribute_to_be_set] = test_case.new_value;
for (var attribute in test_case.expected) {
assert_equals(url[attribute], test_case.expected[attribute])
}
}, "<a>: " + name)
subsetTestByKey(key, test, function() {
var url = document.createElement("area");
url.href = test_case.href;
url[attribute_to_be_set] = test_case.new_value;
for (var attribute in test_case.expected) {
assert_equals(url[attribute], test_case.expected[attribute])
}
}, "<area>: " + name)
}
}
}

View file

@ -0,0 +1,15 @@
<!doctype html>
<meta charset=utf-8>
<script>
self.GLOBAL = {
isWindow: function() { return true; },
isWorker: function() { return false; },
isShadowRealm: function() { return false; },
};
</script>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<div id=log></div>
<script src="../url/url-setters-stripping.any.js"></script>

View file

@ -0,0 +1,125 @@
function urlString({ scheme = "https",
username = "username",
password = "password",
host = "host",
port = "8000",
pathname = "path",
search = "query",
hash = "fragment" }) {
return `${scheme}://${username}:${password}@${host}:${port}/${pathname}?${search}#${hash}`;
}
function urlRecord(scheme) {
return new URL(urlString({ scheme }));
}
for(const scheme of ["https", "wpt++"]) {
for(let i = 0; i < 0x20; i++) {
const stripped = i === 0x09 || i === 0x0A || i === 0x0D;
// It turns out that user agents are surprisingly similar for these ranges so generate fewer
// tests. If this is changed also change the logic for host below.
if (i !== 0 && i !== 0x1F && !stripped) {
continue;
}
const cpString = String.fromCodePoint(i);
const cpReference = "U+" + i.toString(16).toUpperCase().padStart(4, "0");
test(() => {
const expected = scheme === "https" ? (stripped ? "http" : "https") : (stripped ? "wpt--" : "wpt++");
const url = urlRecord(scheme);
url.protocol = String.fromCodePoint(i) + (scheme === "https" ? "http" : "wpt--");
assert_equals(url.protocol, expected + ":", "property");
assert_equals(url.href, urlString({ scheme: expected }), "href");
}, `Setting protocol with leading ${cpReference} (${scheme}:)`);
test(() => {
const expected = scheme === "https" ? (stripped ? "http" : "https") : (stripped ? "wpt--" : "wpt++");
const url = urlRecord(scheme);
url.protocol = (scheme === "https" ? "http" : "wpt--") + String.fromCodePoint(i);
assert_equals(url.protocol, expected + ":", "property");
assert_equals(url.href, urlString({ scheme: expected }), "href");
}, `Setting protocol with ${cpReference} before inserted colon (${scheme}:)`);
// Cannot test protocol with trailing as the algorithm inserts a colon before proceeding
// These do no stripping
for (const property of ["username", "password"]) {
for (const [type, expected, input] of [
["leading", encodeURIComponent(cpString) + "test", String.fromCodePoint(i) + "test"],
["middle", "te" + encodeURIComponent(cpString) + "st", "te" + String.fromCodePoint(i) + "st"],
["trailing", "test" + encodeURIComponent(cpString), "test" + String.fromCodePoint(i)]
]) {
test(() => {
const url = urlRecord(scheme);
url[property] = input;
assert_equals(url[property], expected, "property");
assert_equals(url.href, urlString({ scheme, [property]: expected }), "href");
}, `Setting ${property} with ${type} ${cpReference} (${scheme}:)`);
}
}
for (const [type, expectedPart, input] of [
["leading", (scheme === "https" ? cpString : encodeURIComponent(cpString)) + "test", String.fromCodePoint(i) + "test"],
["middle", "te" + (scheme === "https" ? cpString : encodeURIComponent(cpString)) + "st", "te" + String.fromCodePoint(i) + "st"],
["trailing", "test" + (scheme === "https" ? cpString : encodeURIComponent(cpString)), "test" + String.fromCodePoint(i)]
]) {
test(() => {
const expected = i === 0x00 || (scheme === "https" && i === 0x1F) ? "host" : stripped ? "test" : expectedPart;
const url = urlRecord(scheme);
url.host = input;
assert_equals(url.host, expected + ":8000", "property");
assert_equals(url.href, urlString({ scheme, host: expected }), "href");
}, `Setting host with ${type} ${cpReference} (${scheme}:)`);
test(() => {
const expected = i === 0x00 || (scheme === "https" && i === 0x1F) ? "host" : stripped ? "test" : expectedPart;
const url = urlRecord(scheme);
url.hostname = input;
assert_equals(url.hostname, expected, "property");
assert_equals(url.href, urlString({ scheme, host: expected }), "href");
}, `Setting hostname with ${type} ${cpReference} (${scheme}:)`);
}
test(() => {
const expected = stripped ? "9000" : "8000";
const url = urlRecord(scheme);
url.port = String.fromCodePoint(i) + "9000";
assert_equals(url.port, expected, "property");
assert_equals(url.href, urlString({ scheme, port: expected }), "href");
}, `Setting port with leading ${cpReference} (${scheme}:)`);
test(() => {
const expected = stripped ? "9000" : "90";
const url = urlRecord(scheme);
url.port = "90" + String.fromCodePoint(i) + "00";
assert_equals(url.port, expected, "property");
assert_equals(url.href, urlString({ scheme, port: expected }), "href");
}, `Setting port with middle ${cpReference} (${scheme}:)`);
test(() => {
const expected = "9000";
const url = urlRecord(scheme);
url.port = "9000" + String.fromCodePoint(i);
assert_equals(url.port, expected, "property");
assert_equals(url.href, urlString({ scheme, port: expected }), "href");
}, `Setting port with trailing ${cpReference} (${scheme}:)`);
for (const [property, separator] of [["pathname", "/"], ["search", "?"], ["hash", "#"]]) {
for (const [type, expectedPart, input] of [
["leading", encodeURIComponent(cpString) + "test", String.fromCodePoint(i) + "test"],
["middle", "te" + encodeURIComponent(cpString) + "st", "te" + String.fromCodePoint(i) + "st"],
["trailing", "test" + encodeURIComponent(cpString), "test" + String.fromCodePoint(i)]
]) {
test(() => {
const expected = stripped ? "test" : expectedPart;
const url = urlRecord(scheme);
url[property] = input;
assert_equals(url[property], separator + expected, "property");
assert_equals(url.href, urlString({ scheme, [property]: expected }), "href");
}, `Setting ${property} with ${type} ${cpReference} (${scheme}:)`);
}
}
}
}

View file

@ -0,0 +1,15 @@
<!doctype html>
<meta charset=utf-8>
<script>
self.GLOBAL = {
isWindow: function() { return true; },
isWorker: function() { return false; },
isShadowRealm: function() { return false; },
};
</script>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<script src="../common/subset-tests-by-key.js"></script>
<div id=log></div>
<script src="../url/url-setters.any.js"></script>

View file

@ -0,0 +1,34 @@
// META: script=/common/subset-tests-by-key.js
// META: variant=?include=file
// META: variant=?include=javascript
// META: variant=?include=mailto
// META: variant=?exclude=(file|javascript|mailto)
// Keep this file in sync with url-setters-a-area.window.js.
promise_test(() => fetch("resources/setters_tests.json").then(res => res.json()).then(runURLSettersTests), "Loading data…");
function runURLSettersTests(all_test_cases) {
for (var attribute_to_be_set in all_test_cases) {
if (attribute_to_be_set == "comment") {
continue;
}
var test_cases = all_test_cases[attribute_to_be_set];
for(var i = 0, l = test_cases.length; i < l; i++) {
var test_case = test_cases[i];
var name = "Setting <" + test_case.href + ">." + attribute_to_be_set +
" = '" + test_case.new_value + "'";
if ("comment" in test_case) {
name += " " + test_case.comment;
}
const key = test_case.href.split(":")[0];
subsetTestByKey(key, test, function() {
var url = new URL(test_case.href);
url[attribute_to_be_set] = test_case.new_value;
for (var attribute in test_case.expected) {
assert_equals(url[attribute], test_case.expected[attribute])
}
}, "URL: " + name)
}
}
}

View file

@ -0,0 +1,15 @@
<!doctype html>
<meta charset=utf-8>
<script>
self.GLOBAL = {
isWindow: function() { return true; },
isWorker: function() { return false; },
isShadowRealm: function() { return false; },
};
</script>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<div id=log></div>
<script src="../url/url-statics-parse.any.js"></script>

View file

@ -0,0 +1,50 @@
// This intentionally does not use resources/urltestdata.json to preserve resources.
[
{
"url": undefined,
"base": undefined,
"expected": false
},
{
"url": "aaa:b",
"base": undefined,
"expected": true
},
{
"url": undefined,
"base": "aaa:b",
"expected": false
},
{
"url": "aaa:/b",
"base": undefined,
"expected": true
},
{
"url": undefined,
"base": "aaa:/b",
"expected": true
},
{
"url": "https://test:test",
"base": undefined,
"expected": false
},
{
"url": "a",
"base": "https://b/",
"expected": true
}
].forEach(({ url, base, expected }) => {
test(() => {
if (expected == false) {
assert_equals(URL.parse(url, base), null);
} else {
assert_equals(URL.parse(url, base).href, new URL(url, base).href);
}
}, `URL.parse(${url}, ${base})`);
});
test(() => {
assert_not_equals(URL.parse("https://example/"), URL.parse("https://example/"));
}, `URL.parse() should return a unique object`);

View file

@ -0,0 +1,15 @@
<!doctype html>
<meta charset=utf-8>
<script>
self.GLOBAL = {
isWindow: function() { return true; },
isWorker: function() { return false; },
isShadowRealm: function() { return false; },
};
</script>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<div id=log></div>
<script src="../url/url-tojson.any.js"></script>

View file

@ -0,0 +1,4 @@
test(() => {
const a = new URL("https://example.com/")
assert_equals(JSON.stringify(a), "\"https://example.com/\"")
})

View file

@ -0,0 +1,15 @@
<!doctype html>
<meta charset=utf-8>
<script>
self.GLOBAL = {
isWindow: function() { return true; },
isWorker: function() { return false; },
isShadowRealm: function() { return false; },
};
</script>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<div id=log></div>
<script src="../url/urlencoded-parser.any.js"></script>

View file

@ -0,0 +1,68 @@
[
{ "input": "test", "output": [["test", ""]] },
{ "input": "\uFEFFtest=\uFEFF", "output": [["\uFEFFtest", "\uFEFF"]] },
{ "input": "%EF%BB%BFtest=%EF%BB%BF", "output": [["\uFEFFtest", "\uFEFF"]] },
{ "input": "%EF%BF%BF=%EF%BF%BF", "output": [["\uFFFF", "\uFFFF"]] },
{ "input": "%FE%FF", "output": [["\uFFFD\uFFFD", ""]] },
{ "input": "%FF%FE", "output": [["\uFFFD\uFFFD", ""]] },
{ "input": "†&†=x", "output": [["†", ""], ["†", "x"]] },
{ "input": "%C2", "output": [["\uFFFD", ""]] },
{ "input": "%C2x", "output": [["\uFFFDx", ""]] },
{ "input": "_charset_=windows-1252&test=%C2x", "output": [["_charset_", "windows-1252"], ["test", "\uFFFDx"]] },
{ "input": '', "output": [] },
{ "input": 'a', "output": [['a', '']] },
{ "input": 'a=b', "output": [['a', 'b']] },
{ "input": 'a=', "output": [['a', '']] },
{ "input": '=b', "output": [['', 'b']] },
{ "input": '&', "output": [] },
{ "input": '&a', "output": [['a', '']] },
{ "input": 'a&', "output": [['a', '']] },
{ "input": 'a&a', "output": [['a', ''], ['a', '']] },
{ "input": 'a&b&c', "output": [['a', ''], ['b', ''], ['c', '']] },
{ "input": 'a=b&c=d', "output": [['a', 'b'], ['c', 'd']] },
{ "input": 'a=b&c=d&', "output": [['a', 'b'], ['c', 'd']] },
{ "input": '&&&a=b&&&&c=d&', "output": [['a', 'b'], ['c', 'd']] },
{ "input": 'a=a&a=b&a=c', "output": [['a', 'a'], ['a', 'b'], ['a', 'c']] },
{ "input": 'a==a', "output": [['a', '=a']] },
{ "input": 'a=a+b+c+d', "output": [['a', 'a b c d']] },
{ "input": '%=a', "output": [['%', 'a']] },
{ "input": '%a=a', "output": [['%a', 'a']] },
{ "input": '%a_=a', "output": [['%a_', 'a']] },
{ "input": '%61=a', "output": [['a', 'a']] },
{ "input": '%61+%4d%4D=', "output": [['a MM', '']] },
{ "input": "id=0&value=%", "output": [['id', '0'], ['value', '%']] },
{ "input": "b=%2sf%2a", "output": [['b', '%2sf*']]},
{ "input": "b=%2%2af%2a", "output": [['b', '%2*f*']]},
{ "input": "b=%%2a", "output": [['b', '%*']]}
].forEach((val) => {
test(() => {
let sp = new URLSearchParams(val.input),
i = 0
for (let item of sp) {
assert_array_equals(item, val.output[i])
i++
}
}, "URLSearchParams constructed with: " + val.input)
promise_test(() => {
let init = new Request("about:blank", { body: val.input, method: "LADIDA", headers: {"Content-Type": "application/x-www-form-urlencoded;charset=windows-1252"} }).formData()
return init.then((fd) => {
let i = 0
for (let item of fd) {
assert_array_equals(item, val.output[i])
i++
}
})
}, "request.formData() with input: " + val.input)
promise_test(() => {
let init = new Response(val.input, { headers: {"Content-Type": "application/x-www-form-urlencoded;charset=shift_jis"} }).formData()
return init.then((fd) => {
let i = 0
for (let item of fd) {
assert_array_equals(item, val.output[i])
i++
}
})
}, "response.formData() with input: " + val.input)
});

View file

@ -0,0 +1,15 @@
<!doctype html>
<meta charset=utf-8>
<script>
self.GLOBAL = {
isWindow: function() { return true; },
isWorker: function() { return false; },
isShadowRealm: function() { return false; },
};
</script>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<div id=log></div>
<script src="../url/urlsearchparams-delete.any.js"></script>

View file

@ -0,0 +1,83 @@
test(function() {
var params = new URLSearchParams('a=b&c=d');
params.delete('a');
assert_equals(params + '', 'c=d');
params = new URLSearchParams('a=a&b=b&a=a&c=c');
params.delete('a');
assert_equals(params + '', 'b=b&c=c');
params = new URLSearchParams('a=a&=&b=b&c=c');
params.delete('');
assert_equals(params + '', 'a=a&b=b&c=c');
params = new URLSearchParams('a=a&null=null&b=b');
params.delete(null);
assert_equals(params + '', 'a=a&b=b');
params = new URLSearchParams('a=a&undefined=undefined&b=b');
params.delete(undefined);
assert_equals(params + '', 'a=a&b=b');
}, 'Delete basics');
test(function() {
var params = new URLSearchParams();
params.append('first', 1);
assert_true(params.has('first'), 'Search params object has name "first"');
assert_equals(params.get('first'), '1', 'Search params object has name "first" with value "1"');
params.delete('first');
assert_false(params.has('first'), 'Search params object has no "first" name');
params.append('first', 1);
params.append('first', 10);
params.delete('first');
assert_false(params.has('first'), 'Search params object has no "first" name');
}, 'Deleting appended multiple');
test(function() {
var url = new URL('http://example.com/?param1&param2');
url.searchParams.delete('param1');
url.searchParams.delete('param2');
assert_equals(url.href, 'http://example.com/', 'url.href does not have ?');
assert_equals(url.search, '', 'url.search does not have ?');
}, 'Deleting all params removes ? from URL');
test(function() {
var url = new URL('http://example.com/?');
url.searchParams.delete('param1');
assert_equals(url.href, 'http://example.com/', 'url.href does not have ?');
assert_equals(url.search, '', 'url.search does not have ?');
}, 'Removing non-existent param removes ? from URL');
test(() => {
const url = new URL('data:space ?test');
assert_true(url.searchParams.has('test'));
url.searchParams.delete('test');
assert_false(url.searchParams.has('test'));
assert_equals(url.search, '');
assert_equals(url.pathname, 'space');
assert_equals(url.href, 'data:space');
}, 'Changing the query of a URL with an opaque path can impact the path');
test(() => {
const url = new URL('data:space ?test#test');
url.searchParams.delete('test');
assert_equals(url.search, '');
assert_equals(url.pathname, 'space ');
assert_equals(url.href, 'data:space #test');
}, 'Changing the query of a URL with an opaque path can impact the path if the URL has no fragment');
test(() => {
const params = new URLSearchParams();
params.append('a', 'b');
params.append('a', 'c');
params.append('a', 'd');
params.delete('a', 'c');
assert_equals(params.toString(), 'a=b&a=d');
}, "Two-argument delete()");
test(() => {
const params = new URLSearchParams();
params.append('a', 'b');
params.append('a', 'c');
params.append('b', 'c');
params.append('b', 'd');
params.delete('b', 'c');
params.delete('a', undefined);
assert_equals(params.toString(), 'b=d');
}, "Two-argument delete() respects undefined as second arg");

View file

@ -0,0 +1,15 @@
<!doctype html>
<meta charset=utf-8>
<script>
self.GLOBAL = {
isWindow: function() { return true; },
isWorker: function() { return false; },
isShadowRealm: function() { return false; },
};
</script>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<div id=log></div>
<script src="../url/urlsearchparams-foreach.any.js"></script>

View file

@ -0,0 +1,76 @@
test(function() {
var params = new URLSearchParams('a=1&b=2&c=3');
var keys = [];
var values = [];
params.forEach(function(value, key) {
keys.push(key);
values.push(value);
});
assert_array_equals(keys, ['a', 'b', 'c']);
assert_array_equals(values, ['1', '2', '3']);
}, "ForEach Check");
test(function() {
let a = new URL("http://a.b/c?a=1&b=2&c=3&d=4");
let b = a.searchParams;
var c = [];
for (const i of b) {
a.search = "x=1&y=2&z=3";
c.push(i);
}
assert_array_equals(c[0], ["a","1"]);
assert_array_equals(c[1], ["y","2"]);
assert_array_equals(c[2], ["z","3"]);
}, "For-of Check");
test(function() {
let a = new URL("http://a.b/c");
let b = a.searchParams;
for (const i of b) {
assert_unreached(i);
}
}, "empty");
test(function() {
const url = new URL("http://localhost/query?param0=0&param1=1&param2=2");
const searchParams = url.searchParams;
const seen = [];
for (const param of searchParams) {
if (param[0] === 'param0') {
searchParams.delete('param1');
}
seen.push(param);
}
assert_array_equals(seen[0], ["param0", "0"]);
assert_array_equals(seen[1], ["param2", "2"]);
}, "delete next param during iteration");
test(function() {
const url = new URL("http://localhost/query?param0=0&param1=1&param2=2");
const searchParams = url.searchParams;
const seen = [];
for (const param of searchParams) {
if (param[0] === 'param0') {
searchParams.delete('param0');
// 'param1=1' is now in the first slot, so the next iteration will see 'param2=2'.
} else {
seen.push(param);
}
}
assert_array_equals(seen[0], ["param2", "2"]);
}, "delete current param during iteration");
test(function() {
const url = new URL("http://localhost/query?param0=0&param1=1&param2=2");
const searchParams = url.searchParams;
const seen = [];
for (const param of searchParams) {
seen.push(param[0]);
searchParams.delete(param[0]);
}
assert_array_equals(seen, ["param0", "param2"], "param1 should not have been seen by the loop");
assert_equals(String(searchParams), "param1=1", "param1 should remain");
}, "delete every param seen during iteration");

View file

@ -0,0 +1,15 @@
<!doctype html>
<meta charset=utf-8>
<script>
self.GLOBAL = {
isWindow: function() { return true; },
isWorker: function() { return false; },
isShadowRealm: function() { return false; },
};
</script>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<div id=log></div>
<script src="../url/urlsearchparams-get.any.js"></script>

View file

@ -0,0 +1,21 @@
test(function() {
var params = new URLSearchParams('a=b&c=d');
assert_equals(params.get('a'), 'b');
assert_equals(params.get('c'), 'd');
assert_equals(params.get('e'), null);
params = new URLSearchParams('a=b&c=d&a=e');
assert_equals(params.get('a'), 'b');
params = new URLSearchParams('=b&c=d');
assert_equals(params.get(''), 'b');
params = new URLSearchParams('a=&c=d&a=e');
assert_equals(params.get('a'), '');
}, 'Get basics');
test(function() {
var params = new URLSearchParams('first=second&third&&');
assert_true(params != null, 'constructor returned non-null value.');
assert_true(params.has('first'), 'Search params object has name "first"');
assert_equals(params.get('first'), 'second', 'Search params object has name "first" with value "second"');
assert_equals(params.get('third'), '', 'Search params object has name "third" with the empty value.');
assert_equals(params.get('fourth'), null, 'Search params object has no "fourth" name and value.');
}, 'More get() basics');

View file

@ -0,0 +1,15 @@
<!doctype html>
<meta charset=utf-8>
<script>
self.GLOBAL = {
isWindow: function() { return true; },
isWorker: function() { return false; },
isShadowRealm: function() { return false; },
};
</script>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<div id=log></div>
<script src="../url/urlsearchparams-getall.any.js"></script>

View file

@ -0,0 +1,25 @@
test(function() {
var params = new URLSearchParams('a=b&c=d');
assert_array_equals(params.getAll('a'), ['b']);
assert_array_equals(params.getAll('c'), ['d']);
assert_array_equals(params.getAll('e'), []);
params = new URLSearchParams('a=b&c=d&a=e');
assert_array_equals(params.getAll('a'), ['b', 'e']);
params = new URLSearchParams('=b&c=d');
assert_array_equals(params.getAll(''), ['b']);
params = new URLSearchParams('a=&c=d&a=e');
assert_array_equals(params.getAll('a'), ['', 'e']);
}, 'getAll() basics');
test(function() {
var params = new URLSearchParams('a=1&a=2&a=3&a');
assert_true(params.has('a'), 'Search params object has name "a"');
var matches = params.getAll('a');
assert_true(matches && matches.length == 4, 'Search params object has values for name "a"');
assert_array_equals(matches, ['1', '2', '3', ''], 'Search params object has expected name "a" values');
params.set('a', 'one');
assert_equals(params.get('a'), 'one', 'Search params object has name "a" with value "one"');
var matches = params.getAll('a');
assert_true(matches && matches.length == 1, 'Search params object has values for name "a"');
assert_array_equals(matches, ['one'], 'Search params object has expected name "a" values');
}, 'getAll() multiples');

View file

@ -0,0 +1,15 @@
<!doctype html>
<meta charset=utf-8>
<script>
self.GLOBAL = {
isWindow: function() { return true; },
isWorker: function() { return false; },
isShadowRealm: function() { return false; },
};
</script>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<div id=log></div>
<script src="../url/urlsearchparams-has.any.js"></script>

View file

@ -0,0 +1,45 @@
test(function() {
var params = new URLSearchParams('a=b&c=d');
assert_true(params.has('a'));
assert_true(params.has('c'));
assert_false(params.has('e'));
params = new URLSearchParams('a=b&c=d&a=e');
assert_true(params.has('a'));
params = new URLSearchParams('=b&c=d');
assert_true(params.has(''));
params = new URLSearchParams('null=a');
assert_true(params.has(null));
}, 'Has basics');
test(function() {
var params = new URLSearchParams('a=b&c=d&&');
params.append('first', 1);
params.append('first', 2);
assert_true(params.has('a'), 'Search params object has name "a"');
assert_true(params.has('c'), 'Search params object has name "c"');
assert_true(params.has('first'), 'Search params object has name "first"');
assert_false(params.has('d'), 'Search params object has no name "d"');
params.delete('first');
assert_false(params.has('first'), 'Search params object has no name "first"');
}, 'has() following delete()');
test(() => {
const params = new URLSearchParams("a=b&a=d&c&e&");
assert_true(params.has('a', 'b'));
assert_false(params.has('a', 'c'));
assert_true(params.has('a', 'd'));
assert_true(params.has('e', ''));
params.append('first', null);
assert_false(params.has('first', ''));
assert_true(params.has('first', 'null'));
params.delete('a', 'b');
assert_true(params.has('a', 'd'));
}, "Two-argument has()");
test(() => {
const params = new URLSearchParams("a=b&a=d&c&e&");
assert_true(params.has('a', 'b'));
assert_false(params.has('a', 'c'));
assert_true(params.has('a', 'd'));
assert_true(params.has('a', undefined));
}, "Two-argument has() respects undefined as second arg");

View file

@ -0,0 +1,15 @@
<!doctype html>
<meta charset=utf-8>
<script>
self.GLOBAL = {
isWindow: function() { return true; },
isWorker: function() { return false; },
isShadowRealm: function() { return false; },
};
</script>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<div id=log></div>
<script src="../url/urlsearchparams-set.any.js"></script>

View file

@ -0,0 +1,22 @@
test(function() {
var params = new URLSearchParams('a=b&c=d');
params.set('a', 'B');
assert_equals(params + '', 'a=B&c=d');
params = new URLSearchParams('a=b&c=d&a=e');
params.set('a', 'B');
assert_equals(params + '', 'a=B&c=d')
params.set('e', 'f');
assert_equals(params + '', 'a=B&c=d&e=f')
}, 'Set basics');
test(function() {
var params = new URLSearchParams('a=1&a=2&a=3');
assert_true(params.has('a'), 'Search params object has name "a"');
assert_equals(params.get('a'), '1', 'Search params object has name "a" with value "1"');
params.set('first', 4);
assert_true(params.has('a'), 'Search params object has name "a"');
assert_equals(params.get('a'), '1', 'Search params object has name "a" with value "1"');
params.set('a', 4);
assert_true(params.has('a'), 'Search params object has name "a"');
assert_equals(params.get('a'), '4', 'Search params object has name "a" with value "4"');
}, 'URLSearchParams.set');

View file

@ -0,0 +1,15 @@
<!doctype html>
<meta charset=utf-8>
<script>
self.GLOBAL = {
isWindow: function() { return true; },
isWorker: function() { return false; },
isShadowRealm: function() { return false; },
};
</script>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<div id=log></div>
<script src="../url/urlsearchparams-sort.any.js"></script>

View file

@ -0,0 +1,62 @@
[
{
"input": "z=b&a=b&z=a&a=a",
"output": [["a", "b"], ["a", "a"], ["z", "b"], ["z", "a"]]
},
{
"input": "\uFFFD=x&\uFFFC&\uFFFD=a",
"output": [["\uFFFC", ""], ["\uFFFD", "x"], ["\uFFFD", "a"]]
},
{
"input": "ffi&🌈", // 🌈 > code point, but < code unit because two code units
"output": [["🌈", ""], ["ffi", ""]]
},
{
"input": "é&e\uFFFD&e\u0301",
"output": [["e\u0301", ""], ["e\uFFFD", ""], ["é", ""]]
},
{
"input": "z=z&a=a&z=y&a=b&z=x&a=c&z=w&a=d&z=v&a=e&z=u&a=f&z=t&a=g",
"output": [["a", "a"], ["a", "b"], ["a", "c"], ["a", "d"], ["a", "e"], ["a", "f"], ["a", "g"], ["z", "z"], ["z", "y"], ["z", "x"], ["z", "w"], ["z", "v"], ["z", "u"], ["z", "t"]]
},
{
"input": "bbb&bb&aaa&aa=x&aa=y",
"output": [["aa", "x"], ["aa", "y"], ["aaa", ""], ["bb", ""], ["bbb", ""]]
},
{
"input": "z=z&=f&=t&=x",
"output": [["", "f"], ["", "t"], ["", "x"], ["z", "z"]]
},
{
"input": "a🌈&a💩",
"output": [["a🌈", ""], ["a💩", ""]]
}
].forEach((val) => {
test(() => {
let params = new URLSearchParams(val.input),
i = 0
params.sort()
for(let param of params) {
assert_array_equals(param, val.output[i])
i++
}
}, "Parse and sort: " + val.input)
test(() => {
let url = new URL("?" + val.input, "https://example/")
url.searchParams.sort()
let params = new URLSearchParams(url.search),
i = 0
for(let param of params) {
assert_array_equals(param, val.output[i])
i++
}
}, "URL parse and sort: " + val.input)
})
test(function() {
const url = new URL("http://example.com/?")
url.searchParams.sort()
assert_equals(url.href, "http://example.com/")
assert_equals(url.search, "")
}, "Sorting non-existent params removes ? from URL")

View file

@ -0,0 +1,15 @@
<!doctype html>
<meta charset=utf-8>
<script>
self.GLOBAL = {
isWindow: function() { return true; },
isWorker: function() { return false; },
isShadowRealm: function() { return false; },
};
</script>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<div id=log></div>
<script src="../url/urlsearchparams-stringifier.any.js"></script>

View file

@ -0,0 +1,145 @@
test(function() {
var params = new URLSearchParams();
params.append('a', 'b c');
assert_equals(params + '', 'a=b+c');
params.delete('a');
params.append('a b', 'c');
assert_equals(params + '', 'a+b=c');
}, 'Serialize space');
test(function() {
var params = new URLSearchParams();
params.append('a', '');
assert_equals(params + '', 'a=');
params.append('a', '');
assert_equals(params + '', 'a=&a=');
params.append('', 'b');
assert_equals(params + '', 'a=&a=&=b');
params.append('', '');
assert_equals(params + '', 'a=&a=&=b&=');
params.append('', '');
assert_equals(params + '', 'a=&a=&=b&=&=');
}, 'Serialize empty value');
test(function() {
var params = new URLSearchParams();
params.append('', 'b');
assert_equals(params + '', '=b');
params.append('', 'b');
assert_equals(params + '', '=b&=b');
}, 'Serialize empty name');
test(function() {
var params = new URLSearchParams();
params.append('', '');
assert_equals(params + '', '=');
params.append('', '');
assert_equals(params + '', '=&=');
}, 'Serialize empty name and value');
test(function() {
var params = new URLSearchParams();
params.append('a', 'b+c');
assert_equals(params + '', 'a=b%2Bc');
params.delete('a');
params.append('a+b', 'c');
assert_equals(params + '', 'a%2Bb=c');
}, 'Serialize +');
test(function() {
var params = new URLSearchParams();
params.append('=', 'a');
assert_equals(params + '', '%3D=a');
params.append('b', '=');
assert_equals(params + '', '%3D=a&b=%3D');
}, 'Serialize =');
test(function() {
var params = new URLSearchParams();
params.append('&', 'a');
assert_equals(params + '', '%26=a');
params.append('b', '&');
assert_equals(params + '', '%26=a&b=%26');
}, 'Serialize &');
test(function() {
var params = new URLSearchParams();
params.append('a', '*-._');
assert_equals(params + '', 'a=*-._');
params.delete('a');
params.append('*-._', 'c');
assert_equals(params + '', '*-._=c');
}, 'Serialize *-._');
test(function() {
var params = new URLSearchParams();
params.append('a', 'b%c');
assert_equals(params + '', 'a=b%25c');
params.delete('a');
params.append('a%b', 'c');
assert_equals(params + '', 'a%25b=c');
params = new URLSearchParams('id=0&value=%')
assert_equals(params + '', 'id=0&value=%25')
}, 'Serialize %');
test(function() {
var params = new URLSearchParams();
params.append('a', 'b\0c');
assert_equals(params + '', 'a=b%00c');
params.delete('a');
params.append('a\0b', 'c');
assert_equals(params + '', 'a%00b=c');
}, 'Serialize \\0');
test(function() {
var params = new URLSearchParams();
params.append('a', 'b\uD83D\uDCA9c');
assert_equals(params + '', 'a=b%F0%9F%92%A9c');
params.delete('a');
params.append('a\uD83D\uDCA9b', 'c');
assert_equals(params + '', 'a%F0%9F%92%A9b=c');
}, 'Serialize \uD83D\uDCA9'); // Unicode Character 'PILE OF POO' (U+1F4A9)
test(function() {
var params;
params = new URLSearchParams('a=b&c=d&&e&&');
assert_equals(params.toString(), 'a=b&c=d&e=');
params = new URLSearchParams('a = b &a=b&c=d%20');
assert_equals(params.toString(), 'a+=+b+&a=b&c=d+');
// The lone '=' _does_ survive the roundtrip.
params = new URLSearchParams('a=&a=b');
assert_equals(params.toString(), 'a=&a=b');
params = new URLSearchParams('b=%2sf%2a');
assert_equals(params.toString(), 'b=%252sf*');
params = new URLSearchParams('b=%2%2af%2a');
assert_equals(params.toString(), 'b=%252*f*');
params = new URLSearchParams('b=%%2a');
assert_equals(params.toString(), 'b=%25*');
}, 'URLSearchParams.toString');
test(() => {
const url = new URL('http://www.example.com/?a=b,c');
const params = url.searchParams;
assert_equals(url.toString(), 'http://www.example.com/?a=b,c');
assert_equals(params.toString(), 'a=b%2Cc');
params.append('x', 'y');
assert_equals(url.toString(), 'http://www.example.com/?a=b%2Cc&x=y');
assert_equals(params.toString(), 'a=b%2Cc&x=y');
}, 'URLSearchParams connected to URL');
test(() => {
const url = new URL('http://www.example.com/');
const params = url.searchParams;
params.append('a\nb', 'c\rd');
params.append('e\n\rf', 'g\r\nh');
assert_equals(params.toString(), "a%0Ab=c%0Dd&e%0A%0Df=g%0D%0Ah");
}, 'URLSearchParams must not do newline normalization');