import { describe, expect, it } from "vitest"; import { base64ToBytes, base64UrlToBytes, bytesToBase64, bytesToBase64Url, bytesToHex, convertLineEndings, decodeText, decodeUrlComponent, encodeText, encodeUrlComponent, fromCodePoints, hexToBytes, inspectCodePoints, inspectUrl, normalizeUnicode, parseFormEncoded, segmentGraphemes, stringifyFormEncoded, transformCase, } from "../../src/helpers"; describe("binary and text encoding", () => { it("round-trips canonical Base64, Base64URL, hex, and text encodings", () => { const bytes = encodeText("Hello, ๐ŸŒ", "utf-8"); expect(bytesToBase64(bytes)).toBe("SGVsbG8sIPCfjI0="); expect(decodeText(base64ToBytes("SGVsbG8sIPCfjI0="))).toBe("Hello, ๐ŸŒ"); expect(bytesToBase64Url(new Uint8Array([251, 255]), false)).toBe("-_8"); expect(base64UrlToBytes("-_8")).toEqual(new Uint8Array([251, 255])); expect(base64UrlToBytes("Zg==")).toEqual(new Uint8Array([102])); expect(bytesToHex(bytes).startsWith("48656c6c6f")).toBe(true); expect(hexToBytes("0x00 ff", { allowWhitespace: true })).toEqual( new Uint8Array([0, 255]), ); expect(decodeText(encodeText("Aรฉ", "latin1"), "latin1")).toBe("Aรฉ"); expect(decodeText(encodeText("A๐ŸŒ", "utf-16le"), "utf-16le")).toBe("A๐ŸŒ"); expect(decodeText(encodeText("A๐ŸŒ", "utf-16be"), "utf-16be")).toBe("A๐ŸŒ"); }); it("rejects ambiguous, malformed, and oversized encodings", () => { expect(() => base64ToBytes("Zg")).toThrow(/padding/u); expect(() => base64ToBytes("Zh==")).toThrow(/unused|canonical/u); expect(() => base64UrlToBytes("+w")).toThrow(/Invalid/u); expect(() => base64UrlToBytes("Zg=")).toThrow(/padding/u); expect(() => base64ToBytes("Z g==")).toThrow(/whitespace/u); expect(() => hexToBytes("abc")).toThrow(/pairs/u); expect(() => hexToBytes("ffff", { maxOutputBytes: 1 })).toThrow(/limit/u); expect(() => encodeText("โ‚ฌ", "latin1")).toThrow(/Latin-1/u); }); }); describe("URL, Unicode, case, and line helpers", () => { it("handles URL components and repeated form fields without object coercion", () => { expect(decodeUrlComponent(encodeUrlComponent("a b/โœ“"))).toBe("a b/โœ“"); const entries = parseFormEncoded("tag=one&tag=two+words&empty="); expect(entries).toEqual([ ["tag", "one"], ["tag", "two words"], ["empty", ""], ]); expect(stringifyFormEncoded(entries)).toBe("tag=one&tag=two+words&empty="); expect( inspectUrl("../a?q=1#x", "https://example.test/base/"), ).toMatchObject({ href: "https://example.test/a?q=1#x", protocol: "https:", hostname: "example.test", passwordPresent: false, }); }); it("inspects scalar values and user-perceived graphemes", () => { const points = inspectCodePoints("A๐ŸŒ"); expect(points).toEqual([ { character: "A", codePoint: 65, hex: "U+0041", utf16Index: 0, utf16Length: 1, }, { character: "๐ŸŒ", codePoint: 0x1f30d, hex: "U+1F30D", utf16Index: 1, utf16Length: 2, }, ]); expect(fromCodePoints(points.map((point) => point.codePoint))).toBe("A๐ŸŒ"); expect(segmentGraphemes("๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆeฬ")).toHaveLength(2); expect(normalizeUnicode("e\u0301", "NFC")).toBe("รฉ"); expect(() => fromCodePoints([0xd800])).toThrow(/scalar/u); expect(fromCodePoints(new Array(100_000).fill(0x61))).toHaveLength(100_000); }); it("transforms case and line endings deterministically", () => { expect(transformCase("XML http value", "camel")).toBe("xmlHttpValue"); expect(transformCase("hello-world", "pascal")).toBe("HelloWorld"); expect(transformCase("helloWorld", "snake")).toBe("hello_world"); expect(convertLineEndings("a\r\nb\rc\n", "lf")).toBe("a\nb\nc\n"); expect(convertLineEndings("a\nb", "crlf")).toBe("a\r\nb"); }); });