Skip to content

Commit 231f650

Browse files
committed
test_runner: add support for scheduler.wait on mock timers
This adds support for nodetimers.promises.scheduler.wait on Mocktimers Refs: #55244
1 parent cebf21d commit 231f650

File tree

2 files changed

+157
-12
lines changed

2 files changed

+157
-12
lines changed

lib/internal/test_runner/mock/mock_timers.js

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ function abortIt(signal) {
6262
}
6363

6464
/**
65-
* @enum {('setTimeout'|'setInterval'|'setImmediate'|'Date')[]} Supported timers
65+
* @enum {('setTimeout'|'setInterval'|'setImmediate'|'Date', 'scheduler.wait')[]} Supported timers
6666
*/
67-
const SUPPORTED_APIS = ['setTimeout', 'setInterval', 'setImmediate', 'Date'];
67+
const SUPPORTED_APIS = ['setTimeout', 'setInterval', 'setImmediate', 'Date', 'scheduler.wait'];
6868
const TIMERS_DEFAULT_INTERVAL = {
6969
__proto__: null,
7070
setImmediate: -1,
@@ -106,6 +106,7 @@ class MockTimers {
106106

107107
#realPromisifiedSetTimeout;
108108
#realPromisifiedSetInterval;
109+
#realTimersPromisifiedSchedulerWait;
109110

110111
#realTimersSetTimeout;
111112
#realTimersClearTimeout;
@@ -190,6 +191,13 @@ class MockTimers {
190191
);
191192
}
192193

194+
#restoreOriginalSchedulerWait() {
195+
nodeTimersPromises.scheduler.wait = FunctionPrototypeBind(
196+
this.#realTimersPromisifiedSchedulerWait,
197+
this,
198+
);
199+
}
200+
193201
#restoreOriginalSetTimeout() {
194202
ObjectDefineProperty(
195203
globalThis,
@@ -264,6 +272,14 @@ class MockTimers {
264272
);
265273
}
266274

275+
#storeOriginalSchedulerWait() {
276+
277+
this.#realTimersPromisifiedSchedulerWait = FunctionPrototypeBind(
278+
nodeTimersPromises.scheduler.wait,
279+
this,
280+
);
281+
}
282+
267283
#storeOriginalSetTimeout() {
268284
this.#realSetTimeout = ObjectGetOwnPropertyDescriptor(
269285
globalThis,
@@ -556,8 +572,14 @@ class MockTimers {
556572
const options = {
557573
__proto__: null,
558574
toFake: {
559-
__proto__: null,
560-
setTimeout: () => {
575+
'__proto__': null,
576+
'scheduler.wait': () => {
577+
this.#storeOriginalSchedulerWait();
578+
579+
nodeTimersPromises.scheduler.wait = (delay, options) =>
580+
this.#setTimeoutPromisified(delay, undefined, options);
581+
},
582+
'setTimeout': () => {
561583
this.#storeOriginalSetTimeout();
562584

563585
globalThis.setTimeout = this.#setTimeout;
@@ -571,7 +593,7 @@ class MockTimers {
571593
this,
572594
);
573595
},
574-
setInterval: () => {
596+
'setInterval': () => {
575597
this.#storeOriginalSetInterval();
576598

577599
globalThis.setInterval = this.#setInterval;
@@ -585,7 +607,7 @@ class MockTimers {
585607
this,
586608
);
587609
},
588-
setImmediate: () => {
610+
'setImmediate': () => {
589611
this.#storeOriginalSetImmediate();
590612

591613
// setImmediate functions needs to bind MockTimers
@@ -609,23 +631,26 @@ class MockTimers {
609631
this,
610632
);
611633
},
612-
Date: () => {
634+
'Date': () => {
613635
this.#nativeDateDescriptor = ObjectGetOwnPropertyDescriptor(globalThis, 'Date');
614636
globalThis.Date = this.#createDate();
615637
},
616638
},
617639
toReal: {
618-
__proto__: null,
619-
setTimeout: () => {
640+
'__proto__': null,
641+
'scheduler.wait': () => {
642+
this.#restoreOriginalSchedulerWait();
643+
},
644+
'setTimeout': () => {
620645
this.#restoreOriginalSetTimeout();
621646
},
622-
setInterval: () => {
647+
'setInterval': () => {
623648
this.#restoreOriginalSetInterval();
624649
},
625-
setImmediate: () => {
650+
'setImmediate': () => {
626651
this.#restoreSetImmediate();
627652
},
628-
Date: () => {
653+
'Date': () => {
629654
ObjectDefineProperty(globalThis, 'Date', this.#nativeDateDescriptor);
630655
},
631656
},

test/parallel/test-runner-mock-timers.js

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,126 @@ describe('Mock Timers Test Suite', () => {
775775
});
776776
});
777777

778+
describe('scheduler Suite', () => {
779+
describe('scheduler.wait', () => {
780+
it('should advance in time and trigger timers when calling the .tick function', (t) => {
781+
t.mock.timers.enable({ apis: ['scheduler.wait'] });
782+
783+
const now = Date.now();
784+
const durationAtMost = 100;
785+
786+
const p = nodeTimersPromises.scheduler.wait(4000);
787+
t.mock.timers.tick(4000);
788+
789+
return p.then(common.mustCall((result) => {
790+
assert.strictEqual(result, undefined);
791+
assert.ok(
792+
Date.now() - now < durationAtMost,
793+
`time should be advanced less than the ${durationAtMost}ms`
794+
);
795+
}));
796+
});
797+
798+
it('should advance in time and trigger timers when calling the .tick function multiple times', async (t) => {
799+
t.mock.timers.enable({ apis: ['scheduler.wait'] });
800+
801+
const fn = t.mock.fn();
802+
803+
nodeTimersPromises.scheduler.wait(9999).then(fn);
804+
805+
t.mock.timers.tick(8999);
806+
assert.strictEqual(fn.mock.callCount(), 0);
807+
t.mock.timers.tick(500);
808+
809+
await nodeTimersPromises.setImmediate();
810+
811+
assert.strictEqual(fn.mock.callCount(), 0);
812+
t.mock.timers.tick(500);
813+
814+
await nodeTimersPromises.setImmediate();
815+
assert.strictEqual(fn.mock.callCount(), 1);
816+
});
817+
818+
it('should work with the same params as the original timers/promises/scheduler.wait', async (t) => {
819+
t.mock.timers.enable({ apis: ['scheduler.wait'] });
820+
const controller = new AbortController();
821+
const p = nodeTimersPromises.scheduler.wait(2000, {
822+
ref: true,
823+
signal: controller.signal,
824+
});
825+
826+
t.mock.timers.tick(1000);
827+
t.mock.timers.tick(500);
828+
t.mock.timers.tick(500);
829+
t.mock.timers.tick(500);
830+
831+
const result = await p;
832+
assert.strictEqual(result, undefined);
833+
});
834+
835+
it('should abort operation if timers/promises/scheduler.wait received an aborted signal', async (t) => {
836+
t.mock.timers.enable({ apis: ['scheduler.wait'] });
837+
const controller = new AbortController();
838+
const p = nodeTimersPromises.scheduler.wait(2000, {
839+
ref: true,
840+
signal: controller.signal,
841+
});
842+
843+
t.mock.timers.tick(1000);
844+
controller.abort();
845+
t.mock.timers.tick(500);
846+
t.mock.timers.tick(500);
847+
t.mock.timers.tick(500);
848+
849+
await assert.rejects(() => p, {
850+
name: 'AbortError',
851+
});
852+
});
853+
it('should abort operation even if the .tick was not called', async (t) => {
854+
t.mock.timers.enable({ apis: ['scheduler.wait'] });
855+
const controller = new AbortController();
856+
const p = nodeTimersPromises.scheduler.wait(2000, {
857+
ref: true,
858+
signal: controller.signal,
859+
});
860+
861+
controller.abort();
862+
863+
await assert.rejects(() => p, {
864+
name: 'AbortError',
865+
});
866+
});
867+
868+
it('should abort operation when .abort is called before calling setInterval', async (t) => {
869+
t.mock.timers.enable({ apis: ['scheduler.wait'] });
870+
const controller = new AbortController();
871+
controller.abort();
872+
const p = nodeTimersPromises.scheduler.wait(2000, {
873+
ref: true,
874+
signal: controller.signal,
875+
});
876+
877+
await assert.rejects(() => p, {
878+
name: 'AbortError',
879+
});
880+
});
881+
882+
it('should reject given an an invalid signal instance', async (t) => {
883+
t.mock.timers.enable({ apis: ['scheduler.wait'] });
884+
const p = nodeTimersPromises.scheduler.wait(2000, {
885+
ref: true,
886+
signal: {},
887+
});
888+
889+
await assert.rejects(() => p, {
890+
name: 'TypeError',
891+
code: 'ERR_INVALID_ARG_TYPE',
892+
});
893+
});
894+
895+
});
896+
});
897+
778898
describe('Date Suite', () => {
779899
it('should return the initial UNIX epoch if not specified', (t) => {
780900
t.mock.timers.enable({ apis: ['Date'] });

0 commit comments

Comments
 (0)