From 016741469a0c5449cb81241adb501d86f036ece6 Mon Sep 17 00:00:00 2001 From: Jerel Miller Date: Mon, 13 Jan 2025 13:38:34 -0700 Subject: [PATCH 1/5] fix: Make props optional in `rerender` function returned from `renderHookToSnapshotStream` --- src/renderHookToSnapshotStream.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/renderHookToSnapshotStream.tsx b/src/renderHookToSnapshotStream.tsx index ecf62d382..a5f2afa9b 100644 --- a/src/renderHookToSnapshotStream.tsx +++ b/src/renderHookToSnapshotStream.tsx @@ -41,7 +41,7 @@ export interface SnapshotStream extends Assertable { * Does not advance the render iterator. */ waitForNextSnapshot(options?: NextRenderOptions): Promise - rerender: (rerenderCallbackProps: Props) => Promise + rerender: (rerenderCallbackProps?: Props) => Promise unmount: () => void } @@ -51,17 +51,17 @@ export async function renderHookToSnapshotStream( ): Promise> { const {render, ...stream} = createRenderStream<{value: ReturnValue}, never>() - const HookComponent: React.FC<{arg: Props}> = props => { - stream.replaceSnapshot({value: renderCallback(props.arg)}) + const HookComponent: React.FC<{arg: Props | undefined}> = props => { + stream.replaceSnapshot({value: renderCallback(props.arg!)}) return null } const {rerender: baseRerender, unmount} = await render( - , + , renderOptions, ) - function rerender(rerenderCallbackProps: Props) { + function rerender(rerenderCallbackProps?: Props) { return baseRerender() } From ca944cd0bee7a811f1b47340345f2ab5eb2ac4a7 Mon Sep 17 00:00:00 2001 From: Lenz Weber-Tronic Date: Thu, 16 Jan 2025 14:19:30 +0100 Subject: [PATCH 2/5] less intrusive method --- .../renderHookToSnapshotStream.test.tsx | 40 ++++++++++++++++++- src/renderHookToSnapshotStream.tsx | 13 +++--- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/__tests__/renderHookToSnapshotStream.test.tsx b/src/__tests__/renderHookToSnapshotStream.test.tsx index 108da49e8..bc02b925b 100644 --- a/src/__tests__/renderHookToSnapshotStream.test.tsx +++ b/src/__tests__/renderHookToSnapshotStream.test.tsx @@ -3,7 +3,10 @@ import {EventEmitter} from 'node:events' import {scheduler} from 'node:timers/promises' import {test, expect} from '@jest/globals' -import {renderHookToSnapshotStream} from '@testing-library/react-render-stream' +import { + renderHookToSnapshotStream, + SnapshotStream, +} from '@testing-library/react-render-stream' import * as React from 'react' const testEvents = new EventEmitter<{ @@ -72,3 +75,38 @@ test.each<[type: string, initialValue: unknown, ...nextValues: unknown[]]>([ expect(await takeSnapshot()).toBe(nextValue) } }) + +test.skip('type test: render function without an argument -> no argument required for `rerender`', async () => { + { + // prop type has nothing to infer on - defaults to `void` + const stream = await renderHookToSnapshotStream(() => {}) + const _test1: SnapshotStream = stream + // @ts-expect-error should not be assignable + const _test2: SnapshotStream = stream + await stream.rerender() + // @ts-expect-error invalid argument + await stream.rerender('foo') + } + { + // prop type is implicitly set via the render function argument + const stream = await renderHookToSnapshotStream((_arg1: string) => {}) + // @ts-expect-error should not be assignable + const _test1: SnapshotStream = stream + const _test2: SnapshotStream = stream + // @ts-expect-error missing argument + await stream.rerender() + await stream.rerender('foo') + } + { + // prop type is implicitly set via the initialProps argument + const stream = await renderHookToSnapshotStream(() => {}, { + initialProps: 'initial', + }) + // @ts-expect-error should not be assignable + const _test1: SnapshotStream = stream + const _test2: SnapshotStream = stream + // @ts-expect-error missing argument + await stream.rerender() + await stream.rerender('foo') + } +}) diff --git a/src/renderHookToSnapshotStream.tsx b/src/renderHookToSnapshotStream.tsx index a5f2afa9b..6fb4d5502 100644 --- a/src/renderHookToSnapshotStream.tsx +++ b/src/renderHookToSnapshotStream.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-invalid-void-type */ import {type RenderHookOptions} from '@testing-library/react/pure.js' import React from 'rehackt' import {createRenderStream} from './renderStream/createRenderStream.js' @@ -41,27 +42,27 @@ export interface SnapshotStream extends Assertable { * Does not advance the render iterator. */ waitForNextSnapshot(options?: NextRenderOptions): Promise - rerender: (rerenderCallbackProps?: Props) => Promise + rerender: (rerenderCallbackProps: Props) => Promise unmount: () => void } -export async function renderHookToSnapshotStream( +export async function renderHookToSnapshotStream( renderCallback: (props: Props) => ReturnValue, {initialProps, ...renderOptions}: RenderHookOptions = {}, ): Promise> { const {render, ...stream} = createRenderStream<{value: ReturnValue}, never>() - const HookComponent: React.FC<{arg: Props | undefined}> = props => { - stream.replaceSnapshot({value: renderCallback(props.arg!)}) + const HookComponent: React.FC<{arg: Props}> = props => { + stream.replaceSnapshot({value: renderCallback(props.arg)}) return null } const {rerender: baseRerender, unmount} = await render( - , + , renderOptions, ) - function rerender(rerenderCallbackProps?: Props) { + function rerender(rerenderCallbackProps: Props) { return baseRerender() } From 125098a9180d10ce43280bae5e0adb91ef2fe3a3 Mon Sep 17 00:00:00 2001 From: Lenz Weber-Tronic Date: Thu, 16 Jan 2025 14:20:17 +0100 Subject: [PATCH 3/5] remove eslint hint --- src/renderHookToSnapshotStream.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/renderHookToSnapshotStream.tsx b/src/renderHookToSnapshotStream.tsx index 6fb4d5502..ed72b7141 100644 --- a/src/renderHookToSnapshotStream.tsx +++ b/src/renderHookToSnapshotStream.tsx @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-invalid-void-type */ import {type RenderHookOptions} from '@testing-library/react/pure.js' import React from 'rehackt' import {createRenderStream} from './renderStream/createRenderStream.js' From d5588de0c40937ef49dc3ba255b83d2577fc7ec7 Mon Sep 17 00:00:00 2001 From: Lenz Weber-Tronic Date: Thu, 16 Jan 2025 14:28:36 +0100 Subject: [PATCH 4/5] also handle optional arguments --- src/__tests__/renderHookToSnapshotStream.test.tsx | 10 ++++++++++ src/renderHookToSnapshotStream.tsx | 12 +++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/__tests__/renderHookToSnapshotStream.test.tsx b/src/__tests__/renderHookToSnapshotStream.test.tsx index bc02b925b..c42f3a10a 100644 --- a/src/__tests__/renderHookToSnapshotStream.test.tsx +++ b/src/__tests__/renderHookToSnapshotStream.test.tsx @@ -109,4 +109,14 @@ test.skip('type test: render function without an argument -> no argument require await stream.rerender() await stream.rerender('foo') } + { + // argument is optional + const stream = await renderHookToSnapshotStream((_arg1?: string) => {}) + + const _test1: SnapshotStream = stream + const _test2: SnapshotStream = stream + const _test3: SnapshotStream = stream + await stream.rerender() + await stream.rerender('foo') + } }) diff --git a/src/renderHookToSnapshotStream.tsx b/src/renderHookToSnapshotStream.tsx index ed72b7141..d29b034bf 100644 --- a/src/renderHookToSnapshotStream.tsx +++ b/src/renderHookToSnapshotStream.tsx @@ -41,10 +41,20 @@ export interface SnapshotStream extends Assertable { * Does not advance the render iterator. */ waitForNextSnapshot(options?: NextRenderOptions): Promise - rerender: (rerenderCallbackProps: Props) => Promise + rerender: (rerenderCallbackProps: VoidOptionalArg) => Promise unmount: () => void } +/** + * if `Arg` can be `undefined`, replace it with `void` to make type represent an optional argument in a function argument position + */ +type VoidOptionalArg = Arg extends any // distribute members of a potential `Props` union + ? undefined extends Arg + ? // eslint-disable-next-line @typescript-eslint/no-invalid-void-type + void + : Arg + : Arg + export async function renderHookToSnapshotStream( renderCallback: (props: Props) => ReturnValue, {initialProps, ...renderOptions}: RenderHookOptions = {}, From ac0b317e22380e18867318c8002371ddc8ebd66e Mon Sep 17 00:00:00 2001 From: Lenz Weber-Tronic Date: Thu, 16 Jan 2025 14:29:52 +0100 Subject: [PATCH 5/5] fix last type errors --- src/renderHookToSnapshotStream.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/renderHookToSnapshotStream.tsx b/src/renderHookToSnapshotStream.tsx index d29b034bf..c911232de 100644 --- a/src/renderHookToSnapshotStream.tsx +++ b/src/renderHookToSnapshotStream.tsx @@ -71,8 +71,9 @@ export async function renderHookToSnapshotStream( renderOptions, ) - function rerender(rerenderCallbackProps: Props) { - return baseRerender() + function rerender(rerenderCallbackProps: VoidOptionalArg) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + return baseRerender() } return {