File
Blob: tests/client/lib/emoji.dom.test.ts
| 1 | import { beforeEach, describe, expect, it } from "vitest"; |
| 2 | import { emojis as tiptapEmojis } from "@tiptap/extension-emoji"; |
| 3 | import { normalizeEmoji } from "@/client/lib/emoji"; |
| 4 | import { getEmojiAssetUrl } from "@/client/lib/emoji/asset-lookup"; |
| 5 | import { PICKER_EMOJI_ITEMS } from "@/client/lib/emoji/picker-data"; |
| 6 | import { getEmojiSuggestionItems } from "@/client/components/editor/extensions/emoji"; |
| 7 | import { readRecentEmojis, writeRecentEmoji } from "@/client/lib/emoji/recents"; |
| 8 | import { STORAGE_KEYS } from "@/client/lib/constants"; |
| 9 | |
| 10 | describe("emoji helpers", () => { |
| 11 | it("normalizes variation selectors while keeping the correct Apple asset mapping", () => { |
| 12 | expect(normalizeEmoji("☺️")).toBe("☺"); |
| 13 | expect(getEmojiAssetUrl("☺️")).toBe( |
| 14 | "https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/263a-fe0f.png", |
| 15 | ); |
| 16 | }); |
| 17 | |
| 18 | it("resolves Apple assets for skin-tone variants", () => { |
| 19 | expect(getEmojiAssetUrl("👍🏽")).toBe( |
| 20 | "https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/1f44d-1f3fd.png", |
| 21 | ); |
| 22 | expect(getEmojiAssetUrl("🧔🏻♂️")).toBe( |
| 23 | "https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/1f9d4-1f3fb-200d-2642-fe0f.png", |
| 24 | ); |
| 25 | }); |
| 26 | |
| 27 | it("keeps grouped native-fallback emojis eligible for the picker", () => { |
| 28 | expect(PICKER_EMOJI_ITEMS.some((item) => item.emoji === "🧑🩰")).toBe(true); |
| 29 | expect(PICKER_EMOJI_ITEMS.some((item) => item.emoji === "♀")).toBe(true); |
| 30 | }); |
| 31 | |
| 32 | it("keeps component glyphs out of the picker dataset", () => { |
| 33 | expect(PICKER_EMOJI_ITEMS.some((item) => item.group === "components")).toBe(false); |
| 34 | }); |
| 35 | }); |
| 36 | |
| 37 | describe("emoji recents", () => { |
| 38 | beforeEach(() => { |
| 39 | localStorage.clear(); |
| 40 | }); |
| 41 | |
| 42 | it("returns empty when storage is unset", () => { |
| 43 | expect(readRecentEmojis()).toEqual([]); |
| 44 | }); |
| 45 | |
| 46 | it("returns empty when storage holds malformed JSON", () => { |
| 47 | localStorage.setItem(STORAGE_KEYS.EMOJI_RECENTS, "{not-json"); |
| 48 | expect(readRecentEmojis()).toEqual([]); |
| 49 | }); |
| 50 | |
| 51 | it("returns empty when storage holds a non-array", () => { |
| 52 | localStorage.setItem(STORAGE_KEYS.EMOJI_RECENTS, JSON.stringify({ emoji: "👍" })); |
| 53 | expect(readRecentEmojis()).toEqual([]); |
| 54 | }); |
| 55 | |
| 56 | it("filters out non-string entries", () => { |
| 57 | localStorage.setItem(STORAGE_KEYS.EMOJI_RECENTS, JSON.stringify(["👍", 42, null, "🎉", ""])); |
| 58 | expect(readRecentEmojis()).toEqual(["👍", "🎉"]); |
| 59 | }); |
| 60 | |
| 61 | it("prepends new picks and deduplicates earlier entries", () => { |
| 62 | writeRecentEmoji("👍"); |
| 63 | writeRecentEmoji("🎉"); |
| 64 | writeRecentEmoji("👍"); |
| 65 | expect(readRecentEmojis()).toEqual(["👍", "🎉"]); |
| 66 | }); |
| 67 | |
| 68 | it("caps the stored list at 16 entries", () => { |
| 69 | for (let i = 0; i < 20; i++) { |
| 70 | writeRecentEmoji(`e${i}`); |
| 71 | } |
| 72 | const stored = readRecentEmojis(); |
| 73 | expect(stored).toHaveLength(16); |
| 74 | expect(stored[0]).toBe("e19"); |
| 75 | expect(stored[15]).toBe("e4"); |
| 76 | }); |
| 77 | |
| 78 | it("ignores empty-string writes", () => { |
| 79 | writeRecentEmoji("👍"); |
| 80 | writeRecentEmoji(""); |
| 81 | expect(readRecentEmojis()).toEqual(["👍"]); |
| 82 | }); |
| 83 | }); |
| 84 | |
| 85 | describe("emoji suggestions", () => { |
| 86 | it("returns the first eight base suggestions for an empty query", () => { |
| 87 | const results = getEmojiSuggestionItems(tiptapEmojis, ""); |
| 88 | |
| 89 | expect(results).toHaveLength(8); |
| 90 | expect(results.every((item) => !!item.emoji || !!item.fallbackImage)).toBe(true); |
| 91 | }); |
| 92 | |
| 93 | it("finds base emoji matches by shortcode search", () => { |
| 94 | const results = getEmojiSuggestionItems(tiptapEmojis, "thumb"); |
| 95 | |
| 96 | expect(results.length).toBeGreaterThan(0); |
| 97 | expect(results.some((item) => item.shortcodes.includes("thumbsup"))).toBe(true); |
| 98 | }); |
| 99 | }); |