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