import { describe, expect, it } from "vitest"; import { inspectGraphemeClusters, joinEmojiWithZwj, setEmojiPresentation, setEmojiSkinTone, } from "../../src/unicode/graphemes"; import { codePointFallbackStatus, codePointLabel, compareNormalization, decodeCodePointList, inspectText, parseCodeChartRange, parseCodePointList, } from "../../src/unicode/model"; describe("Unicode helper integration", () => { it("parses and decodes scalar values", () => { expect(parseCodePointList("U+0041 1F600")).toEqual([0x41, 0x1f600]); expect(decodeCodePointList("41, 1F600")).toBe("A๐Ÿ˜€"); expect(() => parseCodePointList("D800")).toThrow(/scalar value/u); }); it("inspects code points separately from graphemes", () => { const result = inspectText("e\u0301"); expect(result.codePointCount).toBe(2); expect(result.graphemes).toHaveLength(1); expect(result.points[1]?.javascript).toBe("\\u0301"); }); it("compares all normalization forms", () => { const result = compareNormalization("e\u0301"); expect(result.forms.find((item) => item.form === "NFC")?.value).toBe("รฉ"); expect(codePointLabel(0x1f600)).toBe("U+1F600"); }); it("classifies non-character code-space regions explicitly", () => { expect(codePointFallbackStatus(0x0378)).toBe("unassigned"); expect(codePointFallbackStatus(0xe000)).toBe("private-use"); expect(codePointFallbackStatus(0xd800)).toBe("surrogate"); expect(codePointFallbackStatus(0xfdd0)).toBe("noncharacter"); expect(codePointFallbackStatus(0x10ffff)).toBe("noncharacter"); }); it("accepts bounded hexadecimal chart ranges", () => { expect(parseCodeChartRange("U+D7F0", "0xE010")).toEqual({ start: 0xd7f0, end: 0xe010, }); expect(() => parseCodeChartRange("110000", "110001")).toThrow(/U\+10FFFF/u); expect(() => parseCodeChartRange("100", "FF")).toThrow(/must not follow/u); }); it("reports emoji structure at grapheme-cluster boundaries", () => { const result = inspectGraphemeClusters("๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ ๐Ÿ‡ฉ๐Ÿ‡ช 1๏ธโƒฃ"); expect(result[0]).toMatchObject({ value: "๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ", containsZwj: true, extendedPictographic: true, }); expect(result.find((item) => item.value === "๐Ÿ‡ฉ๐Ÿ‡ช")?.regionalIndicators).toBe( 2, ); expect(result.find((item) => item.value === "1๏ธโƒฃ")?.keycap).toBe(true); }); it("edits bounded emoji sequences without claiming RGI validity", () => { expect(setEmojiPresentation("โ˜•", "emoji")).toBe("โ˜•๏ธ"); expect(setEmojiSkinTone("๐Ÿ‘", 1)).toBe("๐Ÿ‘๐Ÿป"); expect(setEmojiSkinTone("โ˜๏ธ", 1)).toBe("โ˜๐Ÿป"); expect(joinEmojiWithZwj(["๐Ÿ‘ฉ", "๐Ÿ’ป"])).toBe("๐Ÿ‘ฉโ€๐Ÿ’ป"); expect(() => setEmojiPresentation("๐Ÿ‘ฉโ€๐Ÿ’ป", "text")).toThrow( /individual components/u, ); expect(() => setEmojiPresentation("๐Ÿ‘๐Ÿป", "text")).toThrow( /sequence-specific/u, ); expect(() => joinEmojiWithZwj(["๐Ÿ‘ฉ"])).toThrow(/2โ€“8/u); }); });