/
githubmirror
/
puppeteer
Обзор
Документация
Войти
/
githubmirror
/
puppeteer
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
test/src/utils.ts
247 строк
6 KB
Alex Rudenko
fix(a11y): handle leaf nodes without heuristics that rely on name (#14221)
17 сен 2025, 14:47
Не верифицирован
17 сен 2025, 14:47
076cc2e
Код
Авторство
О чём код?
/** * @license * Copyright 2017 Google Inc. * SPDX-License-Identifier: Apache-2.0 */ import {access, constants, rm, watch} from 'node:fs/promises'; import {tmpdir} from 'node:os'; import {basename, dirname} from 'node:path'; import expect from 'expect'; import type {Frame} from 'puppeteer-core/internal/api/Frame.js'; import type {Page} from 'puppeteer-core/internal/api/Page.js'; import type {EventEmitter} from 'puppeteer-core/internal/common/EventEmitter.js'; import {Deferred} from 'puppeteer-core/internal/util/Deferred.js'; import {compare} from './golden-utils.js'; declare module 'expect' { interface Matchers<R> { toBeGolden(pathOrBuffer: string | Buffer): R; } } export const extendExpectWithToBeGolden = ( goldenDir: string, outputDir: string, ): void => { expect.extend({ toBeGolden: ( testScreenshot: string | Uint8Array, goldenFilePath: string, ) => { const result = compare( goldenDir, outputDir, typeof testScreenshot === 'string' ? testScreenshot : Buffer.from(testScreenshot), goldenFilePath, ); if (result.pass) { return { pass: true, message: () => { return ''; }, }; } else { return { pass: false, message: () => { return result.message; }, }; } }, }); }; export const attachFrame = async ( pageOrFrame: Page | Frame, frameId: string, url: string, ): Promise<Frame> => { using handle = await pageOrFrame.evaluateHandle( async (frameId, url) => { const frame = document.createElement('iframe'); frame.src = url; frame.id = frameId; document.body.appendChild(frame); await new Promise(x => { return (frame.onload = x); }); return frame; }, frameId, url, ); return await handle.contentFrame(); }; export const isFavicon = (request: {url: () => string | string[]}): boolean => { return request.url().includes('favicon.ico'); }; export async function detachFrame( pageOrFrame: Page | Frame, frameId: string, ): Promise<void> { await pageOrFrame.evaluate(frameId => { const frame = document.getElementById(frameId) as HTMLIFrameElement; frame.remove(); }, frameId); } export async function navigateFrame( pageOrFrame: Page | Frame, frameId: string, url: string, ): Promise<void> { await pageOrFrame.evaluate( (frameId, url) => { const frame = document.getElementById(frameId) as HTMLIFrameElement; frame.src = url; return new Promise(x => { return (frame.onload = x); }); }, frameId, url, ); } export const dumpFrames = async ( frame: Frame, indentation = '', ): Promise<string[]> => { let description = frame.url().replace(/:\d{4,5}\//, ':<PORT>/'); using element = await frame.frameElement(); if (element) { const nameOrId = await element.evaluate(frame => { return frame.name || frame.id; }); if (nameOrId) { description += ' (' + nameOrId + ')'; } } const result = [indentation + description]; for (const child of frame.childFrames()) { result.push(...(await dumpFrames(child, ' ' + indentation))); } return result; }; export const waitEvent = async <T = any>( emitter: EventEmitter<any>, eventName: string, predicate: (event: T) => boolean = () => { return true; }, ): Promise<T> => { const deferred = Deferred.create<T>({ timeout: 5000, message: `Waiting for ${eventName} event timed out.`, }); const handler = (event: T) => { if (!predicate(event)) { return; } deferred.resolve(event); }; emitter.on(eventName, handler); try { return await deferred.valueOrThrow(); } finally { emitter.off(eventName, handler); } }; export interface FilePlaceholder { filename: `${string}.webm`; [Symbol.dispose](): void; } export function getUniqueVideoFilePlaceholder( debugging = false, ): FilePlaceholder { const name = debugging ? './debugging' : `${tmpdir()}/test-video-${Math.round(Math.random() * 10000)}`; return { filename: `${name}.webm`, [Symbol.dispose]() { if (debugging) { return; } void rmIfExists(this.filename); }, }; } export function rmIfExists(file: string): Promise<void> { return rm(file).catch(() => {}); } export async function waitForFileExistence( filePath: string, timeout = 1000, ): Promise<void> { try { await access(filePath, constants.R_OK); } catch { return await new Promise(async (resolve, reject) => { const abortController = new AbortController(); const timer = setTimeout(() => { abortController.abort(); reject( new Error( `Exceeded timeout of ${timeout} ms for watching ${filePath}`, ), ); }, timeout); const dir = dirname(filePath); const fileBasename = basename(filePath); const watcher = watch(dir, {signal: abortController.signal}); for await (const event of watcher) { if (event.eventType === 'rename' && event.filename === fileBasename) { clearTimeout(timer); abortController.abort(); resolve(); } } }); } } export function html( strings: TemplateStringsArray, ...values: unknown[] ): string { const bodyContent = strings.reduce((acc, str, i) => { return acc + str + (values[i] || ''); }, ''); return `<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>My test page</title> </head> <body> ${bodyContent} </body> </html>`; } export function htmlRaw( strings: TemplateStringsArray, ...values: unknown[] ): string { return strings.reduce((acc, str, i) => { return acc + str + (values[i] || ''); }, ''); }