import type { CDPSession, Page } from '@playwright/test'; import { createWriteStream, mkdirSync, writeFileSync } from 'node:fs'; import path from 'node:path'; const sessions = new WeakMap(); async function cdp(page: Page): Promise { let s = sessions.get(page); if (!s) { s = await page.context().newCDPSession(page); sessions.set(page, s); } return s; } /** CPU throttling: rate=1 — без замедления, 4 — в 4 раза медленнее. */ export async function throttle(page: Page, rate: number): Promise { await (await cdp(page)).send('Emulation.setCPUThrottlingRate', { rate }); } export async function startTrace(page: Page): Promise { await page.context().browser()!.startTracing(page, { categories: ['devtools.timeline', 'disabled-by-default-devtools.timeline', 'v8.execute', 'blink.user_timing'], screenshots: false, }); } export async function stopTrace(page: Page, file: string): Promise { mkdirSync(path.dirname(file), { recursive: true }); const buf = await page.context().browser()!.stopTracing(); writeFileSync(file, buf); } export async function gc(page: Page): Promise { const s = await cdp(page); await s.send('HeapProfiler.enable'); await s.send('HeapProfiler.collectGarbage'); } export async function domCounters(page: Page): Promise<{ documents: number; nodes: number; jsEventListeners: number }> { return (await (await cdp(page)).send('Memory.getDOMCounters')) as any; } /** Размер JS-кучи после GC, байты. */ export async function heapUsed(page: Page): Promise { await gc(page); const s = await cdp(page); await s.send('Runtime.enable'); const r = (await s.send('Runtime.getHeapUsage')) as { usedSize: number }; return r.usedSize; } export async function heapSnapshot(page: Page, file: string): Promise { mkdirSync(path.dirname(file), { recursive: true }); const s = await cdp(page); const out = createWriteStream(file); const onChunk = (e: { chunk: string }) => { out.write(e.chunk); }; s.on('HeapProfiler.addHeapSnapshotChunk', onChunk); await s.send('HeapProfiler.enable'); await s.send('HeapProfiler.takeHeapSnapshot', { reportProgress: false }); s.off('HeapProfiler.addHeapSnapshotChunk', onChunk); await new Promise((res) => out.end(res)); }