sdlc-hub/web/e2e/board.spec.ts
Andrey Epifancev 8062dc7668 Веб: «Связи» на /app/jira/links, условные пункты меню (#108)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 19:41:32 +04:00

112 lines
7.3 KiB
TypeScript
Raw 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 { expect, test } from "@playwright/test";
import { CW } from "../src/modules/jira/board/constants.ts";
import { agentServeRefresh, jiraEvent, login } from "./hub.ts";
test.beforeEach(async ({ page }) => { await login(page); });
test("перетаскивание за рамку двигает карточку и переживает перезагрузку", async ({ page }) => {
const card = page.locator('.board .card[data-key="AURORA-9001"]');
const before = await card.boundingBox();
const grip = card.locator(".grip.t");
const g = (await grip.boundingBox())!;
await page.mouse.move(g.x + g.width / 2, g.y + g.height / 2);
await page.mouse.down();
await page.mouse.move(g.x + g.width / 2 + 160, g.y + g.height / 2 + 40, { steps: 8 });
await page.mouse.up();
const after = (await card.boundingBox())!;
expect(Math.round(after.x - before!.x)).toBeGreaterThan(100);
await page.reload();
const reloaded = (await page.locator('.board .card[data-key="AURORA-9001"]').boundingBox())!;
expect(Math.abs(reloaded.x - after.x)).toBeLessThan(2);
expect(Math.abs(reloaded.y - after.y)).toBeLessThan(2);
});
test("растяжка за уголок и возврат размера двойным щелчком", async ({ page }) => {
const card = page.locator('.board .card[data-key="AURORA-9002"]');
const rsz = (await card.locator(".rsz").boundingBox())!;
const w0 = (await card.boundingBox())!.width;
await page.mouse.move(rsz.x + 2, rsz.y + 2);
await page.mouse.down();
await page.mouse.move(rsz.x + 82, rsz.y + 42, { steps: 6 });
await page.mouse.up();
await expect.poll(async () => (await card.boundingBox())!.width).toBeGreaterThan(w0 + 60);
await card.locator(".rsz").dblclick();
await expect.poll(async () => Math.round((await card.boundingBox())!.width)).toBe(CW);
});
test("«по датам» — колонки дат, подписи и стрелки", async ({ page }) => {
await page.getByRole("button", { name: "По датам" }).click();
await expect(page.locator(".dcol").first()).toBeVisible();
await expect(page.locator(".dates-track .dlabel").first()).toBeVisible();
await expect(page.locator("svg.edges path")).not.toHaveCount(0);
});
test("«Обновить из Jira» с учебным агентом доходит до конца", async ({ page }) => {
// на странице две кнопки: значок в панели («— все проекты») и кнопка в шапке доски
const btn = page.getByTestId("page-board").getByRole("button", { name: "Обновить из Jira", exact: true });
await btn.click();
await expect(btn).toBeDisabled();
await expect(jiraEvent(page)).toContainText(/пакет #\d+/);
await agentServeRefresh();
await expect(jiraEvent(page)).toContainText(/пакет #\d+ выполнен/, { timeout: 15_000 });
await expect(btn).toBeEnabled();
await expect(page.getByText(/^данные на .* · все проекты$/)).toBeVisible();
await expect(page.locator(".board .card[data-key]")).not.toHaveCount(0);
});
test("ⓘ открывает карточку окном поверх доски", async ({ page }) => {
await page.locator('.board .card[data-key="AURORA-9001"]').getByRole("link", { name: "Открыть карточку AURORA-9001" }).click();
await expect(page).toHaveURL(/\/app\/jira\/issues\/AURORA-9001$/);
await expect(page.getByRole("dialog").getByRole("heading", { name: "Схема свода" })).toBeVisible();
await expect(page.getByTestId("page-board")).toBeAttached();
await expect(page.getByRole("link", { name: "Связи", exact: true })).toHaveAttribute("aria-current", "page");
});
test("M-3: прокрутка и данные доски после окна карточки и после другого раздела", async ({ page }) => {
await page.setViewportSize({ width: 1600, height: 600 });
await expect(page.locator(".board .card[data-key]").first()).toBeVisible();
const y = await page.evaluate(() => { window.scrollTo(0, document.documentElement.scrollHeight); return window.scrollY; });
expect(y).toBeGreaterThan(100);
// реальный щелчок по ⓘ карточки, видимой в окне после прокрутки (иначе Playwright сам сдвинет страницу)
const key = await page.evaluate(() => {
const vh = window.innerHeight;
const a = [...document.querySelectorAll<HTMLElement>(".board .card[data-key] a.open-card")]
.find((e) => { const r = e.getBoundingClientRect(); return r.top > 200 && r.bottom < vh - 20; });
return a?.closest<HTMLElement>(".card")?.dataset.key ?? null;
});
expect(key).not.toBeNull();
let boardLoads = 0;
page.on("request", (r) => { if (new URL(r.url()).pathname === "/board") boardLoads++; });
await page.locator(`.board .card[data-key="${key}"] a.open-card`).click();
await expect(page.getByRole("dialog")).toBeVisible();
await page.keyboard.press("Escape");
await expect(page).toHaveURL(/\/app\/jira\/links\?project=AWPCA$/);
expect(await page.evaluate(() => window.scrollY)).toBeGreaterThan(y - 2);
expect(boardLoads).toBe(0); // доска не перезагружалась
// боковая панель закреплена (#69): ссылки разделов видны и после прокрутки, щелчок не двигает доску
await page.getByRole("link", { name: "Задачи", exact: true }).click();
await expect(page).toHaveURL(/\/app\/jira\/issues\?project=AWPCA$/);
await page.getByRole("link", { name: "Связи", exact: true }).click();
await expect.poll(() => page.evaluate(() => window.scrollY)).toBeGreaterThan(y - 2);
});
test("заметка из окна уходит в хаб, карточка на доске остаётся на месте, прокрутка сохраняется", async ({ page }) => {
// доска показывает вид работы по меткам Jira (fromHub), заметка work_kind в карточку доски не попадает: проверяем снимок /board
await page.locator('.board .card[data-key="AURORA-9002"]').evaluate((e) => { (e as HTMLElement).dataset.marker = "keep"; });
await page.locator('.board .card[data-key="AURORA-9002"] a.open-card').click();
const d = page.getByRole("dialog");
const y = await page.evaluate(() => window.scrollY);
await d.getByRole("button", { name: "Править" }).click();
await d.getByLabel("Вид работы").selectOption("тестирование");
const snap = page.waitForResponse((r) => new URL(r.url()).pathname === "/board" && r.request().method() === "GET");
await d.getByRole("button", { name: "Сохранить" }).click();
const body = await (await snap).json();
expect(body.issues.find((i: { key: string }) => i.key === "AURORA-9002").work_kind).toBe("тестирование");
await expect(d.getByRole("button", { name: "Править" })).toBeVisible();
await page.keyboard.press("Escape");
await expect(page.getByTestId("page-board")).toBeAttached();
await expect(page.locator('.board .card[data-key="AURORA-9002"]')).toBeVisible();
await expect(page.locator('.board .card[data-key="AURORA-9002"]')).toHaveAttribute("data-marker", "keep"); // узел не пересоздан
expect(await page.evaluate(() => window.scrollY)).toBe(y);
});