/**
* @license
* Copyright 2017 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import assert from 'assert' ;
import fs from 'fs' ;
import path from 'path' ;
import {diffLines} from 'diff' ;
import jpeg from 'jpeg-js' ;
import mime from 'mime' ;
import pixelmatch from 'pixelmatch' ;
import {PNG} from 'pngjs' ;
interface DiffFile {
diff: string | Buffer;
ext?: string;
}
const GoldenComparators = new Map<
string,
(
actualBuffer: string | Buffer,
expectedBuffer: string | Buffer,
mimeType: string,
) => DiffFile | undefined
>();
const addSuffix = (
filePath: string,
suffix: string,
customExtension?: string,
): string => {
const dirname = path.dirname(filePath);
const ext = path.extname(filePath);
const name = path.basename(filePath, ext);
return path.join(dirname, name + suffix + (customExtension || ext));
};
const compareImages = (
actualBuffer: string | Buffer,
expectedBuffer: string | Buffer,
mimeType: string,
): DiffFile | undefined => {
assert (typeof actualBuffer !== 'string' );
assert (typeof expectedBuffer !== 'string' );
const actual =
mimeType === 'image/png'
? PNG.sync.read(actualBuffer)
: jpeg.decode(actualBuffer);
const expected =
mimeType === 'image/png'
? PNG.sync.read(expectedBuffer)
: jpeg.decode(expectedBuffer);
if (expected.width !== actual.width || expected.height !== actual.height) {
throw new Error(
`Sizes differ: expected image ${expected.width}px X ${expected.height}px, but got ${actual.width}px X ${actual.height}px.`,
);
}
const diff = new PNG({width: expected.width, height: expected.height});
const count = pixelmatch(
expected.data,
actual.data,
diff.data,
expected.width,
expected.height,
{threshold: 0 .1 },
);
return count > 0 ? {diff: PNG.sync.write(diff)} : undefined;
};
const compareText = (
actual: string | Buffer,
expectedBuffer: string | Buffer,
): DiffFile | undefined => {
assert (
typeof actual === 'string' ,
`Expected type string got ${typeof actual}`,
);
const expected = expectedBuffer.toString('utf-8' );
if (expected === actual) {
return ;
}
const result = diffLines(expected, actual);
const html = result.reduce(
(text, change) => {
text += change.added
? `<span class ='ins' >${change.value}</span>`
: change.removed
? `<span class ='del' >${change.value}</span>`
: change.value;
return text;
},
`<link rel="stylesheet" href="file://${path.join(
__dirname,
'diffstyle.css' ,
)}">`,
);
return {
diff: html,
ext: '.html' ,
};
};
GoldenComparators.set('image/png' , compareImages);
GoldenComparators.set('image/jpeg' , compareImages);
GoldenComparators.set('text/plain' , compareText);
export const compare = (
goldenPath: string,
outputPath: string,
actual: string | Buffer,
goldenName: string,
): {pass: true } | {pass: false ; message: string} => {
goldenPath = path.normalize(goldenPath);
outputPath = path.normalize(outputPath);
const expectedPath = path.join(goldenPath, goldenName);
const actualPath = path.join(outputPath, goldenName);
const messageSuffix = `Output is saved in "${path.basename(
outputPath + '" directory' ,
)}`;
if (!fs.existsSync(expectedPath)) {
ensureOutputDir();
fs.writeFileSync(actualPath, actual);
return {
pass: false ,
message: `${goldenName} is missing in golden results. ${messageSuffix}`,
};
}
const expected = fs.readFileSync(expectedPath);
const mimeType = mime.getType(goldenName);
assert (mimeType);
const comparator = GoldenComparators.get(mimeType);
if (!comparator) {
return {
pass: false ,
message: `Failed to find comparator with type ${mimeType}: ${goldenName}`,
};
}
const result = comparator(actual, expected, mimeType);
if (!result) {
return {pass: true };
}
ensureOutputDir();
if (goldenPath === outputPath) {
fs.writeFileSync(addSuffix(actualPath, '-actual' ), actual);
} else {
fs.writeFileSync(actualPath, actual);
// Copy expected to the output/ folder for convenience.
fs.writeFileSync(addSuffix(actualPath, '-expected' ), expected);
}
if (result) {
const diffPath = addSuffix(actualPath, '-diff' , result.ext);
fs.writeFileSync(diffPath, result.diff);
}
return {
pass: false ,
message: `${goldenName} mismatch! ${messageSuffix}`,
};
function ensureOutputDir() {
if (!fs.existsSync(outputPath)) {
fs.mkdirSync(outputPath);
}
}
};
Messung V0.5 in Prozent C=97 H=77 G=87
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet am 2026-06-04)
¤
*© Formatika GbR, Deutschland