Skip to content

πŸ• Simulate user events for react-testing-library

License

Notifications You must be signed in to change notification settings

9still/user-event

Β 
Β 

Repository files navigation

user-event

dog

Simulate user events for react-testing-library.



Build Status Maintainability Test Coverage All Contributors

The problem

From testing-library/dom-testing-library#107:

[...] it is becoming apparent the need to express user actions on a web page using a higher-level abstraction than fireEvent

The solution

user-event tries to simulate the real events that would happen in the browser as the user interacts with it. For example userEvent.click(checkbox) would change the state of the checkbox.

The library is still a work in progress and any help is appreciated.

Installation

With NPM:

npm install @testing-library/user-event --save-dev

With Yarn:

yarn add @testing-library/user-event --dev

Now simply import it in your tests:

import userEvent from "@testing-library/user-event";

// or

var userEvent = require("@testing-library/user-event");

API

click(element)

Clicks element, depending on what element is it can have different side effects.

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

test("click", () => {
  render(
    <div>
      <label htmlFor="checkbox">Check</label>
      <input id="checkbox" data-testid="checkbox" type="checkbox" />
    </div>
  );

  userEvent.click(screen.getByText("Check"));
  expect(screen.getByTestId("checkbox")).toHaveAttribute("checked", true);
});

You can also ctrlClick / shiftClick etc with

userEvent.click(elem, { ctrlKey: true, shiftKey: true });

See the MouseEvent constructor documentation for more options.

dblClick(element)

Clicks element twice, depending on what element is it can have different side effects.

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

test("double click", () => {
  const onChange = jest.fn();
  render(<input type="checkbox" id="checkbox" onChange={onChange} />);
  const checkbox = screen.getByTestId("checkbox");
  userEvent.dblClick(checkbox);
  expect(onChange).toHaveBeenCalledTimes(2);
  expect(checkbox).toHaveProperty("checked", false);
});

async type(element, text, [options])

Writes text inside an <input> or a <textarea>.

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

test("type", async () => {
  render(<textarea data-testid="email" />);

  await userEvent.type(screen.getByTestId("email"), "Hello, World!");
  expect(screen.getByTestId("email")).toHaveAttribute("value", "Hello, World!");
});

If options.allAtOnce is true, type will write text at once rather than one character at the time. false is the default value.

options.delay is the number of milliseconds that pass between two characters are typed. By default it's 0. You can use this option if your component has a different behavior for fast or slow users.

upload(element, file, [{ clickInit, changeInit }])

Uploads file to an <input>. For uploading multiple files use <input> with multiple attribute and the second upload argument must be array then. Also it's possible to initialize click or change event with using third argument.

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

test("upload file", () => {
  const file = new File(["hello"], "hello.png", { type: "image/png" });

  render(<input type="file" data-testid="upload" />);

  userEvent.upload(screen.getByTestId("upload"), file);

  expect(input.files[0]).toStrictEqual(file);
  expect(input.files.item(0)).toStrictEqual(file);
  expect(input.files).toHaveLength(1);
});

test("upload multiple files", () => {
  const files = [
    new File(["hello"], "hello.png", { type: "image/png" }),
    new File(["there"], "there.png", { type: "image/png" }),
  ];

  render(<input type="file" multiple data-testid="upload" />);

  userEvent.upload(screen.getByTestId("upload"), files);

  expect(input.files).toHaveLength(2);
  expect(input.files[0]).toStrictEqual(files[0]);
  expect(input.files[1]).toStrictEqual(files[1]);
});

clear(element)

Selects the text inside an <input> or <textarea> and deletes it.

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

test("clear", () => {
  render(<textarea data-testid="email" value="Hello, World!" />);

  userEvent.clear(screen.getByTestId("email"));
  expect(screen.getByTestId("email")).toHaveAttribute("value", "");
});

selectOptions(element, values)

Selects the specified option(s) of a <select> or a <select multiple> element.

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

test("selectOptions", () => {
  render(
    <select multiple data-testid="select-multiple">
      <option data-testid="val1" value="1">
        A
      </option>
      <option data-testid="val2" value="2">
        B
      </option>
      <option data-testid="val3" value="3">
        C
      </option>
    </select>
  );

  userEvent.selectOptions(screen.getByTestId("select-multiple"), ["1", "3"]);

  expect(screen.getByTestId("val1").selected).toBe(true);
  expect(screen.getByTestId("val2").selected).toBe(false);
  expect(screen.getByTestId("val3").selected).toBe(true);
});

The values parameter can be either an array of values or a singular scalar value.

tab({shift, focusTrap})

Fires a tab event changing the document.activeElement in the same way the browser does.

Options:

  • shift (default false) can be true or false to invert tab direction.
  • focusTrap (default document) a container element to restrict the tabbing within.

A note about tab: jsdom does not support tabbing, so this feature is a way to enable tests to verify tabbing from the end user's perspective. However, this limitation in jsdom will mean that components like focus-trap-react will not work with userEvent.tab() or jsdom. For that reason, the focusTrap option is available to let you ensure your user is restricted within a focus-trap.

import React from "react";
import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom/extend-expect";
import userEvent from "@testing-library/user-event";

it("should cycle elements in document tab order", () => {
  render(
    <div>
      <input data-testid="element" type="checkbox" />
      <input data-testid="element" type="radio" />
      <input data-testid="element" type="number" />
    </div>
  );

  const [checkbox, radio, number] = screen.getAllByTestId("element");

  expect(document.body).toHaveFocus();

  userEvent.tab();

  expect(checkbox).toHaveFocus();

  userEvent.tab();

  expect(radio).toHaveFocus();

  userEvent.tab();

  expect(number).toHaveFocus();

  userEvent.tab();

  // cycle goes back to first element
  expect(checkbox).toHaveFocus();
});

Contributors

Thanks goes to these wonderful people (emoji key):


Giorgio Polvara

πŸ› πŸ’» πŸ“– πŸ€” πŸš‡ πŸ‘€ ⚠️

Weyert de Boer

πŸ’» ⚠️

Tim Whitbeck

πŸ› πŸ’»

MichaΓ«l De Boey

πŸ“–

Michael Lasky

πŸ’» πŸ“– πŸ€”

Ahmad Esmaeilzadeh

πŸ“–

Caleb Eby

πŸ’» πŸ› πŸ‘€

AdriΓ  Fontcuberta

πŸ› ⚠️ πŸ’»

Sky Wickenden

πŸ› πŸ’»

Bodnar Bogdan

πŸ› πŸ’»

Zach Perrault

πŸ“–

Ryan Stelly

πŸ“–

Ben Monro

πŸ’»

Christopher Martin

πŸ’»

Yuancheng Wu

πŸ‘€

MJ

πŸ“–

Jeff McRiffey

πŸ’» ⚠️

Jaga Santagostino

πŸ’» ⚠️

jordyvandomselaar

πŸ’» ⚠️

Ilya Lyamkin

πŸ’» ⚠️

Kenneth LujΓ‘n Rosas

πŸ’» ⚠️

Joe Morgan

πŸ’»

David Hirtle

πŸ’»

whiteUnicorn

πŸ’»

Matej Ε nuderl

πŸ‘€

Rodrigo Pombo

πŸ’»

Jake Verbaten

πŸ’»

Spencer Miskoviak

πŸ“–

Vadim Shvetsov

πŸ€” πŸ’» ⚠️

This project follows the all-contributors specification. Contributions of any kind welcome!

About

πŸ• Simulate user events for react-testing-library

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 100.0%