mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-09-14 16:08:41 +00:00
138 lines
5.0 KiB
TypeScript
138 lines
5.0 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
|
|
import { mockLangGraphAPI } from "./utils/mock-api";
|
|
|
|
test.describe("Sidebar navigation", () => {
|
|
test("sidebar contains Chats and Agents nav links", async ({ page }) => {
|
|
mockLangGraphAPI(page);
|
|
|
|
await page.goto("/workspace/chats/new");
|
|
|
|
// Sidebar uses data-sidebar="menu-button" with asChild rendering on <Link>
|
|
const sidebar = page.locator("[data-sidebar='sidebar']");
|
|
await expect(sidebar.locator("a[href='/workspace/chats']")).toBeVisible({
|
|
timeout: 15_000,
|
|
});
|
|
await expect(sidebar.locator("a[href='/workspace/agents']")).toBeVisible();
|
|
});
|
|
|
|
test("Agents link navigates to agents page", async ({ page }) => {
|
|
mockLangGraphAPI(page);
|
|
|
|
await page.goto("/workspace/chats/new");
|
|
|
|
const sidebar = page.locator("[data-sidebar='sidebar']");
|
|
const agentsLink = sidebar.locator("a[href='/workspace/agents']");
|
|
await expect(agentsLink).toBeVisible({ timeout: 15_000 });
|
|
await agentsLink.click();
|
|
|
|
await page.waitForURL("**/workspace/agents");
|
|
await expect(page).toHaveURL(/\/workspace\/agents/);
|
|
});
|
|
|
|
test("Agents button is disabled with a hover tooltip when agents_api is off", async ({
|
|
page,
|
|
}) => {
|
|
mockLangGraphAPI(page);
|
|
await page.route("**/api/features", (route) =>
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ agents_api: { enabled: false } }),
|
|
}),
|
|
);
|
|
|
|
await page.goto("/workspace/chats/new");
|
|
|
|
const sidebar = page.locator("[data-sidebar='sidebar']");
|
|
// Chats remains a real link; Agents is no longer a navigable link.
|
|
await expect(sidebar.locator("a[href='/workspace/chats']")).toBeVisible({
|
|
timeout: 15_000,
|
|
});
|
|
await expect(sidebar.locator("a[href='/workspace/agents']")).toHaveCount(0);
|
|
|
|
// The disabled Agents button is rendered and announces its disabled state.
|
|
const agentsButton = sidebar.getByRole("button", { name: "Agents" });
|
|
await expect(agentsButton).toHaveAttribute("aria-disabled", "true");
|
|
|
|
// The button itself has pointer-events suppressed; force the hover so the
|
|
// event reaches the wrapping tooltip-trigger span that surfaces the tooltip.
|
|
await agentsButton.hover({ force: true });
|
|
await expect(page.getByText("Feature not enabled").first()).toBeVisible({
|
|
timeout: 5_000,
|
|
});
|
|
|
|
// Keyboard/screen-reader users get the reason too: the disabled entry
|
|
// stays in the tab order (focusable) and is wired to a visually-hidden
|
|
// description rather than relying on the hover-only tooltip.
|
|
const describedById = await agentsButton.getAttribute("aria-describedby");
|
|
expect(describedById).toBeTruthy();
|
|
await expect(page.locator(`#${describedById}`)).toHaveText(
|
|
"Feature not enabled",
|
|
);
|
|
await agentsButton.focus();
|
|
await expect(agentsButton).toBeFocused();
|
|
});
|
|
|
|
test("mobile welcome layout stays within viewport and opens sidebar", async ({
|
|
page,
|
|
}) => {
|
|
await page.setViewportSize({ width: 390, height: 664 });
|
|
mockLangGraphAPI(page);
|
|
|
|
await page.goto("/workspace/chats/new");
|
|
await page.evaluate(() => {
|
|
document.cookie = "locale=zh-CN; path=/; SameSite=Lax";
|
|
});
|
|
await page.reload();
|
|
|
|
const viewportWidth = page.viewportSize()?.width ?? 390;
|
|
const expectInsideViewport = async (
|
|
locator: ReturnType<typeof page.locator>,
|
|
) => {
|
|
await expect(locator).toBeVisible({ timeout: 15_000 });
|
|
const box = await locator.boundingBox();
|
|
expect(box).not.toBeNull();
|
|
expect(box!.x).toBeGreaterThanOrEqual(-1);
|
|
expect(box!.x + box!.width).toBeLessThanOrEqual(viewportWidth + 1);
|
|
};
|
|
|
|
await expectInsideViewport(page.getByText(/欢迎使用 🦌 DeerFlow/).first());
|
|
await expectInsideViewport(page.getByRole("textbox").first());
|
|
await expectInsideViewport(page.locator("[data-slot='suggestions-list']"));
|
|
|
|
const mobileSidebarTrigger = page
|
|
.locator("[data-sidebar='trigger']:visible")
|
|
.first();
|
|
await expect(mobileSidebarTrigger).toBeVisible();
|
|
const triggerBox = await mobileSidebarTrigger.boundingBox();
|
|
expect(triggerBox).not.toBeNull();
|
|
const triggerReceivesPointerEvents = await page.evaluate(
|
|
({ x, y }) => {
|
|
const trigger = document.elementFromPoint(x, y);
|
|
return trigger?.closest("[data-sidebar='trigger']") !== null;
|
|
},
|
|
{
|
|
x: triggerBox!.x + triggerBox!.width / 2,
|
|
y: triggerBox!.y + triggerBox!.height / 2,
|
|
},
|
|
);
|
|
expect(triggerReceivesPointerEvents).toBe(true);
|
|
await page.mouse.click(
|
|
triggerBox!.x + triggerBox!.width / 2,
|
|
triggerBox!.y + triggerBox!.height / 2,
|
|
);
|
|
|
|
const mobileSidebar = page.locator(
|
|
"[data-mobile='true'][data-sidebar='sidebar']",
|
|
);
|
|
await expect(mobileSidebar).toBeVisible();
|
|
await expect(
|
|
mobileSidebar.locator("a[href='/workspace/chats']"),
|
|
).toBeVisible();
|
|
await expect(
|
|
mobileSidebar.locator("a[href='/workspace/agents']"),
|
|
).toBeVisible();
|
|
});
|
|
});
|