Last active
November 14, 2025 09:34
-
-
Save isocroft/f66e4062eef20beb867dfc2f0a2046d8 to your computer and use it in GitHub Desktop.
This is version 2 of a simple test for how using async/await with react testing library can slow down tests
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import React, { useState } from "react"; | |
| import { render, act, waitFor } from "@testing-library/react"; | |
| import userEvent from "@testing-library/user-event"; | |
| import { roles, Components } from "constants/ui-copy"; | |
| const { _TextBoxes: $RecipeFormTextBoxes, _Buttons: $RecipeFormButtons } = Components.RecipeForm; | |
| function RecipeForm ({ onSubmit }) { | |
| function formToObject(form) { | |
| const formData = new FormData(form); | |
| return Object.fromEntries(formData.entries()); | |
| } | |
| return ( | |
| <form method="post" onSubmit={(event) => { | |
| event.preventDefault(); | |
| onSubmit(formToObject(event.target)); | |
| }}> | |
| <h3 role={roles.heading}>Fill Recipe Form</h3> | |
| <input | |
| type="text" | |
| role={roles.textbox} | |
| name={$RecipeFormTextBoxes.title.name} | |
| aria-label={$RecipeFormTextBoxes.title.name} | |
| /> | |
| <input | |
| type="text" | |
| role={roles.textbox} | |
| name={$RecipeFormTextBoxes.description.name} | |
| aria-label={$RecipeFormTextBoxes.description.name} | |
| /> | |
| <input | |
| type="number" | |
| role={roles.spinbutton} | |
| name={$RecipeFormTextBoxes.servings.name} | |
| aria-label={$RecipeFormTextBoxes.servings.name} | |
| /> | |
| <button | |
| role={roles.button} | |
| type="submit" | |
| aria-label={$RecipeFormButtons.submit.name} | |
| > | |
| {$RecipeFormButtons.submit.text} | |
| </button> | |
| </form> | |
| ); | |
| } | |
| function setupComponentForUserInteraction(jsx) { | |
| return { | |
| user: userEvent.setup(), | |
| ...render(jsx), | |
| }; | |
| } | |
| function fillTheRecipeFormAndSubmitFor (user, screen) { | |
| // Fill the form | |
| const titleBoxEntryPromise = user.type( | |
| screen.getByRole( | |
| roles.textbox, | |
| { name: $RecipeFormTextBoxes.title.name } | |
| ), | |
| "Test recipe", | |
| ); | |
| const descBoxEntryPromise = user.type( | |
| screen.getByRole( | |
| roles.textbox, | |
| { name: $RecipeFormTextBoxes.description.name } | |
| ), | |
| "Delicious recipe", | |
| ); | |
| const servingsBoxEntryPromise = user.type( | |
| screen.getByRole( | |
| roles.spinbutton, | |
| { name: $RecipeFormTextBoxes.servings.name } | |
| ), | |
| "4" | |
| ); | |
| return Promise.all([ | |
| titleBoxEntryPromise, | |
| descBoxEntryPromise, | |
| servingsBoxEntryPromise | |
| ]).then(() => { | |
| // Submit the form | |
| return user.click( | |
| screen.getByRole( | |
| roles.button, | |
| { name: $RecipeFormButtons.submit.name } | |
| ) | |
| ); | |
| }); | |
| } | |
| it("should submit correct recipe form data", async () => { | |
| const mockSubmit = jest.fn(); | |
| const { user, screen } = setupComponentForUserInteraction( | |
| <RecipeForm onSubmit={mockSubmit} /> | |
| ); | |
| await act(() => { | |
| return fillTheRecipeFormAndSubmitFor(user, screen); | |
| }); | |
| expect(mockSubmit).toHaveBeenCalledWith({ | |
| name: "Test recipe", | |
| description: "Delicious recipe", | |
| servings: 4, | |
| }); | |
| }); |
Author
Author
import React, { useState } from "react";
import { render, act } from "@testing-library/react";
import { arrange, waitFor } from "../helpers/custom-test-utils";
import userEvent from "@testing-library/user-event";
import { roles, Components } from "constants/ui-copy";
const { _TextBoxes: $RecipeFormTextBoxes, _Buttons: $RecipeFormButtons } = Components.RecipeForm;
function RecipeForm ({ onSubmit }) {
function formToObject(form) {
const formData = new FormData(form);
return Object.fromEntries(formData.entries());
}
return (
<form method="post" onSubmit={(event) => {
event.preventDefault();
onSubmit(formToObject(event.target));
}}>
<h3 role={roles.heading}>Fill Recipe Form</h3>
<input
type="text"
role={roles.textbox}
name={$RecipeFormTextBoxes.title.name}
aria-label={$RecipeFormTextBoxes.title.name}
/>
<input
type="text"
role={roles.textbox}
name={$RecipeFormTextBoxes.description.name}
aria-label={$RecipeFormTextBoxes.description.name}
/>
<input
type="number"
role={roles.spinbutton}
name={$RecipeFormTextBoxes.servings.name}
aria-label={$RecipeFormTextBoxes.servings.name}
/>
<button
role={roles.button}
type="submit"
aria-label={$RecipeFormButtons.submit.name}
>
{$RecipeFormButtons.submit.text}
</button>
</form>
);
}
function setupComponentForUserInteraction(jsx) {
return {
user: userEvent.setup(),
...render(jsx),
};
}
function fillTheRecipeFormAndSubmitFor (user, screen) {
// Fill the form
const titleBoxEntryPromise = user.type(
screen.getByRole(
roles.textbox,
{ name: $RecipeFormTextBoxes.title.name }
),
"Test recipe",
);
const descBoxEntryPromise = user.type(
screen.getByRole(
roles.textbox,
{ name: $RecipeFormTextBoxes.description.name }
),
"Delicious recipe",
);
const servingsBoxEntryPromise = user.type(
screen.getByRole(
roles.spinbutton,
{ name: $RecipeFormTextBoxes.servings.name }
),
"4"
);
return Promise.all([
titleBoxEntryPromise,
descBoxEntryPromise,
servingsBoxEntryPromise
]).then(() => {
// Submit the form
return user.click(
screen.getByRole(
roles.button,
{ name: $RecipeFormButtons.submit.name }
)
);
});
}
it("should submit correct recipe form data", () => {
const mockSubmit = jest.fn();
/*
const { user: thisUser, screen } = arrange(() => {
return setupComponentForUserInteraction(
<RecipeForm onSubmit={mockSubmit} />
);
});
*/
const { user: thisUser, screen } = setupComponentForUserInteraction(
<RecipeForm onSubmit={mockSubmit} />
);
const promiseOfFilledAndSubmittedForm = act(() => {
return fillTheRecipeFormAndSubmitFor(thisUser, screen);
});
return waitFor(promiseOfFilledAndSubmittedForm).then(() => {
expect(mockSubmit).toHaveBeenCalledWith({
name: "Test recipe",
description: "Delicious recipe",
servings: 4,
});
}).end();
});
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
LIVE DEMO: https://codesandbox.io/s/filter-component-stage-7-62d31p