如何使用@testing-library/user-event 测试 react-jsonschema-form 组件

问题描述

我连最简单的 react jsonschema form 测试都写不出来。因为对输入元素的更改似乎没有反映在 dom 中。

给出这样的最小形式:

const schema = {
  title: "Todo",type: "object",properties: {
    title: { type: "string",title: "Title",default: "A new task" }
  }
};

const formData = { title: "First task aaa" };

export class MyForm extends React.Component {
  render() { return <Form schema={schema} formData={formData} />; }
}

一个最小的测试看起来像

test("changes input",async () => {
  render(<MyForm />);
  const input = screen.getByRole("textBox");
  expect(input.value).toBe("First task aaa");
  await userEvent.type(input,"567890",{ delay: 10 });
  expect(input.value).toBe("567890");
});

(在 Codesandbox 完成示例。)

在表单字段中输入后,文本 First task aaa 应替换为 567890。不幸的是,它不是。input.value 保留值 First task aaa

我隐藏了许多触发事件和等待结果的变体,但输入元素的值始终保持不变。

测试 <MyForm /> 组件时我缺少什么?对我来说似乎很标准。

解决方法

我也可以重现您的问题,看起来 react-jsonschema-form 与 fireEventuserEvent 的配合不佳。

但是,使用 react-doms Simulate 函数,它确实有效:

import React from "react";
import { Simulate } from 'react-dom/test-utils'
import { render } from "@testing-library/react";
import { MyForm } from "./form.js";

// Tests in codesandbox fail in Safari - use Firefox or Chrome
// click on the "Tests" tab in the upper right.

test("changes input",async () => {
  const { getByLabelText } = render(<MyForm />);
  const input = getByLabelText("Title");
  expect(input.value).toBe("First task aaa");

  Simulate.change(input,{ target: { value: '567890' }})

  expect(input.value).toBe("567890");
});
,

就您的 UI 使用 fluent-ui 表单库而言,这会将输入值与您的 formData.title 字段绑定。这可能会中断 userEvent.type 操作。为了简单地测试 userEvent.type 功能,您可以使用纯输入元素制作表单组件,并将您的默认值绑定为输入元素的 defaultValue。

例如:

export class MyForm extends React.Component {
  render() {
    return <input type="text" defaultValue="First task aaa" />;
  }
}

如果您会在测试输出中看到以下错误:

expect(element).toHaveValue(567890)

    Expected the element to have value:
      567890
    Received:
      First task aaa567890

      13 |   expect(input).toHaveValue("First task aaa");
      14 |   userEvent.type(input,'567890');
    > 15 |   expect(input).toHaveValue('567890');
         |                 ^
      16 | });

如您所见,userEvent.type 会将附加输入附加到当前值。因此,您可以只使用 Simulate.change 功能或在 userEvent.clear 行之前使用 userEvent.type 功能,如下所示。

test("changes input",async () => {
  render(<MyForm />);
  const input = screen.getByRole("textbox");

  expect(input).toHaveValue("First task aaa");
  userEvent.clear(input);
  userEvent.type(input,"567890");
  expect(input).toHaveValue("567890");
});

此答案可能对您的问题有所帮助。
干杯!