Back to Deck
[utility]
Playwright Request Cache
ИСХОДНИК TypeScript
ВЕРСИЯ 1.0
АВТОР Cododel
Класс PWCache для Playwright, перехватывающий сетевые запросы и кэширующий ответы (CSS, JS, изображения, шрифты) в файловой системе (./.cache). Ускоряет тесты и снижает использование сети за счет отдачи кэшированных файлов.
Исходный код
import type { Page, Response } from 'playwright'import fs from 'fs'
export class PWCache { private readonly cacheDir = './.cache' statistics: { totalRequests: number totalCached: number totalNotCached: number }
constructor(public page: Page) { this.statistics = new Proxy( { totalRequests: 0, totalCached: 0, totalNotCached: 0, }, { set: (target, key, value) => { // @ts-ignore target[key] = value console.log(`Statistics: ${JSON.stringify(target)}`) return true }, }, ) }
async run() { if (!fs.existsSync(this.cacheDir)) { fs.mkdirSync(this.cacheDir) } await this.page.route('**/*', async (route) => { this.statistics.totalRequests++
const request = route.request() const url = request.url()
if (PWCache._isUrlForCaching(url)) { console.log(`Response Cache: ${url}`) const cachedFile = `${this.cacheDir}/${encodeURIComponent(url)}` if (fs.existsSync(cachedFile)) { this.statistics.totalCached++
const body = fs.readFileSync(cachedFile) await route.fulfill({ body: body, headers: { 'content-type': PWCache._getContentType(url) }, }) return } } else { this.statistics.totalNotCached++ } route.continue() })
this.page.on('response', async (response: Response) => { const request = response.request() const url = request.url() const cachedFile = `${this.cacheDir}/${encodeURIComponent(url)}` console.log(`Response: ${url}`)
try { if (PWCache._isUrlForCaching(url)) { if (url.includes('.mp4')) { const file = await response.finished().then(() => response.body()) fs.writeFileSync(cachedFile, file) } else { const file = await response.body() fs.writeFileSync(cachedFile, file) } } } catch (error) { console.error(`Error Response with cached: ${url}`) console.info(error) } }) }
static _isUrlForCaching(url: string): boolean { const fileExtensions = [ '.css', '.js', '.png', '.jpg', '.woff', '.woff2', '.ogg', '.ttf', '.mp4', ] switch (true) { case fileExtensions.some((ext) => url.split('?')[0].endsWith(ext)): return true default: return false } } static _getContentType(url: string): string { const types: { [key: string]: string } = { '.css': 'text/css', '.js': 'application/javascript', '.png': 'image/png', '.jpg': 'image/jpeg', '.woff': 'font/woff', '.woff2': 'font/woff2', '.ogg': 'audio/ogg', '.ttf': 'font/ttf', }
const ext = url.split('?')[0].split('.').pop()
if (`.${ext}` in types) { return types[`.${ext}`] }
return 'application/octet-stream' }}