feat: default timelines to a shared ambient clock, overridable per timeline#79
Merged
Conversation
…meline
In practice a source, the code under test, and an expectation always want
the same clock, so make sharing the default. Timelines now use an ambient
clock (`getDefaultClock`) unless given an explicit `{ clock }`. Swap the
ambient clock with `setDefaultClock` (e.g. a fake-timers-backed clock in a
test); it returns a restore handle to pair with teardown so the ambient
clock doesn't leak between tests.
Also advance a passed timer one frame at a time (like `dash`), so timelines
sharing a clock interleave in lock-step instead of the source jumping a
timer's whole duration synchronously and racing ahead of an expectation's
barrier.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Threading an explicit
{ clock }through everyTimeline.createproved to be the wrong default: in practice a source, the code under test, and an expectation always need the same clock, and forgetting to share it produced a class of confusing bugs (timer races, deadlocks). The fake-timers clock is already a process-global singleton anyway, so injection was fighting reality.What
getDefaultClock()returns a shared clock;Timelineuses it unless given an explicit{ clock }(the override is unchanged).setDefaultClock(clock)swaps it and returns a restore handle to pair with test teardown, so the ambient clock doesn't leak between tests.onPass. A passedTnnow advances one frame at a time (likedash) instead of jumping its whole duration in one synchronous step. This lets two timelines sharing a clock interleave in lock-step — fixing the "source races ahead of the expectation's barrier" failure on directsource → expectationtiming.Tests
tsc(src + test projects) clean, build clean,bun test→ 13 pass, including new coverage: timelines share the ambient clock by default, an explicit clock overrides it, andsetDefaultClockswaps/restores. ExistingstreamBridgeend-to-end proof still passes.Note
Behavioural change: the default clock is now shared rather than fresh-per-
Timeline. The{ clock }override restores per-timeline isolation where needed. Tests that assert absoluteclock.nowshould reset the ambient clock in abeforeEach(setDefaultClock(new Clock())), as this PR's own tests now do.🤖 Generated with Claude Code