Testing Lab 6: Testing Forms
Objectives
- Update the Form to be more Accessible
- Test Loading Data into the Form
- Test Updating Form Values
- Test Validation Rules
Steps
Update the Form to be more Accessible
One of the great benefits of using React Testing Library is that it helps us build a more accessible application. The way we initially built our form HTML needs to be updated so we can more easily and realiably select various form elements and errors.
Update the form to be more accessible.
src\projects\ProjectForm.tsx
...
function ProjectForm({ project: initialProject, onCancel }: ProjectFormProps) {
...
return (
<form
aria-label="Edit a Project"
name="projectForm"
className="input-group vertical"
onSubmit={handleSubmit}
>
<label htmlFor="name">Project Name</label>
<input
+ id="name"
+ aria-label="project name"
type="text"
name="name"
placeholder="enter name"
value={project.name}
onChange={handleChange}
/>
{errors.name.length > 0 && (
<div
+ role="alert"
className="card error">
<p>{errors.name}</p>
</div>
)}
<label htmlFor="description">Project Description</label>
<textarea
+ id="description"
+ aria-label="project description"
name="description"
placeholder="enter description"
value={project.description}
onChange={handleChange}
/>
{errors.description.length > 0 && (
<div
+ role="alert"
className="card error">
<p>{errors.description}</p>
</div>
)}
<label htmlFor="budget">Project Budget</label>
<input
+ id="budget"
type="number"
name="budget"
placeholder="enter budget"
value={project.budget}
onChange={handleChange}
/>
{errors.budget.length > 0 && (
<div
+ role="alert"
className="card error">
<p>{errors.budget}</p>
</div>
)}
<label htmlFor="isActive">Active?</label>
<input
+ id="isActive"
type="checkbox"
name="isActive"
checked={project.isActive}
onChange={handleChange}
/>
<div className="input-group">
<button className="primary bordered medium">Save</button>
<span />
<button type="button" className="bordered medium" onClick={onCancel}>
cancel
</button>
</div>
</form>
);
}
export default ProjectForm;
Test Loading Data into the Form
Create the file
src\projects\__tests__\ProjectForm-test.tsx
.Add the setup code below to test loading data into the form.
src\projects\__tests__\ProjectForm-test.tsx
import React from 'react';
import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { Project } from '../Project';
import ProjectForm from '../ProjectForm';
import { Provider } from 'react-redux';
import { store } from '../../state';
import userEvent from '@testing-library/user-event';
describe('<ProjectForm />', () => {
let project: Project;
let updatedProject: Project;
let handleCancel: jest.Mock;
let nameTextBox: any;
let descriptionTextBox: HTMLElement;
let budgetTextBox: HTMLElement;
const setup = () => {
render(
<Provider store={store}>
<MemoryRouter>
<ProjectForm project={project} onCancel={handleCancel} />
</MemoryRouter>
</Provider>
);
nameTextBox = screen.getByRole('textbox', {
name: /project name/i,
});
descriptionTextBox = screen.getByRole('textbox', {
name: /project description/i,
});
budgetTextBox = screen.getByRole('spinbutton', {
name: /project budget/i,
});
};
beforeEach(() => {
project = new Project({
id: 1,
name: 'Mission Impossible',
description: 'This is really difficult',
budget: 100,
});
updatedProject = new Project({
name: 'Ghost Protocol',
description:
'Blamed for a terrorist attack on the Kremlin, Ethan Hunt (Tom Cruise) and the entire IMF agency...',
});
handleCancel = jest.fn();
});
test('should load project into form', () => {
setup();
expect(
screen.getByRole('form', {
name: /edit a project/i,
})
).toHaveFormValues({
name: project.name,
description: project.description,
budget: project.budget,
isActive: project.isActive,
});
});
});Verify that the intial test passes.
PASS src/projects/__tests__/ProjectForm-test.tsx
Test Updating Form Values
Test
src\projects\__tests__\ProjectForm-test.tsx
...
describe('<ProjectForm />', () => {
...
+ test('should accept input', async () => {
+ setup();
+ const user = userEvent.setup();
+ await user.clear(nameTextBox);
+ await user.type(nameTextBox, updatedProject.name);
+ expect(nameTextBox).toHaveValue(updatedProject.name);
+
+ await user.clear(descriptionTextBox);
+ await user.type(descriptionTextBox, updatedProject.description);
+ expect(descriptionTextBox).toHaveValue(updatedProject.description);
+
+ await user.clear(budgetTextBox);
+ await user.type(budgetTextBox, updatedProject.budget.toString());
+ expect(budgetTextBox).toHaveValue(updatedProject.budget);
+ });
});
1. **Verify** the **test passes**.
```shell
PASS src/projects/__tests__/ProjectForm-test.tsx
Test Validation Rules
Test
src\projects\__tests__\ProjectForm-test.tsx
...
describe('<ProjectForm />', () => {
...
+ test('name should display required validation', async () => {
+ setup();
+ const user = userEvent.setup();
+ await user.clear(nameTextBox);
+ expect(screen.getByRole('alert')).toBeInTheDocument();
+ });
+
+ test('name should display minlength validation', async () => {
+ setup();
+ const user = userEvent.setup();
+ await user.clear(nameTextBox);
+ await user.type(nameTextBox, 'ab');
+ await expect(screen.getByRole('alert')).toBeInTheDocument();
+ await user.type(nameTextBox, 'c');
+ expect(screen.queryByRole('alert')).not.toBeInTheDocument();
+ });
+
+ test('budget should display not 0 validation', async () => {
+ setup();
+ const user = userEvent.setup();
+ await user.clear(budgetTextBox);
+ await user.type(budgetTextBox, '0');
+ expect(screen.getByRole('alert')).toBeInTheDocument();
+ await user.type(budgetTextBox, '1');
+ expect(screen.queryByRole('alert')).not.toBeInTheDocument();
+ });
});Verify all tests pass.
PASS src/projects/__tests__/ProjectForm-test.tsx