Interagindo com o componente Select

Por não ser um select nativo, a interação com o componente <Select /> do Trangram em um teste automatizado pode ser um pouco desafiadora.

Tentativa 1 (getByRole)

Quem está familiarizado com a biblioteca Testing Library, está acostumado a escrever uma interação bem simples para selecionar uma das opções exibidas por um select:

await userEvent.selectOptions(
  screen.getByRole('combobox', { name: 'Cor' }),
  'Azul'
)

Porém, ao tentar usar essa estratégia em um <Select /> do Tangram, você vai se deparar com o seguinte erro:

TestingLibraryElementError: Unable to find an accessible element with the role "combobox" and name "Cor"

Isso ocorre porque o <Select /> do Tangram não é exatamente um select, mas sim um button.

Tentativa 2 (getByLabelText)

Tendo descoberto essa diferença de role, você poderia tentar uma outra estratégia no teste: obter o <Select /> pelo seu label ao invés de seu role:

await userEvent.selectOptions(
  screen.getByLabelText('Cor'),
  'Azul'
)

Ao usar esta segunda estratégia, você vai se deparar com o seguinte erro:

TestingLibraryElementError: Found a label with the text of: Cor, however the element associated with this label (<div />) is non-labellable [https://html.spec.whatwg.org/multipage/forms.html#category-label]. If you really need to label a <div />, you can use aria-label or aria-labelledby instead.

Isso ocorre porque o <Form.Label /> não se comunica corretamente com o elemento que recebe o id informado em seu atributo htmlFor — no nosso caso, o <Select />.

Solução

Diante de todas estas peculiaridades e ainda que pareça contraintuitivo, a maneira mais eficiente de interagir com um <Select /> do Tangram nos testes automatizados é usando o role que de fato lhe foi atribuído: button:

// 1. Clique no select como se ele fosse um botão usando
// o texto de algum valor já selecionado ou do placeholder
await userEvent.click(
  screen.getByRole('button', {
    name: new RegExp('Selecione uma cor'),
  }),
)

// 2. Clique em uma das opções pelo role
await userEvent.click(
  screen.getByRole('option', {
    name: 'Azul',
  }),
)

// 3. Podemos confirmar que a opção foi de fato
// escolhida verificando o novo texto associado
// ao botão que representa o <Select />
expect(
  screen.getByRole('button', {
    name: new RegExp('Azul'),
  }),
).toBeInTheDocument()
// Atenção: Esse último assert provavelmente não precisará
// ser feito em seu teste, já que o valor do <Select/ >
// acabará sendo garantido no assert que verifica os
// dados enviados pelo formulário em questão.

Dica: Na vida real, uma tela costuma ter muito mais elementos que apenas um <Select />, portanto, a fim de evitar uma eventual colisão com os demais elementos, pode ser útil restringir a query ao <Form.Control /> ao qual o <Select /> pertence, usando a função utilitária within:

const colorSelectFormControl = container.querySelector(
  '#colorSelectFormControl',
)
await userEvent.click(
  within(colorSelectFormControl).getByRole('button', {
    name: new RegExp('Selecione uma cor'),
  }),
)

Você pode conferir a implementação completa deste teste aqui.