Skip to content

fix(⚛️): build the Jest mock's ref handle once, and test Canvas under React Native Testing Library - #529

Open
YevheniiKotyrlo wants to merge 3 commits into
wcandillon:mainfrom
YevheniiKotyrlo:test/component-test-tier
Open

YevheniiKotyrlo wants to merge 3 commits into
wcandillon:mainfrom
YevheniiKotyrlo:test/component-test-tier

Conversation

@YevheniiKotyrlo

@YevheniiKotyrlo YevheniiKotyrlo commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Problem

The Jest mock's Canvas, which apps render in their own Jest tests, builds its ref handle on every render (useImperativeHandle without dependencies). A ref gets a new handle after each re-render, and a state setter used as the ref re-renders its owner until React stops with "Maximum update depth exceeded". Canvas itself builds its handle once since #532.

Nothing catches this: every spec under packages/webgpu/src/__tests__ goes through the e2e harness (jest.config.js gives the tree a globalSetup that waits for the example app), so no test renders a component under Jest. Canvas's layout size (#531) and its single handle (#532) have no device test either.

#525's two fixes reached main through #531 and #532; this carries what remained of it.

Solution

  • The mock builds its handle once.
  • jest.unit.config.js runs src/__tests__/unit with React Native's Jest preset and React Native Testing Library 14, which renders on test-renderer, React 19's replacement for the deprecated react-test-renderer. The e2e config skips that directory, and CI runs yarn test:unit in the build job beside the plugin's tests.
  • The Tests screen gains a canvas laid out at 64 × 32 and drawn at half that size under a scale transform, and a re-render that resolves once a commit has rendered it, for Canvas.spec.ts. setup.ts's isNativeApp() replaces the specs' inline platform checks.

Tests

  • unit/mock.test.tsx: the mock's handle survives re-renders, a state setter as its ref settles, it answers -1 and null and throws from getNativeSurface(), and it keeps its own props off the View it renders. Without the fix, the first two fail: five ref calls instead of one, and "Maximum update depth exceeded".
  • unit/Canvas.test.tsx, with a stand-in RNWebGPU and View's measureLayout: the context is created at the layout size the view measures relative to itself, the ref handle survives re-renders, a state setter as the ref settles, unmount retires the context once, and a context name other than webgpu throws. Against Canvas.tsx from before fix(🎨): size the canvas context from the view's layout, not its transformed box #531 and fix(🎨): add contextId to the useImperativeHandle dependencies in Canvas #532, the first three fail.
  • Canvas.spec.ts on a device: the scaled canvas's context has its layout size, not its transformed box, and its handle is the same after three re-renders.
  • WebPolyfill.spec.ts: the web polyfill's native surface reports the canvas's layout size, reads it without touching the drawing buffer and delegates its DOM methods to the canvas, and a second context at the same size leaves the drawing buffer alone.

Verification

yarn lint · yarn tsc · yarn turbo run build --filter='./packages/*' · the Expo template's web build · yarn test:plugin · yarn test:unit (9 passed) · yarn check-dawn · yarn build:docs. CI's Android e2e step (debug build, Metro with CI=true) on an Android 15 emulator: 225 of 227 passed, main's 219 and the six new cases; the YUV cases of ExternalTextureColorSpace and NativeBufferWrapper fail on main on that emulator too.

On devices the whole suite passes, 227 of 227: on a Galaxy S23 (Android 14), and on an iPhone 12 Pro Max (iOS 27.0.1) built and run as CI's iOS job does on a simulator (Debug build, Metro with CI=true). main passes 221 of 221 on both. Not run here: the SwiftPM example's build and the semantic-release dry run, which needs the repository's token.

… React Native Testing Library

The Jest mock's Canvas built its ref handle on every render
(useImperativeHandle without dependencies), so a ref got a new handle after
each re-render, and a state setter used as the ref re-rendered its owner
until React stopped with "Maximum update depth exceeded". It builds the
handle once, as Canvas does.

Every spec under src/__tests__ goes through the e2e harness, which waits for
the example app, so nothing rendered a component under Jest.
jest.unit.config.js runs src/__tests__/unit with React Native's Jest preset
and React Native Testing Library 14 on test-renderer, React 19's
replacement for react-test-renderer; CI runs it beside the plugin's tests.
The Canvas spec drives a stand-in RNWebGPU module and View's measureLayout,
and the mock's spec holds its handle and its loud failures.

On a device, Canvas.spec.ts checks a canvas drawn at half its layout size
under a scale transform: its context is sized from the layout, and its ref
handle is the same after re-renders. setup.ts's isNativeApp() replaces the
specs' inline platform checks, and WebPolyfill.spec.ts covers the web
polyfill's native surface.
@YevheniiKotyrlo
YevheniiKotyrlo force-pushed the test/component-test-tier branch from 2c50ea9 to 3ce24a1 Compare October 16, 2026 15:28
@YevheniiKotyrlo YevheniiKotyrlo changed the title test(🧪): render Canvas and its Jest mock under React Native Testing Library fix(⚛️): build the Jest mock's ref handle once, and test Canvas under React Native Testing Library Oct 16, 2026

This branch has not been deployed

No deployments
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