testing-library / react-testing-library

🐐 Simple and complete React DOM testing utilities that encourage good testing practices.

Home Page:https://testing-library.com/react

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support complex aria-labelledby queries

jossmac opened this issue · comments

Describe the feature you'd like:

I'd like getByLabelText to resolve the associated elements of aria-labelledby. As I'm writing this it occurs to me that this might be too costly and perhaps better suited to something like axe.

Describe alternatives you've considered:

I've resorted to a combination of getByRole + getByText and checking IDs.

Teachability, Documentation, Adoption, Migration Strategy:

The summarised/pseudo code looks something like:

let contentId = 'content-id';
let spinnerId = 'spinner-id';

return (
  <button
    aria-disabled={props.pending ? true : undefined}
    aria-labelledby={props.pending ? `${contentId} ${spinnerId}` : undefined}
  >
    <span id={contentId}>{props.children}</span>
    {props.pending && (
      <span id={spinnerId} aria-label="pending" aria-hidden>
        ...
      </span>
    )}
  </button>
);

The desired test would look something like:

it('supports "pending" prop', () => {
  let { getByLabelText } = render(<Button pending>Submit</Button>);
  expect(getByLabelText(`Submit ${PENDING_LABEL}`)).toHaveAttribute('aria-disabled', 'true');
});

Hi @jossmac, thanks for opening this.
I'm not sure I'm entirely understanding your request, but since you already have a solution that works, you can build a custom query in your own codebase.
Do you believe this use case is worth adding to the core library?

I wasn't aware of custom queries, thanks for the heads up!

Looking at this with fresh eyes, I think there's a sensible way to write the expectation with existing APIs e.g.

it('supports "pending" prop', () => {
  let { getByRole } = render(<Button pending>Submit</Button>);
  expect(
    getByRole("button", { name: `Submit ${PENDING_LABEL}` })
  ).toHaveAttribute("aria-disabled", "true");
});

Do you believe this use case is worth adding to the core library?

No, I think my mental model was at odds with the library's design decisions.


However, I think it should be documented somewhere* that getByLabelText() will only resolve the first ID when "aria-labelledby" is provided. In addition to improved docs, it could be helpful to warn consumers when the query encounters an "aria-labelledby" value that contains spaces.

Single ID, works as expected:

const { getByLabelText } = render(
  <>
    <div id="label-1">first</div>
    <button aria-labelledby="label-1" />
  </>
);

expect(getByLabelText("first")).toBeVisible(); // passes

Multiple IDs, does not work as expected:

const { getByLabelText } = render(
  <>
    <div id="label-1">first</div>
    <div id="label-2">second</div>
    <button aria-labelledby="label-1 label-2" />
  </>
);

expect(getByLabelText("first second")).toBeVisible(); // fails. shouldn't really, but understandable knowing what i do now

Which could lead to tests passing that really shouldn't e.g.

const { getByLabelText } = render(
  <>
    <div id="label-1">first</div>
    <div id="label-2">second</div>
    <button aria-labelledby="label-1 label-2" />
  </>
);

expect(getByLabelText("first")).toBeVisible(); // passes, incorrectly

*Found this documentation under a somewhat confusing heading:

The example above does NOT find the input node for label text broken up by elements.