import test from 'node:test'; import assert from 'node:assert/strict'; import {readFile, readdir} from 'node:fs/promises'; import {fonts} from '../font-catalog.js'; import {render, registerFont, renderLoaded, resolveFont, FONT_CACHE} from '../renderer.js'; test('catalog includes every bundled FIGlet font', async () => { const files = await readdir(new URL('../fonts/figlet/', import.meta.url)); assert.deepEqual(fonts, files.filter(file => file.endsWith('.flf')).map(file => file.slice(0, -4)).sort()); }); for (const font of fonts) { test(`renders ${font} from its local font file`, async () => { registerFont(font, await readFile(new URL(`../fonts/figlet/${font}.flf`, import.meta.url), 'utf8')); const art = renderLoaded('Hello World! 123', font); assert.equal(typeof art, 'string'); assert.match(art, /\S/); }); } test('Standard preserves lowercase lettering and the familiar FIGlet shape', () => { assert.equal(renderLoaded('Hi', 'Standard'), [ ' _ _ _ ', ' | | | (_)', ' | |_| | |', ' | _ | |', ' |_| |_|_|', ].join('\n')); }); test('old lowercase font links resolve to their library names', () => { for (const [name, expected] of [['standard', 'Standard'], ['slant', 'Slant'], ['block', 'Block'], ['banner', 'Banner'], ['compact', 'Standard'], ['unknown', 'Standard']]) { assert.equal(resolveFont(name), expected); } }); test('missing font requests can be retried', async () => { const originalFetch = globalThis.fetch; const data = await readFile(new URL('../fonts/figlet/Standard.flf', import.meta.url), 'utf8'); const figlet = (await import('../vendor/figlet.js')).default; delete figlet.figFonts.Standard; try { globalThis.fetch = async () => new Response('', {status: 404}); await assert.rejects(render('Hi', 'Standard'), /Cannot load font/); globalThis.fetch = async () => new Response(data); assert.match(await render('Hi', 'Standard'), /\S/); } finally { globalThis.fetch = originalFetch; } }); test('caches only selected fonts and reuses them when offline', async () => { const originalFetch = globalThis.fetch; const originalCaches = globalThis.caches; const entries = new Map(); const figlet = (await import('../vendor/figlet.js')).default; for (const font of ['Standard', 'Slant', 'Small']) delete figlet.figFonts[font]; try { globalThis.caches = { async open(name) { assert.equal(name, FONT_CACHE); return { async match(url) { return entries.get(url)?.clone(); }, async put(url, response) { entries.set(url, response.clone()); }, }; }, }; globalThis.fetch = async url => new Response(await readFile(new URL(url), 'utf8')); await render('Hello', 'Standard'); await render('Hello', 'Slant'); assert.equal(entries.size, 2); assert.ok([...entries.keys()].every(url => /\/(Standard|Slant)\.flf$/.test(url))); delete figlet.figFonts.Standard; globalThis.fetch = async () => { throw new Error('Offline'); }; assert.match(await render('Offline', 'Standard'), /\S/); await assert.rejects(render('Offline', 'Small'), /Offline/); assert.equal(entries.size, 2); } finally { globalThis.fetch = originalFetch; if (originalCaches === undefined) delete globalThis.caches; else globalThis.caches = originalCaches; } }); test('rejects unknown fonts and overlong input', async () => { await assert.rejects(render('Hello', 'unknown'), /Unknown/); await assert.rejects(render('x'.repeat(501)), /too long/); });