tor-browser

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

observable-catch.any.js (8046B)


      1 test(() => {
      2  const source = new Observable(subscriber => {
      3    subscriber.next(1);
      4    subscriber.next(2);
      5    subscriber.next(3);
      6    subscriber.complete();
      7  });
      8 
      9  const caughtObservable = source.catch(() => {
     10    assert_unreached("catch() is not called");
     11  });
     12 
     13  const results = [];
     14 
     15  caughtObservable.subscribe({
     16    next: value => results.push(value),
     17    complete: () => results.push('complete')
     18  });
     19 
     20  assert_array_equals(results, [1, 2, 3, 'complete']);
     21 }, "catch(): Returns an Observable that is a pass-through for next()/complete()");
     22 
     23 test(() => {
     24  let sourceError = new Error("from the source");
     25  const source = new Observable(subscriber => {
     26    subscriber.next(1);
     27    subscriber.next(2);
     28    subscriber.error(sourceError);
     29  });
     30 
     31  const caughtObservable = source.catch(error => {
     32    assert_equals(error, sourceError);
     33    return new Observable(subscriber => {
     34      subscriber.next(3);
     35      subscriber.complete();
     36    });
     37  });
     38 
     39  const results = [];
     40 
     41  caughtObservable.subscribe({
     42    next: value => results.push(value),
     43    complete: () => results.push("complete"),
     44  });
     45 
     46  assert_array_equals(results, [1, 2, 3, 'complete']);
     47 }, "catch(): Handle errors from source and flatten to a new Observable");
     48 
     49 test(() => {
     50  const sourceError = new Error("from the source");
     51  const source = new Observable(subscriber => {
     52    subscriber.next(1);
     53    subscriber.next(2);
     54    subscriber.error(sourceError);
     55  });
     56 
     57  const catchCallbackError = new Error("from the catch callback");
     58  const caughtObservable = source.catch(error => {
     59    assert_equals(error, sourceError);
     60    throw catchCallbackError;
     61  });
     62 
     63  const results = [];
     64 
     65  caughtObservable.subscribe({
     66    next: value => results.push(value),
     67    error: error => {
     68      results.push(error);
     69    },
     70    complete: () => results.push('complete'),
     71  });
     72 
     73  assert_array_equals(results, [1, 2, catchCallbackError]);
     74 }, "catch(): Errors thrown in the catch() callback are sent to the consumer's error handler");
     75 
     76 test(() => {
     77  // A common use case is logging and keeping the stream alive.
     78  const source = new Observable(subscriber => {
     79    subscriber.next(1);
     80    subscriber.next(2);
     81    subscriber.next(3);
     82    subscriber.complete();
     83  });
     84 
     85  const flatteningError = new Error("from the flattening operation");
     86  function errorsOnTwo(value) {
     87    return new Observable(subscriber => {
     88      if (value === 2) {
     89        subscriber.error(flatteningError);
     90      } else {
     91        subscriber.next(value);
     92        subscriber.complete();
     93      }
     94    });
     95  }
     96 
     97  const results = [];
     98 
     99  source.flatMap(value => errorsOnTwo(value)
    100    .catch(error => {
    101      results.push(error);
    102      // This empty array converts to an Observable which automatically
    103      // completes.
    104      return [];
    105    })
    106  ).subscribe({
    107    next: value => results.push(value),
    108    complete: () => results.push("complete")
    109  });
    110 
    111  assert_array_equals(results, [1, flatteningError, 3, "complete"]);
    112 }, "catch(): CatchHandler can return an empty iterable");
    113 
    114 promise_test(async () => {
    115  const sourceError = new Error("from the source");
    116  const source = new Observable(subscriber => {
    117    subscriber.next(1);
    118    subscriber.next(2);
    119    subscriber.error(sourceError);
    120  });
    121 
    122  const caughtObservable = source.catch(error => {
    123    assert_equals(error, sourceError);
    124    return Promise.resolve(error.message);
    125  });
    126 
    127  const results = await caughtObservable.toArray();
    128 
    129  assert_array_equals(results, [1, 2, "from the source"]);
    130 }, "catch(): CatchHandler can return a Promise");
    131 
    132 promise_test(async () => {
    133  const source = new Observable(subscriber => {
    134    subscriber.next(1);
    135    subscriber.next(2);
    136    subscriber.error(new Error('from the source'));
    137  });
    138 
    139  const caughtObservable = source.catch(async function* (error) {
    140    assert_true(error instanceof Error);
    141    assert_equals(error.message, 'from the source');
    142    yield 3;
    143  });
    144 
    145  const results = await caughtObservable.toArray();
    146 
    147  assert_array_equals(results, [1, 2, 3], 'catch(): should handle returning an observable');
    148 }, 'catch(): should handle returning an async iterable');
    149 
    150 test(() => {
    151  const sourceError = new Error("from the source");
    152  const source = new Observable(subscriber => {
    153    subscriber.next(1);
    154    subscriber.next(2);
    155    subscriber.error(sourceError);
    156  });
    157 
    158  const caughtObservable = source.catch(error => {
    159    assert_equals(error, sourceError);
    160    // Primitive values like this are not convertible to an Observable, via the
    161    // `from()` semantics.
    162    return 3;
    163  });
    164 
    165  const results = [];
    166 
    167  caughtObservable.subscribe({
    168    next: value => results.push(value),
    169    error: error => {
    170      assert_true(error instanceof TypeError);
    171      results.push("TypeError");
    172    },
    173    complete: () => results.push("complete"),
    174  });
    175 
    176  assert_array_equals(results, [1, 2, "TypeError"]);
    177 }, "catch(): CatchHandler emits an error if the value returned is not " +
    178   "convertible to an Observable");
    179 
    180 test(() => {
    181  const source = new Observable(subscriber => {
    182    susbcriber.error(new Error("from the source"));
    183  });
    184 
    185  const results = [];
    186 
    187  const innerSubscriptionError = new Error("CatchHandler subscription error");
    188  const catchObservable = source.catch(() => {
    189    results.push('CatchHandler invoked');
    190    return new Observable(subscriber => {
    191      throw innerSubscriptionError;
    192    });
    193  });
    194 
    195  catchObservable.subscribe({
    196    error: e => {
    197      results.push(e);
    198    }
    199  });
    200 
    201  assert_array_equals(results, ['CatchHandler invoked', innerSubscriptionError]);
    202 }, "catch(): CatchHandler returns an Observable that throws immediately on " +
    203   "subscription");
    204 
    205 // This test asserts that the relationship between (a) the AbortSignal passed
    206 // into `subscribe()` and (b) the AbortSignal associated with the Observable
    207 // returned from `catch()`'s CatchHandler is not a "dependent" relationship.
    208 // This is important because Observables have moved away from the "dependent
    209 // abort signal" infrastructure in https://github.com/WICG/observable/pull/154,
    210 // and this test asserts so.
    211 //
    212 // Here are all of the associated Observables and signals in this test:
    213 // 1. Raw outer signal passed into `subscribe()`
    214 // 2. catchObservable's inner Subscriber's signal
    215 //    a. Per the above PR, and Subscriber's initialization logic [1], this
    216 //       signal is set to abort in response to (1)'s abort algorithms. This
    217 //       means its "abort" event gets fired before (1)'s.
    218 // 3. Inner CatchHandler-returned Observable's Subscriber's signal
    219 //    a. Also per [1], this is set to abort in response to (2)'s abort
    220 //       algorithms, since we subscribe to this "inner Observable" with (2)'s
    221 //       signal as the `SubscribeOptions#signal`.
    222 //
    223 // (1), (2), and (3) above all form an abort chain:
    224 // (1) --> (2) --> (3)
    225 //
    226 // …such that when (1) aborts, its abort algorithms immediately abort (2),
    227 // whose abort algorithms immediately abort (3). Finally on the way back up the
    228 // chain, (3)'s `abort` event is fired, (2)'s `abort` event is fired, and then
    229 // (1)'s `abort` event is fired. This ordering of abort events is what this test
    230 // ensures.
    231 //
    232 // [1]: https://wicg.github.io/observable/#ref-for-abortsignal-add
    233 test(() => {
    234  const results = [];
    235  const source = new Observable(subscriber =>
    236      susbcriber.error(new Error("from the source")));
    237 
    238  const catchObservable = source.catch(() => {
    239    return new Observable(subscriber => {
    240      subscriber.addTeardown(() => results.push('inner teardown'));
    241      subscriber.signal.addEventListener('abort',
    242          e => results.push('inner signal abort'));
    243 
    244      // No values or completion. We'll just wait for the subscriber to abort
    245      // its subscription.
    246    });
    247  });
    248 
    249  const ac = new AbortController();
    250  ac.signal.addEventListener('abort', e => results.push('outer signal abort'));
    251  catchObservable.subscribe({}, {signal: ac.signal});
    252  ac.abort();
    253 
    254  assert_array_equals(results, ['inner signal abort', 'inner teardown', 'outer signal abort']);
    255 }, "catch(): Abort order between outer AbortSignal and inner CatchHandler subscriber's AbortSignal");