Back to Mantine

Combobox Testing

apps/help.mantine.dev/src/pages/q/combobox-testing.mdx

9.4.06.1 KB
Original Source

import { Layout } from '@/layout';

export const meta = { title: 'How can I test Select/MultiSelect components?', description: 'Learn how to use react-testing-library to test Select and MultiSelect components.', slug: 'combobox-testing', category: 'testing', tags: [ 'select', 'multi-select', 'tags-input', 'TagsInput', 'autocomplete', 'combobox', 'jest', 'vitest', ], created_at: 'March 15, 2024', last_updated_at: 'March 15, 2024', };

export default Layout(meta);

Getting started

Before jumping into the testing part, make sure that you've configured Jest or Vitest in your project as specified in the documentation. Assume that the render, screen, and userEvent variables are imported from your project's test-utils file.

This guide is applicable to:

Selecting one option (Select, Autocomplete)

To select one of the options in the Select or Autocomplete components, you need to:

  1. Click the input to open the options list
  2. Click the option you want to select

Note that:

  • If you use an array of strings in the data prop, options will have the same value and label
  • It is recommended to set name attribute on all form components that you are planning to test
tsx
import { Select } from '@mantine/core';

function MyForm() {
  return (
    <Select
      name="age"
      label="Select your age"
      data={[
        { value: 'ok', label: 'I am 18 or older' },
        { value: 'not-ok', label: 'I am under 18' },
      ]}
    />
  );
}

it('selects option', () => {
  render(<MyForm />);

  // Click the Select to open the options list
  // Note that the dropdown is closed when one of the options is selected
  // If you want to select several options one after another,
  // you need to click the input again to open the dropdown
  await userEvent.click(screen.getByRole('textbox', { name: 'Select your age' }));

  // Get the option by its label and click it
  await userEvent.click(screen.getByRole('option', { name: 'I am 18 or older' }));

  // Verify that the option is selected
  // This is what the user sees in the input
  expect(screen.getByRole('textbox')).toHaveValue('I am 18 or older');

  // This is what will be submitted with the form
  expect(document.querySelector('input[name="age"]')).toHaveValue('ok');
});

Selecting multiple options (MultiSelect, TagsInput)

Selecting options in the MultiSelect and TagsInput components is similar to Select and Autocomplete. The main difference is that the dropdown does not close when one of the options is selected, so you can select several options one after another without clicking the input again.

tsx
import { MultiSelect } from '@mantine/core';

function MyForm() {
  return (
    <MultiSelect
      name="groceries"
      label="Select groceries"
      data={[
        { value: 'banana', label: 'Banana' },
        { value: 'apple', label: 'Apple' },
        { value: 'orange', label: 'Orange' },
      ]}
    />
  );
}

it('selects multiple options', () => {
  render(<MyForm />);

  // Click the Select to open the options list
  // Note that unlike Select, MultiSelect does not close the dropdown when one of the options is selected
  // You can select several options one after another without clicking the input again
  await userEvent.click(screen.getByRole('textbox', { name: 'Select groceries' }));

  // Click several options to select them
  await userEvent.click(screen.getByRole('option', { name: 'Banana' }));
  await userEvent.click(screen.getByRole('option', { name: 'Apple' }));

  // The best way to verify that options are selected is to check the hidden input value
  expect(document.querySelector('input[name="groceries"]')).toHaveValue('banana,apple');
});

Searching

You can verify that the component is searchable by typing search query and checking that only relevant options are visible.

tsx
import { MultiSelect } from '@mantine/core';

function MyForm() {
  return (
    <MultiSelect
      name="groceries"
      searchable
      label="Select groceries"
      data={[
        { value: 'banana', label: 'Banana' },
        { value: 'apple', label: 'Apple' },
        { value: 'orange', label: 'Orange' },
      ]}
    />
  );
}

it('searches for options', () => {
  render(<MyForm />);

  // Click Select to open the options list
  await userEvent.click(screen.getByRole('textbox', { name: 'Select groceries' }));

  // Type search query
  await userEvent.type(screen.getByRole('textbox', { name: 'Select groceries' }), 'banana');

  // Verify that only one option is visible
  expect(screen.getByRole('option', { name: 'Banana' })).toBeVisible();
  expect(screen.queryByRole('option', { name: 'Apple' })).toBeNull();
  expect(screen.queryByRole('option', { name: 'Orange' })).toBeNull();
});

To verify that the dropdown is opened, you can check that the listbox with the same name as the input is visible.

tsx
import { Select } from '@mantine/core';

function MyForm() {
  return (
    <Select
      name="age"
      label="Select your age"
      data={[
        { value: 'ok', label: 'I am 18 or older' },
        { value: 'not-ok', label: 'I am under 18' },
      ]}
    />
  );
}

it('verifies dropdown opened state', () => {
  render(<MyForm />);

  // Verify that dropdown is closed
  // Listbox has the same name as the textbox
  expect(screen.queryByRole('listbox', { name: 'Select your age' })).toBeNull();

  // Click Select to open the options list
  await userEvent.click(screen.getByRole('textbox', { name: 'Select your age' }));

  // Verify that dropdown is open
  expect(screen.getByRole('listbox', { name: 'Select your age' })).toBeVisible();
});