Files

76 lines
3.0 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { normalizeIcoSizes, packIco } from '../src/lib/ico'
import {
MAX_PNG_DIMENSION,
MAX_PNG_FILE_SIZE,
MAX_PNG_PIXELS,
parsePngHeader,
PNG_HEADER_LENGTH,
PNG_SIGNATURE,
} from '../src/lib/png'
function pngHeader(width: number, height: number): Uint8Array {
const bytes = new Uint8Array(PNG_HEADER_LENGTH)
bytes.set(PNG_SIGNATURE)
const view = new DataView(bytes.buffer)
view.setUint32(8, 13, false)
bytes.set([0x49, 0x48, 0x44, 0x52], 12)
view.setUint32(16, width, false)
view.setUint32(20, height, false)
bytes.set([8, 6, 0, 0, 0], 24)
return bytes
}
describe('packIco', () => {
it('writes a valid ICO directory with PNG frame offsets', () => {
const first = Uint8Array.from([1, 2, 3])
const second = Uint8Array.from([4, 5, 6, 7])
const output = packIco([{ size: 32, data: first }, { size: 256, data: second }])
const view = new DataView(output.buffer)
expect(view.getUint16(0, true)).toBe(0)
expect(view.getUint16(2, true)).toBe(1)
expect(view.getUint16(4, true)).toBe(2)
expect(view.getUint8(6)).toBe(32)
expect(view.getUint8(22)).toBe(0)
expect(view.getUint32(14, true)).toBe(3)
expect(view.getUint32(18, true)).toBe(38)
expect(view.getUint32(34, true)).toBe(41)
expect(Array.from(output.slice(38))).toEqual([1, 2, 3, 4, 5, 6, 7])
})
it('rejects missing or invalid frames', () => {
expect(() => packIco([])).toThrow('至少')
expect(() => packIco([{ size: 512, data: Uint8Array.of(1) }])).toThrow('尺寸无效')
})
it('normalizes sizes before allocating canvases', () => {
expect(normalizeIcoSizes([256, 16, 16, 32])).toEqual([16, 32, 256])
expect(() => normalizeIcoSizes([])).toThrow('至少')
expect(() => normalizeIcoSizes([0])).toThrow('尺寸无效')
expect(() => normalizeIcoSizes([16.5])).toThrow('尺寸无效')
expect(() => normalizeIcoSizes(Array.from({ length: 256 }, () => 16))).toThrow('数量过多')
})
})
describe('PNG header validation', () => {
it('reads valid IHDR dimensions', () => {
expect(parsePngHeader(pngHeader(512, 256))).toEqual({ width: 512, height: 256, pixels: 131_072 })
})
it('rejects invalid signatures, truncated headers and missing IHDR chunks', () => {
expect(() => parsePngHeader(new Uint8Array(PNG_HEADER_LENGTH))).toThrow('有效的 PNG')
expect(() => parsePngHeader(pngHeader(1, 1).slice(0, 24), PNG_HEADER_LENGTH)).toThrow('文件头不完整')
const missingIhdr = pngHeader(1, 1)
missingIhdr[12] = 0
expect(() => parsePngHeader(missingIhdr)).toThrow('IHDR')
})
it('enforces compressed size, dimensions and decoded pixel limits', () => {
expect(() => parsePngHeader(pngHeader(1, 1), MAX_PNG_FILE_SIZE + 1)).toThrow('32 MiB')
expect(() => parsePngHeader(pngHeader(MAX_PNG_DIMENSION + 1, 1))).toThrow('宽高')
expect(() => parsePngHeader(pngHeader(8_000, 4_001))).toThrow('总像素')
expect(() => parsePngHeader(pngHeader(0, 1))).toThrow('尺寸无效')
})
})