import { expect, test } from "@playwright/test"; import { login } from "./hub.ts"; test("фильтр → карточка окном → заметка сохраняется и переживает перезагрузку", async ({ page }) => { await login(page, "/app/issues"); await page.getByLabel("Исполнитель").selectOption("Петров Пётр Петрович"); await expect(page).toHaveURL(/assignee=/); await page.getByRole("link", { name: "AURORA-9002", exact: true }).click(); const d = page.getByRole("dialog"); await expect(d.getByRole("heading", { name: "API свода" })).toBeVisible(); await d.getByRole("button", { name: "Править" }).click(); await d.getByLabel("Осталось").fill("e2e: дописать тесты"); await d.getByRole("button", { name: "Сохранить" }).click(); await expect(d.getByText("e2e: дописать тесты")).toBeVisible(); await page.reload(); await expect(page.getByRole("dialog").getByText("e2e: дописать тесты")).toBeVisible(); await page.keyboard.press("Escape"); await expect(page.getByRole("dialog")).toHaveCount(0); await expect(page).toHaveURL(/\/app\/issues\?assignee=/); await expect(page.getByLabel("Исполнитель")).toHaveValue("Петров Пётр Петрович"); }); test("окно поверх списка: прокрутка списка на месте при открытии и после закрытия", async ({ page }) => { await page.setViewportSize({ width: 1280, height: 320 }); await login(page, "/app/issues"); await expect(page.locator("table.grid tbody tr")).toHaveCount(10); const y = await page.evaluate(() => { window.scrollTo(0, document.documentElement.scrollHeight); return window.scrollY; }); expect(y).toBeGreaterThan(50); await page.locator("table.grid tbody tr").last().getByRole("link").click(); await expect(page.getByRole("dialog")).toBeVisible(); expect(await page.evaluate(() => window.scrollY)).toBe(y); await page.keyboard.press("Escape"); await expect(page.getByRole("dialog")).toHaveCount(0); expect(await page.evaluate(() => window.scrollY)).toBe(y); }); test("переход по связи и «назад»; ✕ и щелчок по фону; после закрытия «назад» не открывает карточку", async ({ page }) => { await login(page); // доска await page.getByRole("link", { name: "Задачи", exact: true }).click(); await page.getByRole("link", { name: "AURORA-9001", exact: true }).click(); const d = page.getByRole("dialog"); await d.getByRole("link", { name: "AURORA-9002", exact: true }).click(); await expect(page).toHaveURL(/\/app\/issues\/AURORA-9002$/); await expect(d.getByRole("heading", { name: "API свода" })).toBeVisible(); await page.goBack(); await expect(d.getByRole("heading", { name: "Схема свода" })).toBeVisible(); await d.getByRole("link", { name: "AURORA-9002", exact: true }).click(); await d.getByRole("button", { name: "Закрыть" }).click(); await expect(page).toHaveURL(/\/app\/issues$/); await expect(page.getByRole("dialog")).toHaveCount(0); await page.getByRole("link", { name: "AURORA-9003", exact: true }).click(); await expect(page.getByRole("dialog")).toBeVisible(); await page.mouse.click(5, 300); // фон слева от окна await expect(page.getByRole("dialog")).toHaveCount(0); await page.goBack(); await expect(page).toHaveURL(/\/app\/board$/); await expect(page.getByRole("dialog")).toHaveCount(0); }); test("прямой адрес и перезагрузка: окно поверх списка, ✕ ведёт в список", async ({ page }) => { await login(page, "/app/issues/AURORA-9001"); const d = page.getByRole("dialog"); await expect(d.getByRole("heading", { name: "Схема свода" })).toBeVisible(); await expect(page.getByTestId("page-issues")).toBeAttached(); await page.reload(); await expect(page.getByRole("dialog").getByRole("heading", { name: "Схема свода" })).toBeVisible(); await page.getByRole("dialog").getByRole("button", { name: "Закрыть" }).click(); await expect(page).toHaveURL(/\/app\/issues$/); await expect(page.getByRole("dialog")).toHaveCount(0); }); test("карточка: описание свёрнуто, комментарии новые сверху и скрываются с запоминанием, команды свёрнуты", async ({ page }) => { await login(page, "/app/issues/AURORA-9001"); const d = page.getByRole("dialog"); await expect(d.getByRole("heading", { name: "Комментарии (3)" })).toBeVisible(); await expect(d.getByTestId("comments").getByRole("listitem").first()).toContainText("[мост] служебный комментарий"); await expect(d.getByRole("button", { name: "развернуть" })).toBeVisible(); await expect(d.locator("pre.description")).toHaveClass(/clamped/); // свёрнуто построчным обрезанием (текст в DOM остаётся) await d.getByRole("button", { name: "развернуть" }).click(); await expect(d.locator("pre.description")).not.toHaveClass(/clamped/); await expect(d.getByText("Конец описания.")).toBeVisible(); await expect(d.getByRole("cell", { name: "ensure_fields" })).toBeHidden(); await d.getByText("Команды моста (2)").click(); await expect(d.getByRole("cell", { name: "ensure_fields" })).toBeVisible(); await d.getByRole("button", { name: "Скрыть" }).click(); await expect(d.getByText("Первый комментарий e2e")).toHaveCount(0); await page.reload(); await expect(page.getByRole("dialog").getByRole("button", { name: "Показать" })).toBeVisible(); await expect(page.getByRole("dialog").getByText("Первый комментарий e2e")).toHaveCount(0); }); test("несохранённая заметка: Esc спрашивает, «Остаться» оставляет окно, «Уйти» закрывает", async ({ page }) => { await login(page, "/app/issues"); await page.getByRole("link", { name: "AURORA-9003", exact: true }).click(); const d = page.getByRole("dialog"); await d.getByRole("button", { name: "Править" }).click(); await d.getByLabel("Сделано").fill("черновик"); await page.keyboard.press("Escape"); await expect(d.getByText("Есть несохранённые правки заметки. Уйти без сохранения?")).toBeVisible(); await d.getByRole("button", { name: "Остаться" }).click(); await expect(d.getByLabel("Сделано")).toHaveValue("черновик"); await page.keyboard.press("Escape"); await d.getByRole("button", { name: "Уйти" }).click(); await expect(page.getByRole("dialog")).toHaveCount(0); await expect(page).toHaveURL(/\/app\/issues$/); }); test("узкий экран: окно на всю ширину, заметки выше связей, без горизонтальной прокрутки", async ({ page }) => { await page.setViewportSize({ width: 600, height: 800 }); await login(page, "/app/issues/AURORA-9001"); const d = page.getByRole("dialog"); await expect(d.getByRole("heading", { name: "Схема свода" })).toBeVisible(); expect((await d.boundingBox())!.width).toBeGreaterThan(590); const notesY = (await d.getByRole("heading", { name: "Служебные заметки" }).boundingBox())!.y; const linksY = (await d.getByRole("heading", { name: "Связи", exact: true }).boundingBox())!.y; expect(notesY).toBeLessThan(linksY); expect(await d.evaluate((el) => el.scrollWidth <= el.clientWidth + 1)).toBe(true); });