tor-browser

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

test-helper.js (10213B)


      1 let log = [];
      2 
      3 function expect_log(test, expected_log) {
      4  test.step_func_done(() => {
      5    const actual_log = log;
      6    log = [];
      7    assert_array_equals(actual_log, expected_log, 'fallback log');
      8  })();
      9 }
     10 
     11 // Results of resolving a specifier using import maps.
     12 const Result = {
     13  // A failure considered as a fetch error in a module script tree.
     14  // <script>'s error event is fired.
     15  FETCH_ERROR: "fetch_error",
     16 
     17  // A failure considered as a parse error in a module script tree.
     18  // Window's error event is fired.
     19  PARSE_ERROR: "parse_error",
     20 
     21  // The specifier is considered as a relative or absolute URL.
     22  // Specifier                 Expected log
     23  // ------------------------- ----------------------
     24  // ...?name=foo              log:foo
     25  // data:...log('foo')        foo
     26  // Others, e.g. bare/bare    relative:bare/bare
     27  // ------------------------- ----------------------
     28  // (The last case assumes a file `bare/bare` that logs `relative:bare/bare`
     29  // exists)
     30  URL: "URL",
     31 };
     32 
     33 const Handler = {
     34  // Handlers for <script> element cases.
     35  // Note that on a parse error both WindowErrorEvent and ScriptLoadEvent are
     36  // called.
     37  ScriptLoadEvent: "<script> element's load event handler",
     38  ScriptErrorEvent: "<script> element's error event handler",
     39  WindowErrorEvent: "window's error event handler",
     40 
     41  // Handlers for dynamic imports.
     42  DynamicImportResolve: "dynamic import resolve",
     43  DynamicImportReject: "dynamic import reject",
     44 };
     45 
     46 // Returns a map with Handler.* as the keys.
     47 function getHandlers(t, specifier, expected) {
     48  let handlers = {};
     49  handlers[Handler.ScriptLoadEvent] = t.unreached_func("Shouldn't load");
     50  handlers[Handler.ScriptErrorEvent] =
     51      t.unreached_func("script's error event shouldn't be fired");
     52  handlers[Handler.WindowErrorEvent] =
     53      t.unreached_func("window's error event shouldn't be fired");
     54  handlers[Handler.DynamicImportResolve] =
     55    t.unreached_func("dynamic import promise shouldn't be resolved");
     56  handlers[Handler.DynamicImportReject] =
     57    t.unreached_func("dynamic import promise shouldn't be rejected");
     58 
     59  if (expected === Result.FETCH_ERROR) {
     60    handlers[Handler.ScriptErrorEvent] = () => expect_log(t, []);
     61    handlers[Handler.DynamicImportReject] = () => expect_log(t, []);
     62  } else if (expected === Result.PARSE_ERROR) {
     63    let error_occurred = false;
     64    handlers[Handler.WindowErrorEvent] = () => { error_occurred = true; };
     65    handlers[Handler.ScriptLoadEvent] = t.step_func(() => {
     66      // Even if a parse error occurs, load event is fired (after
     67      // window.onerror is called), so trigger the load handler only if
     68      // there was no previous window.onerror call.
     69      assert_true(error_occurred, "window.onerror should be fired");
     70      expect_log(t, []);
     71    });
     72    handlers[Handler.DynamicImportReject] = t.step_func(() => {
     73      assert_false(error_occurred,
     74        "window.onerror shouldn't be fired for dynamic imports");
     75      expect_log(t, []);
     76    });
     77  } else {
     78    let expected_log;
     79    if (expected === Result.URL) {
     80      const match_data_url = specifier.match(/data:.*log\.push\('(.*)'\)/);
     81      const match_log_js = specifier.match(/name=(.*)/);
     82      if (match_data_url) {
     83        expected_log = [match_data_url[1]];
     84      } else if (match_log_js) {
     85        expected_log = ["log:" + match_log_js[1]];
     86      } else {
     87        expected_log = ["relative:" + specifier];
     88      }
     89    } else {
     90      expected_log = [expected];
     91    }
     92    handlers[Handler.ScriptLoadEvent] = () => expect_log(t, expected_log);
     93    handlers[Handler.DynamicImportResolve] = () => expect_log(t, expected_log);
     94  }
     95  return handlers;
     96 }
     97 
     98 // Creates an <iframe> and run a test inside the <iframe>
     99 // to separate the module maps and import maps in each test.
    100 function testInIframe(importMapString, importMapBaseURL, testScript) {
    101  const iframe = document.createElement('iframe');
    102  document.body.appendChild(iframe);
    103  if (!importMapBaseURL) {
    104    importMapBaseURL = document.baseURI;
    105  }
    106  let content = `
    107    <script src="/resources/testharness.js"></script>
    108    <script src="/import-maps/resources/test-helper.js"></script>
    109    <base href="${importMapBaseURL}">
    110    <script type="importmap">${importMapString}</script>
    111    <body>
    112    <script>
    113    setup({ allow_uncaught_exception: true });
    114    ${testScript}
    115    </sc` + `ript>
    116  `;
    117  iframe.contentDocument.write(content);
    118  iframe.contentDocument.close();
    119  return fetch_tests_from_window(iframe.contentWindow);
    120 }
    121 
    122 function testScriptElement(importMapString, importMapBaseURL, specifier, expected, type) {
    123  return testInIframe(importMapString, importMapBaseURL, `
    124    const t = async_test("${specifier}: <script src type=${type}>");
    125    const handlers = getHandlers(t, "${specifier}", "${expected}");
    126    const script = document.createElement("script");
    127    script.setAttribute("type", "${type}");
    128    script.setAttribute("src", "${specifier}");
    129    script.addEventListener("load", handlers[Handler.ScriptLoadEvent]);
    130    script.addEventListener("error", handlers[Handler.ScriptErrorEvent]);
    131    window.addEventListener("error", handlers[Handler.WindowErrorEvent]);
    132    document.body.appendChild(script);
    133  `);
    134 }
    135 
    136 function testStaticImport(importMapString, importMapBaseURL, specifier, expected) {
    137  return testInIframe(importMapString, importMapBaseURL, `
    138    const t = async_test("${specifier}: static import");
    139    const handlers = getHandlers(t, "${specifier}", "${expected}");
    140    const script = document.createElement("script");
    141    script.setAttribute("type", "module");
    142    script.setAttribute("src",
    143        "/import-maps/static-import.py?url=" +
    144        encodeURIComponent("${specifier}"));
    145    script.addEventListener("load", handlers[Handler.ScriptLoadEvent]);
    146    script.addEventListener("error", handlers[Handler.ScriptErrorEvent]);
    147    window.addEventListener("error", handlers[Handler.WindowErrorEvent]);
    148    document.body.appendChild(script);
    149  `);
    150 }
    151 
    152 function testDynamicImport(importMapString, importMapBaseURL, specifier, expected, type) {
    153  return testInIframe(importMapString, importMapBaseURL, `
    154    const t = async_test("${specifier}: dynamic import (from ${type})");
    155    const handlers = getHandlers(t, "${specifier}", "${expected}");
    156    const script = document.createElement("script");
    157    script.setAttribute("type", "${type}");
    158    script.innerText =
    159        "import(\\"${specifier}\\")" +
    160        ".then(handlers[Handler.DynamicImportResolve], " +
    161        "handlers[Handler.DynamicImportReject]);";
    162    script.addEventListener("error",
    163        t.unreached_func("top-level inline script shouldn't error"));
    164    document.body.appendChild(script);
    165  `);
    166 }
    167 
    168 function testInIframeInjectBase(importMapString, importMapBaseURL, testScript) {
    169  const iframe = document.createElement('iframe');
    170  document.body.appendChild(iframe);
    171  let content = `
    172    <script src="/resources/testharness.js"></script>
    173    <script src="/import-maps/resources/test-helper.js"></script>
    174    <script src="/import-maps/resources/inject-base.js?pipe=sub&baseurl=${importMapBaseURL}"></script>
    175    <script type="importmap">
    176      ${importMapString}
    177    </script>
    178    <body>
    179    <script>
    180    setup({ allow_uncaught_exception: true });
    181    ${testScript}
    182    </sc` + `ript>
    183  `;
    184  iframe.contentDocument.write(content);
    185  iframe.contentDocument.close();
    186  return fetch_tests_from_window(iframe.contentWindow);
    187 }
    188 
    189 function testStaticImportInjectBase(importMapString, importMapBaseURL, specifier, expected) {
    190  return testInIframeInjectBase(importMapString, importMapBaseURL, `
    191    const t = async_test("${specifier}: static import with inject <base>");
    192    const handlers = getHandlers(t, "${specifier}", "${expected}");
    193    const script = document.createElement("script");
    194    script.setAttribute("type", "module");
    195    script.setAttribute("src",
    196        "/import-maps/static-import.py?url=" +
    197        encodeURIComponent("${specifier}"));
    198    script.addEventListener("load", handlers[Handler.ScriptLoadEvent]);
    199    script.addEventListener("error", handlers[Handler.ScriptErrorEvent]);
    200    window.addEventListener("error", handlers[Handler.WindowErrorEvent]);
    201    document.body.appendChild(script);
    202  `);
    203 }
    204 
    205 function testDynamicImportInjectBase(importMapString, importMapBaseURL, specifier, expected, type) {
    206  return testInIframeInjectBase(importMapString, importMapBaseURL, `
    207    const t = async_test("${specifier}: dynamic import (from ${type}) with inject <base>");
    208    const handlers = getHandlers(t, "${specifier}", "${expected}");
    209    const script = document.createElement("script");
    210    script.setAttribute("type", "${type}");
    211    script.innerText =
    212        "import(\\"${specifier}\\")" +
    213        ".then(handlers[Handler.DynamicImportResolve], " +
    214        "handlers[Handler.DynamicImportReject]);";
    215    script.addEventListener("error",
    216        t.unreached_func("top-level inline script shouldn't error"));
    217    document.body.appendChild(script);
    218  `);
    219 }
    220 
    221 function doTests(importMapString, importMapBaseURL, tests) {
    222  promise_setup(function () {
    223    return new Promise((resolve) => {
    224      window.addEventListener("load", async () => {
    225        for (const specifier in tests) {
    226          // <script src> (module scripts)
    227          await testScriptElement(importMapString, importMapBaseURL, specifier, tests[specifier][0], "module");
    228 
    229          // <script src> (classic scripts)
    230          await testScriptElement(importMapString, importMapBaseURL, specifier, tests[specifier][1], "text/javascript");
    231 
    232          // static imports.
    233          await testStaticImport(importMapString, importMapBaseURL, specifier, tests[specifier][2]);
    234 
    235          // dynamic imports from a module script.
    236          await testDynamicImport(importMapString, importMapBaseURL, specifier, tests[specifier][3], "module");
    237 
    238          // dynamic imports from a classic script.
    239          await testDynamicImport(importMapString, importMapBaseURL, specifier, tests[specifier][3], "text/javascript");
    240        }
    241        done();
    242        resolve();
    243      });
    244    });
    245  }, { explicit_done: true });
    246 }
    247 
    248 function test_loaded(specifier, expected_log, description) {
    249  promise_test(async t => {
    250    log = [];
    251    await import(specifier);
    252    assert_array_equals(log, expected_log);
    253  }, description);
    254 };