Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

View File

@ -0,0 +1,21 @@
promise_test(() => fetch("resources/IdnaTestV2-removed.json").then(res => res.json()).then(runTests), "Loading data…");
function runTests(idnaTests) {
for (const idnaTest of idnaTests) {
if (typeof idnaTest === "string") {
continue // skip comments
}
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,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 : ""}`);
}
}

6
test/fixtures/wpt/url/META.yml vendored Normal file
View File

@ -0,0 +1,6 @@
spec: https://url.spec.whatwg.org/
suggested_reviewers:
- mikewest
- domenic
- annevk
- TimothyGu

57
test/fixtures/wpt/url/README.md vendored Normal file
View File

@ -0,0 +1,57 @@
## urltestdata.json / urltestdata-javascript-only.json
[`resources/urltestdata.json`](resources/urltestdata.json) contains URL parsing tests suitable for any URL parser implementation.
[`resources/urltestdata-javascript-only.json`](resources/urltestdata-javascript-only.json) contains URL parsing tests specifically meant
for JavaScript's `URL()` class as well as other languages accepting non-scalar-value strings.
These files are used as a source of tests by `a-element.html`, `failure.html`, `url-constructor.any.js`,
and other test files in this directory.
Both files share the same format. They consist of a JSON array of comments as strings and test cases as
objects. The keys for each test case are:
* `input`: a string to be parsed as URL.
* `base`: null or a serialized URL (i.e., does not fail parsing).
* Then either
* `failure` whose value is `true`, indicating that parsing `input` relative to `base` returns
failure
* `relativeTo` whose value is "`non-opaque-path-base`" (input does parse against a non-null base
URL without an opaque path) or "`any-base`" (input parses against any non-null base URL), or is
omitted in its entirety (input never parses successfully)
or `href`, `origin`, `protocol`, `username`, `password`, `host`, `hostname`, `port`,
`pathname`, `search`, and `hash` with string values; indicating that parsing `input` should return
an URL record and that the getters of each corresponding attribute in that URL’s [API] should
return the corresponding value.
The `origin` key may be missing. In that case, the API’s `origin` attribute is not tested.
## setters_tests.json
`resources/setters_tests.json` is self-documented.
## toascii.json
`resources/toascii.json` is a JSON resource containing an array where each item is an object
consisting of an optional `comment` field and mandatory `input` and `output` fields. `input` is the
domain to be parsed according to the rules of UTS #46 (as stipulated by the URL Standard). `output`
gives the expected output of the parser after serialization. An `output` of `null` means parsing is
expected to fail.
## URL parser's encoding argument
Tests in `/encoding` and `/html/infrastructure/urls/resolving-urls/query-encoding/` cover the
encoding argument to the URL parser.
There's also limited coverage in `resources/percent-encoding.json` for percent-encode after encoding
with _percentEncodeSet_ set to special-query percent-encode set and _spaceAsPlus_ set to false.
(Improvements to expand coverage here are welcome.)
## Specification
The tests in this directory assert conformance with [the URL Standard][URL].
[parsing]: https://url.spec.whatwg.org/#concept-basic-url-parser
[API]: https://url.spec.whatwg.org/#api
[URL]: https://url.spec.whatwg.org/

View File

@ -0,0 +1,4 @@
features:
- name: url-canparse
files:
- url-statics-canparse.*

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>URL Test</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<base id="base"/>
</head>
<body>
<div id="log"></div>
<script src="resources/a-element-origin.js"></script>
</body>
</html>
<!--
Other dependencies:
* resources/urltestdata.json,
* resources/urltestdata-javascript-only.json,
-->

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,
-->

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>URL Test</title>
<meta name="variant" content="?include=file"/>
<meta name="variant" content="?include=javascript"/>
<meta name="variant" content="?include=mailto"/>
<meta name="variant" content="?exclude=(file|javascript|mailto)"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/subset-tests-by-key.js"></script>
<base id="base"/>
</head>
<body>
<div id="log"></div>
<script src="resources/a-element.js"></script>
</body>
</html>
<!--
Other dependencies:
* resources/urltestdata.json,
* resources/urltestdata-javascript-only.json,
-->

27
test/fixtures/wpt/url/a-element.html vendored Normal file
View File

@ -0,0 +1,27 @@
<!doctype html>
<meta charset=utf-8>
<meta name="variant" content="?include=file">
<meta name="variant" content="?include=javascript">
<meta name="variant" content="?include=mailto">
<meta name="variant" content="?exclude=(file|javascript|mailto)">
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src="/common/subset-tests-by-key.js"></script>
<base id=base>
<div id=log></div>
<script src=resources/a-element.js></script>
<!--
Other dependencies:
* resources/urltestdata.json,
* resources/urltestdata-javascript-only.json,
-->
<a id="multline-entity" download="multline-entity.txt" href="data:text/plain;charset=utf-8,first%20line&#10;second%20line"> Link with embedded \n is parsed correctly </a>
<script type="text/javascript">
test(function() {
const link = document.getElementById("multline-entity");
assert_equals(link.href, "data:text/plain;charset=utf-8,first%20linesecond%20line");
}, "Test that embedded 0x0A is stripped");
</script>

View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Data URI parsing of fragments</title>
<link rel="help" href="https://url.spec.whatwg.org/">
<meta name="assert" content="Fragments should not be included as part of a data URI's body">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<iframe id="iframe"></iframe>
<script>
const IFRAME_DATA_SRC = `data:text/html,
<style>:target { color: green; }<\/style>
<script>window.addEventListener('load', function() {
const data = {
foo_matches_target_selector: document.getElementById('foo').matches(':target'),
body_html: document.body.innerHTML,
};
parent.postMessage(data, '*');
});<\/script>
<p id="foo">This should be the only visible text.</p>#foo`.replace('\n', '');
async_test(function(t) {
window.addEventListener("message", t.step_func_done(function(event) {
assert_true(event.data.foo_matches_target_selector);
assert_equals(event.data.body_html,
'<p id="foo">This should be the only visible text.</p>');
}));
const iframe = document.getElementById("iframe");
iframe.src = IFRAME_DATA_SRC;
});
</script>

61
test/fixtures/wpt/url/failure.html vendored Normal file
View File

@ -0,0 +1,61 @@
<!doctype html>
<meta charset=utf-8>
<title>Test URL parser failure consistency</title>
<meta name=timeout content=long>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<div id=log></div>
<script>
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(runTests), "Loading data…");
function runTests(testData) {
for (const test of testData) {
// skip comments, inputs we expect to pass and relative URLs
if (typeof test === "string" || !test.failure || test.base !== null) {
continue;
}
const name = test.input + " should throw"
self.test(() => {
// URL's constructor's first argument is tested by url-constructor.html
// If a URL fails to parse with any valid base, it must also fail to parse with no base, i.e.
// when used as a base URL itself.
assert_throws_js(TypeError, () => new URL("about:blank", test.input));
}, "URL's constructor's base argument: " + name)
self.test(() => {
const url = new URL("about:blank")
assert_throws_js(TypeError, () => url.href = test.input)
}, "URL's href: " + name)
// The following use cases resolve the URL input relative to the current
// document's URL. If this test input could be construed as a valid URL
// when resolved against a base URL, skip these cases.
if (test.relativeTo === undefined) {
self.test(() => {
const client = new XMLHttpRequest()
assert_throws_dom("SyntaxError", () => client.open("GET", test.input))
}, "XHR: " + name)
self.test(() => {
assert_throws_js(TypeError, () => self.navigator.sendBeacon(test.input))
}, "sendBeacon(): " + name)
self.test(t => {
const frame = document.body.appendChild(document.createElement("iframe"));
t.add_cleanup(() => frame.remove());
assert_throws_dom("SyntaxError", frame.contentWindow.DOMException, () => frame.contentWindow.location = test.input);
}, "Location's href: " + name);
self.test(() => {
assert_throws_dom("SyntaxError", () => self.open(test.input).close())
}, "window.open(): " + name)
}
}
}
</script>

46
test/fixtures/wpt/url/historical.any.js vendored Normal file
View File

@ -0,0 +1,46 @@
if (self.location) {
test(function() {
assert_false("searchParams" in self.location,
"location object should not have a searchParams attribute");
}, "searchParams on location object");
}
if(self.GLOBAL.isWindow()) {
test(() => {
assert_false("searchParams" in document.createElement("a"))
assert_false("searchParams" in document.createElement("area"))
}, "<a> and <area>.searchParams should be undefined");
}
test(function() {
var url = new URL("./foo", "http://www.example.org");
assert_equals(url.href, "http://www.example.org/foo");
assert_throws_js(TypeError, function() {
url.href = "./bar";
});
}, "Setting URL's href attribute and base URLs");
test(function() {
assert_equals(URL.domainToASCII, undefined);
}, "URL.domainToASCII should be undefined");
test(function() {
assert_equals(URL.domainToUnicode, undefined);
}, "URL.domainToUnicode should be undefined");
test(() => {
assert_throws_dom("DataCloneError", () => self.structuredClone(new URL("about:blank")));
}, "URL: no structured serialize/deserialize support");
test(() => {
assert_throws_dom("DataCloneError", () => self.structuredClone(new URLSearchParams()));
}, "URLSearchParams: no structured serialize/deserialize support");
test(() => {
const url = new URL("about:blank");
url.toString = () => { throw 1 };
assert_throws_exactly(1, () => new URL(url), "url argument");
assert_throws_exactly(1, () => new URL("about:blank", url), "base argument");
}, "Constructor only takes strings");
done();

14
test/fixtures/wpt/url/idlharness.any.js vendored Normal file
View File

@ -0,0 +1,14 @@
// META: script=/resources/WebIDLParser.js
// META: script=/resources/idlharness.js
// META: global=window,dedicatedworker,shadowrealm-in-window
idl_test(
['url'],
[], // no deps
idl_array => {
idl_array.add_objects({
URL: ['new URL("http://foo")'],
URLSearchParams: ['new URLSearchParams("hi=there&thank=you")']
});
}
);

View File

@ -0,0 +1,63 @@
// The results of this test are all over the map due to browsers behaving very differently for
// javascript: URLs.
//
// Chromium is pretty close execution-wise, but it parses javascript: URLs incorrectly.
// Gecko navigates to non-string return values of the result of executing a javascript: URL.
// WebKit executes javascript: URLs too early and has a harness error due to URL parsing.
//
// The expectations below should match the HTML and URL standards.
[
{
"description": "javascript: URL that fails to parse due to invalid host",
"input": "javascript://test:test/%0aglobalThis.shouldNotExistA=1",
"property": "shouldNotExistA",
"expected": undefined
},
{
"description": "javascript: URL that fails to parse due to invalid host and has a U+0009 in scheme",
"input": "java\x09script://test:test/%0aglobalThis.shouldNotExistB=1",
"property": "shouldNotExistB",
"expected": undefined
},
{
"description": "javascript: URL without an opaque path",
"input": "javascript://host/1%0a//../0/;globalThis.shouldBeOne=1;/%0aglobalThis.shouldBeOne=2;/..///",
"property": "shouldBeOne",
"expected": 1
},
{
"description": "javascript: URL containing a JavaScript string split over path and query",
// Use ";undefined" to avoid returning a string.
"input": "javascript:globalThis.shouldBeAStringA = \"https://whatsoever.com/?a=b&c=5&x=y\";undefined",
"property": "shouldBeAStringA",
"expected": "https://whatsoever.com/?a=b&c=5&x=y"
},
{
"description": "javascript: URL containing a JavaScript string split over path and query and has a U+000A in scheme",
// Use ";undefined" to avoid returning a string.
"input": "java\x0Ascript:globalThis.shouldBeAStringB = \"https://whatsoever.com/?a=b&c=5&x=y\";undefined",
"property": "shouldBeAStringB",
"expected": "https://whatsoever.com/?a=b&c=5&x=y"
}
].forEach(({ description, input, property, expected }) => {
// Use promise_test so the tests run in sequence. Needed for globalThis.verify below.
promise_test(t => {
const anchor = document.body.appendChild(document.createElement("a"));
t.add_cleanup(() => anchor.remove());
anchor.href = input;
assert_equals(globalThis[property], undefined, "Property is undefined before the click");
anchor.click();
assert_equals(globalThis[property], undefined, "Property is undefined immediately after the click");
// Since we cannot reliably queue a task to run after the task queued as a result of the click()
// above, we do another click() with a new URL.
return new Promise(resolve => {
globalThis.verify = t.step_func(() => {
assert_equals(globalThis[property], expected, `Property is ${expected} once the navigation happened`);
resolve();
});
anchor.href = "javascript:globalThis.verify()";
anchor.click();
});
}, description);
});

View File

@ -0,0 +1,33 @@
promise_test(() => fetch("resources/percent-encoding.json").then(res => res.json()).then(runTests), "Loading data…");
function runTests(testUnits) {
for (const testUnit of testUnits) {
// Ignore comments
if (typeof testUnit === "string") {
continue;
}
for (const encoding of Object.keys(testUnit.output)) {
async_test(t => {
const frame = document.body.appendChild(document.createElement("iframe"));
t.add_cleanup(() => frame.remove());
frame.onload = t.step_func_done(() => {
const output = frame.contentDocument.querySelector("a");
// Test that the fragment is always UTF-8 encoded
assert_equals(output.hash, `#${testUnit.output["utf-8"]}`, "fragment");
assert_equals(output.search, `?${testUnit.output[encoding]}`, "query");
});
frame.src = `resources/percent-encoding.py?encoding=${encoding}&value=${toBase64(testUnit.input)}`;
}, `Input ${testUnit.input} with encoding ${encoding}`);
}
}
}
// Use base64 to avoid relying on the URL parser to get UTF-8 percent-encoding correctly. This does
// not use btoa directly as that only works with code points in the range U+0000 to U+00FF,
// inclusive.
function toBase64(input) {
const bytes = new TextEncoder().encode(input);
const byteString = Array.from(bytes, byte => String.fromCharCode(byte)).join("");
const encoded = self.btoa(byteString);
return encoded;
}

View File

@ -0,0 +1,104 @@
[
"This is generated with the help from ../tools/IdnaTestV2-compare.py.",
"These tests are from an older IdnaTestV2 and thus the comment line may no longer be accurate.",
{
"comment": "P1; V6; V3 (ignored)",
"input": "-\udb40\ude56\ua867\uff0e\udb40\ude82\ud8dc\udd83\ud83c\udd09",
"output": null
},
{
"comment": "P1; V5; V6",
"input": "\ud83c\udd04\uff0e\u1cdc\u2488\u00df",
"output": null
},
{
"comment": "P1; V5; V6",
"input": "\ud83c\udd04\uff0e\u1cdc\u2488SS",
"output": null
},
{
"comment": "P1; V5; V6",
"input": "\ud83c\udd04\uff0e\u1cdc\u2488ss",
"output": null
},
{
"comment": "P1; V5; V6",
"input": "\ud83c\udd04\uff0e\u1cdc\u2488Ss",
"output": null
},
{
"comment": "C2; P1; V6",
"input": "\u0756\u3002\u3164\u200d\u03c2",
"output": null
},
{
"comment": "C2; P1; V6",
"input": "\u0756\u3002\u1160\u200d\u03c2",
"output": null
},
{
"comment": "C2; P1; V6",
"input": "\u0756\u3002\u1160\u200d\u03a3",
"output": null
},
{
"comment": "C2; P1; V6",
"input": "\u0756\u3002\u1160\u200d\u03c3",
"output": null
},
{
"comment": "C2; P1; V6",
"input": "\u0756\u3002\u3164\u200d\u03a3",
"output": null
},
{
"comment": "C2; P1; V6",
"input": "\u0756\u3002\u3164\u200d\u03c3",
"output": null
},
{
"comment": "P1; V6",
"input": "\ud83c\udd07\u4f10\ufe12.\ud831\ude5a\ua8c4",
"output": null
},
{
"comment": "P1; V5; V6",
"input": "\ud802\ude3f.\ud83c\udd06\u2014",
"output": null
},
{
"comment": "C2; P1; V5; V6",
"input": "\u1c32\ud83c\udd08\u2f9b\u05a6\uff0e\u200d\uda7e\udd64\u07fd",
"output": null
},
{
"comment": "C2; P1; V5; V6",
"input": "\ud83e\udc9f\ud83c\udd08\u200d\ua84e\uff61\u0f84",
"output": null
},
{
"comment": "P1; V6",
"input": "\udaa5\udeaa\uff61\ud83c\udd02",
"output": null
},
{
"comment": "C2; P1; V6",
"input": "\u186f\u2689\u59f6\ud83c\udd09\uff0e\u06f7\u200d\ud83c\udfaa\u200d",
"output": null
},
{
"comment": "C1; P1; V5; V6",
"input": "\ua67d\u200c\ud87e\uddf5\ud83c\udd06\uff61\u200c\ud804\udc42\u1b01",
"output": null
},
{
"comment": "C1; P1; V5; V6",
"input": "\ua67d\u200c\u9723\ud83c\udd06\uff61\u200c\ud804\udc42\u1b01",
"output": null
},
{
"comment": "C1; P1; V5; V6; V3 (ignored)",
"input": "-\u1897\u200c\ud83c\udd04.\ud805\udf22",
"output": null
}
]

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 + ">")
}
}

View File

@ -0,0 +1,66 @@
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
if (typeof expected === "string")
continue;
// Fragments are relative against "about:blank"
if (expected.relativeTo === "any-base")
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;
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() {
var url = bURL(expected.input, base)
if(expected.failure) {
if(url.protocol !== ':') {
assert_unreached("Expected URL to fail parsing")
}
assert_equals(url.href, expected.input, "failure should set href to input")
return
}
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")
assert_equals(url.hash, expected.hash, "hash")
}, "Parsing: <" + expected.input + "> against <" + base + ">")
}
}

View File

@ -0,0 +1,48 @@
[
"Tests for percent-encoding.",
{
"input": "\u2020",
"output": {
"big5": "%26%238224%3B",
"euc-kr": "%A2%D3",
"utf-8": "%E2%80%A0",
"windows-1252": "%86"
}
},
"This uses a trailing A to prevent the URL parser from trimming the C0 control.",
{
"input": "\u000EA",
"output": {
"big5": "%0EA",
"iso-2022-jp": "%26%2365533%3BA",
"utf-8": "%0EA"
}
},
{
"input": "\u203E\u005C",
"output": {
"iso-2022-jp": "%1B(J~%1B(B\\",
"utf-8": "%E2%80%BE\\"
}
},
{
"input": "\uE5E5",
"output": {
"gb18030": "%26%2358853%3B",
"utf-8": "%EE%97%A5"
}
},
{
"input": "\u2212",
"output": {
"shift_jis": "%81|",
"utf-8": "%E2%88%92"
}
},
{
"input": "á|",
"output": {
"utf-8": "%C3%A1|"
}
}
]

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,378 @@
[
"This contains assorted IDNA tests that IdnaTestV2 might not cover.",
"Feel free to deduplicate with a clear commit message.",
"",
"If the test only applies to the URL Standard's 'domain to ASCII', ",
"and not to TR46's ToASCII, then tag it with `urlStandardOnly`",
{
"comment": "Label with hyphens in 3rd and 4th position",
"input": "aa--",
"output": "aa--"
},
{
"input": "a†--",
"output": "xn--a---kp0a"
},
{
"input": "ab--c",
"output": "ab--c"
},
{
"comment": "Label with leading hyphen",
"input": "-x",
"output": "-x"
},
{
"input": "-†",
"output": "xn----xhn"
},
{
"input": "-x.xn--zca",
"output": "-x.xn--zca"
},
{
"input": "-x.ß",
"output": "-x.xn--zca"
},
{
"comment": "Label with trailing hyphen",
"input": "x-.xn--zca",
"output": "x-.xn--zca"
},
{
"input": "x-.ß",
"output": "x-.xn--zca"
},
{
"comment": "Empty labels",
"input": "x..xn--zca",
"output": "x..xn--zca"
},
{
"input": "x..ß",
"output": "x..xn--zca"
},
{
"comment": "Invalid Punycode",
"input": "xn--a",
"output": null
},
{
"input": "xn--a.xn--zca",
"output": null
},
{
"input": "xn--a.ß",
"output": null
},
{
"input": "xn--ls8h=",
"output": null
},
{
"comment": "Invalid Punycode (contains non-ASCII character)",
"input": "xn--tešla",
"output": null
},
{
"comment": "Valid Punycode",
"input": "xn--zca.xn--zca",
"output": "xn--zca.xn--zca"
},
{
"comment": "Mixed",
"input": "xn--zca.ß",
"output": "xn--zca.xn--zca"
},
{
"input": "ab--c.xn--zca",
"output": "ab--c.xn--zca"
},
{
"input": "ab--c.ß",
"output": "ab--c.xn--zca"
},
{
"comment": "CheckJoiners is true",
"input": "\u200D.example",
"output": null
},
{
"input": "xn--1ug.example",
"output": null
},
{
"comment": "CheckBidi is true",
"input": "يa",
"output": null
},
{
"input": "xn--a-yoc",
"output": null
},
{
"comment": "processing_option is Nontransitional_Processing",
"input": "ශ්‍රී",
"output": "xn--10cl1a0b660p"
},
{
"input": "نامه‌ای",
"output": "xn--mgba3gch31f060k"
},
{
"comment": "U+FFFD",
"input": "\uFFFD.com",
"output": null
},
{
"comment": "U+FFFD character encoded in Punycode",
"input": "xn--zn7c.com",
"output": null
},
{
"comment": "Label longer than 63 code points",
"input": "x01234567890123456789012345678901234567890123456789012345678901x",
"output": "x01234567890123456789012345678901234567890123456789012345678901x"
},
{
"input": "x01234567890123456789012345678901234567890123456789012345678901†",
"output": "xn--x01234567890123456789012345678901234567890123456789012345678901-6963b"
},
{
"input": "x01234567890123456789012345678901234567890123456789012345678901x.xn--zca",
"output": "x01234567890123456789012345678901234567890123456789012345678901x.xn--zca"
},
{
"input": "x01234567890123456789012345678901234567890123456789012345678901x.ß",
"output": "x01234567890123456789012345678901234567890123456789012345678901x.xn--zca"
},
{
"comment": "Domain excluding TLD longer than 253 code points",
"input": "01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.0123456789012345678901234567890123456789012345678.x",
"output": "01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.0123456789012345678901234567890123456789012345678.x"
},
{
"input": "01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.0123456789012345678901234567890123456789012345678.xn--zca",
"output": "01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.0123456789012345678901234567890123456789012345678.xn--zca"
},
{
"input": "01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.0123456789012345678901234567890123456789012345678.ß",
"output": "01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.01234567890123456789012345678901234567890123456789.0123456789012345678901234567890123456789012345678.xn--zca"
},
{
"comment": "IDNA ignored code points",
"input": "a\u00ADb",
"output": "ab"
},
{
"comment": "Interesting UseSTD3ASCIIRules=false cases",
"input": "≠",
"output": "xn--1ch"
},
{
"input": "≮",
"output": "xn--gdh"
},
{
"input": "≯",
"output": "xn--hdh"
},
{
"comment": "NFC normalization (forbidden < and > characters are normalized to valid ones)",
"input": "=\u0338",
"output": "xn--1ch"
},
{
"input": "<\u0338",
"output": "xn--gdh"
},
{
"input": ">\u0338",
"output": "xn--hdh"
},
{
"comment": "Same with inserted IDNA ignored code point",
"input": "=\u00AD\u0338",
"output": "xn--1ch"
},
{
"input": "<\u00AD\u0338",
"output": "xn--gdh"
},
{
"input": ">\u00AD\u0338",
"output": "xn--hdh"
},
"Tests below are from WebKit (fast/url/idna2003.html & fast/url/idna2008.html; contributed by Chris Weber back in 2011).",
{
"input": "fa\u00DF.de",
"output": "xn--fa-hia.de"
},
{
"input": "\u03B2\u03CC\u03BB\u03BF\u03C2.com",
"output": "xn--nxasmm1c.com"
},
{
"input": "\u0DC1\u0DCA\u200D\u0DBB\u0DD3.com",
"output": "xn--10cl1a0b660p.com"
},
{
"input": "\u0646\u0627\u0645\u0647\u200C\u0627\u06CC.com",
"output": "xn--mgba3gch31f060k.com"
},
{
"input": "www.loo\u0138out.net",
"output": "www.xn--looout-5bb.net"
},
{
"input": "\u15EF\u15EF\u15EF.lookout.net",
"output": "xn--1qeaa.lookout.net"
},
{
"input": "www.lookout.\u0441\u043E\u043C",
"output": "www.lookout.xn--l1adi"
},
{
"input": "www\u2025lookout.net",
"output": null
},
{
"input": "www.lookout\u2027net",
"output": "www.xn--lookoutnet-406e"
},
{
"input": "www.lookout.net\u2A7480",
"output": null,
"urlStandardOnly": true
},
{
"input": "www\u00A0.lookout.net",
"output": null,
"urlStandardOnly": true
},
{
"input": "\u1680lookout.net",
"output": null
},
{
"input": "\u001flookout.net",
"output": null,
"urlStandardOnly": true
},
{
"input": "look\u06DDout.net",
"output": null
},
{
"input": "look\u180Eout.net",
"output": "lookout.net"
},
{
"input": "look\u2060out.net",
"output": "lookout.net"
},
{
"input": "look\uFEFFout.net",
"output": "lookout.net"
},
{
"input": "look\uD83F\uDFFEout.net",
"output": null
},
{
"input": "look\uFFFAout.net",
"output": null
},
{
"input": "look\u2FF0out.net",
"output": null
},
{
"input": "look\u0341out.net",
"output": "xn--looout-kp7b.net"
},
{
"input": "look\u202Eout.net",
"output": null
},
{
"input": "look\u206Bout.net",
"output": "lookout.net"
},
{
"input": "look\uDB40\uDC01out.net",
"output": null
},
{
"input": "look\uDB40\uDC20out.net",
"output": null
},
{
"input": "look\u05BEout.net",
"output": null
},
{
"input": "B\u00FCcher.de",
"output": "xn--bcher-kva.de"
},
{
"input": "\u2665.net",
"output": "xn--g6h.net"
},
{
"input": "\u0378.net",
"output": null
},
{
"input": "\u04C0.com",
"output": "xn--s5a.com"
},
{
"input": "\uD87E\uDC68.com",
"output": "xn--snl.com"
},
{
"input": "\u2183.com",
"output": "xn--r5g.com"
},
{
"input": "look\u034Fout.net",
"output": "lookout.net"
},
{
"input": "gOoGle.com",
"output": "google.com"
},
{
"input": "\u09dc.com",
"output": "xn--15b8c.com"
},
{
"input": "\u1E9E.com",
"output": "xn--zca.com"
},
{
"input": "\u1E9E.foo.com",
"output": "xn--zca.foo.com"
},
{
"input": "-foo.bar.com",
"output": "-foo.bar.com"
},
{
"input": "foo-.bar.com",
"output": "foo-.bar.com"
},
{
"input": "ab--cd.com",
"output": "ab--cd.com"
},
{
"input": "xn--0.com",
"output": null
},
{
"input": "foo\u0300.bar.com",
"output": "xn--fo-3ja.bar.com"
}
]

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

54
test/fixtures/wpt/url/toascii.window.js vendored Normal file
View File

@ -0,0 +1,54 @@
promise_test(() => fetch("resources/toascii.json").then(res => res.json()).then(runTests), "Loading data…");
function makeURL(type, input) {
input = "https://" + input + "/x"
if(type === "url") {
return new URL(input)
} else {
const url = document.createElement(type)
url.href = input
return url
}
}
function runTests(tests) {
for(var i = 0, l = tests.length; i < l; i++) {
let hostTest = tests[i]
if (typeof hostTest === "string") {
continue // skip comments
}
const typeName = { "url": "URL", "a": "<a>", "area": "<area>" }
;["url", "a", "area"].forEach((type) => {
test(() => {
if(hostTest.output !== null) {
const url = makeURL("url", hostTest.input)
assert_equals(url.host, hostTest.output)
assert_equals(url.hostname, hostTest.output)
assert_equals(url.pathname, "/x")
assert_equals(url.href, "https://" + hostTest.output + "/x")
} else {
if(type === "url") {
assert_throws_js(TypeError, () => makeURL("url", hostTest.input))
} else {
const url = makeURL(type, hostTest.input)
assert_equals(url.host, "")
assert_equals(url.hostname, "")
assert_equals(url.pathname, "")
assert_equals(url.href, "https://" + hostTest.input + "/x")
}
}
}, hostTest.input + " (using " + typeName[type] + ")")
;["host", "hostname"].forEach((val) => {
test(() => {
const url = makeURL(type, "x")
url[val] = hostTest.input
if(hostTest.output !== null) {
assert_equals(url[val], hostTest.output)
} else {
assert_equals(url[val], "x")
}
}, hostTest.input + " (using " + typeName[type] + "." + val + ")")
})
})
}
}

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…");

19
test/fixtures/wpt/url/url-origin.any.js vendored Normal file
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,72 @@
function bURL(url, base) {
return new URL(url, base || "about:blank")
}
function runURLSearchParamTests() {
test(function() {
var url = bURL('http://example.org/?a=b')
assert_true("searchParams" in url)
var searchParams = url.searchParams
assert_equals(url.searchParams, searchParams, 'Object identity should hold.')
}, 'URL.searchParams getter')
test(function() {
var url = bURL('http://example.org/?a=b')
assert_true("searchParams" in url)
var searchParams = url.searchParams
assert_equals(searchParams.toString(), 'a=b')
searchParams.set('a', 'b')
assert_equals(url.searchParams.toString(), 'a=b')
assert_equals(url.search, '?a=b')
url.search = ''
assert_equals(url.searchParams.toString(), '')
assert_equals(url.search, '')
assert_equals(searchParams.toString(), '')
}, 'URL.searchParams updating, clearing')
test(function() {
'use strict'
var urlString = 'http://example.org'
var url = bURL(urlString)
assert_throws_js(TypeError, function() { url.searchParams = new URLSearchParams(urlString) })
}, 'URL.searchParams setter, invalid values')
test(function() {
var url = bURL('http://example.org/file?a=b&c=d')
assert_true("searchParams" in url)
var searchParams = url.searchParams
assert_equals(url.search, '?a=b&c=d')
assert_equals(searchParams.toString(), 'a=b&c=d')
// Test that setting 'search' propagates to the URL object's query object.
url.search = 'e=f&g=h'
assert_equals(url.search, '?e=f&g=h')
assert_equals(searchParams.toString(), 'e=f&g=h')
// ..and same but with a leading '?'.
url.search = '?e=f&g=h'
assert_equals(url.search, '?e=f&g=h')
assert_equals(searchParams.toString(), 'e=f&g=h')
// And in the other direction, altering searchParams propagates
// back to 'search'.
searchParams.append('i', ' j ')
assert_equals(url.search, '?e=f&g=h&i=+j+')
assert_equals(url.searchParams.toString(), 'e=f&g=h&i=+j+')
assert_equals(searchParams.get('i'), ' j ')
searchParams.set('e', 'updated')
assert_equals(url.search, '?e=updated&g=h&i=+j+')
assert_equals(searchParams.get('e'), 'updated')
var url2 = bURL('http://example.org/file??a=b&c=d')
assert_equals(url2.search, '??a=b&c=d')
assert_equals(url2.searchParams.toString(), '%3Fa=b&c=d')
url2.href = 'http://example.org/file??a=b'
assert_equals(url2.search, '??a=b')
assert_equals(url2.searchParams.toString(), '%3Fa=b')
}, 'URL.searchParams and URL.search setters, update propagation')
}
runURLSearchParamTests()

View File

@ -0,0 +1,44 @@
// 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(allTestCases) {
for (const [propertyToBeSet, testCases] of Object.entries(allTestCases)) {
if (propertyToBeSet === "comment") {
continue;
}
for (const testCase of testCases) {
const name = `Setting <${testCase.href}>.${propertyToBeSet} = '${testCase.new_value}'${
testCase.comment ? ` ${testCase.comment}` : ''
}`;
const key = testCase.href.split(":")[0];
subsetTestByKey(key, test, () => {
const url = document.createElement("a");
url.href = testCase.href;
url[propertyToBeSet] = testCase.new_value;
for (const [property, expectedValue] of Object.entries(testCase.expected)) {
assert_equals(url[property], expectedValue);
}
}, `<a>: ${name}`);
subsetTestByKey(key, test, () => {
const url = document.createElement("area");
url.href = testCase.href;
url[propertyToBeSet] = testCase.new_value;
for (const [property, expectedValue] of Object.entries(testCase.expected)) {
assert_equals(url[property], expectedValue);
}
}, `<area>: ${name}`);
}
}
}

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,33 @@
// 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(allTestCases) {
for (const [propertyToBeSet, testCases] of Object.entries(allTestCases)) {
if (propertyToBeSet === "comment") {
continue;
}
for (const test_case of testCases) {
const name = `Setting <${test_case.href}>.${propertyToBeSet} = '${test_case.new_value}'${
test_case.comment ? ` ${test_case.comment}` : ''
}`;
const key = test_case.href.split(":")[0];
subsetTestByKey(key, test, () => {
const url = new URL(test_case.href);
url[propertyToBeSet] = test_case.new_value;
for (const [property, expectedValue] of Object.entries(test_case.expected)) {
assert_equals(url[property], expectedValue);
}
}, `URL: ${name}`);
}
}
}

View File

@ -0,0 +1,47 @@
// 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": undefined,
"base": "https://test:test/",
"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(() => {
assert_equals(URL.canParse(url, base), expected);
}, `URL.canParse(${url}, ${base})`);
});

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,4 @@
test(() => {
const a = new URL("https://example.com/")
assert_equals(JSON.stringify(a), "\"https://example.com/\"")
})

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,39 @@
test(function() {
var params = new URLSearchParams();
params.append('a', 'b');
assert_equals(params + '', 'a=b');
params.append('a', 'b');
assert_equals(params + '', 'a=b&a=b');
params.append('a', 'c');
assert_equals(params + '', 'a=b&a=b&a=c');
}, 'Append same name');
test(function() {
var params = new URLSearchParams();
params.append('', '');
assert_equals(params + '', '=');
params.append('', '');
assert_equals(params + '', '=&=');
}, 'Append empty strings');
test(function() {
var params = new URLSearchParams();
params.append(null, null);
assert_equals(params + '', 'null=null');
params.append(null, null);
assert_equals(params + '', 'null=null&null=null');
}, 'Append null');
test(function() {
var params = new URLSearchParams();
params.append('first', 1);
params.append('second', 2);
params.append('third', '');
params.append('first', 10);
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"');
assert_equals(params.get('second'), '2', 'Search params object has name "second" with value "2"');
assert_equals(params.get('third'), '', 'Search params object has name "third" with value ""');
params.append('first', 10);
assert_equals(params.get('first'), '1', 'Search params object has name "first" with value "1"');
}, 'Append multiple');

View File

@ -0,0 +1,224 @@
test(function() {
var params = new URLSearchParams();
assert_equals(params + '', '');
params = new URLSearchParams('');
assert_equals(params + '', '');
params = new URLSearchParams('a=b');
assert_equals(params + '', 'a=b');
params = new URLSearchParams(params);
assert_equals(params + '', 'a=b');
}, 'Basic URLSearchParams construction');
test(function() {
var params = new URLSearchParams()
assert_equals(params.toString(), "")
}, "URLSearchParams constructor, no arguments")
test(function () {
var params = new URLSearchParams("?a=b")
assert_equals(params.toString(), "a=b")
}, 'URLSearchParams constructor, remove leading "?"')
test(() => {
var params = new URLSearchParams(DOMException);
assert_equals(params.toString(), "INDEX_SIZE_ERR=1&DOMSTRING_SIZE_ERR=2&HIERARCHY_REQUEST_ERR=3&WRONG_DOCUMENT_ERR=4&INVALID_CHARACTER_ERR=5&NO_DATA_ALLOWED_ERR=6&NO_MODIFICATION_ALLOWED_ERR=7&NOT_FOUND_ERR=8&NOT_SUPPORTED_ERR=9&INUSE_ATTRIBUTE_ERR=10&INVALID_STATE_ERR=11&SYNTAX_ERR=12&INVALID_MODIFICATION_ERR=13&NAMESPACE_ERR=14&INVALID_ACCESS_ERR=15&VALIDATION_ERR=16&TYPE_MISMATCH_ERR=17&SECURITY_ERR=18&NETWORK_ERR=19&ABORT_ERR=20&URL_MISMATCH_ERR=21&QUOTA_EXCEEDED_ERR=22&TIMEOUT_ERR=23&INVALID_NODE_TYPE_ERR=24&DATA_CLONE_ERR=25")
assert_throws_js(TypeError, () => new URLSearchParams(DOMException.prototype),
"Constructing a URLSearchParams from DOMException.prototype should throw due to branding checks");
}, "URLSearchParams constructor, DOMException as argument")
test(() => {
var params = new URLSearchParams('');
assert_true(params != null, 'constructor returned non-null value.');
assert_equals(Object.getPrototypeOf(params), URLSearchParams.prototype, 'expected URLSearchParams.prototype as prototype.');
}, "URLSearchParams constructor, empty string as argument")
test(() => {
var params = new URLSearchParams({});
assert_equals(params + '', "");
}, 'URLSearchParams constructor, {} as argument');
test(function() {
var params = new URLSearchParams('a=b');
assert_true(params != null, 'constructor returned non-null value.');
assert_true(params.has('a'), 'Search params object has name "a"');
assert_false(params.has('b'), 'Search params object has not got name "b"');
params = new URLSearchParams('a=b&c');
assert_true(params != null, 'constructor returned non-null value.');
assert_true(params.has('a'), 'Search params object has name "a"');
assert_true(params.has('c'), 'Search params object has name "c"');
params = new URLSearchParams('&a&&& &&&&&a+b=& c&m%c3%b8%c3%b8');
assert_true(params != null, 'constructor returned non-null value.');
assert_true(params.has('a'), 'Search params object has name "a"');
assert_true(params.has('a b'), 'Search params object has name "a b"');
assert_true(params.has(' '), 'Search params object has name " "');
assert_false(params.has('c'), 'Search params object did not have the name "c"');
assert_true(params.has(' c'), 'Search params object has name " c"');
assert_true(params.has('møø'), 'Search params object has name "møø"');
params = new URLSearchParams('id=0&value=%');
assert_true(params != null, 'constructor returned non-null value.');
assert_true(params.has('id'), 'Search params object has name "id"');
assert_true(params.has('value'), 'Search params object has name "value"');
assert_equals(params.get('id'), '0');
assert_equals(params.get('value'), '%');
params = new URLSearchParams('b=%2sf%2a');
assert_true(params != null, 'constructor returned non-null value.');
assert_true(params.has('b'), 'Search params object has name "b"');
assert_equals(params.get('b'), '%2sf*');
params = new URLSearchParams('b=%2%2af%2a');
assert_true(params != null, 'constructor returned non-null value.');
assert_true(params.has('b'), 'Search params object has name "b"');
assert_equals(params.get('b'), '%2*f*');
params = new URLSearchParams('b=%%2a');
assert_true(params != null, 'constructor returned non-null value.');
assert_true(params.has('b'), 'Search params object has name "b"');
assert_equals(params.get('b'), '%*');
}, 'URLSearchParams constructor, string.');
test(function() {
var seed = new URLSearchParams('a=b&c=d');
var params = new URLSearchParams(seed);
assert_true(params != null, 'constructor returned non-null value.');
assert_equals(params.get('a'), 'b');
assert_equals(params.get('c'), 'd');
assert_false(params.has('d'));
// The name-value pairs are copied when created; later updates
// should not be observable.
seed.append('e', 'f');
assert_false(params.has('e'));
params.append('g', 'h');
assert_false(seed.has('g'));
}, 'URLSearchParams constructor, object.');
test(function() {
var formData = new FormData()
formData.append('a', 'b')
formData.append('c', 'd')
var params = new URLSearchParams(formData);
assert_true(params != null, 'constructor returned non-null value.');
assert_equals(params.get('a'), 'b');
assert_equals(params.get('c'), 'd');
assert_false(params.has('d'));
// The name-value pairs are copied when created; later updates
// should not be observable.
formData.append('e', 'f');
assert_false(params.has('e'));
params.append('g', 'h');
assert_false(formData.has('g'));
}, 'URLSearchParams constructor, FormData.');
test(function() {
var params = new URLSearchParams('a=b+c');
assert_equals(params.get('a'), 'b c');
params = new URLSearchParams('a+b=c');
assert_equals(params.get('a b'), 'c');
}, 'Parse +');
test(function() {
const testValue = '+15555555555';
const params = new URLSearchParams();
params.set('query', testValue);
var newParams = new URLSearchParams(params.toString());
assert_equals(params.toString(), 'query=%2B15555555555');
assert_equals(params.get('query'), testValue);
assert_equals(newParams.get('query'), testValue);
}, 'Parse encoded +');
test(function() {
var params = new URLSearchParams('a=b c');
assert_equals(params.get('a'), 'b c');
params = new URLSearchParams('a b=c');
assert_equals(params.get('a b'), 'c');
}, 'Parse space');
test(function() {
var params = new URLSearchParams('a=b%20c');
assert_equals(params.get('a'), 'b c');
params = new URLSearchParams('a%20b=c');
assert_equals(params.get('a b'), 'c');
}, 'Parse %20');
test(function() {
var params = new URLSearchParams('a=b\0c');
assert_equals(params.get('a'), 'b\0c');
params = new URLSearchParams('a\0b=c');
assert_equals(params.get('a\0b'), 'c');
}, 'Parse \\0');
test(function() {
var params = new URLSearchParams('a=b%00c');
assert_equals(params.get('a'), 'b\0c');
params = new URLSearchParams('a%00b=c');
assert_equals(params.get('a\0b'), 'c');
}, 'Parse %00');
test(function() {
var params = new URLSearchParams('a=b\u2384');
assert_equals(params.get('a'), 'b\u2384');
params = new URLSearchParams('a\u2384b=c');
assert_equals(params.get('a\u2384b'), 'c');
}, 'Parse \u2384'); // Unicode Character 'COMPOSITION SYMBOL' (U+2384)
test(function() {
var params = new URLSearchParams('a=b%e2%8e%84');
assert_equals(params.get('a'), 'b\u2384');
params = new URLSearchParams('a%e2%8e%84b=c');
assert_equals(params.get('a\u2384b'), 'c');
}, 'Parse %e2%8e%84'); // Unicode Character 'COMPOSITION SYMBOL' (U+2384)
test(function() {
var params = new URLSearchParams('a=b\uD83D\uDCA9c');
assert_equals(params.get('a'), 'b\uD83D\uDCA9c');
params = new URLSearchParams('a\uD83D\uDCA9b=c');
assert_equals(params.get('a\uD83D\uDCA9b'), 'c');
}, 'Parse \uD83D\uDCA9'); // Unicode Character 'PILE OF POO' (U+1F4A9)
test(function() {
var params = new URLSearchParams('a=b%f0%9f%92%a9c');
assert_equals(params.get('a'), 'b\uD83D\uDCA9c');
params = new URLSearchParams('a%f0%9f%92%a9b=c');
assert_equals(params.get('a\uD83D\uDCA9b'), 'c');
}, 'Parse %f0%9f%92%a9'); // Unicode Character 'PILE OF POO' (U+1F4A9)
test(function() {
var params = new URLSearchParams([]);
assert_true(params != null, 'constructor returned non-null value.');
params = new URLSearchParams([['a', 'b'], ['c', 'd']]);
assert_equals(params.get("a"), "b");
assert_equals(params.get("c"), "d");
assert_throws_js(TypeError, function() { new URLSearchParams([[1]]); });
assert_throws_js(TypeError, function() { new URLSearchParams([[1,2,3]]); });
}, "Constructor with sequence of sequences of strings");
[
{ "input": {"+": "%C2"}, "output": [["+", "%C2"]], "name": "object with +" },
{ "input": {c: "x", a: "?"}, "output": [["c", "x"], ["a", "?"]], "name": "object with two keys" },
{ "input": [["c", "x"], ["a", "?"]], "output": [["c", "x"], ["a", "?"]], "name": "array with two keys" },
{ "input": {"\uD835x": "1", "xx": "2", "\uD83Dx": "3"}, "output": [["\uFFFDx", "3"], ["xx", "2"]], "name": "2 unpaired surrogates (no trailing)" },
{ "input": {"x\uDC53": "1", "x\uDC5C": "2", "x\uDC65": "3"}, "output": [["x\uFFFD", "3"]], "name": "3 unpaired surrogates (no leading)" },
{ "input": {"a\0b": "42", "c\uD83D": "23", "d\u1234": "foo"}, "output": [["a\0b", "42"], ["c\uFFFD", "23"], ["d\u1234", "foo"]], "name": "object with NULL, non-ASCII, and surrogate keys" }
].forEach((val) => {
test(() => {
let params = new URLSearchParams(val.input),
i = 0
for (let param of params) {
assert_array_equals(param, val.output[i])
i++
}
}, "Construct with " + val.name)
})
test(() => {
var params = new URLSearchParams()
params[Symbol.iterator] = function *() {
yield ["a", "b"]
}
let params2 = new URLSearchParams(params)
assert_equals(params2.get("a"), "b")
}, "Custom [Symbol.iterator]")

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 %20');
assert_equals(url.href, 'data:space %20');
}, 'Changing the query of a URL with an opaque path with trailing spaces');
test(() => {
const url = new URL('data:space ?test#test');
url.searchParams.delete('test');
assert_equals(url.search, '');
assert_equals(url.pathname, 'space %20');
assert_equals(url.href, 'data:space %20#test');
}, 'Changing the query of a URL with an opaque path with trailing spaces and a 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,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,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,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,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,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,34 @@
test(() => {
const params = new URLSearchParams("a=1&b=2&a=3");
assert_equals(params.size, 3);
params.delete("a");
assert_equals(params.size, 1);
}, "URLSearchParams's size and deletion");
test(() => {
const params = new URLSearchParams("a=1&b=2&a=3");
assert_equals(params.size, 3);
params.append("b", "4");
assert_equals(params.size, 4);
}, "URLSearchParams's size and addition");
test(() => {
const url = new URL("http://localhost/query?a=1&b=2&a=3");
assert_equals(url.searchParams.size, 3);
url.searchParams.delete("a");
assert_equals(url.searchParams.size, 1);
url.searchParams.append("b", 4);
assert_equals(url.searchParams.size, 2);
}, "URLSearchParams's size when obtained from a URL");
test(() => {
const url = new URL("http://localhost/query?a=1&b=2&a=3");
assert_equals(url.searchParams.size, 3);
url.search = "?";
assert_equals(url.searchParams.size, 0);
}, "URLSearchParams's size when obtained from a URL and using .search");

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,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');