- BuilderPage: paginated table, status/type filters, KPI cards, QuotaBadge - CreateFeatureDrawer: title + description form with Zod-mirror validation - BuilderDetailPage: placeholder for FRONTEND-BUILDER-3B - Sidebar: AI-Конструктор entry, ADMIN-only (ThunderboltOutlined) - Routes: /ai-builder (list) + /ai-builder/:id (detail) - 8 Playwright E2E tests (builder.spec.ts) - STOP conditions adapted: 14 statuses, AWAITING_DESIGN_REVIEW, no type in create Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
115 lines
5.7 KiB
TypeScript
115 lines
5.7 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
import { loginAs } from './fixtures/auth';
|
|
|
|
test.describe('AI Builder — Liste + Création', () => {
|
|
|
|
// T-BLD-01: Page renders with table and quota badge
|
|
test('Builder page renders for ADMIN with table and quota', async ({ page }) => {
|
|
await loginAs(page, 'admin');
|
|
await page.click('text=AI-Конструктор');
|
|
await expect(page.locator('[data-testid="builder-page"]')).toBeVisible({ timeout: 10000 });
|
|
await expect(page.locator('[data-testid="builder-table"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="builder-quota-badge"]')).toBeVisible({ timeout: 10000 });
|
|
await expect(page.locator('[data-testid="builder-create-button"]')).toBeVisible();
|
|
});
|
|
|
|
// T-BLD-02: Non-admin cannot see sidebar entry
|
|
test('Non-admin does not see AI-Конструктор sidebar entry', async ({ page }) => {
|
|
await loginAs(page, 'commercial');
|
|
await expect(page.locator('text=AI-Конструктор')).toBeHidden();
|
|
});
|
|
|
|
// T-BLD-03: Status filter works
|
|
test('Status filter changes table results', async ({ page }) => {
|
|
await loginAs(page, 'admin');
|
|
await page.goto('/ai-builder');
|
|
await expect(page.locator('[data-testid="builder-page"]')).toBeVisible({ timeout: 10000 });
|
|
|
|
const statusSelect = page.locator('[data-testid="builder-status-filter"] .ant-select');
|
|
await statusSelect.click();
|
|
await page.locator('.ant-select-dropdown:visible .ant-select-item[title="Черновик"]').click();
|
|
await page.waitForTimeout(500);
|
|
await expect(page.locator('[data-testid="builder-table"]')).toBeVisible();
|
|
});
|
|
|
|
// T-BLD-04: Create drawer opens and validates
|
|
test('Create drawer opens and validates min length', async ({ page }) => {
|
|
await loginAs(page, 'admin');
|
|
await page.goto('/ai-builder');
|
|
await expect(page.locator('[data-testid="builder-create-button"]')).toBeVisible({ timeout: 10000 });
|
|
await page.click('[data-testid="builder-create-button"]');
|
|
|
|
const drawer = page.locator('.ant-drawer');
|
|
await expect(drawer).toBeVisible({ timeout: 5000 });
|
|
await expect(drawer.locator('text=Новая фича')).toBeVisible();
|
|
|
|
// Submit with empty fields → validation errors
|
|
await page.click('[data-testid="builder-create-submit"]');
|
|
await expect(drawer.locator('text=Введите название')).toBeVisible({ timeout: 3000 });
|
|
await expect(drawer.locator('text=Введите описание')).toBeVisible();
|
|
|
|
// Fill title too short
|
|
await page.fill('[data-testid="builder-create-title"]', 'abc');
|
|
await page.click('[data-testid="builder-create-submit"]');
|
|
await expect(drawer.locator('text=Минимум 5 символов')).toBeVisible({ timeout: 3000 });
|
|
});
|
|
|
|
// T-BLD-05: Create feature happy path
|
|
test('Create feature happy path', async ({ page }) => {
|
|
await loginAs(page, 'admin');
|
|
await page.goto('/ai-builder');
|
|
await expect(page.locator('[data-testid="builder-create-button"]')).toBeVisible({ timeout: 10000 });
|
|
await page.click('[data-testid="builder-create-button"]');
|
|
|
|
const drawer = page.locator('.ant-drawer');
|
|
await expect(drawer).toBeVisible({ timeout: 5000 });
|
|
|
|
await page.fill('[data-testid="builder-create-title"]', 'Тестовая фича E2E Builder');
|
|
await page.fill('[data-testid="builder-create-description"]', 'Подробное описание тестовой фичи для E2E тестирования AI Builder модуля.');
|
|
await page.click('[data-testid="builder-create-submit"]');
|
|
|
|
// Drawer should close after creation
|
|
await expect(drawer).toBeHidden({ timeout: 10000 });
|
|
|
|
// Feature should appear in table
|
|
await expect(page.locator('[data-testid="builder-table"]').locator('text=Тестовая фича E2E Builder')).toBeVisible({ timeout: 10000 });
|
|
});
|
|
|
|
// T-BLD-06: Click feature navigates to detail page
|
|
test('Click feature row navigates to detail page', async ({ page }) => {
|
|
await loginAs(page, 'admin');
|
|
await page.goto('/ai-builder');
|
|
await expect(page.locator('[data-testid="builder-table"]')).toBeVisible({ timeout: 10000 });
|
|
|
|
// Wait for at least one row
|
|
const firstLink = page.locator('[data-testid="builder-table"]').locator('a').first();
|
|
if (await firstLink.isVisible({ timeout: 5000 }).catch(() => false)) {
|
|
await firstLink.click();
|
|
await expect(page.locator('[data-testid="builder-detail-page"]')).toBeVisible({ timeout: 10000 });
|
|
await expect(page.locator('text=Фича #')).toBeVisible();
|
|
}
|
|
});
|
|
|
|
// T-BLD-07: Direct navigation to /ai-builder works
|
|
test('Direct navigation to /ai-builder works', async ({ page }) => {
|
|
await loginAs(page, 'admin');
|
|
await page.goto('/ai-builder');
|
|
await expect(page.locator('[data-testid="builder-page"]')).toBeVisible({ timeout: 10000 });
|
|
await expect(page.locator('[data-testid="builder-table"]')).toBeVisible();
|
|
await expect(page.locator('[data-testid="builder-create-button"]')).toBeVisible();
|
|
});
|
|
|
|
// T-BLD-08: Type filter works
|
|
test('Type filter renders options', async ({ page }) => {
|
|
await loginAs(page, 'admin');
|
|
await page.goto('/ai-builder');
|
|
await expect(page.locator('[data-testid="builder-page"]')).toBeVisible({ timeout: 10000 });
|
|
|
|
const typeSelect = page.locator('[data-testid="builder-type-filter"] .ant-select');
|
|
await typeSelect.click();
|
|
await expect(page.locator('.ant-select-dropdown:visible .ant-select-item[title="Новое поле"]')).toBeVisible();
|
|
await expect(page.locator('.ant-select-dropdown:visible .ant-select-item[title="Новая таблица"]')).toBeVisible();
|
|
await expect(page.locator('.ant-select-dropdown:visible .ant-select-item[title="Процесс"]')).toBeVisible();
|
|
});
|
|
});
|