mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-13 08:52:02 +00:00
#50009 moved Aria out of the dashboard header into the sidebar with its own page, and #50195 renamed the Skills nav link and AI Hub tab to Skills Hub and relabeled the skills search box. The UI e2e specs still looked for the old header button, dialog and labels, so the scheduled litellm-e2e run has been red since build 815.
110 lines
4.7 KiB
TypeScript
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 expect(page.getByRole("complementary")).toBeVisible();
|
|
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 = sidebarLink(page, "Aria Agent");
|
|
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,
|
|
});
|
|
}
|