Правила тест-маркеров (data-testid) для Frontend
Цель: устойчивые, семантические идентификаторы на ключевых элементах UI — чтобы их было легко находить в DOM при отладке и в будущих автотестах (Testing Library / Playwright), не завязываясь на вёрстку и стили.
Что использовать (и почему)
data-testid— канонический атрибут. Это значение по умолчанию у Testing Library (getByTestId) и Playwright (getByTestId,testIdAttribute='data-testid'). Устойчив к рефакторингу стилей/разметки.- НЕ
id— должен быть уникален на странице; в списках/таблицах легко получить дубликаты, ломается при нескольких экземплярах. Используемidтолько когда он реально нужен дляlabel htmlFor/a11y. - НЕ
class— классы для стилей (Tailwind/HeroUI); завязывать тесты на классы хрупко.
Что помечаем (и что НЕ помечаем)
Помечаем значимые, запрашиваемые узлы, а не каждый div:
- Страница — корневой контейнер:
page-<entity>(напр.page-employees). - Форма/модалка —
<entity>-<create|edit>-modal(напр.employee-create-modal). - Поле формы — на интерактивном элементе:
field-<FieldName>; ошибка поля:field-error-<FieldName>. (В форм-библиотеке проставляется автоматически из имени поля TanStack Form — отдельно делать не нужно.) - Таблица/список —
<entity>-table; пагинация —list-pagination. - Действия строки — внутри строки:
row-edit,row-delete. - Подтверждение удаления —
confirm-dialog, кнопкиconfirm-dialog-confirm/confirm-dialog-cancel. - Первичная кнопка действия —
<entity>-add-buttonи т. п. - Состояния (по необходимости) —
state-empty/state-error/state-loading.
НЕ помечаем: декоративные обёртки, иконки, элементы без самостоятельной ценности для теста/отладки.
Именование
- kebab/scoped, латиница, стабильно:
<область>-<сущность>-<роль>(employee-create-modal,tenants-table,tenants-add-button). - Имена полей берём как есть из контракта (
field-FirstName,field-Contacts[0].Value) — это даёт однозначную связь поля формы и DOM. - Идентификатор стабилен (не зависит от текста подписи/локализации/порядка стилей).
Как реализовано (переиспользуемо)
Базовые компоненты принимают необязательный проп и рендерят data-testid — точечно проставлять на
каждом элементе не нужно:
- Поля форм (
shared/ui/form/fields/*) — автоdata-testid={field-${field.name}}(+field-error-…). shared/ui/{FormModal,PageHeader,DataTableCard,TableRowActions,ConfirmDialog,ListPagination,StateSurface}— пропtestId→data-testid.FormModalShellпрокидываетtestIdвFormModal.shared/ui/enum-selects/EnumSelectField— пропtestId;FormSelect/bound-селекты проставляютfield-<name>.
Страницы/виджеты передают testId в эти компоненты (см. примеры в pages/EmployeesPage,
widgets/EmployeesTable).
Scope-префиксы (несколько форм на странице)
Чтобы при нескольких формах не было дублей (form-submit, field-DisplayName) и было видно, к какой
форме относится элемент, у форм есть scope-префикс:
FormModalShellвыводит scope изtestId(снимая хвост-modal) или принимает явныйscope. Внутри: поля →<scope>-field-<name>, кнопки →<scope>-submit/<scope>-cancel(напр.employee-create-field-FirstName,employee-create-submit).- Для немодальных форм (page-forms) оборачивайте поля в
FormScopeProvider scope="<scope>"(из@shared/ui/form), чтобы получить тот же префикс. - Без scope поля остаются
field-<name>, кнопки —form-submit/form-cancel(ок для одной формы).
QA-метаданные (data-qa-*, env-gated)
Помимо data-testid (стабильные селекторы) добавляем QA-метаданные — id сущностей для отладки:
data-qa-tenant-id, data-qa-entry-id, data-qa-user-id и т. п.
- Выключаемы через env
VITE_QA_METADATA('true'/'false'); по умолчанию вкл. в dev, выкл. в prod — чтобы лишняя информация (id) не утекала в прод-DOM. - Помощник
qaAttrs({ 'tenant-id': id, … })из@shared/lib/qa-metadataвозвращает{}при выключенном флаге; распыляйте на элемент:<Table.Row {...qaAttrs({ 'tenant-id': tenant.TenantId })}>. - Базовые компоненты
PageHeader/FormModal(Shell)/ConfirmDialogпринимают пропqa→data-qa-*. data-testid— НЕ env-gated (это селектор, не чувствительные данные). Чувствительные id — только вdata-qa-*.
Применение
- Для нового и редактируемого кода — проставлять маркеры по списку выше (boy-scout rule).
- Базовые примитивы — единый источник; не плодить ad-hoc
data-*именования. - Совместимо с
frontend-rules.md,frontend-jsdoc-rules.md,frontend-fsd-and-atomic-rules.md.
Обратные ссылки
Автоссылки: где используется этот документ.
- frontend-rules.md (1): Правила Frontend Разработки