import { expect, test } from "@playwright/test"; import { login } from "./hub.ts"; test("фильтр → карточка окном → заметка сохраняется и переживает перезагрузку", async ({ page }) => { await login(page, "/app/jira/issues"); await page.getByRole("button", { name: /Фильтры/ }).click(); await page.getByRole("combobox", { name: "Исполнитель" }).selectOption("Петров Пётр Петрович"); await expect(page).toHaveURL(/assignee=/); await expect(page.getByText("Исполнитель: Петров Пётр Петрович")).toBeVisible(); // плашка 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\/jira\/issues\?project=AWPCA&assignee=/); await expect(page.getByRole("combobox", { name: "Исполнитель" })).toHaveValue("Петров Пётр Петрович"); // панель осталась раскрытой (sessionStorage) }); test("фильтр в адресе в новой вкладке: панель свёрнута, плашка и счётчик видны, плашка снимает фильтр", async ({ page }) => { await login(page, "/app/jira/issues?project=AWPCA&assignee=" + encodeURIComponent("Петров Пётр Петрович")); await page.evaluate(() => sessionStorage.clear()); await page.reload(); const btn = page.getByRole("button", { name: /Фильтры/ }); await expect(btn).toHaveAttribute("aria-expanded", "false"); await expect(btn).toContainText("1"); await expect(page.getByRole("combobox", { name: "Исполнитель" })).toHaveCount(0); await expect(page.locator("table.issue-table tbody tr")).toHaveCount(2); await page.getByRole("button", { name: "Снять фильтр Исполнитель" }).click(); await expect(page).not.toHaveURL(/assignee=/); await expect(page.locator("table.issue-table tbody tr")).toHaveCount(9); }); test("щелчок по ячейке строки открывает карточку, по выделенному тексту — нет", async ({ page }) => { await login(page, "/app/jira/issues"); await page.getByRole("cell", { name: "Схема свода" }).click(); await expect(page).toHaveURL(/\/app\/jira\/issues\/AURORA-9001/); await expect(page.getByRole("dialog").getByRole("heading", { name: "Схема свода" })).toBeVisible(); }); test("окно поверх списка: прокрутка списка на месте при открытии и после закрытия", async ({ page }) => { await page.setViewportSize({ width: 1280, height: 320 }); await login(page, "/app/jira/issues"); await expect(page.locator("table.issue-table 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.issue-table 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\/jira\/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\/jira\/issues\?project=AWPCA$/); 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\/jira\/board\?project=AWPCA$/); await expect(page.getByRole("dialog")).toHaveCount(0); }); test("прямой адрес и перезагрузка: окно поверх списка, ✕ ведёт в список", async ({ page }) => { await login(page, "/app/jira/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\/jira\/issues\?project=AWPCA$/); await expect(page.getByRole("dialog")).toHaveCount(0); }); test("карточка: описание свёрнуто, комментарии новые сверху и скрываются с запоминанием, команды свёрнуты", async ({ page }) => { await login(page, "/app/jira/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/jira/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/jira/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/jira/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/jira/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\/jira\/issues\?project=AWPCA$/); }); test("узкий экран: окно на всю ширину, заметки выше связей, без горизонтальной прокрутки", async ({ page }) => { await page.setViewportSize({ width: 600, height: 800 }); await login(page, "/app/jira/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/jira/issues"); await expect(page.locator("table.issue-table tbody tr")).toHaveCount(9); await expect(page.getByRole("link", { name: "AURORA-9006", exact: true })).toHaveCount(0); await page.getByRole("radio", { name: "Все" }).click(); await expect(page).toHaveURL(/closed=1/); await expect(page.getByRole("radio", { name: "Все" })).toBeChecked(); await expect(page.getByRole("link", { name: "AURORA-9006", exact: true })).toBeVisible(); await expect(page.locator("table.issue-table tbody tr")).toHaveCount(10); });