import { describe, expect, it } from 'vitest'; import { FONT_FACES, PROOF_SIZE_PX, PROOF_TEXT, WIDTH_DIVERGENCE_EPSILON_PX, faceDescriptor, loadFonts } from './load'; import { ARCHIVO_WOFF2_BASE64 } from './data/archivo.b64'; import { SOURCE_SERIF_4_WOFF2_BASE64 } from './data/source-serif-4.b64'; // woff2 files always start with the 4-byte magic signature "wOF2". const WOFF2_MAGIC = 'wOF2'; function magicBytes(base64: string): string { // 8 base64 chars is a whole multiple of the 4-char/3-byte encoding group, so this // decodes the file's real leading 6 bytes without pulling in a Node Buffer type — // grain's tsconfig deliberately has no @types/node (it's a browser library). return atob(base64.slice(0, 8)).slice(0, 4); } describe('grain self-hosted fonts', () => { it('embeds exactly the two brand faces, self-hosted (no network source)', () => { expect(FONT_FACES).toHaveLength(2); const families = FONT_FACES.map((f) => f.family).sort(); expect(families).toEqual(['Archivo', 'Source Serif 4']); for (const face of FONT_FACES) { expect(face.base64.length).toBeGreaterThan(1000); expect(face.weightRange).toBe('400 700'); } }); it('decodes each embedded base64 payload to real woff2 binary data, not a placeholder', () => { expect(magicBytes(ARCHIVO_WOFF2_BASE64)).toBe(WOFF2_MAGIC); expect(magicBytes(SOURCE_SERIF_4_WOFF2_BASE64)).toBe(WOFF2_MAGIC); }); it('ships no Google Fonts link-tag mechanism (the mechanism this track replaces)', async () => { const loadModule = await import('./load'); expect((loadModule as Record).GOOGLE_FONTS_URL).toBeUndefined(); }); it('proof text and thresholds are sane for a metric-divergence measurement', () => { expect(PROOF_TEXT.length).toBeGreaterThan(20); expect(PROOF_SIZE_PX).toBeGreaterThanOrEqual(24); expect(WIDTH_DIVERGENCE_EPSILON_PX).toBeGreaterThan(0); expect(WIDTH_DIVERGENCE_EPSILON_PX).toBeLessThan(PROOF_SIZE_PX); }); it('constructs every FontFace with display: "block" (migration.md §7) — the FontFace API defaults to "auto" when omitted, so this must be set explicitly', () => { // proveFace() builds `new FontFace(family, url, faceDescriptor(weightRange))` — a real // browser FontFace/document is not available in vitest's node environment (see the // guard-clause test below), so this asserts the exact descriptor object that // reaches the FontFace constructor, not a re-implementation of it. for (const { weightRange } of FONT_FACES) { const descriptor = faceDescriptor(weightRange); expect(descriptor.display).toBe('block'); expect(descriptor.weight).toBe(weightRange); expect(descriptor.style).toBe('normal'); } }); it('degrades to an explicit not-ok report (never throws) outside a DOM environment', async () => { // vitest's default environment is node — no `document`/`FontFace`. This is the // SSR/non-browser guard; the real load + width-divergence proof runs in a real // browser (see the preview/font-proof harness) because jsdom does not implement // real font metrics and a jsdom-only "pass" would be blind to runtime, per // project law. const report = await loadFonts(); expect(report).toEqual({ faces: [], ok: false }); }); });