Test React confirmation window using enzyme - reactjs

I've got a button in React which opens a simple confirmation window when the user clicks on it. Before I added the confirm method, the test below was green. After adding the confirm it's red. How do I need to change the test to work with the additional confirm?
React delete button:
const DeleteButton = (props) => {
const handleDelete = () => {
if(confirm("Are you sure?")) {
props.onDelete(props.id)
}
};
return (
<Button className="btn" onClick={handleDelete}>
<i className="fa fa-trash-o"></i>
</Button>
);
};
Here is the test (using enzyme):
describe('<DeleteButton />', () => {
it("deletes the entry", () => {
const onDelete = sinon.spy();
const props = {id: 1, onDelete: onDelete};
const wrapper = shallow(<DeleteButton {...props} />);
const deleteButton = wrapper.find(Button);
deleteButton.simulate("click");
expect(onDelete.calledOnce).to.equal(true);
});
});

You can stub confirm using sinon.stub.
describe('<DeleteImportButton />', () => {
it("simulates delete event", () => {
const onDeleteImport = sinon.spy();
const props = {id: 1, onDelete: onDeleteImport};
const wrapper = shallow(<DeleteImportButton {...props} />);
const deleteButton = wrapper.find(Button);
const confirmStub = sinon.stub(global, 'confirm');
confirmStub.returns(true);
deleteButton.simulate("click");
expect(confirmStub.calledOnce).to.equal(true);
expect(onDeleteImport.calledOnce).to.equal(true);
confirmStub.restore();
});
});

Related

How can test component rendering JSX element only when boolean flag is true

Suppose I have the following component that I would like to test:
const TestComponent = () => {
const [showModal, setShowModal] = useState(false);
return (
<>
<button
onClick={() => setShowModal(true)}
>
Show Modal
</button>
{ isOpen ? <Modal>...</Modal> : <div>No Modal</div>}
</>
)
}
Now I would like to have the component rendering the Modal component in its initial rendering and test its DOM. How can I pass showModal = true to it?
discribe("Rendered TestComponent", () => {
it("has Modal component", () => {
// Some operation needed here or after rendering the component?
render(<TestComponent />);
expect(screen.getByRole('input', {name: 'first-name' }).toBeInTheDocument;
})
})
First you have to attribute a data-testid to the button tag in order to manipulate it, keep the ID unique throughout the whole application.
Then you can use your favorite test lib to fire a click event, there are many avaliable that can do events, such as fire event:
const TestComponent = () => {
const [showModal, setShowModal] = useState(false);
return (
<>
<button
data-testid="show-modal"
onClick={() => setShowModal(true)}
>
Show Modal
</button>
{ showModal ? <Modal>...</Modal> : <div>No Modal</div>}
</>
)
}
describe("Rendered TestComponent", () => {
it("has Modal component", () => {
const { getByTestId } = render(<TestComponent />);
const showModalButton = getByTestId("show-modal");
fireEvent.click(showModalButton);
expect(screen.getByRole('input', { name: 'first-name' })).toBeInTheDocument;
});
});

How to test a click event that redirects with react testing library

so i have this component.
`type MovieCardProps = {
movie: IMovie;
};
const MovieCard = ({ movie }: MovieCardProps) => {
const navigate = useNavigate();
const date = movie.releaseDate
? new Date(movie.releaseDate).toDateString()
: '';
const image = `${imageBaseUrl}/t/p/w500/${movie.posterPath}`;
const handleClick = () => {
navigate(`/${EPages.movies}/${movie.id}`);
};
return (
<div className='movie-card' onClick={handleClick}>
<img
src={movie.posterPath ? image : noImage}
alt={movie.title}
loading='lazy'
/>
<div className='movie-card__info'>
<h4>{movie.title}</h4>
<p>{date}</p>
<p>Movie</p>
</div>
</div>
);
};
export default MovieCard;`
and this is what i got so far but i dont know how to get the parent div so i can simulate the click event with userEvent.click()
` test('Should navigate to details page on click', async () => {
const mockedNavigate = jest.fn();
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useNavigate: () => mockedNavigate,
}));
renderWithContext(<MovieCard movie={mockMovies[0]} />);
await waitFor(() => {
expect(mockedNavigate).toHaveBeenCalledWith('/path');
});
});`
i want to assert that on clicking the component it calls the navigate funtion with my path

How to ensure useState works when mocking custom react hook

I have a component which imports a custom hook. I want to mock returned values of this hook but ensure the useState still works when I fire and event.
component.tsx
export const Component = () => {
const { expanded, text, handleClick, listOfCards } = useComponent();
return (
<div>
<button id="component" aria-controls="content" aria-expanded={expanded}>
{text}
</button>
{expanded && (
<div role="region" aria-labelledby="component" id="content">
{listOfCards.map((card) => (
<p>{card.name}</p>
))}
</div>
)}
</div>
);
};
useComponent.tsx
const useComponent = () => {
const [expanded, setExpanded] = useState(false);
const { listOfCards } = useAnotherCustomHook();
const { translate } = useTranslationTool();
return {
text: translate("id123"),
expanded,
handleClick: () => setExpanded(!expanded),
listOfCards,
};
};
component.test.tsx
jest.mock("./component.hook");
const mockuseComponent = useComponent as jest.Mock<any>;
test("Checks correct attributes are used, and onClick is called when button is clicked", () => {
mockuseComponent.mockImplementation(() => ({
text: "Click to expand",
listOfCards: [{ name: "name1" }, { name: "name2" }],
}));
render(<Component />);
const button = screen.getByRole("button", { name: "Click to expand" });
expect(button).toHaveAttribute('aria-expanded', 'false');
fireEvent.click(button);
expect(button).toHaveAttribute('aria-expanded', 'true');
});
With the above test aria-expanded doesnt get set to true after we fire the event because im mocking the whole hook. So my question is, is there a way to only mock part of the hook and keep the useState functionality?

How to test changes made by onClick event that calls a setState function, which is passed from another component and changes UI?

Basically the title.
Here is the overview of the App:
const App = () => {
const [isViewFavoriteImages, setIsViewFavoriteImages] = useState(false);
const toggleIsViewFavoriteImages = () => {
setIsViewFavoriteImages(
(prevToggleIsViewFavoriteImagesState) =>
!prevToggleIsViewFavoriteImagesState
);
};
return (
<div className="App">
<div className="container">
<ToggleImagesViewButton
toggleIsViewFavoriteImages={toggleIsViewFavoriteImages}
isViewFavoriteImages={isViewFavoriteImages}
/>
<ImageList isViewFavoriteImages={isViewFavoriteImages} />
</div>
</div>
);
};
export default App;
The button component:
export interface ToggleImageViewButtonProps {
toggleIsViewFavoriteImages: () => void;
isViewFavoriteImages: boolean;
}
const ToggleImageViewButton: React.FC<ToggleImageViewButtonProps> = ({
toggleIsViewFavoriteImages,
isViewFavoriteImages,
}) => {
return (
<button
onClick={toggleIsViewFavoriteImages}
className="btn btn_toggle-image-view"
data-testid="toggle-image-view"
>
{isViewFavoriteImages ? "view all" : "view favorites"}
</button>
);
};
export default ToggleImageViewButton;
And this is how I am testing it:
function renderToggleImagesViewButton(
props: Partial<ToggleImageViewButtonProps> = {}
) {
const defaultProps: ToggleImageViewButtonProps = {
toggleIsViewFavoriteImages: () => {
return;
},
isViewFavoriteImages: true,
};
return render(<ToggleImageViewButton {...defaultProps} {...props} />);
}
describe("<ToggleImagesViewButton />", () => {
test("button inner text should change to 'view all' when the user clicks the button", async () => {
const onToggle = jest.fn();
const { findByTestId } = renderToggleImagesViewButton({
toggleIsViewFavoriteImages: onToggle,
});
const toggleImagesViewButton = await findByTestId("toggle-image-view");
fireEvent.click(toggleImagesViewButton);
expect(toggleImagesViewButton).toHaveTextContent("view favorites");
});
});
This test fails and "view all" is still getting returned.
ToggleImageViewButton doesn't have internal state - the state was lifted to the parent, so testing state changes should happen in the parent's tests.
You could have the following integration test to verify the correct behaviour of the button when used in App.
test("App test", () => {
render(<App />);
const button = screen.getByTestId("toggle-image-view");
expect(button).toHaveTextContent("view favorites");
fireEvent.click(button);
expect(button).toHaveTextContent("view all");
});
As for the ToggleImageViewButton unit tests, you can simply test that it renders the right text based on isViewFavoriteImages value, and that the callback gets called when the button is clicked.
test("ToggleImageViewButton test", () => {
const onToggle = jest.fn();
render(<ToggleImageViewButton isViewFavoriteImages={false} toggleIsViewFavoriteImages={onToggle}/>);
expect(screen.getByTestId("toggle-image-view")).toHaveTextContent("view favorites");
fireEvent.click(screen.getByTestId("toggle-image-view"));
expect(onToggle).toHaveBeenCalled();
});

How to read the children in nodes when testing uisng enzymes

I have a component and I want to test the click method. I am using shallow but my test is failing as it cannot find the button and hence it`s click method. What is wrong with my code?
interface IProps {
label: string;
className: string;
onClick: () => void;
}
export const NewButton: React.StatelessComponent<IProps> = props => {
return (
<Button type="button" className={props.className} onClick={props.onClick}>
{props.label}
</Button>
);
};
import { shallow } from 'enzyme';
import * as React from 'react';
import { NewButton } from "../Buttons";
describe('<NewButton />', () => {
describe('onClick()', () => {
const props = {
className: "buttonSubmit",
label: "submit",
onClick: () => {},
}
test('successfully calls the onClick handler', () => {
const mockOnClick = jest.fn();
const wrapper = shallow(
<NewButton {...props} />
);
const button = wrapper.find('submit').dive();
expect(button.exists()).toEqual(true)
button.simulate('click');
expect(mockOnClick.mock.calls.length).toBe(1);
});
});
});
Since you are using shallow method, it will only render the component that we are testing. It does not render child components. So you should try to find the Button component.
const button = wrapper.find('Button');
After that you should mock the props.onClick event handler passed as props to NewButton component.
const props = {
className: "buttonSubmit",
label: "submit",
onClick: jest.fn(),
}
So you can use
describe('<NewButton />', () => {
describe('onClick()', () => {
const props = {
className: "buttonSubmit",
label: "submit",
onClick: jest.fn(),
}
test('successfully calls the onClick handler', () => {
const wrapper = shallow(
<NewButton {...props} />
);
const button = wrapper.find('Button');
expect(button.exists()).toEqual(true)
button.simulate('click');
// Since we passed "onClick" as props
// we expect it to be called when
// button is clicked
// expect(props.onClick).toBeCalled();
expect(props.onClick.mock.calls.length).toBe(1);
});
});
});

Resources