observable-switchMap.any.js (6909B)
1 test(() => { 2 const source = createTestSubject(); 3 const inner1 = createTestSubject(); 4 const inner2 = createTestSubject(); 5 6 const result = source.switchMap((value, index) => { 7 if (value === 1) { 8 return inner1; 9 } 10 if (value === 2) { 11 return inner2; 12 } 13 throw new Error("invalid "); 14 }); 15 16 const results = []; 17 18 result.subscribe({ 19 next: v => results.push(v), 20 error: e => results.push(e), 21 complete: () => results.push("complete"), 22 }); 23 24 assert_equals(source.subscriberCount(), 1, 25 "source observable is subscribed to"); 26 27 source.next(1); 28 assert_equals(inner1.subscriberCount(), 1, 29 "inner1 observable is subscribed to"); 30 31 inner1.next("1a"); 32 assert_array_equals(results, ["1a"]); 33 34 inner1.next("1b"); 35 assert_array_equals(results, ["1a", "1b"]); 36 37 source.next(2); 38 assert_equals(inner1.subscriberCount(), 0, 39 "inner1 observable is unsubscribed from"); 40 assert_equals(inner2.subscriberCount(), 1, 41 "inner2 observable is subscribed to"); 42 43 inner2.next("2a"); 44 assert_array_equals(results, ["1a", "1b", "2a"]); 45 46 inner2.next("2b"); 47 assert_array_equals(results, ["1a", "1b", "2a", "2b"]); 48 49 inner2.complete(); 50 assert_array_equals(results, ["1a", "1b", "2a", "2b"]); 51 52 source.complete(); 53 assert_array_equals(results, ["1a", "1b", "2a", "2b", "complete"]); 54 }, "switchMap(): result subscribes to one inner observable at a time, " + 55 "unsubscribing from the previous active one when a new one replaces it"); 56 57 test(() => { 58 const source = createTestSubject(); 59 const inner = createTestSubject(); 60 61 const result = source.switchMap(() => inner); 62 63 const results = []; 64 65 result.subscribe({ 66 next: v => results.push(v), 67 error: e => results.push(e), 68 complete: () => results.push("complete"), 69 }); 70 71 assert_equals(source.subscriberCount(), 1, 72 "source observable is subscribed to"); 73 assert_equals(inner.subscriberCount(), 0, 74 "inner observable is not subscribed to"); 75 76 source.next(1); 77 assert_equals(inner.subscriberCount(), 1, 78 "inner observable is subscribed to"); 79 80 inner.next("a"); 81 assert_array_equals(results, ["a"]); 82 83 inner.next("b"); 84 assert_array_equals(results, ["a", "b"]); 85 86 source.complete(); 87 assert_array_equals(results, ["a", "b"], 88 "Result observable does not complete when source observable completes, " + 89 "because inner is still active"); 90 91 inner.next("c"); 92 assert_array_equals(results, ["a", "b", "c"]); 93 94 inner.complete(); 95 assert_array_equals(results, ["a", "b", "c", "complete"], 96 "Result observable completes when inner observable completes, because " + 97 "source is already complete"); 98 }, "switchMap(): result does not complete when the source observable " + 99 "completes, if the inner observable is still active"); 100 101 test(() => { 102 const source = createTestSubject(); 103 104 const e = new Error('thrown from mapper'); 105 const result = source.switchMap(() => { 106 throw e; 107 }); 108 109 const results = []; 110 111 result.subscribe({ 112 next: v => results.push(v), 113 error: e => results.push(e), 114 complete: () => results.push("complete"), 115 }); 116 117 assert_equals(source.subscriberCount(), 1, 118 "source observable is subscribed to"); 119 120 source.next(1); 121 assert_array_equals(results, [e]); 122 assert_equals(source.subscriberCount(), 0, 123 "source observable is unsubscribed from"); 124 }, "switchMap(): result emits an error if Mapper callback throws an error"); 125 126 test(() => { 127 const source = createTestSubject(); 128 const inner = createTestSubject(); 129 130 const result = source.switchMap(() => inner); 131 132 const results = []; 133 134 result.subscribe({ 135 next: v => results.push(v), 136 error: e => results.push(e), 137 complete: () => results.push("complete"), 138 }); 139 140 source.next(1); 141 inner.next("a"); 142 assert_array_equals(results, ["a"]); 143 144 const e = new Error('error from source'); 145 source.error(e); 146 assert_array_equals(results, ["a", e], 147 "switchMap result emits an error if the source emits an error"); 148 assert_equals(inner.subscriberCount(), 0, 149 "inner observable is unsubscribed from"); 150 assert_equals(source.subscriberCount(), 0, 151 "source observable is unsubscribed from"); 152 }, "switchMap(): result emits an error if the source observable emits an " + 153 "error"); 154 155 test(() => { 156 const source = createTestSubject(); 157 const inner = createTestSubject(); 158 159 const result = source.switchMap(() => inner); 160 161 const results = []; 162 163 result.subscribe({ 164 next: v => results.push(v), 165 error: e => results.push(e), 166 complete: () => results.push("complete"), 167 }); 168 169 source.next(1); 170 inner.next("a"); 171 assert_array_equals(results, ["a"]); 172 173 const e = new Error("error from inner"); 174 inner.error(e); 175 assert_array_equals(results, ["a", e], 176 "result emits an error if the inner observable emits an error"); 177 assert_equals(inner.subscriberCount(), 0, 178 "inner observable is unsubscribed from"); 179 assert_equals(source.subscriberCount(), 0, 180 "source observable is unsubscribed from"); 181 }, "switchMap(): result emits an error if the inner observable emits an error"); 182 183 test(() => { 184 const results = []; 185 const source = new Observable(subscriber => { 186 subscriber.next(1); 187 subscriber.addTeardown(() => { 188 results.push('source teardown'); 189 }); 190 subscriber.signal.onabort = e => { 191 results.push('source onabort'); 192 }; 193 }); 194 195 const inner = new Observable(subscriber => { 196 subscriber.addTeardown(() => { 197 results.push('inner teardown'); 198 }); 199 subscriber.signal.onabort = () => { 200 results.push('inner onabort'); 201 }; 202 }); 203 204 const result = source.switchMap(() => inner); 205 206 const ac = new AbortController(); 207 result.subscribe({ 208 next: v => results.push(v), 209 error: e => results.error(e), 210 complete: () => results.complete("complete"), 211 }, {signal: ac.signal}); 212 213 ac.abort(); 214 assert_array_equals(results, [ 215 "source onabort", 216 "source teardown", 217 "inner onabort", 218 "inner teardown", 219 ], "Unsubscription order is correct"); 220 }, "switchMap(): should unsubscribe in the correct order when user aborts " + 221 "the subscription"); 222 223 // A helper function to create an Observable that can be externally controlled 224 // and examined for testing purposes. 225 function createTestSubject() { 226 const subscribers = new Set(); 227 const subject = new Observable(subscriber => { 228 subscribers.add(subscriber); 229 subscriber.addTeardown(() => subscribers.delete(subscriber)); 230 }); 231 232 subject.next = value => { 233 for (const subscriber of Array.from(subscribers)) { 234 subscriber.next(value); 235 } 236 }; 237 subject.error = error => { 238 for (const subscriber of Array.from(subscribers)) { 239 subscriber.error(error); 240 } 241 }; 242 subject.complete = () => { 243 for (const subscriber of Array.from(subscribers)) { 244 subscriber.complete(); 245 } 246 }; 247 subject.subscriberCount = () => { 248 return subscribers.size; 249 }; 250 251 return subject; 252 }