React Testing Library: Within
Opublikowano: 14.10.2023 - tagi: JavaScript React Testowanie Test Lista
Do czego służy within?
React Testing Library udostępnia funkcję o nazwie within, która pozwala na pisanie zapytań tylko w obrębie danego fragmentu z DOM.
Załóżmy, że mamy listę i każdy element listy składa się z innych elementów. Na przykład:
<ul>
<li>
<h2>Item 1</h2>
<button type="button">Edit</button>
<button type="button">Remove</button>
</li>
<li>
<h2>Item 2</h2>
<button type="button">Edit</button>
<button type="button">Remove</button>
</li>
</ul>
Teraz chcemy napisać test, który sprawdzi usuwanie elementu z listy. Jak pobrać referencję do przycisku: "Remove"? Można każdemu przycisku nadać unikalne id za pomocą data-testid.
Można też użyć funkcji within wystarczy, że przekażemy do niej pojedynczy element listy. Następnie za pomocą zapytania zdobyć referencję do przycisku.
Przykład
Poniżej znajduje się prosty kod TODO listy.
Komponent:
import {useState} from "react";
export function MyComponent() {
const [tasks, setTasks] = useState(['Task 1', 'Task 2', 'Task 3']);
const [task, setTask] = useState('');
const onChangeTask = (e) => {
setTask(e.target.value)
};
const onAddTask = (e) => {
e.preventDefault();
setTasks([task, ...tasks]);
setTask('');
}
const onRemoveTask = (taskToRemove) => {
setTasks(tasks.filter(currentTask => currentTask != taskToRemove))
}
return (
<>
<form onSubmit={onAddTask}>
<label htmlFor="newTask">Task title:</label>
<input type="text" id="newTask" value={task} onChange={onChangeTask} />
<button>Add</button>
</form>
<ul>
{
tasks.map((task, index) => (
<li key={index}>
<span data-testid="name">{task}</span>
<button type="button" onClick={() => onRemoveTask(task)}>Remove</button>
</li>
))
}
</ul>
</>
)
}
I chcesz stestować usuwanie zadania (element listy).
Test:
import {render, screen, within} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import {MyComponent} from "./MyComponent";
test('should remove task from list', async () => {
// given
const { getAllByRole, queryByText } = screen;
render(<MyComponent />);
const listItems = getAllByRole('listitem').map((item) => ({
name: within(item).getByTestId('name').textContent,
removeBtn: within(item).getByRole('button', { name: 'Remove' }),
}));
const removeTaskBtn = listItems.find((item) => item.name === 'Task 1').removeBtn;
// when
await userEvent.click(removeTaskBtn);
// then
expect(queryByText('Task 1')).not.toBeInTheDocument();
});
Najpierw iterujemy po wszystkich elementach listy za pomocą getAllByRole. Tworzona jest tablica, która zawiera nazwę elementu i referencję do przycisku do usuwania zadania.
Przy każdej iteracji korzystamy z funkcji within. Przekazywany jest do niej pojedynczy element listy. Dzięki temu możesz napisać zapytanie: Pobierz mi przycisk o nazwie "Remove". Nie ważne, że na liście znajduje się więcej niż jeden taki przycisk o tej samej nazwie!