Skip to content

[Tests] add hooks tests#2008

Merged
ljharb merged 6 commits into
enzymejs:masterfrom
chenesan:react-hooks
Jun 11, 2019
Merged

[Tests] add hooks tests#2008
ljharb merged 6 commits into
enzymejs:masterfrom
chenesan:react-hooks

Conversation

@chenesan

@chenesan chenesan commented Feb 7, 2019

Copy link
Copy Markdown
Contributor

This PR adds support for [basic hooks API] (https://reactjs.org/docs/hooks-reference.html#basic-hooks) (useState, useEffect, useContext, related issue #1938 #1996). Currently I'm not sure what hooks API is already working in enzyme shallow / mount and what isn't, so I'll start from adding test cases to confirm the current supports. I'll try to finish the following checklist in the next few days. Also tell me if it's better to have single PR for each api :-)

I've added some test cases for useState and found it doesn't work in shallow to setState.

(Updated: Created a checklist in #2011 to try to track any issue / PR in hooks, and concentrate on support useState in this PR)

@wodenx

wodenx commented Feb 7, 2019

Copy link
Copy Markdown

Would you consider adding useRef to the shortlist?

@chenesan chenesan mentioned this pull request Feb 8, 2019
14 tasks
@chenesan chenesan changed the title [WIP] Support React hooks [WIP] Support React hooks useState Feb 8, 2019
@chenesan

chenesan commented Feb 8, 2019

Copy link
Copy Markdown
Contributor Author

@wodenx I just created #2011 to try to track all of the hooks API support. And in this PR I'd like to work on useState. Feel free to open issue / PR for useRef :)

expect(wrapper.find('div').length).to.equal(1);
expect(wrapper.find('div').text()).to.equal('0');
});
it('handles setState returned from useState', () => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
it('handles setState returned from useState', () => {
it('handles setState returned from useState', () => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ljharb Can we add setprops test case to simulate componentwillReceiveProps and test using act ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, that sounds great.

describeIf(is('>= 16.8'), 'hooks', () => {
it('handles useState', () => {
const ComponentUsingStateHook = () => {
const [count] = React.useState(0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be pulled in react-compat, and not accessed off of React.

@perrin4869

Copy link
Copy Markdown

Hm... Any clue how you'll get this working? The upstream shallow renderer doesn't seem to support this

@hisapy

hisapy commented Feb 8, 2019

Copy link
Copy Markdown

Hi ...

I just upgraded to React v16.8.1 and I started having these warnings when testing a Hooks based component.

console.error ../../node_modules/react-dom/cjs/react-dom.development.js:506
    Warning: An update to _default inside a test was not wrapped in act(...).
    
    When testing, code that causes React state updates should be wrapped into act(...):
    
    act(() => {
      /* fire events that update state */
    });
    /* assert on the output */
    
    This ensures that you're testing the behavior the user would see in the browser. Learn more at https://fb.me/react-wrap-tests-with-act

Is this something that should be handled by enzyme? I didn't have these warnings when I was at the Hooks alphas

@ljharb

ljharb commented Feb 8, 2019

Copy link
Copy Markdown
Member

@hisapy enzyme has zero support for hooks right now; as for the warnings, that's because react added them between the alphas and the real release.

@hisapy

hisapy commented Feb 8, 2019

Copy link
Copy Markdown

Thx for the quick answer @ljharb ... I wish I could get rid of the warnings ...

Right now I'm trying to use act to see where it takes me.

Just for the record, I'm not actually testing the hooks. My component useState but in my tests I just mount the component and do the usual simulate stuff, like I would without hooks.

@chenesan

chenesan commented Feb 9, 2019

Copy link
Copy Markdown
Contributor Author

@perrin4869 I'd try to see what I can do in react shallow renderer.

@Jessidhia

Copy link
Copy Markdown

act() should be used by any React code that may interact with state, whether you use hooks or not (it'll be important for ConcurrentMode too). It's expected to be reexported and/or automatically wrapped by testing libraries. See https://github.com/threepointone/react-act-examples/blob/master/README.md

@ljharb

ljharb commented Feb 18, 2019

Copy link
Copy Markdown
Member

It seems like something enzyme could/should perhaps abstract away from the user?

@chenesan

Copy link
Copy Markdown
Contributor Author

I think that the failed shallow test is related to react/react#14840 . After react/react#14841 fixes the issue the test should pass.
As for act() I guess we could wrap the call inside returned render of ReactSixteenAdapter.createMountRenderer. But I'm not sure if this should be included in this PR.

@ljharb

ljharb commented Mar 5, 2019

Copy link
Copy Markdown
Member

@chenesan we should probably merge the act change in a separate PR first.

@chenesan

chenesan commented Mar 6, 2019

Copy link
Copy Markdown
Contributor Author

@ljharb I would try to work on act() this week with some use cases in https://github.com/threepointone/react-act-examples/blob/master/README.md

(Update: Send PR in #2034)

@pgangwani

pgangwani commented Mar 6, 2019

Copy link
Copy Markdown

@ljharb @chenesan : I would like to contribute here. Please help me to get kickstarted. Reading Contributing guide.

@chenesan

chenesan commented Mar 6, 2019

Copy link
Copy Markdown
Contributor Author

Hi @pgangwani glad to hear this. What could I do to help you?
If you're not sure where to start maybe you could find issues with help wanted label. Or maybe you could help add some test cases for react hooks

@pgangwani

pgangwani commented Mar 7, 2019 via email

Copy link
Copy Markdown

@ljharb

ljharb commented Mar 7, 2019

Copy link
Copy Markdown
Member

@pgangwani please post a link to a branch on #2029, and i'll cherry-pick it into that PR.

@pgangwani

Copy link
Copy Markdown

@ljharb : Is there any chatroom ? As I am new, may need little help to start contributing so kickstart well and try sinking in the pattern to work with you

@chenesan

chenesan commented Mar 7, 2019

Copy link
Copy Markdown
Contributor Author

Hi @pgangwani there is gitter chatroom :-)

@pgangwani

Copy link
Copy Markdown

@chenesan Added PR #2041 for test cases. Please review

@chenesan

chenesan commented Mar 9, 2019

Copy link
Copy Markdown
Contributor Author

@pgangwani Glad to see your work! I'm not a maintainer here, though. So I have no permission to approve the changes and I think my review doesn't count a lot. Let's wait for @ljharb 's review.

@pgangwani

Copy link
Copy Markdown

@chenesan @ljharb All these cases are already covered in #2041

@chenesan

Copy link
Copy Markdown
Contributor Author

@pgangwani good to see #2041 ;)
@ljharb If these cases are included in #2041, should I close this?

@ljharb

ljharb commented Mar 11, 2019

Copy link
Copy Markdown
Member

@chenesan no; but perhaps you could rebase yours on top of that? I'll keep both open and in sync, and merge them together.

@chenesan

chenesan commented May 29, 2019

Copy link
Copy Markdown
Contributor Author

@Acesmndr I believe that works with shallow after [email protected] (See react/react#15120)

@Acesmndr

Copy link
Copy Markdown

@chenesan I tried but even with React v16.8.6 Enzyme V3.9.0 and Enzyme Adapter v1.13.2 The test is failing. Here is a sample file along with its specs.

import React, { useLayoutEffect, useState } from 'react';

type FormElement = React.FocusEvent<HTMLInputElement>;

const NewForm: React.FC = () => {
  const [email, setEmail] = useState<string>('');
  useLayoutEffect(() => {
    console.log('it should be called but isn\'t called', email);
  }, [email]);

  const handleEmailChange = (evt: FormElement) => {
    const emailValue = (evt.target as HTMLInputElement).value.trim();
    setEmail(emailValue);
    console.log('This gets called but the setEmail is not called');
  };
  
  return (
    <form>
      <input
        id="batman"
        value={email}
        onChange={handleEmailChange}
      />
    </form>
  );
};

export default NewForm;

and the specs:

import * as React from 'react';
import { shallow } from 'enzyme';

import NewForm from './test';

describe('Form', () => {
  let container;

  beforeAll(() => {
    container = shallow(<NewForm />);
  });
  describe('email', () => {
    it('sets value', () => {
      container.find('input').prop('onChange')({
        target: {
          value: '[email protected]',
        },
      });
      expect(container.find('input').prop('value')).toEqual('[email protected]');
      // console.log(container.find('input').props());
    });
  });
});

And the error it's throwing is that email isn't updated even though setEmail is called.
image
Am I doing anything wrong?

@yuritoledo

Copy link
Copy Markdown

@Acesmndr
Have u tried like this?

<input
  id="batman"
  value={email}
  onChange={e => setEmail(e.target.value)}
/>

@Acesmndr

Acesmndr commented Jun 4, 2019

Copy link
Copy Markdown

@yuritoledo Must be one of the dependencies in my project as it didn't work even with that approach. But once I setup everything from scratch using a boilerplate it did indeed work (even my typescript code). :) Must be some rogue npm package which I need to figure out.

@JonRCahill

JonRCahill commented Jun 8, 2019

Copy link
Copy Markdown

I am unable to get shallow working with the useState hook either. Maybe it is because I am using react-native?

My code is as follows:

import React, { useState } from "react";
import { View, Button } from "react-native";
import { shallow } from "enzyme";

const Example = function({ button2Press }) {
const [name, setName] = useState("");

  return (
    <View>
      <Button title="Button 1" onPress={() => setName("Hello")} />
      <Button title="Button 2" onPress={() => button2Press(name)} />
    </View>
  );
};

describe("Example", () => {
  it("updates the state", () => {
    const button2Press = jest.fn();
    const wrapper = shallow(<Example button2Press={button2Press} />)
    const button1 = wrapper.findWhere(node => node.prop("title") === "Button 1")
                        .first();
    const button2 = wrapper.findWhere(node => node.prop("title") === "Button 2")
                        .first();

    button1.props().onPress();
    button2.props().onPress();

    expect(button2Press).toHaveBeenCalledWith("Hello");
  });
});

The error I get is that button2Press is always called with an empty string rather then what is being set in state.

I am using the dependencies:

  • react 16.8.6
  • react-native 0.59.9
  • enzyme 3.9.0
  • enzyme-adapter-react-16 1.13.1

@ljharb

ljharb commented Jun 8, 2019

Copy link
Copy Markdown
Member

@BobaFaux update to enzyme v3.10 and try again. Either way tho, this is a PR to add useState support, so it's likely that until this is merged, it won't work.

@chenesan

chenesan commented Jun 9, 2019

Copy link
Copy Markdown
Contributor Author

@BobaFaux Try to call update() after the first button pressed and find the button2 again:

const button1 = wrapper.findWhere(node => node.prop('title') === 'Button 1')
        .first();
      button1.props().onPress();
      wrapper.update();
      const button2 = wrapper.findWhere(node => node.prop('title') === 'Button 2')
        .first();
      button2.props().onPress(); 
      wrapper.update();
      expect(button2Press).toHaveBeenCalledWith("Hello");

@JonRCahill

Copy link
Copy Markdown

@chenesan thanks that worked for me. I had tried the wrapper.update(); before but my problem was then finding the element again after update is called.

@ljharb ljharb left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've rebased this on top of a commit that adds a new way to share hook tests across shallow and mount. cc @chenesan for final tweaks to get this in

});
});

// todo: enable when useEffect works in the shallow renderer

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the github issue this is linked to?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread packages/enzyme-test-suite/test/shared/hooks/custom.jsx Outdated
);
};

// TODO; useContext seems to not work in the shallow renderer's initial render

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we file a bug on react about this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems not a bug in react side -- I've tried to write a test to render text with the context initial value and that works in shallow. I think it's more about the bug mentioned in #2008 (comment) . But I don't know where the related issue is (e.g. does enzyme has supported .dive() into inner component to get the value from outside provider in shallow?)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, ideally that is supported.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a bug. It looks like even we wrap a Provider outside UiComponent, the context value read by useContext still does not change

});
});

// TODO: useContext: enable when shallow dive supports createContext

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and also add the link here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isShallow,
}) {
describeIf(hasHooks, 'hooks: useEffect', () => {
// TODO: enable when the shallow renderer fixes its bug

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we have an issue link here?

expect(setDocumentTitle.args).to.deep.equal([[expectedCountString(0)]]);
});

// TODO: useEffect fixme

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this broken on mount?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not broken, just need a .act wrap; I'd fix this with .simulate .

);
}

// TODO: fixme when useEffect works in the shallow renderer

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's link the issue here too

Comment thread packages/enzyme-test-suite/test/shared/hooks/useState.jsx Outdated
@chenesan

Copy link
Copy Markdown
Contributor Author

I think those todos are not from this PR originally but the rebased code in #2041 ; I'll look into them to make progress of #2011, though.

@chenesan

Copy link
Copy Markdown
Contributor Author

@ljharb Just responsed all the comments and also fixed the lint issues. could be reviewed again :-)

@chenesan
chenesan force-pushed the react-hooks branch 2 times, most recently from b458b99 to aae8ad1 Compare June 11, 2019 06:47
@chenesan

Copy link
Copy Markdown
Contributor Author

It's strange the the statement in describe of useContext test is still running under React v16.2 even though we have describeIf(hasHook, ...) check, which cause ci failed. I just moved common logic outside of it() into it().

@ljharb

ljharb commented Jun 11, 2019

Copy link
Copy Markdown
Member

@chenesan the describe bodies run even if they're skipped, so they still have to work in all versions.

@ljharb ljharb changed the title [WIP] Support React hooks useState [Tests] add hooks tests Jun 11, 2019
@ljharb
ljharb merged commit d9fa3f0 into enzymejs:master Jun 11, 2019
ljharb added a commit that referenced this pull request Jun 11, 2019
…react-hooks

[Hooks Testing] Added Test cases for useState, useEffects & customHooks
@rodoabad

Copy link
Copy Markdown

Hey @chenesan were you able to get this to work for shallow.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.