Files
2026-07-13 13:39:12 +08:00

161 lines
6.0 KiB
TypeScript

/**
* E2E — AgentBridge page smoke tests
*
* These tests require the OmniRoute server to be running at http://localhost:20128
* (or the URL in PLAYWRIGHT_BASE_URL / baseURL in playwright.config.ts).
*
* CI behaviour: tests are marked `.skip` unless the env var
* `RUN_AGENT_BRIDGE_E2E=1` is set, since they require a full server process
* AND port 443 privileges (or mock). In CI the unit/integration suites provide
* functional coverage; the E2E layer verifies UI navigation and wiring.
*
* To run locally:
* RUN_AGENT_BRIDGE_E2E=1 npx playwright test tests/e2e/agent-bridge.spec.ts
*/
import { test, expect, type Page } from "@playwright/test";
const SKIP = !process.env["RUN_AGENT_BRIDGE_E2E"];
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
async function navigateToAgentBridge(page: Page): Promise<void> {
await page.goto("/dashboard/tools/agent-bridge");
// Wait for the page to settle (auth redirect or dashboard render)
await page.waitForLoadState("networkidle");
}
// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------
test.describe("AgentBridge page", () => {
test.skip(SKIP, "Set RUN_AGENT_BRIDGE_E2E=1 to run AgentBridge E2E tests");
test("page renders and shows heading", async ({ page }) => {
await navigateToAgentBridge(page);
// Should render AgentBridge heading (login may redirect first — tolerate both)
const url = page.url();
if (url.includes("/login")) {
// Server is auth-protected; the page route itself exists
await expect(page.locator("body")).toBeVisible();
return;
}
await expect(page.locator("h1, [data-testid='agent-bridge-heading']").first()).toBeVisible();
});
test("page renders 9 agent cards (or empty-providers state)", async ({ page }) => {
await navigateToAgentBridge(page);
const url = page.url();
if (url.includes("/login")) {
test.skip();
return;
}
// Either: 9 agent cards are visible
// OR: empty-providers state is shown (no providers configured yet)
const agentCards = page.locator("[data-testid='agent-card']");
const emptyState = page.locator("[data-testid='empty-providers-state'], [data-testid='agent-bridge-empty']");
const cardCount = await agentCards.count();
const emptyVisible = await emptyState.isVisible().catch(() => false);
expect(cardCount === 9 || emptyVisible).toBe(true);
});
test("each agent card shows agent name", async ({ page }) => {
await navigateToAgentBridge(page);
const url = page.url();
if (url.includes("/login")) {
test.skip();
return;
}
const agentCards = page.locator("[data-testid='agent-card']");
const count = await agentCards.count();
if (count === 0) {
// Empty providers state — skip the rest
test.skip();
return;
}
expect(count).toBe(9);
// Spot-check: Antigravity and GitHub Copilot cards exist
await expect(page.locator("text=Antigravity").first()).toBeVisible();
await expect(page.locator("text=Copilot, text=GitHub Copilot").first()).toBeVisible().catch(async () => {
// Accept either name variant
await expect(page.locator("text=Copilot").first()).toBeVisible();
});
});
test("AgentBridge Server Card is visible", async ({ page }) => {
await navigateToAgentBridge(page);
const url = page.url();
if (url.includes("/login")) {
test.skip();
return;
}
const serverCard = page.locator(
"[data-testid='agent-bridge-server-card'], [data-testid='server-card']"
);
await expect(serverCard.first()).toBeVisible();
});
test("Setup wizard opens when Setup button is clicked", async ({ page }) => {
await navigateToAgentBridge(page);
const url = page.url();
if (url.includes("/login")) {
test.skip();
return;
}
const agentCards = page.locator("[data-testid='agent-card']");
const count = await agentCards.count();
if (count === 0) {
test.skip();
return;
}
// Click "Setup wizard" on the first card that has one visible
const setupButton = page.locator("[data-testid='setup-wizard-btn'], button:has-text('Setup wizard')").first();
const isVisible = await setupButton.isVisible().catch(() => false);
if (!isVisible) {
// All agents already set up — skip wizard open test
test.skip();
return;
}
await setupButton.click();
// Wizard modal should appear
const wizard = page.locator("[data-testid='setup-wizard'], [role='dialog']").first();
await expect(wizard).toBeVisible({ timeout: 5000 });
});
test("DNS toggle interaction does not crash the page", async ({ page }) => {
await navigateToAgentBridge(page);
const url = page.url();
if (url.includes("/login")) {
test.skip();
return;
}
const dnsToggle = page.locator("[data-testid='dns-toggle']").first();
const isVisible = await dnsToggle.isVisible().catch(() => false);
if (!isVisible) {
test.skip();
return;
}
// Click the toggle — may show a sudo prompt modal or update state
await dnsToggle.click();
// Page should not crash (no error boundary)
await expect(page.locator("[data-testid='error-boundary']")).not.toBeVisible();
await page.waitForTimeout(500);
await expect(page.locator("body")).toBeVisible();
});
test("redirect from /dashboard/system/mitm-proxy to agent-bridge", async ({ page }) => {
// Old mitm-proxy URL should redirect or show moved notice
await page.goto("/dashboard/system/mitm-proxy");
await page.waitForLoadState("networkidle");
const url = page.url();
// Should redirect to agent-bridge or show a moved banner
const isRedirected = url.includes("agent-bridge");
const hasBanner = await page.locator("text=moved, text=AgentBridge").first().isVisible().catch(() => false);
expect(isRedirected || hasBanner).toBe(true);
});
});