Testing: Fixtures & Snapshots
The React Compiler uses a fixture-based testing suite to verify the correctness of its transformation passes, alias analysis, and memoization logic. This system allows developers to write standard JavaScript or TypeScript code and assert how the compiler optimizes it through automated snapshot comparisons.
Fixture Architecture
All compiler tests are located in compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/. Each file in this directory represents a standalone test case. The testing framework automatically discovers these files, runs the compiler against them, and compares the output to a corresponding snapshot file.
Fixture Structure
A standard fixture consists of three parts:
- Compiler Directives: Optional comments at the top of the file to configure compiler flags.
- Source Code: The React components or Hooks to be compiled.
- Test Entrypoint: A mandatory exported object named
FIXTURE_ENTRYPOINT(orTODO_FIXTURE_ENTRYPOINTfor known failing tests).
// 1. Directives
// @validateNoCapitalizedCalls
// 2. Source Code
function Component(props) {
const x = SomeFunc(); // This should trigger a validation error
return <div>{x}</div>;
}
// 3. Entrypoint
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{}],
isComponent: true,
};
The FIXTURE_ENTRYPOINT API
The FIXTURE_ENTRYPOINT object defines how the test runner should execute the compiled code to verify runtime behavior alongside static compilation.
| Property | Type | Description |
| :--- | :--- | :--- |
| fn | Function | The primary function or component to be compiled and tested. |
| params | Array<any> | The arguments passed to fn during the initial test execution. |
| isComponent | boolean \| string | Determines if the compiler should treat the function as a React Component (enforcing Hook rules, etc.). |
| sequentialRenders | Array<any> | An array of prop objects. The runner will call fn with each set of props sequentially to verify that memoization remains stable across renders. |
Compiler Directives (Pragmas)
Directives allow for granular control over the EnvironmentConfig on a per-test basis. These are defined as comments at the start of the fixture file.
@compilationMode:"infer" | "all": Controls whether the compiler should attempt to optimize all functions or only those it infers are React-related.@validateNoCapitalizedCalls: Enables thevalidateNoCapitalizedCallspass, which errors if a capitalized function is called instead of being rendered as JSX.@hookPattern:"...": A regex string used to identify custom Hooks (e.g.,".*\\b(use[^$]+)$").@expectNothingCompiled: Asserts that the compiler should skip this file (e.g., for non-React code).@enablePreserveExistingMemoizationGuarantees: Forces the compiler to respect existinguseMemoanduseCallbackboundaries.
Snapshot Testing Workflow
The compiler tests generate snapshots that capture multiple stages of the compilation pipeline. When a test runs, the framework produces:
- Input Source: The original code.
- HIR (High-level Intermediate Representation): The internal graph-based representation used for data-flow analysis.
- Reactive Scopes: The output of the
PruneNonEscapingScopesandAnalyzeFunctionspasses, showing which values were grouped for memoization. - Output JavaScript: The final transformed code containing the
useMemoCache(the "Memo Cache" API).
Validation Tests
To test compiler errors (e.g., ValidateNoCapitalizedCalls.ts), fixtures are used to trigger CompilerError exceptions. The snapshot system captures the error message, the specific code location, and any suggested fixes.
Example of an error-triggering fixture:
// @validateNoCapitalizedCalls
function Foo() {
let x = Bar;
x(); // The compiler will catch this capitalized call during the validation pass
}
Execution and Regression
Tests are executed via Jest. To update snapshots after making changes to the compiler's internal logic (such as modifying PruneNonEscapingScopes.ts):
yarn jest packages/babel-plugin-react-compiler -u
The runner ensures that:
- The code compiles without unexpected errors.
- The generated code is syntactically valid.
- The runtime behavior (evaluated via
paramsorsequentialRenders) matches the expected output of the uncompiled version.