mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-14 10:52:17 +03:00
PR #9870 moved the proxy-registry bulk-assign action into the toolbar's "More actions" (…) overflow menu, which renders its items only while open. The e2e smoke flow still clicked the testid directly, so the locator never resolved and the test burned its full 180s budget. Open the menu first and assert it is visible before clicking; every existing assertion is unchanged.
224 lines
7.4 KiB
TypeScript
224 lines
7.4 KiB
TypeScript
import { test, expect } from "@playwright/test";
|
|
import { gotoDashboardRoute } from "./helpers/dashboardAuth";
|
|
|
|
type ProxyStub = {
|
|
id: string;
|
|
name: string;
|
|
type: string;
|
|
host: string;
|
|
port: number;
|
|
status: string;
|
|
region?: string | null;
|
|
notes?: string | null;
|
|
};
|
|
|
|
test.describe("Proxy Registry smoke flow", () => {
|
|
test("create, edit, bulk-assign modal, and delete proxy from settings advanced", async ({
|
|
page,
|
|
}) => {
|
|
const state: {
|
|
proxies: ProxyStub[];
|
|
nextId: number;
|
|
bulkAssignCalls: number;
|
|
} = {
|
|
proxies: [],
|
|
nextId: 1,
|
|
bulkAssignCalls: 0,
|
|
};
|
|
|
|
await page.route("**/api/settings/proxy?level=global", async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ proxy: null }),
|
|
});
|
|
});
|
|
|
|
await page.route("**/api/settings/proxies/health?hours=24", async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({
|
|
items: state.proxies.map((p) => ({
|
|
proxyId: p.id,
|
|
totalRequests: 0,
|
|
successRate: null,
|
|
avgLatencyMs: null,
|
|
lastSeenAt: null,
|
|
})),
|
|
total: state.proxies.length,
|
|
windowHours: 24,
|
|
}),
|
|
});
|
|
});
|
|
|
|
await page.route("**/api/settings/proxies/bulk-assign", async (route) => {
|
|
if (route.request().method() !== "PUT") {
|
|
await route.fulfill({
|
|
status: 405,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ error: "method not allowed in test stub" }),
|
|
});
|
|
return;
|
|
}
|
|
|
|
state.bulkAssignCalls += 1;
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({
|
|
success: true,
|
|
scope: "provider",
|
|
requested: 2,
|
|
updated: 2,
|
|
failed: [],
|
|
}),
|
|
});
|
|
});
|
|
|
|
await page.route("**/api/settings/proxies*", async (route) => {
|
|
const req = route.request();
|
|
const method = req.method();
|
|
const url = new URL(req.url());
|
|
const id = url.searchParams.get("id");
|
|
const whereUsed = url.searchParams.get("whereUsed");
|
|
|
|
if (method === "GET" && id && whereUsed === "1") {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ count: 0, assignments: [] }),
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (method === "GET") {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ items: state.proxies, total: state.proxies.length }),
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (method === "POST") {
|
|
const payload = req.postDataJSON() as Partial<ProxyStub>;
|
|
const proxy: ProxyStub = {
|
|
id: `proxy-${state.nextId++}`,
|
|
name: payload.name || "Proxy",
|
|
type: payload.type || "http",
|
|
host: payload.host || "localhost",
|
|
port: Number(payload.port || 8080),
|
|
status: payload.status || "active",
|
|
region: payload.region || null,
|
|
notes: payload.notes || null,
|
|
};
|
|
state.proxies.unshift(proxy);
|
|
await route.fulfill({
|
|
status: 201,
|
|
contentType: "application/json",
|
|
body: JSON.stringify(proxy),
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (method === "PATCH") {
|
|
const payload = req.postDataJSON() as Partial<ProxyStub> & { id?: string };
|
|
const index = state.proxies.findIndex((p) => p.id === payload.id);
|
|
if (index === -1) {
|
|
await route.fulfill({
|
|
status: 404,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ error: { message: "Proxy not found", type: "not_found" } }),
|
|
});
|
|
return;
|
|
}
|
|
|
|
const updated = {
|
|
...state.proxies[index],
|
|
...payload,
|
|
} as ProxyStub;
|
|
state.proxies[index] = updated;
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify(updated),
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (method === "DELETE") {
|
|
if (!id) {
|
|
await route.fulfill({
|
|
status: 400,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ error: { message: "id is required", type: "invalid_request" } }),
|
|
});
|
|
return;
|
|
}
|
|
state.proxies = state.proxies.filter((p) => p.id !== id);
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ success: true }),
|
|
});
|
|
return;
|
|
}
|
|
|
|
await route.fulfill({
|
|
status: 405,
|
|
contentType: "application/json",
|
|
body: JSON.stringify({ error: "method not allowed in test stub" }),
|
|
});
|
|
});
|
|
|
|
// The proxy registry now lives under the "Proxy Pool" sub-tab of the proxy
|
|
// settings page; navigate directly to it so the heading renders.
|
|
await gotoDashboardRoute(page, "/dashboard/system/proxy?tab=proxy-pool");
|
|
|
|
await expect(page.getByRole("heading", { name: "Proxy Registry" })).toBeVisible();
|
|
|
|
await page.getByTestId("proxy-registry-open-create").click();
|
|
const createDialog = page.getByRole("dialog");
|
|
await expect(createDialog.getByText("Create Proxy")).toBeVisible();
|
|
await createDialog.getByTestId("proxy-registry-name-input").fill("Registry Smoke Proxy");
|
|
await createDialog.getByTestId("proxy-registry-host-input").fill("smoke.local");
|
|
await createDialog.getByRole("button", { name: "Save" }).click();
|
|
|
|
await expect(page.locator("table")).toContainText("Registry Smoke Proxy");
|
|
await expect(page.locator("table")).toContainText("http://smoke.local:8080");
|
|
|
|
const row = page.locator("tr", { hasText: "Registry Smoke Proxy" });
|
|
await row.getByRole("button", { name: "Edit" }).click();
|
|
const editDialog = page.getByRole("dialog");
|
|
await expect(editDialog.getByText("Edit Proxy")).toBeVisible();
|
|
await editDialog.getByTestId("proxy-registry-host-input").fill("smoke-updated.local");
|
|
await editDialog.getByRole("button", { name: "Save" }).click();
|
|
|
|
await expect(page.locator("table")).toContainText("http://smoke-updated.local:8080");
|
|
|
|
// Bulk assign moved into the toolbar's "More actions" (⋯) overflow menu in
|
|
// #9870 — the menu only renders its items once opened, so open it first.
|
|
await page.getByTestId("proxy-registry-more-actions").click();
|
|
const actionsMenu = page.getByRole("menu");
|
|
await expect(actionsMenu).toBeVisible();
|
|
await actionsMenu.getByTestId("proxy-registry-open-bulk").click();
|
|
const bulkDialog = page.getByRole("dialog");
|
|
await expect(bulkDialog.getByText("Bulk Proxy Assignment")).toBeVisible();
|
|
await bulkDialog.getByTestId("proxy-registry-bulk-scopeids-input").fill("openai,anthropic");
|
|
await bulkDialog.getByTestId("proxy-registry-bulk-apply").click();
|
|
|
|
await expect
|
|
.poll(() => state.bulkAssignCalls, {
|
|
message: "Expected bulk assign API to be called exactly once",
|
|
})
|
|
.toBe(1);
|
|
|
|
await row.getByRole("button", { name: "Delete" }).click();
|
|
await expect
|
|
.poll(() => state.proxies.some((proxy) => proxy.name === "Registry Smoke Proxy"))
|
|
.toBe(false);
|
|
await expect(page.locator("tr", { hasText: "Registry Smoke Proxy" })).toHaveCount(0);
|
|
});
|
|
});
|