test(ui): e2e cover team model edit + admin identity in navbar (#28652)

* test(ui): e2e cover team model edit + admin identity in navbar

Adds two Playwright tests as part of the manual-QA → e2e migration:
"Edit team model selection" exercises the Settings tab Models multi-select
+ Save Changes flow on a seeded team, and the existing login test now
opens the User dropdown and asserts the role and User ID render — guarding
against regressions where login succeeds but the auth context is empty.

Resolves LIT-3093

* test(ui): restore seeded models in team-edit test so retries don't fail

The 'Edit team model selection' test removed fake-anthropic-claude from
E2E_TEAM_CRUD_ID without restoring it. CI runs with retries: 2 and the seed
script runs once before the suite, so a flake on this test would fail the
retry at the "tag is visible" assertion. Wrap the test in try/finally and
restore the seeded models via /team/update before and after.

* test(e2e): fail loudly if team/update restore call fails

Surfaces the real cause when the master key is wrong or the proxy is
unreachable, instead of silently leaving the team in a stale state and
failing later on the visibility assertion.

* fix(e2e): match navbar account button by aria-label, not non-existent "User" text

The previous trigger filter (hasText: /^User$/) didn't match the rendered
UserDropdown button — its text is the displayName ("Account" for the
master-key admin, an email for SSO users), never "User". The evaluate
call then timed out after 15s in CI. Use the stable aria-label prefix
the component always emits, and click directly since the dropdown is
configured trigger=["click"] (the synthetic hover was unnecessary).
This commit is contained in:
ryan-crabbe-berri
2026-05-27 14:45:22 -07:00
committed by GitHub
parent ea015332d8
commit 7cae5dc08a
2 changed files with 62 additions and 0 deletions
@@ -10,4 +10,21 @@ test("user can log in", async ({ page }) => {
await expect(loginButton).toBeEnabled();
await loginButton.click();
await expect(page.getByText("Virtual Keys")).toBeVisible();
// Match the navbar account button by its stable aria-label (UserDropdown.tsx
// emits "Account menu — <role> — signed in as <email|id>"). Earlier this used
// `hasText: /^User$/`, which never matched the rendered button (text is
// displayName = "Account" for the master-key admin), so the trigger evaluate
// would time out in CI.
const userTrigger = page.locator('button[aria-label^="Account menu"]').first();
await userTrigger.click();
// Filter by the popupRender wrapper class to disambiguate from other
// ant-dropdown popups.
const popup = page.locator(".ant-dropdown:visible").filter({
has: page.locator(".bg-white.rounded-lg.shadow-lg"),
}).first();
await expect(popup).toBeVisible({ timeout: 5_000 });
await expect(popup.getByText("Admin", { exact: true })).toBeVisible({ timeout: 5_000 });
await expect(popup.getByText("default_user_id", { exact: true })).toBeVisible({ timeout: 5_000 });
});
@@ -131,4 +131,49 @@ test.describe("Proxy Admin - Teams", () => {
await expect(page.getByText(/updated|success/i).first()).toBeVisible({ timeout: 10_000 });
});
test("Edit team model selection", async ({ page, request }) => {
// Restore the seeded models via API in case a prior run (or a CI retry)
// left this team mutated — the assertion below requires fake-anthropic-claude
// to be present.
const masterKey = process.env.LITELLM_MASTER_KEY || "sk-1234";
const seededModels = ["fake-openai-gpt-4", "fake-anthropic-claude"];
const restore = async () => {
const res = await request.post("http://localhost:4000/team/update", {
headers: { Authorization: `Bearer ${masterKey}` },
data: { team_id: E2E_TEAM_CRUD_ID, models: seededModels },
});
expect(res.ok(), `restore failed: ${res.status()} ${await res.text()}`).toBeTruthy();
};
await restore();
try {
await navigateToPage(page, Page.Teams);
await dismissFeedbackPopup(page);
await clickTeamId(page, E2E_TEAM_CRUD_ID);
await page.getByRole("tab", { name: "Settings" }).click();
await page.getByRole("button", { name: "Edit Settings" }).click();
// Remove the anthropic tag — other tests against this team use "All Team
// Models" so they pick up whatever remains.
const modelsSelect = page.locator("[data-testid='models-select']");
await expect(modelsSelect).toBeVisible({ timeout: 10_000 });
const anthropicTag = modelsSelect
.locator(".ant-select-selection-item")
.filter({ hasText: "fake-anthropic-claude" });
await expect(anthropicTag).toBeVisible({ timeout: 5_000 });
await anthropicTag.locator(".ant-select-selection-item-remove").click();
await page.getByRole("button", { name: "Save Changes" }).click();
await expect(page.getByText(/Team settings updated|updated successfully/i).first())
.toBeVisible({ timeout: 10_000 });
} finally {
// Leave the team in its seeded state for any subsequent test or rerun.
await restore();
}
});
});