f99010fae1
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Windows) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Has been cancelled
155 lines
4.0 KiB
TypeScript
155 lines
4.0 KiB
TypeScript
import { test, expect } from "@playwright/test";
|
|
import {
|
|
createMockSessions,
|
|
handleSessionsRoute,
|
|
sessionsRoutePattern,
|
|
} from "./helpers/mock-sessions";
|
|
import {
|
|
ITEM_HEIGHT,
|
|
} from "../src/lib/components/sidebar/session-list-utils";
|
|
import {
|
|
getScrollTop,
|
|
scrollListTo,
|
|
waitForScrollHeight,
|
|
} from "./helpers/virtual-list-helpers";
|
|
import { SessionsPage } from "./pages/sessions-page";
|
|
|
|
const TOTAL_SESSIONS = 500;
|
|
const DEEP_SESSIONS = 2000;
|
|
const MIDDLE_INDEX = Math.floor(DEEP_SESSIONS / 2);
|
|
const LAST_INDEX = DEEP_SESSIONS - 1;
|
|
const DEEP_SCROLL_HEIGHT = DEEP_SESSIONS * ITEM_HEIGHT;
|
|
|
|
/** Expected header text after all deep sessions load (en-US). */
|
|
const DEEP_COUNT_TEXT = `${DEEP_SESSIONS.toLocaleString("en-US")} sessions`;
|
|
|
|
const sessions = createMockSessions(
|
|
TOTAL_SESSIONS,
|
|
"session",
|
|
(i) => (i % 2 === 0 ? "project-alpha" : "project-beta"),
|
|
);
|
|
|
|
const deepSessions = createMockSessions(
|
|
DEEP_SESSIONS,
|
|
"deep-session",
|
|
() => "deep",
|
|
);
|
|
|
|
const tinySessions = [sessions[0]];
|
|
|
|
test.describe("Virtual list behavior", () => {
|
|
let sp: SessionsPage;
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.route(
|
|
sessionsRoutePattern,
|
|
handleSessionsRoute([
|
|
{ sessions, project: null },
|
|
{ sessions: deepSessions, project: "deep" },
|
|
{ sessions: tinySessions, project: "tiny" },
|
|
]),
|
|
);
|
|
|
|
await page.route("**/api/v1/projects*", async (route) => {
|
|
await route.fulfill({
|
|
json: {
|
|
projects: [
|
|
{ name: "project-alpha", session_count: 250 },
|
|
{ name: "project-beta", session_count: 250 },
|
|
{ name: "tiny", session_count: 1 },
|
|
{ name: "deep", session_count: DEEP_SESSIONS },
|
|
],
|
|
},
|
|
});
|
|
});
|
|
|
|
sp = new SessionsPage(page);
|
|
await page.goto("/");
|
|
await expect(sp.sessionItems.first()).toBeVisible();
|
|
});
|
|
|
|
test("renders end of list when scrolling down", async () => {
|
|
await scrollListTo(sp.sessionListScroll, "bottom");
|
|
|
|
await expect(
|
|
sp.page.getByText(
|
|
`Hello from session ${TOTAL_SESSIONS - 1}`,
|
|
),
|
|
).toBeVisible();
|
|
});
|
|
|
|
test("clamps scroll position when filtering", async () => {
|
|
await scrollListTo(sp.sessionListScroll, 2000);
|
|
|
|
await expect
|
|
.poll(() => getScrollTop(sp.sessionListScroll))
|
|
.toBeGreaterThan(0);
|
|
|
|
await sp.filterByProject("tiny");
|
|
|
|
// Wait for filtered results to render before checking
|
|
// scroll position — on CI the re-render can be slow.
|
|
await expect(sp.sessionCount).toHaveText("1 session", {
|
|
timeout: 5_000,
|
|
});
|
|
|
|
await expect
|
|
.poll(() => getScrollTop(sp.sessionListScroll), {
|
|
timeout: 5_000,
|
|
})
|
|
.toBe(0);
|
|
});
|
|
|
|
test("scrolls to an unloaded middle range", async () => {
|
|
await sp.filterByProject("deep");
|
|
|
|
// Wait for all pages to load so scrollHeight reflects the
|
|
// full dataset. The virtual list sizes itself from
|
|
// sessions.length, so scrolling before loading completes
|
|
// would land at the wrong position.
|
|
await expect(sp.sessionListHeader).toContainText(
|
|
DEEP_COUNT_TEXT,
|
|
{ timeout: 15_000 },
|
|
);
|
|
await waitForScrollHeight(
|
|
sp.sessionListScroll,
|
|
DEEP_SCROLL_HEIGHT,
|
|
);
|
|
|
|
await scrollListTo(sp.sessionListScroll, "middle");
|
|
|
|
await expect(
|
|
sp.page.getByRole("button", {
|
|
name: new RegExp(
|
|
`Hello from deep-session ${MIDDLE_INDEX}`,
|
|
"i",
|
|
),
|
|
}),
|
|
).toBeVisible({ timeout: 10_000 });
|
|
});
|
|
|
|
test("scrolls to the end of a large list", async () => {
|
|
await sp.filterByProject("deep");
|
|
|
|
await expect(sp.sessionListHeader).toContainText(
|
|
DEEP_COUNT_TEXT,
|
|
{ timeout: 15_000 },
|
|
);
|
|
await waitForScrollHeight(
|
|
sp.sessionListScroll,
|
|
DEEP_SCROLL_HEIGHT,
|
|
);
|
|
|
|
await scrollListTo(sp.sessionListScroll, "bottom");
|
|
|
|
await expect(
|
|
sp.page.getByRole("button", {
|
|
name: new RegExp(
|
|
`Hello from deep-session ${LAST_INDEX}`,
|
|
"i",
|
|
),
|
|
}),
|
|
).toBeVisible({ timeout: 10_000 });
|
|
});
|
|
});
|