• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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