Not simulating 'change' using enzyme - reactjs

I have a Reactjs component and it has one button and a date-range picker.
I want to simulate onclick and onchange events of button and picker respectively.
I'm able to simulate onclick of the button. but on change of datepicker is not working
I have tried this
headerComponent.find(`#prev_button`).at(1).simulate("click");
headerComponent.find(`#dropdown`).at(1).simulate("change", { value: "t" });
please see this sandbox click here for full code and test file

Based on Enzyme documentation you make a mistake on your second argument on simulate function.
To simulate changes on the input, you should change it like this :
headerComponent.find(`#dropdown`).at(1).simulate("change", { target: { value: "t" } });

Testing with enzyme is tricky. You should try not to test dependencies because you trust those are already tested. Having said that, you could shallow render instead of mounting and look for the RangePicker component in the shallow tree, get the handler you are passing in the onChange prop and call it manually, then check the callback prop you pass to your component is called with the expected value.
describe.only("test", () => {
it("should render", () => {
const callBackToSetDates = jest.fn();
const callBackToSetFilter = jest.fn();
const wrapper = shallow(
<Header
{...headerProps1}
callBackToSetDates={callBackToSetDates}
callBackToSetFilter={callBackToSetFilter}
/>
);
const rangePickerOnChange = wrapper.find("RangePicker").prop("onChange");
rangePickerOnChange("someValue");
expect(callBackToSetDates).toHaveBeenCalledWith("someValue");
});
});
the purpose is to test only the logic you add inside your component, i.e., you transform the value you get from the RangePicker to something else
<RangePicker
...
onChange={(value) => {
callBackToSetDates(`I'm transforming ${value}`);
}}
/>
and in your test
rangePickerOnChange("someValue");
expect(callBackToSetDates).toHaveBeenCalledWith("I'm transforming someValue");
you can see it working here https://codesandbox.io/s/cool-rosalind-uec6t?file=/src/tests/index.test.js
If you really want to keep testing what the actual user sees, you'll need to fire the events that the user does when using the component. In this case: you need to click the input, look for a date, click it, then click another date to completely fire the onChange event of the RangePicker component. You might look at how antd test it and copy the necessary jest configuration they have to mock some DOM APIs

Related

How can I test if a blur event happen in onClick event handler?

I am trying to write a jest test for my inputBox component that include a button.
Have a function 'onClick' as a prop inside this component.
<inputBox
placeholder={'Type here..'}
onClick={() => {inputRef.current.blur()}}
button={true}
ref={inputRef}
/>
I want to test the event 'inputBox is blur once I click the button'.
Below is the test code:
it('invokes blur after click button',() => {
const onBlurSpy = jest.fn();
const { getAllByRole } = render(inputBox);
fireEvent.click(getAllByRole('button'))
expect(onBlurSpy).toHaveBeenCalled();
});
Receive below error:
expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
Any idea for this?
The onBlurSpy mock function isn't being called in the test because it isn't attached to the inputBox component. The blur function would likely need to be passed in as a prop to the component if you wanted to use jest.fn() to mock it.
If you want to check if the blur function has been called without adjusting the component's props, I'd recommend using Jest's spyOn method.
Give the function you want to spy on a name so it can be referenced:
<inputBox
...
onClick={this.blurFunction}
...
/>
Then use Jest's spyOn function in the test:
const onBlurSpy = jest.spyOn(inputBox.instance(), "blurFunction")
I'd also recommend reading this answer from a related StackOverflow question for some additional context.

Testing onChange event with Jest

According to my code coverage, I need to test the function called in my onChange event. This is actually where I update my functional component's state using the useState hook.
Here is my component :
const Component:React.FC<{}> = () => {
const {value, setState} = useState('');
return(
<View>
<CustomComponent
onChange={(value) => setState(value)}
/>
</View>
)
}
The customComponent is a component derived from the React Input component.
When text is changed inside of it, it calls the onChange function passed as its prop with the text. This is how it comes up to the parent Component which sets the value of the text input in its state as displayed above.
My code coverage returns this analysis :
onChange={//(value) => setState(value)//}
Where the code between the // has to be covered. I don't really understand how I can cover this. First thought was use mock functions, but I can't seem to find how to mock it to the onChange event since I don't pass anything as prop to the main Component.
After a few tests, I finally understood that the coverage wasn't asking for the actual test of the onChange function but actually the value that is evaluated. Therefore, here is what I am doing:
Fetching the TextInput Child component
Changing its Text
Evaluating what it renders
I am using #testing-library/react-native here because it makes selecting tree components easier with the use of accessibilityLabel for example (It actually made me understand the importance of that prop).
Here is what a test looks like:
describe('Testing useState functions', () => {
test('test', () => {
//Rendering the component and its tree
const { container, getByLabelText } = render(<SignupView />);
//Extracting the child, username_input component with his accessibilityLabel
const username_input = getByLabelText('username_input');
const email_input = getByLabelText('email_input');
//Fire a native changeText event with a specific value
fireEvent.changeText(username_input, 'doe');
fireEvent.changeText(email_input, 'doe#joe.com');
//Checking the rendered value
expect(username_input.props.value).toEqual('doe');
expect(email_input.props.value).toEqual('doe#joe.com');
});
});

How to test a function that has been passed by props

If I have a component like this
import React from "react";
const GettingStarted = ({ object }) => <Button onPress={() => object.next()} />;
How would you test with jest that object.next() gets called inside the component?
The basic idea would be to spy on the function being passed to the onPress. Then, you would simulate an onPress event on the button and check that the spied upon function was called with any parameters, etc. Then you would test the actual output of the function. So, for example, if the function changes the text in the button from 'Click Me' to 'Clicked!', you would assert on the first text property before the click and then check the updated one.
Example with Jest:
const onPressSpy = jest.fn();
const gettingStartedButton = shallow(<GettingStarted object={onPressSpy} />);
expect(gettingStartedButton.find('button').children().text()).toBe('Click Me!');
gettingStartedButton.find('button').simulate('press');
expect(onPressSpy).toHaveBeenCalled();
expect(gettingStartedButton.find('button').children().text()).toBe('Clicked!');

How to use enzyme ShallowWrapper to find a React Component stored as a prop in another React Component?

I have a jest/enzyme test which creates a ShallowWrapper around a component, finds a specified semantic-ui-react Button (by id), simulates a click on the button, and then looks to see if the click toggled certain content.
Sample JSX:
<Popup
trigger={<Button onClick={this.toggleShowThing} id="special-btn">a button</Button>}
content="Popup Words"
/>
{this.state.showThing &&
<div className="special-thing">The Thing's Words</div>
}
Sample Test:
it('shows the thing when the button is clicked', () => {
const wrapper = shallow(<MyComponent />);
wrapper.find('#special-btn').simulate('click', { preventDefault() {} });
expect(wrapper.find('.special-thing').exists()).toBe(true);
});
This test worked when I just had the Button. When I added the Popup and the Button was placed into the trigger prop then I received an error because #special-btn could not be found.
Error: Method “props” is only meant to be run on a single node. 0 found instead.
An enzyme snapshot of the component shows that the Popup looks like this:
<Popup
content="Popup Words"
on="hover"
position="top left"
trigger={
<Button
id="special-btn"
onClick={[Function]}
>
a button
</Button>
}
/>
I need my test to work again. How do I gain access to the #special-btn again in the test so that I can call .simulate('click') on it?
This is what worked for me, although there is no documentation for it:
import {shallow, ShallowWrapper} from "enzyme";
it('shows the thing when the button is clicked', () => {
const wrapper = shallow(<MyComponent />);
const button = new ShallowWrapper(
wrapper.find('Popup').prop('trigger'), wrapper
);
button.simulate('click', { preventDefault() {} });
expect(wrapper.find('.special-thing').exists()).toBe(true);
});
In other words:
Find the Popup component.
Get the component rendered in its trigger prop. Note that this is not yet a shallow wrapper, so no fancy APIs yet.
Manually create the wrapper using ShallowWrapper (it's important to pass the second argument).
Now you can access all the enzyme APIs to interact with the button.
Note, it seems that you can avoid using the constructor and use wrap() utility method instead (also not documented):
const button = wrapper.wrap(wrapper.find('Popup').prop('trigger'));
Assuming Popup is some third-party component that has already been tested, I would approach testing the following way:
(1) Find the Popup and check if the trigger prop's Button's onClick prop is componentWrapper.instance().toggleShowThing
(2) As a separate thing, set this.state.showThing to false and verify no div with className special-thing is rendered; set this.state.showThing to true and verify it is rendered.
(*) this.toggleShowThing should also be tested on its own.
The problem is that you cannot do it. You need to rewrite your test. Your button is now wrapped by a Popup component thus you don't have access to it. But you can move your selector to the Popup and test if clicking the popup triggers required change. There's no other way around.
// JSX
<Popup
trigger={<Button onClick={this.toggleShowThing} id="special-btn">a button</Button>}
content="Popup Words"
id="popup"
/>
{this.state.showThing &&
<div className="special-thing">The Thing's Words</div>
}
// test
it('shows the thing when the button is clicked', () => {
const wrapper = shallow(<MyComponent />);
wrapper.find('#popup').simulate('click', { preventDefault() {} });
expect(wrapper.find('.special-thing').exists()).toBe(true);
});

React Native Testing - How to TDD and test components beyond snapshots?

I'm really struggling with how to test React Native components beyond using snapshots from jest. Those are great for testing the look of something based on some data passed into a component, but it doesn't seem like there is a way to test events or event handlers, checking if event listeners were setup or disconnected correctly in lifecycle methods, etc...like it feels like I'm either missing something or the tooling is not complete.
Also on a side note snapshot testing feels backwards in terms of TDD since you can only write your tests once you have your application code...any thoughts on this?
With snapshot test you can only check the values in props and styles etc. To check some logic in container i used 'shallow' from enzyme ;)
You can still use snapshots with events for things like: click a button and verify the rendered output after the click:
it('should render Markdown in preview mode', () => {
const wrapper = shallow(
<MarkdownEditor value="*Hello* Jest!" />
);
expect(wrapper).toMatchSnapshot();
wrapper.find('[name="toggle-preview"]').simulate('click');
expect(wrapper).toMatchSnapshot();
});
To test that event handler was called property you can do something like that:
it('should pass a selected value to the onChange handler', () => {
const value = '2';
const onChange = jest.fn();
const wrapper = shallow(
<Select items={ITEMS} onChange={onChange} />
);
expect(wrapper).toMatchSnapshot();
wrapper.find('select').simulate('change', {
target: { value },
});
expect(onChange).toBeCalledWith(value);
});
(Both examples are from my article on testing React components with Jest.)

Resources