observable-map.any.js (5165B)
1 test(() => { 2 const results = []; 3 const indices = []; 4 const source = new Observable((subscriber) => { 5 subscriber.next(1); 6 subscriber.next(2); 7 subscriber.next(3); 8 subscriber.complete(); 9 }); 10 11 const mapped = source.map((value, i) => { 12 indices.push(i); 13 return value * 2; 14 }); 15 16 assert_true(mapped instanceof Observable, "map() returns an Observable"); 17 18 assert_array_equals(results, [], "Does not map until subscribed (values)"); 19 assert_array_equals(indices, [], "Does not map until subscribed (indices)"); 20 21 mapped.subscribe({ 22 next: (value) => results.push(value), 23 error: () => results.push('error'), 24 complete: () => results.push('complete'), 25 }); 26 27 assert_array_equals(results, [2, 4, 6, 'complete']); 28 assert_array_equals(indices, [0, 1, 2]); 29 }, "map(): Maps values correctly"); 30 31 test(() => { 32 const error = new Error("error"); 33 const results = []; 34 let teardownCalled = false; 35 36 const source = new Observable((subscriber) => { 37 subscriber.addTeardown(() => teardownCalled = true); 38 39 subscriber.next(1); 40 assert_false(teardownCalled, 41 "Teardown not called until until map unsubscribes due to error"); 42 subscriber.next(2); 43 assert_true(teardownCalled, "Teardown called once map unsubscribes due to error"); 44 assert_false(subscriber.active, "Unsubscription makes Subscriber inactive"); 45 subscriber.next(3); 46 subscriber.complete(); 47 }); 48 49 const mapped = source.map((value) => { 50 if (value === 2) { 51 throw error; 52 } 53 return value * 2; 54 }); 55 56 mapped.subscribe({ 57 next: (value) => results.push(value), 58 error: (error) => results.push(error), 59 complete: () => results.push("complete"), 60 }); 61 62 assert_array_equals(results, [2, error], 63 "Mapper errors are emitted to Observer error() handler"); 64 }, "map(): Mapper errors are emitted to Observer error() handler"); 65 66 test(() => { 67 const source = new Observable(subscriber => { 68 subscriber.next(1); 69 subscriber.complete(); 70 subscriber.next(2); 71 }); 72 73 let mapperCalls = 0; 74 const results = []; 75 source.map(v => { 76 mapperCalls++; 77 return v * 2; 78 }).subscribe({ 79 next: v => results.push(v), 80 error: e => results.push(e), 81 complete: () => results.push('complete'), 82 }); 83 84 assert_equals(mapperCalls, 1, "Mapper is not called after complete()"); 85 assert_array_equals(results, [2, "complete"]); 86 }, "map(): Passes complete() through from source Observable"); 87 88 test(() => { 89 const source = new Observable(subscriber => { 90 subscriber.next(1); 91 subscriber.error('error'); 92 subscriber.next(2); 93 }); 94 95 let mapperCalls = 0; 96 const results = []; 97 source.map(v => { 98 mapperCalls++; 99 return v * 2; 100 }).subscribe({ 101 next: v => results.push(v), 102 error: e => results.push(e), 103 complete: () => results.push('complete'), 104 }); 105 106 assert_equals(mapperCalls, 1, "Mapper is not called after error()"); 107 assert_array_equals(results, [2, "error"]); 108 }, "map(): Passes error() through from source Observable"); 109 110 // This is mostly ensuring that the ordering in 111 // https://wicg.github.io/observable/#dom-subscriber-complete is consistent. 112 // 113 // That is, the `Subscriber#complete()` method *first* closes itself and signals 114 // abort on its own `Subscriber#signal()` and *then* calls whatever supplied 115 // completion algorithm exists. In the case of `map()`, the "supplied completion 116 // algorithm" is simply a set of internal observer steps that call 117 // `Subscriber#complete()` on the *outer* mapper's Observer. This means the 118 // outer Observer is notified of completion *after* the source Subscriber's 119 // signal is aborted / torn down. 120 test(() => { 121 const results = []; 122 const source = new Observable(subscriber => { 123 subscriber.addTeardown(() => results.push('source teardown')); 124 subscriber.signal.addEventListener('abort', 125 () => results.push('source abort event')); 126 127 subscriber.complete(); 128 }); 129 130 source.map(() => results.push('mapper called')).subscribe({ 131 complete: () => results.push('map observable complete'), 132 }); 133 134 assert_array_equals(results, 135 ['source abort event', 'source teardown', 'map observable complete']); 136 }, "map(): Upon source completion, source Observable teardown sequence " + 137 "happens before downstream mapper complete() is called"); 138 139 test(() => { 140 const results = []; 141 let sourceSubscriber = null; 142 const source = new Observable(subscriber => { 143 subscriber.addTeardown(() => results.push('source teardown')); 144 sourceSubscriber = subscriber; 145 146 subscriber.next(1); 147 }); 148 149 const controller = new AbortController(); 150 source.map(v => v * 2).subscribe({ 151 next: v => { 152 results.push(v); 153 154 // Triggers unsubscription to `source`. 155 controller.abort(); 156 157 // Does nothing, since `source` is already torn down. 158 sourceSubscriber.next(100); 159 }, 160 complete: () => results.push('mapper complete'), 161 error: e => results.push('mapper error'), 162 }, {signal: controller.signal}); 163 164 assert_array_equals(results, [2, 'source teardown']); 165 }, "map(): Map observable unsubscription causes source Observable " + 166 "unsubscription. Mapper Observer's complete()/error() are not called");