Vấn đề gần như luôn nằm ở role hoặc accessible name — hai thứ Testing Library đọc từ accessibility tree, không phải từ cái mắt nhìn thấy.
Bước đầu tiên: in ra những gì thư viện thấy.
import { logRoles } from "@testing-library/dom"
const { container } = render(<Toolbar />)
logRoles(container) // lists every role and its accessible name
screen.debug() // prints the current DOMCác nguyên nhân theo tần suất:
- Role không phải button. <div onClick> không có role; <a> có role link. Nếu code dùng <div> làm nút thì đó là bug accessibility thật, sửa markup chứ đừng đổi query sang getByTestId.
- Accessible name khác chữ hiển thị. Nút chỉ có icon thì name rỗng, cần aria-label. Nếu có cả aria-label và text thì aria-label thắng — query theo text sẽ fail.
- Name không khớp chính xác. Option name mặc định so khớp đầy đủ sau khi normalize; text có ký tự ẩn, dấu tiếng Việt hay khoảng trắng thừa thì trượt. Dùng regex /luu/i.
- Element bị loại khỏi accessibility tree. aria-hidden="true", hidden, hoặc display: none từ CSS — getByRole bỏ qua chúng. Chỉ khi thật sự cần mới dùng { hidden: true }, và hiểu rằng khi đó test không còn phản ánh trải nghiệm screen reader.
- Component chưa render xong (modal mở sau animation) → dùng findByRole.
Một lưu ý về môi trường: jsdom không tính CSS layout, nên visibility: hidden từ file CSS ngoài không được áp dụng; ngược lại các assertion phụ thuộc kích thước phần tử luôn cho 0 — những case đó phải để cho e2e.