import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import fs from 'node:fs/promises'; import path from 'node:path'; import os from 'node:os'; import sharp from 'sharp'; import { registerIcoConverter, encodeIcoFromInput, DEFAULT_ICON_SIZE } from '../../src/converters/ico.js'; import { resolve, listTargetFormats } from '../../src/converters/registry.js'; import { detectInputMime } from '../../src/mime.js'; let tmpDir; beforeAll(async () => { registerIcoConverter(); tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'converter-ico-')); }); afterAll(async () => { await fs.rm(tmpDir, { recursive: true, force: true }); }); describe('ICO converter registration', () => { it('registers ico as both a source and a target for every image format', () => { expect(listTargetFormats('ico')).toContain('png'); expect(listTargetFormats('ico')).toContain('jpg'); expect(listTargetFormats('png')).toContain('ico'); expect(listTargetFormats('jpg')).toContain('ico'); }); }); describe('encodeIcoFromInput', () => { it('defaults to a 256x256 icon when no size is given', async () => { const inputPath = path.join(import.meta.dirname, '..', 'fixtures', 'sample.png'); const icoBuffer = await encodeIcoFromInput(inputPath); const outputPath = path.join(tmpDir, 'default-size.ico'); await fs.writeFile(outputPath, icoBuffer); const detected = await detectInputMime(outputPath); expect(detected.mime).toBe('image/x-icon'); }); it('produces an icon at the requested size', async () => { const inputPath = path.join(import.meta.dirname, '..', 'fixtures', 'sample.png'); const icoBuffer = await encodeIcoFromInput(inputPath, 32); const { decodeIco } = await import('icojs'); const [image] = await decodeIco(icoBuffer, 'image/png'); expect(image.width).toBe(32); expect(image.height).toBe(32); }); }); describe('ico -> image conversion', () => { it('converts an ICO fixture to PNG, picking the largest embedded image', async () => { const source = path.join(import.meta.dirname, '..', 'fixtures', 'sample.png'); const icoPath = path.join(tmpDir, 'multi.ico'); const small = await sharp(source).resize(16, 16).png().toBuffer(); const large = await sharp(source).resize(48, 48).png().toBuffer(); const { encodeIco } = await import('icojs'); await fs.writeFile(icoPath, Buffer.from(await encodeIco([{ buffer: small }, { buffer: large }]))); const outputPath = path.join(tmpDir, 'from-ico.png'); const entry = resolve('ico', 'png'); await entry.convert(icoPath, outputPath); const meta = await sharp(outputPath).metadata(); expect(meta.width).toBe(48); expect(meta.height).toBe(48); }); }); describe('image -> ico conversion', () => { it('converts a PNG fixture to a valid ICO at the requested size', async () => { const inputPath = path.join(import.meta.dirname, '..', 'fixtures', 'sample.png'); const outputPath = path.join(tmpDir, 'output.ico'); const entry = resolve('png', 'ico'); await entry.convert(inputPath, outputPath, { iconSize: 48 }); const detected = await detectInputMime(outputPath); expect(detected.mime).toBe('image/x-icon'); const { decodeIco } = await import('icojs'); const [image] = await decodeIco(await fs.readFile(outputPath), 'image/png'); expect(image.width).toBe(48); }); it('defaults to 256px when no iconSize is given', async () => { const inputPath = path.join(import.meta.dirname, '..', 'fixtures', 'sample.png'); const outputPath = path.join(tmpDir, 'output-default.ico'); const entry = resolve('png', 'ico'); await entry.convert(inputPath, outputPath); const { decodeIco } = await import('icojs'); const [image] = await decodeIco(await fs.readFile(outputPath), 'image/png'); expect(image.width).toBe(DEFAULT_ICON_SIZE); }); });