src/compare-pdf-to-snapshot.ts (77 lines of code) (raw):

import { join } from 'path' import { existsSync, mkdirSync, unlinkSync } from 'fs' import { pdf2png, writeImages } from './pdf2png' import { compareImages, CompareImagesOpts, HighlightColor } from './compare-images' import Jimp from 'jimp' export type RectangleMask = Readonly<{ type: 'rectangle-mask' x: number y: number width: number height: number color: HighlightColor }> export type RegionMask = RectangleMask export type MaskRegions = (page: number) => ReadonlyArray<RegionMask> | undefined const colorToNum: Record<HighlightColor, number> = { Red: 0xff0000ff, Green: 0x00ff00ff, Blue: 0x0000ffff, White: 0x00000000, Cyan: 0x00ffffff, Magenta: 0xff00ffff, Yellow: 0xffff00ff, Black: 0x000000ff, Gray: 0xbfbfbfff, } const maskImgWithRegions = (maskRegions: MaskRegions) => (images: ReadonlyArray<Jimp>): ReadonlyArray<Jimp> => { images.forEach((img, idx) => { ;(maskRegions(idx + 1) || []).forEach(({ type, x, y, width, height, color }) => { if (type === 'rectangle-mask') { img.composite(new Jimp(width, height, colorToNum[color]), x, y) } }) }) return images } export type CompareOptions = CompareImagesOpts & { maskRegions: MaskRegions } export const snapshotsDirName = '__snapshots__' /** * Compare pdf to persisted snapshot. If one does not exist it is created * @param pdf - path to pdf file or pdf loaded as Buffer * @param snapshotDir - path to a directory where __snapshots__ folder is going to be created * @param snapshotName - uniq name of a snapshot in the above path * @param compareOptions - image comparison options * @param compareOptions.tolerance - number value for error tolerance, ranges 0-1 (default: 0) * @param compareOptions.maskRegions - `(page: number) => ReadonlyArray<RegionMask> | undefined` mask predefined regions per page, i.e. when there are parts of the pdf that change between tests */ export const comparePdfToSnapshot = ( pdf: string | Buffer, snapshotDir: string, snapshotName: string, { maskRegions = () => [], ...restOpts }: Partial<CompareOptions> = {}, ): Promise<boolean> => { const dir = join(snapshotDir, snapshotsDirName) if (!existsSync(dir)) { mkdirSync(dir, { recursive: true }) } const snapshotPath = join(dir, snapshotName + '.png') if (!existsSync(snapshotPath)) { return pdf2png(pdf) .then(maskImgWithRegions(maskRegions)) .then(writeImages(snapshotPath)) .then(() => true) } return pdf2png(pdf) .then(maskImgWithRegions(maskRegions)) .then((images) => compareImages(snapshotPath, images, restOpts).then((result) => { const diffSnapshotPath = join(dir, snapshotName + '.diff.png') if (result.equal) { if (existsSync(diffSnapshotPath)) { unlinkSync(diffSnapshotPath) } return true } const newSnapshotPath = join(dir, snapshotName + '.new.png') return writeImages(newSnapshotPath)(images) .then(() => writeImages(diffSnapshotPath)(result.diffs.map((x) => x.diff))) .then(() => false) }), ) }