188 lines
7.8 KiB
JavaScript
188 lines
7.8 KiB
JavaScript
const kComponents = [
|
|
'protocol',
|
|
'username',
|
|
'password',
|
|
'hostname',
|
|
'port',
|
|
'password',
|
|
'pathname',
|
|
'search',
|
|
'hash',
|
|
];
|
|
|
|
function runTests(data) {
|
|
for (let entry of data) {
|
|
test(function() {
|
|
if (entry.expected_obj === 'error') {
|
|
assert_throws_js(TypeError, _ => new URLPattern(...entry.pattern),
|
|
'URLPattern() constructor');
|
|
return;
|
|
}
|
|
|
|
const pattern = new URLPattern(...entry.pattern);
|
|
|
|
// If the expected_obj property is not present we will automatically
|
|
// fill it with the most likely expected values.
|
|
entry.expected_obj = entry.expected_obj || {};
|
|
|
|
// The compiled URLPattern object should have a property for each
|
|
// component exposing the compiled pattern string.
|
|
for (let component of kComponents) {
|
|
// If the test case explicitly provides an expected pattern string,
|
|
// then use that. This is necessary in cases where the original
|
|
// construction pattern gets canonicalized, etc.
|
|
let expected = entry.expected_obj[component];
|
|
|
|
// If there is no explicit expected pattern string, then compute
|
|
// the expected value based on the URLPattern constructor args.
|
|
if (expected == undefined) {
|
|
// First determine if there is a baseURL present in the pattern
|
|
// input. A baseURL can be the source for many component patterns.
|
|
let baseURL = null;
|
|
if (entry.pattern.length > 0 && entry.pattern[0].baseURL) {
|
|
baseURL = new URL(entry.pattern[0].baseURL);
|
|
} else if (entry.pattern.length > 1 &&
|
|
typeof entry.pattern[1] === 'string') {
|
|
baseURL = new URL(entry.pattern[1]);
|
|
}
|
|
|
|
const EARLIER_COMPONENTS = {
|
|
protocol: [],
|
|
hostname: ["protocol"],
|
|
port: ["protocol", "hostname"],
|
|
username: [],
|
|
password: [],
|
|
pathname: ["protocol", "hostname", "port"],
|
|
search: ["protocol", "hostname", "port", "pathname"],
|
|
hash: ["protocol", "hostname", "port", "pathname", "search"],
|
|
};
|
|
|
|
// We automatically populate the expected pattern string using
|
|
// the following options in priority order:
|
|
//
|
|
// 1. If the original input explicitly provided a pattern, then
|
|
// echo that back as the expected value.
|
|
// 2. If an "earlier" component is specified, then a wildcard
|
|
// will be used rather than inheriting from the base URL.
|
|
// 3. If the baseURL exists and provides a component value then
|
|
// use that for the expected pattern.
|
|
// 4. Otherwise fall back on the default pattern of `*` for an
|
|
// empty component pattern.
|
|
//
|
|
// Note that username and password are never inherited, and will only
|
|
// need to match if explicitly specified.
|
|
if (entry.exactly_empty_components &&
|
|
entry.exactly_empty_components.includes(component)) {
|
|
expected = '';
|
|
} else if (typeof entry.pattern[0] === 'object' &&
|
|
entry.pattern[0][component]) {
|
|
expected = entry.pattern[0][component];
|
|
} else if (typeof entry.pattern[0] === 'object' &&
|
|
EARLIER_COMPONENTS[component].some(c => c in entry.pattern[0])) {
|
|
expected = '*';
|
|
} else if (baseURL && component !== 'username' && component !== 'password') {
|
|
let base_value = baseURL[component];
|
|
// Unfortunately some URL() getters include separator chars; e.g.
|
|
// the trailing `:` for the protocol. Strip those off if necessary.
|
|
if (component === 'protocol')
|
|
base_value = base_value.substring(0, base_value.length - 1);
|
|
else if (component === 'search' || component === 'hash')
|
|
base_value = base_value.substring(1, base_value.length);
|
|
expected = base_value;
|
|
} else {
|
|
expected = '*';
|
|
}
|
|
}
|
|
|
|
// Finally, assert that the compiled object property matches the
|
|
// expected property.
|
|
assert_equals(pattern[component], expected,
|
|
`compiled pattern property '${component}'`);
|
|
}
|
|
|
|
if (entry.expected_match === 'error') {
|
|
assert_throws_js(TypeError, _ => pattern.test(...entry.inputs),
|
|
'test() result');
|
|
assert_throws_js(TypeError, _ => pattern.exec(...entry.inputs),
|
|
'exec() result');
|
|
return;
|
|
}
|
|
|
|
// First, validate the test() method by converting the expected result to
|
|
// a truthy value.
|
|
assert_equals(pattern.test(...entry.inputs), !!entry.expected_match,
|
|
'test() result');
|
|
|
|
// Next, start validating the exec() method.
|
|
const exec_result = pattern.exec(...entry.inputs);
|
|
|
|
// On a failed match exec() returns null.
|
|
if (!entry.expected_match || typeof entry.expected_match !== "object") {
|
|
assert_equals(exec_result, entry.expected_match, 'exec() failed match result');
|
|
return;
|
|
}
|
|
|
|
if (!entry.expected_match.inputs)
|
|
entry.expected_match.inputs = entry.inputs;
|
|
|
|
// Next verify the result.input is correct. This may be a structured
|
|
// URLPatternInit dictionary object or a URL string.
|
|
assert_equals(exec_result.inputs.length,
|
|
entry.expected_match.inputs.length,
|
|
'exec() result.inputs.length');
|
|
for (let i = 0; i < exec_result.inputs.length; ++i) {
|
|
const input = exec_result.inputs[i];
|
|
const expected_input = entry.expected_match.inputs[i];
|
|
if (typeof input === 'string') {
|
|
assert_equals(input, expected_input, `exec() result.inputs[${i}]`);
|
|
continue;
|
|
}
|
|
for (let component of kComponents) {
|
|
assert_equals(input[component], expected_input[component],
|
|
`exec() result.inputs[${i}][${component}]`);
|
|
}
|
|
}
|
|
|
|
// Next we will compare the URLPatternComponentResult for each of these
|
|
// expected components.
|
|
for (let component of kComponents) {
|
|
let expected_obj = entry.expected_match[component];
|
|
|
|
// If the test expectations don't include a component object, then
|
|
// we auto-generate one. This is convenient for the many cases
|
|
// where the pattern has a default wildcard or empty string pattern
|
|
// for a component and the input is essentially empty.
|
|
if (!expected_obj) {
|
|
expected_obj = { input: '', groups: {} };
|
|
|
|
// Next, we must treat default wildcards differently than empty string
|
|
// patterns. The wildcard results in a capture group, but the empty
|
|
// string pattern does not. The expectation object must list which
|
|
// components should be empty instead of wildcards in
|
|
// |exactly_empty_components|.
|
|
if (!entry.exactly_empty_components ||
|
|
!entry.exactly_empty_components.includes(component)) {
|
|
expected_obj.groups['0'] = '';
|
|
}
|
|
}
|
|
// JSON does not allow us to use undefined directly, so the data file
|
|
// contains null instead. Translate to the expected undefined value
|
|
// here.
|
|
for (const key in expected_obj.groups) {
|
|
if (expected_obj.groups[key] === null) {
|
|
expected_obj.groups[key] = undefined;
|
|
}
|
|
}
|
|
assert_object_equals(exec_result[component], expected_obj,
|
|
`exec() result for ${component}`);
|
|
}
|
|
}, `Pattern: ${JSON.stringify(entry.pattern)} ` +
|
|
`Inputs: ${JSON.stringify(entry.inputs)}`);
|
|
}
|
|
}
|
|
|
|
promise_test(async function() {
|
|
const response = await fetch('resources/urlpatterntestdata.json');
|
|
const data = await response.json();
|
|
runTests(data);
|
|
}, 'Loading data...');
|