import { parse } from '../src'; import { ALL_EMOJI, EMOJIBASE_VERSION } from './fixtures/allEmoji'; import { emoji, bigEmoji, paragraph, plain, emojiUnicode } from './helpers'; test.each([ [':smile: asd', [paragraph([emoji('smile'), plain(' asd')])]], [':smile:asd', [paragraph([plain(':smile:asd')])]], ['text:inner:outer', [paragraph([plain('text:inner:outer')])]], ['10:20:30', [paragraph([plain('10:20:30')])]], ['10:20:30:', [paragraph([plain('10:20:30:')])]], ['":smile:"', [paragraph([plain('":smile:"')])]], ['":smile: "', [paragraph([plain('":smile: "')])]], ['" :smile: "', [paragraph([plain('" '), emoji('smile'), plain(' "')])]], [ `:smile: :smile: `, [bigEmoji([emoji('smile'), emoji('smile')])], ], ['asdas :smile: asd', [paragraph([plain('asdas '), emoji('smile'), plain(' asd')])]], [ 'normal emojis :smile: :smile: :smile:', [paragraph([plain('normal emojis '), emoji('smile'), plain(' '), emoji('smile'), plain(' '), emoji('smile')])], ], [':smile::smile::smile:', [bigEmoji([emoji('smile'), emoji('smile'), emoji('smile')])]], [' :smile::smile::smile: ', [bigEmoji([emoji('smile'), emoji('smile'), emoji('smile')])]], ['\n :smile::smile::smile: \n', [bigEmoji([emoji('smile'), emoji('smile'), emoji('smile')])]], [':smile: :smile: :smile:', [bigEmoji([emoji('smile'), emoji('smile'), emoji('smile')])]], [':smile::smile:', [bigEmoji([emoji('smile'), emoji('smile')])]], [':smile:a:smile:', [paragraph([plain(':smile:a:smile:')])]], [':smile:', [bigEmoji([emoji('smile')])]], ['Hi :+1:', [paragraph([plain('Hi '), emoji('+1')])]], ['Hi :+1_tone4:', [paragraph([plain('Hi '), emoji('+1_tone4')])]], ['Hi :thumbsup_tone3:', [paragraph([plain('Hi '), emoji('thumbsup_tone3')])]], ['Hi :yes_tone3:', [paragraph([plain('Hi '), emoji('yes_tone3')])]], ['Hi :clapping_hands_tone2:', [paragraph([plain('Hi '), emoji('clapping_hands_tone2')])]], ['Hi :pray_tone4:', [paragraph([plain('Hi '), emoji('pray_tone4')])]], ['Hi :regional_indicator_a:', [paragraph([plain('Hi '), emoji('regional_indicator_a')])]], ['Hi :digit_zero:', [paragraph([plain('Hi '), emoji('digit_zero')])]], ['Hi :pound_symbol:', [paragraph([plain('Hi '), emoji('pound_symbol')])]], ['Hi :asterisk_symbol:', [paragraph([plain('Hi '), emoji('asterisk_symbol')])]], ['Hi :tone3:', [paragraph([plain('Hi '), emoji('tone3')])]], [':thumbsup_tone3:', [bigEmoji([emoji('thumbsup_tone3')])]], [':regional_indicator_a:', [bigEmoji([emoji('regional_indicator_a')])]], [':digit_zero:', [bigEmoji([emoji('digit_zero')])]], ])('parses %p', (input, output) => { expect(parse(input)).toEqual(output); }); // Tests for unicode emojis test.each([ ['๐Ÿ˜€', [bigEmoji([emojiUnicode('๐Ÿ˜€')])]], ['๐Ÿ˜ƒ', [bigEmoji([emojiUnicode('๐Ÿ˜ƒ')])]], ['๐Ÿฅต', [bigEmoji([emojiUnicode('๐Ÿฅต')])]], ['๐Ÿงฟ', [bigEmoji([emojiUnicode('๐Ÿงฟ')])]], ['๐Ÿถ', [bigEmoji([emojiUnicode('๐Ÿถ')])]], ['๐Ÿ', [bigEmoji([emojiUnicode('๐Ÿ')])]], ['โšฝ', [bigEmoji([emojiUnicode('โšฝ')])]], ['โšฝ๏ธ', [bigEmoji([emojiUnicode('โšฝ๏ธ')])]], ['๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ', [bigEmoji([emojiUnicode('๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ')])]], ['๐Ÿš—', [bigEmoji([emojiUnicode('๐Ÿš—')])]], ['โŒš๏ธ', [bigEmoji([emojiUnicode('โŒš๏ธ')])]], ['โค๏ธ', [bigEmoji([emojiUnicode('โค๏ธ')])]], ['๐Ÿณ๏ธ', [bigEmoji([emojiUnicode('๐Ÿณ๏ธ')])]], ['๐Ÿง‘๐Ÿพโ€๐Ÿ’ป', [bigEmoji([emojiUnicode('๐Ÿง‘๐Ÿพโ€๐Ÿ’ป')])]], ['๐Ÿง‘๐Ÿพโ€๐Ÿ’ป๐Ÿง‘๐Ÿพโ€๐Ÿ’ป', [bigEmoji([emojiUnicode('๐Ÿง‘๐Ÿพโ€๐Ÿ’ป'), emojiUnicode('๐Ÿง‘๐Ÿพโ€๐Ÿ’ป')])]], ['๐Ÿง‘๐Ÿพโ€๐Ÿ’ป๐Ÿง‘๐Ÿพโ€๐Ÿ’ป๐Ÿง‘๐Ÿพโ€๐Ÿ’ป', [bigEmoji([emojiUnicode('๐Ÿง‘๐Ÿพโ€๐Ÿ’ป'), emojiUnicode('๐Ÿง‘๐Ÿพโ€๐Ÿ’ป'), emojiUnicode('๐Ÿง‘๐Ÿพโ€๐Ÿ’ป')])]], ['๐Ÿ‘†๐Ÿฝ', [bigEmoji([emojiUnicode('๐Ÿ‘†๐Ÿฝ')])]], ['๐Ÿ‘†๐Ÿฝ๐Ÿ‘†๐Ÿฝ', [bigEmoji([emojiUnicode('๐Ÿ‘†๐Ÿฝ'), emojiUnicode('๐Ÿ‘†๐Ÿฝ')])]], ['๐Ÿ‘†๐Ÿฝ๐Ÿ‘†๐Ÿฝ๐Ÿ‘†๐Ÿฝ', [bigEmoji([emojiUnicode('๐Ÿ‘†๐Ÿฝ'), emojiUnicode('๐Ÿ‘†๐Ÿฝ'), emojiUnicode('๐Ÿ‘†๐Ÿฝ')])]], ['๐Ÿ‘†๐Ÿบ', [bigEmoji([emojiUnicode('๐Ÿ‘†'), emojiUnicode('๐Ÿบ')])]], ['โœ‹๐Ÿฝ', [bigEmoji([emojiUnicode('โœ‹๐Ÿฝ')])]], ['โœŒ๐Ÿฝ', [bigEmoji([emojiUnicode('โœŒ๐Ÿฝ')])]], ['โœ๐Ÿฝ', [bigEmoji([emojiUnicode('โœ๐Ÿฝ')])]], ['โ˜๐Ÿฝ', [bigEmoji([emojiUnicode('โ˜๐Ÿฝ')])]], ['โ›น๐Ÿฝ', [bigEmoji([emojiUnicode('โ›น๐Ÿฝ')])]], ['โœ‹๐ŸฝโœŒ๐Ÿฝ', [bigEmoji([emojiUnicode('โœ‹๐Ÿฝ'), emojiUnicode('โœŒ๐Ÿฝ')])]], ['โœ‹๐Ÿบ', [bigEmoji([emojiUnicode('โœ‹'), emojiUnicode('๐Ÿบ')])]], ['Hi โœ‹๐Ÿฝ', [paragraph([plain('Hi '), emojiUnicode('โœ‹๐Ÿฝ')])]], ['Hi ๐Ÿ‘', [paragraph([plain('Hi '), emojiUnicode('๐Ÿ‘')])]], // Symbols and Pictographs Extended-A (U+1FA00โ€“U+1FAFF) โ€” Unicode 13+/14+ ['๐Ÿซ ', [bigEmoji([emojiUnicode('๐Ÿซ ')])]], ['๐Ÿซก', [bigEmoji([emojiUnicode('๐Ÿซก')])]], ['๐Ÿซถ', [bigEmoji([emojiUnicode('๐Ÿซถ')])]], ['Hi ๐Ÿซ ', [paragraph([plain('Hi '), emojiUnicode('๐Ÿซ ')])]], // ZWJ sequences involving Dingbats (โค๏ธ U+2764) โ€” couple/kiss emojis ['๐Ÿ‘จโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ', [bigEmoji([emojiUnicode('๐Ÿ‘จโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ')])]], ['๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ', [bigEmoji([emojiUnicode('๐Ÿ‘ฉโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘ฉ')])]], ['๐Ÿ‘จโ€โค๏ธโ€๐Ÿ‘จ', [bigEmoji([emojiUnicode('๐Ÿ‘จโ€โค๏ธโ€๐Ÿ‘จ')])]], ['๐Ÿ’‘', [bigEmoji([emojiUnicode('๐Ÿ’‘')])]], ['Hi ๐Ÿ‘จโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ', [paragraph([plain('Hi '), emojiUnicode('๐Ÿ‘จโ€โค๏ธโ€๐Ÿ’‹โ€๐Ÿ‘จ')])]], // ZWJ sequences involving Misc Symbols (โš•๏ธ, โš–๏ธ, โœˆ๏ธ) โ€” profession emojis ['๐Ÿ‘จโ€โš•๏ธ', [bigEmoji([emojiUnicode('๐Ÿ‘จโ€โš•๏ธ')])]], ['๐Ÿ‘ฉโ€โš–๏ธ', [bigEmoji([emojiUnicode('๐Ÿ‘ฉโ€โš–๏ธ')])]], ['๐Ÿ‘จโ€โœˆ๏ธ', [bigEmoji([emojiUnicode('๐Ÿ‘จโ€โœˆ๏ธ')])]], ['๐Ÿฉท', [bigEmoji([emojiUnicode('๐Ÿฉท')])]], // v15 ['๐Ÿซฑ๐Ÿฟโ€๐Ÿซฒ๐Ÿป', [bigEmoji([emojiUnicode('๐Ÿซฑ๐Ÿฟโ€๐Ÿซฒ๐Ÿป')])]], // v14 ['๐Ÿซฉ', [bigEmoji([emojiUnicode('๐Ÿซฉ')])]], // v16 ['๐Ÿ‡จ๐Ÿ‡ถ', [bigEmoji([emojiUnicode('๐Ÿ‡จ๐Ÿ‡ถ')])]], // v16 // ZWJ sequences anchored by an Emoticon (U+1F600-U+1F64F) ['๐Ÿ˜ถโ€๐ŸŒซ๏ธ', [bigEmoji([emojiUnicode('๐Ÿ˜ถโ€๐ŸŒซ๏ธ')])]], ['๐Ÿ˜ฎโ€๐Ÿ’จ', [bigEmoji([emojiUnicode('๐Ÿ˜ฎโ€๐Ÿ’จ')])]], ['๐Ÿ˜ตโ€๐Ÿ’ซ', [bigEmoji([emojiUnicode('๐Ÿ˜ตโ€๐Ÿ’ซ')])]], ['Hi ๐Ÿ˜ถโ€๐ŸŒซ๏ธ', [paragraph([plain('Hi '), emojiUnicode('๐Ÿ˜ถโ€๐ŸŒซ๏ธ')])]], ['๐Ÿ˜ถโ€๐ŸŒซ๏ธ๐Ÿ˜ฎโ€๐Ÿ’จ', [bigEmoji([emojiUnicode('๐Ÿ˜ถโ€๐ŸŒซ๏ธ'), emojiUnicode('๐Ÿ˜ฎโ€๐Ÿ’จ')])]], ['๐Ÿ™‹๐Ÿฝโ€โ™‚๏ธ', [bigEmoji([emojiUnicode('๐Ÿ™‹๐Ÿฝโ€โ™‚๏ธ')])]], ['๐Ÿ˜€', [bigEmoji([emojiUnicode('๐Ÿ˜€')])]], // standalone emoticon still matches // Emoji tag sequences โ€” England/Scotland/Wales flags ['๐Ÿด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ', [bigEmoji([emojiUnicode('๐Ÿด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ')])]], ['๐Ÿด๓ ง๓ ข๓ ณ๓ ฃ๓ ด๓ ฟ', [bigEmoji([emojiUnicode('๐Ÿด๓ ง๓ ข๓ ณ๓ ฃ๓ ด๓ ฟ')])]], ['๐Ÿด๓ ง๓ ข๓ ท๓ ฌ๓ ณ๓ ฟ', [bigEmoji([emojiUnicode('๐Ÿด๓ ง๓ ข๓ ท๓ ฌ๓ ณ๓ ฟ')])]], ['Hi ๐Ÿด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ', [paragraph([plain('Hi '), emojiUnicode('๐Ÿด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ')])]], ['๐Ÿด', [bigEmoji([emojiUnicode('๐Ÿด')])]], // standalone black flag still matches ['๐Ÿดโ€โ˜ ๏ธ', [bigEmoji([emojiUnicode('๐Ÿดโ€โ˜ ๏ธ')])]], // pirate flag (ZWJ, not tag sequence) still matches // Consecutive sequence emojis โ€” each sequence must be consumed whole, without stealing from its neighbor ['๐Ÿด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ๐Ÿด๓ ง๓ ข๓ ณ๓ ฃ๓ ด๓ ฟ๐Ÿด๓ ง๓ ข๓ ท๓ ฌ๓ ณ๓ ฟ', [bigEmoji([emojiUnicode('๐Ÿด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ'), emojiUnicode('๐Ÿด๓ ง๓ ข๓ ณ๓ ฃ๓ ด๓ ฟ'), emojiUnicode('๐Ÿด๓ ง๓ ข๓ ท๓ ฌ๓ ณ๓ ฟ')])]], ['๐Ÿด๐Ÿด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ', [bigEmoji([emojiUnicode('๐Ÿด'), emojiUnicode('๐Ÿด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ')])]], ['๐Ÿด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ๐Ÿด', [bigEmoji([emojiUnicode('๐Ÿด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ'), emojiUnicode('๐Ÿด')])]], ['๐Ÿ˜ถโ€๐ŸŒซ๏ธ๐Ÿ˜ฎโ€๐Ÿ’จ๐Ÿ˜ตโ€๐Ÿ’ซ', [bigEmoji([emojiUnicode('๐Ÿ˜ถโ€๐ŸŒซ๏ธ'), emojiUnicode('๐Ÿ˜ฎโ€๐Ÿ’จ'), emojiUnicode('๐Ÿ˜ตโ€๐Ÿ’ซ')])]], ['๐Ÿ˜€๐Ÿ˜ถโ€๐ŸŒซ๏ธ', [bigEmoji([emojiUnicode('๐Ÿ˜€'), emojiUnicode('๐Ÿ˜ถโ€๐ŸŒซ๏ธ')])]], ['๐Ÿ˜ถโ€๐ŸŒซ๏ธ๐Ÿด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ๐Ÿดโ€โ˜ ๏ธ', [bigEmoji([emojiUnicode('๐Ÿ˜ถโ€๐ŸŒซ๏ธ'), emojiUnicode('๐Ÿด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ'), emojiUnicode('๐Ÿดโ€โ˜ ๏ธ')])]], // More than 3 emojis: no BigEmoji, plain paragraph of emoji tokens ['๐Ÿ˜ถโ€๐ŸŒซ๏ธ๐Ÿ˜ฎโ€๐Ÿ’จ๐Ÿ˜ตโ€๐Ÿ’ซ๐Ÿ‘', [paragraph([emojiUnicode('๐Ÿ˜ถโ€๐ŸŒซ๏ธ'), emojiUnicode('๐Ÿ˜ฎโ€๐Ÿ’จ'), emojiUnicode('๐Ÿ˜ตโ€๐Ÿ’ซ'), emojiUnicode('๐Ÿ‘')])]], ['England ๐Ÿด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ and Scotland ๐Ÿด๓ ง๓ ข๓ ณ๓ ฃ๓ ด๓ ฟ', [paragraph([plain('England '), emojiUnicode('๐Ÿด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ'), plain(' and Scotland '), emojiUnicode('๐Ÿด๓ ง๓ ข๓ ณ๓ ฃ๓ ด๓ ฟ')])]], ])('parses %p', (input, output) => { expect(parse(input)).toEqual(output); }); // KNOWN_UNSUPPORTED: the 26 regional indicators. Alone they're just letters, not emojis; combined // in a pair they form a flag (๐Ÿ‡บ + ๐Ÿ‡ธ = ๐Ÿ‡บ๐Ÿ‡ธ). Real letter emojis (๐Ÿ…ฐ๏ธ โ“‚๏ธ) are supported. Stale check // below keeps the list honest. // prettier-ignore const KNOWN_UNSUPPORTED = new Set('๐Ÿ‡ฆ ๐Ÿ‡ง ๐Ÿ‡จ ๐Ÿ‡ฉ ๐Ÿ‡ช ๐Ÿ‡ซ ๐Ÿ‡ฌ ๐Ÿ‡ญ ๐Ÿ‡ฎ ๐Ÿ‡ฏ ๐Ÿ‡ฐ ๐Ÿ‡ฑ ๐Ÿ‡ฒ ๐Ÿ‡ณ ๐Ÿ‡ด ๐Ÿ‡ต ๐Ÿ‡ถ ๐Ÿ‡ท ๐Ÿ‡ธ ๐Ÿ‡น ๐Ÿ‡บ ๐Ÿ‡ป ๐Ÿ‡ผ ๐Ÿ‡ฝ ๐Ÿ‡พ ๐Ÿ‡ฟ'.split(' ')); function parsesWhole(input: string): boolean { const nodes = parse(input); if (!Array.isArray(nodes) || nodes.length !== 1) { return false; } const [node] = nodes; if (node.type !== 'BIG_EMOJI' || node.value.length !== 1) { return false; } const [child] = node.value; if (child.type !== 'EMOJI' || !('unicode' in child)) { return false; } return child.unicode === input; } test(`every emoji parses as one token, except documented components (emojibase@${EMOJIBASE_VERSION})`, () => { expect(ALL_EMOJI.length).toBeGreaterThan(3000); const inFixture = new Set(ALL_EMOJI); expect(ALL_EMOJI.filter((e) => !KNOWN_UNSUPPORTED.has(e) && !parsesWhole(e))).toEqual([]); expect([...KNOWN_UNSUPPORTED].filter((e) => !inFixture.has(e) || parsesWhole(e))).toEqual([]); });