import * as ReactTestingLibrary from "@testing-library/react";
import type { ReactNode } from "react";
import { StrictMode } from "react";
import { flushMicrotasks, nextFrame, wrapAsync } from "./__utils.ts";
export * from "./index.ts";
/**
* Options for the `render` function. Accepts every option from Testing Library's
* `render` (except `queries`), plus `strictMode` to wrap the rendered UI in
* React's `StrictMode`.
* @example
* ```tsx
* const options: RenderOptions = { strictMode: true };
* await render(, options);
* ```
*/
export interface RenderOptions extends Omit<
ReactTestingLibrary.RenderOptions,
"queries"
> {
strictMode?: boolean;
}
function wrapRender any>(
renderFn: T,
): Promise> {
return wrapAsync(async () => {
const output: ReturnType = renderFn();
await flushMicrotasks();
await nextFrame();
await flushMicrotasks();
return output;
});
}
/**
* Renders a React element into the document for testing, waiting for effects and
* the next frame to flush before resolving.
*
* Built on Testing Library's `render`, it returns `unmount` to remove the tree and
* an async `rerender` to update it with new UI. Pass `strictMode: true` to wrap
* the element in React's `StrictMode`, or any other Testing Library render option.
* @example
* ```tsx
* const { rerender, unmount } = await render();
* await click(q.button("Submit"));
* await rerender();
* unmount();
* ```
*/
export async function render(ui: ReactNode, options?: RenderOptions) {
const { strictMode, wrapper: Wrapper, ...renderOptions } = options ?? {};
const wrapper = (props: { children: ReactNode }) => {
const element = Wrapper ? : props.children;
if (!strictMode) return element;
return {element};
};
return wrapRender(() => {
const { unmount, rerender } = ReactTestingLibrary.render(ui, {
...renderOptions,
wrapper,
});
return {
unmount,
rerender: (newUi: ReactNode) => wrapRender(() => rerender(newUi)),
};
});
}