Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions ui/litellm-dashboard/e2e_tests/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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=<ms> is set, useful for headed local debugging */
launchOptions: {
slowMo: process.env.SLOWMO ? (parseInt(process.env.SLOWMO, 10) || 0) : 0,
},
},

/* Configure projects for major browsers */
Expand Down
18 changes: 7 additions & 11 deletions ui/litellm-dashboard/e2e_tests/run_e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
# ================================================================

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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=$!
Expand Down
29 changes: 29 additions & 0 deletions ui/litellm-dashboard/e2e_tests/tests/proxy-admin/keys.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
});
});
Loading