1// Tests for AbortSignal.any() and subclasses that don't use a controller. 2function abortSignalAnySignalOnlyTests(signalInterface) { 3 const desc = `${signalInterface.name}.any()` 4 5 test(t => { 6 const signal = signalInterface.any([]); 7 assert_false(signal.aborted); 8 }, `${desc} works with an empty array of signals`); 9} 10 11// Tests for AbortSignal.any() and subclasses that use a controller. 12function abortSignalAnyTests(signalInterface, controllerInterface) { 13 const suffix = `(using ${controllerInterface.name})`; 14 const desc = `${signalInterface.name}.any()`; 15 16 test(t => { 17 const controller = new controllerInterface(); 18 const signal = controller.signal; 19 const cloneSignal = signalInterface.any([signal]); 20 assert_false(cloneSignal.aborted); 21 assert_true("reason" in cloneSignal, "cloneSignal has reason property"); 22 assert_equals(cloneSignal.reason, undefined, 23 "cloneSignal.reason is initially undefined"); 24 assert_not_equals(signal, cloneSignal, 25 `${desc} returns a new signal.`); 26 27 let eventFired = false; 28 cloneSignal.onabort = t.step_func((e) => { 29 assert_equals(e.target, cloneSignal, 30 `The event target is the signal returned by ${desc}`); 31 eventFired = true; 32 }); 33 34 controller.abort("reason string"); 35 assert_true(signal.aborted); 36 assert_true(cloneSignal.aborted); 37 assert_true(eventFired); 38 assert_equals(cloneSignal.reason, "reason string", 39 `${desc} propagates the abort reason`); 40 }, `${desc} follows a single signal ${suffix}`); 41 42 test(t => { 43 for (let i = 0; i < 3; ++i) { 44 const controllers = []; 45 for (let j = 0; j < 3; ++j) { 46 controllers.push(new controllerInterface()); 47 } 48 const combinedSignal = signalInterface.any(controllers.map(c => c.signal)); 49 50 let eventFired = false; 51 combinedSignal.onabort = t.step_func((e) => { 52 assert_equals(e.target, combinedSignal, 53 `The event target is the signal returned by ${desc}`); 54 eventFired = true; 55 }); 56 57 controllers[i].abort(); 58 assert_true(eventFired); 59 assert_true(combinedSignal.aborted); 60 assert_true(combinedSignal.reason instanceof DOMException, 61 "signal.reason is a DOMException"); 62 assert_equals(combinedSignal.reason.name, "AbortError", 63 "signal.reason is a AbortError"); 64 } 65 }, `${desc} follows multiple signals ${suffix}`); 66 67 test(t => { 68 const controllers = []; 69 for (let i = 0; i < 3; ++i) { 70 controllers.push(new controllerInterface()); 71 } 72 controllers[1].abort("reason 1"); 73 controllers[2].abort("reason 2"); 74 75 const signal = signalInterface.any(controllers.map(c => c.signal)); 76 assert_true(signal.aborted); 77 assert_equals(signal.reason, "reason 1", 78 "The signal should be aborted with the first reason"); 79 }, `${desc} returns an aborted signal if passed an aborted signal ${suffix}`); 80 81 test(t => { 82 const controller = new controllerInterface(); 83 const signal = signalInterface.any([controller.signal, controller.signal]); 84 assert_false(signal.aborted); 85 controller.abort("reason"); 86 assert_true(signal.aborted); 87 assert_equals(signal.reason, "reason"); 88 }, `${desc} can be passed the same signal more than once ${suffix}`); 89 90 test(t => { 91 const controller1 = new controllerInterface(); 92 controller1.abort("reason 1"); 93 const controller2 = new controllerInterface(); 94 controller2.abort("reason 2"); 95 96 const signal = signalInterface.any([controller1.signal, controller2.signal, controller1.signal]); 97 assert_true(signal.aborted); 98 assert_equals(signal.reason, "reason 1"); 99 }, `${desc} uses the first instance of a duplicate signal ${suffix}`); 100 101 test(t => { 102 for (let i = 0; i < 3; ++i) { 103 const controllers = []; 104 for (let j = 0; j < 3; ++j) { 105 controllers.push(new controllerInterface()); 106 } 107 const combinedSignal1 = 108 signalInterface.any([controllers[0].signal, controllers[1].signal]); 109 const combinedSignal2 = 110 signalInterface.any([combinedSignal1, controllers[2].signal]); 111 112 let eventFired = false; 113 combinedSignal2.onabort = t.step_func((e) => { 114 eventFired = true; 115 }); 116 117 controllers[i].abort(); 118 assert_true(eventFired); 119 assert_true(combinedSignal2.aborted); 120 assert_true(combinedSignal2.reason instanceof DOMException, 121 "signal.reason is a DOMException"); 122 assert_equals(combinedSignal2.reason.name, "AbortError", 123 "signal.reason is a AbortError"); 124 } 125 }, `${desc} signals are composable ${suffix}`); 126 127 async_test(t => { 128 const controller = new controllerInterface(); 129 const timeoutSignal = AbortSignal.timeout(5); 130 131 const combinedSignal = signalInterface.any([controller.signal, timeoutSignal]); 132 133 combinedSignal.onabort = t.step_func_done(() => { 134 assert_true(combinedSignal.aborted); 135 assert_true(combinedSignal.reason instanceof DOMException, 136 "combinedSignal.reason is a DOMException"); 137 assert_equals(combinedSignal.reason.name, "TimeoutError", 138 "combinedSignal.reason is a TimeoutError"); 139 }); 140 }, `${desc} works with signals returned by AbortSignal.timeout() ${suffix}`); 141 142 test(t => { 143 const controller = new controllerInterface(); 144 let combined = signalInterface.any([controller.signal]); 145 combined = signalInterface.any([combined]); 146 combined = signalInterface.any([combined]); 147 combined = signalInterface.any([combined]); 148 149 let eventFired = false; 150 combined.onabort = () => { 151 eventFired = true; 152 } 153 154 assert_false(eventFired); 155 assert_false(combined.aborted); 156 157 controller.abort("the reason"); 158 159 assert_true(eventFired); 160 assert_true(combined.aborted); 161 assert_equals(combined.reason, "the reason"); 162 }, `${desc} works with intermediate signals ${suffix}`); 163 164 test(t => { 165 const controller = new controllerInterface(); 166 const signals = []; 167 // The first event should be dispatched on the originating signal. 168 signals.push(controller.signal); 169 // All dependents are linked to `controller.signal` (never to another 170 // composite signal), so this is the order events should fire. 171 signals.push(signalInterface.any([controller.signal])); 172 signals.push(signalInterface.any([controller.signal])); 173 signals.push(signalInterface.any([signals[0]])); 174 signals.push(signalInterface.any([signals[1]])); 175 176 let result = ""; 177 for (let i = 0; i < signals.length; i++) { 178 signals[i].addEventListener('abort', () => { 179 result += i; 180 }); 181 } 182 controller.abort(); 183 assert_equals(result, "01234"); 184 }, `Abort events for ${desc} signals fire in the right order ${suffix}`); 185} 186