◐ Shell
clean mode source ↗

test_runner: add mock-timers support for AbortSignal.timeout · nodejs/node@01a9552

@@ -23,6 +23,7 @@ const {

2323

validateAbortSignal,

2424

validateNumber,

2525

validateStringArray,

26+

validateUint32,

2627

} = require('internal/validators');

27282829

const {

@@ -34,6 +35,7 @@ const {

3435

} = require('internal/errors');

35363637

const { addAbortListener } = require('internal/events/abort_listener');

38+

const { AbortController, AbortSignal } = require('internal/abort_controller');

37393840

const { TIMEOUT_MAX } = require('internal/timers');

3941

@@ -60,9 +62,17 @@ function abortIt(signal) {

6062

}

61636264

/**

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+

];

6676

const TIMERS_DEFAULT_INTERVAL = {

6777

__proto__: null,

6878

setImmediate: -1,

@@ -115,6 +125,7 @@ class MockTimers {

115125

#realPromisifiedSetImmediate;

116126117127

#nativeDateDescriptor;

128+

#realAbortSignalTimeout;

118129119130

#timersInContext = [];

120131

#isEnabled = false;

@@ -297,6 +308,14 @@ class MockTimers {

297308

);

298309

}

299310311+

#storeOriginalAbortSignalTimeout() {

312+

this.#realAbortSignalTimeout = ObjectGetOwnPropertyDescriptor(AbortSignal, 'timeout');

313+

}

314+315+

#restoreOriginalAbortSignalTimeout() {

316+

ObjectDefineProperty(AbortSignal, 'timeout', this.#realAbortSignalTimeout);

317+

}

318+300319

#createTimer(isInterval, callback, delay, ...args) {

301320

if (delay > TIMEOUT_MAX) {

302321

delay = 1;

@@ -604,6 +623,27 @@ class MockTimers {

604623

this.#nativeDateDescriptor = ObjectGetOwnPropertyDescriptor(globalThis, 'Date');

605624

globalThis.Date = this.#createDate();

606625

},

626+

'AbortSignal.timeout': () => {

627+

this.#storeOriginalAbortSignalTimeout();

628+

const mock = this;

629+

ObjectDefineProperty(AbortSignal, 'timeout', {

630+

__proto__: null,

631+

configurable: true,

632+

writable: true,

633+

value: function value(delay) {

634+

validateUint32(delay, 'delay', false);

635+

const controller = new AbortController();

636+

// Don't keep an unused binding to the timer; mock tick controls it

637+

mock.#setTimeout(

638+

() => {

639+

controller.abort();

640+

},

641+

delay,

642+

);

643+

return controller.signal;

644+

},

645+

});

646+

},

607647

},

608648

toReal: {

609649

'__proto__': null,

@@ -622,6 +662,9 @@ class MockTimers {

622662

'Date': () => {

623663

ObjectDefineProperty(globalThis, 'Date', this.#nativeDateDescriptor);

624664

},

665+

'AbortSignal.timeout': () => {

666+

this.#restoreOriginalAbortSignalTimeout();

667+

},

625668

},

626669

};

627670

@@ -664,10 +707,11 @@ class MockTimers {

664707

}

665708666709

/**

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

710+

* @typedef {{apis: SupportedApis;now: number | Date;}} EnableOptions Options to enable the timers

711+

* @property {SupportedApis} apis List of timers to enable, defaults to all

669712

* @property {number | Date} now The epoch to which the timers should be set to, defaults to 0

670713

*/

714+671715

/**

672716

* Enables the MockTimers replacing the native timers with the fake ones.

673717

* @param {EnableOptions} [options]

@@ -686,8 +730,8 @@ class MockTimers {

686730687731

internalOptions.apis ||= SUPPORTED_APIS;

688732689-

validateStringArray(internalOptions.apis, 'options.apis');

690733

// Check that the timers passed are supported

734+

validateStringArray(internalOptions.apis, 'options.apis');

691735

ArrayPrototypeForEach(internalOptions.apis, (timer) => {

692736

if (!ArrayPrototypeIncludes(SUPPORTED_APIS, timer)) {

693737

throw new ERR_INVALID_ARG_VALUE(