Skip to content

refactor(v4): finish moving the specs onto the shipped test helpers - #866

Merged
titouanmathis merged 8 commits into
mainfrom
refactor/v4-finish-test-helper-sweep
Aug 24, 2026
Merged

refactor(v4): finish moving the specs onto the shipped test helpers#866
titouanmathis merged 8 commits into
mainfrom
refactor/v4-finish-test-helper-sweep

Conversation

@titouanmathis

Copy link
Copy Markdown
Contributor

Finishes the move onto the shipped ./test helpers, so that every remaining hand-rolled instance is a decision rather than an oversight.

getInstance takes a nullable element

getInstance<T>(el: Element | null, name: string): T | undefined. The body was always el[INSTANCES]?.get(name), so it already tolerated null — the narrow parameter only forced callers to assert. "No element" and "no instance" both honestly answer undefined.

That removed 29 dead non-null assertions on querySelector() arguments. The ~202 ! on results stay: T | undefined is the correct return type, and stripping it at a call site that just mounted the component is a real claim about a real thing.

Converted

Was Now
hand-rolled js-toolkit:diagnostic listeners in 12 spec files captureDiagnostics()
raw INSTANCES symbol reads in swap.spec.ts (6) and Base.spec.ts (1) getInstance()
local poll helpers in Toaster.spec.ts, Toast.spec.ts waitFor()
local recorders in Timer.spec.ts, TimerProgress.spec.ts thin adapters over recordEvents()
positive settle-loops waitFor()
a local mount(html) in FetchShopifyPartial.spec.ts shadowing the shipped mount rebuilt on the shipped helper, renamed

Deliberately kept, with reasons

These are the exemptions, not the leftovers.

Raw INSTANCES access — 3 files.

  • Base.spec.ts:1332[INSTANCES]?.set(...). A write. No accessor expresses it.
  • instances.spec.ts — asserts el[INSTANCES] is undefined, i.e. the map was never created. getInstance() returning undefined is the weaker claim that the map exists with no entry, so the raw read is the right tool in the module's own spec.
  • coexistence.spec.ts — exists to test v3 and v4 sharing a document through Symbol.for, and reads [...keys()]. The symbol is the subject.

console spies — 5 sites.

  • Action.spec.ts:61,74 — the action's expression is literally console.log(ctx). The spy is the assertion target.
  • DataDerived.spec.ts:97,124 and DataBind.spec.ts:298 — assert a console.error reached the sink.
  • diagnostics.spec.ts, negotiated-events.spec.ts, Base.spec.ts ($warn/$error) — assert about the sink: does cancelling suppress it, does the shorthand reach it. captureDiagnostics() cancels every event, which would make each of these vacuous.

Base.spec.ts's own diagnostic recorder — captures targets: EventTarget[], i.e. which element the diagnostic started on. captureDiagnostics() takes a single target and does not expose that.

events.spec.ts — asserts the channel's event name, not what travels on it.

The 9 remaining settle-loops, renamed to quiet(). These back negative assertions — "nothing loaded", "nothing mounted yet". They cannot become waitFor, because polling for an absence passes before anything has happened. This is the asymmetry recorded in REPORT.md:900 and in waitFor's own doc comment. The rename replaces four names (observed, settled, included, ready) with one that says what the helper is for, and each carries a comment saying why it is not a poll.

The ~47 local render() helpers — they build component-specific markup, which no shared helper can do. Only the identical DOM-plus-settle copies were ever duplication, and those are gone.

mutation.spec.ts's recordFor() and attribute-namespaces.spec.ts's recorder() — neither records DOM events. One builds a MutationRecord fixture; the other collects qualifier callbacks. Name collision only.

Verification

Command Result
packages/v4 npm run test 114 files, 1573 tests passed
packages/v4 npm run lint:types exit 0
packages/v4 npm run check:diagnostics sinks centralized, references tree-shakeable
packages/v4 npm run build 263 modules, done
packages/v4 npm run check:package 594 files, 299.5 kB packed; node, TypeScript and browser consumers passed
root npm run subpaths:check both packages up to date
root npm run lint:fmt clean, 979 files

Test count unchanged at 1573 — this PR moves specs onto shipped helpers, it does not add or remove coverage.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NnepYhqjPMcFCSb43PoHRM

titouanmathis and others added 8 commits August 24, 2026 17:47
`getInstance()` read `el[INSTANCES]?.get(name)`, a body that already
tolerated a missing element, behind an `el: Element` parameter that did
not. Every caller holding a `querySelector()` result had to assert the
element away with a `!` — 58 of them across the specs — to state
something the function never needed.

The parameter is now `Element | null`. "No element" and "no instance"
are the same answer to the caller, and `undefined` says it for both.
The return stays `T | undefined`: an absent element is a fact the caller
may reasonably not know, an absent instance is one it must handle.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NnepYhqjPMcFCSb43PoHRM
`INSTANCES` is documented as not public API, so a spec spelling
`el[INSTANCES]?.get(name)` was reaching past the lookup written for it.
Every such read across ten spec files now calls `getInstance()`, which
the widened parameter makes ergonomic on a `querySelector()` result.
Three local `instanceOf` / `instance` wrappers went with them, and
`.has()` reads became `toBeUndefined()` on the lookup.

Three uses stay and now say why:

- `Base.spec.ts` writes with `.set()` to file one instance under a
  second name; `getInstance()` only reads.
- `instances.spec.ts` reads the map to state that none exists, which is
  the premise its assertions rest on — and testing these exports with
  themselves would prove nothing.
- `coexistence.spec.ts` has the symbol key itself under test, against
  v3's `__base__`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NnepYhqjPMcFCSb43PoHRM
Fourteen spec files attached their own listener to the diagnostic
channel, each re-deriving the same three lines — push the detail, cancel
the event, remember to detach. `captureDiagnostics()` from `./test` is
that, written once, so they now call it.

Three real defects went with the duplication:

- `attribute-namespaces.spec.ts` never removed its listener, so it
  leaked into every later test in the file.
- `swap.spec.ts` had a local helper named `captureDiagnostics` which did
  *not* cancel, so its warnings reached the console during the run.
- `FigureVideoTwicpics.spec.ts` waited six settle rounds for a
  diagnostic that `waitFor` now polls for.

`scheduler.spec.ts` also drops three `{ once: true }` listeners, which
turns "the first diagnostic was this one" into "exactly this one
arrived".

Left hand-rolled, and now saying so in the file:

- `diagnostics.spec.ts` owns the channel. Six of its seven listeners
  assert cancellation itself, the order against the console sink, the
  target a dispatch started on, or detail mutation — all of which
  `captureDiagnostics()` either performs or hides. The seventh only
  counts, and converted.
- `mount-strategies.spec.ts` asserts `{ target: el, defaultPrevented }`
  on the event object, which the helper does not hand back.
- `autoload.spec.ts` asserts the same diagnostic reaching an element,
  `document` and `window`, which needs three listeners.
- `Base.spec.ts` asserts which element a diagnostic started on.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NnepYhqjPMcFCSb43PoHRM
Nine more spec files kept a raw listener even though they already
imported `captureDiagnostics()` for their other tests. Each converted
site only collected details, so the helper is a straight replacement.

Two of them shadowed the shipped helper with a local function of the
same name — `storage/createStorage.spec.ts` and
`storage/providers.spec.ts` — which is the one thing a reader cannot
resolve at a glance. Both are now `catchDiagnostics()`, built on the
shipped capture and named for what they add: a bounded run.
`negotiated-events.spec.ts`, `services/service.spec.ts` and
`services/mutation.spec.ts` gained the same shape.

Every listener left raw asserts something the helper does not hand
back — the event's `target`, its `defaultPrevented`, or three targets
at once for a propagation test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NnepYhqjPMcFCSb43PoHRM
Three specs re-implemented the poll: `Toaster.spec.ts` and
`Toast.spec.ts` each carried a `waitFor` with the same signature as the
shipped one, and `viewTransition.spec.ts` spun 50 microtasks in
`waitForBatch`. All three now call `waitFor()`, which advances the
framework's own scheduler between attempts instead of spinning.

`services/pointer.spec.ts` also had a local `frames()` shadowing the
shipped one, differing only in its default count. The one call site
relying on that default now names the count it needs.

`FetchShopifyPartial.spec.ts` defined a local `async mount(html)`
shadowing the shipped `mount`, so a reader could not tell which one a
call resolved to. It is now `mountWithInstance()`, built on `mount()`
and named for what it adds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NnepYhqjPMcFCSb43PoHRM
`TimerProgress.spec.ts` was the one remaining hand-rolled event
recorder: a listener pushing `detail.ratio` into an array. It now
projects `recordEvents()`, the way `Timer.spec.ts` already did.

The other four recorders in the audit stay, and each has a reason:

- `attribute-namespaces.spec.ts` records binder callbacks, not events.
- `services/mutation.spec.ts` records service props off `subscribe()`,
  also not events.
- `Base.spec.ts` records the element each diagnostic *started on*, which
  `recordEvents()` and `captureDiagnostics()` both drop.
- `Timer.spec.ts` already wraps `recordEvents()`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NnepYhqjPMcFCSb43PoHRM
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NnepYhqjPMcFCSb43PoHRM
`getInstance` takes `Element | null` now, so narrowing a `querySelector()`
result before passing it asserts something the signature already allows.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NnepYhqjPMcFCSb43PoHRM
@github-actions

Copy link
Copy Markdown

Export size

Bundled per export with peer dependencies left external, dynamic imports excluded and the output minified; sizes are gzipped.

@studiometa/js-toolkit-v4

Export Size (gzip) Diff
(barrel) 22.73 kB +2 B (+0.0%)
getInstance 131 B +2 B (+1.6%)
Unchanged (397)

@studiometa/js-toolkit

Export Size (gzip) Diff
(barrel) 17.48 kB
AbstractService 598 B
Base 9.1 kB
ComponentLoader 2.31 kB
DEFAULT_DIAGNOSTIC_PREFIX 102 B
DragService 2.02 kB
IDLE_TIMEOUT 57 B
KeyService 935 B
LoadService 666 B
MutationService 849 B
PointerService 1.13 kB
RafService 1020 B
ResizeService 1.12 kB
ScrollService 1.36 kB
VISIBLE_ROOT_MARGIN 72 B
autoload 2.4 kB
closestComponent 419 B
composeManifests 119 B
createApp 996 B
defineFeatures 326 B
defineManifest 512 B
fromMetaGlob 228 B
fromWebpackContext 131 B
getClosestParent 197 B
getDirectChildren 202 B
getInstanceFromElement 125 B
getInstances 187 B
getScopedGroups 104 B
importOnInteraction 926 B
importOnMediaQuery 243 B
importWhenIdle 225 B
importWhenPrefersMotion 271 B
importWhenVisible 935 B
isDirectChild 218 B
logTree 551 B
queryComponent 594 B
queryComponentAll 601 B
readEagerTokens 201 B
registerComponent 305 B
registerComponents 356 B
registerManifest 2.87 kB
registerManifests 2.89 kB
useDrag 2.05 kB
useKey 943 B
useLoad 676 B
useMutation 876 B
usePointer 1.15 kB
useRaf 1 kB
useResize 1.13 kB
useScroll 1.36 kB
utils 10.08 kB
utils/Queue 291 B
utils/SmartQueue 473 B
utils/addClass 240 B
utils/addStyle 239 B
utils/animate 3.34 kB
utils/boundingRectToCircle 206 B
utils/cache 208 B
utils/camelCase 405 B
utils/clamp 98 B
utils/clamp01 114 B
utils/collideCircleCircle 129 B
utils/collideCircleRect 192 B
utils/collidePointCircle 128 B
utils/collidePointRect 122 B
utils/collideRectRect 128 B
utils/createEaseInOut 123 B
utils/createEaseOut 91 B
utils/createElement 635 B
utils/createLocalStorage 1.32 kB
utils/createLocalStorageProvider 296 B
utils/createMemoryStorageProvider 174 B
utils/createNoopProvider 128 B
utils/createRange 115 B
utils/createSessionStorage 1.32 kB
utils/createSessionStorageProvider 288 B
utils/createStorage 1.3 kB
utils/createUrlSearchParamsInHashProvider 461 B
utils/createUrlSearchParamsInHashStorage 1.35 kB
utils/createUrlSearchParamsProvider 429 B
utils/createUrlSearchParamsStorage 1.34 kB
utils/damp 106 B
utils/dashCase 404 B
utils/debounce 122 B
utils/domScheduler 310 B
utils/ease 519 B
utils/easeInCirc 285 B
utils/easeInCubic 287 B
utils/easeInExpo 286 B
utils/easeInOutCirc 288 B
utils/easeInOutCubic 289 B
utils/easeInOutExpo 288 B
utils/easeInOutQuad 288 B
utils/easeInOutQuart 289 B
utils/easeInOutQuint 289 B
utils/easeInOutSine 288 B
utils/easeInQuad 285 B
utils/easeInQuart 286 B
utils/easeInQuint 286 B
utils/easeInSine 285 B
utils/easeLinear 77 B
utils/easeOutCirc 286 B
utils/easeOutCubic 288 B
utils/easeOutExpo 286 B
utils/easeOutQuad 286 B
utils/easeOutQuart 286 B
utils/easeOutQuint 286 B
utils/easeOutSine 286 B
utils/endsWith 128 B
utils/fold 168 B
utils/getAncestorWhere 123 B
utils/getAncestorWhereUntil 148 B
utils/getComponentResolver 140 B
utils/getOffsetSizes 194 B
utils/hasWindow 88 B
utils/historyPush 524 B
utils/historyReplace 526 B
utils/inertiaFinalValue 169 B
utils/isArray 63 B
utils/isBoolean 78 B
utils/isDefined 75 B
utils/isDev 78 B
utils/isEmpty 206 B
utils/isEmptyString 108 B
utils/isFunction 79 B
utils/isNull 68 B
utils/isNumber 91 B
utils/isObject 108 B
utils/isString 77 B
utils/keyCodes 122 B
utils/lerp 84 B
utils/loadElement 220 B
utils/loadIframe 241 B
utils/loadImage 241 B
utils/loadLink 237 B
utils/loadScript 251 B
utils/localStorageProvider 839 B
utils/lowerCase 404 B
utils/map 93 B
utils/matrix 136 B
utils/mean 126 B
utils/memo 130 B
utils/memoize 228 B
utils/memoryStorageProvider 843 B
utils/nextFrame 179 B
utils/nextMicrotask 133 B
utils/nextTick 148 B
utils/noop 62 B
utils/noopValue 76 B
utils/objectToURLSearchParams 322 B
utils/pascalCase 407 B
utils/random 93 B
utils/randomInt 113 B
utils/randomItem 234 B
utils/removeClass 242 B
utils/removeStyle 243 B
utils/round 95 B
utils/saveActiveElement 92 B
utils/scrollTo 2.31 kB
utils/sessionStorageProvider 838 B
utils/smoothTo 476 B
utils/snakeCase 406 B
utils/spring 154 B
utils/startsWith 125 B
utils/throttle 125 B
utils/toggleClass 242 B
utils/transform 347 B
utils/transition 1010 B
utils/trapFocus 441 B
utils/tween 1.72 kB
utils/untrapFocus 120 B
utils/upperCase 404 B
utils/urlSearchParamsInHashProvider 845 B
utils/urlSearchParamsProvider 839 B
utils/useScheduler 309 B
utils/wait 103 B
utils/withLeadingCharacters 135 B
utils/withLeadingSlash 142 B
utils/withTrailingCharacters 135 B
utils/withTrailingSlash 142 B
utils/withoutLeadingCharacters 122 B
utils/withoutLeadingCharactersRecursive 165 B
utils/withoutLeadingSlash 133 B
utils/withoutTrailingCharacters 122 B
utils/withoutTrailingCharactersRecursive 165 B
utils/withoutTrailingSlash 133 B
utils/wrap 122 B
version 56 B
withBreakpointManager 1.54 kB
withBreakpointObserver 1.71 kB
withDrag 2.18 kB
withExtraConfig 163 B
withFreezedOptions 187 B
withGroup 455 B
withIntersectionObserver 303 B
withMountOnMediaQuery 393 B
withMountWhenInView 347 B
withMountWhenPrefersMotion 431 B
withMutation 1010 B
withName 109 B
withRelativePointer 1.29 kB
withResponsiveOptions 2.4 kB
withScrolledInView 3.05 kB

@studiometa/js-toolkit-v4

Export Size (gzip) Diff
BREAKPOINTS 778 B
Base 8.59 kB
DIAGNOSTICS 714 B
DRAG_MODES 162 B
EVENTS 153 B
MOUNT_ATTRIBUTE 69 B
SWAP_MODES 129 B
children 244 B
component 11.66 kB
createContext 472 B
createFallbackProvider 1.33 kB
createGroup 1.07 kB
createLocalStorage 2.33 kB
createMemoryStorageProvider 1.21 kB
createService 640 B
createServiceMixin 1015 B
createSessionStorage 2.33 kB
createStorage 2.3 kB
createUrlSearchParamsInHashProvider 1.21 kB
createUrlSearchParamsInHashStorage 2.34 kB
createUrlSearchParamsProvider 1.21 kB
createUrlSearchParamsStorage 2.34 kB
defaultScheduler 1.5 kB
defineManifest 983 B
domUpdate 1.23 kB
emitExtendable 1.09 kB
fromMetaGlob 203 B
fromWebpackContext 131 B
getBreakpoints 776 B
getInstances 2.87 kB
getMountedInstances 2.87 kB
getUnmountedInstances 2.88 kB
inject 176 B
injectContext 675 B
injectContextSync 634 B
jsonSerializer 95 B
localStorageProvider 1.21 kB
memoryStorageProvider 1.21 kB
namespaceQualifier 120 B
nextFrame 115 B
on 8.93 kB
perTarget 322 B
provide 185 B
provideContext 704 B
provideRootContext 748 B
read 126 B
registerComponent 11.27 kB
registerComponents 11.27 kB
registerManifest 11.33 kB
reportDiagnostic 329 B
sessionStorageProvider 1.21 kB
setBreakpoints 806 B
signal 925 B
subscribeContext 1.44 kB
swap 2.9 kB
test 11.77 kB
toggle 177 B
until 172 B
urlSearchParamsInHashProvider 1.21 kB
urlSearchParamsProvider 1.21 kB
useBreakpoint 1.44 kB
useDrag 3.36 kB
useInView 1.43 kB
useKey 1.47 kB
useMediaQuery 1.06 kB
useMutation 1.4 kB
usePointer 1.85 kB
usePrefersReducedMotion 1.09 kB
useRaf 1.95 kB
useResize 1.43 kB
useScroll 2.67 kB
useScrollProgress 3.64 kB
useWindowScroll 2.66 kB
useWindowSize 1.43 kB
utils 9.25 kB
utils/DEFAULT_DAMP_FACTOR 109 B
utils/INERTIA_FRAME 97 B
utils/MAX_SPRING_RATIO 100 B
utils/SCROLL_ALIGNMENTS 117 B
utils/SCROLL_AXES 100 B
utils/TRANSFORM_PROPS 137 B
utils/TRANSITION_OPTIONS 132 B
utils/camelCase 449 B
utils/capitalize 119 B
utils/clamp 133 B
utils/clamp01 149 B
utils/clampDampFactor 157 B
utils/createEaseInOut 120 B
utils/createEaseOut 91 B
utils/createElement 638 B
utils/createRange 205 B
utils/damp 211 B
utils/debounce 121 B
utils/decayOver 162 B
utils/deepmerge 312 B
utils/easeInCirc 94 B
utils/easeInCubic 81 B
utils/easeInExpo 97 B
utils/easeInOutCirc 150 B
utils/easeInOutCubic 141 B
utils/easeInOutExpo 150 B
utils/easeInOutQuad 139 B
utils/easeInOutQuart 140 B
utils/easeInOutQuint 140 B
utils/easeInOutSine 156 B
utils/easeInQuad 80 B
utils/easeInQuart 81 B
utils/easeInQuint 81 B
utils/easeInSine 104 B
utils/easeLinear 77 B
utils/easeOutCirc 121 B
utils/easeOutCubic 111 B
utils/easeOutExpo 124 B
utils/easeOutQuad 110 B
utils/easeOutQuart 112 B
utils/easeOutQuint 111 B
utils/easeOutSine 132 B
utils/enterTransition 679 B
utils/fold 200 B
utils/getOffsetSizes 268 B
utils/historyPush 391 B
utils/historyReplace 392 B
utils/inertiaDecay 199 B
utils/inertiaFinalValue 187 B
utils/inertiaStep 232 B
utils/inertiaTimeConstant 178 B
utils/isBoolean 90 B
utils/isDefined 87 B
utils/isFunction 86 B
utils/isNull 78 B
utils/isNumber 103 B
utils/isObject 115 B
utils/isString 89 B
utils/kebabCase 421 B
utils/leaveTransition 679 B
utils/lerp 120 B
utils/loadImage 245 B
utils/loadLink 776 B
utils/loadScript 697 B
utils/lockScroll 565 B
utils/lowerCase 84 B
utils/map 128 B
utils/matrix 150 B
utils/mean 147 B
utils/memo 217 B
utils/noop 62 B
utils/noopValue 76 B
utils/objectToURLSearchParams 266 B
utils/pascalCase 434 B
utils/random 93 B
utils/randomInt 132 B
utils/randomItem 163 B
utils/round 130 B
utils/saveActiveElement 571 B
utils/scrollPosition 857 B
utils/scrollTo 1.86 kB
utils/selectorFor 2.78 kB
utils/setClassesOrStyles 218 B
utils/smoothTo 2.83 kB
utils/snakeCase 421 B
utils/spring 343 B
utils/throttle 151 B
utils/transform 286 B
utils/transition 577 B
utils/trapFocus 717 B
utils/untrapFocus 587 B
utils/upperCase 84 B
utils/wait 103 B
utils/withLeadingCharacters 142 B
utils/withLeadingSlash 152 B
utils/withTrailingCharacters 143 B
utils/withTrailingSlash 153 B
utils/withoutLeadingCharacters 127 B
utils/withoutLeadingCharactersRecursive 144 B
utils/withoutLeadingSlash 138 B
utils/withoutTrailingCharacters 129 B
utils/withoutTrailingCharactersRecursive 147 B
utils/withoutTrailingSlash 140 B
utils/wrap 154 B
viewTransition 1.66 kB
warn 321 B
watchAttributeNamespace 2.54 kB
watchAttributes 2.02 kB
whenDOMSettled 2.35 kB
withDrag 4.02 kB
withInView 2.11 kB
withKey 2.15 kB
withMutation 2.09 kB
withPointer 2.52 kB
withRaf 2.62 kB
withResize 2.1 kB
withScroll 3.33 kB
withScrollProgress 4.33 kB
write 124 B

@codecov

codecov Bot commented Aug 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.06%. Comparing base (e8ce6b7) to head (e4e453f).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #866   +/-   ##
=======================================
  Coverage   97.06%   97.06%           
=======================================
  Files         176      176           
  Lines        4561     4561           
  Branches     1330     1331    +1     
=======================================
  Hits         4427     4427           
  Misses        122      122           
  Partials       12       12           
Flag Coverage Δ
eslint-plugin-js-toolkit 94.55% <ø> (ø)
js-toolkit 97.93% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

Copy link
Copy Markdown

Code Review

Risk: Low — The refactor is safe to merge; no concrete defects were found in the reviewed changes.

The MR updates the v4 specs to use the shipped getInstance(), captureDiagnostics(), waitFor(), frames(), mount(), and recordEvents() helpers, while retaining raw implementations where the tests specifically exercise lower-level behavior. The nullable getInstance() API change is internally consistent, and the reviewed replacements preserve the original assertions and cleanup behavior.

Reviewed all 47 staged diff files listed in the request, including packages/v4/src/instances.ts and the v4 source and migration spec diffs. No listed files were left unopened or unreviewed.


Review usage: 89,439 in (16,150 cached) / 2,144 out tokens — $0.0530 (openrouter/openai/gpt-5.6-luna, thinking: low)

Reviewed by @weareikko/code-review v0.9.5 for commit e4e453f.

@titouanmathis
titouanmathis merged commit f2d4994 into main Aug 24, 2026
11 of 12 checks passed
@titouanmathis
titouanmathis deleted the refactor/v4-finish-test-helper-sweep branch August 24, 2026 17:58
@github-actions

Copy link
Copy Markdown

v4 mount benchmarks

Base and head measured on this runner, alternating over 3 rounds each; every value is the median of the round medians. Running both sides on one machine is what removes cross-machine noise — a cached baseline from another runner would put it back.

A move under 25%, or on a benchmark under 5 ms, is not reported as a change: it is inside the measured noise of a shared runner.

No benchmark moved beyond the noise floor.

Within noise (18)
Group Benchmark Base Head us / component Change
mount 1000 components, one insertion control — declared but unregistered 2.00 ms 2.70 ms 2.70 +35.0%
mount 1000 components, one insertion flat 18.1 ms 18.1 ms 18.10 0.0%
mount 1000 components, one insertion in-view — one controller per element 31.9 ms 32.9 ms 32.90 +3.1%
mount 1000 components, one insertion nested 4 deep 15.9 ms 15.6 ms 15.60 -1.9%
mount 1000 components, one insertion realistic — 5 refs, 3 options, 4 handlers 74.7 ms 74.9 ms 74.90 +0.3%
mount 1000 components, one insertion responsive option — breakpoint cascade per mount 17.6 ms 17.5 ms 17.50 -0.6%
mount 1000 flat components, 1 vs 10 insertions 1 insertion 12.9 ms 12.9 ms 12.90 0.0%
mount 1000 flat components, 1 vs 10 insertions 10 insertions 13.3 ms 13.6 ms 13.60 +2.3%
mount 5000 components, one insertion control — declared but unregistered 18.1 ms 18.2 ms 3.64 +0.6%
mount 5000 components, one insertion flat 69.8 ms 74.6 ms 14.92 +6.9%
mount 5000 components, one insertion in-view — one controller per element 178.4 ms 173.3 ms 34.66 -2.9%
mount 5000 components, one insertion nested 4 deep 71.8 ms 71.9 ms 14.38 +0.1%
mount 5000 components, one insertion realistic — 5 refs, 3 options, 4 handlers 335.8 ms 360.9 ms 72.18 +7.5%
mount 5000 components, one insertion responsive option — breakpoint cascade per mount 87.1 ms 90.0 ms 18.00 +3.3%
mount 5000 flat components, 1 vs 10 insertions 1 insertion 73.0 ms 74.8 ms 14.96 +2.5%
mount 5000 flat components, 1 vs 10 insertions 10 insertions 75.6 ms 76.7 ms 15.34 +1.5%
unmount 1000 flat components, one removal flat 3.00 ms 3.30 ms 3.30 +10.0%
unmount 5000 flat components, one removal flat 18.7 ms 19.7 ms 3.94 +5.3%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant