test(e2e): follow the Aria sidebar move and the Skills Hub rename in UI specs (#50453)

#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.
This commit is contained in:
yuneng-jiang 2026-11-06 14:59:01 -08:00 • committed by GitHub
parent e3387b6fc5
commit e555a79cee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 24 additions and 24 deletions

View file

@ -79,7 +79,7 @@ export const MIGRATED_E2E_PAGES: Readonly<Record<string, MigratedPage>> = {
group: "Tools",
content: { role: "heading", name: "Tool Policies" },
},
skills: { segment: "skills", linkName: "Skills", content: { role: "heading", name: "Skills" } },
skills: { segment: "skills", linkName: "Skills Hub", content: { role: "heading", name: "Skills Hub" } },
caching: { segment: "caching", linkName: "Response Cache", content: { role: "tab", name: "Cache Settings" } },
"cost-tracking": {
segment: "cost-tracking",

View file

@ -79,7 +79,7 @@ export async function setAriaHidden(page: PlaywrightPage, hidden: boolean): Prom
}
await page.keyboard.press("Escape");
await expect(panel).toBeHidden();
const aria = page.getByRole("banner").getByRole("button", { name: "Aria Agent", exact: true });
const aria = sidebarLink(page, "Aria Agent");
if (premium && hidden) {
await expect(aria).toBeHidden();
} else {

View file

@ -1,6 +1,12 @@
import { test, expect, type APIRequestContext, type Locator, type Page as PlaywrightPage } from "@playwright/test";
import { ADMIN_STORAGE_PATH } from "../../constants";
import { navigateToPage, dismissFeedbackPopup, setAriaHidden } from "../../helpers/navigation";
import {
navigateToPage,
dismissFeedbackPopup,
expectUiRoute,
setAriaHidden,
sidebarLink,
} from "../../helpers/navigation";
import { Page } from "../../fixtures/pages";
import {
CHAT_MODEL_A,
@ -51,12 +57,18 @@ async function setRowsPerPage(page: PlaywrightPage, size: "25" | "50" | "100"):
await page.getByRole("option", { name: size, exact: true }).click();
}
test.describe("Logs pagination with Aria in the header", () => {
async function openAriaFromSidebar(page: PlaywrightPage): Promise<void> {
await sidebarLink(page, "Aria Agent").click();
await expectUiRoute(page, "aria");
await expect(page.getByRole("region", { name: "Aria Agent", exact: true })).toBeVisible();
}
test.describe("Logs pagination with Aria in the sidebar", () => {
test.use({ storageState: ADMIN_STORAGE_PATH });
test("keeps pagination reachable and Aria available at 1440px", async ({ page, request }) => {
await page.setViewportSize({ width: 1440, height: 900 });
const alias = `e2e-aria-header-${uniqueSuffix()}`;
const alias = `e2e-aria-sidebar-${uniqueSuffix()}`;
const key = await createVirtualKey(request, { key_alias: alias });
try {
const ids = await Promise.all(
@ -76,20 +88,13 @@ test.describe("Logs pagination with Aria in the header", () => {
await expect(visibleTestId(page, "pagination-range")).toHaveText("Showing 1-25 of 26");
await visibleTestId(page, "pagination-next").click();
await expect(visibleTestId(page, "pagination-range")).toHaveText("Showing 26-26 of 26");
const aria = page.getByRole("banner").getByRole("button", { name: "Aria Agent", exact: true });
await aria.click();
await expect(page.getByRole("dialog", { name: "Aria Agent", exact: true })).toBeVisible();
await page.keyboard.press("Escape");
await expect(aria).toBeFocused();
await visibleTestId(page, "pagination-first").click();
await expect(visibleTestId(page, "pagination-range")).toHaveText("Showing 1-25 of 26");
await openAriaFromSidebar(page);
for (const destination of [Page.NewUsage, Page.LlmPlayground, Page.ApiKeys]) {
await navigateToPage(page, destination);
await aria.click();
await expect(page.getByRole("dialog", { name: "Aria Agent", exact: true })).toBeVisible();
await page.keyboard.press("Escape");
await expect(aria).toBeFocused();
await openAriaFromSidebar(page);
}
} finally {
await deleteVirtualKey(request, key.token);

View file

@ -39,21 +39,16 @@ test.describe("AI Hub (internal admin view)", () => {
});
});
test("AI Hub tab list renders Model Hub, Agent Hub, MCP Hub and Skill Hub", async ({ page }) => {
test("AI Hub tab list renders Model Hub, Agent Hub, MCP Hub and Skills Hub", async ({ page }) => {
await navigateToPage(page, Page.ModelHubTable);
// The tab strip lives in the main view; check each tab is present and clickable.
// (The "Claude Code Plugin Marketplace" tab from the manual-QA checklist was
// renamed to "Skill Hub" — verify the current label here so the test stays
// in sync with the UI.)
//
// Note: unlike the public /ui/model_hub_table view (test below), the admin
// ModelHubTable renders all four tabs unconditionally — there are no `&&`
// guards around <Tab>Agent Hub</Tab> or <Tab>MCP Hub</Tab> in the source
// (ModelHubTable.tsx ~L436-439). Asserting all four here is intentional:
// this pins the manual-QA contract that the AI Hub tab strip exposes
// exactly these labels regardless of seeded agent/MCP data.
for (const tabName of ["Model Hub", "Agent Hub", "MCP Hub", "Skill Hub"]) {
for (const tabName of ["Model Hub", "Agent Hub", "MCP Hub", "Skills Hub"]) {
const tab = page.getByRole("tab", { name: tabName });
await expect(tab, `${tabName} tab should be present`).toBeVisible({ timeout: 5_000 });
await tab.click();
@ -75,7 +70,7 @@ test.describe("Public model hub (/ui/model_hub_table)", () => {
await expect(page.getByRole("tab")).toHaveCount(2);
await page.getByRole("tab", { name: "Skills Hub", exact: true }).click();
await expect(
page.getByRole("textbox", { name: "Search skills", exact: true }),
page.getByRole("textbox", { name: "Search skills, tags, or people…", exact: true }),
).toBeVisible();
});
@ -105,7 +100,7 @@ test.describe("Public model hub (/ui/model_hub_table)", () => {
await dismissFeedbackPopup(page);
await page.getByRole("checkbox", { name: domain, exact: true }).check();
await page
.getByRole("textbox", { name: "Search skills", exact: true })
.getByRole("textbox", { name: "Search skills, tags, or people…", exact: true })
.fill(skillName);
await page.getByRole("link").filter({ hasText: skillName }).click();