tor-browser

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

browser_resources_error_messages.js (24213B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 // Test the ResourceCommand API around ERROR_MESSAGE
      7 // Reproduces assertions from devtools/shared/webconsole/test/chrome/test_page_errors.html
      8 
      9 // Create a simple server so we have a nice sourceName in the resources packets.
     10 const httpServer = createTestHTTPServer();
     11 httpServer.registerPathHandler(`/test_page_errors.html`, (req, res) => {
     12  res.setStatusLine(req.httpVersion, 200, "OK");
     13  res.write(`<!DOCTYPE html><meta charset=utf8>Test Error Messages`);
     14 });
     15 
     16 const TEST_URI = `http://localhost:${httpServer.identity.primaryPort}/test_page_errors.html`;
     17 
     18 const { getMdnLinkParams } = ChromeUtils.importESModule(
     19  "resource://devtools/shared/mdn.mjs"
     20 );
     21 
     22 add_task(async function () {
     23  // Disable the preloaded process as it creates processes intermittently
     24  // which forces the emission of RDP requests we aren't correctly waiting for.
     25  await pushPref("dom.ipc.processPrelaunch.enabled", false);
     26 
     27  await testErrorMessagesResources();
     28  await testErrorMessagesResourcesWithIgnoreExistingResources();
     29 });
     30 
     31 async function testErrorMessagesResources() {
     32  // Open a test tab
     33  const tab = await addTab(TEST_URI);
     34 
     35  const { client, resourceCommand, targetCommand } =
     36    await initResourceCommand(tab);
     37 
     38  const receivedMessages = [];
     39  // The expected messages are the errors, twice (once for cached messages, once for live messages)
     40  const expectedMessages = Array.from(expectedPageErrors.values()).concat(
     41    Array.from(expectedPageErrors.values())
     42  );
     43 
     44  info(
     45    "Log some errors *before* calling ResourceCommand.watchResources in order to assert" +
     46      " the behavior of already existing messages."
     47  );
     48  await triggerErrors(tab, resourceCommand);
     49 
     50  let done;
     51  const onAllErrorReceived = new Promise(resolve => (done = resolve));
     52  const onAvailable = resources => {
     53    for (const resource of resources) {
     54      const { pageError } = resource;
     55 
     56      is(
     57        resource.targetFront,
     58        targetCommand.targetFront,
     59        "The targetFront property is the expected one"
     60      );
     61 
     62      if (!pageError.sourceName.includes("test_page_errors")) {
     63        info(`Ignore error from unknown source: "${pageError.sourceName}"`);
     64        continue;
     65      }
     66 
     67      const index = receivedMessages.length;
     68      receivedMessages.push(resource);
     69 
     70      const isAlreadyExistingResource =
     71        receivedMessages.length <= expectedPageErrors.size;
     72      is(
     73        resource.isAlreadyExistingResource,
     74        isAlreadyExistingResource,
     75        "isAlreadyExistingResource has expected value"
     76      );
     77 
     78      info(`checking received page error #${index}: ${pageError.errorMessage}`);
     79      ok(pageError, "The resource has a pageError attribute");
     80      checkPageErrorResource(pageError, expectedMessages[index]);
     81 
     82      if (receivedMessages.length == expectedMessages.length) {
     83        done();
     84      }
     85    }
     86  };
     87 
     88  await resourceCommand.watchResources([resourceCommand.TYPES.ERROR_MESSAGE], {
     89    onAvailable,
     90  });
     91 
     92  await BrowserTestUtils.waitForCondition(
     93    () => receivedMessages.length === expectedPageErrors.size
     94  );
     95 
     96  info(
     97    "Now log errors *after* the call to ResourceCommand.watchResources and after having" +
     98      " received all existing messages"
     99  );
    100  await triggerErrors(tab, resourceCommand);
    101 
    102  info("Waiting for all expected errors to be received");
    103  await onAllErrorReceived;
    104  ok(true, "All the expected errors were received");
    105 
    106  Services.console.reset();
    107  targetCommand.destroy();
    108  await client.close();
    109 }
    110 
    111 async function testErrorMessagesResourcesWithIgnoreExistingResources() {
    112  info("Test ignoreExistingResources option for ERROR_MESSAGE");
    113  const tab = await addTab(TEST_URI);
    114 
    115  const { client, resourceCommand, targetCommand } =
    116    await initResourceCommand(tab);
    117 
    118  info(
    119    "Check whether onAvailable will not be called with existing error messages"
    120  );
    121  await triggerErrors(tab, resourceCommand);
    122 
    123  const availableResources = [];
    124  await resourceCommand.watchResources([resourceCommand.TYPES.ERROR_MESSAGE], {
    125    onAvailable: resources => availableResources.push(...resources),
    126    ignoreExistingResources: true,
    127  });
    128  is(
    129    availableResources.length,
    130    0,
    131    "onAvailable wasn't called for existing error messages"
    132  );
    133 
    134  info(
    135    "Check whether onAvailable will be called with the future error messages"
    136  );
    137  await triggerErrors(tab, resourceCommand);
    138 
    139  const expectedMessages = Array.from(expectedPageErrors.values());
    140  await waitUntil(() => availableResources.length === expectedMessages.length);
    141  for (let i = 0; i < expectedMessages.length; i++) {
    142    const resource = availableResources[i];
    143    const { pageError } = resource;
    144    const expected = expectedMessages[i];
    145    checkPageErrorResource(pageError, expected);
    146    is(
    147      resource.isAlreadyExistingResource,
    148      false,
    149      "isAlreadyExistingResource is set to false for live messages"
    150    );
    151  }
    152 
    153  Services.console.reset();
    154  targetCommand.destroy();
    155  await client.close();
    156 }
    157 
    158 /**
    159 * Triggers all the errors in the content page.
    160 */
    161 async function triggerErrors(tab, resourceCommand) {
    162  for (const [expression, expected] of expectedPageErrors.entries()) {
    163    if (
    164      !expected[noUncaughtException] &&
    165      !Services.appinfo.browserTabsRemoteAutostart
    166    ) {
    167      expectUncaughtException();
    168    }
    169 
    170    const { promise: onErrorMessage, resolve } = Promise.withResolvers();
    171    const onAvailable = resources => {
    172      if (
    173        resources.some(r =>
    174          expected.errorMessage.test(r.pageError.errorMessage)
    175        )
    176      ) {
    177        resolve();
    178      }
    179    };
    180    await resourceCommand.watchResources(
    181      [resourceCommand.TYPES.ERROR_MESSAGE],
    182      {
    183        onAvailable,
    184        ignoreExistingResources: true,
    185      }
    186    );
    187 
    188    await ContentTask.spawn(
    189      tab.linkedBrowser,
    190      expression,
    191      function frameScript(expr) {
    192        const document = content.document;
    193        const scriptEl = document.createElement("script");
    194        scriptEl.textContent = expr;
    195        document.body.appendChild(scriptEl);
    196      }
    197    );
    198 
    199    await onErrorMessage;
    200    await resourceCommand.unwatchResources(
    201      [resourceCommand.TYPES.ERROR_MESSAGE],
    202      {
    203        onAvailable,
    204      }
    205    );
    206  }
    207 }
    208 
    209 function checkPageErrorResource(pageErrorResource, expected) {
    210  // Let's remove test harness related frames in stacktrace
    211  const clonedPageErrorResource = { ...pageErrorResource };
    212  if (clonedPageErrorResource.stacktrace) {
    213    const index = clonedPageErrorResource.stacktrace.findIndex(frame =>
    214      frame.filename.startsWith("resource://testing-common/content-task.js")
    215    );
    216    if (index > -1) {
    217      clonedPageErrorResource.stacktrace =
    218        clonedPageErrorResource.stacktrace.slice(0, index);
    219    }
    220  }
    221  checkObject(clonedPageErrorResource, expected);
    222 }
    223 
    224 const noUncaughtException = Symbol();
    225 const NUMBER_REGEX = /^\d+$/;
    226 // timeStamp are the result of a number in microsecond divided by 1000.
    227 // so we can't expect a precise number of decimals, or even if there would
    228 // be decimals at all.
    229 const FRACTIONAL_NUMBER_REGEX = /^\d+(\.\d{1,3})?$/;
    230 
    231 const mdnUrl = path =>
    232  `https://developer.mozilla.org/${path}?${getMdnLinkParams("firefox-console-errors")}`;
    233 
    234 const expectedPageErrors = new Map([
    235  [
    236    "document.doTheImpossible();",
    237    {
    238      errorMessage: /doTheImpossible/,
    239      errorMessageName: "JSMSG_NOT_FUNCTION",
    240      sourceName: /test_page_errors/,
    241      category: "content javascript",
    242      timeStamp: FRACTIONAL_NUMBER_REGEX,
    243      error: true,
    244      warning: false,
    245      info: false,
    246      lineNumber: NUMBER_REGEX,
    247      columnNumber: NUMBER_REGEX,
    248      exceptionDocURL: mdnUrl(
    249        "docs/Web/JavaScript/Reference/Errors/Not_a_function"
    250      ),
    251      innerWindowID: NUMBER_REGEX,
    252      private: false,
    253      stacktrace: [
    254        {
    255          filename: /test_page_errors\.html/,
    256          lineNumber: 1,
    257          columnNumber: 10,
    258          functionName: null,
    259        },
    260      ],
    261      notes: null,
    262      chromeContext: false,
    263      isPromiseRejection: false,
    264      isForwardedFromContentProcess: false,
    265    },
    266  ],
    267  [
    268    "(42).toString(0);",
    269    {
    270      errorMessage: /radix/,
    271      errorMessageName: "JSMSG_BAD_RADIX",
    272      sourceName: /test_page_errors/,
    273      category: "content javascript",
    274      timeStamp: FRACTIONAL_NUMBER_REGEX,
    275      error: true,
    276      warning: false,
    277      info: false,
    278      lineNumber: NUMBER_REGEX,
    279      columnNumber: NUMBER_REGEX,
    280      exceptionDocURL: mdnUrl("docs/Web/JavaScript/Reference/Errors/Bad_radix"),
    281      innerWindowID: NUMBER_REGEX,
    282      private: false,
    283      stacktrace: [
    284        {
    285          filename: /test_page_errors\.html/,
    286          lineNumber: 1,
    287          columnNumber: 6,
    288          functionName: null,
    289        },
    290      ],
    291      notes: null,
    292      chromeContext: false,
    293      isPromiseRejection: false,
    294      isForwardedFromContentProcess: false,
    295    },
    296  ],
    297  [
    298    "'use strict'; (Object.freeze({name: 'Elsa', score: 157})).score = 0;",
    299    {
    300      errorMessage: /read.only/,
    301      errorMessageName: "JSMSG_READ_ONLY",
    302      sourceName: /test_page_errors/,
    303      category: "content javascript",
    304      timeStamp: FRACTIONAL_NUMBER_REGEX,
    305      error: true,
    306      warning: false,
    307      info: false,
    308      lineNumber: NUMBER_REGEX,
    309      columnNumber: NUMBER_REGEX,
    310      exceptionDocURL: mdnUrl("docs/Web/JavaScript/Reference/Errors/Read-only"),
    311      innerWindowID: NUMBER_REGEX,
    312      private: false,
    313      stacktrace: [
    314        {
    315          filename: /test_page_errors\.html/,
    316          lineNumber: 1,
    317          columnNumber: 23,
    318          functionName: null,
    319        },
    320      ],
    321      notes: null,
    322      chromeContext: false,
    323      isPromiseRejection: false,
    324      isForwardedFromContentProcess: false,
    325    },
    326  ],
    327  [
    328    "([]).length = -1",
    329    {
    330      errorMessage: /array length/,
    331      errorMessageName: "JSMSG_BAD_ARRAY_LENGTH",
    332      sourceName: /test_page_errors/,
    333      category: "content javascript",
    334      timeStamp: FRACTIONAL_NUMBER_REGEX,
    335      error: true,
    336      warning: false,
    337      info: false,
    338      lineNumber: NUMBER_REGEX,
    339      columnNumber: NUMBER_REGEX,
    340      exceptionDocURL: mdnUrl(
    341        "docs/Web/JavaScript/Reference/Errors/Invalid_array_length"
    342      ),
    343      innerWindowID: NUMBER_REGEX,
    344      private: false,
    345      stacktrace: [
    346        {
    347          filename: /test_page_errors\.html/,
    348          lineNumber: 1,
    349          columnNumber: 2,
    350          functionName: null,
    351        },
    352      ],
    353      notes: null,
    354      chromeContext: false,
    355      isPromiseRejection: false,
    356      isForwardedFromContentProcess: false,
    357    },
    358  ],
    359  [
    360    "'abc'.repeat(-1);",
    361    {
    362      errorMessage: /repeat count.*non-negative/,
    363      errorMessageName: "JSMSG_NEGATIVE_REPETITION_COUNT",
    364      sourceName: /test_page_errors/,
    365      category: "content javascript",
    366      timeStamp: FRACTIONAL_NUMBER_REGEX,
    367      error: true,
    368      warning: false,
    369      info: false,
    370      lineNumber: NUMBER_REGEX,
    371      columnNumber: NUMBER_REGEX,
    372      exceptionDocURL: mdnUrl(
    373        "docs/Web/JavaScript/Reference/Errors/Negative_repetition_count"
    374      ),
    375      innerWindowID: NUMBER_REGEX,
    376      private: false,
    377      stacktrace: [
    378        {
    379          filename: "self-hosted",
    380          sourceId: null,
    381          lineNumber: NUMBER_REGEX,
    382          columnNumber: NUMBER_REGEX,
    383          functionName: "repeat",
    384        },
    385        {
    386          filename: /test_page_errors\.html/,
    387          lineNumber: 1,
    388          columnNumber: 7,
    389          functionName: null,
    390        },
    391      ],
    392      notes: null,
    393      chromeContext: false,
    394      isPromiseRejection: false,
    395      isForwardedFromContentProcess: false,
    396    },
    397  ],
    398  [
    399    "'a'.repeat(2e28);",
    400    {
    401      errorMessage: /repeat count.*less than infinity/,
    402      errorMessageName: "JSMSG_RESULTING_STRING_TOO_LARGE",
    403      sourceName: /test_page_errors/,
    404      category: "content javascript",
    405      timeStamp: FRACTIONAL_NUMBER_REGEX,
    406      error: true,
    407      warning: false,
    408      info: false,
    409      lineNumber: NUMBER_REGEX,
    410      columnNumber: NUMBER_REGEX,
    411      exceptionDocURL: mdnUrl(
    412        "docs/Web/JavaScript/Reference/Errors/Resulting_string_too_large"
    413      ),
    414      innerWindowID: NUMBER_REGEX,
    415      private: false,
    416      stacktrace: [
    417        {
    418          filename: "self-hosted",
    419          sourceId: null,
    420          lineNumber: NUMBER_REGEX,
    421          columnNumber: NUMBER_REGEX,
    422          functionName: "repeat",
    423        },
    424        {
    425          filename: /test_page_errors\.html/,
    426          lineNumber: 1,
    427          columnNumber: 5,
    428          functionName: null,
    429        },
    430      ],
    431      notes: null,
    432      chromeContext: false,
    433      isPromiseRejection: false,
    434      isForwardedFromContentProcess: false,
    435    },
    436  ],
    437  [
    438    "77.1234.toExponential(-1);",
    439    {
    440      errorMessage: /out of range/,
    441      errorMessageName: "JSMSG_PRECISION_RANGE",
    442      sourceName: /test_page_errors/,
    443      category: "content javascript",
    444      timeStamp: FRACTIONAL_NUMBER_REGEX,
    445      error: true,
    446      warning: false,
    447      info: false,
    448      lineNumber: NUMBER_REGEX,
    449      columnNumber: NUMBER_REGEX,
    450      exceptionDocURL: mdnUrl(
    451        "docs/Web/JavaScript/Reference/Errors/Precision_range"
    452      ),
    453      innerWindowID: NUMBER_REGEX,
    454      private: false,
    455      stacktrace: [
    456        {
    457          filename: /test_page_errors\.html/,
    458          lineNumber: 1,
    459          columnNumber: 9,
    460          functionName: null,
    461        },
    462      ],
    463      notes: null,
    464      chromeContext: false,
    465      isPromiseRejection: false,
    466      isForwardedFromContentProcess: false,
    467    },
    468  ],
    469  [
    470    "function a() { return; 1 + 1; }",
    471    {
    472      errorMessage: /unreachable code/,
    473      errorMessageName: "JSMSG_STMT_AFTER_RETURN",
    474      sourceName: /test_page_errors/,
    475      category: "content javascript",
    476      timeStamp: FRACTIONAL_NUMBER_REGEX,
    477      error: false,
    478      warning: true,
    479      info: false,
    480      sourceId: null,
    481      lineNumber: NUMBER_REGEX,
    482      columnNumber: NUMBER_REGEX,
    483      exceptionDocURL: mdnUrl(
    484        "docs/Web/JavaScript/Reference/Errors/Stmt_after_return"
    485      ),
    486      innerWindowID: NUMBER_REGEX,
    487      private: false,
    488      stacktrace: null,
    489      notes: null,
    490      chromeContext: false,
    491      isPromiseRejection: false,
    492      isForwardedFromContentProcess: false,
    493    },
    494  ],
    495  [
    496    "{let a, a;}",
    497    {
    498      errorMessage: /redeclaration of/,
    499      errorMessageName: "JSMSG_REDECLARED_VAR",
    500      sourceName: /test_page_errors/,
    501      category: "content javascript",
    502      timeStamp: FRACTIONAL_NUMBER_REGEX,
    503      error: true,
    504      warning: false,
    505      info: false,
    506      sourceId: null,
    507      lineNumber: NUMBER_REGEX,
    508      columnNumber: NUMBER_REGEX,
    509      exceptionDocURL: mdnUrl(
    510        "docs/Web/JavaScript/Reference/Errors/Redeclared_parameter"
    511      ),
    512      innerWindowID: NUMBER_REGEX,
    513      private: false,
    514      stacktrace: [],
    515      chromeContext: false,
    516      isPromiseRejection: false,
    517      isForwardedFromContentProcess: false,
    518      notes: [
    519        {
    520          messageBody: /Previously declared at line/,
    521          frame: {
    522            source: /test_page_errors/,
    523          },
    524        },
    525      ],
    526    },
    527  ],
    528  [
    529    `var error = new TypeError("abc");
    530      error.name = "MyError";
    531      error.message = "here";
    532      throw error`,
    533    {
    534      errorMessage: /MyError: here/,
    535      errorMessageName: "",
    536      sourceName: /test_page_errors/,
    537      category: "content javascript",
    538      timeStamp: FRACTIONAL_NUMBER_REGEX,
    539      error: true,
    540      warning: false,
    541      info: false,
    542      lineNumber: NUMBER_REGEX,
    543      columnNumber: NUMBER_REGEX,
    544      exceptionDocURL: undefined,
    545      innerWindowID: NUMBER_REGEX,
    546      private: false,
    547      stacktrace: [
    548        {
    549          filename: /test_page_errors\.html/,
    550          lineNumber: 1,
    551          columnNumber: 13,
    552          functionName: null,
    553        },
    554      ],
    555      notes: null,
    556      chromeContext: false,
    557      isPromiseRejection: false,
    558      isForwardedFromContentProcess: false,
    559    },
    560  ],
    561  [
    562    "DOMTokenList.prototype.contains.call([])",
    563    {
    564      errorMessage: /does not implement interface/,
    565      errorMessageName: "MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE",
    566      sourceName: /test_page_errors/,
    567      category: "content javascript",
    568      timeStamp: FRACTIONAL_NUMBER_REGEX,
    569      error: true,
    570      warning: false,
    571      info: false,
    572      lineNumber: NUMBER_REGEX,
    573      columnNumber: NUMBER_REGEX,
    574      exceptionDocURL: undefined,
    575      innerWindowID: NUMBER_REGEX,
    576      private: false,
    577      stacktrace: [
    578        {
    579          filename: /test_page_errors\.html/,
    580          lineNumber: 1,
    581          columnNumber: 33,
    582          functionName: null,
    583        },
    584      ],
    585      notes: null,
    586      chromeContext: false,
    587      isPromiseRejection: false,
    588      isForwardedFromContentProcess: false,
    589    },
    590  ],
    591  [
    592    `
    593      function promiseThrow() {
    594        var error2 = new TypeError("abc");
    595        error2.name = "MyPromiseError";
    596        error2.message = "here2";
    597        return Promise.reject(error2);
    598      }
    599      promiseThrow()`,
    600    {
    601      errorMessage: /MyPromiseError: here2/,
    602      errorMessageName: "",
    603      sourceName: /test_page_errors/,
    604      category: "content javascript",
    605      timeStamp: FRACTIONAL_NUMBER_REGEX,
    606      error: true,
    607      warning: false,
    608      info: false,
    609      lineNumber: NUMBER_REGEX,
    610      columnNumber: NUMBER_REGEX,
    611      exceptionDocURL: undefined,
    612      innerWindowID: NUMBER_REGEX,
    613      private: false,
    614      stacktrace: [
    615        {
    616          filename: /test_page_errors\.html/,
    617          sourceId: null,
    618          lineNumber: 6,
    619          columnNumber: 24,
    620          functionName: "promiseThrow",
    621        },
    622        {
    623          filename: /test_page_errors\.html/,
    624          sourceId: null,
    625          lineNumber: 8,
    626          columnNumber: 7,
    627          functionName: null,
    628        },
    629      ],
    630      notes: null,
    631      chromeContext: false,
    632      isPromiseRejection: true,
    633      isForwardedFromContentProcess: false,
    634      [noUncaughtException]: true,
    635    },
    636  ],
    637  [
    638    // Error with a cause
    639    `var originalError = new TypeError("abc");
    640      var error = new Error("something went wrong", { cause: originalError })
    641      throw error`,
    642    {
    643      errorMessage: /Error: something went wrong/,
    644      errorMessageName: "",
    645      sourceName: /test_page_errors/,
    646      category: "content javascript",
    647      timeStamp: FRACTIONAL_NUMBER_REGEX,
    648      error: true,
    649      warning: false,
    650      info: false,
    651      lineNumber: NUMBER_REGEX,
    652      columnNumber: NUMBER_REGEX,
    653      innerWindowID: NUMBER_REGEX,
    654      private: false,
    655      stacktrace: [
    656        {
    657          filename: /test_page_errors\.html/,
    658          lineNumber: 2,
    659          columnNumber: 19,
    660          functionName: null,
    661        },
    662      ],
    663      exception: {
    664        preview: {
    665          cause: {
    666            class: "TypeError",
    667            preview: {
    668              message: "abc",
    669            },
    670          },
    671        },
    672      },
    673      notes: null,
    674      chromeContext: false,
    675      isPromiseRejection: false,
    676      isForwardedFromContentProcess: false,
    677    },
    678  ],
    679  [
    680    // Error with a cause chain
    681    `var a = new Error("err-a");
    682     var b = new Error("err-b", { cause: a });
    683     var c = new Error("err-c", { cause: b });
    684     var d = new Error("err-d", { cause: c });
    685      throw d`,
    686    {
    687      errorMessage: /Error: err-d/,
    688      errorMessageName: "",
    689      sourceName: /test_page_errors/,
    690      category: "content javascript",
    691      timeStamp: FRACTIONAL_NUMBER_REGEX,
    692      error: true,
    693      warning: false,
    694      info: false,
    695      lineNumber: NUMBER_REGEX,
    696      columnNumber: NUMBER_REGEX,
    697      innerWindowID: NUMBER_REGEX,
    698      private: false,
    699      stacktrace: [
    700        {
    701          filename: /test_page_errors\.html/,
    702          lineNumber: 4,
    703          columnNumber: 14,
    704          functionName: null,
    705        },
    706      ],
    707      exception: {
    708        preview: {
    709          cause: {
    710            class: "Error",
    711            preview: {
    712              message: "err-c",
    713              cause: {
    714                class: "Error",
    715                preview: {
    716                  message: "err-b",
    717                  cause: {
    718                    class: "Error",
    719                    preview: {
    720                      message: "err-a",
    721                    },
    722                  },
    723                },
    724              },
    725            },
    726          },
    727        },
    728      },
    729      notes: null,
    730      chromeContext: false,
    731      isPromiseRejection: false,
    732      isForwardedFromContentProcess: false,
    733    },
    734  ],
    735  [
    736    // Error with a null cause
    737    `throw new Error("something went wrong", { cause: null })`,
    738    {
    739      errorMessage: /Error: something went wrong/,
    740      errorMessageName: "",
    741      sourceName: /test_page_errors/,
    742      category: "content javascript",
    743      timeStamp: FRACTIONAL_NUMBER_REGEX,
    744      error: true,
    745      warning: false,
    746      info: false,
    747      lineNumber: NUMBER_REGEX,
    748      columnNumber: NUMBER_REGEX,
    749      innerWindowID: NUMBER_REGEX,
    750      private: false,
    751      stacktrace: [
    752        {
    753          filename: /test_page_errors\.html/,
    754          lineNumber: 1,
    755          columnNumber: 7,
    756          functionName: null,
    757        },
    758      ],
    759      exception: {
    760        preview: {
    761          cause: {
    762            type: "null",
    763          },
    764        },
    765      },
    766      notes: null,
    767      chromeContext: false,
    768      isPromiseRejection: false,
    769      isForwardedFromContentProcess: false,
    770    },
    771  ],
    772  [
    773    // Error with an undefined cause
    774    `throw new Error("something went wrong", { cause: undefined })`,
    775    {
    776      errorMessage: /Error: something went wrong/,
    777      errorMessageName: "",
    778      sourceName: /test_page_errors/,
    779      category: "content javascript",
    780      timeStamp: FRACTIONAL_NUMBER_REGEX,
    781      error: true,
    782      warning: false,
    783      info: false,
    784      lineNumber: NUMBER_REGEX,
    785      columnNumber: NUMBER_REGEX,
    786      innerWindowID: NUMBER_REGEX,
    787      private: false,
    788      stacktrace: [
    789        {
    790          filename: /test_page_errors\.html/,
    791          lineNumber: 1,
    792          columnNumber: 7,
    793          functionName: null,
    794        },
    795      ],
    796      exception: {
    797        preview: {
    798          cause: {
    799            type: "undefined",
    800          },
    801        },
    802      },
    803      notes: null,
    804      chromeContext: false,
    805      isPromiseRejection: false,
    806      isForwardedFromContentProcess: false,
    807    },
    808  ],
    809  [
    810    // Error with a number cause
    811    `throw new Error("something went wrong", { cause: 0 })`,
    812    {
    813      errorMessage: /Error: something went wrong/,
    814      errorMessageName: "",
    815      sourceName: /test_page_errors/,
    816      category: "content javascript",
    817      timeStamp: FRACTIONAL_NUMBER_REGEX,
    818      error: true,
    819      warning: false,
    820      info: false,
    821      lineNumber: NUMBER_REGEX,
    822      columnNumber: NUMBER_REGEX,
    823      innerWindowID: NUMBER_REGEX,
    824      private: false,
    825      stacktrace: [
    826        {
    827          filename: /test_page_errors\.html/,
    828          lineNumber: 1,
    829          columnNumber: 7,
    830          functionName: null,
    831        },
    832      ],
    833      exception: {
    834        preview: {
    835          cause: 0,
    836        },
    837      },
    838      notes: null,
    839      chromeContext: false,
    840      isPromiseRejection: false,
    841      isForwardedFromContentProcess: false,
    842    },
    843  ],
    844  [
    845    // Error with a string cause
    846    `throw new Error("something went wrong", { cause: "ooops" })`,
    847    {
    848      errorMessage: /Error: something went wrong/,
    849      errorMessageName: "",
    850      sourceName: /test_page_errors/,
    851      category: "content javascript",
    852      timeStamp: FRACTIONAL_NUMBER_REGEX,
    853      error: true,
    854      warning: false,
    855      info: false,
    856      lineNumber: NUMBER_REGEX,
    857      columnNumber: NUMBER_REGEX,
    858      innerWindowID: NUMBER_REGEX,
    859      private: false,
    860      stacktrace: [
    861        {
    862          filename: /test_page_errors\.html/,
    863          lineNumber: 1,
    864          columnNumber: 7,
    865          functionName: null,
    866        },
    867      ],
    868      exception: {
    869        preview: {
    870          cause: "ooops",
    871        },
    872      },
    873      notes: null,
    874      chromeContext: false,
    875      isPromiseRejection: false,
    876      isForwardedFromContentProcess: false,
    877    },
    878  ],
    879 ]);