Files
team-planner/tests/e2e/phase3.2.spec.ts
vigdorov 7421f33de8
All checks were successful
continuous-integration/drone/push Build is passing
fix bus phase 3/2
2026-01-15 12:05:57 +03:00

315 lines
14 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { test, expect } from '@playwright/test';
/**
* E2E тесты для Фазы 3.2 Team Planner
* - Детальный просмотр идеи (IdeaDetailModal)
* - Управление видимостью колонок
*
* Используем data-testid для стабильных селекторов
*/
test.describe('Фаза 3.2: Детальный просмотр идеи', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.waitForSelector('[data-testid="ideas-table"]', { timeout: 10000 });
});
test('Кнопка "Подробнее" присутствует в каждой строке', async ({ page }) => {
const emptyState = page.locator('[data-testid="ideas-empty-state"]');
const hasData = !(await emptyState.isVisible().catch(() => false));
if (hasData) {
const viewButtons = page.locator('[data-testid="view-details-button"]');
const buttonCount = await viewButtons.count();
expect(buttonCount).toBeGreaterThan(0);
}
});
test('Клик на кнопку "Подробнее" открывает модалку', async ({ page }) => {
const emptyState = page.locator('[data-testid="ideas-empty-state"]');
const hasData = !(await emptyState.isVisible().catch(() => false));
test.skip(!hasData, 'Нет данных для тестирования');
const viewButton = page.locator('[data-testid="view-details-button"]').first();
await viewButton.click();
const modal = page.locator('[data-testid="idea-detail-modal"]');
await expect(modal).toBeVisible({ timeout: 5000 });
});
test('Модалка показывает заголовок идеи', async ({ page }) => {
const emptyState = page.locator('[data-testid="ideas-empty-state"]');
const hasData = !(await emptyState.isVisible().catch(() => false));
test.skip(!hasData, 'Нет данных для тестирования');
const viewButton = page.locator('[data-testid="view-details-button"]').first();
await viewButton.click();
const modal = page.locator('[data-testid="idea-detail-modal"]');
await expect(modal).toBeVisible({ timeout: 5000 });
// Проверяем наличие заголовка
const title = modal.locator('[data-testid="idea-detail-title"]');
await expect(title).toBeVisible();
const titleText = await title.textContent();
expect(titleText?.length).toBeGreaterThan(0);
});
test('Модалка показывает все поля идеи', async ({ page }) => {
const emptyState = page.locator('[data-testid="ideas-empty-state"]');
const hasData = !(await emptyState.isVisible().catch(() => false));
test.skip(!hasData, 'Нет данных для тестирования');
const viewButton = page.locator('[data-testid="view-details-button"]').first();
await viewButton.click();
const modal = page.locator('[data-testid="idea-detail-modal"]');
await expect(modal).toBeVisible({ timeout: 5000 });
// Проверяем наличие основных полей
await expect(modal.locator('[data-testid="idea-detail-status"]')).toBeVisible();
await expect(modal.locator('[data-testid="idea-detail-priority"]')).toBeVisible();
await expect(modal.locator('[data-testid="idea-detail-description"]')).toBeVisible();
await expect(modal.locator('[data-testid="idea-detail-specification-status"]')).toBeVisible();
});
test('Кнопка редактирования переводит модалку в режим редактирования', async ({ page }) => {
const emptyState = page.locator('[data-testid="ideas-empty-state"]');
const hasData = !(await emptyState.isVisible().catch(() => false));
test.skip(!hasData, 'Нет данных для тестирования');
const viewButton = page.locator('[data-testid="view-details-button"]').first();
await viewButton.click();
const modal = page.locator('[data-testid="idea-detail-modal"]');
await expect(modal).toBeVisible({ timeout: 5000 });
// Нажимаем "Редактировать"
const editButton = modal.locator('[data-testid="idea-detail-edit-button"]');
await editButton.click();
// Должны появиться поля ввода
await expect(modal.locator('[data-testid="idea-detail-title-input"]')).toBeVisible();
// И кнопки сохранения/отмены
await expect(modal.locator('[data-testid="idea-detail-save-button"]')).toBeVisible();
await expect(modal.locator('[data-testid="idea-detail-cancel-button"]')).toBeVisible();
});
test('Кнопка "Отмена" возвращает режим просмотра', async ({ page }) => {
const emptyState = page.locator('[data-testid="ideas-empty-state"]');
const hasData = !(await emptyState.isVisible().catch(() => false));
test.skip(!hasData, 'Нет данных для тестирования');
const viewButton = page.locator('[data-testid="view-details-button"]').first();
await viewButton.click();
const modal = page.locator('[data-testid="idea-detail-modal"]');
await expect(modal).toBeVisible({ timeout: 5000 });
// Нажимаем "Редактировать"
await modal.locator('[data-testid="idea-detail-edit-button"]').click();
await expect(modal.locator('[data-testid="idea-detail-title-input"]')).toBeVisible();
// Нажимаем "Отмена"
await modal.locator('[data-testid="idea-detail-cancel-button"]').click();
// Должен вернуться режим просмотра
await expect(modal.locator('[data-testid="idea-detail-title"]')).toBeVisible();
await expect(modal.locator('[data-testid="idea-detail-edit-button"]')).toBeVisible();
});
test('Сохранение изменений работает корректно', async ({ page }) => {
const emptyState = page.locator('[data-testid="ideas-empty-state"]');
const hasData = !(await emptyState.isVisible().catch(() => false));
test.skip(!hasData, 'Нет данных для тестирования');
const viewButton = page.locator('[data-testid="view-details-button"]').first();
await viewButton.click();
const modal = page.locator('[data-testid="idea-detail-modal"]');
await expect(modal).toBeVisible({ timeout: 5000 });
// Запоминаем исходный заголовок
const originalTitle = await modal.locator('[data-testid="idea-detail-title"]').textContent();
// Нажимаем "Редактировать"
await modal.locator('[data-testid="idea-detail-edit-button"]').click();
// Изменяем заголовок
const titleInput = modal.locator('[data-testid="idea-detail-title-input"]');
const newTitle = `${originalTitle} (изменено ${Date.now()})`;
await titleInput.fill(newTitle);
// Сохраняем
await modal.locator('[data-testid="idea-detail-save-button"]').click();
// Ждём возврата в режим просмотра
await expect(modal.locator('[data-testid="idea-detail-title"]')).toBeVisible({ timeout: 5000 });
// Проверяем что заголовок обновился
const updatedTitle = await modal.locator('[data-testid="idea-detail-title"]').textContent();
expect(updatedTitle).toBe(newTitle);
});
test('Статус ТЗ сохраняется после редактирования идеи', async ({ page }) => {
/**
* Регрессионный тест:
* 1. Сгенерировать ТЗ для идеи
* 2. Открыть модалку детального просмотра
* 3. Отредактировать и сохранить
* 4. Статус ТЗ должен остаться "Есть"
*/
const emptyState = page.locator('[data-testid="ideas-empty-state"]');
const hasData = !(await emptyState.isVisible().catch(() => false));
test.skip(!hasData, 'Нет данных для тестирования');
// Сначала генерируем ТЗ для первой идеи
const firstRow = page.locator('[data-testid^="idea-row-"]').first();
const specButton = firstRow.locator('[data-testid="specification-button"]');
await specButton.click();
const specModal = page.locator('[data-testid="specification-modal"]');
await expect(specModal).toBeVisible({ timeout: 5000 });
// Ждём пока сгенерируется ТЗ (контент или ошибка)
const specContent = specModal.locator('[data-testid="specification-content"]');
const specError = specModal.locator('[data-testid="specification-error"]');
await expect(specContent.or(specError)).toBeVisible({ timeout: 60000 });
const hasSpec = await specContent.isVisible().catch(() => false);
test.skip(!hasSpec, 'Не удалось сгенерировать ТЗ');
// Закрываем модалку ТЗ
await page.locator('[data-testid="specification-close-button"]').click();
await expect(specModal).not.toBeVisible({ timeout: 3000 });
// Теперь открываем детальный просмотр
const viewButton = firstRow.locator('[data-testid="view-details-button"]');
await viewButton.click();
const detailModal = page.locator('[data-testid="idea-detail-modal"]');
await expect(detailModal).toBeVisible({ timeout: 5000 });
// Проверяем что ТЗ есть
const specStatus = detailModal.locator('[data-testid="idea-detail-specification-status"]');
await expect(specStatus).toContainText('Есть');
// Нажимаем "Редактировать"
await detailModal.locator('[data-testid="idea-detail-edit-button"]').click();
// Меняем описание (не трогаем ТЗ)
const descInput = detailModal.locator('[data-testid="idea-detail-description-input"]');
await descInput.fill('Обновлённое описание для теста ' + Date.now());
// Сохраняем
await detailModal.locator('[data-testid="idea-detail-save-button"]').click();
// Ждём возврата в режим просмотра
await expect(detailModal.locator('[data-testid="idea-detail-title"]')).toBeVisible({ timeout: 5000 });
// БАГ: Статус ТЗ должен остаться "Есть", но показывает "Нет"
await expect(specStatus).toContainText('Есть');
});
});
test.describe('Фаза 3.2: Управление видимостью колонок', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.waitForSelector('[data-testid="ideas-table"]', { timeout: 10000 });
});
test('Кнопка настройки колонок присутствует', async ({ page }) => {
const visibilityButton = page.locator('[data-testid="column-visibility-button"]');
await expect(visibilityButton).toBeVisible();
});
test('Клик на кнопку открывает меню с колонками', async ({ page }) => {
const visibilityButton = page.locator('[data-testid="column-visibility-button"]');
await visibilityButton.click();
const menu = page.locator('[data-testid="column-visibility-menu"]');
await expect(menu).toBeVisible({ timeout: 3000 });
});
test('Меню содержит опции для всех колонок', async ({ page }) => {
const visibilityButton = page.locator('[data-testid="column-visibility-button"]');
await visibilityButton.click();
const menu = page.locator('[data-testid="column-visibility-menu"]');
await expect(menu).toBeVisible({ timeout: 3000 });
// Проверяем наличие основных колонок
await expect(menu.locator('[data-testid="column-visibility-item-status"]')).toBeVisible();
await expect(menu.locator('[data-testid="column-visibility-item-priority"]')).toBeVisible();
await expect(menu.locator('[data-testid="column-visibility-item-description"]')).toBeVisible();
});
test('Переключение видимости колонки работает', async ({ page }) => {
// Проверяем что колонка "Описание" видна
const table = page.locator('[data-testid="ideas-table"]');
const descHeader = table.locator('th', { hasText: 'Описание' });
await expect(descHeader).toBeVisible();
// Открываем меню
const visibilityButton = page.locator('[data-testid="column-visibility-button"]');
await visibilityButton.click();
// Скрываем колонку "Описание"
const descItem = page.locator('[data-testid="column-visibility-item-description"]');
await descItem.click();
// Закрываем меню кликом вне его
await page.keyboard.press('Escape');
// Колонка должна скрыться
await expect(descHeader).not.toBeVisible();
// Возвращаем обратно
await visibilityButton.click();
await descItem.click();
await page.keyboard.press('Escape');
// Колонка должна появиться снова
await expect(descHeader).toBeVisible();
});
test('Кнопка "Показать все" возвращает все колонки', async ({ page }) => {
// Скрываем несколько колонок
const visibilityButton = page.locator('[data-testid="column-visibility-button"]');
await visibilityButton.click();
await page.locator('[data-testid="column-visibility-item-description"]').click();
await page.locator('[data-testid="column-visibility-item-module"]').click();
// Нажимаем "Показать все"
await page.locator('[data-testid="column-visibility-show-all"]').click();
await page.keyboard.press('Escape');
// Все колонки должны быть видны
const table = page.locator('[data-testid="ideas-table"]');
await expect(table.locator('th', { hasText: 'Описание' })).toBeVisible();
await expect(table.locator('th', { hasText: 'Модуль' })).toBeVisible();
});
test('Название и действия нельзя скрыть', async ({ page }) => {
const visibilityButton = page.locator('[data-testid="column-visibility-button"]');
await visibilityButton.click();
// Проверяем что "Название" disabled
const titleItem = page.locator('[data-testid="column-visibility-item-title"]');
await expect(titleItem).toHaveAttribute('aria-disabled', 'true');
// Действия тоже disabled
const actionsItem = page.locator('[data-testid="column-visibility-item-actions"]');
await expect(actionsItem).toHaveAttribute('aria-disabled', 'true');
});
});