Skip to content

Commit 3fcca16

Browse files
ErickWendelmarco-ippolito
authored andcommitted
test_runner: add support for scheduler.wait on mock timers
This adds support for nodetimers.promises.scheduler.wait on Mocktimers Refs: #55244 PR-URL: #55244 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Chemi Atlow <chemi@atlow.co.il> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
1 parent 8c8de30 commit 3fcca16

File tree

2 files changed

+157
-12
lines changed

2 files changed

+157
-12
lines changed

lib/internal/test_runner/mock/mock_timers.js

+37-12
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@ function abortIt(signal) {
6060
}
6161

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

105105
#realPromisifiedSetTimeout;
106106
#realPromisifiedSetInterval;
107+
#realTimersPromisifiedSchedulerWait;
107108

108109
#realTimersSetTimeout;
109110
#realTimersClearTimeout;
@@ -188,6 +189,13 @@ class MockTimers {
188189
);
189190
}
190191

192+
#restoreOriginalSchedulerWait() {
193+
nodeTimersPromises.scheduler.wait = FunctionPrototypeBind(
194+
this.#realTimersPromisifiedSchedulerWait,
195+
this,
196+
);
197+
}
198+
191199
#restoreOriginalSetTimeout() {
192200
ObjectDefineProperty(
193201
globalThis,
@@ -262,6 +270,14 @@ class MockTimers {
262270
);
263271
}
264272

273+
#storeOriginalSchedulerWait() {
274+
275+
this.#realTimersPromisifiedSchedulerWait = FunctionPrototypeBind(
276+
nodeTimersPromises.scheduler.wait,
277+
this,
278+
);
279+
}
280+
265281
#storeOriginalSetTimeout() {
266282
this.#realSetTimeout = ObjectGetOwnPropertyDescriptor(
267283
globalThis,
@@ -558,8 +574,14 @@ class MockTimers {
558574
const options = {
559575
__proto__: null,
560576
toFake: {
561-
__proto__: null,
562-
setTimeout: () => {
577+
'__proto__': null,
578+
'scheduler.wait': () => {
579+
this.#storeOriginalSchedulerWait();
580+
581+
nodeTimersPromises.scheduler.wait = (delay, options) =>
582+
this.#setTimeoutPromisified(delay, undefined, options);
583+
},
584+
'setTimeout': () => {
563585
this.#storeOriginalSetTimeout();
564586

565587
globalThis.setTimeout = this.#setTimeout;
@@ -573,7 +595,7 @@ class MockTimers {
573595
this,
574596
);
575597
},
576-
setInterval: () => {
598+
'setInterval': () => {
577599
this.#storeOriginalSetInterval();
578600

579601
globalThis.setInterval = this.#setInterval;
@@ -587,7 +609,7 @@ class MockTimers {
587609
this,
588610
);
589611
},
590-
setImmediate: () => {
612+
'setImmediate': () => {
591613
this.#storeOriginalSetImmediate();
592614

593615
// setImmediate functions needs to bind MockTimers
@@ -611,23 +633,26 @@ class MockTimers {
611633
this,
612634
);
613635
},
614-
Date: () => {
636+
'Date': () => {
615637
this.#nativeDateDescriptor = ObjectGetOwnPropertyDescriptor(globalThis, 'Date');
616638
globalThis.Date = this.#createDate();
617639
},
618640
},
619641
toReal: {
620-
__proto__: null,
621-
setTimeout: () => {
642+
'__proto__': null,
643+
'scheduler.wait': () => {
644+
this.#restoreOriginalSchedulerWait();
645+
},
646+
'setTimeout': () => {
622647
this.#restoreOriginalSetTimeout();
623648
},
624-
setInterval: () => {
649+
'setInterval': () => {
625650
this.#restoreOriginalSetInterval();
626651
},
627-
setImmediate: () => {
652+
'setImmediate': () => {
628653
this.#restoreSetImmediate();
629654
},
630-
Date: () => {
655+
'Date': () => {
631656
ObjectDefineProperty(globalThis, 'Date', this.#nativeDateDescriptor);
632657
},
633658
},

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

+120
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,126 @@ describe('Mock Timers Test Suite', () => {
791791
});
792792
});
793793

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

0 commit comments

Comments
 (0)