Test useRef's currents' properties using jest-enzyme - reactjs

I am trying to test a hidden file inputs click using Jest-enzyme, for the following component.
const UploadField = ({handleFileUpload}) => {
const hiddenFileInputRef = React.useRef(null);
const handleChange = (event) => {
handleFileUpload(event.target.files[0]);
};
const handleClick = () => {
hiddenFileInputRef.current.click();
};
return (
<>
<input
type="file"
ref={hiddenFileInputRef}
onChange={handleChange}
style={{display: 'none'}}
/>
<div
onClick={handleClick}
className="upload-button-container">
upload file
</div>
</>
);
};
i tried the following test :
it('should call handle click of hidden fileinput on click of div', () => {
const useRefSpy = jest
.spyOn(React, 'useRef')
.mockReturnValueOnce({current: {click: jest.fn()}});
let divWrapper = wrapper.find(
'.upload-button-container'
);
divWrapper.simulate('click');
expect(useRefSpy).toBeCalledTimes(1);
});
It gives an error "TypeError: Cannot read property 'click' of null" . what am i doing wrong here?

Try to check if you were able find the right div by logging wrapperDiv.html() or debugging
I think you might have to pass the event like in this post. Simulating a Div Click on Enzyme and React

Related

How to test onClick which programmatically fires file input

I'm fairly new to testing. I want to test whether my onClick function fired properly or not.
Behaviour of onClick function is firing file input programmatically.
const Component = () => {
const onBtnClick = (e: SyntheticEvent) => {
e.stopPropagation();
inputFileRef?.current?.click();
};
return (
<Box data-testid="upload-wrapper">
<input
data-testid="upload-input"
type="file"
hidden
ref={inputFileRef}
onChangeCapture={onFileChangeCapture}
accept="image/jpeg,image/png,image/jpg"
tabIndex={-1}
/>
<Button data-testid="upload-btn" onClick={onBtnClick}>
Upload image
</Button>
)
}
I found the solution from react-dropzone testing source file. At least it is working for my case. Here is the link react-dropzone#src/index.spec.js regarding opening file dialog programmatically.
My code:
it('Should open file dialog', () => {
const onClickSpy = vi.spyOn(HTMLInputElement.prototype, 'click');
renderScreen({});
fireEvent.click(screen.getByTestId('image-upload-btn'));
expect(onClickSpy).toHaveBeenCalled();
});
[1]: https://github.com/react-dropzone/react-dropzone/blob/master/src/index.spec.js#L3371

react testing library and jest with search input to update value on props

Demo
The search has several props to handle debouncing and api calls being made. Therefor, the state of the search will be the following:
Empty: shows a search-icon with no text in the input
Recently typed text: shows a spinner(replacing the search-icon) to indicate a lookup is happening
debounced typed text: after xSeconds, a clear-x-icon(replacing the spinner) shows with the typed out text.
The Problem
I can't figure out how to get the unit test to update the passed value in the props after the input has been updated by the mock function.
The Component Code
function SearchBox({
isSearching,
searchValue,
handleSearch,
clearSearch
}) {
const classes = useSearchStyles();
return (
<div className={classes.searchContainer}>
{isSearching ? (
<div className={classes.spinnerContainer}>
<Spinner />
</div>
) : searchValue ? (
<Button icon className={classes.clearIcon} onClick={clearSearch}>
<ClearIcon />
</Button>
) : (
<SearchIcon className={classes.searchIcon} />
)}
<Input
className={classes.searchInput}
type="text"
placeholder="Search"
maxLength={500}
value={searchValue}
onChange={(e: ChangeEvent<HTMLInputElement>) => handleSearch(e.target.value)}
/>
</div>
);
}
The Test Code
import React from 'react';
import { render, screen, waitFor } from '#testing-library/react';
import userEvent from '#testing-library/user-event';
import SearchBox from './SearchBox';
// ===========================================================================
// =============================== Setup Tests ===============================
// ===========================================================================
const searchValue = 'test';
const mockHandleSearchFn = jest.fn();
const mockClearSearchFn = jest.fn();
function TestContainer() {
return (
<SearchBox
isSearching={false}
searchValue=""
handleSearch={mockHandleSearchFn}
clearSearch={mockClearSearchFn}
/>
);
}
// ===========================================================================
// =============================== Start Tests ===============================
// ===========================================================================
describe('SearchBox', () => {
describe('should match snapshot when', () => {
it('input is empty', () => {
const testComponent = render(<TestContainer />);
expect(testComponent.container).toMatchSnapshot();
});
it('then, enters text in input and handleSearch is invoked', async () => {
const userViewer = userEvent.setup();
render(<TestContainer />);
const searchInput = await waitFor(() => screen.getByPlaceholderText(/Search/i));
await userViewer.type(searchInput, searchValue);
expect(mockHandleSearchFn).toHaveBeenCalledTimes(1);
expect(searchInput).toHaveAttribute('value', searchValue); // <------------------------ failing here
});
Tests Execution Output Example
When you type into an <input /> field, it doesn't set the value attribute. You can see this by typing into a plain input and inspecting the element.
The proper way to access the input value is via toHaveValue:
expect(searchInput).toHaveValue(searchValue);

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??

Test document listener with React Testing Library

I'm attempting to test a React component similar to the following:
import React, { useState, useEffect, useRef } from "react";
export default function Tooltip({ children }) {
const [open, setOpen] = useState(false);
const wrapperRef = useRef(null);
const handleClickOutside = (event) => {
if (
open &&
wrapperRef.current &&
!wrapperRef.current.contains(event.target)
) {
setOpen(false);
}
};
useEffect(() => {
document.addEventListener("click", handleClickOutside);
return () => {
document.removeEventListener("click", handleClickOutside);
};
});
const className = `tooltip-wrapper${(open && " open") || ""}`;
return (
<span ref={wrapperRef} className={className}>
<button type="button" onClick={() => setOpen(!open)} />
<span>{children}</span>
<br />
<span>DEBUG: className is {className}</span>
</span>
);
}
Clicking on the tooltip button changes the state to open (changing the className), and clicking again outside of the component changes it to closed.
The component works (with appropriate styling), and all of the React Testing Library (with user-event) tests work except for clicking outside.
it("should close the tooltip on click outside", () => {
// Arrange
render(
<div>
<p>outside</p>
<Tooltip>content</Tooltip>
</div>
);
const button = screen.getByRole("button");
userEvent.click(button);
// Temporary assertion - passes
expect(button.parentElement).toHaveClass("open");
// Act
const outside = screen.getByText("outside");
// Gives should be wrapped into act(...) warning otherwise
act(() => {
userEvent.click(outside);
});
// Assert
expect(button.parentElement).not.toHaveClass("open"); // FAILS
});
I don't understand why I had to wrap the click event in act - that's generally not necessary with React Testing Library.
I also don't understand why the final assertion fails. The click handler is called twice, but open is true both times.
There are a bunch of articles about limitations of React synthetic events, but it's not clear to me how to put all of this together.
I finally got it working.
it("should close the tooltip on click outside", async () => {
// Arrange
render(
<div>
<p data-testid="outside">outside</p>
<Tooltip>content</Tooltip>
</div>
);
const button = screen.getByRole("button");
userEvent.click(button);
// Verify initial state
expect(button.parentElement).toHaveClass("open");
const outside = screen.getByTestId("outside");
// Act
userEvent.click(outside);
// Assert
await waitFor(() => expect(button.parentElement).not.toHaveClass("open"));
});
The key seems to be to be sure that all activity completes before the test ends.
Say a test triggers a click event that in turn sets state. Setting state typically causes a rerender, and your test will need to wait for that to occur. Normally you do that by waiting for the new state to be displayed.
In this particular case waitFor was appropriate.

issue while resetting the recaptcha - Cannot read property 'reset' of null for the code

react-google-recaptcha version: 2.0.0-rc.1
I am having an issue while resetting the recaptcha
I am using a functional component
extracts of code as below
// imports etc.. here
const Login: NextPage = (props:any) => {
// othere initializations here...
const recaptchaInputRef:any = React.createRef();
const handleSubmit = async (event) => {
// some if condition
// and else
// and inside there
recaptchaInputRef.current.reset();
}
return (
<React.Fragment>
<form onSubmit={e => handleSubmit(e)}>
// other components and elements
<ReCAPTCHA
ref={recaptchaInputRef}
sitekey={props.recaptchaKey}
onChange={ onChange }
onExpired={ onExpired }
/>
<Button type="submit">Sign In</Button>
</form>
</React.Fragment>
);
now the issue is, I get - Cannot read property 'reset' of null for the code -> recaptchaInputRef.current.reset();
I changed the reference to the element as below:-
const recaptchaInputRef:any = useRef({});
// this is the new react hooks way of the reference declaration
This worked for me very well as required, but I would be happy to get better on this

Resources