e2e и снимки боковой панели, ARCHITECTURE (#69)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
c9672183cf
commit
1f74a27cb3
@ -32,8 +32,10 @@
|
||||
и `storage.ts` (ключи `localStorage` прежней страницы; у проекта AWPCA — прежние, у остальных с суффиксом `@<метка>`; набор ключей — состояние модуля, выставляется при монтировании страницы), отрисовка — `web/src/board/components`.
|
||||
Вид — дизайн-система AURA: токены цвета, шрифта, отступов и теней (светлая и тёмная темы) — `web/src/styles/tokens.css`,
|
||||
единственное место с литералами цвета; базовые компоненты (`Button`, `Notice`, `Dialog`, `Field`, `Panel`, меню и др.) —
|
||||
`web/src/ui/`, остальные стили подключаются к токенам по семантическим именам. Обновление данных из Jira (кнопка в
|
||||
верхней панели, отслеживание пакета) — `web/src/shell/refresh.ts` (провайдер в `App.tsx`); доска отдаёт наружу только
|
||||
`web/src/ui/`, остальные стили подключаются к токенам по семантическим именам. Оболочка — `web/src/shell/Layout.tsx`: grid из боковой
|
||||
панели `Sidebar.tsx` (закреплена у левого края, сворачивается в полосу значков — ключ `jira-hub-sidebar`; на экранах
|
||||
уже 640 px — строка с «Меню» и выезжающая панель) и страницы; прокрутка — у окна. Обновление данных из Jira (кнопка в
|
||||
боковой панели, отслеживание пакета) — `web/src/shell/refresh.ts` (провайдер в `App.tsx`); доска отдаёт наружу только
|
||||
источник и события снимка.
|
||||
|
||||
Цепочки: `api → queue → commands/mirror/settings/projects → db`; `db → projects` (только миграция, ленивый импорт); `projects → settings`; `db → backup → paths`, `db → paths`; `files → paths`; `api → relay → settings`; `mcp_server → client → HTTP API`; `importer → mirror/settings`.
|
||||
|
||||
@ -82,10 +82,10 @@ test("M-3: прокрутка и данные доски после окна к
|
||||
await expect(page).toHaveURL(/\/app\/board\?project=AWPCA$/);
|
||||
expect(await page.evaluate(() => window.scrollY)).toBeGreaterThan(y - 2);
|
||||
expect(boardLoads).toBe(0); // доска не перезагружалась
|
||||
// ссылки меню вверху страницы: обычный click прокрутил бы доску к ним до перехода и затёр запомненное, поэтому dispatchEvent
|
||||
await page.getByRole("link", { name: "Задачи", exact: true }).dispatchEvent("click");
|
||||
// боковая панель закреплена (#69): ссылки разделов видны и после прокрутки, щелчок не двигает доску
|
||||
await page.getByRole("link", { name: "Задачи", exact: true }).click();
|
||||
await expect(page).toHaveURL(/\/app\/issues\?project=AWPCA$/);
|
||||
await page.getByRole("link", { name: "Доска связей", exact: true }).dispatchEvent("click");
|
||||
await page.getByRole("link", { name: "Доска связей", exact: true }).click();
|
||||
await expect.poll(() => page.evaluate(() => window.scrollY)).toBeGreaterThan(y - 2);
|
||||
});
|
||||
|
||||
|
||||
@ -9,7 +9,8 @@ export async function login(page: Page, path = "/app/board") {
|
||||
await page.goto(path);
|
||||
await page.getByLabel("Клиентский токен хаба").fill(tokens().client);
|
||||
await page.getByRole("button", { name: "Войти" }).click();
|
||||
await expect(page.getByRole("link", { name: "Доска связей" })).toBeVisible();
|
||||
// оболочка: на широком экране — ссылки разделов в боковой панели, на узком — кнопка «Меню» (#69)
|
||||
await expect(page.getByRole("link", { name: "Доска связей" }).or(page.getByRole("button", { name: "Меню" }))).toBeVisible();
|
||||
}
|
||||
|
||||
async function call(path: string, body: unknown) {
|
||||
|
||||
44
web/e2e/layout.spec.ts
Normal file
44
web/e2e/layout.spec.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import { login } from "./hub.ts";
|
||||
|
||||
// оболочка с боковой панелью (#69)
|
||||
test("панель на месте после прокрутки доски до низа", async ({ page }) => {
|
||||
await page.setViewportSize({ width: 1600, height: 600 });
|
||||
await login(page);
|
||||
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);
|
||||
const side = page.getByRole("complementary", { name: "Панель приложения" });
|
||||
await expect(side.getByRole("link", { name: "Задачи" })).toBeInViewport();
|
||||
await expect(side.getByRole("button", { name: "Обновить из Jira" })).toBeInViewport();
|
||||
expect((await side.boundingBox())!.y).toBe(0);
|
||||
});
|
||||
|
||||
test("свёрнутая панель переживает перезагрузку, доска шире", async ({ page }) => {
|
||||
await login(page);
|
||||
const wrap = page.locator(".wrap");
|
||||
await expect(wrap).toBeVisible();
|
||||
const before = (await wrap.boundingBox())!.width;
|
||||
await page.getByRole("button", { name: "Свернуть панель" }).click();
|
||||
await page.reload();
|
||||
await expect(page.getByRole("button", { name: "Развернуть панель", exact: true })).toBeVisible();
|
||||
await expect.poll(async () => (await wrap.boundingBox())!.width).toBeGreaterThan(before + 150);
|
||||
await page.getByRole("link", { name: "Задачи" }).click();
|
||||
await expect(page).toHaveURL(/\/app\/issues\?project=AWPCA$/);
|
||||
await page.getByRole("button", { name: "Развернуть панель", exact: true }).click();
|
||||
await expect(page.getByRole("button", { name: "Свернуть панель" })).toBeVisible();
|
||||
});
|
||||
|
||||
test("390 px: «Меню» открывает панель, «Задачи» закрывает её и открывает список; без горизонтальной прокрутки", async ({ page }) => {
|
||||
await page.setViewportSize({ width: 390, height: 800 });
|
||||
await login(page);
|
||||
expect(await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth)).toBe(true);
|
||||
await page.getByRole("button", { name: "Меню" }).click();
|
||||
const side = page.getByRole("complementary", { name: "Панель приложения" });
|
||||
await expect(side).toBeVisible();
|
||||
await side.getByRole("link", { name: "Задачи" }).click();
|
||||
await expect(page).toHaveURL(/\/app\/issues\?project=AWPCA$/);
|
||||
await expect(side).toHaveCount(0);
|
||||
await expect(page.getByTestId("page-issues")).toBeVisible();
|
||||
expect(await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth)).toBe(true);
|
||||
});
|
||||
@ -80,12 +80,31 @@ for (const theme of ["light", "dark"] as const) {
|
||||
|
||||
test("new-project", async ({ page }) => {
|
||||
await login(page);
|
||||
if (w < 640) await page.getByRole("button", { name: "Меню" }).click(); // на узком экране проект — в выезжающей панели
|
||||
await page.getByLabel("Проект", { exact: true }).selectOption({ label: "+ Новый проект…" });
|
||||
await expect(page.getByRole("dialog", { name: "Новый проект" })).toBeVisible();
|
||||
await settle(page);
|
||||
await shot(page, "new-project");
|
||||
});
|
||||
|
||||
test("board-collapsed", async ({ page }) => {
|
||||
test.skip(w < 640, "свёртка — только на широком экране");
|
||||
await page.addInitScript(() => { localStorage.setItem("jira-hub-sidebar", "collapsed"); });
|
||||
await login(page);
|
||||
await expect(page.locator(".board .card[data-key]").first()).toBeVisible();
|
||||
await settle(page);
|
||||
await shot(page, "board-collapsed");
|
||||
});
|
||||
|
||||
test("menu", async ({ page }) => {
|
||||
test.skip(w >= 640, "выезжающая панель — только на узком экране");
|
||||
await login(page);
|
||||
await page.getByRole("button", { name: "Меню" }).click();
|
||||
await expect(page.getByRole("complementary", { name: "Панель приложения" })).toBeVisible();
|
||||
await settle(page);
|
||||
await page.screenshot({ path: `${OUT}/${theme}-${w}-menu.png` });
|
||||
});
|
||||
|
||||
test("refresh", async ({ page }) => {
|
||||
await login(page);
|
||||
await page.getByRole("button", { name: "Обновить из Jira" }).click();
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
.sidebar__head{display:flex;align-items:center;justify-content:space-between;gap:var(--space-2);min-height:var(--control-h)}
|
||||
.sidebar__brand{font-size:16px;line-height:24px;font-weight:700;color:var(--color-text-primary);white-space:nowrap;padding-left:var(--space-2)}
|
||||
.sidebar__project-tag{
|
||||
max-width:48px;padding:2px var(--space-1);border:1px solid var(--color-border-default);border-radius:var(--radius-xs);
|
||||
max-width:100%;padding:2px;border:1px solid var(--color-border-default);border-radius:var(--radius-xs);
|
||||
background:var(--color-bg-subtle);color:var(--color-text-secondary);font:inherit;font-size:var(--fs-caption);line-height:var(--lh-caption);
|
||||
font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:pointer;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user