{"id":28423724,"url":"https://github.com/testing-library/react-render-stream-testing-library","last_synced_at":"2025-06-25T11:32:09.405Z","repository":{"id":257822054,"uuid":"868908543","full_name":"testing-library/react-render-stream-testing-library","owner":"testing-library","description":"A library to make commited-render-to-committed-render assertions on your React components and hooks.","archived":false,"fork":false,"pushed_at":"2025-01-16T15:48:24.000Z","size":1255,"stargazers_count":100,"open_issues_count":0,"forks_count":4,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-06-11T13:27:46.340Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@testing-library/react-render-stream","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/testing-library.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-10-07T11:52:04.000Z","updated_at":"2025-05-30T20:59:49.000Z","dependencies_parsed_at":null,"dependency_job_id":"5db9c081-a38c-445b-8068-b779c5f75d72","html_url":"https://github.com/testing-library/react-render-stream-testing-library","commit_stats":{"total_commits":83,"total_committers":6,"mean_commits":"13.833333333333334","dds":"0.12048192771084343","last_synced_commit":"c4b152e54c2816ae17ef35d9ccef893cf5c14ac8"},"previous_names":["testing-library/react-render-stream-testing-library"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/testing-library/react-render-stream-testing-library","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testing-library%2Freact-render-stream-testing-library","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testing-library%2Freact-render-stream-testing-library/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testing-library%2Freact-render-stream-testing-library/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testing-library%2Freact-render-stream-testing-library/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/testing-library","download_url":"https://codeload.github.com/testing-library/react-render-stream-testing-library/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testing-library%2Freact-render-stream-testing-library/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260926069,"owners_count":23083767,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-06-05T09:09:18.418Z","updated_at":"2025-06-25T11:32:09.390Z","avatar_url":"https://github.com/testing-library.png","language":"TypeScript","readme":"# @testing-library/react-render-stream\n\n## What is this library?\n\nThis library allows you to make committed-render-to-committed-render assertions\non your React components and hooks. This is usually not necessary, but can be\nhighly beneficial when testing hot code paths.\n\n## Who is this library for?\n\nThis library is intended to test libraries or library-like code. It requires you\nto write additional components so you can test how your components interact with\nother components in specific scenarios.\n\nAs such, it is not intended to be used for end-to-end testing of your\napplication.\n\n## Brought to you by Apollo\n\n\u003ca style=\"display: flex; justify-content: center; height: 40px; margin: 1em\" href=\"https://www.apollographql.com/\"\u003e\u003cimg src=\"./other/apollo-wordmark.svg\" height=\"40\" alt=\"\"\u003e\n\u003c/a\u003e\n\nThis library originally was part of the Apollo Client test suite and is\nmaintained by the Apollo Client team.\n\n### Usage examples:\n\n#### `createRenderStream` with DOM snapshots\n\nIf used with `snapshotDOM`, RSTL will create a snapshot of your DOM after every\nrender, and you can iterate through all the intermediate states of your DOM at\nyour own pace, independenly of how fast these renders actually happened.\n\n```jsx\ntest('iterate through renders with DOM snapshots', async () =\u003e {\n  const {takeRender, render} = createRenderStream({\n    snapshotDOM: true,\n  })\n  const utils = await render(\u003cCounter /\u003e)\n  const incrementButton = utils.getByText('Increment')\n  await userEvent.click(incrementButton)\n  await userEvent.click(incrementButton)\n  {\n    const {withinDOM} = await takeRender()\n    const input = withinDOM().getByLabelText('Value')\n    expect(input.value).toBe('0')\n  }\n  {\n    const {withinDOM} = await takeRender()\n    const input = withinDOM().getByLabelText('Value')\n    expect(input.value).toBe('1')\n  }\n  {\n    const {withinDOM} = await takeRender()\n    const input = withinDOM().getByLabelText('Value')\n    expect(input.value).toBe('2')\n  }\n})\n```\n\n### `renderHookToSnapshotStream`\n\nUsage is very similar to RTL's `renderHook`, but you get a `snapshotStream`\nobject back that you can iterate with `takeSnapshot` calls.\n\n```jsx\ntest('`useQuery` with `skip`', async () =\u003e {\n  const {takeSnapshot, rerender} = await renderHookToSnapshotStream(\n    ({skip}) =\u003e useQuery(query, {skip}),\n    {\n      wrapper: ({children}) =\u003e \u003cProvider client={client}\u003e{children}\u003c/Provider\u003e,\n    },\n  )\n\n  {\n    const result = await takeSnapshot()\n    expect(result.loading).toBe(true)\n    expect(result.data).toBe(undefined)\n  }\n  {\n    const result = await takeSnapshot()\n    expect(result.loading).toBe(false)\n    expect(result.data).toEqual({hello: 'world 1'})\n  }\n\n  await rerender({skip: true})\n  {\n    const snapshot = await takeSnapshot()\n    expect(snapshot.loading).toBe(false)\n    expect(snapshot.data).toEqual(undefined)\n  }\n})\n```\n\n### Tracking which components rerender with `useTrackRenders`\n\nYou can track if a component was rerendered during a specific render by calling\n`useTrackRenders` within it.\n\n```jsx\ntest('`useTrackRenders` with suspense', async () =\u003e {\n  function ErrorComponent() {\n    useTrackRenders()\n    // return ...\n  }\n  function DataComponent() {\n    useTrackRenders()\n    const data = useSuspenseQuery(someQuery)\n    // return ...\n  }\n  function LoadingComponent() {\n    useTrackRenders()\n    // return ...\n  }\n  function App() {\n    useTrackRenders()\n    return (\n      \u003cErrorBoundary FallbackComponent={ErrorComponent}\u003e\n        \u003cReact.Suspense fallback={\u003cLoadingComponent /\u003e}\u003e\n          \u003cDataComponent /\u003e\n        \u003c/React.Suspense\u003e\n      \u003c/ErrorBoundary\u003e\n    )\n  }\n\n  const {takeRender, render} = createRenderStream()\n  await render(\u003cApp /\u003e)\n  {\n    const {renderedComponents} = await takeRender()\n    expect(renderedComponents).toEqual([App, LoadingComponent])\n  }\n  {\n    const {renderedComponents} = await takeRender()\n    expect(renderedComponents).toEqual([DataComponent])\n  }\n})\n```\n\n\u003e [!NOTE]\n\u003e\n\u003e The order of components in `renderedComponents` is the order of execution of\n\u003e `useLayoutEffect`. Keep in mind that this might not be the order you would\n\u003e expect.\n\n### taking custom snapshots inside of helper Components with `replaceSnapshot`\n\nIf you need to, you can also take custom snapshots of data in each render.\n\n```tsx\ntest('custom snapshots with `replaceSnapshot`', async () =\u003e {\n  function Counter() {\n    const [value, setValue] = React.useState(0)\n    replaceSnapshot({value})\n    // return ...\n  }\n\n  const {takeRender, replaceSnapshot, render} = createRenderStream\u003c{\n    value: number\n  }\u003e()\n  const utils = await render(\u003cCounter /\u003e)\n  const incrementButton = utils.getByText('Increment')\n  await userEvent.click(incrementButton)\n  {\n    const {snapshot} = await takeRender()\n    expect(snapshot).toEqual({value: 0})\n  }\n  {\n    const {snapshot} = await takeRender()\n    expect(snapshot).toEqual({value: 1})\n  }\n})\n```\n\n\u003e [!TIP]\n\u003e\n\u003e `replaceSnapshot` can also be called with a callback that gives you access to\n\u003e the last snapshot value.\n\n\u003e [!TIP]\n\u003e\n\u003e You can also use `mergeSnapshot`, which shallowly merges the last snapshot\n\u003e with the new one instead of replacing it.\n\n### Making assertions directly after a render with `onRender`\n\n```tsx\ntest('assertions in `onRender`', async () =\u003e {\n  function Counter() {\n    const [value, setValue] = React.useState(0)\n    replaceSnapshot({value})\n    return (\n      \u003cCounterForm value={value} onIncrement={() =\u003e setValue(v =\u003e v + 1)} /\u003e\n    )\n  }\n\n  const {takeRender, replaceSnapshot, utils} = await renderToRenderStream\u003c{\n    value: number\n  }\u003e({\n    onRender(info) {\n      // you can use `expect` here\n      expect(info.count).toBe(info.snapshot.value + 1)\n    },\n  })\n  const incrementButton = utils.getByText('Increment')\n  await userEvent.click(incrementButton)\n  await userEvent.click(incrementButton)\n  await takeRender()\n  await takeRender()\n  await takeRender()\n})\n```\n\n\u003e [!NOTE]\n\u003e\n\u003e `info` contains the\n\u003e [base profiling information](https://react.dev/reference/react/Profiler#onrender-parameters)\n\u003e passed into `onRender` of React's `Profiler` component, as well as `snapshot`,\n\u003e `replaceSnapshot` and `mergeSnapshot`\n\n### `expect(...)[.not].toRerender()` and `expect(...)[.not].toRenderExactlyTimes(n)`\n\nThis library adds to matchers to `expect` that can be used like\n\n```tsx\ntest('basic functionality', async () =\u003e {\n  const {takeRender} = await renderToRenderStream(\u003cRerenderingComponent /\u003e)\n\n  await expect(takeRender).toRerender()\n  await takeRender()\n\n  // trigger a rerender somehow\n  await expect(takeRender).toRerender()\n  await takeRender()\n\n  // ensure at the end of a test that no more renders will happen\n  await expect(takeRender).not.toRerender()\n  await expect(takeRender).toRenderExactlyTimes(2)\n})\n```\n\nThese matchers can be used on multiple different objects:\n\n```ts\nawait expect(takeRender).toRerender()\nawait expect(renderStream).toRerender()\nawait expect(takeSnapshot).toRerender()\nawait expect(snapshotStream).toRerender()\n```\n\n\u003e [!NOTE]\n\u003e\n\u003e By default, `.toRerender` and `toRenderExactlyTimes` will wait 100ms for\n\u003e renders or to ensure no more renders happens.\n\u003e\n\u003e You can modify that with the `timeout` option:\n\u003e\n\u003e ```js\n\u003e await expect(takeRender).not.toRerender({timeout: 300})\n\u003e ```\n\n\u003e [!TIP]\n\u003e\n\u003e If you don't want these matchers not to be automatically installed, you can\n\u003e import from `@testing-library/react-render-stream/pure` instead.  \n\u003e Keep in mind that if you use the `/pure` import, you have to call the\n\u003e `cleanup` export manually after each test.\n\n## Usage side-by side with `@testing-library/react` or other tools that use `act` or set `IS_REACT_ACT_ENVIRONMENT`\n\nThis library should not be used with `act`, and it will throw an error if\n`IS_REACT_ACT_ENVIRONMENT` is `true`.\n\nReact Testing Library sets `IS_REACT_ACT_ENVIRONMENT` to `true` globally, and\nwraps some helpers like `userEvent.click` in `act` calls.  \nTo use this library side-by-side with React Testing Library, we ship the\n`disableActEnvironment` helper to undo these changes temporarily.\n\nIt returns a `Disposable` and can be used together with the\n[`using` keyword](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management)\nto automatically clean up once the scope is left:\n\n```ts\ntest('my test', () =\u003e {\n  using _disabledAct = disableActEnvironment()\n\n  // your test code here\n\n  // as soon as this scope is left, the environment will be cleaned up\n})\n```\n\nIf you cannot use `using`, you can also manually call the returned `cleanup`\nfunction. We recommend using `finally` to ensure the act environment is cleaned\nup if your test fails, otherwise it could leak between tests:\n\n```ts\ntest('my test', () =\u003e {\n  const {cleanup} = disableActEnvironment()\n\n  try {\n    // your test code here\n  } finally {\n    cleanup()\n  }\n})\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftesting-library%2Freact-render-stream-testing-library","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftesting-library%2Freact-render-stream-testing-library","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftesting-library%2Freact-render-stream-testing-library/lists"}