Files
Snow Lee (Sungwon)andGitHub 6bb7debe2b fix(webapp): #content shows it scrolls, Public links moves up (BEA-79) (#128)
Project settings at 1440x900 ended at People. Public links — the section
that answers "is anything of ours public right now?" — was below the fold
of #content, the app's only scroll container, and with macOS overlay
scrollbars nothing at rest said there was more.

#content now takes a real scrollbar. The plan called for scrollbar-width
+ scrollbar-color alongside ::-webkit-scrollbar; measured, that combination
is a no-op: Chromium drops every ::-webkit-scrollbar rule as soon as either
standard property is set on the same element, and the standard properties
alone leave the macOS overlay bar, which takes no width and is invisible at
rest. So the standard properties sit behind @supports not
selector(::-webkit-scrollbar) — Firefox only. Verified: 10px reserved in
Chromium, 0 on touch.

Public links also moves from fourth card to second (General → Public links
→ People → About → Danger zone), so the security answer is above the fold
even without the cue.

browse.spec reached the shares table by .last(), which the reorder would
have silently repointed at the members table — both render through
AdminTable. It selects .shares-table now.

Known gap: Firefox on macOS always uses overlay scrollbars and no CSS
overrides it, so the bar there stays hidden at rest — that is an OS
setting, not something this can fix.
2026-08-11 01:36:19 +09:00

49 lines
2.0 KiB
TypeScript

import { Page } from "@playwright/test";
export const ADMIN = "e2e@example.com";
export const MEMBER = "member@example.com";
// Org member cut back to read-only on "wiki" by the seeded harness.
export const READER = "reader@example.com";
export const PASSWORD = "e2e-pass-1";
// One real form login per identity per run, then the session cookie is
// reused — the server rate-limits credential POSTs to 10/min per IP, which
// a fresh login in every spec would trip.
type Cookies = Awaited<ReturnType<ReturnType<Page["context"]>["cookies"]>>;
const sessions = new Map<string, Cookies>();
// Signs in (through the server-rendered /auth pages on first use) and waits
// for the SPA shell to render.
export async function login(page: Page, email: string = ADMIN) {
const cached = sessions.get(email);
if (cached) {
await page.context().addCookies(cached);
await page.goto("/");
await page.waitForSelector("#sidebar");
return;
}
await page.goto("/");
await page.waitForURL(/auth\/login/);
await page.fill('input[name="email"]', email);
await page.fill('input[name="password"]', PASSWORD);
await page.click("button[type=submit]");
await page.waitForSelector("#sidebar");
sessions.set(email, await page.context().cookies());
}
// Note for anything that scrolls or screenshots a route: the document itself
// never scrolls — #content is the one scroll container. A full-page screenshot
// stops at the viewport and window.scrollTo does nothing; scroll #content.
export async function wikiId(page: Page): Promise<string> {
const out = await (await page.request.get("/api/projects")).json();
return out.projects.find((p: { name: string }) => p.name === "wiki").id;
}
// Implementation-agnostic toast assertion: matches the legacy #toast div or
// a sonner toast, so specs pin BEHAVIOR (text shown) not markup.
export async function expectToast(page: Page, text: string | RegExp) {
const { expect } = await import("@playwright/test");
await expect(page.locator("#toast.show, [data-sonner-toast]").filter({ hasText: text }).first()).toBeVisible();
}