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

Unable to find Component by displayName when using React.forwardRef #1896

Closed
jackson-sandland opened this issue Nov 9, 2018 · 4 comments
Closed

Comments

@jackson-sandland
Copy link

jackson-sandland commented Nov 9, 2018

Current behavior

Unable to find a childComponent by displayName when shallow rendering the parent while using wrapper.find("displayName") unless displayName includes ForwardRef(childComponent) || ForwardRef

I'm aware of some issues regarding forwardRef, and displayName so I've tried a few different approaches.

Test:

  it("renders text", () => {
    component = shallow(<Parent {...props} />).find("Button");
    component.simulate("click");
    expect(props.toggleText).to.have.been.called;
    expect(component.children().text()).to.contain("text");
  });

Default approach: (Above test fails)

childComponent only discoverable via find by "ForwardRef"

const Button = forwardRef((props, ref) => (
  return (
    <button
      className={props.className}
      ref={ref}
    >
      <span>{children}</span>
    </button>
  )
));
export default Button;

Approach #1: (Above test fails)

childComponent only discoverable via find by "ForwardRef(Button)"

const Button = (props, ref) => {
  const {
    className,
  } = props;
  return (
    <button
      className={className}
      ref={ref}
    >
      <span>{children}</span>
    </button>
  );
};

const buttonWithRefForwarding = forwardRef(Button);
buttonWithRefForwarding.displayName = "Button";

export default buttonWithRefForwarding;

Output of wrapper.debug():

<div className="container">
  <ForwardRef(Button) className="btn">
    Text here
  </ForwardRef(Button)>
</div>

Approach #2:

(Above test passes with this approach in the parentComponent unit test above, but fails right out of the gate for the Button unit tests)

const Button = function Button(Component) {
  const returnedButton = (props) => {
    const {
      className,
    } = props;
    return (
      <button 
        className={className}
        ref={ref}
        >
          <span>{children}</span>
      </button>
    );

    function forwardRef(props, ref) {
      return returnedButton;
    }

    return React.forwardRef(forwardRef);
  }
}

export default Button;

Failing Button Unit test:

describe("<Button />", () => {
  let component;
  let props = {
    className: "class"
  }
  beforeEach(() => {
    component = shallow(<Button {...props} >{children}</Button>);
  });
  
  These both fail -->
  it("should have a displayName, () => {
    console.log(component.displayName) <-- undefined
    console.log(component.debug()) <-- '{{ $$typeof: Object(Symbol(react.forward_ref)_16.czyhiszjh), 
    render: [Function: forwardRef] }}'
  })
  
  it("should set displayName", () => {
    const button = mount(
      <div>
        <Button {...props}>{children}</Button>
      </div>
    );
    expect(button.find("Button")).to.have.length(1);
  });

Console output on failure:

✗ should set displayName
	Invariant Violation: Objects are not valid as a React child (found: object with keys {$$typeof, render}). If you meant to render a collection of children, use an array instead.
	    in Button
	    in div (created by WrapperComponent)
	    in WrapperComponent

Output of wrapper.debug() In the parentComponent:

<div className="container">
  <Button className="btn">
    Text here
  </Button)>
</div>

Output of wrapper in the childComponent:

ShallowWrapper()

Output of wrapper.debug() In the childComponent:

''

Output of wrapper.find('Button') In the childComponent:

ShallowWrapper()

Output of wrapper.find(Button) In the childComponent:

ShallowWrapper()

Expected behavior

Users should be able to use wrapper.find('displayName") to make assertions on a childComponent that uses React.forwardRef without having to set the displayName with ForwardRef(childComponent) || ForwardRef

Your environment

Mac OS High Sierra 10.13.6
Mac Book Pro

API

shallow

Version

library version
enzyme @3.6.0
react @16.5.2
react-dom@ 16.5.2
react-test-renderer @16.5.2

Adapter

library version
enzyme-adapter-react-16 @1.5.0
enzyme-adapter-utils @1.8.0
@ljharb
Copy link
Member

ljharb commented Nov 10, 2018

Approach 1 fails because that's not how React.forwardRef works - approach 2 is correct.

With that approach, can you show the code for wrapper, wrapper.debug(), and then wrapper.find(Button) vs wrapper.find('Button')?

@jackson-sandland
Copy link
Author

This has been resolved. The issue was needing a clean install after updating all dependencies.
Running rm -rf node_modules followed by npm i was the solution.
Appreciate your help.

@ljharb
Copy link
Member

ljharb commented Nov 12, 2018

This appears to be working for me with latest enzyme and the enzyme adapters. Can you try updating enzyme-adapter-react-16?

@ljharb ljharb closed this as completed Nov 12, 2018
@ljharb ljharb reopened this Nov 12, 2018
@ljharb
Copy link
Member

ljharb commented Nov 12, 2018

I'll close this with some more test cases :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants