我制作注册模式屏幕,并在提交时检查 isValid 值。
本地环境(npm start启动),这个isValid值没有问题。
但是在 jest 的单元测试过程中,即使满足验证规则并且在错误变量中没有检测到错误,isValid 值也始终为 false。
所以我想问一下为什么。
TeamUpdateModal.tsx
import React, { FC } from "react";
import { useAppDispatch, useAppSelector } from "../../../../redux/hooks";
import { Team, getTeamsAsync, selectProgress, updateTeamAsync } from "../../../../redux/features/teams/teamsSlice";
import {
Box,
Button,
CircularProgress,
FormControlLabel,
FormLabel,
Modal,
Stack,
Switch,
TextField,
Typography,
} from "@mui/material";
import { Controller, useForm } from "react-hook-form";
import { useParams } from "react-router-dom";
interface Props {
open: boolean;
handleClose: () => void;
team: Team | undefined;
}
const TeamUpdateModal: FC<Props> = ({ open, handleClose, team }) => {
const dispatch = useAppDispatch();
const { teamId } = useParams();
const isProgress = useAppSelector<boolean>(selectProgress);
const {
control,
handleSubmit,
formState: { isValid, errors },
register,
} = useForm();
const style = {
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: 400,
bgcolor: "background.paper",
border: "2px solid #000",
boxShadow: 24,
pt: 2,
px: 4,
pb: 3,
};
const onCancel = () => {
handleClose();
};
const updateTeam = handleSubmit(async (data) => {
if (!isValid) return;
if (teamId != undefined) {
const result = await dispatch(
updateTeamAsync({
teamId,
teamName: data["teamName"],
companyName: data["companyName"],
})
);
}
});
return (
<Modal
open={open}
onClose={() => handleClose()}
aria-labelledby="update-team-modal"
aria-describedby="update modal for team setting"
>
<Box sx={{ ...style, width: 800 }}>
<Typography sx={{ mt: 2, mb: 2, fontWeight: "bold", fontSize: "20px" }}>Team Update</Typography>
<form onSubmit={updateTeam}>
<Stack spacing={3}>
<Controller
name="companyName"
control={control}
defaultValue={team?.companyName || ""}
render={(): JSX.Element => {
return (
<TextField
variant="outlined"
label="Company Name *"
error={!!errors["companyName"]}
InputProps={{
inputProps: { maxLength: 50 },
}}
sx={{ width: "100%" }}
{...register("companyName", {
required: "required",
maxLength: 50,
})}
autoComplete="true"
helperText={errors["companyName"]?.message}
/>
);
}}
/>
<Controller
name="teamName"
control={control}
defaultValue={team?.teamName || ""}
render={(): JSX.Element => {
return (
<TextField
variant="outlined"
label="Team Name *"
error={!!errors["teamName"]}
sx={{ width: "100%" }}
InputProps={{
inputProps: { maxLength: 50 },
}}
{...register("teamName", {
required: "required",
maxLength: 50,
})}
autoComplete="true"
helperText={errors["teamName"]?.message}
/>
);
}}
/>
<Stack direction="row" marginTop={5} justifyContent="flex-end" spacing={3}>
<Button type="submit" disabled={isProgress}>
{isProgress ? <CircularProgress /> : "SUBMIT"}
</Button>
<Button onClick={onCancel}>CANCEL</Button>
</Stack>
</Stack>
</form>
</Box>
</Modal>
);
};
export default TeamUpdateModal;
TeamUpdateModal.test.tsx
import { act, fireEvent, render, screen } from "@testing-library/react";
import React from "react";
import TeamRegistrationModal from "./TeamRegistrationModal";
const mockDispatch = jest.fn();
const mockCreateTeamAsync = jest.fn();
const mockSelectProgress = jest.fn();
const mockHandleClose = jest.fn();
jest.mock("../../../../redux/hooks", () => ({
useAppDispatch: () => mockDispatch,
useAppSelector: (arg: () => void) => {
if (arg.name === "selectProgress") false;
return arg();
},
}));
jest.mock("../../../../redux/features/teams/teamsSlice", () => ({
__esModule: true,
...jest.requireActual("../../../../redux/features/teams/teamsSlice"),
createTeamAsync: (payload: {
teamName: string;
companyName: string;
}) => mockCreateTeamAsync(payload),
selectProgress: () => mockSelectProgress(),
}));
describe("TeamRegistrationModal", () => {
beforeEach(() => {
mockCreateTeamAsync.mockClear();
mockSelectProgress.mockClear();
mockHandleClose.mockClear();
});
test("Input is submitted successfully", async () => {
act(() => {
render(<TeamRegistrationModal open={true} handleClose={mockHandleClose} />);
});
const teamNameInput = screen.getByLabelText("Team Name *", { selector: "input" }) as HTMLInputElement;
const companyNameInput = screen.getByLabelText("Company Name *", { selector: "input" }) as HTMLInputElement;
fireEvent.input(teamNameInput, { target: { value: "username" } });
fireEvent.input(companyNameInput, { target: { value: "company" } });
const button = screen.getByText("SUBMIT");
fireEvent.click(button);
expect(mockCreateTeamAsync).toBeCalledTimes(1); <= error occurs
});
});
我将 useForm 模式插入为 onBlur,但错误并未消除。
https://github.com/orgs/react-hook-form/discussions/9699
将其放在单击之前,让 useForm 将 isValid 设置为 true
// Add custom wait to get correct form state in next re-rendering
await new Promise((resolve) => setTimeout(resolve, 0));