loading

v33.5.1

select option

Testing ink Components

General information about how to write tests for ink components

ink + React Testing Library

Props in ink are standardized to work well with React Testing Library (RTL), which is widely used in Carta.

React Testing Library is built to work with the implementation side of testing, focusing on user interaction through mouse, trackpad and keyboard as well as assistive technologies.

This means that ink also had to do an audit of our components to make them more accessible through the addition of accessible names with aria-labels and roles to interactive elements that had none.

Testing structure

RTL is designed to interact with pages and components exactly as your users would, ensuring it can only access what they can see.

RTL primary selects through interactive components such as buttons, inputs, and text. As a result, ink prioritizes those as selectors.

Interactive elements

Interactive elements in RTL range from buttons to any text that can be seen on the screen by the user or screen readers, so selecting components really depends on what you're trying to test.

For example:

Roles and other accessible attributes

RTL makes very good use of accessible attributes, these attributes are (but not limited to): role, title, aria-label, alt.

HTML elements have implicit roles, so the majority of ink components have their respective accessible role defined, and you can find them through code inspection.

If you can't find the component's role, want help with your testing, or want to let us know we forgot to add a proper role to a component, reach out to the #ink channel

To use these attributes you can use, for example:

  • Use getByLabelText to get by input visible labels or screen reader visible aria-labels;
  • Use getByTitle to select any component with a title attribute like svgs and images;
  • Use getByAltText to select images that have the alt attribute;

Selecting specific components

One of the issues with using roles is you might have more than just one input or select on a given page, how to make sure you're selecting the right one?

Many people will use queryByRole and manually select one of the returning components using bracket notation ([]), however, this approach is wrong and very prone to error. If ink changes the component's structure or there is a need to change the order of the components on the page, the test would break.

In order to properly select the component you need to add options to your query, specifically, the component's name:

<Form>
<Field htmlFor="newinput-fullname" label="Full name">
<NewInput id="newinput-fullname" />
</Field>
<Field htmlFor="newinput-email" label="Email">
<NewInput id="newinput-email" />
</Field>
</Form>

Selecting the first input:

screen.getByRole("textbox", { name: /full name/i }); // You can also use the specific string instead of regex

Selecting the second input:

screen.getByRole("textbox", { name: /email/i })

We highly encourage users to select components using this technique as it is best practice, more readable, and also less prone to errors.

A good way to know the accessible name of a component is running your test with an empty getByRole("") as the test will show you all the accessible names in your code.

Another way is inspecting your code with RTL's chrome extension as it will suggest possible ways of selecting the component you want based on the DOM attributes.

Selecting hidden components

Some images or elements might be hidden from screen readers for accessibility, which might pose a problem for some tests. To select these types of components, there's another option that can be used:

screen.getByRole("img", { name: "edit", hidden: true })

This will make sure the test can see your component and select it.

data-testids

According to RTL's guiding principles, data-testids are considered the last resource for testing, and should be used sparingly.

To help with those cases, every ink component will have at least one data-testid that can be used to select the outer most relevant wrapper element in the component. For example Modal:

<NewDatePicker.Wrapper data-testid="your-testid">
<NewDatePicker.Input />
<NewDatePicker.Calendar />
</NewDatePicker.Wrapper>

Some of ink's components might be too complex to have only one data-testid so ink adds other relevant data-testids . For example SpreadsheetV2 has different data-testids for the Slider, the Menu, and the Table. But the user only needs to provide a single data-testid and an id that is modified internally to have respectivelly -slider, -menu, -table at the end of the id provided.

<SpreadsheetWrapper data-testid="your-testid" id="your-id">
<SpreadsheetMenu data-testid="your-id-menu" />
<SpreadsheetTable data-testid="your-id-table" />
<SpreadsheetSlider data-testid="your-id-slider" />
</SpreadsheetWrapper>

To select data-testids on tests, use .getByTestId()

Interacting on tests

ink, as does React Testing Library, suggests the use of userEvent instead of fireEvent as it is a more complete solution and simulates entire interactions.

ink also recommends the use of act around userEvent:

act(() => {
userEvent.click(screen.getByText("Row 1"));
})

Is this page helpful?