mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
--- ## Sprout 🌱 Overview **(Overview copied from [sprout/README.md](https://github.com/facebook/react-forget/blob/0468ddf8bbee54a9394317a667ea4ed96bb3bef3/forget/packages/sprout/README.md))** React Forget test framework that executes compiler fixtures. Currently, Sprout runs each fixture with a known set of inputs and annotations. We hope to add fuzzing capabilities to Sprout, synthesizing sets of program inputs based on type and/or effect annotations. Sprout is currently WIP and only executes files listed in `src/SproutOnlyFilterTodoRemove.ts`. ### Milestones: - [✅] Render fixtures with React runtime / `testing-library/react`. - [ ] Make Sprout CLI -runnable and report results in process exit code. After this point: - Sprout can be enabled by default and added to the Github Actions pipeline. - `SproutOnlyFilterTodoRemove` can be renamed to `SproutSkipFilter`. - All new tests should provide a `FIXTURE_ENTRYPOINT`. - [ ] Annotate `FIXTURE_ENTRYPOINT` (fn entrypoint and params) for rest of fixtures. - [ ] Edit rest of fixtures to use shared functions or define their own helpers. - [ ] *(optional)* Store Sprout output as snapshot files. i.e. each fixture could have a `fixture.js`, `fixture.snap.md`, and `fixture.sprout.md`. ### Constraints Each fixture test executed by Sprout needs to export a `FIXTURE_ENTRYPOINT`, a single function and parameter set with the following type signature. ```js type FIXTURE_ENTRYPOINT<T> = { // function to be invoked fn: ((...params: Array<T>) => any), // params to pass to fn params: Array<T>, // true if fn should be rendered as a React Component // i.e. returns jsx or primitives isComponent?: boolean, } ``` Example: ```js // test.js function MyComponent(props) { return <div>{props.a + props.b}</div>; } export const FIXTURE_ENTRYPOINT = { fn: MyComponent, params: [{a: "hello ", b: "world"}], isComponent: true, }; ``` --- ## Implementation Details - jest-worker test orchestrator (similar to Snap 🫰). I chose to write a test runner instead of directly using Jest for flexibility and speed. - Sprout 🌱 currently runs much more code per fixture (2 babel transform pipelines + 2 `exec(...)` than snap, so all scaling concerns apply. - Sprout may need more customization in the future (e.g. fuzzing component inputs, caching artifacts) - We probably want to add snapshot files for Sprout, which is much easier with a custom runner. This is also one of the main reasons we wrote Snap. Jest consolidates all external snapshots (i.e. non-inline snapshots) from a test into [a single file](https://github.com/facebook/react-forget/blob/d0a006ffa9842ee9db6b23ada94622af901eb07f/forget/src/__tests__/__snapshots__/compiler-test.ts.snap). This was painful mainly for rebasing changes, but also for small papercuts (e.g. needing to run `yarn test -u` twice when deleting a fixture) - Currently does not save output to snapshot file, but we can easily add this later (i.e. each test would have a `.snap.md` and `.sprout.md` file) - Supports filter mode (same testfilter.txt file as snap) - Currently does not support watch mode. I expect that sprout's primary use will be catching bugs in the PR / Github Actions phase. Can be changed if we need to iterate on sprout output while developing. - All tests are run with `react-test-renderer` to access the React Runtime, which is needed for calls to `useMemoCache` (and other potential hooks). - react-test-renderer required a mocked DOM, so I ~~used js-dom~~ did a terrible js-dom hack to add document, window, and other browser globals to the jest-worker globals, then exec the test code in the jest worker global. I can clean this up later by bundling library code (e.g. react, react-test-renderer) to not use `require(...)`, then calling `exec` on all "test client code" in the js-dom mock global (instead of the real jest worker one) - Tests marked `isComponent` need to return valid jsx or primitives. Values returned by all other tests are converted to a string primitive via JSON.stringify. ```sh # install new dependencies to node_modules $ yarn $ cd forget/packages/babel-plugin-react-forget $ yarn sprout:build && yarn sprout PASS alias-nested-member-path PASS assignment-variations-complex-lvalue PASS assignment-variations PASS chained-assignment-expressions PASS computed-call-evaluation-order PASS const-propagation-into-function-expression-primitive PASS constant-propagation-for PASS constant-propagation-while PASS constant-propagation ... ✨ Done in 3.91s. ```