Skip to content

JS-driven screenshot approach via ScreenshotHelper native module#106

Open
EmilioBejasa wants to merge 2 commits into
mainfrom
ui-thread-approach
Open

JS-driven screenshot approach via ScreenshotHelper native module#106
EmilioBejasa wants to merge 2 commits into
mainfrom
ui-thread-approach

Conversation

@EmilioBejasa
Copy link
Copy Markdown
Collaborator

Summary

  • Adds ScreenshotHelperModule — a native module that JS calls when a component has finished rendering
  • Each story variant is wrapped with withAutoScreenshot(name, Component) HOC which calls NativeModules.ScreenshotHelper.takeScreenshot(name) from useEffect + requestAnimationFrame
  • ReactHostFixture sets a CountDownLatch + view reference before starting each surface, then waits for JS to release it
  • Adds ScreenshotHelperPackage to register the module with the isolated ReactHostImpl

Why

Instead of using ReactMarker.CONTENT_APPEARED (a native heuristic), the JS render cycle itself determines when the screenshot is taken. useEffect fires after React has committed the tree; requestAnimationFrame defers by one frame to let native layout settle. This inverts control to the JS side.

Test plan

  • All 20 screenshots record successfully locally (app:recordDebugAndroidTestScreenshotTest)
  • CI passes

🤖 Generated with Claude Code

Instead of waiting for ReactMarker.CONTENT_APPEARED from the native side,
each component calls NativeModules.ScreenshotHelper.takeScreenshot(name)
from useEffect via requestAnimationFrame. The native module takes the
screenshot and releases a CountDownLatch, unblocking the test thread.

This inverts control: the JS render cycle determines when the screenshot
is taken, rather than native heuristics about when React is "done".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@EmilioBejasa EmilioBejasa requested a review from tdrhq April 7, 2026 18:32
Instead of 19 sequential parameterized tests, ComponentSurfaceTest.screenshotAll
starts all surfaces in a single runOnMainSync. The JS thread queues work for all
of them at once; each calls ScreenshotHelper.takeScreenshot independently and
counts down a shared CountDownLatch. Test count drops from 21 to 3.

ScreenshotHelperModule now uses ConcurrentHashMap<String, WeakReference<View>>
and a single shared CountDownLatch instead of single-value statics.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

val bundleLoader = JSBundleLoader.createAssetLoader(
context, "assets://index.android.bundle", true)
val delegate = DefaultReactHostDelegate(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after PR #103 is approved and merged, I want you to refactor the code from IsolatedTest, not copy-paste a completely duplicate version

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.

2 participants