Blob: docs/VERIFICATION.md
wimp - Verification
The app was verified end to end in a real headless Chromium driven by Playwright, in addition to type checking and production builds. This matters because the critical paths (OffscreenCanvas transform, wasm-vips init, jSquash WASM encoders, Web Worker messaging, cross-origin isolation) only truly exercise at runtime.
Static checks
- npx tsc -> 0 errors (strict, single config).
- npm run build -> succeeds; worker chunk and all codec .wasm assets emit; wasm-vips bundles (vips.wasm ~5.9 MB, lazy-loaded).
- React Compiler -> confirmed active: the production bundle contains useMemoCache and __COMPILER_RUNTIME.
- Tailwind v4 -> custom @theme utilities (bg-surface, text-muted, bg-checker, ring-accent-strong, opacity variants) all present in the built CSS.
Runtime matrix (headless Chromium via Playwright)
Source for these runs: a generated 2000x1500 RGB PNG (a gradient with a grid). Served with npm run preview (COOP/COEP headers on). crossOriginIsolated was true. No console errors and no page errors in any run.
| Scenario | Output format | Dimensions | Engine | Encoder |
|---|---|---|---|---|
| OG image template | JPEG | 1200x630 | vips | jsquash |
| Avatar template | WebP | 512x512 | vips | jsquash |
| Favicon template | PNG | 256x256 | vips | jsquash |
| Favicon, format switched to AVIF | AVIF | 256x256 | vips | jsquash |
| Original (no downscale) | JPEG | 2000x1500 | canvas | jsquash |
| Keyboard rotate ']' + flip 'h' | JPEG | 1500x1125 | canvas | jsquash |
| YouTube thumb template | JPEG | 1280x720 | canvas/vips | jsquash |
Format was confirmed by magic bytes; JPEG dimensions parsed from the SOF marker. The result-card text reported the same dimensions plus the engine and encoder.
Notes:
- vips engages on meaningful downscales (output smaller than the crop) and when cross-origin isolation is available; the "canvas" rows confirm the fallback path is healthy too.
- Exact target dimensions for fixed-output templates prove the transform and output sizing are correct.
Layout (responsive)
- Desktop 1366px: both side trays inline (left x=0, right flush to the right edge), the mobile pill hidden.
- Mobile 390px: trays sit off-canvas at rest (left right-edge at x<=0, right at x>=viewport width); tapping Edit slides the tools drawer to x=0; tapping Export slides the export drawer in; export works from inside the drawer.
Framing guide
- US passport template: one head ellipse rendered, bounded inside the crop area (593x593 in the test viewport), plus an eye line.
- Other templates (e.g. OG): no guide rendered.
A screenshot was captured during these runs showing the full UI with the guide.
Reproducing locally
Prerequisites: a Chromium that Playwright can launch.
npm run build
npm run preview # serves dist/ on http://localhost:4173 with COOP/COEPThen a Playwright script can:
- goto the preview URL and assert self.crossOriginIsolated is true.
- setInputFiles on input[type=file] with an image to load it.
- Open the template picker (button aria-label "Choose template") and click a template by name; or set the format via the Segmented control (role=tab).
- Wait until the "Export & download" button is enabled, click it, and capture the download event. Inspect magic bytes and dimensions.
- Read the result card (the
containing the word "engine") for the reported format, encoder, and engine.
Environment note: in the build environment used here, no browser was on PATH; Playwright was pointed at an existing binary with launch({ executablePath: ... }). A committed E2E suite is listed as deferred work because the browser path is environment-specific (see docs/deferred/enhancements.md).
Known gaps in verification
- Visual quality of the vips vs canvas resample was not pixel-compared; both paths produce correct dimensions and valid files.
- Clipboard Copy and AVIF behavior vary by browser; see docs/deferred/known-limitations.md.
- Tested in Chromium only. Safari-specific canvas filter behavior is documented as a known limitation rather than tested.