# E2E Testing Guide Руководство по написанию e2e тестов для Team Planner. ## Принципы ### 1. Тесты следуют требованиям, а не коду Тесты должны проверять **пользовательские сценарии** из требований, а не адаптироваться под текущую реализацию. ``` ❌ Плохо: "Проверить что кнопка имеет класс .MuiButton-contained" ✅ Хорошо: "Проверить что пользователь может создать новую идею" ``` **Порядок работы:** 1. Прочитать требования к фазе/фиче в `ROADMAP.md` и `REQUIREMENTS.md` 2. Выделить пользовательские сценарии 3. Написать тесты для каждого сценария 4. Убедиться что тесты проверяют бизнес-логику, а не детали реализации ### 2. Стабильные селекторы через data-testid **Никогда не использовать:** - Позиционные селекторы: `tbody tr`, `.nth(2)`, `:first-child` - CSS классы MUI: `.MuiButton-root`, `.MuiTableCell-body` - Структурные селекторы: `table > tbody > tr > td` **Всегда использовать:** - `data-testid` для уникальной идентификации элементов - `[role="..."]` только для стандартных ARIA ролей (tab, dialog, listbox) - Текстовые селекторы только для статичного контента ```typescript // ❌ Плохо - сломается при изменении структуры const row = page.locator('tbody tr').nth(2); const button = page.locator('.MuiIconButton-root').first(); // ✅ Хорошо - стабильно при рефакторинге const row = page.locator('[data-testid="idea-row-123"]'); const button = page.locator('[data-testid="delete-idea-button"]'); ``` ## Соглашения по data-testid ### Именование | Паттерн | Пример | Использование | |---------|--------|---------------| | `{component}-{element}` | `ideas-table` | Основные элементы | | `{component}-{element}-{id}` | `idea-row-123` | Динамические элементы | | `{action}-{target}-button` | `delete-idea-button` | Кнопки действий | | `{name}-input` | `member-name-input` | Поля ввода | | `{name}-modal` | `team-member-modal` | Модальные окна | | `filter-{name}` | `filter-status` | Фильтры | ### Обязательные data-testid по компонентам #### Таблицы ``` {name}-table - сам table элемент {name}-table-container - обёртка таблицы {name}-empty-state - состояние "нет данных" {item}-row-{id} - строка с данными ``` #### Формы и модалки ``` {name}-modal - Dialog компонент {name}-form - form элемент {field}-input - поля ввода (TextField) {field}-select - выпадающие списки (FormControl) submit-{action}-button - кнопка отправки cancel-{action}-button - кнопка отмены ``` #### Действия в строках ``` edit-{item}-button - редактирование delete-{item}-button - удаление toggle-{feature}-button - переключение ``` ## Работа с MUI компонентами ### Popover / Menu MUI Popover рендерится через Portal в ``. Для добавления `data-testid` используй `slotProps`: ```tsx , }} > ``` ### Dialog Dialog также использует Portal. Добавляй `data-testid` напрямую: ```tsx ``` ### Select / Combobox Для работы с MUI Select: ```typescript // Открыть dropdown await page.locator('[data-testid="filter-status"] [role="combobox"]').click(); // Выбрать опцию из listbox const listbox = page.locator('[role="listbox"]'); await listbox.locator('[role="option"]').filter({ hasText: 'Бэклог' }).click(); ``` ### TextField TextField в MUI оборачивает input в несколько div. Для доступа к самому input: ```typescript // data-testid на TextField // В тесте - добавляем input селектор const input = page.locator('[data-testid="member-name-input"] input'); await input.fill('Имя'); ``` ## Структура тестов ### Файловая организация ``` tests/ ├── e2e/ │ ├── auth.setup.ts # Аутентификация (запускается первой) │ ├── phase1.spec.ts # Тесты фазы 1 │ ├── phase2.spec.ts # Тесты фазы 2 │ └── phase3.spec.ts # Тесты фазы 3 └── playwright.config.ts ``` ### Шаблон тестового файла ```typescript import { test, expect } from '@playwright/test'; /** * E2E тесты для Фазы N Team Planner * - Фича 1 * - Фича 2 * * Используем data-testid для стабильных селекторов */ test.describe('Фаза N: Название фичи', () => { test.beforeEach(async ({ page }) => { await page.goto('/'); // Ждём загрузки основного элемента await page.waitForSelector('[data-testid="main-element"]', { timeout: 10000 }); }); test('Описание сценария', async ({ page }) => { // Arrange - подготовка const element = page.locator('[data-testid="element"]'); // Act - действие await element.click(); // Assert - проверка await expect(element).toBeVisible(); }); }); ``` ### Группировка тестов Группируй тесты по фичам/сценариям, а не по компонентам: ```typescript // ❌ Плохо - группировка по компонентам test.describe('Button tests', () => { ... }); test.describe('Modal tests', () => { ... }); // ✅ Хорошо - группировка по фичам test.describe('Фаза 2: Управление командой - CRUD участников', () => { ... }); test.describe('Фаза 2: Управление командой - Вкладка Роли', () => { ... }); ``` ## Обработка edge cases ### Проверка наличия данных ```typescript test('Тест с данными', async ({ page }) => { const emptyState = page.locator('[data-testid="ideas-empty-state"]'); const hasData = !(await emptyState.isVisible().catch(() => false)); // Пропускаем тест если нет данных test.skip(!hasData, 'Нет данных для тестирования'); // Продолжаем тест... }); ``` ### Работа с динамическими ID ```typescript // Для элементов с динамическими ID используй prefix-селектор const ideaRows = page.locator('[data-testid^="idea-row-"]'); const rowCount = await ideaRows.count(); ``` ### Ожидание после действий ```typescript // После клика, который вызывает API запрос await button.click(); await page.waitForTimeout(500); // Даём время на запрос // Лучше - ждать конкретный результат await expect(newElement).toBeVisible({ timeout: 5000 }); ``` ## Чеклист перед написанием тестов - [ ] Прочитаны требования к фиче в ROADMAP.md - [ ] Определены пользовательские сценарии - [ ] Проверено наличие data-testid в компонентах - [ ] Если data-testid отсутствуют - добавить их в компоненты - [ ] Тесты не зависят от порядка/позиции элементов в DOM - [ ] Тесты корректно обрабатывают случай отсутствия данных ## Добавление data-testid в компоненты При добавлении новых компонентов или фич, сразу добавляй data-testid: ```tsx // Таблица {items.map(item => ( ))}
// Модалка с формой
``` ## Запуск тестов ```bash # Все тесты npx playwright test # Конкретный файл npx playwright test e2e/phase2.spec.ts # С UI режимом для отладки npx playwright test --ui # Только упавшие тесты npx playwright test --last-failed ```