Simplificando testes com react-select-event

Simplificando testes com react-select-event

A squad em que atuo faz uso do componente react-select para apresentação de campos de seleção de alguns formulários em nossas aplicações frontend. Encontramos dificuldades para implementar testes utilizando a biblioteca React Testing Library que validassem o comportamento deste campo complexo em nossos componentes.

Estávamos seguindo por uma abordagem de tentar manipular os elementos gerados pelos componentes fornecidos pelo react-select através dos métodos utilitários do React Testing Library. Porém, não obtivemos sucesso e o código dos testes estavam cada vez mais obtendo uma complexidade desnecessária.

Ao consultar a documentação do próprio React Testing Library, mais especificamente na seção do ecossistema da biblioteca, nos deparamos com a lib “react-select-event”:

Segundo sua descrição: “react-select-event é uma biblioteca complementar para a React Testing Library que fornece métodos auxiliares para interagir com elementos react-select.“.

EUREKA!

No exemplo apresentado logo de cara, tínhamos um campo de seleção simples, que tinha como “fonte de dados” um simples array que era passado via prop options:

Este exemplo não era suficiente para nós, uma vez que nossos campos react-select consultam uma API para retornar a lista de opções com base no que foi digitado pelo usuário, ou seja, temos um cenário assíncrono em questão.

Acessamos então o repositório no GitHub da biblioteca em busca de mais exemplos.

No repositório, encontramos diversos exemplos de implementação, inclusive aquele que precisávamos:

Vamos dar uma olhada em um exemplo prático:

Considere o seguinte componente que implementa um formulário simples com apenas um campo de seleção:

import { useState } from react;
import AsyncSelect from react-select/async;

import { ColourOption, colourOptions } from ../data;
import { SingleValue } from react-select;

const Form: React.FC = () => {
const [selectedOption, setSelectedOption] = useState<string>();

const filterColors = (inputValue: string) => {
return colourOptions.filter((i) =>
i.label.toLowerCase().includes(inputValue.toLowerCase())
);
};

const promiseOptions = (inputValue: string) =>
new Promise<ColourOption[]>((resolve) => {
setTimeout(() => {
resolve(filterColors(inputValue));
}, 1000);
});

const handleChange = (option: SingleValue<ColourOption>) => {
if (option) {
setSelectedOption(option.label);
}
};

return (
<form>
<label htmlFor=color>Cor</label>
<AsyncSelect
defaultOptions
loadOptions={promiseOptions}
name=color
onChange={handleChange}
inputId=color
/>
<p>Cor selecionada: {selectedOption}</p>
</form>
);
};

export { Form };

Utilizando somente os recursos disponíveis no React Testing Library, seguiríamos pelo caminho de tentar manipular diretamente o valor do input:

import { fireEvent, render, screen } from @testing-library/react;

import { Form } from ../../components/Form;

describe(Form, () => {
it(should present selected color, async () => {
render(<Form />);

const input = screen.getByLabelText(Cor);
fireEvent.change(input, { target: { value: purple } });

expect(
await screen.findByText(Cor selecionada: Purple)
).toBeInTheDocument();
});
});

Ou tentaríamos simular o preenchimento do campo e, após, a seleção da opção via seta para baixo e tecla enter:

import { fireEvent, render, screen } from @testing-library/react;

import { Form } from ../../components/Form;

describe(Form, () => {
it(should present selected color, async () => {
render(<Form />);

const input = screen.getByLabelText(Cor);
fireEvent.change(input, { target: { value: purple } });
fireEvent.keyDown(input, { key: ArrowDown, code: ArrowDown });
fireEvent.keyDown(input, { key: Enter, code: Enter });

expect(
await screen.findByText(Cor selecionada: Purple)
).toBeInTheDocument();
});
});

Estes foram os caminhos que tentamos trilhar. Porém, somente tivemos êxito quando adotamos o react-select-event. Com ele, o teste fica assim:

import { render, screen } from @testing-library/react;
import selectEvent from react-select-event;

import { Form } from ../../components/Form;

describe(Form, () => {
it(should present selected color, async () => {
render(<Form />);

const input = screen.getByLabelText(Cor);
await selectEvent.select(input, Purple);

expect(
await screen.findByText(Cor selecionada: Purple)
).toBeInTheDocument();
});
});

A biblioteca ainda disponibiliza outros métodos além do select que podem atender a outros cenários de teste que você necessite implementar:

Depois desta pequena jornada, ficamos com uma lição: “se a solução está ficando complexa, é grande a chance de estarmos trilhando o caminho errado”.

Please follow and like us:
Pin Share