test(() => {
const source = createTestSubject();
const inner1 = createTestSubject();
const inner2 = createTestSubject();
const result = source.switchMap((value, index) => {
if (value === 1) {
return inner1;
}
if (value === 2) {
return inner2;
}
throw new Error("invalid ");
});
const results = [];
result.subscribe({
next: v => results.push(v),
error: e => results.push(e),
complete: () => results.push("complete"),
});
assert_equals(source.subscriberCount(), 1,
"source observable is subscribed to");
source.next(1);
assert_equals(inner1.subscriberCount(), 1,
"inner1 observable is subscribed to");
inner1.next("1a");
assert_array_equals(results, ["1a"]);
inner1.next("1b");
assert_array_equals(results, ["1a", "1b"]);
source.next(2);
assert_equals(inner1.subscriberCount(), 0,
"inner1 observable is unsubscribed from");
assert_equals(inner2.subscriberCount(), 1,
"inner2 observable is subscribed to");
inner2.next("2a");
assert_array_equals(results, ["1a", "1b", "2a"]);
inner2.next("2b");
assert_array_equals(results, ["1a", "1b", "2a", "2b"]);
inner2.complete();
assert_array_equals(results, ["1a", "1b", "2a", "2b"]);
source.complete();
assert_array_equals(results, ["1a", "1b", "2a", "2b", "complete"]);
}, "switchMap(): result subscribes to one inner observable at a time, " +
"unsubscribing from the previous active one when a new one replaces it");
test(() => {
const source = createTestSubject();
const inner = createTestSubject();
const result = source.switchMap(() => inner);
const results = [];
result.subscribe({
next: v => results.push(v),
error: e => results.push(e),
complete: () => results.push("complete"),
});
assert_equals(source.subscriberCount(), 1,
"source observable is subscribed to");
assert_equals(inner.subscriberCount(), 0,
"inner observable is not subscribed to");
source.next(1);
assert_equals(inner.subscriberCount(), 1,
"inner observable is subscribed to");
inner.next("a");
assert_array_equals(results, ["a"]);
inner.next("b");
assert_array_equals(results, ["a", "b"]);
source.complete();
assert_array_equals(results, ["a", "b"],
"Result observable does not complete when source observable completes, " +
"because inner is still active");
inner.next("c");
assert_array_equals(results, ["a", "b", "c"]);
inner.complete();
assert_array_equals(results, ["a", "b", "c", "complete"],
"Result observable completes when inner observable completes, because " +
"source is already complete");
}, "switchMap(): result does not complete when the source observable " +
"completes, if the inner observable is still active");
test(() => {
const source = createTestSubject();
const e = new Error('thrown from mapper');
const result = source.switchMap(() => {
throw e;
});
const results = [];
result.subscribe({
next: v => results.push(v),
error: e => results.push(e),
complete: () => results.push("complete"),
});
assert_equals(source.subscriberCount(), 1,
"source observable is subscribed to");
source.next(1);
assert_array_equals(results, [e]);
assert_equals(source.subscriberCount(), 0,
"source observable is unsubscribed from");
}, "switchMap(): result emits an error if Mapper callback throws an error");
test(() => {
const source = createTestSubject();
const inner = createTestSubject();
const result = source.switchMap(() => inner);
const results = [];
result.subscribe({
next: v => results.push(v),
error: e => results.push(e),
complete: () => results.push("complete"),
});
source.next(1);
inner.next("a");
assert_array_equals(results, ["a"]);
const e = new Error('error from source');
source.error(e);
assert_array_equals(results, ["a", e],
"switchMap result emits an error if the source emits an error");
assert_equals(inner.subscriberCount(), 0,
"inner observable is unsubscribed from");
assert_equals(source.subscriberCount(), 0,
"source observable is unsubscribed from");
}, "switchMap(): result emits an error if the source observable emits an " +
"error");
test(() => {
const source = createTestSubject();
const inner = createTestSubject();
const result = source.switchMap(() => inner);
const results = [];
result.subscribe({
next: v => results.push(v),
error: e => results.push(e),
complete: () => results.push("complete"),
});
source.next(1);
inner.next("a");
assert_array_equals(results, ["a"]);
const e = new Error("error from inner");
inner.error(e);
assert_array_equals(results, ["a", e],
"result emits an error if the inner observable emits an error");
assert_equals(inner.subscriberCount(), 0,
"inner observable is unsubscribed from");
assert_equals(source.subscriberCount(), 0,
"source observable is unsubscribed from");
}, "switchMap(): result emits an error if the inner observable emits an error");
test(() => {
const results = [];
const source = new Observable(subscriber => {
subscriber.next(1);
subscriber.addTeardown(() => {
results.push('source teardown');
});
subscriber.signal.onabort = e => {
results.push('source onabort');
};
});
const inner = new Observable(subscriber => {
subscriber.addTeardown(() => {
results.push('inner teardown');
});
subscriber.signal.onabort = () => {
results.push('inner onabort');
};
});
const result = source.switchMap(() => inner);
const ac = new AbortController();
result.subscribe({
next: v => results.push(v),
error: e => results.error(e),
complete: () => results.complete("complete"),
}, {signal: ac.signal});
ac.abort();
assert_array_equals(results, [
"source onabort",
"source teardown",
"inner onabort",
"inner teardown",
], "Unsubscription order is correct");
}, "switchMap(): should unsubscribe in the correct order when user aborts " +
"the subscription");
// A helper function to create an Observable that can be externally controlled
// and examined for testing purposes.
function createTestSubject() {
const subscribers = new Set();
const subject = new Observable(subscriber => {
subscribers.add(subscriber);
subscriber.addTeardown(() => subscribers.delete(subscriber));
});
subject.next = value => {
for (const subscriber of Array.from(subscribers)) {
subscriber.next(value);
}
};
subject.error = error => {
for (const subscriber of Array.from(subscribers)) {
subscriber.error(error);
}
};
subject.complete = () => {
for (const subscriber of Array.from(subscribers)) {
subscriber.complete();
}
};
subject.subscriberCount = () => {
return subscribers.size;
};
return subject;
}