tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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