sdlc-hub/web/e2e/geometry.spec.ts
Andrey Epifancev cec8beecfb feat(web): шапка доски, панель управления по группам, сворачиваемая легенда (#65)
BoardHeader вместо Toolbar, панель управления в Panel, легенда сворачивается
(jira-hub-legend), ящик проблем, экспорт и таблица связей в Panel.
Dropdown: явный проп menu; тест фонового обновления проектов мокает GET /batches/7.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 00:33:32 +04:00

79 lines
4.6 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, type Page } from "@playwright/test";
import { login } from "./hub.ts";
// то, что в jsdom не проверить: реальная геометрия, ширины и поведение при смене фильтров
function watchErrors(page: Page): string[] {
const errs: string[] = [];
page.on("pageerror", (e) => errs.push("pageerror: " + e.message));
page.on("console", (m) => { if (m.type() === "error") errs.push("console: " + m.text()); });
return errs;
}
test("высота карточек берётся по содержимому из слоя измерения, а не 120px", async ({ page }) => {
await login(page);
await expect(page.locator(".board .card[data-key]").first()).toBeVisible();
const rows = await page.evaluate(() => [...document.querySelectorAll<HTMLElement>(".board .card[data-key]")].map((e) => ({
key: e.dataset.key!, h: e.offsetHeight, fits: e.scrollHeight <= e.clientHeight + 1,
})));
expect(rows.length).toBeGreaterThan(0);
for (const r of rows) {
expect(r.fits, `${r.key}: содержимое не помещается`).toBe(true);
expect(r.h % 8, `${r.key}: высота не на сетке 8`).toBe(0);
}
// на фикстуре hub-basic содержимое пяти карточек одной высоты, поэтому «разные высоты» не проверяем (отклонение от плана):
// высота не равна прежней фиксированной 120px
expect(rows.some((r) => r.h !== 120), "все карточки по 120px — высота не по содержимому").toBe(true);
});
test("при открытой панели «Проблемы» полоса дат по ширине равна обёртке холста", async ({ page }) => {
await login(page);
await page.getByRole("button", { name: "По датам" }).click();
await expect(page.locator(".dates-in")).toBeVisible();
for (const open of [false, true]) {
if (open) {
await page.getByRole("button", { name: /^Проблемы/ }).click();
await expect(page.locator(".drawer")).toBeVisible();
}
await expect.poll(async () => {
const a = (await page.locator(".dates-in").boundingBox())!.width, b = (await page.locator(".stage .wrap").boundingBox())!.width;
return Math.abs(a - b);
}, `панель ${open ? "открыта" : "закрыта"}`).toBeLessThanOrEqual(1);
}
});
test("смена спринта и исполнителя, убирающая карточки, не даёт ошибок и белого экрана", async ({ page }) => {
const errs = watchErrors(page);
await login(page);
await expect(page.locator(".board .card[data-key]").first()).toBeVisible();
const total = await page.locator(".board .card[data-key]").count();
const dd = (i: number) => page.locator(".board-controls .multi-btn").nth(i); // 0 — спринты, 1 — исполнители
for (const [i, name] of [[0, "Спринт 3"], [1, "Сидорова"]] as const) {
await dd(i).click();
await page.getByRole("button", { name: "Снять выбор" }).click();
await page.locator(".dd-item", { hasText: name }).first().locator("input").check();
await page.keyboard.press("Escape");
await expect(page.locator("h1")).toHaveText("Доска связей");
await expect(page.locator(".sticky .ui-page-header")).toBeVisible();
await expect(page.locator(".board")).toBeVisible();
await expect.poll(() => page.locator(".board .card[data-key]:not(.dim)").count(), `фильтр «${name}»`).toBeLessThan(total);
await dd(i).click();
await page.getByRole("button", { name: "Снять выбор" }).click();
await page.keyboard.press("Escape");
}
await page.getByRole("button", { name: "По датам" }).click();
await expect(page.locator(".sticky .ui-page-header")).toBeVisible();
expect(errs).toEqual([]);
});
test("h1 на страницах доски и задач, h2 в окне карточки", async ({ page }) => {
await login(page);
await expect(page.locator("h1")).toHaveText("Доска связей");
await page.getByRole("link", { name: "Задачи" }).click();
await expect(page.getByRole("heading", { level: 1, name: "Задачи" })).toBeVisible();
await page.getByRole("link", { name: "AURORA-9002" }).click();
// карточка — окно поверх списка: её заголовок h2 (тема задачи), h1 остаётся у страницы под окном
await expect(page.getByRole("dialog").getByRole("heading", { level: 2, name: "API свода" })).toBeVisible();
await expect(page.locator("h1")).toHaveCount(1);
});