Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TabPanel role with a incorrectly name when using getByRole (but works using getByText) #886

Closed
smblee opened this issue Mar 19, 2021 · 10 comments

Comments

@smblee
Copy link

smblee commented Mar 19, 2021

"@testing-library/react": "11.0.2",
"@testing-library/user-event": "^13.0.2",
"@testing-library/jest-dom": "5.11.4"
I am using jest with latest version of CRA.
default Jest environment (JSDom)

Node version 14

Relevant code or config:
Here is the error I get when I try

render(<Test />);

const tab1 = screen.getByRole('tab', { name: /Tab 1/ });
const tab2 = screen.getByRole('tab', { name: /Tab 2/ });
const panel1 = screen.getByRole('tabpanel', { name: /Panel 1/ });
const panel2 = screen.getByRole('tabpanel', { name: /Panel 2/ });

userEvent.click(tab2);
expect(tab1).not.toHaveFocus();
expect(panel1).not.toBeVisible();
expect(tab2).toHaveFocus();
expect(panel2).toBeVisible();

I have been refactoring to use screen.getByRole with getByText and previous this test was working, but now it doesn't seem to work. Mainly the error I am getting is the following

Unable to find an accessible element with the role "tabpanel" and name /Panel 1/

and here are the rest of the error (notice the tabpanel where it expects Name as "Tab 1" instead of "Panel 1"):

Here are the accessible roles:

  tablist:

  Name "Pages":
  <div
    aria-label="Pages"
    aria-orientation="horizontal"
    id="id-iqb0tk"
    role="tablist"
    variant="graph"
  />

  --------------------------------------------------
  tab:

  Name "Tab 1":
  <button
    aria-controls="id-iqb0tk-3"
    aria-selected="true"
    class="css-vurnku"
    id="id-iqb0tk-1"
    role="tab"
    tabindex="0"
  />

  Name "Tab 2":
  <button
    aria-controls="id-iqb0tk-4"
    aria-selected="false"
    class="css-vurnku"
    id="id-iqb0tk-2"
    role="tab"
    tabindex="-1"
  />

  --------------------------------------------------
  tabpanel:

  Name "Tab 1":
  <div
    aria-labelledby="id-iqb0tk-1"
    id="id-iqb0tk-3"
    role="tabpanel"
    style=""
    tabindex="0"
    variant="graph"
  />

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

<body>
  <div>
    <div
      aria-label="Pages"
      aria-orientation="horizontal"
      id="id-iqb0tk"
      role="tablist"
      variant="graph"
    >
      <div
        class="css-vurnku"
      >
        <button
          aria-controls="id-iqb0tk-3"
          aria-selected="true"
          class="css-vurnku"
          id="id-iqb0tk-1"
          role="tab"
          tabindex="0"
        >
          Tab 1
        </button>
        <button
          aria-controls="id-iqb0tk-4"
          aria-selected="false"
          class="css-vurnku"
          id="id-iqb0tk-2"
          role="tab"
          tabindex="-1"
        >
          Tab 2
        </button>
      </div>
    </div>
    <div
      aria-labelledby="id-iqb0tk-1"
      id="id-iqb0tk-3"
      role="tabpanel"
      style=""
      tabindex="0"
      variant="graph"
    >
      <div>
        Panel 1
      </div>
    </div>
    <div
      aria-labelledby="id-iqb0tk-2"
      hidden=""
      id="id-iqb0tk-4"
      role="tabpanel"
      style="display: none;"
      tabindex="0"
      variant="graph"
    >
      Panel 2
    </div>
  </div>
</body>
TestingLibraryElementError: Unable to find an accessible element with the role "tabpanel" and name `/Panel 1/`

Here are the accessible roles:

  tablist:

  Name "Pages":
  <div
    aria-label="Pages"
    aria-orientation="horizontal"
    id="id-iqb0tk"
    role="tablist"
    variant="graph"
  />

  --------------------------------------------------
  tab:

  Name "Tab 1":
  <button
    aria-controls="id-iqb0tk-3"
    aria-selected="true"
    class="css-vurnku"
    id="id-iqb0tk-1"
    role="tab"
    tabindex="0"
  />

  Name "Tab 2":
  <button
    aria-controls="id-iqb0tk-4"
    aria-selected="false"
    class="css-vurnku"
    id="id-iqb0tk-2"
    role="tab"
    tabindex="-1"
  />

  --------------------------------------------------
  tabpanel:

  Name "Tab 1":
  <div
    aria-labelledby="id-iqb0tk-1"
    id="id-iqb0tk-3"
    role="tabpanel"
    style=""
    tabindex="0"
    variant="graph"
  />

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

<body>
  <div>
    <div
      aria-label="Pages"
      aria-orientation="horizontal"
      id="id-iqb0tk"
      role="tablist"
      variant="graph"
    >
      <div
        class="css-vurnku"
      >
        <button
          aria-controls="id-iqb0tk-3"
          aria-selected="true"
          class="css-vurnku"
          id="id-iqb0tk-1"
          role="tab"
          tabindex="0"
        >
          Tab 1
        </button>
        <button
          aria-controls="id-iqb0tk-4"
          aria-selected="false"
          class="css-vurnku"
          id="id-iqb0tk-2"
          role="tab"
          tabindex="-1"
        >
          Tab 2
        </button>
      </div>
    </div>
    <div
      aria-labelledby="id-iqb0tk-1"
      id="id-iqb0tk-3"
      role="tabpanel"
      style=""
      tabindex="0"
      variant="graph"
    >
      <div>
        Panel 1
      </div>
    </div>
    <div
      aria-labelledby="id-iqb0tk-2"
      hidden=""
      id="id-iqb0tk-4"
      role="tabpanel"
      style="display: none;"
      tabindex="0"
      variant="graph"
    >
      Panel 2
    </div>
  </div>
</body>
@eps1lon
Copy link
Member

eps1lon commented Mar 19, 2021

Thanks for the report.

ByText and ByRole with name are not equivalent. ByRole looks for the accessible name which is correct here (see the aria-labelledby). I don't know the ByText semantics but it looks like it's using the textContent here. The difference is expected.

Does this clear things up?

@smblee
Copy link
Author

smblee commented Mar 19, 2021

I wonder if it's correct to look at aria-labelledby for a tabpanel since I really do see it just as a link rather than an identity of tabpanel. With that said, I can see there isn't really a good way to define individual tabpanels if not using id (which is not recommended) or the inner content (in this case I happened to call it Panel 1 and Panel 2). If it's the intended behavior, then I guess I have to resort to ByText in this case - is this the recommended approach here?

Also I assumed ByRole's name parameter would behave the same way ByText's parameter does, but by the sounds of it they are different. Maybe I missed it, but is there a documentation that outlines the differences?

@eps1lon
Copy link
Member

eps1lon commented Mar 19, 2021

I wonder if it's correct to look at aria-labelledby for a tabpanel since I really do see it just as a link rather than an identity of tabpanel. With that said

It is correct by spec: https://www.w3.org/TR/accname-1.1/

Do you have an example of assistive technology the announces something different?

Also I assumed ByRole's name parameter would behave the same way ByText's parameter does

Then we wouldn't need two different APIs.

Maybe I missed it, but is there a documentation that outlines the differences?

Sure:

This will search for all elements that have a text node with textContent matching the given TextMatch.

-- https://testing-library.com/docs/queries/bytext/

You can query the returned element(s) by their accessible name.

-- https://testing-library.com/docs/queries/byrole#api

@smblee
Copy link
Author

smblee commented Mar 19, 2021

Great thank you for the quick response.

Would you mind giving a quick answer for this question as well?

If it's the intended behavior, then I guess I have to resort to ByText in this case - is this the recommended approach here?

@eps1lon
Copy link
Member

eps1lon commented Mar 19, 2021

If it's the intended behavior, then I guess I have to resort to ByText in this case - is this the recommended approach here?

I would recommend ByRole here (which is what we recommend to start with).

Tabpanels usually have quite a bit of content so querying by textContent might either become unreadable (can have quite a lot of text) or brittle by relying on regular expressions. Imagine you would query a browser tab by its text content vs querying it by its title.

@smblee
Copy link
Author

smblee commented Mar 19, 2021

Ah okay. So doing this right?

    const panel1 = screen.getByRole('tabpanel', { name: 'Tab 1' });
    const panel2 = screen.getByRole('tabpanel', { name: 'Tab 2' });

I actually run into the issue of only one of the panels getting picked up. (Seems like non-visible panel via style display: none and hidden prop is not getting picked up?) but if you look at the DOM tree, it's technically there with a proper aria-labelledby. What do you suggest in this case? (I am trying to check certain panel does not show)

Edit: Here is the actual error log:


Unable to find an accessible element with the role "tabpanel" and name "Tab 2"

Here are the accessible roles:

  tablist:

  Name "Pages":
  <div
    aria-label="Pages"
    aria-orientation="horizontal"
    id="id-8klut3"
    role="tablist"
    variant="graph"
  />

  --------------------------------------------------
  tab:

  Name "Tab 1":
  <button
    aria-controls="id-8klut3-3"
    aria-selected="true"
    class="css-vurnku"
    id="id-8klut3-1"
    role="tab"
    tabindex="0"
  />

  Name "Tab 2":
  <button
    aria-controls="id-8klut3-4"
    aria-selected="false"
    class="css-vurnku"
    id="id-8klut3-2"
    role="tab"
    tabindex="-1"
  />

  --------------------------------------------------
  tabpanel:

  Name "Tab 1":
  <div
    aria-labelledby="id-8klut3-1"
    id="id-8klut3-3"
    role="tabpanel"
    style=""
    tabindex="0"
    variant="graph"
  />

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

<body>
  <div>
    <div
      aria-label="Pages"
      aria-orientation="horizontal"
      id="id-8klut3"
      role="tablist"
      variant="graph"
    >
      <div
        class="css-vurnku"
      >
        <button
          aria-controls="id-8klut3-3"
          aria-selected="true"
          class="css-vurnku"
          id="id-8klut3-1"
          role="tab"
          tabindex="0"
        >
          Tab 1
        </button>
        <button
          aria-controls="id-8klut3-4"
          aria-selected="false"
          class="css-vurnku"
          id="id-8klut3-2"
          role="tab"
          tabindex="-1"
        >
          Tab 2
        </button>
      </div>
    </div>
    <div
      aria-labelledby="id-8klut3-1"
      id="id-8klut3-3"
      role="tabpanel"
      style=""
      tabindex="0"
      variant="graph"
    >
      <div>
        Panel 1
      </div>
    </div>
    <div
      aria-labelledby="id-8klut3-2"
      hidden=""
      id="id-8klut3-4"
      role="tabpanel"
      style="display: none;"
      tabindex="0"
      variant="graph"
    >
      Panel 2
    </div>
  </div>
</body>
TestingLibraryElementError: Unable to find an accessible element with the role "tabpanel" and name "Tab 2"

Here are the accessible roles:

  tablist:

  Name "Pages":
  <div
    aria-label="Pages"
    aria-orientation="horizontal"
    id="id-8klut3"
    role="tablist"
    variant="graph"
  />

  --------------------------------------------------
  tab:

  Name "Tab 1":
  <button
    aria-controls="id-8klut3-3"
    aria-selected="true"
    class="css-vurnku"
    id="id-8klut3-1"
    role="tab"
    tabindex="0"
  />

  Name "Tab 2":
  <button
    aria-controls="id-8klut3-4"
    aria-selected="false"
    class="css-vurnku"
    id="id-8klut3-2"
    role="tab"
    tabindex="-1"
  />

  --------------------------------------------------
  tabpanel:

  Name "Tab 1":
  <div
    aria-labelledby="id-8klut3-1"
    id="id-8klut3-3"
    role="tabpanel"
    style=""
    tabindex="0"
    variant="graph"
  />

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

<body>
  <div>
    <div
      aria-label="Pages"
      aria-orientation="horizontal"
      id="id-8klut3"
      role="tablist"
      variant="graph"
    >
      <div
        class="css-vurnku"
      >
        <button
          aria-controls="id-8klut3-3"
          aria-selected="true"
          class="css-vurnku"
          id="id-8klut3-1"
          role="tab"
          tabindex="0"
        >
          Tab 1
        </button>
        <button
          aria-controls="id-8klut3-4"
          aria-selected="false"
          class="css-vurnku"
          id="id-8klut3-2"
          role="tab"
          tabindex="-1"
        >
          Tab 2
        </button>
      </div>
    </div>
    <div
      aria-labelledby="id-8klut3-1"
      id="id-8klut3-3"
      role="tabpanel"
      style=""
      tabindex="0"
      variant="graph"
    >
      <div>
        Panel 1
      </div>
    </div>
    <div
      aria-labelledby="id-8klut3-2"
      hidden=""
      id="id-8klut3-4"
      role="tabpanel"
      style="display: none;"
      tabindex="0"
      variant="graph"
    >
      Panel 2
    </div>
  </div>
</body>

@eps1lon
Copy link
Member

eps1lon commented Mar 19, 2021

(Seems like non-visible panel via style display: none is not getting picked up?)

Yep, we exclude inaccessible elements by default (note that Tab 2 is not picked up as a tabpanel in the error diagnostics). You can query them with hidden: true but understand that a user would not be able to access the panel at that moment.

@smblee
Copy link
Author

smblee commented Mar 19, 2021

Yea the test is explicitly trying to test that the element doesn't show. I just gave it a try with hidden: true and getting the same error.

Looks like it's getting picked up as a tabpanel but not assigned a Name (excerpt from error diagnostics)

  Name "":
  <div
    aria-labelledby="id-l19tfl-2"
    hidden=""
    id="id-l19tfl-4"
    role="tabpanel"
    style="display: none;"
    tabindex="0"
    variant="graph"
  />

@eps1lon
Copy link
Member

eps1lon commented Mar 19, 2021

Yea the test is explicitly trying to test that the element doesn't show. I just gave it a try with hidden: true and getting the same error

Is the referenced element hidden? That would be the same issue as testing-library/dom-testing-library#846 for which we don't have a good solution. I suggest you add a data-testid and assert that the element is inaccessible instead. This gives you a clearer failure description if the implementation does break.

@eps1lon
Copy link
Member

eps1lon commented Mar 19, 2021

Closing in favor of testing-library/dom-testing-library#846

@eps1lon eps1lon closed this as completed Mar 19, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants