11import { RenderHookOptions } from "@testing-library/react" ;
22import {
33 createProfiler ,
4+ NextRenderOptions ,
45 ProfiledComponentFields ,
56 ValidSnapshot ,
67} from "./profile/profile.js" ;
78import { Render } from "./profile/Render.js" ;
89import { createElement } from "react" ;
9-
10- type StringReplaceRenderWithSnapshot < T extends string > =
11- T extends `${infer Pre } Render${infer Post } ` ? `${Pre } Snapshot${Post } ` : T ;
12-
13- type ResultReplaceRenderWithSnapshot < T > = T extends (
14- ...args : infer Args
15- ) => Render < infer Snapshot >
16- ? ( ...args : Args ) => Snapshot
17- : T extends ( ...args : infer Args ) => Promise < Render < infer Snapshot > >
18- ? ( ...args : Args ) => Promise < Snapshot >
19- : T ;
20-
21- type ProfiledHookFields < ReturnValue > =
22- ProfiledComponentFields < ReturnValue > extends infer PC
23- ? {
24- [ K in keyof PC as StringReplaceRenderWithSnapshot <
25- K & string
26- > ] : ResultReplaceRenderWithSnapshot < PC [ K ] > ;
27- }
28- : never ;
10+ import { Assertable , assertableSymbol , markAssertable } from "./assertable.js" ;
2911
3012/** @internal */
31- export interface ProfiledHook < Props , ReturnValue extends ValidSnapshot >
32- extends ProfiledHookFields < ReturnValue > {
33- //Profiler: RenderStream<ReturnValue>;
13+ export interface ProfiledHook < Snapshot extends ValidSnapshot >
14+ extends Assertable {
15+ /**
16+ * An array of all renders that have happened so far.
17+ * Errors thrown during component render will be captured here, too.
18+ */
19+ renders : Array <
20+ Render < Snapshot > | { phase : "snapshotError" ; count : number ; error : unknown }
21+ > ;
22+ /**
23+ * Peeks the next render from the current iterator position, without advancing the iterator.
24+ * If no render has happened yet, it will wait for the next render to happen.
25+ * @throws {WaitForRenderTimeoutError } if no render happens within the timeout
26+ */
27+ peekSnapshot ( options ?: NextRenderOptions ) : Promise < Snapshot > ;
28+ /**
29+ * Iterates to the next render and returns it.
30+ * If no render has happened yet, it will wait for the next render to happen.
31+ * @throws {WaitForRenderTimeoutError } if no render happens within the timeout
32+ */
33+ takeSnapshot : Assertable &
34+ ( ( options ?: NextRenderOptions ) => Promise < Snapshot > ) ;
35+ /**
36+ * Returns the total number of renders.
37+ */
38+ totalSnapshotCount ( ) : number ;
39+ /**
40+ * Returns the current render.
41+ * @throws {Error } if no render has happened yet
42+ */
43+ getCurrentSnapshot ( ) : Snapshot ;
44+ /**
45+ * Waits for the next render to happen.
46+ * Does not advance the render iterator.
47+ */
48+ waitForNextSnapshot ( options ?: NextRenderOptions ) : Promise < Snapshot > ;
49+ }
50+
51+ interface HookSnapshotStream < Props , ReturnValue extends ValidSnapshot >
52+ extends ProfiledHook < ReturnValue > ,
53+ Assertable {
54+ rerender : ( rerenderCallbackProps : Props ) => void ;
55+ unmount : ( ) => void ;
3456}
3557
3658export function renderHookToSnapshotStream <
@@ -39,13 +61,7 @@ export function renderHookToSnapshotStream<
3961> (
4062 renderCallback : ( props : Props ) => ReturnValue ,
4163 { initialProps, ...options } : RenderHookOptions < Props > = { }
42- ) : [
43- stream : ProfiledHook < Props , ReturnValue > ,
44- renderResult : {
45- rerender : ( rerenderCallbackProps : Props ) => void ;
46- unmount : ( ) => void ;
47- } ,
48- ] {
64+ ) : HookSnapshotStream < Props , ReturnValue > {
4965 const { render, ...stream } = createProfiler < ReturnValue > ( ) ;
5066
5167 const ProfiledHook : React . FC < Props > = ( props ) => {
@@ -62,26 +78,23 @@ export function renderHookToSnapshotStream<
6278 return baseRerender ( createElement ( ProfiledHook , rerenderCallbackProps ) ) ;
6379 }
6480
65- return [
66- Object . assign ( { } , stream , {
67- renders : stream . renders ,
68- totalSnapshotCount : stream . totalRenderCount ,
69- async peekSnapshot ( options ) {
70- return ( await stream . peekRender ( options ) ) . snapshot ;
71- } ,
72- async takeSnapshot ( options ) {
73- return ( await stream . takeRender ( options ) ) . snapshot ;
74- } ,
75- getCurrentSnapshot ( ) {
76- return stream . getCurrentRender ( ) . snapshot ;
77- } ,
78- async waitForNextSnapshot ( options ) {
79- return ( await stream . waitForNextRender ( options ) ) . snapshot ;
80- } ,
81- } satisfies ProfiledHookFields < ReturnValue > ) ,
82- {
83- rerender,
84- unmount,
81+ return {
82+ [ assertableSymbol ] : stream ,
83+ renders : stream . renders ,
84+ totalSnapshotCount : stream . totalRenderCount ,
85+ async peekSnapshot ( options ) {
86+ return ( await stream . peekRender ( options ) ) . snapshot ;
87+ } ,
88+ takeSnapshot : markAssertable ( async function takeSnapshot ( options ) {
89+ return ( await stream . takeRender ( options ) ) . snapshot ;
90+ } , stream ) ,
91+ getCurrentSnapshot ( ) {
92+ return stream . getCurrentRender ( ) . snapshot ;
8593 } ,
86- ] ;
94+ async waitForNextSnapshot ( options ) {
95+ return ( await stream . waitForNextRender ( options ) ) . snapshot ;
96+ } ,
97+ rerender,
98+ unmount,
99+ } ;
87100}
0 commit comments