How to filter getByLabelText query when found multiple elements?

Viewed 4053

how to filter the getByLabelText query, if It is throwing an error of the type: TestingLibraryElementError: Found multiple elements with the text of: /to/i, I have the following HTML structure, which has some nested labels:

Current HTML structure

<div class="ant-row ant-form-item ant-radio-group-cards" style="row-gap: 0px;">
    <div class="ant-col ant-form-item-label">
        <!-- <label> with "To" text -->
        <label for="transferFunds_to" class="ant-form-item-required" title="To">To</label> 
    </div>
    <div class="ant-col ant-form-item-control">
        <div class="ant-form-item-control-input">
            <div class="ant-form-item-control-input-content">
                <!-- Other <label> with "To" text here -->
                <label class="ant-radio-button-wrapper">
                    <span class="ant-radio-button"><input id="transferFunds_to" type="radio" class="ant-radio-button-input" value="" /><span class="ant-radio-button-inner"></span></span>
                    <span>
                        <li class="ant-list-item">
                            <div class="ant-list-item-meta">
                                <div class="ant-list-item-meta-avatar">
                                    <svg width="1em" height="1em" viewBox="0 0 16 16" fill="none" style="font-size: 48px;"></svg>
                                </div>
                                <div class="ant-list-item-meta-content">
                                    <!-- "To" word here inside a label -->
                                    <h4 class="ant-list-item-meta-title">Account to</h4>
                                    <div class="ant-list-item-meta-description">Choose the account to transfer to</div>
                                </div>
                            </div>
                            <span role="img" class="anticon icon-size-middle" style="align-self: center;">
                                <svg width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false" class=""></svg>
                            </span>
                        </li>
                    </span>
                </label>
            </div>
        </div>
    </div>
</div>

RTL Test

import { screen } from '@testing-library/react';

// query applied
screen.getByLabelText(/to/i);
2 Answers

Issue

All getBy* queries throw an error when there are zero matches or greater than one match.

Queries

enter image description here

The error says you should use one of the getAllBy* queries which return an array of matches and only throws an error if no matches are found.

Solutions

Since you want to target a specific input element with id="transferFunds_to" attribute you've at least a couple options:

  1. Add a testid to the input and query by that.

    <input
      id="transferFunds_to"
      data-testid="transferFunds_to" // <-- add a data test id
      type="radio"
      class="ant-radio-button-input"
      value=""
    />
    

    test

    test("case-sensitive To", () => {
      render(<App />);
      screen.getByTestId("transferFunds_to");
    });
    
  2. Use a manual query and target by the specified id attribute.

    test("case-sensitive To", () => {
      const { container } = render(<App />);
      container.querySelector("#transferFunds_to")
    });
    

Edit how-to-filter-getbylabeltext-query-when-found-multiple-elements

thanks for the codesandbox Cristian: https://codesandbox.io/s/trhh8

as you pointed in your question, there is conflicting text matching on both labels

texts "Account to", "Choose the account to transfer to" inside the second label

if you are happy with that, and you don't want to change the html structure, the only available option is to use a more strict regex in your screen.getByLabelText

i forked your example and updated the regex to screen.getByLabelText(/^to$/i), and it worked as expected, since all other "to" are followed by or have a space after it, e.g. " to" or "to ", the label "To" doesn't have pre or post space, which fulfills the regex

the final test case is:

import { screen, render } from "@testing-library/react";
import App from "./App";

test("case-sensitive To", () => {
  render(<App />);
  expect(screen.getByLabelText(/^to$/i).id).toBe("transferFunds_to");
});

have a look on https://codesandbox.io/s/react-testing-library-get-by-label-regex-g8tlx

of course, you can also change the API and use a .querySelector or more generic JS apis

Related