@@ -23,6 +23,7 @@ const {
2323 validateAbortSignal,
2424 validateNumber,
2525 validateStringArray,
26+ validateUint32,
2627} = require ( 'internal/validators' ) ;
2728
2829const {
@@ -34,6 +35,7 @@ const {
3435} = require ( 'internal/errors' ) ;
3536
3637const { addAbortListener } = require ( 'internal/events/abort_listener' ) ;
38+ const { AbortController, AbortSignal } = require ( 'internal/abort_controller' ) ;
3739
3840const { TIMEOUT_MAX } = require ( 'internal/timers' ) ;
3941
@@ -60,9 +62,17 @@ function abortIt(signal) {
6062}
6163
6264/**
63- * @enum {('setTimeout'|'setInterval'|'setImmediate'|'Date'|'scheduler.wait')[]} Supported timers
65+ * @typedef {('setTimeout'|'setInterval'|'setImmediate'|'Date'|'scheduler.wait'|'AbortSignal.timeout')[] } SupportedApis
66+ * Supported timers that can be enabled via MockTimers.enable({ apis: [...] })
6467 */
65- const SUPPORTED_APIS = [ 'setTimeout' , 'setInterval' , 'setImmediate' , 'Date' , 'scheduler.wait' ] ;
68+ const SUPPORTED_APIS = [
69+ 'setTimeout' ,
70+ 'setInterval' ,
71+ 'setImmediate' ,
72+ 'Date' ,
73+ 'scheduler.wait' ,
74+ 'AbortSignal.timeout' ,
75+ ] ;
6676const TIMERS_DEFAULT_INTERVAL = {
6777 __proto__ : null ,
6878 setImmediate : - 1 ,
@@ -115,6 +125,7 @@ class MockTimers {
115125 #realPromisifiedSetImmediate;
116126
117127 #nativeDateDescriptor;
128+ #realAbortSignalTimeout;
118129
119130 #timersInContext = [ ] ;
120131 #isEnabled = false ;
@@ -297,6 +308,18 @@ class MockTimers {
297308 ) ;
298309 }
299310
311+ #storeOriginalAbortSignalTimeout( ) {
312+ this . #realAbortSignalTimeout = ObjectGetOwnPropertyDescriptor ( AbortSignal , 'timeout' ) ;
313+ }
314+
315+ #restoreOriginalAbortSignalTimeout( ) {
316+ if ( this . #realAbortSignalTimeout) {
317+ ObjectDefineProperty ( AbortSignal , 'timeout' , this . #realAbortSignalTimeout) ;
318+ } else {
319+ delete AbortSignal . timeout ;
320+ }
321+ }
322+
300323 #createTimer( isInterval , callback , delay , ...args ) {
301324 if ( delay > TIMEOUT_MAX ) {
302325 delay = 1 ;
@@ -604,6 +627,27 @@ class MockTimers {
604627 this . #nativeDateDescriptor = ObjectGetOwnPropertyDescriptor ( globalThis , 'Date' ) ;
605628 globalThis . Date = this . #createDate( ) ;
606629 } ,
630+ 'AbortSignal.timeout' : ( ) => {
631+ this . #storeOriginalAbortSignalTimeout( ) ;
632+ const mock = this ;
633+ ObjectDefineProperty ( AbortSignal , 'timeout' , {
634+ __proto__ : null ,
635+ configurable : true ,
636+ writable : true ,
637+ value : function value ( delay ) {
638+ validateUint32 ( delay , 'delay' , false ) ;
639+ const controller = new AbortController ( ) ;
640+ // Don't keep an unused binding to the timer; mock tick controls it
641+ mock . #setTimeout(
642+ ( ) => {
643+ controller . abort ( ) ;
644+ } ,
645+ delay ,
646+ ) ;
647+ return controller . signal ;
648+ } ,
649+ } ) ;
650+ } ,
607651 } ,
608652 toReal : {
609653 '__proto__' : null ,
@@ -622,6 +666,9 @@ class MockTimers {
622666 'Date' : ( ) => {
623667 ObjectDefineProperty ( globalThis , 'Date' , this . #nativeDateDescriptor) ;
624668 } ,
669+ 'AbortSignal.timeout' : ( ) => {
670+ this . #restoreOriginalAbortSignalTimeout( ) ;
671+ } ,
625672 } ,
626673 } ;
627674
@@ -664,10 +711,12 @@ class MockTimers {
664711 }
665712
666713 /**
667- * @typedef {{apis: SUPPORTED_APIS;now: number | Date;} } EnableOptions Options to enable the timers
668- * @property {SUPPORTED_APIS } apis List of timers to enable, defaults to all
669- * @property {number | Date } now The epoch to which the timers should be set to, defaults to 0
714+ * EnableOptions type:
715+ * @typedef {object } EnableOptions
716+ * @property {Array<string> } apis List of timers to enable, defaults to all
717+ * @property {number|Date } now The epoch to which the timers should be set, defaults to 0
670718 */
719+
671720 /**
672721 * Enables the MockTimers replacing the native timers with the fake ones.
673722 * @param {EnableOptions } [options]
@@ -686,8 +735,8 @@ class MockTimers {
686735
687736 internalOptions . apis ||= SUPPORTED_APIS ;
688737
689- validateStringArray ( internalOptions . apis , 'options.apis' ) ;
690738 // Check that the timers passed are supported
739+ validateStringArray ( internalOptions . apis , 'options.apis' ) ;
691740 ArrayPrototypeForEach ( internalOptions . apis , ( timer ) => {
692741 if ( ! ArrayPrototypeIncludes ( SUPPORTED_APIS , timer ) ) {
693742 throw new ERR_INVALID_ARG_VALUE (
0 commit comments