end fase 2

This commit is contained in:
2026-01-15 00:18:35 +03:00
parent 85e7966c97
commit 739a7d172d
63 changed files with 3194 additions and 322 deletions

View File

@ -8,13 +8,15 @@ import { test, expect } from '@playwright/test';
* - Создание идей
* - Inline-редактирование
* - Удаление
*
* Используем data-testid для стабильных селекторов
*/
test.describe('Фаза 1: Базовый функционал', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
// Ждём загрузки таблицы
await page.waitForSelector('table, [role="grid"]', { timeout: 10000 });
// Ждём загрузки таблицы идей
await page.waitForSelector('[data-testid="ideas-table"]', { timeout: 10000 });
});
test('Страница загружается', async ({ page }) => {
@ -23,29 +25,22 @@ test.describe('Фаза 1: Базовый функционал', () => {
});
test('Таблица идей отображается', async ({ page }) => {
const table = page.locator('table, [role="grid"]');
const table = page.locator('[data-testid="ideas-table"]');
await expect(table).toBeVisible();
});
test('Таблица имеет заголовки колонок', async ({ page }) => {
const headers = page.locator('th, [role="columnheader"]');
const count = await headers.count();
expect(count).toBeGreaterThan(0);
// Проверяем что есть хотя бы несколько важных колонок
const headerTexts = await headers.allTextContents();
expect(headerTexts.length).toBeGreaterThan(0);
test('Контейнер таблицы присутствует', async ({ page }) => {
const container = page.locator('[data-testid="ideas-table-container"]');
await expect(container).toBeVisible();
});
test('Фильтры присутствуют на странице', async ({ page }) => {
// Ищем элементы фильтров (inputs, selects, MUI компоненты)
const filterElements = page.locator('input, [role="combobox"], .MuiSelect-select');
const count = await filterElements.count();
expect(count).toBeGreaterThanOrEqual(1);
const filters = page.locator('[data-testid="ideas-filters"]');
await expect(filters).toBeVisible();
});
test('Поле поиска работает', async ({ page }) => {
const searchInput = page.locator('input[placeholder*="Поиск"]');
const searchInput = page.locator('[data-testid="search-input"] input');
await expect(searchInput).toBeVisible();
await searchInput.fill('test');
@ -55,13 +50,19 @@ test.describe('Фаза 1: Базовый функционал', () => {
await searchInput.clear();
});
test('Кнопка создания идеи существует', async ({ page }) => {
const buttons = page.locator('button');
const createButton = buttons.filter({
hasText: /создать|добавить|новая|\+/i,
});
test('Фильтр статуса присутствует', async ({ page }) => {
const statusFilter = page.locator('[data-testid="filter-status"]');
await expect(statusFilter).toBeVisible();
});
await expect(createButton.first()).toBeVisible();
test('Фильтр приоритета присутствует', async ({ page }) => {
const priorityFilter = page.locator('[data-testid="filter-priority"]');
await expect(priorityFilter).toBeVisible();
});
test('Фильтр модуля присутствует', async ({ page }) => {
const moduleFilter = page.locator('[data-testid="filter-module"]');
await expect(moduleFilter).toBeVisible();
});
test('Модалка создания открывается', async ({ page }) => {
@ -73,8 +74,8 @@ test.describe('Фаза 1: Базовый функционал', () => {
await createButton.click();
// Проверяем что модалка открылась (используем .first() т.к. MUI создаёт вложенные элементы)
const modal = page.locator('[role="dialog"]').first();
// Проверяем что модалка открылась
const modal = page.locator('[data-testid="create-idea-modal"]');
await expect(modal).toBeVisible();
// Закрываем модалку
@ -82,74 +83,116 @@ test.describe('Фаза 1: Базовый функционал', () => {
await expect(modal).toBeHidden();
});
test('Таблица показывает данные или empty state', async ({ page }) => {
const rows = page.locator('tbody tr, [role="row"]');
const rowCount = await rows.count();
test('Модалка создания содержит необходимые поля', async ({ page }) => {
const createButton = page
.locator('button')
.filter({ hasText: /создать|добавить|новая/i })
.first();
if (rowCount > 1) {
// Есть данные
expect(rowCount).toBeGreaterThan(1);
} else {
// Ищем empty state
const emptyState = page.locator('text=/нет|пусто|Нет идей/i');
const hasEmptyState = (await emptyState.count()) > 0;
expect(hasEmptyState || rowCount >= 1).toBeTruthy();
}
await createButton.click();
const modal = page.locator('[data-testid="create-idea-modal"]');
await expect(modal).toBeVisible();
// Проверяем наличие формы и поля ввода
const form = page.locator('[data-testid="create-idea-form"]');
await expect(form).toBeVisible();
const titleInput = page.locator('[data-testid="idea-title-input"]');
await expect(titleInput).toBeVisible();
const cancelButton = page.locator('[data-testid="cancel-create-idea"]');
await expect(cancelButton).toBeVisible();
const submitButton = page.locator('[data-testid="submit-create-idea"]');
await expect(submitButton).toBeVisible();
await page.keyboard.press('Escape');
});
test('Таблица показывает данные или empty state', async ({ page }) => {
// Проверяем либо есть строки с данными, либо пустое состояние
const emptyState = page.locator('[data-testid="ideas-empty-state"]');
const ideaRows = page.locator('[data-testid^="idea-row-"]');
const hasEmptyState = await emptyState.isVisible().catch(() => false);
const rowCount = await ideaRows.count();
expect(hasEmptyState || rowCount > 0).toBeTruthy();
});
test('Пагинация присутствует', async ({ page }) => {
const pagination = page.locator(
'.MuiTablePagination-root, [aria-label*="pagination"], nav[aria-label*="pagination"]'
);
const pagination = page.locator('.MuiTablePagination-root');
await expect(pagination.first()).toBeVisible();
});
test('Inline-редактирование работает (double-click)', async ({ page }) => {
// Находим ячейки таблицы (пропускаем первую - drag handle)
const cells = page.locator('tbody td');
const cellCount = await cells.count();
// Проверяем есть ли данные
const ideaRows = page.locator('[data-testid^="idea-row-"]');
const rowCount = await ideaRows.count();
if (cellCount > 1) {
// Пробуем double-click на ячейках (начиная со второй)
for (let i = 1; i < Math.min(cellCount, 6); i++) {
const cell = cells.nth(i);
const text = await cell.textContent();
if (rowCount > 0) {
// Находим первую строку и кликаем дважды на ячейку
const firstRow = ideaRows.first();
const cells = firstRow.locator('td');
const cellCount = await cells.count();
if (text && text.trim()) {
await cell.dblclick();
await page.waitForTimeout(500);
if (cellCount > 2) {
// Пробуем double-click на ячейках (пропускаем drag handle и color)
const cell = cells.nth(2);
await cell.dblclick();
await page.waitForTimeout(500);
// Проверяем появился ли input для редактирования
const input = page.locator(
'.MuiInputBase-input, input.MuiInput-input, tbody input, [role="combobox"]'
);
const inputCount = await input.count();
// Проверяем появился ли input для редактирования
const input = page.locator('.MuiInputBase-input, [role="combobox"]');
const inputCount = await input.count();
if (inputCount > 0) {
// Отменяем редактирование
await page.keyboard.press('Escape');
return; // Тест прошёл
}
if (inputCount > 0) {
// Отменяем редактирование
await page.keyboard.press('Escape');
}
}
}
// Если нет данных для inline-редактирования - это ОК
// Тест прошёл без ошибок
expect(true).toBeTruthy();
});
test('Кнопка удаления в таблице', async ({ page }) => {
// Ищем иконки/кнопки удаления в строках
const deleteButtons = page.locator(
'tbody button[aria-label*="delete"], tbody button[aria-label*="удалить"], tbody [data-testid="DeleteIcon"], tbody svg'
);
test('Кнопка удаления присутствует в строке', async ({ page }) => {
const ideaRows = page.locator('[data-testid^="idea-row-"]');
const rowCount = await ideaRows.count();
const count = await deleteButtons.count();
// Если есть данные, должны быть кнопки удаления
const rows = await page.locator('tbody tr').count();
if (rowCount > 0) {
const deleteButtons = page.locator('[data-testid="delete-idea-button"]');
const count = await deleteButtons.count();
expect(count).toBeGreaterThan(0);
}
});
if (rows > 0) {
expect(count).toBeGreaterThanOrEqual(0);
test('Кнопка сброса фильтров появляется при активных фильтрах', async ({ page }) => {
// Изначально кнопка сброса скрыта
const clearButton = page.locator('[data-testid="clear-filters-button"]');
await expect(clearButton).toBeHidden();
// Выбираем статус в фильтре
const statusFilter = page.locator('[data-testid="filter-status"]');
await statusFilter.locator('[role="combobox"]').click();
const listbox = page.locator('[role="listbox"]');
await expect(listbox).toBeVisible();
// Выбираем любой статус кроме "Все"
const options = listbox.locator('[role="option"]');
const optionCount = await options.count();
if (optionCount > 1) {
await options.nth(1).click();
// Теперь кнопка сброса должна быть видна
await expect(clearButton).toBeVisible();
// Сбрасываем фильтры
await clearButton.click();
await expect(clearButton).toBeHidden();
}
});
});