sdlc-hub/web/e2e/issues.spec.ts
2026-10-17 11:37:14 +04:00

188 lines
13 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 { 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(9); // AURORA-9006 «Готово» скрыта
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.locator(".links-sec").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.locator(".links-sec").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("div.description")).toHaveClass(/clamped/); // свёрнуто по высоте (текст в DOM остаётся)
const hClamped = (await d.locator("div.description").boundingBox())!.height;
expect(await d.locator("div.description").evaluate((e) => e.scrollHeight > e.clientHeight)).toBe(true);
await d.getByRole("button", { name: "развернуть" }).click();
await expect(d.locator("div.description")).not.toHaveClass(/clamped/);
expect((await d.locator("div.description").boundingBox())!.height).toBeGreaterThan(hClamped);
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("разметка Jira: описание и комментарии отрендерены, «исходник» у каждого блока, ключ задачи — ссылка", async ({ page }) => {
await login(page, "/app/issues/AURORA-9001");
const d = page.getByRole("dialog");
const desc = d.locator("section", { has: page.getByRole("heading", { name: "Описание" }) });
await expect(desc.getByRole("heading", { level: 3, name: "Контекст" })).toBeVisible();
await expect(desc.getByRole("listitem").first()).toHaveText("Таблицы.");
await expect(desc.getByText("КФО", { exact: true })).toHaveJSProperty("tagName", "STRONG");
await expect(desc).not.toContainText("h3.");
await desc.getByRole("button", { name: "исходник" }).click();
await expect(desc.locator("pre.description")).toContainText("h3. Контекст");
await expect(desc.locator("pre.description")).toContainText("# Таблицы.");
await desc.getByRole("button", { name: "текст" }).click();
await expect(desc.getByRole("heading", { level: 3, name: "Контекст" })).toBeVisible();
const items = d.getByTestId("comments").getByRole("listitem");
const first = items.filter({ hasText: "Первый комментарий e2e" });
await expect(first.getByText("Первый комментарий e2e")).toHaveJSProperty("tagName", "STRONG");
await first.getByRole("button", { name: "исходник" }).click();
await expect(first.locator("pre.body")).toContainText("*Первый комментарий e2e*");
await expect(d.locator("pre.body")).toHaveCount(1);
await items.filter({ hasText: "Схему согласовали" }).getByRole("link", { name: "AURORA-9002" }).click();
await expect(d.getByRole("heading", { name: "API свода" })).toBeVisible();
await page.goBack();
await expect(d.getByRole("heading", { name: "Схема свода" })).toBeVisible();
await expect(d.locator("pre.body, pre.description")).toHaveCount(0); // после перехода всё снова отрендерено
});
test("C-1: повторное открытие из кэша — длинное описание свёрнуто с кнопкой «развернуть»", async ({ page }) => {
await login(page, "/app/issues");
await page.getByRole("link", { name: "AURORA-9001", exact: true }).click();
const d = page.getByRole("dialog");
await expect(d.getByRole("button", { name: "развернуть" })).toBeVisible();
await page.keyboard.press("Escape");
await expect(page.getByRole("dialog")).toHaveCount(0);
await page.getByRole("link", { name: "AURORA-9001", exact: true }).click();
await expect(d.getByRole("button", { name: "развернуть" })).toBeVisible();
});
test("I-1: телефон 375 px — таблица описания прокручивается внутри, заголовки таблицы без чужого стиля", async ({ page }) => {
await page.setViewportSize({ width: 375, height: 800 });
await login(page, "/app/issues/AURORA-9001");
const d = page.getByRole("dialog");
await d.getByRole("button", { name: "развернуть" }).click();
const th = d.getByRole("columnheader", { name: "Поле", exact: true });
await expect(th).toBeAttached();
expect(await d.evaluate((el) => el.scrollWidth <= el.clientWidth + 1)).toBe(true);
expect(await th.evaluate((el) => getComputedStyle(el).textTransform)).toBe("none");
expect(await d.getByRole("cell", { name: "data_type" }).evaluate((el) => el.scrollWidth <= el.clientWidth + 1)).toBe(true); // слово не рвётся (M-3)
});
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);
await d.getByRole("button", { name: "развернуть" }).click(); // широкая таблица описания прокручивается внутри
await expect(d.getByRole("columnheader", { name: "Источник" })).toBeAttached();
expect(await d.evaluate((el) => el.scrollWidth <= el.clientWidth + 1)).toBe(true);
});
test("закрытые скрыты по умолчанию, снятый флажок показывает их", async ({ page }) => {
await login(page, "/app/issues");
await expect(page.locator("table.grid tbody tr")).toHaveCount(9);
await expect(page.getByRole("link", { name: "AURORA-9006", exact: true })).toHaveCount(0);
const hide = page.getByRole("checkbox", { name: "скрыть закрытые" });
await hide.click(); // не uncheck(): адрес меняется навигацией, флажок следует за ним не сразу
await expect(page).toHaveURL(/closed=1/);
await expect(hide).not.toBeChecked();
await expect(page.getByRole("link", { name: "AURORA-9006", exact: true })).toBeVisible();
await expect(page.locator("table.grid tbody tr")).toHaveCount(10);
});