BangSo/apps/web/e2e/mcp-oauth.spec.ts

108 lines
4.0 KiB
TypeScript
Raw Permalink Normal View History

2026-09-01 16:51:19 +00:00
import { expect, type Route, test } from "@playwright/test";
import type { McpServer } from "@rakazo/contracts";
import { captureScreenshot, completeOnboarding, signup } from "./helpers";
test("connects an MCP server through the OAuth popup callback", async ({ page }, testInfo) => {
const stamp = Date.now();
await signup(page, `mcp-oauth-${stamp}@rakazo.test`, "password12", "MCP OAuth");
await completeOnboarding(page);
let oauthStatus: McpServer["oauthStatus"] = "none";
const server: McpServer = {
id: "mcp-oauth-server",
spaceId: "mcp-oauth-workspace",
slug: "linear",
name: "Linear MCP",
description: "",
transport: "streamable_http",
endpoint: "https://mcp.linear.test/mcp",
command: null,
args: [],
envKeys: [],
headerKeys: [],
hasSecret: false,
oauthStatus,
enabled: true,
revision: 1,
createdAt: "2026-08-24T00:00:00.000Z",
updatedAt: "2026-08-24T00:00:00.000Z",
};
const browserOrigin = new URL(page.url()).origin;
let releaseCompletion = () => {};
let markCompletionStarted = () => {};
const completionGate = new Promise<void>((resolve) => {
releaseCompletion = resolve;
});
const completionStarted = new Promise<void>((resolve) => {
markCompletionStarted = resolve;
});
await page.context().route("**/rpc/mcp/servers/list", async (route) => {
await route.fulfill({
contentType: "application/json",
body: JSON.stringify({ json: [{ ...server, oauthStatus }] }),
});
});
await page.context().route("**/rpc/mcp/assignments/all", async (route) => {
await route.fulfill({ contentType: "application/json", body: JSON.stringify({ json: [] }) });
});
await page.context().route("**/rpc/mcp/oauth/begin", async (route) => {
expect(route.request().postDataJSON()).toEqual({
json: {
serverId: server.id,
redirectUri: `${browserOrigin}/mcp/oauth/callback`,
},
});
await route.fulfill({
contentType: "application/json",
body: JSON.stringify({
json: {
status: "authorization_required",
sessionId: "mcp-oauth-session",
authorizationUrl: `${browserOrigin}/mcp/oauth/callback?code=fake-code&state=mcp-oauth-session`,
},
}),
});
});
await page.context().route("**/rpc/mcp/oauth/complete", async (route: Route) => {
expect(route.request().postDataJSON()).toEqual({
json: {
sessionId: "mcp-oauth-session",
code: "fake-code",
state: "mcp-oauth-session",
},
});
markCompletionStarted();
await completionGate;
oauthStatus = "connected";
await route.fulfill({
contentType: "application/json",
body: JSON.stringify({ json: { ok: true } }),
});
});
await page.getByText("Integrations", { exact: true }).click();
await page.getByTestId("integrations-advanced").evaluate((element) => {
(element as HTMLDetailsElement).open = true;
});
await page.getByRole("button", { name: "MCP servers", exact: true }).click();
await expect(page.getByRole("heading", { name: "MCP servers" })).toBeVisible();
await expect(page.getByText("Linear MCP", { exact: true })).toBeVisible();
await expect(page.getByText("No credential saved", { exact: true })).toBeVisible();
await captureScreenshot(page, testInfo, "mcp-oauth-ready");
const popupPromise = page.waitForEvent("popup");
await page.getByRole("button", { name: "Connect OAuth", exact: true }).click();
const popup = await popupPromise;
await completionStarted;
await expect(popup.getByText("Finishing MCP connection…", { exact: true })).toBeVisible();
await captureScreenshot(popup, testInfo, "mcp-oauth-callback");
releaseCompletion();
await expect(page.getByText("OAuth connected", { exact: true })).toBeVisible();
await expect(page.getByRole("button", { name: "Reconnect OAuth", exact: true })).toBeVisible();
await expect(page.getByRole("button", { name: "Disconnect", exact: true })).toBeVisible();
await expect.poll(() => popup.isClosed()).toBe(true);
await captureScreenshot(page, testInfo, "mcp-oauth-connected");
});