Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/internal/test_runner/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ function stopTest(timeout, signal) {

disposeFunction = () => {
abortListener[SymbolDispose]();
timer[SymbolDispose]();
clearTimeout(timer);
};
}

Expand Down
43 changes: 43 additions & 0 deletions test/fixtures/test-runner/mock-timers-with-timeout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use strict';

// Simulate @sinonjs/fake-timers: patch the timers module BEFORE
// the test runner is loaded, so the test runner captures the patched
// versions at import time.
const nodeTimers = require('node:timers');
const originalSetTimeout = nodeTimers.setTimeout;
const originalClearTimeout = nodeTimers.clearTimeout;

const fakeTimers = new Map();
let nextId = 1;

nodeTimers.setTimeout = (fn, delay, ...args) => {
const id = nextId++;
const timer = originalSetTimeout(fn, delay, ...args);
fakeTimers.set(id, timer);
// Sinon fake timers return an object with unref/ref but without
// Symbol.dispose, which would cause the test runner to throw.
return { id, unref() {}, ref() {} };
};

nodeTimers.clearTimeout = (id) => {
if (id != null && typeof id === 'object') id = id.id;
const timer = fakeTimers.get(id);
if (timer) {
originalClearTimeout(timer);
fakeTimers.delete(id);
}
};

// Now load the test runner - it will capture our patched setTimeout/clearTimeout
const { test } = require('node:test');

test('test with fake timers and timeout', { timeout: 10_000 }, () => {
// This test verifies that the test runner works when setTimeout returns
// an object without Symbol.dispose (like sinon fake timers).
// Previously, the test runner called timer[Symbol.dispose]() which would
// throw TypeError on objects returned by fake timer implementations.
});

// Restore
nodeTimers.setTimeout = originalSetTimeout;
nodeTimers.clearTimeout = originalClearTimeout;
14 changes: 14 additions & 0 deletions test/parallel/test-runner-mock-timers-with-timeout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use strict';
require('../common');
const fixtures = require('../common/fixtures');
const assert = require('node:assert');
const { spawnSync } = require('node:child_process');
const { test } = require('node:test');

test('mock timers do not break test timeout cleanup', async () => {
const fixture = fixtures.path('test-runner', 'mock-timers-with-timeout.js');
const cp = spawnSync(process.execPath, ['--test', fixture], {
timeout: 30_000,
});
assert.strictEqual(cp.status, 0, `Test failed:\nstdout: ${cp.stdout}\nstderr: ${cp.stderr}`);
});
Loading