Initial release of Web Tools 0.0.5
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
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('尺寸无效')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user