dom-testing-library icon indicating copy to clipboard operation
dom-testing-library copied to clipboard

RTL can't find <fieldset> element and it's children with getByRole

Open RikvdSar opened this issue 1 year ago • 6 comments

  • @testing-library/dom version: 10.4.0
  • Testing Framework and version: testing-library/react: 16.0.0
  • vitest:^1.6.0
  • DOM Environment: jsdom: 24.1.0

Situation

RTL can't find the second fieldset element and can't find any accessible roles within that fieldset element. It CAN find all element with getByText. The HTML for the first fieldset elements looks like this (CAN find this element with getByRole("radiogroup")):

<fieldset class="..." role="radiogroup" aria-labelledby="..." aria-describedby="...">
   <label class="..." id="...">...</label>
</fieldset>

The HTML for the second (sibling) fieldset elements looks like this (can't find this element with getByRole("group"), but can find it with getByText):

<fieldset class="..." role="group" aria-labelledby="...">
   <legend class="..." id="...">...</legend>
   <button type="button" aria-expanded="false" aria-controls="...">...</button>
   "More accessible elements in here like heading which cannot be found by role"
</fieldset>

Trying to getByRole the button within the fieldset with getByRole:

expect(screen.getByText("form.privacy.show-extra-info")).toBeInTheDocument();

What happened:

(relevant) test output:

TestingLibraryElementError: Unable to find an accessible element with the role "button" and name "form.privacy.show-extra-info"

Here are the accessible roles:

  --------------------------------------------------
  radiogroup:

  Name "form.frequency.label":
  <fieldset
    aria-describedby="subscribeRadioGroupHelp"
    aria-labelledby="subscribeRadioGroupLabel"
    class="field-container radio-group has-help-text"
    role="radiogroup"
  />

  --------------------------------------------------
  button: _=> this is a button outside the <fieldset> element_

  Name "form.submit.label":
  <button
    aria-live="polite"
    class="button button--large button--default"
    type="submit"
  />

  --------------------------------------------------
        <fieldset
            aria-labelledby="privacyLegend"
            class="ro-form__fieldset"
            role="group"
          >
            <legend
              class="ro-form__legend"
              id="privacyLegend"
            >
              form.privacy.legend
            </legend>
            <div
              class="ro-form__fieldset-contents"
            >
              <div
                class="rich-text avg-block__intro on-gray-background"
              >
                <p>
                  form.privacy.intro.p1
                </p>
                <p>
                  form.privacy.intro.p2
                </p>
                <p>
                  form.privacy.intro.p3
                </p>
              </div>
              <div
                class="accordion ro-form__field"
              >
                <div
                  class="default-accordion-item accordion-item"
                >
                  <h3
                    class="default-accordion-item__header accordion-item__header"
                  >
                    <button
                      aria-controls="accordionPanel:r3:0"
                      aria-expanded="false"
                      class="default-accordion-item__button accordion-item__button"
                      id="accordionItemTitle:r3:0"
                      type="button"
                    >
                      <div
                        class="default-accordion-item__icon accordion-item__icon"
                        data-testid="icon"
                      >
                      </div>
                      <span
                        class="default-accordion-item__button-content"
                      >
                        <span
                          class="default-accordion-item__button-text"
                        >
                          <span
                            class="default-accordion-item__title"
                          >
                            form.privacy.show-extra-info
                          </span>
                        </span>
                      </span>
                    </button>
                  </h3>

RikvdSar avatar Aug 13 '24 12:08 RikvdSar

Hi @RikvdSar. This current issue doesn't have enough details for us to try and debug it, also it looks like you wrote getByRole but in the example you've used getByText. Can you please create a minimal reproduction using https://testing-library.com/new-dtl so we'll be able to help?

MatanBobi avatar Aug 14 '24 08:08 MatanBobi

I got a similar issue with the link and img roles. If I explicitly add the role="link" to the <a> tag then the tests pass. This started to happen when we used any versions of @testing-library/dom >= 10.0.0, with version 9.3.4 everything works as expected.

The libraries that I'm using:

    "@testing-library/dom": "10.0.0",
    "@testing-library/jest-dom": "6.5.0",
    "@testing-library/react": "16.0.0",

Below is an example where it can't find the img in the test

My React component

import React, { forwardRef } from 'react';

import * as styles from './styles';

type TextIconVariant = 'leading' | 'trailing';

export interface TextIconProps {
  children: React.ReactNode;
  icon: string;
  iconHeight: number;
  iconWidth: number;
  onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
  variant?: TextIconVariant;
}

export const TextIcon = forwardRef<HTMLButtonElement | null, TextIconProps>(
  (
    { children, icon, iconHeight, iconWidth, variant = 'trailing', ...buttonProps }: TextIconProps,
    forwardedRef,
  ) => (
    <button css={styles.iconButton} data-variant={variant} ref={forwardedRef} {...buttonProps}>
      {children}
      <img alt="" height={iconHeight} src={icon} width={iconWidth} />
    </button>
  ),
);

and the test file:

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

import { TextIcon } from './TextIcon';

test('TextIcon renders a button containing an icon and child elements', () => {
  const text = 'title';

  render(
    <TextIcon icon="icon" iconHeight={25} iconWidth={20} onClick={jest.fn()}>
      {text}
    </TextIcon>,
  );

  expect(screen.getByRole('button', { name: text })).toBeInTheDocument();
  expect(screen.getByRole('img')).toBeInTheDocument();
});

The error I'm getting is:

    TestingLibraryElementError: Unable to find an accessible element with the role "img"

    Here are the accessible roles:

      button:

      Name "title":
      <button
        css="
        cursor: inherit;
        padding: 0;
        border: none;
        background: none;
        color: black;
        border-radius: 0;

        &:hover {
          .,button__content, {
            color: inherit;
          }
        }

        .,button__content, {
          width: 100%;
          display: flex;
          align-items: center;
          justify-content: space-between;

          &::after {
            display: none;
          }
        }

        &[data-variant='leading'] {
          .,button__content, {
            flex-direction: row-reverse;
          }
        }
      "
        data-variant="trailing"
      />

      --------------------------------------------------
      presentation:

      Name "":
      <img
        alt=""
        height="25"
        src="icon"
        width="20"
      />

      --------------------------------------------------

    Ignored nodes: comments, script, style
    <body>
      <div>
        <button
          css="
      cursor: inherit;
      padding: 0;
      border: none;
      background: none;
      color: black;
      border-radius: 0;

      &:hover {
        .,button__content, {
          color: inherit;
        }
      }

      .,button__content, {
        width: 100%;
        display: flex;
        align-items: center;
        justify-content: space-between;

        &::after {
          display: none;
        }
      }

      &[data-variant='leading'] {
        .,button__content, {
          flex-direction: row-reverse;
        }
      }
    "
          data-variant="trailing"
        >
          title
          <img
            alt=""
            height="25"
            src="icon"
            width="20"
          />
        </button>
      </div>
    </body>

      14 |
      15 |   expect(screen.getByRole('button', { name: text })).toBeInTheDocument();
    > 16 |   expect(screen.getByRole('img')).toBeInTheDocument();
         |                 ^
      17 | });
      18 |

eianc avatar Aug 29 '24 11:08 eianc

Do I need to look for role presentation because the image is a descendent of the button? https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/button_role#all_descendants_are_presentational:~:text=There%20are%20some,support%20semantic%20children

eianc avatar Aug 29 '24 12:08 eianc

@eianc, if I understand correctly, your issue is different and is related to a change in the ARIA spec. The spec dictates that an empty alt will imply role "none" or "presentation". If you remove the alt attribute, it will get role img. screenshot from the ARIA spec showing what an img with no alt text implies

MatanBobi avatar Aug 30 '24 05:08 MatanBobi

Thank you @MatanBobi that makes sense! I fixed my tests

eianc avatar Aug 30 '24 10:08 eianc

Have issue combobox

"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.2.0",
"@testing-library/user-event": "^14.6.1",

here is my test

  it('should render', () => {
    const { baseElement } = render(<Select open />);

    console.log(baseElement.querySelector('[role="combobox"]'));
    console.log(screen.getByRole('combobox'));

    expect(screen.getByRole('listbox')).toBeInTheDocument();
  });

first log returns element then test fails with exception here is my output

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

    Here are the accessible roles:

      listbox:

      Name "":
      <div
        class="select-content overflow-hidden bg-surface-secondary data-[side=bottom]:rounded-b-3 data-[side=top]:rounded-t-3 data-[side=bottom]:pt-7 data-[side=top]:pb-7"
        data-align="center"
        data-side="bottom"
        data-state="open"
        dir="ltr"
        id="radix-:r0:"
        role="listbox"
        style="box-sizing: border-box; display: flex; flex-direction: column; outline: none; --radix-select-content-transform-origin: var(--radix-popper-transform-origin); --radix-select-content-available-width: var(--radix-popper-available-width); --radix-select-content-available-height: var(--radix-popper-available-height); --radix-select-trigger-width: var(--radix-popper-anchor-width); --radix-select-trigger-height: var(--radix-popper-anchor-height); animation: none; pointer-events: auto;"
        tabindex="-1"
      />

      --------------------------------------------------
      presentation:

      Name "":
      <div
        data-radix-select-viewport=""
        role="presentation"
        style="position: relative; flex: 1; overflow: hidden auto;"
      />

      --------------------------------------------------

    Ignored nodes: comments, script, style
    <body
      data-scroll-locked="1"
      style="pointer-events: none;"
    >
      <span
        aria-hidden="true"
        data-aria-hidden="true"
        data-radix-focus-guard=""
        style="outline: none; opacity: 0; position: fixed; pointer-events: none;"
        tabindex="0"
      />
      <div
        aria-hidden="true"
        data-aria-hidden="true"
      >
        <button
          aria-autocomplete="none"
          aria-controls="radix-:r0:"
          aria-expanded="true"
          class="input-base relative flex h-12 min-w-[15rem] items-center justify-between rounded text-primary data-[state=open]:z-50 data-[placeholder]:text-secondary"
          data-placeholder=""
          data-state="open"
          dir="ltr"
          role="combobox"
          type="button"
        >
          <span
            style="pointer-events: none;"
          />
          <span
            aria-hidden="true"
            class="ml-4"
          >
            <svg
              class="icon chevron-16 icon-16 -rotate-90 fill-icons-primary"
              height="16"
              width="16"
            >
              <use
                xlink:href="test-file-stub#chevron-16"
              />
            </svg>
          </span>
        </button>
      </div>
      <div
        data-radix-popper-content-wrapper=""
        dir="ltr"
        style="position: fixed; left: 0px; top: 0px; transform: translate(0, -200%); min-width: max-content;"
      >
        <div
          class="select-content overflow-hidden bg-surface-secondary data-[side=bottom]:rounded-b-3 data-[side=top]:rounded-t-3 data-[side=bottom]:pt-7 data-[side=top]:pb-7"
          data-align="center"
          data-side="bottom"
          data-state="open"
          dir="ltr"
          id="radix-:r0:"
          role="listbox"
          style="box-sizing: border-box; display: flex; flex-direction: column; outline: none; --radix-select-content-transform-origin: var(--radix-popper-transform-origin); --radix-select-content-available-width: var(--radix-popper-available-width); --radix-select-content-available-height: var(--radix-popper-available-height); --radix-select-trigger-width: var(--radix-popper-anchor-width); --radix-select-trigger-height: var(--radix-popper-anchor-height); animation: none; pointer-events: auto;"
          tabindex="-1"
        >
          <div
            data-radix-select-viewport=""
            role="presentation"
            style="position: relative; flex: 1; overflow: hidden auto;"
          />
        </div>
      </div>
      <span
        aria-hidden="true"
        data-aria-hidden="true"
        data-radix-focus-guard=""
        style="outline: none; opacity: 0; position: fixed; pointer-events: none;"
        tabindex="0"
      />
    </body>

can't understand what's wrong with that

NuclleaR avatar Mar 03 '25 14:03 NuclleaR