diff --git a/frontend/src/lib/document/viewer/PdfViewerFixtures.svelte.test.ts b/frontend/src/lib/document/viewer/PdfViewerFixtures.svelte.test.ts new file mode 100644 index 00000000..2ec78f32 --- /dev/null +++ b/frontend/src/lib/document/viewer/PdfViewerFixtures.svelte.test.ts @@ -0,0 +1,52 @@ +import { describe, it, expect, afterEach, vi } from 'vitest'; +import { cleanup, render } from 'vitest-browser-svelte'; +import PdfViewer from './PdfViewer.svelte'; +import ccittUrl from './fixtures/ccitt-g4.pdf?url'; +import jpegUrl from './fixtures/jpeg-dct.pdf?url'; + +// Behavioral, real-render coverage of the wasm decode path. Unlike the rest of +// the viewer tests, these use the REAL pdf.js loader (no libLoader prop) so the +// page is actually decoded and painted, and the wasm is fetched from +// /pdfjs-wasm/ exactly as in production. CI runs this in a real Chromium. +// See issue #708. + +afterEach(cleanup); + +// A blank page is a uniform white canvas. A rendered page has dark glyph pixels. +function countNonBackgroundPixels(canvas: HTMLCanvasElement): number { + const ctx = canvas.getContext('2d'); + if (!ctx || canvas.width === 0 || canvas.height === 0) return 0; + const { data } = ctx.getImageData(0, 0, canvas.width, canvas.height); + let count = 0; + for (let i = 0; i < data.length; i += 4) { + const r = data[i]; + const g = data[i + 1]; + const b = data[i + 2]; + const a = data[i + 3]; + if (a > 0 && (r < 250 || g < 250 || b < 250)) count++; + } + return count; +} + +async function expectNonBlankRender(url: string): Promise { + render(PdfViewer, { url, documentId: 'fixture' }); + await vi.waitFor( + () => { + const canvas = document.querySelector('canvas'); + expect(canvas).not.toBeNull(); + expect((canvas as HTMLCanvasElement).width).toBeGreaterThan(0); + expect(countNonBackgroundPixels(canvas as HTMLCanvasElement)).toBeGreaterThan(50); + }, + { timeout: 20000, interval: 250 } + ); +} + +describe('PdfViewer — real codec fixtures (wasm decode path)', () => { + it('renders a CCITT (G4 fax) scan as a non-blank page — same jbig2.wasm path JBIG2 uses', async () => { + await expectNonBlankRender(ccittUrl); + }); + + it('renders a DCTDecode (JPEG) PDF as a non-blank page — no regression', async () => { + await expectNonBlankRender(jpegUrl); + }); +}); diff --git a/frontend/src/lib/document/viewer/fixtures/ccitt-g4.pdf b/frontend/src/lib/document/viewer/fixtures/ccitt-g4.pdf new file mode 100644 index 00000000..585893e5 Binary files /dev/null and b/frontend/src/lib/document/viewer/fixtures/ccitt-g4.pdf differ diff --git a/frontend/src/lib/document/viewer/fixtures/jpeg-dct.pdf b/frontend/src/lib/document/viewer/fixtures/jpeg-dct.pdf new file mode 100644 index 00000000..6e20aebe Binary files /dev/null and b/frontend/src/lib/document/viewer/fixtures/jpeg-dct.pdf differ