88 * @format
99 */
1010
11- import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment' ;
12-
11+ import DOMException from '../../../errors/DOMException' ;
1312import { AbortController } from '../AbortController' ;
1413import { AbortSignal } from '../AbortSignal' ;
1514import Event from 'react-native/src/private/webapis/dom/events/Event' ;
@@ -58,6 +57,7 @@ describe('AbortController', () => {
5857
5958 it ( 'should not be callable' , ( ) => {
6059 expect ( ( ) => {
60+ // $FlowExpectedError[prop-missing]
6161 // $FlowExpectedError[constructor-as-function]
6262 AbortController ( ) ;
6363 } ) . toThrow ( TypeError ) ;
@@ -75,6 +75,7 @@ describe('AbortController', () => {
7575 } ) ;
7676
7777 it ( 'should be stringified as [object AbortController]' , ( ) => {
78+ // $FlowExpectedError[method-unbinding]
7879 expect ( Object . prototype . toString . call ( controller ) ) . toBe (
7980 '[object AbortController]' ,
8081 ) ;
@@ -103,11 +104,9 @@ describe('AbortController', () => {
103104 const keys = new Set ( [
104105 'aborted' ,
105106 'onabort' ,
106- // TODO
107- // 'reason',
107+ 'reason' ,
108+ // TODO: The modern class syntax was specifically designed to prevent this, ensuring methods don't "pollute" standard loops.
108109 // 'throwIfAborted',
109- // 'when',
110- // TODO: Problem with EventTarget: the modern class syntax was specifically designed to prevent this, ensuring methods don't "pollute" standard loops.
111110 // 'addEventListener',
112111 // 'dispatchEvent',
113112 // 'removeEventListener',
@@ -125,7 +124,12 @@ describe('AbortController', () => {
125124 expect ( signal . aborted ) . toBe ( false ) ;
126125 } ) ;
127126
127+ it ( "should have 'reason' property which is undefined by default" , ( ) => {
128+ expect ( signal . reason ) . toBe ( undefined ) ;
129+ } ) ;
130+
128131 it ( "should have 'onabort' property which is null by default" , ( ) => {
132+ // $FlowExpectedError[prop-missing]
129133 expect ( signal . onabort ) . toBe ( null ) ;
130134 } ) ;
131135
@@ -144,6 +148,7 @@ describe('AbortController', () => {
144148 } ) ;
145149
146150 it ( 'should be stringified as [object AbortSignal]' , ( ) => {
151+ // $FlowExpectedError[method-unbinding]
147152 expect ( Object . prototype . toString . call ( signal ) ) . toBe (
148153 '[object AbortSignal]' ,
149154 ) ;
@@ -156,6 +161,45 @@ describe('AbortController', () => {
156161 expect ( controller . signal . aborted ) . toBe ( true ) ;
157162 } ) ;
158163
164+ it ( "should set default 'reason' when called without an argument" , ( ) => {
165+ controller . abort ( ) ;
166+
167+ expect ( controller . signal . reason ) . toBeInstanceOf ( DOMException ) ;
168+ expect ( controller . signal . reason ) . toMatchObject ( {
169+ name : 'AbortError' ,
170+ message : 'signal is aborted without reason' ,
171+ } ) ;
172+ } ) ;
173+
174+ it ( "should set the provided 'reason' when called with an argument" , ( ) => {
175+ const reason = new Error ( 'boom' ) ;
176+
177+ controller . abort ( reason ) ;
178+
179+ expect ( controller . signal . reason ) . toBe ( reason ) ;
180+ } ) ;
181+
182+ it ( "should make 'throwIfAborted' throw the abort reason" , ( ) => {
183+ const reason = { message : 'boom' } ;
184+
185+ controller . abort ( reason ) ;
186+
187+ let thrown ;
188+ try {
189+ controller . signal . throwIfAborted ( ) ;
190+ } catch ( error ) {
191+ thrown = error ;
192+ }
193+
194+ expect ( thrown ) . toBe ( reason ) ;
195+ } ) ;
196+
197+ it ( "should not throw from 'throwIfAborted' before aborting" , ( ) => {
198+ expect ( ( ) => {
199+ controller . signal . throwIfAborted ( ) ;
200+ } ) . not . toThrow ( ) ;
201+ } ) ;
202+
159203 it ( "should fire 'abort' event on 'signal' (addEventListener)" , ( ) => {
160204 const listener = createListener ( ) ;
161205 controller . signal . addEventListener ( 'abort' , listener ) ;
@@ -167,6 +211,7 @@ describe('AbortController', () => {
167211 it ( "should fire 'abort' event on 'signal' (onabort)" , ( ) => {
168212 const listener = createListener ( ) ;
169213 // $FlowExpectedError[incompatible-type]
214+ // $FlowExpectedError[prop-missing]
170215 controller . signal . onabort = listener ;
171216 controller . abort ( ) ;
172217
@@ -186,15 +231,79 @@ describe('AbortController', () => {
186231
187232 it ( "should throw a TypeError if 'this' is not an AbortController object" , ( ) => {
188233 expect ( ( ) => {
234+ // $FlowExpectedError[method-unbinding]
189235 controller . abort . call ( { } ) ;
190236 } ) . toThrow ( TypeError ) ;
191237 } ) ;
192238 } ) ;
239+
240+ describe ( "'any' static method" , ( ) => {
241+ it ( "should abort when one of the provided signals aborts" , ( ) => {
242+ const first = new AbortController ( ) ;
243+ const second = new AbortController ( ) ;
244+ const reason = new Error ( 'stop' ) ;
245+
246+ const signal = AbortSignal . any ( [ first . signal , second . signal ] ) ;
247+
248+ expect ( signal ) . toBeInstanceOf ( AbortSignal ) ;
249+ expect ( signal . aborted ) . toBe ( false ) ;
250+
251+ second . abort ( reason ) ;
252+
253+ expect ( signal . aborted ) . toBe ( true ) ;
254+ expect ( signal . reason ) . toBe ( reason ) ;
255+ } ) ;
256+
257+ it ( "should abort immediately if one of the provided signals is already aborted" , ( ) => {
258+ const first = new AbortController ( ) ;
259+ const second = new AbortController ( ) ;
260+ const reason = new Error ( 'already aborted' ) ;
261+
262+ second . abort ( reason ) ;
263+
264+ const signal = AbortSignal . any ( [ first . signal , second . signal ] ) ;
265+
266+ expect ( signal . aborted ) . toBe ( true ) ;
267+ expect ( signal . reason ) . toBe ( reason ) ;
268+ } ) ;
269+ } ) ;
270+
271+ describe ( "'timeout' static method" , ( ) => {
272+ beforeEach ( ( ) => {
273+ jest . useFakeTimers ( ) ;
274+ } ) ;
275+
276+ afterEach ( ( ) => {
277+ jest . useRealTimers ( ) ;
278+ } ) ;
279+
280+ it ( "should abort after the timeout with a TimeoutError reason" , ( ) => {
281+ const signal = AbortSignal . timeout ( 10 ) ;
282+
283+ expect ( signal . aborted ) . toBe ( false ) ;
284+
285+ jest . advanceTimersByTime ( 10 ) ;
286+
287+ expect ( signal . aborted ) . toBe ( true ) ;
288+ expect ( signal . reason ) . toBeInstanceOf ( DOMException ) ;
289+ expect ( signal . reason ) . toMatchObject ( {
290+ name : 'TimeoutError' ,
291+ message : 'signal timed out' ,
292+ } ) ;
293+ } ) ;
294+
295+ it ( "should throw a TypeError for a negative timeout" , ( ) => {
296+ expect ( ( ) => {
297+ AbortSignal . timeout ( - 1 ) ;
298+ } ) . toThrow ( TypeError ) ;
299+ } ) ;
300+ } ) ;
193301} ) ;
194302
195303describe ( 'AbortSignal' , ( ) => {
196304 it ( 'should not be callable' , ( ) => {
197305 expect ( ( ) => {
306+ // $FlowExpectedError[prop-missing]
198307 // $FlowExpectedError[constructor-as-function]
199308 AbortSignal ( ) ;
200309 } ) . toThrow ( TypeError ) ;
0 commit comments