import { describe, expect, it } from "@suaveplan/testing/runner"; import { sniffBom } from "./sniff-bom.js"; import { sniffHtmlMeta } from "./sniff-html-meta.js"; import { sniffXmlDecl } from "./sniff-xml-decl.js"; describe("sniffBom", () => { it("detects UTF-8 BOM (EF BB BF)", { timeout: 5000 }, () => { const bytes = new Uint8Array([0xef, 0xbb, 0xbf, 0x68, 0x65]); const result = sniffBom(bytes); expect(result?.charset).toBe("utf-8"); expect(result?.bomLength).toBe(3); }); it("detects UTF-16 LE BOM (FF FE)", { timeout: 5000 }, () => { const bytes = new Uint8Array([0xff, 0xfe, 0x68, 0x00]); const result = sniffBom(bytes); expect(result?.charset).toBe("utf-16le"); expect(result?.bomLength).toBe(2); }); it("detects UTF-16 BE BOM (FE FF)", { timeout: 5000 }, () => { const bytes = new Uint8Array([0xfe, 0xff, 0x00, 0x68]); const result = sniffBom(bytes); expect(result?.charset).toBe("utf-16be"); expect(result?.bomLength).toBe(2); }); it("detects UTF-32 LE BOM (FF FE 00 00)", { timeout: 5000 }, () => { const bytes = new Uint8Array([0xff, 0xfe, 0x00, 0x00, 0x68]); const result = sniffBom(bytes); expect(result?.charset).toBe("utf-32le"); expect(result?.bomLength).toBe(4); }); it("detects UTF-32 BE BOM (00 00 FE FF)", { timeout: 5000 }, () => { const bytes = new Uint8Array([0x00, 0x00, 0xfe, 0xff, 0x68]); const result = sniffBom(bytes); expect(result?.charset).toBe("utf-32be"); expect(result?.bomLength).toBe(4); }); it("returns null for no BOM", { timeout: 5000 }, () => { const bytes = new Uint8Array([0x68, 0x65, 0x6c, 0x6c, 0x6f]); expect(sniffBom(bytes)).toBeNull(); }); it("returns null for empty buffer", { timeout: 5000 }, () => { expect(sniffBom(new Uint8Array(0))).toBeNull(); }); it("accepts ArrayBuffer", { timeout: 5000 }, () => { const buf = new Uint8Array([0xef, 0xbb, 0xbf, 0x41]).buffer; expect(sniffBom(buf)?.charset).toBe("utf-8"); }); it("UTF-32 LE takes priority over UTF-16 LE when first 4 bytes are FF FE 00 00", { timeout: 5000, }, () => { const bytes = new Uint8Array([0xff, 0xfe, 0x00, 0x00]); expect(sniffBom(bytes)?.charset).toBe("utf-32le"); }); }); describe("sniffHtmlMeta", () => { it("detects charset from ", { timeout: 5000 }, () => { const html = ''; expect(sniffHtmlMeta(html)).toBe("utf-8"); }); it("detects charset from without quotes", { timeout: 5000, }, () => { const html = ""; expect(sniffHtmlMeta(html)).toBe("utf-8"); }); it("detects charset from http-equiv content-type", { timeout: 5000, }, () => { const html = ''; expect(sniffHtmlMeta(html)).toBe("iso-8859-1"); }); it("returns null when no charset found", { timeout: 5000 }, () => { expect( sniffHtmlMeta("Hello") ).toBeNull(); }); it("only scans first 1024 characters", { timeout: 5000 }, () => { const prefix = "x".repeat(1100); const html = `${prefix}`; expect(sniffHtmlMeta(html)).toBeNull(); }); it("returns lowercase charset", { timeout: 5000 }, () => { expect(sniffHtmlMeta('')).toBe("iso-8859-1"); }); }); describe("sniffXmlDecl", () => { it("detects encoding from XML declaration", { timeout: 5000 }, () => { const xml = ''; expect(sniffXmlDecl(xml)).toBe("utf-8"); }); it("detects encoding with single quotes", { timeout: 5000 }, () => { const xml = ""; expect(sniffXmlDecl(xml)).toBe("iso-8859-1"); }); it("returns null when no encoding attribute", { timeout: 5000 }, () => { const xml = ''; expect(sniffXmlDecl(xml)).toBeNull(); }); it("returns null for non-XML content", { timeout: 5000 }, () => { expect(sniffXmlDecl("not xml")).toBeNull(); }); it("only scans first 256 characters", { timeout: 5000 }, () => { const prefix = "x".repeat(300); const xml = `${prefix}`; expect(sniffXmlDecl(xml)).toBeNull(); }); it("returns lowercase encoding", { timeout: 5000 }, () => { const xml = ''; expect(sniffXmlDecl(xml)).toBe("utf-16"); }); });