testing-library / testing-library/dom-testing-library

Off-screen/hidden elements still queryable

Open
#196 43 comments 26 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

help wanted
Dominant language
JavaScript
Stars
3.3k
Forks
474
PR merge metrics
No merged PRs in 30d

Description

  • dom-testing-library version: 3.16.4
  • react version: 16.8.0-alpha.1
  • node version: 8.11.3
  • npm (or yarn) version: 5.6.0
Relevant code or config:
test("off-screen element not queryable", () => {
  const { queryByText } = render(
    <div style={{ height: "150px", overflow: "auto" }}>
      <div style={{ height: "100px" }}>Paragraph-1</div>
      <div style={{ height: "100px" }}>Paragraph-2</div>
      <div style={{ height: "100px" }}>Paragraph-3</div>
      <div style={{ height: "100px" }}>Paragraph-4</div>
    </div>
  );
  const paragraph = queryByText("Paragraph-4");
  expect(paragraph).toBeNull();
});

test("non-displayed element not queryable", () => {
  const { queryByText } = render(
    <div style={{ height: "200px", overflow: "auto" }}>
      <div style={{ height: "100px" }}>Paragraph-1</div>
      <div style={{ height: "100px", display: "none" }}>
        NotDisplayed-Paragraph
      </div>
    </div>
  );
  const paragraph = queryByText("NotDisplayed-Paragraph");
  expect(paragraph).toBeNull();
});

test("hidden element not queryable", () => {
  const { queryByText } = render(
    <div style={{ height: "200px", overflow: "auto" }}>
      <div style={{ height: "100px" }}>Paragraph-1</div>
      <div style={{ height: "100px", visibility: "hidden" }}>
        Hidden-Paragraph
      </div>
    </div>
  );
  const paragraph = queryByText("Hidden-Paragraph");
  expect(paragraph).toBeNull();
});
What you did:

Tried to write the tests above, expected them to pass.

What happened:

Tests failed:

off-screen element not queryable
2ms
expect(received).toBeNull()

Expected value to be null, instead received
  <div style="height: 100px;">Paragraph-4</div>

  14 |     </div>
  15 |   );
  16 |   const paragraph = queryByText("Paragraph-4");
> 17 |   expect(paragraph).toBeNull();
  18 | });
  20 | test("non-displayed element not queryable", () => {
non-displayed element not queryable
1ms
expect(received).toBeNull()

Expected value to be null, instead received
  <div style="height: 100px; display: none;">NotDisplayed-Paragraph</div>

  27 |     </div>
  28 |   );
  29 |   const paragraph = queryByText("NotDisplayed-Paragraph");
> 30 |   expect(paragraph).toBeNull();
  31 | });
  33 | test("hidden element not queryable", () => {
hidden element not queryable
1ms
expect(received).toBeNull()

Expected value to be null, instead received
  <div style="height: 100px; visibility: hidden;">Hidden-Paragraph</div>

  40 |     </div>
  41 |   );
  42 |   const paragraph = queryByText("Hidden-Paragraph");
> 43 |   expect(paragraph).toBeNull();
  44 | });
Reproduction:

https://codesandbox.io/s/2zwjwyp13n

Problem description:

First off, I'm new to react-testing-library. I read @kentcdodds philosophy - this quote I totally agree with: "The more your tests resemble the way your software is used, the more confidence they can give you.".

That said, I was expecting in the tests above that the given elements would not be able to queried because they are hidden/non-visible/off-screen. I dug around in the dom-testing-library code and noticed that querySelector is still being used but I don't understand how this implementation accounts for elements that the user cannot see, like things that are off-screen, hidden, etc.

I would absolutely love to be able to write tests that exercise a product the way a user would interact with it. But I'm unsure if I can trust dom-testing-library in this sense.

Outside of hidden/off-screen elements cases (and not related to this issue), what about clicking on elements that are unclickable? This is a (surprisingly) frequent z-index bug I've seen. fireEvent wouldn't really cut it. There are lots of edge cases here...

Suggested solution:

I realize that it would be quite difficult to account for all of the cases where an element can't be queried (off the top of my head - opacity = 0, transforms off screen, scaled down, filters, clip-path, etc.) But I would expect at least display = none, visibility = hidden, and off-screen elements to be accounted for. I think this can be handled through getBoundingClientRect but that would sacrifice perf. Maybe this is something that a user could toggle through render?

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Review the dom-testing-library querySelector-based implementation and its existing tests first. Clarify whether display:none, visibility:hidden, and off-screen elements should be excluded and whether the behavior is opt-in; done means focused tests cover the agreed cases without assuming unsupported visual edge cases.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, react
Domain
testing-qa
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.