test(rsvp): stop RSVPController tests leaking real timers into teardown (#4355)
rsvp-controller.test.ts calls controller.start() in ~20 tests but never stops
the controller. start() schedules a countdown (setInterval, 500ms x3) that then
schedules the recurring word-advance (setTimeout). On the real clock those fire
~1.5s later — after the test file's jsdom env is torn down — and
emitStateChange's dispatchEvent(new CustomEvent(...)) runs against a stale realm
and throws. Vitest reports it as an unhandled error and fails the whole run
intermittently on CI:
TypeError: Failed to execute 'dispatchEvent' on 'EventTarget': parameter 1 is
not of type 'Event'.
at RSVPController.advanceToNextWord -> emitStateChange (Timeout._onTimeout)
Fake only the timer functions (setTimeout/setInterval + their clears) for this
suite via vi.useFakeTimers({ toFake: [...] }); useRealTimers in afterEach
discards any still-pending fakes. The tests assert synchronously and never
advance playback, so faking the timers changes no assertion; Date/performance
stay real so CFI/position checks are unaffected. Test-only; production behaviour
is unchanged.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -48,6 +48,23 @@ function createMockView(primaryIndex: number, docs: Document[]): FoliateView {
|
||||
}
|
||||
|
||||
describe('RSVPController', () => {
|
||||
// start() schedules a countdown (setInterval) which then schedules the
|
||||
// recurring word-advance (setTimeout). These tests assert synchronously and
|
||||
// never stop the controller, so on the real clock those timers fire ~1.5s
|
||||
// later — after the test file's jsdom env has been torn down — and throw an
|
||||
// unhandled error from emitStateChange's dispatchEvent (a stale-realm
|
||||
// CustomEvent), failing the whole run intermittently on CI. Fake only the
|
||||
// timer functions so they can never fire on the real clock; useRealTimers in
|
||||
// afterEach discards any still-pending fakes. Date/performance stay real, so
|
||||
// the CFI/position assertions are unaffected.
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers({ toFake: ['setTimeout', 'setInterval', 'clearTimeout', 'clearInterval'] });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
describe('start()', () => {
|
||||
test('extracts words from primary spine document only', () => {
|
||||
const ch1Doc = makeDoc('Hello world');
|
||||
|
||||
Reference in New Issue
Block a user