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

CSS selectors match component props rather than rendered DOM #2560

Closed
2 of 13 tasks
iangreenleaf opened this issue Jun 2, 2022 · 6 comments
Closed
2 of 13 tasks

CSS selectors match component props rather than rendered DOM #2560

iangreenleaf opened this issue Jun 2, 2022 · 6 comments

Comments

@iangreenleaf
Copy link

Current behavior

Using CSS selectors for a class name will match a component with that value in a className prop, even if it's not in the output at all. For example:

      const MyElement = (_props) => (<div className='abcd' />);
      const el = mount(<MyElement className='foobar' />);
      expect(el.exists('.foobar')).toBe(true); // passes

Surprisingly, this is not the case in shallow, just mount. render works correctly, as you might expect since it's parsing the HTML output.

Expected behavior

I would expect CSS selectors to match against the rendered output, and to fail if a class name is not present in the HTML.

Your environment

node 14.19.1

API

  • shallow
  • mount
  • render

Version

library version
enzyme 3.10.0
react 16.12.0
react-dom 16.12.0
react-test-renderer 16.12.0
adapter (below)

Adapter

  • enzyme-adapter-react-16
  • enzyme-adapter-react-16.3
  • enzyme-adapter-react-16.2
  • enzyme-adapter-react-16.1
  • enzyme-adapter-react-15
  • enzyme-adapter-react-15.4
  • enzyme-adapter-react-14
  • enzyme-adapter-react-13
  • enzyme-adapter-react-helper
  • others ( )
@ljharb
Copy link
Member

ljharb commented Jun 2, 2022

In shallow, the wrapper is what's being rendered. In mount, the wrapper is the component itself - so that part isn't surprising. If your component renders a custom component with a className, using shallow, the selector would match it as well.

What's the output of el.debug()? That might make the behavior clearer.

@iangreenleaf
Copy link
Author

Here's the output of el.debug():

    <MyElement className="foobar">
      <div className="abcd" />
    </MyElement>

@ljharb
Copy link
Member

ljharb commented Jun 2, 2022

As you can see, .foobar matches the outer element. If you do el.children().exists('.foobar'), or el.hostNodes().exists('.foobar'), i'd expect both to be false.

@iangreenleaf
Copy link
Author

My main point of contention is that el.html(), and the actual HTML that makes it to a browser, is:

  <div className="abcd" />

So when we query for a CSS selector of .foobar, it's implied that we want a DOM node with class="foobar", which doesn't exist. Enzyme is spotting a component with a prop of className: "foobar" and treating it as the same thing, but it's really not.

@ljharb
Copy link
Member

ljharb commented Jun 2, 2022

It's not implied, though - because when working with enzyme wrappers, you're working with the component tree, not the final DOM that the browser sees (especially since one of the most important use cases of enzyme is testing server-rendering, where there's no DOM at all).

I understand this is violating your personal expectations, but it's the way enzyme's always worked, quite intentionally. This confusion is why .hostNodes() was added, though - so you can quickly filter down to just the DOM elements, and then operate on the resulting wrapper instead.

@iangreenleaf
Copy link
Author

Fair enough. I do think this is weird behavior, but I can understand how enzyme is built around some assumptions that lead to this happening.

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