Skip to content

Commit

Permalink
A test renderer for React hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
steveluscher committed Feb 7, 2024
1 parent cd39736 commit ac9ee8f
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
"jest-runner-prettier": "^1.0.0",
"prettier": "^3.1",
"react": "^18",
"react-error-boundary": "^4.0.12",
"react-test-renderer": "^18",
"test-config": "workspace:*",
"tsconfig": "workspace:*",
Expand Down
70 changes: 70 additions & 0 deletions packages/react/src/test-renderer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import { act, create, ReactTestRenderer } from 'react-test-renderer';

type Result<T> =
| {
__type: 'error';
current: Error;
reset: () => void;
}
| {
__type: 'result';
current?: T;
};

type TestComponentProps<THookReturn> = {
executor(): THookReturn;
resultRef: Result<THookReturn>;
};

function TestComponentHookRenderer<THookFn extends (...args: never) => unknown>({
executor,
resultRef,
}: TestComponentProps<ReturnType<THookFn>>) {
resultRef.current = executor();
return null;
}

function TestComponent<THookFn extends (...args: never) => unknown>({
executor,
resultRef,
}: TestComponentProps<ReturnType<THookFn>>) {
return (
<ErrorBoundary
fallbackRender={({ error, resetErrorBoundary }) => {
resultRef.__type = 'error';
resultRef.current = error;
(resultRef as Extract<typeof resultRef, { __type: 'error' }>).reset = resetErrorBoundary;
return null;
}}
onReset={() => {
resultRef.__type = 'result';
delete resultRef.current;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
delete (resultRef as any).reset;
}}
>
<TestComponentHookRenderer executor={executor} resultRef={resultRef} />
</ErrorBoundary>
);
}

export function renderHook<THookReturn>(executor: () => THookReturn): {
rerenderHook(nextExecutor: () => THookReturn): void;
result: Readonly<Result<THookReturn>>;
} {
const result = { __type: 'result' } as Result<THookReturn>;
let testRenderer: ReactTestRenderer;
act(() => {
testRenderer = create(<TestComponent executor={executor} resultRef={result} />);
});
return {
rerenderHook(nextExecutor) {
act(() => {
testRenderer.update(<TestComponent executor={nextExecutor} resultRef={result} />);
});
},
result,
};
}
12 changes: 12 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit ac9ee8f

Please sign in to comment.