From 2d48048d6991a0cf1063af831806f753878a3b41 Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Wed, 20 May 2026 09:24:30 -0700 Subject: [PATCH 1/2] test(ui-e2e): add admin key creation with a specific proxy model Adds Playwright coverage for creating a key (no team) scoped to a single proxy model, complementing the existing All-Proxy-Models test. Uses a DOM-dispatched click on the antd dropdown option since the popup animation can render the option outside the viewport. --- .../e2e_tests/tests/proxy-admin/keys.spec.ts | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/ui/litellm-dashboard/e2e_tests/tests/proxy-admin/keys.spec.ts b/ui/litellm-dashboard/e2e_tests/tests/proxy-admin/keys.spec.ts index 14ceb1a4a6bb..83906c8c0bf4 100644 --- a/ui/litellm-dashboard/e2e_tests/tests/proxy-admin/keys.spec.ts +++ b/ui/litellm-dashboard/e2e_tests/tests/proxy-admin/keys.spec.ts @@ -126,4 +126,36 @@ test.describe("Proxy Admin - Keys", () => { await expect(page.getByText(E2E_INTERNAL_USER_KEY_ALIAS)).toBeVisible({ timeout: 10_000 }); }); + + test("Create a key with a specific proxy model (no team)", async ({ page }) => { + await navigateToPage(page, Page.ApiKeys); + await dismissFeedbackPopup(page); + + await page.getByRole("button", { name: /Create New Key/i }).click(); + + await expect(page.getByText("Key Ownership")).toBeVisible({ timeout: 10_000 }); + + const keyName = `e2e-admin-specific-${Date.now()}`; + await page.getByTestId("base-input").fill(keyName); + + // Open the model multi-select and pick a single specific model. Use + // getByRole("option", ...) to avoid the strict-mode collision between + // the option container and its inner text node. + const modelName = "fake-openai-gpt-4"; + await page.locator(".ant-select-selection-overflow").click(); + const option = page.locator(".ant-select-dropdown:visible").getByRole("option", { name: modelName, exact: true }); + await option.waitFor({ state: "attached" }); + // Dispatch the click via the DOM — antd's dropdown can render the option + // off-viewport during the open animation, which trips Playwright's + // visibility/stability checks. The click handler fires regardless. + await option.evaluate((el: HTMLElement) => el.click()); + await page.keyboard.press("Escape"); + + await page.getByRole("button", { name: "Create Key", exact: true }).click(); + + await expect(page.getByText("Save your Key")).toBeVisible({ timeout: 10_000 }); + await page.keyboard.press("Escape"); + + await expect(page.getByText(keyName)).toBeVisible({ timeout: 10_000 }); + }); }); From 26b3c2a54bd51337f45902d1ddea8c426198bd05 Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Wed, 20 May 2026 11:23:21 -0700 Subject: [PATCH 2/2] test(ui-e2e): verify scoped key works against mock /chat/completions Extend the "Create a key with a specific proxy model" test to extract the new key from the success modal and POST to /chat/completions for the scoped model, asserting 200 and the mock response body. Without this the test could pass even if the model selection failed to register. --- .../e2e_tests/tests/proxy-admin/keys.spec.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/ui/litellm-dashboard/e2e_tests/tests/proxy-admin/keys.spec.ts b/ui/litellm-dashboard/e2e_tests/tests/proxy-admin/keys.spec.ts index 83906c8c0bf4..41a944e8c9ae 100644 --- a/ui/litellm-dashboard/e2e_tests/tests/proxy-admin/keys.spec.ts +++ b/ui/litellm-dashboard/e2e_tests/tests/proxy-admin/keys.spec.ts @@ -154,6 +154,25 @@ test.describe("Proxy Admin - Keys", () => { await page.getByRole("button", { name: "Create Key", exact: true }).click(); await expect(page.getByText("Save your Key")).toBeVisible({ timeout: 10_000 }); + + // Grab the new key from the success modal (rendered inside a
) and
+    // verify it can call /chat/completions for the model it was scoped to.
+    // The mock LLM server (fixtures/mock_llm_server/server.py) replies with
+    // a fixed "This is a mock response." body.
+    const apiKey = (await page.locator(".ant-modal:visible pre").innerText()).trim();
+    expect(apiKey).toMatch(/^sk-/);
+
+    const response = await page.request.post("/chat/completions", {
+      headers: { Authorization: `Bearer ${apiKey}` },
+      data: {
+        model: modelName,
+        messages: [{ role: "user", content: "ping" }],
+      },
+    });
+    expect(response.status()).toBe(200);
+    const body = await response.json();
+    expect(body.choices?.[0]?.message?.content).toBe("This is a mock response.");
+
     await page.keyboard.press("Escape");
 
     await expect(page.getByText(keyName)).toBeVisible({ timeout: 10_000 });