How to test react-jsonschema-form components with @testing-library/user-event

1.1k Views Asked by At

I faile to write even the most simple tests for react jsonschema form. Because changes to input elements seem not to be reflected in the dom.

Given a minimal form like this:

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} />; }
}

a minimal test would look like

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");
});

(Complete sample over at Codesandbox.)

After typing in the Form field the text First task aaa should be replaced by 567890. Unfortunately it is not.input.value keeps the value First task aaa.

I thied many variants of firing events and waiting for results but always the value of the input element stays unchanged.

What am I missing to test the <MyForm /> component? Seems pretty standard to me.

2

There are 2 best solutions below

0
Chris On

I can reproduce your problem as well and it looks like react-jsonschema-form is not playing well with fireEvent or userEvent.

However, using the react-doms Simulate function, it does work:

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");
});
0
James Lin On

In terms of you are using fluent-ui Form library for your UI, and this will bind the input value with your formData.title field. This may interrupt userEvent.type action. To simply test userEvent.type functionality, you can make your form component with pure input element, and bind your default value as defaultValue of the input element.

For example:

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

Incase you will see below error on your test output:

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 | });

As you can see, userEvent.type will append additional input to the current value. So, you can just use Simulate.change functionality or use userEvent.clear function before your userEvent.type line like below.

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");
});

This answer could helpful for your question.
Cheers!