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

Mount find() Does Not Find Rendered Marker #1233

Closed
dschinkel opened this issue Oct 5, 2017 · 58 comments
Closed

Mount find() Does Not Find Rendered Marker #1233

dschinkel opened this issue Oct 5, 2017 · 58 comments

Comments

@dschinkel
Copy link

dschinkel commented Oct 5, 2017

This one is pretty frustrating. I mount this container. It does hit the code I expect that contains my css feature marker (ft-playback-error). But in the end I end up with not found.

screencast of this issue: https://youtu.be/kh-UJTig3Qg

   it.only('renders a coordinates message in the player when no coordinates exist', () => {
      const store = createStore(reducers, {}),

        liveScreen = mount(
          <Provider store={createStore(reducers, {})}>
            <Live
              breakpoint="site-web"
              coordinates={null}
              renderInfoInDetail={() => {}}
              setOverlay={() => {}}
              store={store}
              AccessEnablerUrl=""
              getUseUtagGlobal={() => {}}
              pageViewEvent={() => {}}
            />
          </Provider>),
        message = liveScreen.find('.ft-playback-error');

      expect(message).to.have.length(1);
    });

message.nodes ends up being 0 (or aka not found). Why?
screen shot 2017-10-05 at 10 54 05 am

I assumed when you mounted, it runs every child's render() (every child that's being hit down the codepath of container..obviously depending on whatever logic is in its render will determine which children are being rendered) method of this container.

@ljharb
Copy link
Member

ljharb commented Oct 5, 2017

Can you share the code that contains the class name?

@dschinkel
Copy link
Author

yes I can share temporarily https://gist.github.com/dschinkel/e1e58c1cbdc60962078e1afe21cffe4b

@3axap4eHko
Copy link

the same for me:

console.log(element.html());
expect(element.find('.feature__media').length).toEqual(1);

I'm getting

expect(received).toEqual(expected)

    Expected value to equal:
      1
    Received:
      0

<div class="feature"><div class="feature__cover"><div class="feature__media">Episode</div></div><div class="feature__details"><h3 class="feature__type">episode</h3><div class="feature__title"></div></div></div>

but .feature__cover found well

@dschinkel
Copy link
Author

dschinkel commented Oct 5, 2017

@ljharb @3axap4eHko yea exactly... I feel like mount() isn't able to dig down the entire app tree..? Iike the behavior I feel I've noticed is that it sorta stops 2-3 children down..

@ljharb
Copy link
Member

ljharb commented Oct 5, 2017

@dschinkel how is getPlaybackErrorProps() used? Specifically, can I see the code that invokes playbackErrorChildren?

@ljharb
Copy link
Member

ljharb commented Oct 5, 2017

@aweary any chance this is an issue with the RST selector parser?

@aweary
Copy link
Collaborator

aweary commented Oct 5, 2017

@ljharb .feature__cover is parsed correctly, so I don't think so, but it could be related to the traversal logic. @dschinkel @3axap4eHko if you use findWhere does it work?

element.findWhere(node => node.hasClass('.feature__cover'))

If it doesn't, can you verify if findWhere traverses the node you're targeting? A reduced example that we could reproduce the issue with would be useful, it's hard to say whats going on with a 800+ line example that imports from other unknown files.

@dschinkel
Copy link
Author

dschinkel commented Oct 5, 2017

@ljharb hehe yea let me show you that one...from this other 1000 line fing container (going from from 1 100 line container to another 1000 container :) might be why mount() doesn't work, too much freakin code in a container).

https://gist.github.com/dschinkel/8d176d9abc19f9cbe39d5a4d6edc2c43

@dschinkel
Copy link
Author

dschinkel commented Oct 5, 2017

interesting @aweary I get this in the console when I tried your route:
message = liveScreen.findWhere(node => node.hasClass('.ft-playback-error'))

this printed to the console:
It looks like you're calling ReactWrapper::hasClass() with a CSS selector. hasClass() expects a class name, not a CSS selector. when I tried your example

@aweary
Copy link
Collaborator

aweary commented Oct 5, 2017

@dschinkel sorry my mistake, remove the leading . (just was the class name)

@dschinkel
Copy link
Author

dschinkel commented Oct 5, 2017

nope, got to the same result, not found :( but thanks. I mean when you're dealing with 2 1000 line containers, Um...who knows with that kind of rabbit hole. :(. I do know that the code that had my feature marker was indeed hit.

@aweary
Copy link
Collaborator

aweary commented Oct 5, 2017

@dschinkel can you verify if it's traversing the node you're looking for? You can try just logging out node.props() inside the findWhere callback to see if its calling it with the node(s) you'd expect it to.

@dschinkel
Copy link
Author

dschinkel commented Oct 5, 2017

@aweary

message = liveScreen.findWhere(node => {
          console.log(node.props())
          return node.hasClass('ft-playback-error')
        })

output / rabbit hole does show it:

 children: 
   { '$$typeof': Symbol(react.element),
     type: 'div',
     key: null,
     ref: null,
     props: { className: 'undefined ft-playback-error', children: [Array] },

full output:
https://gist.github.com/dschinkel/78f875359ef35af37eb5356cd8ed30aa

Note: I've also tried searching dcg-playback-error which lives in another component which is also part of what's rendered (it's just a shared component that lives further down in the tree) and the find() did not find that either even though it's clearly there in the results.

related (I've had this issue before, never resolved it) #1197 (comment)

btw: accidentally hit the close button, reopened the post.

@dschinkel dschinkel reopened this Oct 5, 2017
@dschinkel
Copy link
Author

dschinkel commented Oct 6, 2017

this is crazy. I think I'm losing my mind. I've never had this problem in other projects (not that I recall) or at least this is the first time I've been having these issues..

Here is a test for another part in the same project but on a different container (different from the code I posted) but shows the weirdness I've been having as well in this example:

https://youtu.be/GJV9VduT0DY

@dschinkel
Copy link
Author

sorry I missed this @aweary:

If it doesn't, can you verify if findWhere traverses the node you're targeting? A reduced example that we could reproduce the issue with would be useful, it's hard to say whats going on with a 800+ line example that imports from other unknown files.

let me check..

@dschinkel
Copy link
Author

dschinkel commented Oct 6, 2017

@aweary Per this:

If it doesn't, can you verify if findWhere traverses the node you're targeting? A reduced example that we could reproduce the issue with would be useful, it's hard to say whats going on with a 800+ line example that imports from other unknown files.

This shows it's traversing that node (New Cast)
https://youtu.be/wMK49azSTTo

also I'm not using an enzyme adapter, never have, is that something new? In our case we're running React 15.1, do I need enzyme-adapter-react-15.4?. I'm using enzyme 2.6.0 in a personal project and mounting works fine...and that project is not using any enzyme adapters either.

Our current versions and what is installed are the following:

    "react": "^15.2.1",
    "react-dom": "^15.0.1",
    "chai": "^3.5.0",
    "chai-enzyme": "^0.6.1",
    "enzyme": "^2.6.0",

@3axap4eHko
Copy link

@aweary it does not work also. I use "react": "^16.0.0", and "enzyme-adapter-react-16": "^1.0.1",

@aweary
Copy link
Collaborator

aweary commented Oct 6, 2017

@dschinkel sorry I assumed you were using Enzyme 3. The adapters are part of the rewrite, check out the migration guide. We fixed a number of selector parsing issues in the rewrite, and this could be related to one of them.

In our case we're running React 15.1, do I need enzyme-adapter-react-15.4

You would use enzyme-adapter-react-15 if you're not on version 15.4

At this point, it's going to be difficult for me to help further without a small, succinct example reproducing the issue that I can run locally and investigate.

@dschinkel
Copy link
Author

dschinkel commented Oct 6, 2017

ah ok I thought I was on the up and up...apparently my head is too buried in enzyme code. I didn't realize there was a rewrite recently.

Thanks I'll try it out and report back!! Thanks for you help so far @aweary @ljharb

Without having to look too much when was this rewrite done? I tried installing Enzyme 3 the other day along with updating chai-enzyme, but got chai-enzyme errors which I believe is now being fixed...there's an issue for it in chai-enzyme where it was blowing up on me and someone was already working on a fix for that so I had to revert back to enzyme <3

Looks like I actually need enzyme-adapter-react-15.4 which covers React 5.0.0-0 - 15.4.x since we use React 15.1

@dschinkel
Copy link
Author

dschinkel commented Oct 6, 2017

hmm we're using React 15.1, not sure if the team wants to be upgrading react right now (not my call)
. Here's how I added the adapter in one of my test helpers:

import chai, { expect } from 'chai';
import chaiEnzyme from 'chai-enzyme';
import { shallow, mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-15.4';

Enzyme.configure({ adapter: new Adapter() });

Then tried to run tests and got:
Error('react-dom@15.5+ and react-test-renderer are implicit dependencies when using ' + 'react@15.5+ with enzyme

@3axap4eHko
Copy link

@aweary I'm using "enzyme": "^3.1.0" and adapter is "enzyme-adapter-react-16": "^1.0.1" and I have the same problem

@3axap4eHko
Copy link

3axap4eHko commented Oct 10, 2017

This is what I found console.log(element.html()); returns

<div class="feature">
  <div class="feature__cover">
    <div class="feature__media">Episode</div>
  </div>
  <div class="feature__details"><h3 class="feature__type">episode</h3>
    <div class="feature__title"></div>
  </div>
</div>

but console.log(JSON.stringify(element.getNodesInternal(), null, ' ')); returns

Quite different JSON
[
      {
        "nodeType": "host",
        "type": "div",
        "props": {
          "className": "feature",
          "children": [
            {
              "type": "div",
              "key": null,
              "ref": null,
              "props": {
                "className": "feature__cover",
                "children": {
                  "key": null,
                  "ref": null,
                  "props": {
                    "feature": {
                      "id": "169934",
                      "series_id": "SH015468900000",
                      "style": "S",
                      "call_to_action": "WATCH THE TRAILER",
                      "call_to_action_override_long": "",
                      "image_url": true,
                      "subheading": "",
                      "suppress_season_number_episode_number": false
                    }
                  },
                  "_owner": null,
                  "_store": {}
                }
              },
              "_owner": null,
              "_store": {}
            },
            {
              "type": "div",
              "key": null,
              "ref": null,
              "props": {
                "className": "feature__details",
                "children": [
                  {
                    "type": "h3",
                    "key": null,
                    "ref": null,
                    "props": {
                      "className": "feature__type",
                      "children": "episode"
                    },
                    "_owner": null,
                    "_store": {}
                  },
                  {
                    "type": "div",
                    "key": null,
                    "ref": null,
                    "props": {
                      "className": "feature__title"
                    },
                    "_owner": null,
                    "_store": {}
                  }
                ]
              },
              "_owner": null,
              "_store": {}
            }
          ]
        },
        "key": null,
        "ref": null,
        "instance": null,
        "rendered": [
          {
            "nodeType": "host",
            "type": "div",
            "props": {
              "className": "feature__cover",
              "children": {
                "key": null,
                "ref": null,
                "props": {
                  "feature": {
                    "id": "169934",
                    "series_id": "SH015468900000",
                    "style": "S",
                    "call_to_action": "WATCH THE TRAILER",
                    "call_to_action_override_long": "",
                    "image_url": true,
                    "subheading": "",
                    "suppress_season_number_episode_number": false
                  }
                },
                "_owner": null,
                "_store": {}
              }
            },
            "key": null,
            "ref": null,
            "instance": null,
            "rendered": {
              "nodeType": "function",
              "props": {
                "feature": {
                  "id": "169934",
                  "series_id": "SH015468900000",
                  "style": "S",
                  "call_to_action": "WATCH THE TRAILER",
                  "call_to_action_override_long": "",
                  "image_url": true,
                  "subheading": "",
                  "suppress_season_number_episode_number": false
                }
              },
              "key": null,
              "ref": null,
              "instance": null,
              "rendered": null
            }
          },
          {
            "nodeType": "host",
            "type": "div",
            "props": {
              "className": "feature__details",
              "children": [
                {
                  "type": "h3",
                  "key": null,
                  "ref": null,
                  "props": {
                    "className": "feature__type",
                    "children": "episode"
                  },
                  "_owner": null,
                  "_store": {}
                },
                {
                  "type": "div",
                  "key": null,
                  "ref": null,
                  "props": {
                    "className": "feature__title"
                  },
                  "_owner": null,
                  "_store": {}
                }
              ]
            },
            "key": null,
            "ref": null,
            "instance": null,
            "rendered": [
              {
                "nodeType": "host",
                "type": "h3",
                "props": {
                  "className": "feature__type",
                  "children": "episode"
                },
                "key": null,
                "ref": null,
                "instance": null,
                "rendered": "episode"
              },
              {
                "nodeType": "host",
                "type": "div",
                "props": {
                  "className": "feature__title"
                },
                "key": null,
                "ref": null,
                "instance": null,
                "rendered": null
              }
            ]
          }
        ]
      }
    ]

@ljharb
Copy link
Member

ljharb commented Oct 11, 2017

I wouldn't rely on the JSON version of getNodesInternal() - that's an internal method anyways, so you definitely shouldn't rely on it.

What about element.debug()?

@dschinkel
Copy link
Author

@ljharb @aweary I'm hesitant to upgrade to 3.x..as I'm seeing quite a few issues out there for find() with 3.x. Should I wait?

@ljharb
Copy link
Member

ljharb commented Oct 11, 2017

It's hard to know if there are actually issues with v3, or if they're solely with the React 16 adapter, because people for some reason are upgrading to enzyme 3 and react 16 at the same time.

If you stick with React 15, and upgrade to v3, and follow the migration guide, you should be fine.

@3axap4eHko
Copy link

It looks like it does not traverse over subcomponents imported from other files

@ljharb
Copy link
Member

ljharb commented Oct 16, 2017

@3axap4eHko which, shallow? The entire point of shallow rendering is to never traverse subcomponents.

@sadu
Copy link

sadu commented Nov 10, 2017

@dmccown1500 I also had a similar issue, seems like redux and enzyme not working properly.
.html() was giving proper markup but .find('selector') wasn't working (enzyme v3.1.0)

wrapper.update() after modifying the redux state fixed wrapper.find('.deep-nested-component') returning an empty set 👍

@deathmood
Copy link

@sadu thanks for the tip. Had similar issue - wrapper.html() returned correct tree but when I was doing wrapper.find(...) it seemed like it was searching in the tree of the previous state. wrapper.update() helped

@msafi
Copy link

msafi commented Jan 26, 2018

This needs to be reopened. This is still a problem.

@ljharb
Copy link
Member

ljharb commented Jan 26, 2018

@msafi this specific issue was in v2; could you file a new issue if you're seeing problems in v3?

@msafi
Copy link

msafi commented Jan 26, 2018

Just tried with Enzyme 3. This is no longer an issue. Thank you!

@SeanRoberts
Copy link

I just encountered this issue with redux and enzyme 3.3.0, the .update() workaround does work.

@realmhamdy
Copy link

realmhamdy commented Mar 2, 2018

I'm having this issue. Enzyme 3.3.0 with MobX. HTML is correct but find() doesn't work. The update() trick didn't help

@JaSpr
Copy link

JaSpr commented Mar 4, 2018

Can confirm with versions:

+-- enzyme@3.3.0
+-- enzyme-adapter-react-16@1.1.1

when the component being tested must be wrapped by a redux Provider:

wrapper = mount(
  React.createElement(Provider, {store},
    React.createElement(MyComponent, props)
  )
);

then wrapper.html() always shows the child nodes, but wrapper.find(...) will not find them UNLESS wrapper.update() is called first

@ljharb
Copy link
Member

ljharb commented Mar 5, 2018

@github-account-because-they-want-it MobX isn't compatible with enzyme with a react adapter; you'd need a mobx adapter. Are you using one?

@joemcelroy
Copy link
Contributor

if noone if seeing the same results from wrapper.debug() and wrapper.html() after calling update, use wrapper = wrapper.update(). In enzyme 3, the wrapper is immutable.

@notdotscott
Copy link

Confirmed again:

"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",

I'm not using Redux. wrapper.html() was correct but wrapper.find() couldn't find anything. Added a wrapper.update() before the find and it worked.

@willetvary
Copy link

@dmccown1500 The update works great!

@timbakkum
Copy link

still an issue but calling update() before using stuff like find() works

@StefanoSega
Copy link

still an issue and in my situation even wrapper.update(); or wrapper = wrapper.update(); don't help.

I'm testing a method that does DOM manipulation like creating and appending DOMs to an element that I pass as argument:

export const applyRippleEffectElab = (event, element, options) => {
  const ripplerContainer = element.querySelector('.ripple-container');
  const offsetInfo = element.getBoundingClientRect();
  if (ripplerContainer) {
    ripplerContainer.remove();
  }
  const rippleContainer = document.createElement('div');
  rippleContainer.style.position = 'absolute';
  rippleContainer.style.zIndex = 99;
  rippleContainer.style.width = '100%';
  rippleContainer.style.left = '0';
  rippleContainer.style.top = '0';
  rippleContainer.style.right = '0';
  rippleContainer.style.bottom = '0';
  rippleContainer.className = 'ripple-container';
  rippleContainer.style.overflow = 'hidden';
  if (options && options.roundShape) {
    rippleContainer.style.borderRadius = '50%';
  }
  element.appendChild(rippleContainer);

  const maxLength = offsetInfo.width > offsetInfo.height ? offsetInfo.width : offsetInfo.height;
  const sizeMultiplier = (options && options.sizeMultiplier) || 2;
  const circleD = maxLength * sizeMultiplier;

  const ripple = document.createElement('div');
  ripple.style.position = 'absolute';
  ripple.style.width = `${circleD}px`;
  ripple.style.height = `${circleD}px`;
  ripple.style.borderRadius = '500px';
  ripple.style.left = `${(event.pageX - offsetInfo.left) - (circleD / 2)}px`;
  ripple.style.top = `${(event.pageY - offsetInfo.top) - (circleD / 2)}px`;
  ripple.className = 'ripple';
  if (options && options.color) {
    ripple.style.backgroundColor = options.color;
  }
  if (options && options.opacity) {
    ripple.style.opacity = options.opacity.toString();
  }
  rippleContainer.appendChild(ripple);

  ripple.addEventListener('animationend', () => {
    rippleContainer.remove();
  }, false);
};

the test looks like:

import React from 'react';
import { mount } from 'enzyme';

const materialDesign = require('../../../react/utils/materialDesign');

let wrapper;

describe('applyRippleEffect', () => {
  describe('when clicking with no options', () => {
    beforeAll(() => {
      wrapper = mount(<div id="test" />);

      const event = {
        pageX: 0,
        pageY: 0,
      };
      const element = wrapper.instance();

      materialDesign.applyRippleEffectElab(event, element);
    });

    test('it should create a ripple-container element and inside a ripple element', () => {
      wrapper = wrapper.update();
      console.log(wrapper.html());
      expect(wrapper.find('.ripple-container').length).toBe(1);
      expect(wrapper.find('.ripple-container .ripple').length).toBe(1);
    });
  });
});

console.log(wrapper.html()); returns this:

<div id="test"><div style="position: absolute; z-index: 99; width: 100%; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: hidden;" cl
ass="ripple-container"><div style="position: absolute; width: 0px; height: 0px; border-radius: 500px; left: 0px; top: 0px;" class="ripple"></div><
/div></div>

@ljharb
Copy link
Member

ljharb commented Jul 5, 2018

Manually manipulating the DOM isn't really something that's idiomatic in React, and it wouldn't surprise me if you ran into issues doing that with jsdom. Please file a new issue if you think there's an issue with enzyme.

@JSEvgeny
Copy link

For me the problem with .find() was because of wrapping a component with <React.Fragment>. Changed it back to

and that did the trick. Seems like Enzyme 3.3.0 doesn't like React.Fragment

@ljharb
Copy link
Member

ljharb commented Jul 19, 2018

@EvgenyW3 see #1213; fragments aren't currently supported.

@d-e-p
Copy link

d-e-p commented Jul 20, 2018

@StefanoSega I had a similar problem. I have a component written in pure JS that inserts html inside React component. After component mount, I'm using render method before find and it works as expected. Example:

const wrapper = mount(<Provider store={store}><Units /></Provider>);
const nodes = wrapper.render().find('.unit'));

@wwwennn
Copy link

wwwennn commented Aug 24, 2018

meet with the same problem. html() is right but find couldn't find the dom. update() doesn't help.

@paddotk
Copy link

paddotk commented Sep 19, 2018

Using .render().find() did the trick for me, as in @d-e-p's example. Thanks @d-e-p

@testacode
Copy link

Using .render().find() doesn't allow me to simulate a click for a button. This is still happening.

@ljharb
Copy link
Member

ljharb commented Oct 16, 2018

I wouldn't recommend using .render(), nor using .simulate at all. If you want to invoke an onClick prop, do it explicitly.

byCedric added a commit to byCedric/GitHub-Website-legacy that referenced this issue Oct 20, 2018
This is a better workaround as opposed to comparing html output.

enzymejs/enzyme#1233
byCedric added a commit to byCedric/GitHub-Website-legacy that referenced this issue Oct 20, 2018
* test: refactor github organism test code with `.update()`

This is a better workaround as opposed to comparing html output.

enzymejs/enzyme#1233

* test: change github user highlight tests to `.update()` workaround

* test: add unit tests for app page component
@IamTheHttp
Copy link

still happened for me(I was also updating to React 16 and Enzyme all at once).

If you guys still wonder why people do that, it's because I updated to React 16, and then realized Enzyme has to be updated as well.. I guess for most people there's no reason to update enzyme alone if all is working :)

In any case, wrapper.update() solved it for me, wrapper.html() was showing the right DOM, but wrapper.find(...) was failing

@ljharb
Copy link
Member

ljharb commented Feb 16, 2019

@IamTheHttp you should absolutely downgrade back to react 15, upgrade to enzyme 3 and get everything passing, and then update to react 16.

@ErikParso
Copy link

@StefanoSega I had a similar problem. I have a component written in pure JS that inserts html inside React component. After component mount, I'm using render method before find and it works as expected. Example:

const wrapper = mount(<Provider store={store}><Units /></Provider>);
const nodes = wrapper.render().find('.unit'));

This wont allow me to simulate click.

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