From 84a6cb942ffae9555c11f09f501289fee8aa7a6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Thu, 30 Apr 2026 08:44:09 -0700 Subject: [PATCH] Improve Fantom documentation in __docs__/README.md (#56660) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Expand `private/react-native-fantom/__docs__/README.md` so it's the single source of truth for Fantom usage. Added sections: - Updated the Usage example to import `setUpDefaultReactNativeEnvironment` and explicitly warn against `InitializeCore` (which installs LogBox and breaks Fantom's error handling). All real `-itest.js` files use this import. - New `### Conventions` section: `__tests__` placement, `-benchmark-itest.js` suffix, using `Fantom.runTask()` for rendering, the `ensureInstance` helper, component-specific instance types, ref-based element access, testing imperative APIs / method timing / edge cases, `takeMountingManagerLogs` for command verification, and "don't oversimplify assertions" / "avoid trivial tests" guidance. - New `### Assertions` section: prefer `.toEqual()` + inline JSX over `.toMatchSnapshot()`, full examples for `getRenderedOutput().toJSX()` with `includeLayoutMetrics` and the `props` filter, plus an element-level assertion example covering `tagName`, `getBoundingClientRect`, and child structure. - New `### Limitations` section: cannot nest `runTask()`, subset of Jest API, tests must live under `packages/react-native`. - New `#### C++ sampling profiler` subsection under Profiling, documenting `FANTOM_PROFILE_CPP` (parallel to the existing JS profiler section). Pure API references (`setLogBoxCheckEnabled`, `dispatchNativeEvent`, `scheduleTask`, `enqueueNativeEvent`, `runOnUIThread`, `runWorkLoop`, `scrollTo`, `takeJSMemoryHeapSnapshot`, `createRoot` options, etc.) are not duplicated — they continue to live in the inline source docs in `src/index.js`, which the README points to. Changelog: [Internal] Reviewed By: mdvacca, cipolleschi Differential Revision: D103217408 --- .../react-native-fantom/__docs__/README.md | 116 +++++++++++++++++- 1 file changed, 115 insertions(+), 1 deletion(-) diff --git a/private/react-native-fantom/__docs__/README.md b/private/react-native-fantom/__docs__/README.md index d2d7e3e6805..c1405341328 100644 --- a/private/react-native-fantom/__docs__/README.md +++ b/private/react-native-fantom/__docs__/README.md @@ -70,9 +70,13 @@ only a subset of Jest's Global API is currently available. For example, APIs. If you are blocked by the lack of a specific API, please reach out to us. Most of the interesting APIs are available via the `@react-native/fantom` -package: +package. Test files should also import `setUpDefaultReactNativeEnvironment` at +the top to set up the React Native environment. **Do not import +`InitializeCore`** — it installs LogBox, which interferes with Fantom's error +handling. ```javascript +import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment'; import * as Fantom from '@react-native/fantom'; describe('My feature', () => { @@ -108,6 +112,97 @@ Similar to Jest, you can also run Fantom in watch mode using `--watch`: yarn fantom --watch ``` +### Conventions + +- Place test files in `__tests__` directories alongside the code being tested. +- Benchmark tests use the `-benchmark-itest.js` suffix. +- Use `Fantom.runTask()` to render and run synchronous operations; it ensures + the React work is flushed before assertions. +- Access elements through refs and use the + [`ensureInstance`](../../../packages/react-native/src/private/__tests__/utilities/ensureInstance.js) + helper for type-safe access to the underlying instance: + + ```javascript + import ensureInstance from 'react-native/src/private/__tests__/utilities/ensureInstance'; + + const element = ensureInstance(elementRef.current, ReactNativeElement); + ``` + +- Prefer component-specific instance types (`TextInputInstance`, + `ScrollViewInstance`, etc.) over the generic `HostInstance` when available. +- For components with imperative APIs (`focus`, `blur`, `clear`, etc.), test: + - Each method's behaviour and edge cases (e.g. `blur` when not focused). + - Method timing — that it works when called from refs, `useLayoutEffect`, and + `useEffect`. +- Verify that native commands are dispatched with + `root.takeMountingManagerLogs()`. +- Don't write tests with only trivial assertions like + `expect(element).toBeDefined()` when more complex behaviour is under test. + When a test fails, understand what should render rather than weakening the + assertion to make it pass. + +### Assertions + +Prefer evaluating rendered output inline with `.toEqual()` and inline JSX over +`.toMatchSnapshot()` or weak numeric assertions like +`element.childNodes.length`. + +```javascript +// Get JSX representation +expect(root.getRenderedOutput().toJSX()).toEqual( + , +); + +// Include layout metrics +expect(root.getRenderedOutput({includeLayoutMetrics: true}).toJSX()).toEqual( + , +); + +// Filter to specific props for minimal assertions +expect(root.getRenderedOutput({props: ['backgroundColor']}).toJSX()).toEqual( + , +); +``` + +For element-level assertions, get a typed reference and inspect tag names, +layout metrics, or children: + +```javascript +const elementRef = createRef(); + +Fantom.runTask(() => { + root.render( + + the quick brown fox + , + ); +}); + +const element = ensureInstance(elementRef.current, ReactNativeElement); +expect(element.tagName).toBe('RN:View'); + +const bounds = element.getBoundingClientRect(); +expect(bounds.width).toBe(100); +expect(bounds.height).toBe(50); + +expect(root.getRenderedOutput().toJSX()).toEqual( + + the quick brown fox + , +); +``` + +### Limitations + +- `Fantom.runTask()` calls cannot be nested — doing so will throw. +- Only a subset of Jest's Global API is available (e.g. `test.each` is not + implemented). Reach out if you're blocked by a specific missing API. +- Tests must live within `packages/react-native`; Fantom is not currently + intended for application-specific code. + ### Test configuration You can configure certain aspects of the test execution using pragmas in the @@ -303,6 +398,25 @@ memory heap and print a message indicating where it was saved. E.g.: You can have multiple calls to `Fantom.takeJSMemoryHeapSnapshot()` in your test, and each one will create a different file. +#### C++ sampling profiler + +You can automatically record C++ sampling profiler traces (with DWARF call +graphs) by wrapping the Fantom tester binary with Linux `perf record`. Run your +fantom test with the flag `FANTOM_PROFILE_CPP`: + +```shell +FANTOM_PROFILE_CPP=1 yarn fantom +``` + +Output is saved to `.out/cpp-traces/perf-.data`. Analyze it with: + +```shell +perf report -i .out/cpp-traces/perf-.data +``` + +This is not available on CI (the runner will throw an error if attempted) and +requires `perf` to be installed on the host. + ### Running the full suite locally When running the entire suite locally, set `FANTOM_FORCE_CI_MODE=1`: