import { existsSync } from 'node:fs'; import { resolve } from 'node:path'; import { describe, expect, it, vi } from 'vitest'; import { ICONS, INKSCAPE_CANDIDATES, exportArgs, findInkscape, makeIcons, } from './make-icons.mjs'; /** * The PNG icons are rendered from SVG by Inkscape rather than checked in by * hand, so the manifest, the service worker and the HTML can keep pointing at * the same filenames while the mark changes. These tests pin the plan (which * file comes from which source, at what size) and the Inkscape invocation; * they never run Inkscape. */ const ROOT = resolve(__dirname, '..'); describe('ICONS', () => { it('covers every raster icon the app references, from a source that exists', () => { const outs = ICONS.map((i) => i.out); expect(outs).toEqual( expect.arrayContaining([ 'public/favicon-32.png', 'public/apple-touch-icon.png', 'public/icon-192.png', 'public/icon-512.png', 'public/icon-512-maskable.png', ]), ); for (const icon of ICONS) { expect(existsSync(resolve(ROOT, icon.source)), icon.source).toBe(true); } }); it('renders the tab favicon from the bare mark and the app icons from the opaque source', () => { const favicon = ICONS.find((i) => i.out === 'public/favicon-32.png'); expect(favicon?.source).toBe('public/favicon.svg'); expect(favicon?.size).toBe(32); for (const out of ['public/apple-touch-icon.png', 'public/icon-192.png', 'public/icon-512.png']) { expect(ICONS.find((i) => i.out === out)?.source).toBe('scripts/icons/app-icon.svg'); } expect(ICONS.find((i) => i.out === 'public/icon-512-maskable.png')?.source).toBe( 'scripts/icons/app-icon-maskable.svg', ); }); }); describe('findInkscape', () => { it('prefers an explicit INKSCAPE variable', () => { expect(findInkscape({ INKSCAPE: 'D:\\tools\\inkscape.com' }, () => true)).toBe( 'D:\\tools\\inkscape.com', ); }); it('falls back to the first standard install location that exists', () => { const second = INKSCAPE_CANDIDATES[1]!; expect(findInkscape({}, (p: string) => p === second)).toBe(second); }); it('falls back to PATH lookup when nothing is installed in a standard place', () => { expect(findInkscape({}, () => false)).toBe('inkscape'); }); }); describe('exportArgs', () => { it('builds an Inkscape 1.x PNG export at the requested square size', () => { expect(exportArgs({ source: 'in.svg', out: 'out.png', size: 180 })).toEqual([ 'in.svg', '--export-type=png', '--export-filename=out.png', '--export-width=180', '--export-height=180', ]); }); }); describe('makeIcons', () => { it('runs one export per icon, with paths resolved against the project root', () => { const run = vi.fn(); const log = vi.spyOn(console, 'log').mockImplementation(() => {}); try { makeIcons(ROOT, run); } finally { log.mockRestore(); } expect(run).toHaveBeenCalledTimes(ICONS.length); const [, args, opts] = run.mock.calls[0]!; expect(args[0]).toBe(resolve(ROOT, ICONS[0]!.source)); expect(args).toContain(`--export-filename=${resolve(ROOT, ICONS[0]!.out)}`); expect(opts).toEqual({ stdio: 'inherit' }); }); });