You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何测试无法检测到表单字段非空时登录按钮启用状态?

登录按钮启用测试失败排查与修复

问题描述

我实现了一个登录页面,规则是当emailId和password字段为空时保持按钮禁用,两个字段都填入文本后启用按钮。但运行测试时出现错误提示Received element is not enabled,求指导问题出在哪里。

代码展示

Login.tsx

const Login = () => {
    const navigate = useNavigate();

    const [error, setError] = useState("");
    const [emailId, setEmailId] = useState("");
    const [password, setPassword] = useState("");
    const [loading, setLoading] = useState(false);

    return (
        <>
            <NavBar variant="secondary" button={<SignupBtn />} />
            <Container>
                <MuiTextField
                    fullWidth
                    label="Email address"
                    id="outlined-size-small"
                    size="small"
                    onChange={(event) => {
                        setEmailId(event.target.value);
                        setError("");
                    }}
                />
                <MuiTextField
                    fullWidth
                    label="Password"
                    id="outlined-size-small"
                    size="small"
                    onChange={(event) => {
                        setPassword(event.target.value);
                        setError("");
                    }}
                />
                {error && (
                    <Alert variant="outlined" severity="error">
                        {error}
                    </Alert>
                )}
                <Button variant="contained" disabled={!emailId || !password} onClick={(e) => handleLogin({ e, navigate, data: { emailId, password }, setError, setLoading })}>
                    {loading ? <Progress size={20} /> : "Login"}
                </Button>
            </Container>
        </>
    );
};

测试代码

test("button enables when form fields are not empty", async () => {
  
render(<Login />, { wrapper: BrowserRouter });
  
const user = userEvent.setup();
  
const emailTextField = screen.getByLabelText(/email address/i); 
const passwordTextField = screen.getByLabelText(/password/i); 
const LoginBtn = screen.getByRole("button", { name: "Login" });
  
await waitFor(async () => {
  
   await user.type(emailTextField, "hello");  
   await user.type(passwordTextField, "12345");
   expect(LoginBtn).toBeEnabled();  
  });
});

错误信息

Received element is not enabled:
    expect(LoginBtn).toBeEnabled();
                     ^

问题原因

  1. 输入框未绑定value属性:这是核心问题。你的MuiTextField没有将emailId和password状态绑定到value属性,属于非受控组件写法,但你又依赖state判断按钮禁用状态,导致输入时state更新和输入框实际值不同步,React无法正确触发重渲染更新按钮状态。
  2. 按钮元素引用过时:测试一开始就获取了LoginBtn元素,但React状态更新后按钮会重新渲染,之前获取的元素还是旧的禁用状态引用,没有同步最新状态。
  3. waitFor使用逻辑错误:在waitFor内部同时执行输入操作和断言,输入操作是异步的,应该先完成输入再等待状态更新断言。

修复方案

第一步:修复输入框的受控绑定

修改Login.tsx中的两个MuiTextField,添加value属性绑定对应state:

// 邮箱输入框
<MuiTextField
    fullWidth
    label="Email address"
    id="outlined-size-small"
    size="small"
    value={emailId} // 新增:绑定state到输入框value
    onChange={(event) => {
        setEmailId(event.target.value);
        setError("");
    }}
/>

// 密码输入框
<MuiTextField
    fullWidth
    label="Password"
    id="outlined-size-small"
    size="small"
    value={password} // 新增:绑定state到输入框value
    onChange={(event) => {
        setPassword(event.target.value);
        setError("");
    }}
/>

第二步:修改测试代码,获取最新按钮元素

调整测试逻辑,输入完成后重新获取按钮元素,或者在waitFor中直接获取最新元素断言:

方案一:输入完成后重新获取按钮

test("button enables when form fields are not empty", async () => {
  render(<Login />, { wrapper: BrowserRouter });
  
  const user = userEvent.setup();
  
  const emailTextField = screen.getByLabelText(/email address/i); 
  const passwordTextField = screen.getByLabelText(/password/i); 
  
  // 完成输入操作
  await user.type(emailTextField, "hello");  
  await user.type(passwordTextField, "12345");
  
  // 输入完成后获取最新的按钮元素
  const loginBtn = screen.getByRole("button", { name: "Login" });
  expect(loginBtn).toBeEnabled();  
});

方案二:用waitFor等待按钮状态更新

test("button enables when form fields are not empty", async () => {
  render(<Login />, { wrapper: BrowserRouter });
  
  const user = userEvent.setup();
  
  const emailTextField = screen.getByLabelText(/email address/i); 
  const passwordTextField = screen.getByLabelText(/password/i); 
  
  await user.type(emailTextField, "hello");  
  await user.type(passwordTextField, "12345");
  
  // 等待按钮变为可用状态,同时获取最新元素
  await waitFor(() => {
    expect(screen.getByRole("button", { name: "Login" })).toBeEnabled();
  });
});

内容的提问来源于stack exchange,提问作者Pawan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 15:45:53