Pular para o conteúdo principal

renderDataHook()

renderDataHook() é útil para testar hooks que dependem do Reactive Data Client. Ele espelha o renderHook() do @testing-library/react-hooks, mas o faz com um boundary <Suspense/> e também dentro de um contexto <Provider />.

observação

renderDataHook() cria um contexto Provider com novas instâncias de manager. Isso significa que cada chamada a renderDataHook() resultará em um estado de cache totalmente novo, assim como em um estado de manager novo.

Tipo
type RenderDataHook = {
<P, R, T = any>(
callback: (props: P) => R,
options?: {
initialProps?: P;
initialFixtures?: Fixture[];
resolverFixtures?: (Fixture | Interceptor<T>)[];
getInitialInterceptorData?: () => T;
wrapper?: React.ComponentType;
},
): {
rerender: (props?: Props) => void;
result: {
current: Result;
error?: Error;
};
unmount: () => void;
controller: Controller;
cleanup(): void;
allSettled(): Promise<unknown>;
/* @deprecated */
waitForNextUpdate: (options?: waitForOptions) => Promise<void>;
waitFor<T>(
callback: () => Promise<T> | T,
options?: waitForOptions,
): Promise<T>;
};
/** cleanup is automatic; only needed for ordering (e.g., before jest.useRealTimers()) */
cleanup(): void;
allSettled(): Promise<unknown>;
};

Uso​

import { useSuspense } from '@data-client/react';
import { renderDataHook } from '@data-client/test';
import { Article, ArticleResource } from './resources/Article';

const response = {
id: 5,
title: 'hi ho',
content: 'whatever',
tags: ['a', 'best', 'react'],
};

it('useSuspense() should render the response', async () => {
const { result, waitFor } = renderDataHook(
() => {
return useSuspense(ArticleResource.get, { id: 5 });
},
{
initialFixtures: [
{
endpoint: ArticleResource.get,
args: [{ id: 5 }],
response,
},
],
},
);
expect(result.current instanceof Article).toBe(true);
expect(result.current.title).toBe(response.title);
});

Argumentos​

callback​

Hook a ser executado dentro do React. O valor de retorno ficará disponível em result.current

options.initialFixtures​

Pode ser usado para preencher previamente o cache se o teste espera que os valores do cache já estejam preenchidos. Recebe um array de fixtures

Isso tem o mesmo efeito de inicializar <DataProvider /> com mockInitialState()

options.resolverFixtures​

Estas fixtures ou interceptors são usadas para resolver quaisquer novas requisições. Isso é mais útil para simular fetches imperativos, como mutações, mas também permite testar estados de suspensão ou transições.

Funciona adicionando o MockResolver como wrapper.

options.getInitialInterceptorData​

Função que inicializa o atributo this de todos os interceptors.

options.initialProps​

Os valores iniciais a passar para a função de callback

options.wrapper​

Passe um componente React na opção wrapper para que ele seja renderizado ao redor do elemento interno

Retorno​

controller​

Controller para despachar efeitos imperativos

import { act } from '@testing-library/react';
import { useSuspense } from '@data-client/react';
import { renderDataHook } from '@data-client/test';
import { Todo, TodoResource } from './resources/Todo';

it('should update', async () => {
const id = 5;
const payload = { title: 'first item', id, completed: false };
const { result, controller } = renderDataHook(
() => {
return useSuspense(TodoResource.getList);
},
{
initialFixtures: [
{
endpoint: TodoResource.getList,
args: [],
response: [payload],
},
],
resolverFixtures: [
{
endpoint: TodoResource.update,
response: ({ id }, body) => ({ ...body, id }),
},
],
},
);
expect(result.current).toEqual([Todo.fromJS(payload)]);
await act(async () => {
await controller.fetch(
TodoResource.update,
{ id },
{ title: 'updated title' },
);
});
expect(result.current[0].title).toBe('updated title');
});

cleanup()​

Limpa todos os managers usados nesta renderização. Isso é especialmente importante ao simular timers, pois o funcionamento interno do Reactive Data Client depende de timers reais para evitar race conditions.

A limpeza é executada automaticamente após cada teste por meio de um hook afterEach em nível de módulo (semelhante ao @testing-library/react). Chamadas manuais só são necessárias quando você precisa controlar a ordem da limpeza dentro do corpo de um teste -- por exemplo, limpar antes de trocar de timers falsos para timers reais:

it('should handle polling', async () => {
jest.useFakeTimers();
const { result } = renderDataHook(/* ... */);
// ... assertions ...
renderDataHook.cleanup(); // must run while fake timers are still active
jest.useRealTimers();
});

allSettled()​

Retorna uma promise que é resolvida assim que todas as requisições em andamento forem concluídas.

Também disponível no valor de retorno de cada chamada de renderDataHook().

result​

  • current (any) - o valor de retorno da função callback
  • error (Error) - o erro lançado caso a função callback tenha lançado um erro durante a renderização

waitFor​

Retorna uma Promise que é resolvida se o callback fornecido for executado sem exceção e retornar um valor truthy ou undefined. É seguro usar o resultado de renderDataHook no callback para fazer asserções ou testar valores.

waitForNextUpdate​

Descontinuado

Use waitFor em vez disso

Retorna uma Promise que é resolvida na próxima vez que o hook renderizar, normalmente quando o estado é atualizado como resultado de uma ação assíncrona.

rerender​

(function([newProps])) - função para rerrenderizar o componente de teste, incluindo quaisquer hooks chamados na função callback. Se newProps forem passadas, elas substituirão as initialProps passadas à função callback nas renderizações seguintes.

unmount​

(function()) - função para desmontar o componente de teste, normalmente usada para disparar os efeitos de limpeza dos hooks useEffect.

Exemplos​

import { useSuspense } from '@data-client/react';
import { renderDataHook } from '@data-client/test';
import { Article, ArticleResource } from './resources/Article';

const response = {
id: 5,
title: 'hi ho',
content: 'whatever',
tags: ['a', 'best', 'react'],
};

it('should resolve useSuspense()', async () => {
const { result, waitFor, controller } = renderDataHook(
() => {
return useSuspense(ArticleResource.get, response);
},
{
resolverFixtures: [
{
endpoint: ArticleResource.get,
response: ({ id }) => ({ ...response, id }),
},
{
endpoint: ArticleResource.partialUpdate,
response: ({ id }, body) => ({ ...body, id }),
},
],
},
);
// this indicates suspense
expect(result.current).toBeUndefined();
await waitFor(() => expect(result.current).toBeDefined());
expect(result.current instanceof Article).toBe(true);
expect(result.current.title).toBe(response.title);
await controller.fetch(
ArticleResource.partialUpdate,
{ id: response.id },
{ title: 'updated title' },
);
expect(result.current.title).toBe('updated title');
});