/**
* @ 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] || '' );
}, '' );
}
Messung V0.5 in Prozent C=100 H=92 G=95
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-10-04)
¤
*© Formatika GbR, Deutschland