Error: Not implemented: HTMLFormElement.prototype.submit
Asked Answered
B

4

38

This is how my test case looks :

const renderCard = ({
  onSubmit = jest.fn(),
}: RenderCardParams = {}) => {
  return render(
        <Card title={title} onSubmit={onSubmit}>
          <Button type="submit">Save</Button>
        </Card>,
  );
}; 

it("should invoke onSubmit when form is submitted", async () => {
      const onSubmit = jest.fn();
      window.HTMLFormElement.prototype.submit = () => {};
      const { getByText } = renderCard({ onSubmit });
      const button = getByText("Save").closest("button");
      if (button) {
        fireEvent.click(button);
      }
      await wait(() => expect(onSubmit).toHaveBeenCalled());
 });

I am receiving "Error: Not implemented: HTMLFormElement.prototype.submit". I tried the solution mentioned here https://github.com/jsdom/jsdom/issues/1937 , but it did not work. I do not want to silence the errors but implement the test correctly. Thank you.

Brockwell answered 5/6, 2020 at 13:15 Comment(0)
M
55

I had a similar issue which was resolved by calling the event.preventDefault() method.

This would need to be called in your 'onSubmit' function i think

const onSubmit = jest.fn(e => e.preventDefault());

edit: woops forgot to invoke it! πŸ˜…

Macaque answered 16/6, 2020 at 8:46 Comment(4)
For me this worked submit = jest.fn().mockImplementation((e) => e.preventDefault()); – Concoff
Side note: I came across this error when I used an onClick instead on onSubmit on a form – Prandial
Can you explain (1) What's happening (error case) and (2) why e.preventDefault helps? – Mackler
Adding the call to preventDefault on form submits is a very good idea in react, as the default browser behavior for the form is to reload the page. We rarely want that to happen in react. – Aphorism
E
9

I had to mock the implementation of my onSubmit function to clear the error, preventing the default submit behavior.

Give something like this a try:

const onSubmit = jest.fn();


it("should invoke onSubmit when form is submitted", async () => {
      onSubmit.mockImplementation(event => {
        event.preventDefault();
      });

      const { getByText } = renderCard({ onSubmit });
      const button = getByText("Save").closest("button");
      if (button) {
        fireEvent.click(button);
      }
      await wait(() => expect(onSubmit).toHaveBeenCalled());
 });

You can read more about event.preventDefault in React here and here.

Something else that worked for me was using RTL's fireEvent.submit, although you need to get a form element for that to work. Assuming your implementation of Card renders a form in a way that's recognizable by getByRole, you could also try something like this:

fireEvent.submit(screen.getByRole('form'));

That would be in lieu of simulating the button click, which may be acceptable in your case since you seem to be testing form submission rather than the button's behavior.

Engelhart answered 22/11, 2020 at 20:24 Comment(0)
S
5

I had also a similar issue.
However, for me this was happening because I was using userEvent.click and the solution of placing preventDefault() in the fn didn't work for me since the click event and submit event are different.

I had to switch to fireEvent.submit and that worked without the need to prevent default and be able to test the form submission.

PS: I was using a form to wrap the inputs

Error

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

Work

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

This is the basic form implentation

   <form onSubmit={handleSubmit}>
     <input type={'text'} placeholder={'user name'} value={username} onChange={e => setUsername(e.target.value)} />
     <br/>
     <input type={'text'} placeholder={'password'} value={password} onChange={e => setPassword(e.target.value)} />
     <br/>
     <input type={'submit'} disabled={!username && !password} value={'Login'} />
   </form>

And here you can see the implementation of the test to handle the submitted event

it('should handle submit event', async () => {
    const handleSubmitFn = jest.fn();
    render(<Form handleSubmit={handleSubmitFn}/>);

    const username = screen.getByPlaceholderText(/user name/);
    const password= screen.getByPlaceholderText(/password/);
    const submitButton = screen.getByText(/login/i);

    userEvent.type(username, 'myuserbla');
    userEvent.type(password, 'mypwd');

    fireEvent.submit(submitButton)

    await waitFor(() => {
      expect(handleSubmitFn).toHaveBeenCalledTimes(1);
    })
  })
Scenery answered 22/11, 2022 at 2:43 Comment(0)
C
-2

Adding type="button" to the button element is also effective. This will dismiss that error.

Realised this because I had a save and cancel button. I tested the save first and this error didn't occur but when I tested the cancel the error occurred.

When I came to this answer, reading the first answer gave me a clue and as I was about to add the event.preventDefault() I realised type="button" was missing on the cancel button.

Both buttons are child elements of the form.

Candis answered 5/3, 2022 at 23:15 Comment(2)
This kept my enter button from submitting a form, however. – Vivid
The button to submit the form should be a type="submit", and the submit event of the form should be handled. Changing the type alters the semantics of the form. As @Vivid writes, the browser by default submit the form when you press enter. Changing the type also affects how screen readers and other assistive technologies can interact with the form. – Aphorism

© 2022 - 2024 β€” McMap. All rights reserved.