promise_test(async () => { const results = []; const source = new Observable(subscriber => { subscriber.addTeardown(() => results.push('teardown')); subscriber.next(1); results.push(subscriber.active ? 'active' : 'inactive'); results.push(subscriber.signal.aborted ? 'aborted' : 'not aborted') // Ignored. subscriber.next(2); subscriber.complete(); }); const value = await source.first(); assert_array_equals(results, ['teardown', 'inactive', 'aborted']); assert_equals(value, 1, "Promise resolves with the first value from the source Observable"); }, "first(): Promise resolves with the first value from the source Observable"); promise_test(async (t) => { const error = new Error("error from source"); const source = new Observable(subscriber => { subscriber.error(error); }); return promise_rejects_exactly(t, error, source.first(), "Promise rejects with source Observable error"); }, "first(): Promise rejects with the error emitted from the source Observable"); promise_test(async (t) => { const source = new Observable(subscriber => { subscriber.complete(); }); return promise_rejects_js(t, RangeError, source.first(), "Upon complete(), first() Promise rejects with RangeError"); }, "first(): Promise rejects with RangeError when source Observable " + "completes without emitting any values"); promise_test(async (t) => { const source = new Observable(subscriber => {}); const controller = new AbortController(); const promise = source.first({ signal: controller.signal }); controller.abort(); return promise_rejects_dom(t, "AbortError", promise, "Promise rejects with a DOMException for abortion"); }, "first(): Aborting a signal rejects the Promise with an AbortError DOMException"); promise_test(async () => { const results = []; const source = new Observable(subscriber => { results.push("source subscribe"); subscriber.addTeardown(() => results.push("source teardown")); subscriber.signal.addEventListener("abort", () => results.push("source abort")); results.push("before source next 1"); subscriber.next(1); results.push("after source next 1"); }); results.push("calling first"); const promise = source.first(); assert_array_equals(results, [ "calling first", "source subscribe", "before source next 1", "source abort", "source teardown", "after source next 1" ], "Array values after first() is called"); const firstValue = await promise; results.push(`first resolved with: ${firstValue}`); assert_array_equals(results, [ "calling first", "source subscribe", "before source next 1", "source abort", "source teardown", "after source next 1", "first resolved with: 1", ], "Array values after Promise is awaited"); }, "first(): Lifecycle");