run relay server in-process during tests for WS message interception
This commit is contained in:
@@ -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
@@ -1,6 +1,6 @@
|
|||||||
import { createMCPClient } from './mcp-client.js'
|
import { createMCPClient } from './mcp-client.js'
|
||||||
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
|
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 { promisify } from 'node:util'
|
||||||
import { chromium, BrowserContext } from 'playwright-core'
|
import { chromium, BrowserContext } from 'playwright-core'
|
||||||
import path from 'node:path'
|
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 type { Protocol } from 'devtools-protocol'
|
||||||
import { imageSize } from 'image-size'
|
import { imageSize } from 'image-size'
|
||||||
import { getCDPSessionForPage } from './cdp-session.js'
|
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 window: any
|
||||||
declare const document: any
|
declare const document: any
|
||||||
@@ -63,7 +65,7 @@ async function killProcessOnPort(port: number): Promise<void> {
|
|||||||
interface TestContext {
|
interface TestContext {
|
||||||
browserContext: Awaited<ReturnType<typeof chromium.launchPersistentContext>>
|
browserContext: Awaited<ReturnType<typeof chromium.launchPersistentContext>>
|
||||||
userDataDir: string
|
userDataDir: string
|
||||||
relayServerProcess: ReturnType<typeof spawn>
|
relayServer: { close(): void }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setupTestContext({ tempDirPrefix }: { tempDirPrefix: string }): Promise<TestContext> {
|
async function setupTestContext({ tempDirPrefix }: { tempDirPrefix: string }): Promise<TestContext> {
|
||||||
@@ -74,31 +76,8 @@ async function setupTestContext({ tempDirPrefix }: { tempDirPrefix: string }): P
|
|||||||
console.log('Extension built')
|
console.log('Extension built')
|
||||||
|
|
||||||
const localLogPath = path.join(process.cwd(), 'relay-server.log')
|
const localLogPath = path.join(process.cwd(), 'relay-server.log')
|
||||||
const relayServerProcess = spawn('pnpm', ['tsx', 'src/start-relay-server.ts'], {
|
const logger = createFileLogger({ logFilePath: localLogPath })
|
||||||
cwd: process.cwd(),
|
const relayServer = await startPlayWriterCDPRelayServer({ port: 19988, logger })
|
||||||
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 userDataDir = fs.mkdtempSync(path.join(os.tmpdir(), tempDirPrefix))
|
const userDataDir = fs.mkdtempSync(path.join(os.tmpdir(), tempDirPrefix))
|
||||||
const extensionPath = path.resolve('../extension/dist')
|
const extensionPath = path.resolve('../extension/dist')
|
||||||
@@ -121,17 +100,16 @@ async function setupTestContext({ tempDirPrefix }: { tempDirPrefix: string }): P
|
|||||||
await globalThis.toggleExtensionForActiveTab()
|
await globalThis.toggleExtensionForActiveTab()
|
||||||
})
|
})
|
||||||
|
|
||||||
return { browserContext, userDataDir, relayServerProcess }
|
return { browserContext, userDataDir, relayServer }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function cleanupTestContext(ctx: TestContext | null, cleanup?: (() => Promise<void>) | null): Promise<void> {
|
async function cleanupTestContext(ctx: TestContext | null, cleanup?: (() => Promise<void>) | null): Promise<void> {
|
||||||
if (ctx?.browserContext) {
|
if (ctx?.browserContext) {
|
||||||
await ctx.browserContext.close()
|
await ctx.browserContext.close()
|
||||||
}
|
}
|
||||||
if (ctx?.relayServerProcess) {
|
if (ctx?.relayServer) {
|
||||||
ctx.relayServerProcess.kill()
|
ctx.relayServer.close()
|
||||||
}
|
}
|
||||||
await killProcessOnPort(19988)
|
|
||||||
|
|
||||||
if (ctx?.userDataDir) {
|
if (ctx?.userDataDir) {
|
||||||
try {
|
try {
|
||||||
@@ -1796,13 +1774,20 @@ describe('CDP Session Tests', () => {
|
|||||||
sampleFunctionNames: functionNames,
|
sampleFunctionNames: functionNames,
|
||||||
}).toMatchInlineSnapshot(`
|
}).toMatchInlineSnapshot(`
|
||||||
{
|
{
|
||||||
"durationMicroseconds": 6205,
|
"durationMicroseconds": 12993,
|
||||||
"hasNodes": true,
|
"hasNodes": true,
|
||||||
"nodeCount": 3,
|
"nodeCount": 21,
|
||||||
"sampleFunctionNames": [
|
"sampleFunctionNames": [
|
||||||
"(root)",
|
"(root)",
|
||||||
"(program)",
|
"(program)",
|
||||||
"(idle)",
|
"(idle)",
|
||||||
|
"evaluate",
|
||||||
|
"fibonacci",
|
||||||
|
"fibonacci",
|
||||||
|
"fibonacci",
|
||||||
|
"fibonacci",
|
||||||
|
"fibonacci",
|
||||||
|
"fibonacci",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
|
|||||||
@@ -1,25 +1,11 @@
|
|||||||
import { startPlayWriterCDPRelayServer } from './extension/cdp-relay.js'
|
import { startPlayWriterCDPRelayServer } from './extension/cdp-relay.js'
|
||||||
import fs from 'node:fs'
|
import { createFileLogger } from './create-logger.js'
|
||||||
import util from 'node:util'
|
import { getLogFilePath } from './utils.js'
|
||||||
import { ensureDataDir, getLogFilePath } from './utils.js'
|
|
||||||
|
|
||||||
const logFilePath = getLogFilePath()
|
const logFilePath = getLogFilePath()
|
||||||
|
|
||||||
process.title = 'playwriter-ws-server'
|
process.title = 'playwriter-ws-server'
|
||||||
ensureDataDir()
|
|
||||||
fs.writeFileSync(logFilePath, '')
|
|
||||||
|
|
||||||
const log = (...args: any[]) => {
|
const logger = createFileLogger({ logFilePath })
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
process.on('uncaughtException', async (err) => {
|
process.on('uncaughtException', async (err) => {
|
||||||
await logger.error('Uncaught Exception:', err);
|
await logger.error('Uncaught Exception:', err);
|
||||||
|
|||||||
Reference in New Issue
Block a user