import { State, Manager, SubscriptionManager, NetworkManager, PollingSubscription, Controller, } from '@rest-hooks/react'; import React, { memo, Suspense } from 'react'; import { renderHook, act, RenderHookResult } from './renderHook.cjs'; import { createControllerInterceptor } from '../createControllerInterceptor.js'; import { createFixtureMap } from '../createFixtureMap.js'; import { Interceptor, Fixture, FixtureEndpoint } from '../fixtureTypes.js'; import MockResolver from '../MockResolver.js'; import mockInitialState from '../mockState.js'; export type { RenderHookOptions } from './renderHook.cjs'; export default function makeRenderRestHook( Provider: React.ComponentType, ) { /** Wraps dispatches that are typically called declaratively in act() */ class ActController extends Controller { constructor(...args: any) { super(...args); const { setResponse, resolve } = this; this.setResponse = (...args) => { let promise: any; act(() => { promise = setResponse.call(this, ...args); }); return promise; }; this.resolve = (...args) => { let promise: any; act(() => { promise = resolve.call(this, ...args); }); return promise; }; } } const renderRestHook: RenderRestHook = (( callback: (props: P) => R, options?: { initialProps?: P; initialFixtures?: Fixture[]; resolverFixtures?: (Fixture | Interceptor)[]; getInitialInterceptorData?: () => T; wrapper?: React.ComponentType>; }, ): RenderHookResult & { controller: Controller } => { // we want fresh manager state in each instance const managers = [ new NetworkManager(), new SubscriptionManager(PollingSubscription), ]; renderRestHook.cleanup = () => { (managers[0] as any).cleanupDate = Infinity; Object.values( (managers[0] as any).rejectors as Record, ).forEach(rej => { rej(); }); (managers[0] as any).clearAll(); managers.forEach(manager => manager.cleanup()); }; renderRestHook.allSettled = () => { return (managers[0] as NetworkManager).allSettled(); }; const initialState: State = mockInitialState( options?.initialFixtures, ); // TODO: controller provided to middleware should be same as useController() - so pull out the mockresolver stuff and don't actually // use the component here const ProviderWithResolver: React.ComponentType = options ?.resolverFixtures?.length ? memo(function ProviderWithResolver({ children, }: React.PropsWithChildren

) { return ( {children} ); }) : memo(function ProviderWithResolver({ children, }: React.PropsWithChildren

) { return ( {children} ); }); const Wrapper = options?.wrapper; const ProviderWithWrapper = Wrapper ? function ProviderWrapped(props: React.PropsWithChildren

) { return ( ); } : ProviderWithResolver; const wrapper: React.ComponentType = ({ children, ...props }: React.PropsWithChildren

) => ( {children} ); const ret: any = renderHook(callback, { ...options, wrapper, }); const [fixtureMap, interceptors] = createFixtureMap( options?.resolverFixtures, ); ret.controller = createControllerInterceptor( (managers[0] as any).controller, fixtureMap, interceptors, options?.getInitialInterceptorData ?? (() => ({})), !options?.resolverFixtures?.length, ); return ret; }) as any; renderRestHook.cleanup = () => {}; renderRestHook.allSettled = () => Promise.allSettled([]); return renderRestHook; } interface ProviderProps { children: React.ReactNode; managers: Manager[]; initialState: State; Controller: typeof Controller; } type RenderRestHook = (( callback: (props: P) => R, options?: { initialProps?: P; initialFixtures?: readonly Fixture[]; readonly resolverFixtures?: readonly (Fixture | Interceptor)[]; wrapper?: React.ComponentType>; }, ) => RenderHookResult & { controller: Controller; }) & { cleanup: () => void; allSettled: () => Promise[]> | undefined; };