litellm/tests/e2e/ui/helpers/navigation.ts
moe-berri dbd55d3202
fix(ui): keep Aria accessible in the dashboard header (#48454)
* fix(ui): keep Aria accessible in the dashboard header

* test(ui): wait for every pagination fixture log

* test(ui): update assistant setup for Aria branding
2026-10-26 14:55:08 -07:00

110 lines
4.7 KiB
TypeScript

import { Page } from "../fixtures/pages";
import { Page as PlaywrightPage, expect } from "@playwright/test";
import { UI_BASE_URL } from "../constants";
export const sidebarLink = (page: PlaywrightPage, name: string | RegExp) =>
page.getByRole("complementary").getByRole("link", { name, exact: true });
export async function clickSidebarLink(page: PlaywrightPage, name: string | RegExp, groupName?: string): Promise<void> {
const link = sidebarLink(page, name);
if (groupName && !(await link.isVisible())) {
const group = page.getByRole("complementary").getByRole("button", { name: groupName, exact: true });
await expect(group).toBeVisible();
if ((await group.getAttribute("aria-expanded")) === "false") {
await group.click();
}
}
await link.click();
}
export async function expectUiRoute(page: PlaywrightPage, segment: string): Promise<void> {
const root = (process.env.SERVER_ROOT_PATH ?? "").replace(/\/+$/, "");
const expected = new URL(`${root}/ui/${segment}`, UI_BASE_URL);
await expect(page, `navigate to ${expected.pathname}`).toHaveURL(
(url) => url.origin === expected.origin && url.pathname.replace(/\/+$/, "") === expected.pathname,
);
}
/**
* Navigates to a specific page using the page query parameter.
* Waits for the sidebar to be visible before returning.
*/
export async function navigateToPage(page: PlaywrightPage, pageEnum: Page): Promise<void> {
// A fresh deep-link can race the auth bootstrap: the app briefly treats the
// session as anonymous, bounces through /ui/login, and lands back on the
// default page with the ?page= param dropped. Re-issue the navigation until
// the requested page sticks (auth is warm by the second load) so callers never
// assert against the default page.
for (let attempt = 0; attempt < 3; attempt++) {
await page.goto(`/ui?page=${pageEnum}`);
await page.waitForLoadState("networkidle");
const url = new URL(page.url());
const onLegacyRoot = url.pathname.replace(/\/+$/, "").endsWith("/ui");
if (!onLegacyRoot || url.searchParams.get("page") === pageEnum) {
break;
}
}
// Dismiss the "Quick feedback" popup if it appears
await dismissFeedbackPopup(page);
}
/**
* Dismiss the "Quick feedback" popup that may appear on any page.
*/
export async function dismissFeedbackPopup(page: PlaywrightPage): Promise<void> {
const dismissButton = page.getByText("Don't ask me again");
if (await dismissButton.isVisible({ timeout: 1_500 }).catch(() => false)) {
await dismissButton.click();
// Wait for the popup to disappear
await expect(dismissButton)
.not.toBeVisible({ timeout: 2_000 })
.catch(() => {});
}
}
export async function setAriaHidden(page: PlaywrightPage, hidden: boolean): Promise<void> {
await page.getByRole("button", { name: /Account menu/i }).click();
const panel = page.getByTestId("sidebar-account-menu-panel");
await expect(panel).toBeVisible({ timeout: 5_000 });
const premium = await panel.getByText("Premium", { exact: true }).isVisible();
const toggle = panel.getByRole("switch", { name: "Toggle hide Aria Agent", exact: true });
if (premium) {
await expect(toggle).toBeVisible();
if ((await toggle.getAttribute("aria-checked")) !== String(hidden)) {
await toggle.click();
}
await expect(toggle).toHaveAttribute("aria-checked", String(hidden));
} else {
await expect(toggle).toHaveCount(0);
}
await page.keyboard.press("Escape");
await expect(panel).toBeHidden();
const aria = page.getByRole("banner").getByRole("button", { name: "Aria Agent", exact: true });
if (premium && hidden) {
await expect(aria).toBeHidden();
} else {
await expect(aria).toBeVisible();
}
}
/**
* Click on a team ID in the table. Team IDs are rendered differently depending
* on the component version — try button first (Tremor Button), fall back to
* clickable span (Teams Typography.Text).
*/
export async function clickTeamId(page: PlaywrightPage, teamId: string): Promise<void> {
const cell = page.locator("td").filter({ hasText: teamId }).first();
await expect(cell).toBeVisible({ timeout: 10_000 });
await cell.click();
await expect(page.getByText("Back to Teams")).toBeVisible({ timeout: 10_000 });
}
export async function openKeyDetail(page: PlaywrightPage, alias: string): Promise<void> {
await page.getByPlaceholder("Search by key alias or ID").fill(alias);
const row = page.getByRole("row").filter({ hasText: alias });
await expect(row, `key row "${alias}" never appeared on the Virtual Keys page`).toBeVisible({ timeout: 15_000 });
await row.getByRole("button", { name: alias }).click();
await expect(page.getByText("Back to Keys"), `key detail for "${alias}" never opened`).toBeVisible({
timeout: 15_000,
});
}