diff --git a/.gitignore b/.gitignore index b56602c..4a0f716 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ web/test-results/ web/playwright-report/ web/.e2e-hub/ *.tsbuildinfo +web/e2e-screens/ diff --git a/web/e2e/screens.spec.ts b/web/e2e/screens.spec.ts new file mode 100644 index 0000000..a0f876c --- /dev/null +++ b/web/e2e/screens.spec.ts @@ -0,0 +1,100 @@ +import { expect, test, type Page } from "@playwright/test"; +import { mkdirSync } from "node:fs"; +import { resolve } from "node:path"; +import { agentServeRefresh, login } from "./hub.ts"; + +// Снимки экранов для проверки вида (запуск: SCREENS=1 npx playwright test e2e/screens.spec.ts). +// Результат — web/e2e-screens/<тема>-<ширина>-<экран>.png, в git не попадает. +test.skip(!process.env.SCREENS, "снимки только по SCREENS=1"); + +const OUT = resolve(import.meta.dirname, "../e2e-screens"); +const SIZES = [{ w: 1920, h: 1080 }, { w: 390, h: 844 }]; + +for (const theme of ["light", "dark"] as const) { + for (const { w, h } of SIZES) { + test.describe(`${theme} ${w}`, () => { + test.use({ viewport: { width: w, height: h }, colorScheme: theme }); + test.beforeEach(async ({ page }) => { + mkdirSync(OUT, { recursive: true }); + await page.addInitScript((t) => { localStorage.setItem("graph-theme", t); }, theme); + }); + const shot = (page: Page, name: string) => + page.screenshot({ path: `${OUT}/${theme}-${w}-${name}.png`, fullPage: true }); + const settle = async (page: Page) => { + await page.evaluate(() => document.fonts.ready.then(() => undefined)); + await page.waitForTimeout(400); // раскладка доски и переходы успокаиваются до снимка + }; + + test("login", async ({ page }) => { + await page.goto("/app/board"); + await expect(page.getByLabel("Клиентский токен хаба")).toBeVisible(); + await settle(page); + await shot(page, "login"); + }); + + test("board", async ({ page }) => { + await login(page); + await expect(page.locator(".board .card[data-key]").first()).toBeVisible(); + await settle(page); + await shot(page, "board"); + }); + + test("board-bydate", async ({ page }) => { + await login(page); + await page.getByRole("button", { name: "По датам" }).click(); + await expect(page.locator(".dcol").first()).toBeVisible(); + await settle(page); + await shot(page, "board-bydate"); + }); + + test("board-problems", async ({ page }) => { + await login(page); + await expect(page.locator(".board .card[data-key]").first()).toBeVisible(); + await page.getByRole("button", { name: /Проблемы/ }).click(); + await settle(page); + await shot(page, "board-problems"); + }); + + test("issues", async ({ page }) => { + await login(page, "/app/issues"); + await expect(page.locator("table.issue-table tbody tr").first()).toBeVisible(); + await settle(page); + await shot(page, "issues"); + }); + + test("issues-filters", async ({ page }) => { + await login(page, "/app/issues"); + await page.getByRole("button", { name: /Фильтры/ }).click(); + await page.getByRole("combobox", { name: "Исполнитель" }).selectOption("Петров Пётр Петрович"); + await expect(page).toHaveURL(/assignee=/); + await settle(page); + await shot(page, "issues-filters"); + }); + + test("card", async ({ page }) => { + await login(page, "/app/issues/AURORA-9001"); + await expect(page.getByRole("dialog").getByRole("heading", { name: "Схема свода" })).toBeVisible(); + await settle(page); + await shot(page, "card"); + }); + + test("new-project", async ({ page }) => { + await login(page); + await page.getByLabel("Проект", { exact: true }).selectOption({ label: "+ Новый проект…" }); + await expect(page.getByRole("dialog", { name: "Новый проект" })).toBeVisible(); + await settle(page); + await shot(page, "new-project"); + }); + + test("refresh", async ({ page }) => { + await login(page); + await page.getByRole("button", { name: "Обновить из Jira" }).click(); + await expect(page.getByRole("status")).toContainText(/пакет #\d+/); + await settle(page); + await shot(page, "refresh"); + await agentServeRefresh(); // не оставляем пакет в очереди тестового хаба + await expect(page.getByRole("status")).toContainText(/выполнен/, { timeout: 15_000 }); + }); + }); + } +}