Compare commits

...
Author SHA1 Message Date
paseo-bot 130cbc961f test(app): show the slash autocomplete popover losing its anchor
Both tests fail on main. With the popover open, toggling the desktop sidebar
moves the composer, and the popover keeps the offsets measured before the
toggle: it lands 160px away from the composer, and when the sidebar expands it
is pushed 90px past the right edge of a 1280px viewport.

Evidence for #4872.
2026-09-22 18:00:07 +02:00
@@ -0,0 +1,140 @@
import { expect, test, type Page } from "../support/fixtures";
import { composerLocator, expectComposerVisible } from "../support/helpers/composer";
import { openAgentRoute, seedMockAgentWorkspace } from "../support/helpers/mock-agent";
import { daemonWsRoutePattern } from "../support/helpers/daemon-port";
const TEST_COMMANDS = [
{ name: "help", description: "Show help", argumentHint: "" },
{ name: "tdd", description: "Red test first", argumentHint: "" },
{ name: "hello", description: "Greeting", argumentHint: "" },
] as const;
async function installListCommandsStub(page: Page): Promise<void> {
await page.routeWebSocket(daemonWsRoutePattern(), (ws) => {
const server = ws.connectToServer();
ws.onMessage((message) => server.send(message));
server.onMessage((message) => {
if (typeof message !== "string") {
ws.send(message);
return;
}
try {
const parsed = JSON.parse(message) as {
type?: string;
message?: { type?: string; payload?: { commands?: unknown; error?: string | null } };
};
if (
parsed.type === "session" &&
parsed.message?.type === "list_commands_response" &&
parsed.message.payload
) {
parsed.message.payload.commands = TEST_COMMANDS;
parsed.message.payload.error = null;
ws.send(JSON.stringify(parsed));
return;
}
} catch {
// forward
}
ws.send(message);
});
});
}
async function boxes(page: Page) {
const popover = await page.evaluate(() => {
const element = document.querySelector('[data-testid="composer-autocomplete-popover"]');
if (!(element instanceof HTMLElement)) return null;
const rect = element.getBoundingClientRect();
return {
x: Math.round(rect.x),
y: Math.round(rect.y),
width: Math.round(rect.width),
height: Math.round(rect.height),
};
});
const composerBox = await page.getByTestId("message-input-root").boundingBox();
return {
popover,
composer: composerBox
? {
x: Math.round(composerBox.x),
y: Math.round(composerBox.y),
width: Math.round(composerBox.width),
height: Math.round(composerBox.height),
}
: null,
};
}
// Failing evidence for https://github.com/getpaseo/paseo/issues/4872: the slash
// autocomplete popover measures its anchor once per slash session, so a layout change
// under an open popover leaves it anchored to where the composer used to be.
test.describe("autocomplete popover anchoring", () => {
test("follows the composer when the sidebar closes while the popover is open", async ({
page,
}) => {
await installListCommandsStub(page);
const session = await seedMockAgentWorkspace({
repoPrefix: "repro-4872-anchor-a-",
title: "Repro 4872 anchor A",
});
try {
await openAgentRoute(page, session);
await expectComposerVisible(page, { timeout: 30_000 });
const input = composerLocator(page);
await expect(input).toBeEditable({ timeout: 30_000 });
await input.click();
await page.keyboard.type("/");
await expect(page.getByText("/help", { exact: true })).toBeVisible({ timeout: 30_000 });
console.log("A BEFORE", JSON.stringify(await boxes(page)));
await page.getByTestId("menu-button").first().click();
await page.waitForTimeout(1200);
const after = await boxes(page);
console.log("A AFTER", JSON.stringify(after));
expect(
Math.abs((after.popover?.x ?? 0) - (after.composer?.x ?? 0)),
"popover stays anchored to the composer",
).toBeLessThanOrEqual(4);
} finally {
await session.cleanup();
}
});
test("stays on screen when the sidebar opens while the popover is open", async ({ page }) => {
await installListCommandsStub(page);
const session = await seedMockAgentWorkspace({
repoPrefix: "repro-4872-anchor-b-",
title: "Repro 4872 anchor B",
});
try {
await openAgentRoute(page, session);
await expectComposerVisible(page, { timeout: 30_000 });
// Collapse the sidebar first, so the toggle under test expands it.
await page.getByTestId("menu-button").first().click();
await page.waitForTimeout(1200);
const input = composerLocator(page);
await expect(input).toBeEditable({ timeout: 30_000 });
await input.click();
await page.keyboard.type("/");
await expect(page.getByText("/help", { exact: true })).toBeVisible({ timeout: 30_000 });
console.log("B BEFORE", JSON.stringify(await boxes(page)));
await page.getByTestId("menu-button").first().click();
await page.waitForTimeout(1200);
const after = await boxes(page);
console.log("B AFTER", JSON.stringify(after), "viewport", JSON.stringify(page.viewportSize()));
const popover = page.getByTestId("composer-autocomplete-popover").first();
await expect(popover).toBeInViewport({ ratio: 0.9, timeout: 5_000 });
} finally {
await session.cleanup();
}
});
});