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

react-admin中SimpleForm输入合法内容后SaveButton仍禁用的测试问题

问题解决:React-Admin SimpleForm测试中SaveButton始终禁用

核心问题分析

自定义SaveCancelDeleteToolbar通过useFormContext获取的isValid状态在测试环境未更新,导致组件未重新渲染,SaveButton保持禁用。这通常是由于Form上下文不共享、测试DOM操作方式不当或校验规则未生效导致的。

解决方案步骤

1. 修复FormContextWrapper的上下文冲突

你自定义的FormContextWrapper如果自行创建了useForm实例,会覆盖react-admin SimpleForm内部的FormContext,导致Toolbar和表单无法共享同一表单状态。建议移除自定义Wrapper,直接使用react-admin提供的上下文:

// 测试文件中移除自定义FormContextWrapper
const { user } = setup(
  <ValueList deleted={false} />
);

如果必须保留Wrapper(比如避免测试崩溃),确保它传递的是SimpleForm的上下文而非独立实例:

// 正确的FormContextWrapper实现(复用react-admin上下文)
import { FormProvider } from 'react-hook-form';
import { FormContext as RaFormContext } from 'react-admin';
import { useContext } from 'react';

export const FormContextWrapper = ({ children, resource }) => {
  const formMethods = useContext(RaFormContext);
  return <FormProvider {...formMethods}>{children}</FormProvider>;
};

2. 优化测试中的DOM元素获取方式

避免直接通过children层级获取输入框,改用testing-library的语义化查询方法,更稳定且符合测试最佳实践:

// 替换原有的输入框获取代码
const inputValue = screen.getByRole('textbox', { 
  name: /resources.values.fields.value/i 
});

3. 简化用户输入操作

使用@testing-library/user-event的type方法,无需手动触发focus和blur,它会模拟真实用户的输入流程:

// 移除fireEvent.focus/blur,直接用user.type
await user.type(inputValue, '1');

4. 校验自定义规则noEmptySpace的有效性

检查noEmptySpace校验函数是否正确处理了输入值'1',比如是否误判非空字符串为无效:

// 示例正确的noEmptySpace规则
const noEmptySpace = (value: string) => {
  if (!value.trim()) {
    return 'Value cannot be empty or whitespace';
  }
  return undefined;
};

5. 给SaveButton添加测试标识

为SaveButton添加data-testid,避免依赖文本查询的不确定性:

// Toolbar组件中修改SaveButton
<SaveButton 
  data-testid="save-button"
  disabled={!isValid} 
  onClick={closeDrawer} 
/>

// 测试中获取按钮
const saveButton = screen.getByTestId('save-button');
expect(saveButton).toBeDisabled(); // 初始状态
await user.type(inputValue, '1');
await waitFor(() => {
  expect(saveButton).not.toBeDisabled();
});

最终测试代码示例

it('should enable save button when valid input is entered', async () => {
  const { user } = setup(<ValueList deleted={false} />);

  // 打开SimpleForm的代码...

  // 初始状态校验
  const saveButton = screen.getByTestId('save-button');
  expect(saveButton).toBeDisabled();

  const inputValue = screen.getByRole('textbox', { 
    name: /resources.values.fields.value/i 
  });
  await user.type(inputValue, '1');

  // 等待状态更新
  await waitFor(() => {
    expect(saveButton).not.toBeDisabled();
  });
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 03:16:08