tor-browser

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

observable-toArray.any.js (6537B)


      1 // Because we test that the global error handler is called at various times.
      2 setup({allow_uncaught_exception: true});
      3 
      4 promise_test(async () => {
      5  const observable = new Observable(subscriber => {
      6    subscriber.next(1);
      7    subscriber.next(2);
      8    subscriber.next(3);
      9    subscriber.complete();
     10  });
     11 
     12  const array = await observable.toArray();
     13  assert_array_equals(array, [1, 2, 3]);
     14 }, "toArray(): basic next/complete");
     15 
     16 promise_test(async t => {
     17  let errorReported = null;
     18  let innerSubscriber = null;
     19  self.addEventListener('error', e => errorReported = e, {once: true});
     20 
     21  const error = new Error("custom error");
     22  const observable = new Observable(subscriber => {
     23    innerSubscriber = subscriber;
     24    subscriber.error(error);
     25  });
     26 
     27  try {
     28    const array = await observable.toArray();
     29    assert_unreached("toArray() promise must not resolve");
     30  } catch (e) {
     31    assert_equals(e, error);
     32    assert_equals(errorReported, null);
     33 
     34    // Calls to `error()` after the subscription is closed still report the
     35    // exception.
     36    innerSubscriber.error(error);
     37    assert_not_equals(errorReported, null, "Exception was reported to global");
     38    assert_true(errorReported.message.includes("custom error"), "Error message matches");
     39    assert_greater_than(errorReported.lineno, 0, "Error lineno is greater than 0");
     40    assert_greater_than(errorReported.colno, 0, "Error lineno is greater than 0");
     41    assert_equals(errorReported.error, error, "Error object is equivalent");
     42  }
     43 }, "toArray(): first error() rejects promise; subsequent error()s report the exceptions");
     44 
     45 promise_test(async t => {
     46  let errorReported = null;
     47  let innerSubscriber = null;
     48  self.addEventListener('error', e => errorReported = e, {once: true});
     49 
     50  const error = new Error("custom error");
     51  const observable = new Observable(subscriber => {
     52    innerSubscriber = subscriber;
     53    subscriber.complete();
     54  });
     55 
     56  const array = await observable.toArray();
     57  assert_array_equals(array, []);
     58  assert_equals(errorReported, null);
     59 
     60  // Calls to `error()` after the subscription is closed still report the
     61  // exception.
     62  innerSubscriber.error(error);
     63  assert_not_equals(errorReported, null, "Exception was reported to global");
     64  assert_true(errorReported.message.includes("custom error"), "Error message matches");
     65  assert_greater_than(errorReported.lineno, 0, "Error lineno is greater than 0");
     66  assert_greater_than(errorReported.colno, 0, "Error lineno is greater than 0");
     67  assert_equals(errorReported.error, error, "Error object is equivalent");
     68 }, "toArray(): complete() resolves promise; subsequent error()s report the exceptions");
     69 
     70 promise_test(async () => {
     71  // This tracks whether `postSubscriptionPromise` has had its then handler run.
     72  // This helps us keep track of the timing/ordering of everything. Calling a
     73  // Promise-returning operator with an aborted signal must *immediately* reject
     74  // the returned Promise, which means code "awaiting" it should run before any
     75  // subsequent Promise resolution/rejection handlers are run.
     76  let postSubscriptionPromiseResolved = false;
     77  let subscriptionImmediatelyInactive = false;
     78 
     79  const observable = new Observable(subscriber => {
     80    const inactive = !subscriber.active;
     81    subscriptionImmediatelyInactive = inactive;
     82  });
     83 
     84  const rejectedPromise = observable.toArray({signal: AbortSignal.abort()})
     85  .then(() => {
     86    assert_unreached("Operator promise must not resolve its abort signal is " +
     87                     "rejected");
     88  }, () => {
     89    // See the documentation above. The rejection handler (i.e., this code) for
     90    // immediately-aborted operator Promises runs before any later-scheduled
     91    // Promise resolution/rejections.
     92    assert_false(postSubscriptionPromiseResolved,
     93        "Operator promise rejects before later promise");
     94  });
     95  const postSubscriptionPromise =
     96      Promise.resolve().then(() => postSubscriptionPromiseResolved = true);
     97 
     98  await rejectedPromise;
     99 }, "toArray(): Subscribing with an aborted signal returns an immediately " +
    100   "rejected promise");
    101 
    102 promise_test(async () => {
    103  let postSubscriptionPromiseResolved = false;
    104 
    105  const observable = new Observable(subscriber => {});
    106  const controller = new AbortController();
    107  const arrayPromise = observable.toArray({signal: controller.signal})
    108  .then(() => {
    109    assert_unreached("Operator promise must not resolve if its abort signal " +
    110    "is rejected");
    111  }, () => {
    112    assert_false(postSubscriptionPromiseResolved,
    113                 "controller.abort() synchronously rejects the operator " +
    114                 "Promise");
    115  });
    116 
    117  // This must synchronously reject `arrayPromise`, scheduling in the next
    118  // microtask.
    119  controller.abort();
    120  Promise.resolve().then(value => postSubscriptionPromiseResolved = true);
    121 
    122  await arrayPromise;
    123 }, "toArray(): Aborting the passed-in signal rejects the returned promise");
    124 
    125 // See https://github.com/WICG/observable/issues/96 for discussion about this.
    126 promise_test(async () => {
    127  const results = [];
    128 
    129  const observable = new Observable(subscriber => {
    130    results.push(`Subscribed. active: ${subscriber.active}`);
    131 
    132    subscriber.signal.addEventListener('abort', e => {
    133      results.push("Inner signal abort event");
    134      Promise.resolve("Inner signal Promise").then(value => results.push(value));
    135    });
    136 
    137    subscriber.addTeardown(() => {
    138      results.push("Teardown");
    139      Promise.resolve("Teardown Promise").then(value => results.push(value));
    140    });
    141  });
    142 
    143  const controller = new AbortController();
    144  controller.signal.addEventListener('abort', e => {
    145    results.push("Outer signal abort event");
    146    Promise.resolve("Outer signal Promise").then(value => results.push(value));
    147  });
    148 
    149  // Subscribe.
    150  observable.toArray({signal: controller.signal});
    151  controller.abort();
    152 
    153  assert_array_equals(results, [
    154    "Subscribed. active: true",
    155    "Inner signal abort event",
    156    "Teardown",
    157    "Outer signal abort event",
    158  ], "Events and teardowns are fired in the right ordered");
    159 
    160  // Everything microtask above should be queued up by now, so queue one more
    161  // final microtask that will run after all of the others, wait for it, and the
    162  // check `results` is right.
    163  await Promise.resolve();
    164  assert_array_equals(results, [
    165    "Subscribed. active: true",
    166    "Inner signal abort event",
    167    "Teardown",
    168    "Outer signal abort event",
    169    "Inner signal Promise",
    170    "Teardown Promise",
    171    "Outer signal Promise",
    172  ], "Promises resolve in the right order");
    173 }, "Operator Promise abort ordering");