/* * Copyright (C) 2026 Fluxer Contributors * * This file is part of Fluxer. * * Fluxer is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Fluxer is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Fluxer. If not, see . */ import {isValidSingleUnicodeEmoji} from '@fluxer/schema/src/primitives/EmojiValidators'; import {describe, expect, it} from 'vitest'; describe('isValidSingleUnicodeEmoji', () => { describe('valid single emojis', () => { it('accepts simple emoji', () => { expect(isValidSingleUnicodeEmoji('๐Ÿ‘')).toBe(true); }); it('accepts common face emojis', () => { expect(isValidSingleUnicodeEmoji('๐Ÿ˜€')).toBe(true); expect(isValidSingleUnicodeEmoji('๐Ÿ˜‚')).toBe(true); expect(isValidSingleUnicodeEmoji('๐Ÿฅบ')).toBe(true); expect(isValidSingleUnicodeEmoji('๐Ÿ˜ก')).toBe(true); expect(isValidSingleUnicodeEmoji('๐Ÿค”')).toBe(true); }); it('accepts emoji with skin tone modifier', () => { expect(isValidSingleUnicodeEmoji('๐Ÿ‘๐Ÿฟ')).toBe(true); expect(isValidSingleUnicodeEmoji('๐Ÿ‘๐Ÿป')).toBe(true); expect(isValidSingleUnicodeEmoji('๐Ÿ‘๐Ÿฝ')).toBe(true); }); it('accepts ZWJ sequence emojis', () => { expect(isValidSingleUnicodeEmoji('๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ')).toBe(true); expect(isValidSingleUnicodeEmoji('๐Ÿ‘ฉโ€๐Ÿ’ป')).toBe(true); expect(isValidSingleUnicodeEmoji('๐Ÿง‘โ€๐ŸŽ„')).toBe(true); }); it('accepts ZWJ sequence with skin tone at correct position', () => { expect(isValidSingleUnicodeEmoji('๐Ÿง‘๐Ÿฟโ€๐ŸŽ„')).toBe(true); expect(isValidSingleUnicodeEmoji('๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ’ป')).toBe(true); }); it('accepts flag emojis', () => { expect(isValidSingleUnicodeEmoji('๐Ÿ‡บ๐Ÿ‡ธ')).toBe(true); expect(isValidSingleUnicodeEmoji('๐Ÿ‡ฌ๐Ÿ‡ง')).toBe(true); expect(isValidSingleUnicodeEmoji('๐Ÿณ๏ธโ€๐ŸŒˆ')).toBe(true); }); it('accepts single regional indicator symbols', () => { expect(isValidSingleUnicodeEmoji('๐Ÿ‡ฆ')).toBe(true); expect(isValidSingleUnicodeEmoji('๐Ÿ‡ง')).toBe(true); expect(isValidSingleUnicodeEmoji('๐Ÿ‡ต')).toBe(true); expect(isValidSingleUnicodeEmoji('๐Ÿ‡ฟ')).toBe(true); }); it('accepts all 26 regional indicator symbols', () => { for (let cp = 0x1f1e6; cp <= 0x1f1ff; cp++) { expect(isValidSingleUnicodeEmoji(String.fromCodePoint(cp))).toBe(true); } }); it('accepts keycap emojis', () => { expect(isValidSingleUnicodeEmoji('1๏ธโƒฃ')).toBe(true); expect(isValidSingleUnicodeEmoji('#๏ธโƒฃ')).toBe(true); expect(isValidSingleUnicodeEmoji('*๏ธโƒฃ')).toBe(true); expect(isValidSingleUnicodeEmoji('0๏ธโƒฃ')).toBe(true); expect(isValidSingleUnicodeEmoji('9๏ธโƒฃ')).toBe(true); }); it('accepts variation selector emojis', () => { expect(isValidSingleUnicodeEmoji('โค๏ธ')).toBe(true); expect(isValidSingleUnicodeEmoji('โ˜€๏ธ')).toBe(true); }); it('accepts text-style emojis without variation selector', () => { expect(isValidSingleUnicodeEmoji('โค')).toBe(true); expect(isValidSingleUnicodeEmoji('โ˜€')).toBe(true); expect(isValidSingleUnicodeEmoji('โ˜บ')).toBe(true); }); it('accepts copyright, registered, and trademark symbols', () => { expect(isValidSingleUnicodeEmoji('ยฉ')).toBe(true); expect(isValidSingleUnicodeEmoji('ยฉ๏ธ')).toBe(true); expect(isValidSingleUnicodeEmoji('ยฎ')).toBe(true); expect(isValidSingleUnicodeEmoji('ยฎ๏ธ')).toBe(true); expect(isValidSingleUnicodeEmoji('โ„ข')).toBe(true); expect(isValidSingleUnicodeEmoji('โ„ข๏ธ')).toBe(true); }); it('accepts animal and nature emojis', () => { expect(isValidSingleUnicodeEmoji('๐Ÿฑ')).toBe(true); expect(isValidSingleUnicodeEmoji('๐ŸŒธ')).toBe(true); expect(isValidSingleUnicodeEmoji('๐ŸŒ')).toBe(true); }); it('accepts food and object emojis', () => { expect(isValidSingleUnicodeEmoji('๐Ÿ•')).toBe(true); expect(isValidSingleUnicodeEmoji('๐ŸŽธ')).toBe(true); expect(isValidSingleUnicodeEmoji('๐Ÿ’Ž')).toBe(true); }); it('accepts symbol emojis', () => { expect(isValidSingleUnicodeEmoji('โœ…')).toBe(true); expect(isValidSingleUnicodeEmoji('โŒ')).toBe(true); expect(isValidSingleUnicodeEmoji('โš ๏ธ')).toBe(true); expect(isValidSingleUnicodeEmoji('๐Ÿ’ฏ')).toBe(true); }); }); describe('invalid inputs', () => { it('rejects empty string', () => { expect(isValidSingleUnicodeEmoji('')).toBe(false); }); it('rejects plain text', () => { expect(isValidSingleUnicodeEmoji('hello')).toBe(false); expect(isValidSingleUnicodeEmoji('abc')).toBe(false); }); it('rejects single ascii characters', () => { expect(isValidSingleUnicodeEmoji('a')).toBe(false); expect(isValidSingleUnicodeEmoji('1')).toBe(false); expect(isValidSingleUnicodeEmoji('#')).toBe(false); expect(isValidSingleUnicodeEmoji(' ')).toBe(false); }); it('rejects multiple emojis', () => { expect(isValidSingleUnicodeEmoji('๐Ÿ‘๐Ÿ‘')).toBe(false); expect(isValidSingleUnicodeEmoji('๐ŸŽ‰๐ŸŽŠ')).toBe(false); expect(isValidSingleUnicodeEmoji('๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ง')).toBe(false); }); it('rejects multiple regional indicator symbols', () => { expect(isValidSingleUnicodeEmoji('\u{1F1E6}\u{1F1E7}')).toBe(false); }); it('rejects emoji with trailing text', () => { expect(isValidSingleUnicodeEmoji('๐Ÿ‘abc')).toBe(false); expect(isValidSingleUnicodeEmoji('๐ŸŽ‰!')).toBe(false); }); it('rejects emoji with leading text', () => { expect(isValidSingleUnicodeEmoji('abc๐Ÿ‘')).toBe(false); expect(isValidSingleUnicodeEmoji('!๐ŸŽ‰')).toBe(false); }); it('rejects unicode characters that are not emoji', () => { expect(isValidSingleUnicodeEmoji('รฉ')).toBe(false); expect(isValidSingleUnicodeEmoji('ไธญ')).toBe(false); expect(isValidSingleUnicodeEmoji('ฮฑ')).toBe(false); }); it('rejects regional indicator with trailing text', () => { expect(isValidSingleUnicodeEmoji('\u{1F1F5}abc')).toBe(false); }); it('rejects regional indicator with leading text', () => { expect(isValidSingleUnicodeEmoji('abc\u{1F1F5}')).toBe(false); }); }); describe('malformed emoji sequences', () => { it('rejects skin tone at wrong position in ZWJ sequence', () => { expect(isValidSingleUnicodeEmoji('๐Ÿง‘โ€๐ŸŽ„๐Ÿฟ')).toBe(false); }); it('accepts standalone skin tone modifier as valid emoji', () => { expect(isValidSingleUnicodeEmoji('๐Ÿฟ')).toBe(true); expect(isValidSingleUnicodeEmoji('๐Ÿป')).toBe(true); }); it('rejects standalone ZWJ character', () => { expect(isValidSingleUnicodeEmoji('\u200D')).toBe(false); }); it('rejects emoji followed by standalone skin tone', () => { expect(isValidSingleUnicodeEmoji('๐ŸŽ„๐Ÿฟ')).toBe(false); }); it('rejects double skin tone modifiers', () => { expect(isValidSingleUnicodeEmoji('๐Ÿ‘๐Ÿฟ๐Ÿป')).toBe(false); }); }); });