enzyme: New "ref" style is not supported with mount()

It seems that wrapper.ref() does not work with the new “ref” style.

Here is a reproduction.

src/MyComponent.js

import React from 'react';

export class MyComponent extends React.Component {
  render() {
    return (
      <div>
        <Subcomponent 
          ref="oldStyle"
        />
        <Subcomponent 
          ref={(ref) => this.newStyle = ref}
        />
      </div>
    );
  }
}

export class Subcomponent extends React.Component {
  render() {
    return (
      <span>
        Hello
      </span>
    )
  }

The second test fails

import React from 'react';
import { mount, shallow } from 'enzyme';
import {MyComponent, Subcomponent} from '../src/MyComponent';
import { expect } from 'chai';

describe('<MyComponent />', () => {
  it('should find the old-style ref-ed component', () => {
    const wrapper = mount(<MyComponent />);
    expect(wrapper.ref('oldStyle').type()).to.equal(Subcomponent);
  });

  it('should find the new-style ref-ed component', () => {
    const wrapper = mount(<MyComponent />);
    expect(wrapper.ref('newStyle').type()).to.equal(Subcomponent);
  });
});

Looking at the react doc, they mention that the “‘ref’ callback will be executed immediately after the component is mounted”. So maybe this bug is caused because enzyme does not go through all the full life-cycle events of the component.

Thanks!

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 31
  • Comments: 24 (8 by maintainers)

Most upvoted comments

Is there a solution to original issue posted by @jnak with new style refs? Seems this was hijacked about discussion of how to find DOM elements contained within a wrapped Modal. Original question refers more to how to access a member variable rather than a DOM element. Or is this still an unresolved bug?

@draktheas wrapper.instance().memberProperty should work fine?

Thanks @jramirezl, it works!

const wrapper = mount(<MyModalContainer />)
const dialog = wrapper.find(Modal).getNode()._modal.getDialogElement()
const modal = new ReactWrapper(dialog, dialog)
console.log(modal.html())
console.log(modal.find('.thing-inside-modal'))

@ljharb looks like forceUpdate is deprecated and currently it’s just wrapper.update(). But still it hadn’t helped much.

But in contrast everything works ok after I call wrapper.setProps. After that all refs are accessible in render method.

UPD: Oh, looks like it’s connected with #1245

@ygnoh ref callbacks should work; you’d do wrapper.instance().property where your ref callback assigned to this.property. If that doesn’t work for you (using mount), please file an issue

I can’t get the combination of mount/ref/memberProperty to work somehow.

// FileUpload.js
import React, { PureComponent} from 'react';
import PropTypes from 'prop-types';

export default class FileUpload extends PureComponent {
    static propTypes = {
        uploadFile: PropTypes.func.isRequired,
    };

    fileInput = null;

    uploadFile = () => {
        // At this point this.fileInput is undefined
        if (this.fileInput.files.length) {
            this.props.uploadFile(this.fileInput.files[0]);
        }
    };

    render() {
        return (
            <input
                type="file"
                ref={(elem) => { this.fileInput = elem; }}
                onChange={this.uploadFile}
            />
        );
    }
}



// FileUpload.test.js
import React from 'react';
import { mount, configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import FileUpload from './FileUpload';

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

describe('FieldUpload', () => {
    it('calls uploadFile if a file was selected', () => {
        const uploadFile = jest.fn();
        const component = mount(
            <FileUpload uploadFile={uploadFile}/>
        );

        const file = 'test.zip';
        const changeEvent = {
            target: {
                files: [file]
            }
        };
        component.find('input[type="file"]').simulate('change', changeEvent);
        expect(uploadFile).toHaveBeenCalledWith(file);
    });
});

The test fails because the mock function is not called. Any ideas?

I figured this out.

import { Modal } from 'react-bootstrap'

const modal = home.find(Modal)
const instance = modal.node._modal
var dialog = new ReactWrapper(instance, instance)

If there are better ways to do this, would love to hear it. Also is this the correct way to create a new instance of ReactWrapper from a random react component?

Faced some issue with new styled refs.

I’m using ref check to render node like

<div ref={(elem) => this.div = elem} />

{ this.div && this.props.showSmthAwesome && <AwesomeComponent />}

When I run enzyme.mount, looks like this.div remains undefined and it doesn’t mount <AwesomeComponent />

Is it something known or a bug?

@ljharb If I want to use new ref style like @timotgl’s code, should I stub all functions which contains the refs? or should I always stub the refs in test code? I think Enzyme have to treat the refs correctly 😦

@timotgl it’s less performant and impossible to test.

@timotgl yes, a few things. a) you can’t mock out something via a prop that isn’t a prop. b) don’t ever use arrow functions in class methods. use a constructor-bound instance method, and then you can spy on FileUpload.prototype.uploadFile.