mirror of
https://github.com/nodejs/node.git
synced 2026-10-14 10:26:26 -04:00
test_runner: add mock-timers support for AbortSignal.timeout
PR-URL: https://github.com/nodejs/node/pull/60751 Fixes: https://github.com/nodejs/node/issues/60509 Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Stefan Stojanovic <stefan.stojanovic@janeasystems.com> Reviewed-By: René <contact.9a5d6388@renegade334.me.uk> Reviewed-By: Jacob Smith <jacob@frende.me>
This commit is contained in:
1 parent
045febcecc
commit
d1ac02f841
2 files changed
+70
-5
No files matched your search
@@ -23,6 +23,7 @@ const {
|
||||
validateAbortSignal,
|
||||
validateNumber,
|
||||
validateStringArray,
|
||||
validateUint32,
|
||||
} = require('internal/validators');
|
||||
|
||||
const {
|
||||
@@ -34,6 +35,7 @@ const {
|
||||
} = require('internal/errors');
|
||||
|
||||
const { addAbortListener } = require('internal/events/abort_listener');
|
||||
const { AbortController, AbortSignal } = require('internal/abort_controller');
|
||||
|
||||
const { TIMEOUT_MAX } = require('internal/timers');
|
||||
|
||||
@@ -60,9 +62,17 @@ function abortIt(signal) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @enum {('setTimeout'|'setInterval'|'setImmediate'|'Date'|'scheduler.wait')[]} Supported timers
|
||||
* @typedef {('setTimeout'|'setInterval'|'setImmediate'|'Date'|'scheduler.wait'|'AbortSignal.timeout')[]} SupportedApis
|
||||
* Supported timers that can be enabled via MockTimers.enable({ apis: [...] })
|
||||
*/
|
||||
const SUPPORTED_APIS = ['setTimeout', 'setInterval', 'setImmediate', 'Date', 'scheduler.wait'];
|
||||
const SUPPORTED_APIS = [
|
||||
'setTimeout',
|
||||
'setInterval',
|
||||
'setImmediate',
|
||||
'Date',
|
||||
'scheduler.wait',
|
||||
'AbortSignal.timeout',
|
||||
];
|
||||
const TIMERS_DEFAULT_INTERVAL = {
|
||||
__proto__: null,
|
||||
setImmediate: -1,
|
||||
@@ -115,6 +125,7 @@ class MockTimers {
|
||||
#realPromisifiedSetImmediate;
|
||||
|
||||
#nativeDateDescriptor;
|
||||
#realAbortSignalTimeout;
|
||||
|
||||
#timersInContext = [];
|
||||
#isEnabled = false;
|
||||
@@ -297,6 +308,14 @@ class MockTimers {
|
||||
);
|
||||
}
|
||||
|
||||
#storeOriginalAbortSignalTimeout() {
|
||||
this.#realAbortSignalTimeout = ObjectGetOwnPropertyDescriptor(AbortSignal, 'timeout');
|
||||
}
|
||||
|
||||
#restoreOriginalAbortSignalTimeout() {
|
||||
ObjectDefineProperty(AbortSignal, 'timeout', this.#realAbortSignalTimeout);
|
||||
}
|
||||
|
||||
#createTimer(isInterval, callback, delay, ...args) {
|
||||
if (delay > TIMEOUT_MAX) {
|
||||
delay = 1;
|
||||
@@ -604,6 +623,27 @@ class MockTimers {
|
||||
this.#nativeDateDescriptor = ObjectGetOwnPropertyDescriptor(globalThis, 'Date');
|
||||
globalThis.Date = this.#createDate();
|
||||
},
|
||||
'AbortSignal.timeout': () => {
|
||||
this.#storeOriginalAbortSignalTimeout();
|
||||
const mock = this;
|
||||
ObjectDefineProperty(AbortSignal, 'timeout', {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: function value(delay) {
|
||||
validateUint32(delay, 'delay', false);
|
||||
const controller = new AbortController();
|
||||
// Don't keep an unused binding to the timer; mock tick controls it
|
||||
mock.#setTimeout(
|
||||
() => {
|
||||
controller.abort();
|
||||
},
|
||||
delay,
|
||||
);
|
||||
return controller.signal;
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
toReal: {
|
||||
'__proto__': null,
|
||||
@@ -622,6 +662,9 @@ class MockTimers {
|
||||
'Date': () => {
|
||||
ObjectDefineProperty(globalThis, 'Date', this.#nativeDateDescriptor);
|
||||
},
|
||||
'AbortSignal.timeout': () => {
|
||||
this.#restoreOriginalAbortSignalTimeout();
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -664,10 +707,11 @@ class MockTimers {
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {{apis: SUPPORTED_APIS;now: number | Date;}} EnableOptions Options to enable the timers
|
||||
* @property {SUPPORTED_APIS} apis List of timers to enable, defaults to all
|
||||
* @typedef {{apis: SupportedApis;now: number | Date;}} EnableOptions Options to enable the timers
|
||||
* @property {SupportedApis} apis List of timers to enable, defaults to all
|
||||
* @property {number | Date} now The epoch to which the timers should be set to, defaults to 0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Enables the MockTimers replacing the native timers with the fake ones.
|
||||
* @param {EnableOptions} [options]
|
||||
@@ -686,8 +730,8 @@ class MockTimers {
|
||||
|
||||
internalOptions.apis ||= SUPPORTED_APIS;
|
||||
|
||||
validateStringArray(internalOptions.apis, 'options.apis');
|
||||
// Check that the timers passed are supported
|
||||
validateStringArray(internalOptions.apis, 'options.apis');
|
||||
ArrayPrototypeForEach(internalOptions.apis, (timer) => {
|
||||
if (!ArrayPrototypeIncludes(SUPPORTED_APIS, timer)) {
|
||||
throw new ERR_INVALID_ARG_VALUE(
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const { mock } = require('node:test');
|
||||
|
||||
const originalAbortSignalTimeout = AbortSignal.timeout;
|
||||
|
||||
mock.timers.enable({ apis: ['AbortSignal.timeout'] });
|
||||
|
||||
const signal = AbortSignal.timeout(50);
|
||||
assert.strictEqual(signal.aborted, false);
|
||||
|
||||
mock.timers.tick(49);
|
||||
assert.strictEqual(signal.aborted, false);
|
||||
|
||||
mock.timers.tick(1);
|
||||
assert.strictEqual(signal.aborted, true);
|
||||
|
||||
mock.timers.reset();
|
||||
assert.strictEqual(AbortSignal.timeout, originalAbortSignalTimeout);
|
||||
Reference in new issue
Block a user