test(document): behavioral CCITT/DCT render fixtures prove the wasm path
Render committed synthetic fixtures through PdfViewer with the REAL pdf.js loader and assert the canvas is non-blank (sampled dark-pixel count). The CCITT (G4 fax) fixture exercises the shared jbig2.wasm decode path — the same module pdf.js uses for JBIG2 — so it transitively covers the JBIG2 acceptance criterion (the archive sample found zero true JBIG2 docs and jbig2enc is unavailable to synthesize one). The JPEG/DCTDecode fixture guards against regressing the natively-decoded path. Verified the CCITT case goes red when wasmUrl is removed. Fixtures are hermetic, committed assets (~2-5 KB each), generated with ImageMagick — never fetched from staging at test time. CI browser mode. Refs #708 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -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<void> {
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
BIN
frontend/src/lib/document/viewer/fixtures/ccitt-g4.pdf
Normal file
BIN
frontend/src/lib/document/viewer/fixtures/ccitt-g4.pdf
Normal file
Binary file not shown.
BIN
frontend/src/lib/document/viewer/fixtures/jpeg-dct.pdf
Normal file
BIN
frontend/src/lib/document/viewer/fixtures/jpeg-dct.pdf
Normal file
Binary file not shown.
Reference in New Issue
Block a user