refactor tests functions into utils
This commit is contained in:
+6
-104
@@ -1,8 +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 } from 'node:child_process'
|
import { chromium } from 'playwright-core'
|
||||||
import { promisify } from 'node:util'
|
|
||||||
import { chromium, BrowserContext } from 'playwright-core'
|
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
import os from 'node:os'
|
import os from 'node:os'
|
||||||
@@ -13,39 +11,14 @@ import { imageSize } from 'image-size'
|
|||||||
import { getCDPSessionForPage } from './cdp-session.js'
|
import { getCDPSessionForPage } from './cdp-session.js'
|
||||||
import { Debugger } from './debugger.js'
|
import { Debugger } from './debugger.js'
|
||||||
import { Editor } from './editor.js'
|
import { Editor } from './editor.js'
|
||||||
import { startPlayWriterCDPRelayServer, type RelayServer } from './cdp-relay.js'
|
|
||||||
import { createFileLogger } from './create-logger.js'
|
|
||||||
import type { CDPCommand } from './cdp-types.js'
|
import type { CDPCommand } from './cdp-types.js'
|
||||||
import { killPortProcess } from 'kill-port-process'
|
import { setupTestContext, cleanupTestContext, getExtensionServiceWorker, type TestContext } from './test-utils.js'
|
||||||
|
|
||||||
declare const window: any
|
declare const window: any
|
||||||
declare const document: any
|
declare const document: any
|
||||||
|
|
||||||
const TEST_PORT = 19987
|
const TEST_PORT = 19987
|
||||||
|
|
||||||
const execAsync = promisify(exec)
|
|
||||||
|
|
||||||
async function getExtensionServiceWorker(context: BrowserContext) {
|
|
||||||
let serviceWorkers = context.serviceWorkers().filter(sw => sw.url().startsWith('chrome-extension://'))
|
|
||||||
let serviceWorker = serviceWorkers[0]
|
|
||||||
if (!serviceWorker) {
|
|
||||||
serviceWorker = await context.waitForEvent('serviceworker', {
|
|
||||||
predicate: (sw) => sw.url().startsWith('chrome-extension://')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < 50; i++) {
|
|
||||||
const isReady = await serviceWorker.evaluate(() => {
|
|
||||||
// @ts-ignore
|
|
||||||
return typeof globalThis.toggleExtensionForActiveTab === 'function'
|
|
||||||
})
|
|
||||||
if (isReady) break
|
|
||||||
await new Promise(r => setTimeout(r, 100))
|
|
||||||
}
|
|
||||||
|
|
||||||
return serviceWorker
|
|
||||||
}
|
|
||||||
|
|
||||||
function js(strings: TemplateStringsArray, ...values: any[]): string {
|
function js(strings: TemplateStringsArray, ...values: any[]): string {
|
||||||
return strings.reduce(
|
return strings.reduce(
|
||||||
(result, str, i) => result + str + (values[i] || ''),
|
(result, str, i) => result + str + (values[i] || ''),
|
||||||
@@ -53,77 +26,6 @@ function js(strings: TemplateStringsArray, ...values: any[]): string {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function killProcessOnPort(port: number): Promise<void> {
|
|
||||||
try {
|
|
||||||
await killPortProcess(port)
|
|
||||||
console.log(`Killed processes on port ${port}`)
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error killing process on port:', err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TestContext {
|
|
||||||
browserContext: Awaited<ReturnType<typeof chromium.launchPersistentContext>>
|
|
||||||
userDataDir: string
|
|
||||||
relayServer: RelayServer
|
|
||||||
}
|
|
||||||
|
|
||||||
async function setupTestContext({ tempDirPrefix }: { tempDirPrefix: string }): Promise<TestContext> {
|
|
||||||
await killProcessOnPort(TEST_PORT)
|
|
||||||
|
|
||||||
console.log('Building extension...')
|
|
||||||
await execAsync(`TESTING=1 PLAYWRITER_PORT=${TEST_PORT} pnpm build`, { cwd: '../extension' })
|
|
||||||
console.log('Extension built')
|
|
||||||
|
|
||||||
const localLogPath = path.join(process.cwd(), 'relay-server.log')
|
|
||||||
const logger = createFileLogger({ logFilePath: localLogPath })
|
|
||||||
const relayServer = await startPlayWriterCDPRelayServer({ port: TEST_PORT, logger })
|
|
||||||
|
|
||||||
const userDataDir = fs.mkdtempSync(path.join(os.tmpdir(), tempDirPrefix))
|
|
||||||
const extensionPath = path.resolve('../extension/dist')
|
|
||||||
|
|
||||||
const browserContext = await chromium.launchPersistentContext(userDataDir, {
|
|
||||||
channel: 'chromium',
|
|
||||||
headless: !process.env.HEADFUL,
|
|
||||||
colorScheme: 'dark',
|
|
||||||
args: [
|
|
||||||
`--disable-extensions-except=${extensionPath}`,
|
|
||||||
`--load-extension=${extensionPath}`,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
const serviceWorker = await getExtensionServiceWorker(browserContext)
|
|
||||||
|
|
||||||
const page = await browserContext.newPage()
|
|
||||||
await page.goto('about:blank')
|
|
||||||
|
|
||||||
await serviceWorker.evaluate(async () => {
|
|
||||||
await globalThis.toggleExtensionForActiveTab()
|
|
||||||
})
|
|
||||||
|
|
||||||
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?.relayServer) {
|
|
||||||
ctx.relayServer.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx?.userDataDir) {
|
|
||||||
try {
|
|
||||||
fs.rmSync(ctx.userDataDir, { recursive: true, force: true })
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to cleanup user data dir:', e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cleanup) {
|
|
||||||
await cleanup()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
var toggleExtensionForActiveTab: () => Promise<{ isConnected: boolean; state: ExtensionState }>;
|
var toggleExtensionForActiveTab: () => Promise<{ isConnected: boolean; state: ExtensionState }>;
|
||||||
var getExtensionState: () => ExtensionState;
|
var getExtensionState: () => ExtensionState;
|
||||||
@@ -136,7 +38,7 @@ describe('MCP Server Tests', () => {
|
|||||||
let testCtx: TestContext | null = null
|
let testCtx: TestContext | null = null
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
testCtx = await setupTestContext({ tempDirPrefix: 'pw-test-' })
|
testCtx = await setupTestContext({ port: TEST_PORT, tempDirPrefix: 'pw-test-', toggleExtension: true })
|
||||||
|
|
||||||
const result = await createMCPClient({ port: TEST_PORT })
|
const result = await createMCPClient({ port: TEST_PORT })
|
||||||
client = result.client
|
client = result.client
|
||||||
@@ -2679,7 +2581,7 @@ describe('CDP Session Tests', () => {
|
|||||||
let testCtx: TestContext | null = null
|
let testCtx: TestContext | null = null
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
testCtx = await setupTestContext({ tempDirPrefix: 'pw-cdp-test-' })
|
testCtx = await setupTestContext({ port: TEST_PORT, tempDirPrefix: 'pw-cdp-test-', toggleExtension: true })
|
||||||
|
|
||||||
const serviceWorker = await getExtensionServiceWorker(testCtx.browserContext)
|
const serviceWorker = await getExtensionServiceWorker(testCtx.browserContext)
|
||||||
await serviceWorker.evaluate(async () => {
|
await serviceWorker.evaluate(async () => {
|
||||||
@@ -3606,7 +3508,7 @@ describe('Service Worker Target Tests', () => {
|
|||||||
let testCtx: TestContext | null = null
|
let testCtx: TestContext | null = null
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
testCtx = await setupTestContext({ tempDirPrefix: 'pw-sw-test-' })
|
testCtx = await setupTestContext({ port: TEST_PORT, tempDirPrefix: 'pw-sw-test-', toggleExtension: true })
|
||||||
}, 600000)
|
}, 600000)
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
@@ -3727,7 +3629,7 @@ describe('Auto-enable Tests', () => {
|
|||||||
process.env.PLAYWRITER_AUTO_ENABLE = '1'
|
process.env.PLAYWRITER_AUTO_ENABLE = '1'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
testCtx = await setupTestContext({ tempDirPrefix: 'pw-auto-test-' })
|
testCtx = await setupTestContext({ port: TEST_PORT, tempDirPrefix: 'pw-auto-test-' })
|
||||||
|
|
||||||
// Disconnect all tabs to start with a clean state
|
// Disconnect all tabs to start with a clean state
|
||||||
const serviceWorker = await getExtensionServiceWorker(testCtx.browserContext)
|
const serviceWorker = await getExtensionServiceWorker(testCtx.browserContext)
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import { exec } from 'node:child_process'
|
||||||
|
import { promisify } from 'node:util'
|
||||||
|
import { chromium, BrowserContext } from 'playwright-core'
|
||||||
|
import path from 'node:path'
|
||||||
|
import fs from 'node:fs'
|
||||||
|
import os from 'node:os'
|
||||||
|
import { startPlayWriterCDPRelayServer, type RelayServer } from './cdp-relay.js'
|
||||||
|
import { createFileLogger } from './create-logger.js'
|
||||||
|
import { killPortProcess } from 'kill-port-process'
|
||||||
|
|
||||||
|
const execAsync = promisify(exec)
|
||||||
|
|
||||||
|
export async function getExtensionServiceWorker(context: BrowserContext) {
|
||||||
|
let serviceWorkers = context.serviceWorkers().filter((sw) => sw.url().startsWith('chrome-extension://'))
|
||||||
|
let serviceWorker = serviceWorkers[0]
|
||||||
|
if (!serviceWorker) {
|
||||||
|
serviceWorker = await context.waitForEvent('serviceworker', {
|
||||||
|
predicate: (sw) => sw.url().startsWith('chrome-extension://'),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 50; i++) {
|
||||||
|
const isReady = await serviceWorker.evaluate(() => {
|
||||||
|
// @ts-ignore
|
||||||
|
return typeof globalThis.toggleExtensionForActiveTab === 'function'
|
||||||
|
})
|
||||||
|
if (isReady) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
await new Promise((r) => setTimeout(r, 100))
|
||||||
|
}
|
||||||
|
|
||||||
|
return serviceWorker
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TestContext {
|
||||||
|
browserContext: BrowserContext
|
||||||
|
userDataDir: string
|
||||||
|
relayServer: RelayServer
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setupTestContext({
|
||||||
|
port,
|
||||||
|
tempDirPrefix,
|
||||||
|
toggleExtension = false,
|
||||||
|
}: {
|
||||||
|
port: number
|
||||||
|
tempDirPrefix: string
|
||||||
|
/** Create initial page and toggle extension on it */
|
||||||
|
toggleExtension?: boolean
|
||||||
|
}): Promise<TestContext> {
|
||||||
|
await killPortProcess(port).catch(() => {})
|
||||||
|
|
||||||
|
console.log('Building extension...')
|
||||||
|
await execAsync(`TESTING=1 PLAYWRITER_PORT=${port} pnpm build`, { cwd: '../extension' })
|
||||||
|
console.log('Extension built')
|
||||||
|
|
||||||
|
const localLogPath = path.join(process.cwd(), 'relay-server.log')
|
||||||
|
const logger = createFileLogger({ logFilePath: localLogPath })
|
||||||
|
const relayServer = await startPlayWriterCDPRelayServer({ port, logger })
|
||||||
|
|
||||||
|
const userDataDir = fs.mkdtempSync(path.join(os.tmpdir(), tempDirPrefix))
|
||||||
|
const extensionPath = path.resolve('../extension/dist')
|
||||||
|
|
||||||
|
const browserContext = await chromium.launchPersistentContext(userDataDir, {
|
||||||
|
channel: 'chromium',
|
||||||
|
headless: !process.env.HEADFUL,
|
||||||
|
colorScheme: 'dark',
|
||||||
|
args: [`--disable-extensions-except=${extensionPath}`, `--load-extension=${extensionPath}`],
|
||||||
|
})
|
||||||
|
|
||||||
|
const serviceWorker = await getExtensionServiceWorker(browserContext)
|
||||||
|
|
||||||
|
if (toggleExtension) {
|
||||||
|
const page = await browserContext.newPage()
|
||||||
|
await page.goto('about:blank')
|
||||||
|
await serviceWorker.evaluate(async () => {
|
||||||
|
await (globalThis as any).toggleExtensionForActiveTab()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return { browserContext, userDataDir, relayServer }
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function cleanupTestContext(ctx: TestContext | null, cleanup?: (() => Promise<void>) | null): Promise<void> {
|
||||||
|
if (ctx?.browserContext) {
|
||||||
|
await ctx.browserContext.close()
|
||||||
|
}
|
||||||
|
if (ctx?.relayServer) {
|
||||||
|
ctx.relayServer.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx?.userDataDir) {
|
||||||
|
try {
|
||||||
|
fs.rmSync(ctx.userDataDir, { recursive: true, force: true })
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to cleanup user data dir:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cleanup) {
|
||||||
|
await cleanup()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user