Skip to content

Off-screen/hidden elements still queryable #196

Open
@adiun

Description

@adiun
  • 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?

Metadata

Metadata

Assignees

No one assigned

    Labels

    help wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions