Skip to content

Commit 44ac0e1

Browse files
mcollinaaduh95
authored andcommitted
test_runner: make it compatible with fake timers
Signed-off-by: Matteo Collina <hello@matteocollina.com> PR-URL: #59272 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Chemi Atlow <chemi@atlow.co.il> Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Aviv Keller <me@aviv.sh> Reviewed-By: Paolo Insogna <paolo@cowtech.it>
1 parent 198b6e0 commit 44ac0e1

3 files changed

Lines changed: 58 additions & 1 deletion

File tree

‎lib/internal/test_runner/test.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ function stopTest(timeout, signal) {
149149

150150
disposeFunction=()=>{
151151
abortListener[SymbolDispose]();
152-
timer[SymbolDispose]();
152+
clearTimeout(timer);
153153
};
154154
}
155155

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
'use strict';
2+
3+
// Simulate @sinonjs/fake-timers: patch the timers module BEFORE
4+
// the test runner is loaded, so the test runner captures the patched
5+
// versions at import time.
6+
constnodeTimers=require('node:timers');
7+
constoriginalSetTimeout=nodeTimers.setTimeout;
8+
constoriginalClearTimeout=nodeTimers.clearTimeout;
9+
10+
constfakeTimers=newMap();
11+
letnextId=1;
12+
13+
nodeTimers.setTimeout=(fn,delay, ...args)=>{
14+
constid=nextId++;
15+
consttimer=originalSetTimeout(fn,delay, ...args);
16+
fakeTimers.set(id,timer);
17+
// Sinon fake timers return an object with unref/ref but without
18+
// Symbol.dispose, which would cause the test runner to throw.
19+
return{ id,unref(){},ref(){}};
20+
};
21+
22+
nodeTimers.clearTimeout=(id)=>{
23+
if(id!=null&&typeofid==='object')id=id.id;
24+
consttimer=fakeTimers.get(id);
25+
if(timer){
26+
originalClearTimeout(timer);
27+
fakeTimers.delete(id);
28+
}
29+
};
30+
31+
// Now load the test runner - it will capture our patched setTimeout/clearTimeout
32+
const{ test }=require('node:test');
33+
34+
test('test with fake timers and timeout',{timeout: 10_000},()=>{
35+
// This test verifies that the test runner works when setTimeout returns
36+
// an object without Symbol.dispose (like sinon fake timers).
37+
// Previously, the test runner called timer[Symbol.dispose]() which would
38+
// throw TypeError on objects returned by fake timer implementations.
39+
});
40+
41+
// Restore
42+
nodeTimers.setTimeout=originalSetTimeout;
43+
nodeTimers.clearTimeout=originalClearTimeout;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
'use strict';
2+
require('../common');
3+
constfixtures=require('../common/fixtures');
4+
constassert=require('node:assert');
5+
const{ spawnSync }=require('node:child_process');
6+
const{ test }=require('node:test');
7+
8+
test('mock timers do not break test timeout cleanup',async()=>{
9+
constfixture=fixtures.path('test-runner','mock-timers-with-timeout.js');
10+
constcp=spawnSync(process.execPath,['--test',fixture],{
11+
timeout: 30_000,
12+
});
13+
assert.strictEqual(cp.status,0,`Test failed:\nstdout: ${cp.stdout}\nstderr: ${cp.stderr}`);
14+
});

0 commit comments

Comments
 (0)