UNPKG

mod-arch-shared

Version:

Shared library for modular architecture micro-frontend projects

126 lines 4.76 kB
import { renderHook as renderHookRTL, waitFor, } from '@testing-library/react'; /** * Wrapper on top of RTL `renderHook` returning a result that implements the `RenderHookResultExt` interface. * * `renderHook` provides full control over the rendering of your hook including the ability to wrap the test component. * This is usually used to add context providers from `React.createContext` for the hook to access with `useContext`. * `initialProps` and props subsequently set by `rerender` will be provided to the wrapper. * * ``` * const renderResult = renderHook(({ who }: { who: string }) => useSayHello(who), { initialProps: { who: 'world' }}); * expect(renderResult).hookToBe('Hello world!'); * renderResult.rerender({ who: 'there' }); * expect(renderResult).hookToBe('Hello there!'); * ``` */ export const renderHook = (render, options) => { let updateCount = 0; let prevResult; let currentResult; const renderResult = renderHookRTL((props) => { updateCount++; prevResult = currentResult; currentResult = render(props); return currentResult; }, options); const renderResultExt = { ...renderResult, getPreviousResult: () => (updateCount > 1 ? prevResult : renderResult.result.current), getUpdateCount: () => updateCount, waitForNextUpdate: async (currentOptions) => { const expected = updateCount; try { await waitFor(() => expect(updateCount).toBeGreaterThan(expected), currentOptions); } catch { throw new Error('waitForNextUpdate timed out'); } }, }; return renderResultExt; }; /** * Lightweight API for testing a single hook. * * Prefer this method of testing over `renderHook` for simplicity. * * ``` * const renderResult = testHook(useSayHello)('world'); * expectHook(renderResult).toBe('Hello world!'); * renderResult.rerender('there'); * expectHook(renderResult).toBe('Hello there!'); * ``` */ export const testHook = (hook) => // not ideal to nest functions in terms of API but cannot find a better way to infer P from hook and not initialParams (...initialParams) => { const renderResult = renderHook(({ $params }) => hook(...$params), { initialProps: { $params: initialParams, }, }); return { ...renderResult, rerender: (...params) => renderResult.rerender({ $params: params }), }; }; /** * A helper function for asserting the return value of hooks based on `useFetchState`. * * eg. * ``` * expectHook(renderResult).isStrictEqual(standardUseFetchState('test value', true)) * ``` * is equivalent to: * ``` * expectHook(renderResult).isStrictEqual(['test value', true, undefined, expect.any(Function)]) * ``` */ export const standardUseFetchState = (data, loaded = false, error) => [data, loaded, error, expect.any(Function)]; // create a new asymmetric matcher that matches everything const everything = () => { const r = expect.anything(); r.asymmetricMatch = () => true; return r; }; /** * Extracts a subset of values from the source that can be used to compare equality. * * Recursively traverses the `booleanTarget`. For every property or array index equal to `true`, * adds the value of the source to the result wrapped in custom matcher `expect.isIdentityEqual`. * If the entry is `false` or `undefined`, add an everything matcher to the result. */ export const createComparativeValue = (source, booleanTarget) => createComparativeValueRecursive(source, booleanTarget); const createComparativeValueRecursive = (source, // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type booleanTarget) => { if (typeof booleanTarget === 'boolean') { return booleanTarget ? expect.isIdentityEqual(source) : everything(); } if (Array.isArray(booleanTarget)) { if (Array.isArray(source)) { const r = new Array(source.length).fill(everything()); booleanTarget.forEach((b, i) => { if (b != null) { r[i] = createComparativeValueRecursive(source[i], b); } }); return r; } return undefined; } if (source == null || typeof source === 'string' || typeof source === 'number' || typeof source === 'function') { return source; } const obj = {}; const btObj = booleanTarget; Object.keys(btObj).forEach((key) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any obj[key] = createComparativeValueRecursive(source[key], btObj[key]); }); return expect.objectContaining(obj); }; //# sourceMappingURL=hooks.js.map