metallkart-erp/tests/e2e/builder.spec.ts
louis a0704edfa3 feat(frontend): AI Builder page + create drawer + E2E tests (FRONTEND-BUILDER-3A)
- 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>
2026-05-18 10:57:30 +04:00

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();
});
});