33

Description:

I am trying to test that a form submits when the user presses the "Enter" key. I have a passing test for when pressing the Submit button, but I also want to be sure the form submits with the keyboard (convenience and a11y).

Code:

test("should submit when pressing enter", () => {
  const handleSubmit = jest.fn();
  const { getByLabelText } = render(<App handleSubmit={handleSubmit} />);
  const input = getByLabelText("Name:");

  fireEvent.change(input, { target: { value: "abc" } });
  fireEvent.keyPress(input, { key: "Enter", code: 13, charCode: 13 });

  expect(handleSubmit).toHaveBeenCalled();
});

Here is a CodeSandbox with the minimal amount of code needed.

skyboyer
  • 22,209
  • 7
  • 57
  • 64
jaypee
  • 1,757
  • 3
  • 9
  • 12

7 Answers7

16

It's a little less clear what the source of the interaction is intended to be, but submit can be called on the input and it appears to fix the test in the sandbox:

fireEvent.submit(input);
Kevin Gorski
  • 3,745
  • 1
  • 22
  • 16
  • 6
    Author states "I am trying to test that a form submits when the user presses the "Enter" key.". Your answer will not simulate that behavior. – DarkTrick Aug 04 '22 at 08:53
16

The following worked for me:

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

test("should submit when pressing enter", () => {
  const handleSubmit = jest.fn();
  const { getByLabelText } = render(<App handleSubmit={handleSubmit} />);
  const input = getByLabelText("Name:");

  userEvent.type(input, "abc{enter}");

  expect(handleSubmit).toHaveBeenCalled();
});
Peter Cardenas
  • 508
  • 4
  • 13
  • I don't have onsubmit prop on my component. I have onkeydown function . When i press enter key on input onkeydown function calls and from there i am submitting the form. So how can i spyon for onkeydown method. I want to check my function calling or not. Please help – krishna Jan 22 '22 at 22:56
  • You can check if the view updates according to what happens after the submit function is executed. For example, if you type enter, the form submits and a div with "Form Submitted" pops up, you can check if an element with that text exists. However, if you really need to check if that function executes, then you can jest.spyOn any utility functions that submit function calls. – Peter Cardenas Jan 23 '22 at 06:41
  • I tried jest.spyon(component.prototype, 'method'); but i am getting an error like can't spyon premitive value. In my component onkeydown is a function and i am submitting form from there. I want to check onkeydown calling or not using spyon. – krishna Jan 23 '22 at 08:45
  • 1
    oh sorry I should have clarified. I meant that if your component method calls any utility functions that are imported like lodash, fetch, or your own utility function, you can jest.spyOn that. hope this helps – Peter Cardenas Jan 24 '22 at 03:39
  • this is the only correct way that does not cause act error – marko kraljevic Jun 18 '22 at 19:00
1

You can submit by button but the event target will be the button and not the form. To resolve this:

  • submit the form
  • useRef on the form
  • ByTestId on the form

Submitting the form is only accessible if it has an accessible name. In this sense, using role="my-form" (ByRole) or aria-label="form's purpose" (ByLabelText or ByRole("form")).

import "@testing-library/jest-dom/extend-expect";
import { getByRole, fireEvent } from '@testing-library/dom';

test("test form", () => {
  const div = document.createElement("div");

  div.innerHTML = `
  <form role="my-form">
    <label for="first_name">
      First Name:
      <input id="first_name" type="text" />
    </label>
    <button type="submit">Submit</button>
  </form>
  `;

  const handleSubmit = jest.fn();
  div.querySelector('form').onsubmit = handleSubmit;

  fireEvent.submit(getByRole(div, "my-form"));
  expect(handleSubmit).toHaveBeenCalledTimes(1);
});
Robert Brisita
  • 5,461
  • 3
  • 36
  • 35
  • 2
    Just FYI that `my-form` is not a valid ARIA role. Please make sure to use valid ARIA roles. In this case simply `role="form"`. More info https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles – oyalhi Mar 20 '21 at 21:28
0

I followed the above answer but it's not worked for me because I am using keydown listener to check my input.

Please remember, use only e.key not e.keyCode/e.which its deperecated. https://stackoverflow.com/a/4471635/8798220

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

const Input = () => {
  const [value, setValue] = useState("");

  const handleEnter = (e) => {
    if (e.key === "Enter") {
      const value = e.target.value;
      // API CALLING / LOGIC
    }
  };

  return (
    <input
      placeholder="Search..."
      value={value}
      onChange={(e) => setValue(e.target.value)}
      onKeyDown={handleEnter}
    />
  );
};

it("should submit when pressing enter", async () => {
  const text = "hello";

  render(<Input />);

  const input = screen.getByPlaceholderText("Search...");
  await userEvent.type(input, `${text}[Enter]`);

  expect(input).toHaveValue(text);
  // check about enter logic.
});
Nisharg Shah
  • 16,638
  • 10
  • 62
  • 73
0

I found a way to force it without any changes or new libraries as follow

const btnDeepPropsKey = Object.keys(
  wrapper.getByTestId('input-search')
)?.find(item => item?.includes('__reactProps'))

await waitFor(async () =>
  (wrapper.getByTestId('input-search') as any)?.[
    btnDeepPropsKey
  ]?.onKeyPress({ key: 'Enter' })
)

If you can't reach that prop you can log it as well

console.log(wrapper.getByTestId("input-search"))

Note that some states may have been lost if you try to access any other onKeyPress of those props that was shown in log above

Murilo Medeiros
  • 251
  • 2
  • 12
-1

To simulate keyboard show/hide I first focus on input and then simulate typing. This way you can fire onSubmitEditing event to simulate submit button pressed on a Keyboard.

import { fireEvent } from '@testing-library/react-native'

const input = getByTestId('input');
fireEvent.focus(input);
fireEvent.changeText(input, 'hello world')
fireEvent.submitEditing(input);
-1

You are not passing the correct options to fireEvent.keyDown event. Try to pass keyCode: 13 along with key: "enter"

The following piece of code works for me

it('Check if added todo show in list with label', () => {
  render(<TodoApp/>)
  const input = screen.getByTestId('add-todo')

  fireEvent.change(input, {target: {value: 'Task with label @health'}});
  fireEvent.keyDown(input, {key: 'enter', keyCode: 13})

  const todoList = screen.getByTestId('todo-list')
  expect(todoList).toHaveTextContent('health')
});
Ain
  • 708
  • 1
  • 8
  • 16