urlpatterntests.js (7974B)
1 const kComponents = [ 2 'protocol', 3 'username', 4 'password', 5 'hostname', 6 'port', 7 'password', 8 'pathname', 9 'search', 10 'hash', 11 ]; 12 13 function runTests(data) { 14 for (let entry of data) { 15 test(function() { 16 if (entry.expected_obj === 'error') { 17 assert_throws_js(TypeError, _ => new URLPattern(...entry.pattern), 18 'URLPattern() constructor'); 19 return; 20 } 21 22 const pattern = new URLPattern(...entry.pattern); 23 24 // If the expected_obj property is not present we will automatically 25 // fill it with the most likely expected values. 26 entry.expected_obj = entry.expected_obj || {}; 27 28 // The compiled URLPattern object should have a property for each 29 // component exposing the compiled pattern string. 30 for (let component of kComponents) { 31 // If the test case explicitly provides an expected pattern string, 32 // then use that. This is necessary in cases where the original 33 // construction pattern gets canonicalized, etc. 34 let expected = entry.expected_obj[component]; 35 36 // If there is no explicit expected pattern string, then compute 37 // the expected value based on the URLPattern constructor args. 38 if (expected == undefined) { 39 // First determine if there is a baseURL present in the pattern 40 // input. A baseURL can be the source for many component patterns. 41 let baseURL = null; 42 if (entry.pattern.length > 0 && entry.pattern[0].baseURL) { 43 baseURL = new URL(entry.pattern[0].baseURL); 44 } else if (entry.pattern.length > 1 && 45 typeof entry.pattern[1] === 'string') { 46 baseURL = new URL(entry.pattern[1]); 47 } 48 49 const EARLIER_COMPONENTS = { 50 protocol: [], 51 hostname: ["protocol"], 52 port: ["protocol", "hostname"], 53 username: [], 54 password: [], 55 pathname: ["protocol", "hostname", "port"], 56 search: ["protocol", "hostname", "port", "pathname"], 57 hash: ["protocol", "hostname", "port", "pathname", "search"], 58 }; 59 60 // We automatically populate the expected pattern string using 61 // the following options in priority order: 62 // 63 // 1. If the original input explicitly provided a pattern, then 64 // echo that back as the expected value. 65 // 2. If an "earlier" component is specified, then a wildcard 66 // will be used rather than inheriting from the base URL. 67 // 3. If the baseURL exists and provides a component value then 68 // use that for the expected pattern. 69 // 4. Otherwise fall back on the default pattern of `*` for an 70 // empty component pattern. 71 // 72 // Note that username and password are never inherited, and will only 73 // need to match if explicitly specified. 74 if (entry.exactly_empty_components && 75 entry.exactly_empty_components.includes(component)) { 76 expected = ''; 77 } else if (typeof entry.pattern[0] === 'object' && 78 entry.pattern[0][component]) { 79 expected = entry.pattern[0][component]; 80 } else if (typeof entry.pattern[0] === 'object' && 81 EARLIER_COMPONENTS[component].some(c => c in entry.pattern[0])) { 82 expected = '*'; 83 } else if (baseURL && component !== 'username' && component !== 'password') { 84 let base_value = baseURL[component]; 85 // Unfortunately some URL() getters include separator chars; e.g. 86 // the trailing `:` for the protocol. Strip those off if necessary. 87 if (component === 'protocol') 88 base_value = base_value.substring(0, base_value.length - 1); 89 else if (component === 'search' || component === 'hash') 90 base_value = base_value.substring(1, base_value.length); 91 expected = base_value; 92 } else { 93 expected = '*'; 94 } 95 } 96 97 // Finally, assert that the compiled object property matches the 98 // expected property. 99 assert_equals(pattern[component], expected, 100 `compiled pattern property '${component}'`); 101 } 102 103 if (entry.expected_match === 'error') { 104 assert_throws_js(TypeError, _ => pattern.test(...entry.inputs), 105 'test() result'); 106 assert_throws_js(TypeError, _ => pattern.exec(...entry.inputs), 107 'exec() result'); 108 return; 109 } 110 111 // First, validate the test() method by converting the expected result to 112 // a truthy value. 113 assert_equals(pattern.test(...entry.inputs), !!entry.expected_match, 114 'test() result'); 115 116 // Next, start validating the exec() method. 117 const exec_result = pattern.exec(...entry.inputs); 118 119 // On a failed match exec() returns null. 120 if (!entry.expected_match || typeof entry.expected_match !== "object") { 121 assert_equals(exec_result, entry.expected_match, 'exec() failed match result'); 122 return; 123 } 124 125 if (!entry.expected_match.inputs) 126 entry.expected_match.inputs = entry.inputs; 127 128 // Next verify the result.input is correct. This may be a structured 129 // URLPatternInit dictionary object or a URL string. 130 assert_equals(exec_result.inputs.length, 131 entry.expected_match.inputs.length, 132 'exec() result.inputs.length'); 133 for (let i = 0; i < exec_result.inputs.length; ++i) { 134 const input = exec_result.inputs[i]; 135 const expected_input = entry.expected_match.inputs[i]; 136 if (typeof input === 'string') { 137 assert_equals(input, expected_input, `exec() result.inputs[${i}]`); 138 continue; 139 } 140 for (let component of kComponents) { 141 assert_equals(input[component], expected_input[component], 142 `exec() result.inputs[${i}][${component}]`); 143 } 144 } 145 146 // Next we will compare the URLPatternComponentResult for each of these 147 // expected components. 148 for (let component of kComponents) { 149 let expected_obj = entry.expected_match[component]; 150 151 // If the test expectations don't include a component object, then 152 // we auto-generate one. This is convenient for the many cases 153 // where the pattern has a default wildcard or empty string pattern 154 // for a component and the input is essentially empty. 155 if (!expected_obj) { 156 expected_obj = { input: '', groups: {} }; 157 158 // Next, we must treat default wildcards differently than empty string 159 // patterns. The wildcard results in a capture group, but the empty 160 // string pattern does not. The expectation object must list which 161 // components should be empty instead of wildcards in 162 // |exactly_empty_components|. 163 if (!entry.exactly_empty_components || 164 !entry.exactly_empty_components.includes(component)) { 165 expected_obj.groups['0'] = ''; 166 } 167 } 168 // JSON does not allow us to use undefined directly, so the data file 169 // contains null instead. Translate to the expected undefined value 170 // here. 171 for (const key in expected_obj.groups) { 172 if (expected_obj.groups[key] === null) { 173 expected_obj.groups[key] = undefined; 174 } 175 } 176 assert_object_equals(exec_result[component], expected_obj, 177 `exec() result for ${component}`); 178 } 179 }, `Pattern: ${JSON.stringify(entry.pattern)} ` + 180 `Inputs: ${JSON.stringify(entry.inputs)}`); 181 } 182 } 183 184 promise_test(async function() { 185 const response = await fetch('resources/urlpatterntestdata.json'); 186 const data = await response.json(); 187 runTests(data); 188 }, 'Loading data...');