React mock submit form function using Jest and Enzyme - reactjs

I'm trying to create a "simple" test to check if the onSubmit function of the form has been added / not removed from the component.
Form component:
<form onSubmit={SubmitForm} data-test="submit-new-product-form" >
<TextField type="text" ref={enteredNameRef} className="text-field" label="Name" value={enteredName} onChange={event => { handleChange('enteredName', setEnteredName, event.target.value) } } data-test="input-entered-name" /></form>
Test example:
test('should call submitNewProduct function upon form submission', () => {
let submitNewProduct = jest.fn(); //This is the mock I'm trying to put to check if it was called
let wrapper = shallow(<AddNewProduct />);
let form = wrapper.find("[data-test='submit-new-product-form']");
//This is where things don't make sense to me. Above I have the form component and I need to assign the
//submitNewProduct mock function to its onSubmit prop. But I don't know how.
form.simulate('submit', { preventDefault: () => console.log('preventDefault') });
expect(submitNewProduct).toHaveBeenCalled();
});
Any ideas please?

Related

How to do the unit testing of history.push function?

I want to unit test the following element, especially the onClick function, But have no idea how to do the unit testing
const baseURL = "/Security/Users/";
return (
<div>
<div className="flex-between">
<button
className="bt-save"
onClick={() => history.push(baseURL + "Add")}
>
Add
</button>
</div>
</div>
);
this is related to react unit testing using jest. From this, I want to unit test the Add Button onClick function.
Here is my approach to unit test this function
it('Should run the callback function when clicked', async () => {
const onClick = jest.fn(baseURL + "Add")
render(<button push={onClick}> Add </button>)
const addButton = screen.getByText('Add')
await userEvent.click(addButton)
expect(onClick).toHaveBeenCalled()
})
I'm getting the following error when I'm trying to do the testing.
I'm getting this result on the console
Can anyone help me understand this onClick function unit testing
You can accept an event callback as a prop of the component:
<button
className="bt-save"
onClick={() => props.onAdd()}
>
Then you can unit test using React Testin Library:
it("Should call onSave callback when clicking Add", async () => {
const onAddMock = jest.fn();
render(<MyComponent onAdd={onAddMock} />);
await userEvent.click(screen.getByText("Add"));
expect(onAddMock).toHaveBeenCalled();
}

Is there a way to have 2 onSubmit events in React?

I was wondering if anyone could explain how I can have 2 onSubmit events in React? I have a button for clicking a form on submission, and it has a thank you message popping up, but I'm trying to clear the localStorage at the same the form is submitted. I have tried this:
const handleSubmit = (e) => {
e.preventDefault();
alert(`Thank you for your order! ^_^ Please check your texts for updates!`);
toggle()
};
const clearCartStorage = () => {
localStorage.clear();
}
<Form onSubmit={handleSubmit && clearCartStorage()}>
These work separately, but when I have the && only the clearCartStorage function will run without the handleSubmit pop up.
Make a new function which calls those other functions:
<Form onSubmit={(e) => {
handleSubmit(e);
clearCartStorage();
})>
Careful, you are invoking clearCartStorage when you create the Form component there.
<Form onSubmit={handleSubmit && clearCartStorage()}> ❌❌❌
It should take one function which can call multiple.
I would set it up like this. It's more common to keep the function definition out of the returned JSX.
const MyComponent = () => {
const handleSubmit = (e) => {
e.preventDefault();
clearCartStorage()
alertUser()
};
const clearCartStorage = () => {
localStorage.clear();
}
const alertUser = () => {
alert(`Thank you for your order! ^_^ Please check your texts for updates!`);
toggle()
}
return <Form onSubmit={handleSubmit}>
}
Neither of the responses worked. I should have posted my whole code, but what I did to fix it was create a separate component with the new function that runs the alert and clearStorage functions. Having everything on the same component within a modal div was not working.
So I basically have:
<Modal>
<FormComponent /> // my new function in here
</Modal>

How to test if some element was enabled/disabled correctly after click a Checkbox?

I'm trying to test a component that have a <Switch/> (using Material-UI) and a <TextField/>. When user clicks in this Switch, it makes another field (<TextField/>) enable/disabled. The code works well, but my test using JEST always fails.
...
export function MyComponent(): JSX.Element{
const [enableExpiresOn, setEnableExpiresOn] = useState(false);
...
function handleAccountExpiresOn(e: React.ChangeEvent<HTMLInputElement>) {
setEnableExpiresOn(e.target.checked);
}
return (
<form ...>
...
<div>
<FormControlLabel
control={
<Switch
id="account-expires"
onChange={handleAccountExpiresOn}
name="account-expires"
color="primary"
checked={enableExpiresOn}
/>
}
label={t("Account Expires On")}
/>
</div>
<div>
<TextField
id="account-expires-on"
label={t("Account Expires On")}
type="datetime-local"
variant="outlined"
required={enableExpiresOn}
InputLabelProps={{
shrink: true,
}}
disabled={!enableExpiresOn}
/>
</div>
...
</form>
);
}
And in my JEST test file, I have the follow (as you can see, I'm trying to simulate a click in the Switcher):
describe("Should render correctly", () => {
let wrapper;
beforeAll(() => {
wrapper = mount(<MyComponent />);
});
...
fit("Should enable Account Expires On when switcher is on/checked", async () => {
wrapper.find("#account-expires").at(0).simulate("click");
expect(
wrapper.find("#account-expires-on").get(0).props.disabled
).toEqual(false);
});
However, Jest gives me an error in this expectation, saying that it have received true instead false.
I tried to put a setTimeout around the expectation, hoping that it would solve the problem because maybe we need to wait for the click to have effect, but it doesn't work.
I'm newer with JEST and React Hooks. So, I don't know if it is the best way to test what this behaviour or there is a better way.
Used Technologies
React Hooks
NextJS
Material UI
TypeScript
Jest
Enzyme
useState is async, jest check assertion before element get disabled.
Can try:
describe("Should render correctly", () => {
let wrapper;
beforeAll(() => {
wrapper = mount(<MyComponent />);
});
...
fit("Should enable Account Expires On when switcher is on/checked", () => {
jest.useFakeTimers()
wrapper.find("#account-expires").at(0).simulate("click");
jest.runAllTimers()
// or possibly
// await new Promise(resolve => setTimeout(resolve, 0))
expect(
wrapper.find("#account-expires-on").get(0).props.disabled
).toEqual(false);
});
And can google it
UPD
Can try to simulate onChange event or wrapper.find("#account-expires").at(0).props().onChange(); from here
Similar troubles with testing-library https://github.com/mui-org/material-ui/issues/17697

Testing Formik using enzyme and jest in react

My Formik page:
const IssueForm =({values,errors,touched,isSubmitting})=>{
const [modified,setModified] = useState(false);
const handleModified = () => setModified(true);
const handleSubmit = () => setModified(false);
return(
<div><br></br><br></br><br></br><br></br>
<h1>Add Issue</h1><br></br>
<Form>
<div>
<label>Description: </label>
<Field width="100px" type="text" name="issueDescription" placeholder="issuedescription" onInput={handleModified}/>
{touched.issueDescription && errors.issueDescription&&<span style={{color:'red'}}>{errors.issueDescription}</span>}
</div><div><br></br>
<button type ="submit" disabled={isSubmitting} onClick={handleSubmit}>Submit</button>
</Form>
</div>
)
};
I have mentioned one field .like this I have so many fields.
when I used normal form I generated test cases successfully as below.
describe('Test UserForm using Shallow rendering', () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<UserForm/>);
});
it('has 1 text input elements', () => {
expect(wrapper.find('input').length).toEqual(1);
})
it('should allow to type in issuedescription input field', () => {
wrapper.find('input#issueDescription').simulate('change', {
target: {value: 'Sachin'}
});
wrapper.update();
expect(wrapper.find('input#issueDescription').prop('value')).toEqual('Sachin');
})
});
when I changed my normal from to formik I executed the same test cases in formik I am getting error.
for first case :
expect(received).toEqual(expected) // deep equality
Expected: 1
Received: 0
for second case:
Method “simulate” is meant to be run on 1 node. 0 found instead.
I need to run this two cases for formik page .Iam new to react .can anyone help??

How to fire and test a real paste event (not simulated by calling the prop) in Jest and Enzyme

I'm trying to unit test a very simple feature in a React app where I'm blocking the user from pasting into a textarea by adding an event.preventDefault() in the event handler, like so:
function handlePaste(event) {
event.preventDefault();
}
// ... pass it down as props
<TextareaComponent onPaste={handlePaste} />
The problem I'm having is that every method I've found of dispatching events in Jest or Enzyme just "simulates" the event by getting the function passed to the onPaste prop and calling it directly with a mock event object. That's not what I'm interested in testing.
Ideally I want to do something like this, testing that the actual value of the input hasn't changed after pasting:
const wrapper = mount(<ParentComponent inputValue="Prefilled text" />);
const input = wrapper.find(TextareaComponent);
expect(input.value).toEqual("Prefilled text")
input.doAPaste("Pasted text")
expect(input.value).not.toEqual("Pasted text")
expect(input.value).toEqual("Prefilled text")
But haven't been able to find a method that works. Any help would be appreciated!
Since you're just testing against a synthetic event (and not some sort of secondary action -- like a pop up that warns the user that pasting is disabled), then the easiest and correct solution is to simulate a paste event, pass it a mocked preventDefault function, and then assert that the mocked function was called.
Attempting to make assertions against a real paste event is pointless as this a React/Javascript implementation (for example, making assertions that a callback function is called when an onPaste/onChange event is triggered). Instead, you'll want to test against what happens as a result of calling the callback function (in this example, making assertions that event.preventDefault was called -- if it wasn't called, then we know the callback function was never executed!).
Working example (click the Tests tab to run the assertions):
To keep it simple, I'm just asserting that the input is initially empty and then only updates the value if an onChange event was triggered. This can very easily be adapted to have some sort of passed in prop influence the default input's value.
App.js
import React, { useCallback, useState } from "react";
const App = () => {
const [value, setValue] = useState("");
const handleChange = useCallback(
({ target: { value } }) => setValue(value),
[]
);
const handlePaste = useCallback((e) => {
e.preventDefault();
}, []);
const resetValue = useCallback(() => {
setValue("");
}, []);
const handleSubmit = useCallback(
(e) => {
e.preventDefault();
console.log(`Submitted value: ${value}`);
setValue("");
},
[value]
);
return (
<form onSubmit={handleSubmit}>
<label htmlFor="foo">
<input
id="foo"
type="text"
data-testid="test-input"
value={value}
onPaste={handlePaste}
onChange={handleChange}
/>
</label>
<br />
<button data-testid="reset-button" type="button" onClick={resetValue}>
Reset
</button>
<button type="submit">Submit</button>
</form>
);
};
export default App;
App.test.js
import React from "react";
import { configure, mount } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import App from "./App";
configure({ adapter: new Adapter() });
const value = "Hello";
describe("App", () => {
let wrapper;
let inputNode;
beforeEach(() => {
wrapper = mount(<App />);
// finding the input node by a 'data-testid'; this is not required, but easier
// when working with multiple form elements and can be easily removed
// when the app is compiled for production
inputNode = () => wrapper.find("[data-testid='test-input']");
});
it("initially displays an empty input", () => {
expect(inputNode()).toHaveLength(1);
expect(inputNode().props().value).toEqual("");
});
it("updates the input's value", () => {
inputNode().simulate("change", { target: { value } });
expect(inputNode().props().value).toEqual(value);
});
it("prevents the input's value from updating from a paste event", () => {
const mockPreventDefault = jest.fn();
const prefilledText = "Goodbye";
// updating input with prefilled text
inputNode().simulate("change", { target: { value: prefilledText } });
// simulating a paste event with a mocked preventDefault
// the target.value isn't required, but included for illustration purposes
inputNode().simulate("paste", {
preventDefault: mockPreventDefault,
target: { value }
});
// asserting that "event.preventDefault" was called
expect(mockPreventDefault).toHaveBeenCalled();
// asserting that the input's value wasn't changed
expect(inputNode().props().value).toEqual(prefilledText);
});
it("resets the input's value", () => {
inputNode().simulate("change", { target: { value } });
wrapper.find("[data-testid='reset-button']").simulate("click");
expect(inputNode().props().value).toEqual("");
});
it("submits the input's value", () => {
inputNode().simulate("change", { target: { value } });
wrapper.find("form").simulate("submit");
expect(inputNode().props().value).toEqual("");
});
});

Resources