From b7ff5c6d751fff87b61b3daa6ef02ee924c88eb3 Mon Sep 17 00:00:00 2001 From: "Tommy D. Rossi" Date: Thu, 18 Dec 2025 16:11:10 +0100 Subject: [PATCH] run relay server in-process during tests for WS message interception --- playwriter/src/create-logger.ts | 26 ++++++++++++++ playwriter/src/mcp.test.ts | 51 ++++++++++------------------ playwriter/src/start-relay-server.ts | 20 ++--------- 3 files changed, 47 insertions(+), 50 deletions(-) create mode 100644 playwriter/src/create-logger.ts diff --git a/playwriter/src/create-logger.ts b/playwriter/src/create-logger.ts new file mode 100644 index 0000000..c5d5d5d --- /dev/null +++ b/playwriter/src/create-logger.ts @@ -0,0 +1,26 @@ +import fs from 'node:fs' +import util from 'node:util' +import { ensureDataDir, getLogFilePath } from './utils.js' + +export type Logger = { + log(...args: unknown[]): void + error(...args: unknown[]): void +} + +export function createFileLogger({ logFilePath }: { logFilePath?: string } = {}): Logger { + const resolvedLogFilePath = logFilePath || getLogFilePath() + ensureDataDir() + fs.writeFileSync(resolvedLogFilePath, '') + + const log = (...args: unknown[]) => { + const message = args.map(arg => + typeof arg === 'string' ? arg : util.inspect(arg, { depth: null, colors: false }) + ).join(' ') + fs.appendFileSync(resolvedLogFilePath, message + '\n') + } + + return { + log, + error: log + } +} diff --git a/playwriter/src/mcp.test.ts b/playwriter/src/mcp.test.ts index f4f0b19..133c75a 100644 --- a/playwriter/src/mcp.test.ts +++ b/playwriter/src/mcp.test.ts @@ -1,6 +1,6 @@ import { createMCPClient } from './mcp-client.js' import { describe, it, expect, beforeAll, afterAll } from 'vitest' -import { exec, spawn } from 'node:child_process' +import { exec } from 'node:child_process' import { promisify } from 'node:util' import { chromium, BrowserContext } from 'playwright-core' import path from 'node:path' @@ -11,6 +11,8 @@ import type { ExtensionState } from 'mcp-extension/src/types.js' import type { Protocol } from 'devtools-protocol' import { imageSize } from 'image-size' import { getCDPSessionForPage } from './cdp-session.js' +import { startPlayWriterCDPRelayServer } from './extension/cdp-relay.js' +import { createFileLogger } from './create-logger.js' declare const window: any declare const document: any @@ -63,7 +65,7 @@ async function killProcessOnPort(port: number): Promise { interface TestContext { browserContext: Awaited> userDataDir: string - relayServerProcess: ReturnType + relayServer: { close(): void } } async function setupTestContext({ tempDirPrefix }: { tempDirPrefix: string }): Promise { @@ -74,31 +76,8 @@ async function setupTestContext({ tempDirPrefix }: { tempDirPrefix: string }): P console.log('Extension built') const localLogPath = path.join(process.cwd(), 'relay-server.log') - const relayServerProcess = spawn('pnpm', ['tsx', 'src/start-relay-server.ts'], { - cwd: process.cwd(), - stdio: 'inherit', - env: { ...process.env, PLAYWRITER_LOG_PATH: localLogPath } - }) - - await new Promise((resolve, reject) => { - let retries = 0 - const interval = setInterval(async () => { - try { - const { stdout } = await execAsync('lsof -ti:19988') - if (stdout.trim()) { - clearInterval(interval) - resolve() - } - } catch { - // ignore - } - retries++ - if (retries > 30) { - clearInterval(interval) - reject(new Error('Relay server failed to start')) - } - }, 1000) - }) + const logger = createFileLogger({ logFilePath: localLogPath }) + const relayServer = await startPlayWriterCDPRelayServer({ port: 19988, logger }) const userDataDir = fs.mkdtempSync(path.join(os.tmpdir(), tempDirPrefix)) const extensionPath = path.resolve('../extension/dist') @@ -121,17 +100,16 @@ async function setupTestContext({ tempDirPrefix }: { tempDirPrefix: string }): P await globalThis.toggleExtensionForActiveTab() }) - return { browserContext, userDataDir, relayServerProcess } + return { browserContext, userDataDir, relayServer } } async function cleanupTestContext(ctx: TestContext | null, cleanup?: (() => Promise) | null): Promise { if (ctx?.browserContext) { await ctx.browserContext.close() } - if (ctx?.relayServerProcess) { - ctx.relayServerProcess.kill() + if (ctx?.relayServer) { + ctx.relayServer.close() } - await killProcessOnPort(19988) if (ctx?.userDataDir) { try { @@ -1796,13 +1774,20 @@ describe('CDP Session Tests', () => { sampleFunctionNames: functionNames, }).toMatchInlineSnapshot(` { - "durationMicroseconds": 6205, + "durationMicroseconds": 12993, "hasNodes": true, - "nodeCount": 3, + "nodeCount": 21, "sampleFunctionNames": [ "(root)", "(program)", "(idle)", + "evaluate", + "fibonacci", + "fibonacci", + "fibonacci", + "fibonacci", + "fibonacci", + "fibonacci", ], } `) diff --git a/playwriter/src/start-relay-server.ts b/playwriter/src/start-relay-server.ts index 325c759..84c085c 100644 --- a/playwriter/src/start-relay-server.ts +++ b/playwriter/src/start-relay-server.ts @@ -1,25 +1,11 @@ import { startPlayWriterCDPRelayServer } from './extension/cdp-relay.js' -import fs from 'node:fs' -import util from 'node:util' -import { ensureDataDir, getLogFilePath } from './utils.js' +import { createFileLogger } from './create-logger.js' +import { getLogFilePath } from './utils.js' const logFilePath = getLogFilePath() - process.title = 'playwriter-ws-server' -ensureDataDir() -fs.writeFileSync(logFilePath, '') -const log = (...args: any[]) => { - const message = args.map(arg => - typeof arg === 'string' ? arg : util.inspect(arg, { depth: null, colors: false }) - ).join(' ') - return fs.promises.appendFile(logFilePath, message + '\n') -} - -const logger = { - log, - error: log -} +const logger = createFileLogger({ logFilePath }) process.on('uncaughtException', async (err) => { await logger.error('Uncaught Exception:', err);