mirror of
https://github.com/BerriAI/litellm.git
synced 2026-08-28 05:25:59 +00:00
Relocates ui/litellm-dashboard/e2e_tests to tests/e2e/ui so all end to end suites live under tests/e2e. The suite stays in TypeScript and becomes a self-contained npm package with its own package.json, lockfile and tsconfig instead of leaning on the dashboard's toolchain; the dashboard drops its @playwright/test dependency, e2e scripts and knip/vitest/tsconfig carve-outs. CI paths follow the move: both CircleCI jobs (main e2e and the SERVER_ROOT_PATH migration smoke) and the test_server_root_path workflow now install and run Playwright from tests/e2e/ui, with the node cache keyed on both lockfiles. classify_changes.sh treats tests/e2e/ui as client so spec edits keep skipping backend jobs. The suite's mock LLM fixture is excluded from the e2e basedpyright zero-error gate in pyrightconfig.json since it belongs to the TS suite, not the typed Python harness.
51 lines
2.1 KiB
TypeScript
51 lines
2.1 KiB
TypeScript
import { Page } from "../fixtures/pages";
|
|
import { Page as PlaywrightPage, expect } from "@playwright/test";
|
|
|
|
/**
|
|
* 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(() => {});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 });
|
|
}
|