diff --git a/src/compare-pdf-to-snapshot.test.ts b/src/compare-pdf-to-snapshot.test.ts index a3e2ca6..10a3604 100644 --- a/src/compare-pdf-to-snapshot.test.ts +++ b/src/compare-pdf-to-snapshot.test.ts @@ -115,7 +115,12 @@ describe('comparePdfToSnapshot()', () => { })) it('two-page.pdf', () => testPdf2png(twoPage, 'two-page')) it('two-page.pdf buffer', () => readFile(twoPage).then((x) => testPdf2png(x, 'two-page'))) - }) + it('two-page-separate-pages (combinePages: false)', () => { + return testPdf2png(twoPagePdfPath, 'two-page-separate-pages', { + combinePages: false + }) + }) + }); describe('mask regions', () => { const blueMask: RegionMask = { diff --git a/src/compare-pdf-to-snapshot.ts b/src/compare-pdf-to-snapshot.ts index 8b47c61..c802345 100644 --- a/src/compare-pdf-to-snapshot.ts +++ b/src/compare-pdf-to-snapshot.ts @@ -1,5 +1,5 @@ import * as path from 'node:path' -import { access, mkdir, unlink } from 'node:fs/promises' +import { access, mkdir, unlink, readdir } from 'node:fs/promises' import { pdf2png } from './pdf2png/pdf2png' import { compareImages } from './compare-images' import { Jimp, JimpInstance } from 'jimp' @@ -90,6 +90,12 @@ const maskImgWithRegions = * fields is inlined. */ export type CompareOptions = { + /** + * Whether to combine all pages into a single image. + * + * @defaultValue true + */ + combinePages?: boolean /** * Number value for error tolerance in the range [0, 1]. * @@ -142,10 +148,25 @@ export async function comparePdfToSnapshot( options?: CompareOptions, ): Promise { const mergedOptions = mergeOptionsWithDefaults(options) - const snapshotContext = await createSnapshotContext(snapshotDir, snapshotName) + const snapshotContext = await createSnapshotContext(snapshotDir, snapshotName, mergedOptions) + + // When combinePages is false, we need to process each page as a separate context + if (Array.isArray(snapshotContext)) { + try { + // Ensure snapshots exists for all pages. If any are missing, we + // should re-generate all of them. + for (const context of snapshotContext) { + await access(context.path) + } + + return compareWithSnapshot(pdf, snapshotContext, mergedOptions) + } catch { + return handleMissingSnapshot(pdf, snapshotContext[0], mergedOptions) + } + } + // Check if snapshot exits and handle accordingly try { - // Check if snapshot exits and handle accordingly await access(snapshotContext.path) return compareWithSnapshot(pdf, snapshotContext, mergedOptions) } catch { @@ -163,6 +184,7 @@ type SnapshotContext = { function mergeOptionsWithDefaults(options?: CompareOptions): Required { return { + combinePages: options?.combinePages ?? true, maskRegions: options?.maskRegions ?? (() => []), pdf2PngOptions: options?.pdf2PngOptions ?? { dpi: Dpi.High }, failOnMissingSnapshot: options?.failOnMissingSnapshot ?? false, @@ -180,7 +202,8 @@ export const snapshotsDirName = SNAPSHOTS_DIR_NAME async function createSnapshotContext( snapshotDir: string, snapshotName: string, -): Promise { + options: Required, +): Promise> { const dirPath = path.join(snapshotDir, SNAPSHOTS_DIR_NAME) try { await access(dirPath) @@ -190,6 +213,21 @@ async function createSnapshotContext( const basePath = path.join(dirPath, snapshotName) + // When combinePages is false, we need to create a separate snapshot for each page + if (options.combinePages === false) { + const files = await readdir(dirPath) + return files.filter((file: string) => file.startsWith(snapshotName)).map((file: string) => { + const fileNameWithoutExt = file.substring(0, file.lastIndexOf('.')) + return ({ + name: snapshotName, + dirPath, + path: path.join(dirPath, file), + diffPath: path.join(dirPath, `${fileNameWithoutExt}.diff.png`), + newPath: path.join(dirPath, `${fileNameWithoutExt}.new.png`), + }) + }) + } + return { name: snapshotName, dirPath, @@ -202,7 +240,7 @@ async function createSnapshotContext( async function handleMissingSnapshot( pdf: string | Buffer, snapshotContext: SnapshotContext, - { failOnMissingSnapshot, maskRegions, pdf2PngOptions }: Required, + { combinePages, failOnMissingSnapshot, maskRegions, pdf2PngOptions }: Required, ): Promise { if (failOnMissingSnapshot) { return false @@ -210,32 +248,50 @@ async function handleMissingSnapshot( // Generate snapshot if missing const images = await pdf2png(pdf, pdf2PngOptions).then(maskImgWithRegions(maskRegions)) - await writeImages(snapshotContext.path)(images) + await writeImages(snapshotContext.path, combinePages)(images) return true } -async function compareWithSnapshot( - pdf: string | Buffer, +async function compareContext( snapshotContext: SnapshotContext, - { maskRegions, pdf2PngOptions, tolerance }: Required, -): Promise { - const images = await pdf2png(pdf, pdf2PngOptions).then(maskImgWithRegions(maskRegions)) + images: ReadonlyArray, + { combinePages, tolerance }: Required +) { const result = await compareImages(snapshotContext.path, images, { tolerance }) if (result.equal) { await removeIfExists(snapshotContext.diffPath) await removeIfExists(snapshotContext.newPath) - return true } - await writeImages(snapshotContext.newPath)(images) - await writeImages(snapshotContext.diffPath)(result.diffs.map((x) => x.diff)) + await writeImages(snapshotContext.newPath, combinePages)(images) + await writeImages(snapshotContext.diffPath, combinePages)(result.diffs.map((x) => x.diff)) return false } +async function compareWithSnapshot( + pdf: string | Buffer, + snapshotContext: SnapshotContext | Array, + options: Required, +): Promise { + const { maskRegions, pdf2PngOptions } = options; + const images = await pdf2png(pdf, pdf2PngOptions).then(maskImgWithRegions(maskRegions)) + + if (Array.isArray(snapshotContext)) { + let results: Array> = []; + for (let i = 0, l = snapshotContext.length; i < l; i++) { + results.push(compareContext(snapshotContext[i], [images[i]], options)); + } + + return (await Promise.all(results)).every((result) => result) + } + + return await compareContext(snapshotContext, images, options) +} + async function removeIfExists(filePath: string): Promise { try { await unlink(filePath) diff --git a/src/test-data/pdf2png-expected/__snapshots__/two-page-separate-pages_1.png b/src/test-data/pdf2png-expected/__snapshots__/two-page-separate-pages_1.png new file mode 100644 index 0000000..f13af5a Binary files /dev/null and b/src/test-data/pdf2png-expected/__snapshots__/two-page-separate-pages_1.png differ diff --git a/src/test-data/pdf2png-expected/__snapshots__/two-page-separate-pages_2.png b/src/test-data/pdf2png-expected/__snapshots__/two-page-separate-pages_2.png new file mode 100644 index 0000000..fd5d1c9 Binary files /dev/null and b/src/test-data/pdf2png-expected/__snapshots__/two-page-separate-pages_2.png differ