run relay server in-process during tests for WS message interception

This commit is contained in:
Tommy D. Rossi
2025-12-18 16:11:10 +01:00
parent daf01b8b69
commit b7ff5c6d75
3 changed files with 47 additions and 50 deletions
+26
View File
@@ -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
}
}
+18 -33
View File
@@ -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<void> {
interface TestContext {
browserContext: Awaited<ReturnType<typeof chromium.launchPersistentContext>>
userDataDir: string
relayServerProcess: ReturnType<typeof spawn>
relayServer: { close(): void }
}
async function setupTestContext({ tempDirPrefix }: { tempDirPrefix: string }): Promise<TestContext> {
@@ -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<void>((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<void>) | null): Promise<void> {
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",
],
}
`)
+3 -17
View File
@@ -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);