Files
paseo/packages/app/e2e/bottom-sheet-reopen.spec.ts
T
Mohamed BoudraandGitHub 17aa957d3e Stabilize bottom-sheet reopen e2e by retrying the backdrop tap (#1238)
The "model selector can open, close, reopen, and close again" test fails on
loaded CI runners: a single backdrop tap that lands while the sheet is still
present-animating is dropped (Gorhom ignores backdrop presses until the sheet
settles at its snap point), so the sheet never dismisses and the backdrop stays
visible. The reopen behaviour itself is correct — the visibility tracker is
unit-tested and the spec passes locally (3x), so this is test-interaction
fragility, not a product regression.

Re-tap the backdrop until the sheet dismisses, mirroring the resilient close
loop already used in e2e/helpers/app.ts. It stays a pure backdrop dismissal (no
Escape/pan fallback) so the real close path is still exercised, and the
post-close "stays hidden" guard that protects the dismiss-then-re-present
regression is unchanged.
2026-05-30 22:59:02 +08:00

108 lines
3.8 KiB
TypeScript

import { expect, test, type Page } from "./fixtures";
import { expectComposerVisible } from "./helpers/composer";
import { openAgentRoute, seedMockAgentWorkspace } from "./helpers/mock-agent";
const MOBILE_VIEWPORT = { width: 390, height: 844 };
async function openMockAgentAtMobileBreakpoint(page: Page) {
await page.setViewportSize(MOBILE_VIEWPORT);
const session = await seedMockAgentWorkspace({
repoPrefix: "bottom-sheet-reopen-",
title: "Bottom sheet reopen e2e",
initialPrompt: "Prepare a bottom sheet reopen test agent.",
});
await openAgentRoute(page, session);
await expect(page.getByTestId("workspace-tab-switcher-trigger")).toBeVisible({
timeout: 30_000,
});
await expectComposerVisible(page);
await expect(page.getByRole("button", { name: /Select model/ })).toBeVisible({
timeout: 30_000,
});
return session;
}
async function withMobileMockAgent(page: Page, run: () => Promise<void>) {
const session = await openMockAgentAtMobileBreakpoint(page);
try {
await run();
} finally {
await session.cleanup();
}
}
function bottomSheetBackdrop(page: Page) {
return page.getByRole("button", { name: "Bottom sheet backdrop" }).first();
}
async function expectBottomSheetOpen(page: Page) {
await expect(bottomSheetBackdrop(page)).toBeVisible({ timeout: 10_000 });
}
async function closeBottomSheetWithBackdrop(page: Page) {
const backdrop = bottomSheetBackdrop(page);
// A single backdrop tap can be dropped when it lands mid present-animation:
// Gorhom ignores backdrop presses until the sheet settles at its snap point,
// which a loaded CI runner makes likely (the model selector's sheet animates a
// touch longer than the tab switcher's). Re-tap until the sheet dismisses. This
// stays a pure backdrop dismissal — no Escape/pan fallback — so it still
// exercises the real close path; the post-close guard below is what protects
// the regression this test exists for: a sheet that dismisses, then re-presents.
await expect(async () => {
if (await backdrop.isVisible()) {
const box = await backdrop.boundingBox();
expect(box).not.toBeNull();
await page.mouse.click(box!.x + box!.width / 2, box!.y + 24);
}
await expect(backdrop).not.toBeVisible({ timeout: 1_000 });
}).toPass({ timeout: 15_000 });
// Guard against the regression where the sheet starts dismissing, then re-presents.
await page.waitForTimeout(500);
await expect(backdrop).not.toBeVisible({ timeout: 1_000 });
}
async function openTabSwitcher(page: Page) {
const trigger = page.getByRole("button", { name: /Switch tabs/ });
await trigger.click();
await expectBottomSheetOpen(page);
}
async function openModelSelector(page: Page) {
await page.getByRole("button", { name: /Select model/ }).click();
await expectBottomSheetOpen(page);
await expect(
page.getByLabel("Bottom Sheet", { exact: true }).getByText("Ten second stream", {
exact: true,
}),
).toBeVisible({ timeout: 10_000 });
}
async function openAndCloseTabSwitcherTwice(page: Page) {
await openTabSwitcher(page);
await closeBottomSheetWithBackdrop(page);
await openTabSwitcher(page);
await closeBottomSheetWithBackdrop(page);
}
async function openAndCloseModelSelectorTwice(page: Page) {
await openModelSelector(page);
await closeBottomSheetWithBackdrop(page);
await openModelSelector(page);
await closeBottomSheetWithBackdrop(page);
}
test.describe("mobile bottom sheet reopen", () => {
test("tab switcher can open, close, reopen, and close again", async ({ page }) => {
await withMobileMockAgent(page, async () => {
await openAndCloseTabSwitcherTwice(page);
});
});
test("model selector can open, close, reopen, and close again", async ({ page }) => {
await withMobileMockAgent(page, async () => {
await openAndCloseModelSelectorTwice(page);
});
});
});