fix(web): запоздавшее scroll после ухода с доски не затирает запомненную прокрутку; e2e M-3 на реальном щелчке (M-3, #29)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
8d31fdf111
commit
d54596776e
@ -64,10 +64,16 @@ test("M-3: прокрутка доски после возврата из кар
|
|||||||
await expect(page.locator(".board .card[data-key]").first()).toBeVisible();
|
await expect(page.locator(".board .card[data-key]").first()).toBeVisible();
|
||||||
const y = await page.evaluate(() => { window.scrollTo(0, document.documentElement.scrollHeight); return window.scrollY; });
|
const y = await page.evaluate(() => { window.scrollTo(0, document.documentElement.scrollHeight); return window.scrollY; });
|
||||||
expect(y).toBeGreaterThan(100);
|
expect(y).toBeGreaterThan(100);
|
||||||
// событие прокрутки приходит на следующем кадре: без ожидания страница уходит раньше, чем доска запомнит позицию
|
// реальный щелчок по ⓘ карточки, видимой в окне после прокрутки (иначе Playwright сам сдвинет страницу)
|
||||||
await page.evaluate(() => new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r))));
|
const key = await page.evaluate(() => {
|
||||||
// щелчок без прокрутки к элементу: иначе Playwright сам сдвинет страницу перед переходом
|
const vh = window.innerHeight;
|
||||||
await page.evaluate(() => (document.querySelector(".board .card[data-key] a.open-card") as HTMLElement).click());
|
const a = [...document.querySelectorAll<HTMLElement>(".board .card[data-key] a.open-card")]
|
||||||
|
.find((e) => { const r = e.getBoundingClientRect(); return r.top > 200 && r.bottom < vh - 20; });
|
||||||
|
return a?.closest<HTMLElement>(".card")?.dataset.key ?? null;
|
||||||
|
});
|
||||||
|
expect(key).not.toBeNull();
|
||||||
|
const link = page.locator(`.board .card[data-key="${key}"] a.open-card`);
|
||||||
|
await link.click();
|
||||||
await expect(page).toHaveURL(/\/app\/issues\//);
|
await expect(page).toHaveURL(/\/app\/issues\//);
|
||||||
await page.getByRole("link", { name: "Доска", exact: true }).click();
|
await page.getByRole("link", { name: "Доска", exact: true }).click();
|
||||||
await expect.poll(() => page.evaluate(() => window.scrollY)).toBeGreaterThan(y - 2);
|
await expect.poll(() => page.evaluate(() => window.scrollY)).toBeGreaterThan(y - 2);
|
||||||
|
|||||||
@ -141,4 +141,22 @@ describe("BoardCanvas", () => {
|
|||||||
Object.defineProperty(window, "scrollY", { configurable: true, value: 0 });
|
Object.defineProperty(window, "scrollY", { configurable: true, value: 0 });
|
||||||
vi.restoreAllMocks();
|
vi.restoreAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("M-3: запоздавшее scroll после ухода (прокрутка зажата в 0) не затирает запомненное", () => {
|
||||||
|
vi.spyOn(window, "scrollTo").mockImplementation(() => {});
|
||||||
|
const handlers: EventListener[] = [];
|
||||||
|
const add = window.addEventListener.bind(window);
|
||||||
|
vi.spyOn(window, "addEventListener").mockImplementation((t: string, h: EventListenerOrEventListenerObject, o?: boolean | AddEventListenerOptions) => {
|
||||||
|
if (t === "scroll") handlers.push(h as EventListener);
|
||||||
|
add(t, h, o);
|
||||||
|
});
|
||||||
|
const r = renderWithProviders(<BoardCanvas {...baseProps()} />);
|
||||||
|
Object.defineProperty(window, "scrollY", { configurable: true, value: 640 });
|
||||||
|
fireEvent.scroll(window);
|
||||||
|
r.unmount();
|
||||||
|
Object.defineProperty(window, "scrollY", { configurable: true, value: 0 });
|
||||||
|
handlers.forEach((h) => h(new Event("scroll"))); // событие, дошедшее до слушателя уже после снятия доски
|
||||||
|
expect(readBoardScroll()).toMatchObject({ y: 640 });
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -146,7 +146,9 @@ export function BoardCanvas({ vis, byDate, snap, today, store, sprintSel, emptyT
|
|||||||
// под короткую страницу карточки
|
// под короткую страницу карточки
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const w = wrapRef.current;
|
const w = wrapRef.current;
|
||||||
const save = () => saveBoardScroll({ x: window.scrollX, y: window.scrollY, left: w?.scrollLeft ?? 0 });
|
// после ухода со страницы браузер зажимает прокрутку короткой страницы в 0 и шлёт scroll: доску уже убрали из DOM —
|
||||||
|
// такое событие не должно затирать запомненное
|
||||||
|
const save = () => { if (w && !w.isConnected) return; saveBoardScroll({ x: window.scrollX, y: window.scrollY, left: w?.scrollLeft ?? 0 }); };
|
||||||
window.addEventListener("scroll", save, { passive: true });
|
window.addEventListener("scroll", save, { passive: true });
|
||||||
w?.addEventListener("scroll", save, { passive: true });
|
w?.addEventListener("scroll", save, { passive: true });
|
||||||
return () => { window.removeEventListener("scroll", save); w?.removeEventListener("scroll", save); };
|
return () => { window.removeEventListener("scroll", save); w?.removeEventListener("scroll", save); };
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user