Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
2.5k views
in Technique[技术] by (71.8m points)

reactjs - How to test trigger on change event by selecting data list

How to test a situation where user selects option from data list? I am asserting onChange callback on the input using @testing-library/user-event.

This is my React component:

// MyInput.js

import React from "react";

const MyInput = ({ value, list, id, onChange }) => {
  return (
    <label>
      <strong>{id}</strong>
      <div>
        <input type="text" value={value} onChange={onChange} list={id} />
        <datalist id={id} aria-label="datalist-items">
          {list.map((item) => (
            <option
              key={`item-${item.id}`}
              aria-label="data-list-item"
              value={item.value}
            />
          ))}
        </datalist>
      </div>
    </label>
  );
};

export default MyInput;

This is my failing test

// MyInput.spec.js

import React from "react";
import { render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";

import MyInput from "./MyInput";
import data from "./data.json";

describe("MyInput", () => {
  it("should trigger onChange with selected option", () => {
    const onChange = jest.fn();
    const list = [...data.list];
    const screen = render(<MyInput onChange={onChange} list={list} />);

    userEvent.selectOptions(screen.getByLabelText("datalist-items"), "first");

    expect(onChange).toHaveBeenCalledWith("first");
  });
});

Data provided to component:

// data.json

{
  "list": [
    { "id": 1, "value": "first" },
    { "id": 2, "value": "second" }
  ]
}

However that does not work. Test reports failure:

expect(jest.fn()).toHaveBeenCalledWith(...expected)

Expected: "first"

Number of calls: 0

  14 |     userEvent.selectOptions(screen.getByLabelText("datalist-items"), "first");
> 16 |     expect(onChange).toHaveBeenCalledWith("first");
  17 |   });
  18 | });

You can find a live example in this CodeSandbox.

I want to simulate real-life scenario where user clicks into an input, the data list is rendered and user clicks one of the options. Should I be somehow targeting the rendered datalist instead? And how?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The onChange event handler is used by <input type='text'> jsx, so you should use type(element, text, [options]) to writes text inside an <input> or a <textarea>. selectOptions(element, values) is used for selecting the specified option(s) of a <select> or a <select multiple> element.

MyInput.jsx:

import React from 'react';

const MyInput = ({ value, list, id, onChange }) => {
  return (
    <label>
      <strong>{id}</strong>
      <div>
        <input type="text" value={value} onChange={onChange} data-testid="test" list={id} />
        <datalist id={id} aria-label="datalist-items">
          {list.map((item) => (
            <option key={`item-${item.id}`} aria-label="data-list-item" value={item.value} />
          ))}
        </datalist>
      </div>
    </label>
  );
};

export default MyInput;

MyInput.spec.jsx:

import React from 'react';
import { render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

import MyInput from './MyInput';
import data from './data.json';

describe('MyInput', () => {
  it('should trigger onChange with selected option', () => {
    expect.assertions(6);
    let events = [];
    const onChange = jest.fn().mockImplementation((e) => {
      e.persist();
      events.push(e);
    });
    const list = [...data.list];
    const screen = render(<MyInput onChange={onChange} list={list} id="test" />);
    userEvent.type(screen.getByTestId('test'), 'first');
    expect(onChange).toBeCalledTimes(5);
    events.forEach((e) => {
      expect(onChange).toHaveBeenCalledWith(e);
    });
  });
});

test result:

 PASS  examples/65687415/MyInput.spec.jsx
  MyInput
    ? should trigger onChange with selected option (44 ms)

-------------|---------|----------|---------|---------|-------------------
File         | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------|---------|----------|---------|---------|-------------------
All files    |     100 |      100 |     100 |     100 |                   
 MyInput.jsx |     100 |      100 |     100 |     100 |                   
-------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        5.175 s

package versions:

"react": "^16.14.0",
"@testing-library/react": "^11.2.2",
"@testing-library/user-event": "^12.6.0",

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...