Skip to content

Commit 69ee56a

Browse files
ErickWendeladuh95
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 22d7777 commit 69ee56a

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
@@ -64,9 +64,9 @@ function abortIt(signal) {
6464
}
6565

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

109109
#realPromisifiedSetTimeout;
110110
#realPromisifiedSetInterval;
111+
#realTimersPromisifiedSchedulerWait;
111112

112113
#realTimersSetTimeout;
113114
#realTimersClearTimeout;
@@ -192,6 +193,13 @@ class MockTimers {
192193
);
193194
}
194195

196+
#restoreOriginalSchedulerWait() {
197+
nodeTimersPromises.scheduler.wait = FunctionPrototypeBind(
198+
this.#realTimersPromisifiedSchedulerWait,
199+
this,
200+
);
201+
}
202+
195203
#restoreOriginalSetTimeout() {
196204
ObjectDefineProperty(
197205
globalThis,
@@ -266,6 +274,14 @@ class MockTimers {
266274
);
267275
}
268276

277+
#storeOriginalSchedulerWait() {
278+
279+
this.#realTimersPromisifiedSchedulerWait = FunctionPrototypeBind(
280+
nodeTimersPromises.scheduler.wait,
281+
this,
282+
);
283+
}
284+
269285
#storeOriginalSetTimeout() {
270286
this.#realSetTimeout = ObjectGetOwnPropertyDescriptor(
271287
globalThis,
@@ -562,8 +578,14 @@ class MockTimers {
562578
const options = {
563579
__proto__: null,
564580
toFake: {
565-
__proto__: null,
566-
setTimeout: () => {
581+
'__proto__': null,
582+
'scheduler.wait': () => {
583+
this.#storeOriginalSchedulerWait();
584+
585+
nodeTimersPromises.scheduler.wait = (delay, options) =>
586+
this.#setTimeoutPromisified(delay, undefined, options);
587+
},
588+
'setTimeout': () => {
567589
this.#storeOriginalSetTimeout();
568590

569591
globalThis.setTimeout = this.#setTimeout;
@@ -577,7 +599,7 @@ class MockTimers {
577599
this,
578600
);
579601
},
580-
setInterval: () => {
602+
'setInterval': () => {
581603
this.#storeOriginalSetInterval();
582604

583605
globalThis.setInterval = this.#setInterval;
@@ -591,7 +613,7 @@ class MockTimers {
591613
this,
592614
);
593615
},
594-
setImmediate: () => {
616+
'setImmediate': () => {
595617
this.#storeOriginalSetImmediate();
596618

597619
// setImmediate functions needs to bind MockTimers
@@ -615,23 +637,26 @@ class MockTimers {
615637
this,
616638
);
617639
},
618-
Date: () => {
640+
'Date': () => {
619641
this.#nativeDateDescriptor = ObjectGetOwnPropertyDescriptor(globalThis, 'Date');
620642
globalThis.Date = this.#createDate();
621643
},
622644
},
623645
toReal: {
624-
__proto__: null,
625-
setTimeout: () => {
646+
'__proto__': null,
647+
'scheduler.wait': () => {
648+
this.#restoreOriginalSchedulerWait();
649+
},
650+
'setTimeout': () => {
626651
this.#restoreOriginalSetTimeout();
627652
},
628-
setInterval: () => {
653+
'setInterval': () => {
629654
this.#restoreOriginalSetInterval();
630655
},
631-
setImmediate: () => {
656+
'setImmediate': () => {
632657
this.#restoreSetImmediate();
633658
},
634-
Date: () => {
659+
'Date': () => {
635660
ObjectDefineProperty(globalThis, 'Date', this.#nativeDateDescriptor);
636661
},
637662
},

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)