reactjsjestjsmockingreact-testing-librarymsw

React MSW axios testing with jest doesnt't get triggered


I am trying to mock post request to api that returns status of 404, so after it returns response i can display an alert message which has data-testid="error-pw-api". But feels like the msw's post mock doesn't get triggered at all and it says can't find this element with this data-testid. I am on this issue quite long time hence i need help.

"axios": "^0.27.2", "msw": "^0.44.2",

The component looks like this:

import React, { useState } from 'react'
import { Alert, Modal, Spinner } from 'react-bootstrap'
import { toaster, translation } from '../../models/main';
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import { passwordSchema } from '../../utils/validationSchemas';
import axios from 'axios';


type Props = {
    isModalOpen: boolean;
    closeModal: () => void;
    l: translation;
    toaster: toaster;
    userName: string
}


const PasswordChange = ({ isModalOpen, closeModal, l, userName, toaster }: Props) => {
    const { register, handleSubmit, formState: { errors }, reset, clearErrors } = useForm({
        mode: "onSubmit",
        resolver: yupResolver(passwordSchema(userName, l)),
    });
    const [isPasswordVisible, setIsPasswordVisible] = useState(false)
    const [isLoading, setIsLoading] = useState(false)
    const [serverError, setServerError] = useState("")

    const submitChanges = async (data) => {
        clearErrors()
        setIsLoading(true);
        try {
            const url = '/user/' + userName + '/password'
            const result = await axios.post(url,
                {
                    password: data.passwordOne,
                    oldPassword: data.oldPassword
                })
            toaster.success(l("account_settings.message.password_changed_success"))
            closeModal()
            setServerError("")
            reset()
        } catch (err) {
            const errorMessage = err.response.status === 404 && err.response.data.message ? l("account_settings.message.old_password_error") : err.response.statusText;
            setServerError(l("account_settings.message.server_error") + errorMessage)
        }
        finally {
            setIsLoading(false)
        }
    }

    const handleClose = () => {
        reset()
        closeModal()
        setServerError("")
        setIsLoading(false)
        setIsPasswordVisible(false)
    }

    return (
        <Modal
            size="lg"
            show={isModalOpen}
            onHide={handleClose}
            aria-labelledby="example-modal-sizes-title-lg"
        >
            <Modal.Header closeButton>
                <Modal.Title id="example-modal-sizes-title-lg">
                    {l("account_settings.change_password")}
                </Modal.Title>
            </Modal.Header>
            <form onSubmit={handleSubmit(data => submitChanges(data))}>
                <Modal.Body>
                    <div className="form-group">
                        <label
                            htmlFor="oldPassword">{l("account_settings.enter_old_password_label")}</label>
                        <input
                            required
                            id="oldPassword"
                            name="oldPassword"
                            data-testid="oldPassword"
                            type={isPasswordVisible ? "text" : "password"}
                            className={`form-control ${errors.oldPassword ? "is-invalid" : "border border-dark"}`}
                            {...register("oldPassword")}
                            placeholder={l("account_settings.old_password_placeholder")} />
                        {errors.oldPassword && <div className="invalid-feedback" data-testid="error-oldpw">
                            {errors.oldPassword.message}
                        </div>}
                    </div>
                    <div className="form-group">
                        <label htmlFor="passwordOne">{l("account_settings.enter_password")}</label>
                        <input
                            required
                            id="passwordOne"
                            data-testid="passwordOne"
                            name="passwordOne"
                            type={isPasswordVisible ? "text" : "password"}
                            className={`form-control ${errors.passwordOne ? "is-invalid" : "border border-dark"}`}
                            {...register("passwordOne")}
                            placeholder={l("account_settings.password")} />
                        {errors.passwordOne && <div className="invalid-feedback" data-testid="error-pw-one">
                            {errors.passwordOne.message}
                        </div>}

                    </div>
                    <div className="form-group">
                        <label htmlFor="passwordTwo">{l("account_settings.enter_password_again")}</label>
                        <input
                            required
                            id="passwordTwo"
                            data-testid="passwordTwo"
                            name="passwordTwo"
                            type={isPasswordVisible ? "text" : "password"}
                            className={`form-control ${errors.passwordTwo ? "is-invalid" : "border border-dark"}`}
                            {...register("passwordTwo")}
                            placeholder={l("account_settings.verify_password")} />
                        {errors.passwordTwo && <div className="invalid-feedback" data-testid="error-pw-two">
                            {errors.passwordTwo.message}
                        </div>}
                    </div>
                    {serverError && <div className="my-2" datat-testid="error-pw-api">
                        <Alert variant="danger">
                            <i className="fa-solid fa-triangle-exclamation"></i> {serverError}
                        </Alert>
                    </div>}
                </Modal.Body>
                <Modal.Footer>
                    <button
                        className="btn btn-outline-dark"
                        type="button"
                        onClick={() => setIsPasswordVisible(prev => !prev)}
                    >
                        {isPasswordVisible ?
                            <i className="fas fa-eye mt-2" /> : <i className="fas fa-eye-slash mt-2"></i>
                        }
                    </button>
                    <button
                        id="submit-pw-change"
                        data-testid="submit-pw-change"
                        disabled={isLoading}
                        type="submit"
                        className="btn btn-primary">
                        {isLoading && <Spinner data-testid="spinner-btn-pw-change" animation="border" variant="light" size='sm' className="mx-1" />}
                        {l("account_settings.change_password_button")}
                    </button>
                    <button
                        type="button"
                        className="btn btn-outline-dark"
                        onClick={handleClose}>
                        {l("buttons.cancel")}
                    </button>
                </Modal.Footer>
            </form>
        </Modal>
    )
}
export default PasswordChange;

And the test file itself is like

import { fireEvent, render, cleanup } from "@testing-library/react";
import React from "react";
import { act } from "react-dom/test-utils";
import PasswordChange from "../../src/components/UserDetails/passwordChange";
import { mockedToaster, mockFunc } from "./userDetails.test";
import { server } from "../__mocks__/serverMock";
import { rest } from "msw";

    jest.mock("axios")
    beforeAll(() => server.listen());
    afterAll(() => server.close());
    afterEach(() => server.resetHandlers());
    describe("Server error tests", () => {
        it("Test validation with ser verror", async () => {
            await act(async () => {
                const { findByTestId, getByTestId, queryByTestId } = render(<PasswordChange
                    isModalOpen={true}
                    closeModal={mockFunc}
                    l={mockedL}
                    toaster={mockedToaster}
                    userName={"randomname"} />)
                fireEvent.change(await getByTestId("oldPassword"), { target: { value: 'RandomGang420££' } })
                fireEvent.change(await getByTestId("passwordOne"), { target: { value: 'RandomGang420££' } })
                fireEvent.change(await getByTestId("passwordTwo"), { target: { value: 'RandomGang420££' } })
                fireEvent.submit(await getByTestId("submit-pw-change"));
                server.use(rest.post("/user/:user/password", async (req, res, ctx) => {
                    return res(
                        ctx.status(404),
                    )
                }))
                expect(await queryByTestId("error-pw-one")).toBeNull()
                expect(await queryByTestId("error-pw-two")).toBeNull()
                expect(await findByTestId("error-pw-api")).toBeInTheDocument()
            })
        });
    })

Solution

  • The reason MSW is not kicking in here is because you are using jest to fully mock axios. So MSW should work if you remove jest.mock("axios").