caraclarke
caraclarke

Reputation: 410

Mocking current value of react ref in jest for shallow test

I am trying to mock this.ref.current.value as part of a test of a function in my React component. Currently current is null because I am doing a shallow mount of my component. I am trying to find a way to mock what current.value returns for my ref so that I can test another part of the function, what the refs value is doesn't actually matter for this test.

Actual function:

copyDefinitionTarget = selectedInput => () => {
    // get and select the input value
    const copyText = this[`${selectedInput}`].current.value;

    // copy to clipboard
    navigator.clipboard.writeText(copyText);
  };

Test code:

it('calls navigator.clipboard appropriately to copy the text in copyDefinitionTarget', () => {
    component = shallow(<Alert {...props} />);

    component
      .dive()
      .find('Item')
      .dive()
      .find('Button')
      .simulate('click');

    expect(navigator.clipboard.writeText).toHaveBeenCalled();
  });

Test failure:

TypeError: Cannot read property 'value' of null

      50 |     // get and select the input value
    > 51 |     const copyText = this[`${selectedInput}`].current.value;

Is there any way to do this? I care about testing that navigator.clipboard was called not what it was called with.

Updating because I've changed my code to use this.ref instead of stringRefName Actual function:

copyDefinitionTarget = selectedInput => () => {
    // get and select the input value
    const copyText = selectedInput.current.value;
    // copy to clipboard
    navigator.clipboard.writeText(copyText);
  };

Test code:

it('calls navigator.clipboard appropriately to copy the text in copyDefinitionTarget', () => {
    component = shallow(<Alert {...props} />);
instance = component.instance();

    // we are clicking on the first Alert Item
    // so mock that ref specifically
    instance.firstNameRef = {
      current: {
        value: 'stuff'
      }
    };

    component
      .dive()
      .find('Item')
      .dive()
      .find('Button')
      .simulate('click');

    expect(navigator.clipboard.writeText).toHaveBeenCalled();
  });

Function call:

<Item
 inputRef={this.firstNameRef}
 inputValue={`${realmName}`}
 copyDefinitionTarget={this.copyDefinitionTarget(this.firstNameRef)}
/>

Upvotes: 6

Views: 17251

Answers (2)

etarhan
etarhan

Reputation: 4176

You could go ahead and do something like this:

const component = shallow(<Alert {...props} />);
const selectedInput = 'ref';
component.instance()[selectedInput] = {
  current: {
    value: 'stuff'
  }
}
navigator.clipboard = {writeText: jest.fn()}
component
  .dive()
  .find('Item')
  .dive()
  .find('Button')
  .simulate('click');
expect(navigator.clipboard.writeText).toHaveBeenCalled();

Note: I am not sure what kind of string selectedInput should be, you could pass any string which is appropriate according to your real component code.

Since the ref exists as a instance property on the component, you can just pass any object you wish as long as it looks like current.value, then you can replace the copy function with a mock, simulate the click and then see if writeText was called.

Upvotes: 4

caraclarke
caraclarke

Reputation: 410

Posting another approach that could be taken in case you are passing the actual ref instead of a string. I pulled getting the value from the ref into its own function and mocked that in my test

Actual code:

  // get the value from ref
  getRefValue = ref => ref.current.value;

  // copy value
  copyDefinitionTarget = selectedInput => () => {
    // get and select the input value
    const copyText = this.getRefValue(selectedInput);
    // copy to clipboard
    navigator.clipboard.writeText(copyText);
  };

Test code:

it('calls navigator.clipboard appropriately to copy the text in copyDefinitionTarget', () => {
    component = shallow(<MetadataAlert {...props} />);
    instance = component.instance();

    jest.spyOn(instance, 'getRefValue').mockImplementationOnce(() => '💻');

    component
      .dive()
      .find('Item')
      .dive()
      .find('Button')
      .simulate('click');

    expect(navigator.clipboard.writeText).toHaveBeenCalled();
  });

Upvotes: 0

Related Questions