diff --git a/ui/litellm-dashboard/e2e_tests/playwright.config.ts b/ui/litellm-dashboard/e2e_tests/playwright.config.ts index ec4d3a6ddb0f..6964fe52a142 100644 --- a/ui/litellm-dashboard/e2e_tests/playwright.config.ts +++ b/ui/litellm-dashboard/e2e_tests/playwright.config.ts @@ -28,6 +28,11 @@ export default defineConfig({ /* Action timeout for clicks, fills, waitForSelector, etc. */ actionTimeout: 15 * 1000, navigationTimeout: 30 * 1000, + + /* Slow down actions when SLOWMO= is set, useful for headed local debugging */ + launchOptions: { + slowMo: process.env.SLOWMO ? (parseInt(process.env.SLOWMO, 10) || 0) : 0, + }, }, /* Configure projects for major browsers */ diff --git a/ui/litellm-dashboard/e2e_tests/run_e2e.sh b/ui/litellm-dashboard/e2e_tests/run_e2e.sh index 4e3a47edfbda..f8f570cda891 100755 --- a/ui/litellm-dashboard/e2e_tests/run_e2e.sh +++ b/ui/litellm-dashboard/e2e_tests/run_e2e.sh @@ -15,7 +15,7 @@ set -euo pipefail # In CI (CI=true), expects: # - PostgreSQL already running on 127.0.0.1:5432 # - DATABASE_URL already set -# - Python/Poetry already installed +# - Python/uv already installed # - Node.js/npx already available # ================================================================ @@ -48,7 +48,7 @@ cleanup() { trap cleanup EXIT INT TERM # --- Pre-flight checks --- -for cmd in python3 npx poetry; do +for cmd in python3 npx uv; do command -v "$cmd" >/dev/null 2>&1 || { echo "Error: $cmd not found."; exit 1; } done @@ -117,19 +117,15 @@ echo "UI build copied and restructured" # --- Python environment --- echo "=== Setting up Python environment ===" cd "$REPO_ROOT" -if ! poetry run python3 -c "import prisma" 2>/dev/null; then - echo "Installing Python dependencies (first run)..." - poetry install --with dev,proxy-dev --extras "proxy" --quiet - poetry run pip install nodejs-wheel-binaries 2>/dev/null || true - poetry run prisma generate --schema litellm/proxy/schema.prisma -fi +uv sync --group dev --group proxy-dev --extra proxy --frozen --quiet +uv run --no-sync python -m prisma generate --schema litellm/proxy/schema.prisma echo "=== Pushing Prisma schema to database ===" -poetry run prisma db push --schema litellm/proxy/schema.prisma --accept-data-loss +uv run --no-sync python -m prisma db push --schema litellm/proxy/schema.prisma --accept-data-loss # --- Mock LLM server --- echo "=== Starting mock LLM server ===" -poetry run python3 "$SCRIPT_DIR/fixtures/mock_llm_server/server.py" & +uv run --no-sync python "$SCRIPT_DIR/fixtures/mock_llm_server/server.py" & MOCK_PID=$! for i in $(seq 1 15); do @@ -140,7 +136,7 @@ done # --- LiteLLM proxy --- echo "=== Starting LiteLLM proxy ===" cd "$REPO_ROOT" -poetry run python3 -m litellm.proxy.proxy_cli \ +uv run --no-sync python -m litellm.proxy.proxy_cli \ --config "$SCRIPT_DIR/fixtures/config.yml" \ --port 4000 & PROXY_PID=$! 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..a2f2449e1fb5 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,33 @@ test.describe("Proxy Admin - Keys", () => { await expect(page.getByText(E2E_INTERNAL_USER_KEY_ALIAS)).toBeVisible({ timeout: 10_000 }); }); + + test("Create a key with All Proxy Models (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-allproxy-${Date.now()}`; + await page.getByTestId("base-input").fill(keyName); + + // No team selection — leave team dropdown empty so the key is owned by the admin user + + // Select models — open the multi-select and pick the all-models meta-option. + // The Create Key modal labels this "All Team Models" even when no team is selected + // (see src/components/organisms/create_key_button.tsx:944), unlike the team/user + // settings screens which use "All Proxy Models". + await page.locator(".ant-select-selection-overflow").click(); + await page.locator(".ant-select-dropdown:visible").getByText("All Team Models").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 }); + }); });