From 996b9d6ea627a9c97d04f8ea6d926fc7974f02bf Mon Sep 17 00:00:00 2001 From: Micheal Parks Date: Thu, 16 Apr 2026 16:46:08 -0400 Subject: [PATCH] fix render --- .changeset/free-ants-turn.md | 5 +++++ README.md | 25 +++++++++---------------- src/lib/pure.js | 5 +++++ src/routes/__tests__/Invalidate.spec.ts | 9 --------- 4 files changed, 19 insertions(+), 25 deletions(-) create mode 100644 .changeset/free-ants-turn.md diff --git a/.changeset/free-ants-turn.md b/.changeset/free-ants-turn.md new file mode 100644 index 0000000..90b21fd --- /dev/null +++ b/.changeset/free-ants-turn.md @@ -0,0 +1,5 @@ +--- +'@threlte/test': minor +--- + +`render()` now automatically advances the scheduler once with `delta: 0` after mounting diff --git a/README.md b/README.md index 6070369..64c5351 100644 --- a/README.md +++ b/README.md @@ -63,29 +63,22 @@ const { frameInvalidated } = advance() expect(frameInvalidated).toBe(true) ``` -#### First advance after render +#### Initial advance on render -The first `advance()` call after `render()` will always return `{ frameInvalidated: true }`. This is expected — Threlte's internal setup (renderer properties, camera, resize detection) calls `invalidate()` during initialization, just as it does in production on the first animation frame. +`render()` automatically calls `advance({ delta: 0 })` once before returning. This ensures that scene matrices (`matrixWorld`) are computed for all objects, so positional assertions are immediately valid without requiring a manual `advance()` call first. -When testing invalidation behavior, call `advance()` once to drain the setup invalidation, then use subsequent calls for your assertions: +There are a few consequences worth knowing: -```ts -const { advance, rerender } = render(MyComponent, { - props: { autoInvalidate: false }, -}) +**The first user `advance()` is the second scheduler tick.** Any `useTask` with `autoStart: true` will have already run once with `delta: 0` by the time `render()` returns. If your task has side effects that should only run from explicit test code, start the task with `autoStart: false` and control it manually. -// Drain setup invalidation -advance() +**The initial `frameInvalidated` signal is consumed internally.** Any `invalidate()` calls that fire during component initialization are cleared before `render()` returns. You do not need to drain a setup frame before testing invalidation logic — assertions on `frameInvalidated` reflect only what happened during your explicit `advance()` call. -// Now test real invalidation behavior -const { frameInvalidated } = advance() -expect(frameInvalidated).toBe(false) +**Note:** tasks that compute `1 / delta` will receive `Infinity` on this initial tick and should guard against it. -// Trigger invalidation via prop change -await rerender({ someProp: 'newValue' }) +The initial delta can be overridden via the third argument to `render()`: -const result = advance() -expect(result.frameInvalidated).toBe(true) +```ts +render(MyComponent, {}, { delta: 16 }) ``` ### fireEvent diff --git a/src/lib/pure.js b/src/lib/pure.js index d0072d4..4ac4f34 100644 --- a/src/lib/pure.js +++ b/src/lib/pure.js @@ -19,6 +19,7 @@ import { setup } from './setup.js' * @typedef {{ * container: HTMLElement * baseElement: HTMLElement + * canvas: HTMLCanvasElement * camera: import('@threlte/core').CurrentWritable * scene: import('three').Scene * context: import('@threlte/core').ThrelteContext @@ -42,6 +43,7 @@ import { setup } from './setup.js' * baseElement?: HTMLElement * canvas?: HTMLCanvasElement * context?: Record + * delta?: number * }} renderOptions * @returns {RenderResult} The rendered component and bound testing functions. */ @@ -62,10 +64,13 @@ const render = (Component, options = {}, renderOptions = {}) => { }, }) + component.advance({ delta: renderOptions.delta ?? 0 }) + const handlers = component.interactivityContext.handlers return { baseElement, + canvas, camera: component.context.camera, component: component.ref, container, diff --git a/src/routes/__tests__/Invalidate.spec.ts b/src/routes/__tests__/Invalidate.spec.ts index 9cb112b..3074642 100644 --- a/src/routes/__tests__/Invalidate.spec.ts +++ b/src/routes/__tests__/Invalidate.spec.ts @@ -12,9 +12,6 @@ describe('', () => { }, }) - // First advance drains setup invalidation - advance() - const { frameInvalidated } = advance() expect(frameInvalidated).toBe(false) }) @@ -27,8 +24,6 @@ describe('', () => { }, }) - advance() - const { frameInvalidated } = advance() expect(frameInvalidated).toBe(true) }) @@ -41,8 +36,6 @@ describe('', () => { }, }) - advance() - const { frameInvalidated } = advance() expect(frameInvalidated).toBe(false) }) @@ -55,8 +48,6 @@ describe('', () => { }, }) - advance() - const first = advance() expect(first.frameInvalidated).toBe(false)