Leonardo Giraldi Moreno Giuranno
Posted on August 7, 2024
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".
Posted on August 7, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.