Root cause: the per-character font probes were inline spans side by side, so Chrome shaped f + i into the fi ligature across them and the lone i probe reported no glyph of ours. Each probe is now its own block. The Google Fonts text= subsets keep the GSUB ligatures; regression test with a real Montserrat text= subset. --help / -h prints the command's usage and exits 0 before any config, render or network; unknown flags exit 2 with the usage (cli/data/flags.json from tools/gen-flags.ts). Command tables moved to cli/commands.ts; ads apple reachable again; shots render --scenes rejects unknown ids at once.
49 lines
3.3 KiB
TypeScript
49 lines
3.3 KiB
TypeScript
// Regression: ligatures must not read as system-font glyphs. STAR DASH's "Outfits, ..." caption failed `shots check`
|
|
// with `i drawn with a system font` although Montserrat drew every glyph: the per-character probe spans sat side by
|
|
// side, Chrome shaped "f" + "i" into the fi ligature across them and the lone "i" span reported no glyph of ours.
|
|
// The fixture is a real Google Fonts css2 `text=` subset of Montserrat (as `shots fonts` downloads it; OFL, see
|
|
// fixtures/fonts/Montserrat-OFL.txt); it keeps the GSUB ligatures (ff, fi, ffi). Skips when no Chromium can launch.
|
|
import { afterAll, describe, expect, test } from 'bun:test';
|
|
import { readFileSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import { measureFonts } from '../cli/shots/glyphs';
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
let browser: any = null;
|
|
let skipWhy = '';
|
|
try {
|
|
const { chromium } = await import('playwright-core');
|
|
try { browser = await chromium.launch({ headless: true }); } catch { browser = await chromium.launch({ headless: true, channel: 'chrome' }); }
|
|
} catch (e) { skipWhy = (e as Error).message.split('\n')[0]!; }
|
|
if (!browser) console.warn(`glyph ligature test skipped: no Chromium could launch (${skipWhy}). Run \`bunx playwright install chromium\`.`);
|
|
|
|
afterAll(async () => { await browser?.close(); }, 30_000);
|
|
|
|
const font = readFileSync(join(import.meta.dir, 'fixtures', 'fonts', 'montserrat-text-subset.woff2')).toString('base64');
|
|
const TEXT = 'Outfits, fit, office, affine';
|
|
|
|
describe.skipIf(!browser)('shots font usage: ligatures', () => {
|
|
for (const weight of [400, 600, 800]) test(`"${TEXT}" in a Montserrat text= subset, weight ${weight}: zero system-font glyphs`, async () => {
|
|
const page = await browser.newPage();
|
|
try {
|
|
await page.setContent(`<style>@font-face { font-family: 'Montserrat'; font-weight: 100 900; font-synthesis: none; src: url(data:font/woff2;base64,${font}) format('woff2'); }
|
|
p { font-family: 'Montserrat'; font-weight: ${weight}; font-size: 48px; } small { font-family: 'Montserrat'; font-weight: ${weight}; }</style>
|
|
<p class="sub">${TEXT}</p><div><small>mounts and vehicles</small></div>`);
|
|
await page.evaluate('document.fonts.ready.then(() => 0)');
|
|
// the subset really ligates: the line has fewer glyphs than characters, all from our font
|
|
const cdp = await page.context().newCDPSession(page);
|
|
await cdp.send('DOM.enable'); await cdp.send('CSS.enable');
|
|
const { root } = await cdp.send('DOM.getDocument', { depth: 0 });
|
|
const { nodeId } = await cdp.send('DOM.querySelector', { nodeId: root.nodeId, selector: 'p' });
|
|
const { fonts } = await cdp.send('CSS.getPlatformFontsForNode', { nodeId }) as { fonts: { isCustomFont: boolean; glyphCount: number }[] };
|
|
await cdp.detach();
|
|
expect(fonts.every(f => f.isCustomFont)).toBe(true);
|
|
expect(fonts.reduce((n, f) => n + f.glyphCount, 0)).toBeLessThan(TEXT.length);
|
|
|
|
const measured = await measureFonts(page, ['Montserrat']);
|
|
expect(measured.map(m => m.el)).toEqual(['p.sub', 'div small']);
|
|
for (const m of measured) expect(Object.keys(m.faces)).toEqual(['Montserrat']);
|
|
expect(await page.evaluate("document.getElementById('sb-glyph-probes')")).toBeNull();
|
|
} finally { await page.close(); }
|
|
}, 30_000);
|
|
});
|