import { glEmojiTag } from '~/behaviors/gl_emoji'; import { isEmojiUnicodeSupported, isFlagEmoji, isKeycapEmoji, isSkinToneComboEmoji, isHorceRacingSkinToneComboEmoji, isPersonZwjEmoji, } from '~/behaviors/gl_emoji/is_emoji_unicode_supported'; const emptySupportMap = { personZwj: false, horseRacing: false, flag: false, skinToneModifier: false, '9.0': false, '8.0': false, '7.0': false, 6.1: false, '6.0': false, 5.2: false, 5.1: false, 4.1: false, '4.0': false, 3.2: false, '3.0': false, 1.1: false, }; const emojiFixtureMap = { bomb: { name: 'bomb', moji: 'π£', unicodeVersion: '6.0', }, construction_worker_tone5: { name: 'construction_worker_tone5', moji: 'π·πΏ', unicodeVersion: '8.0', }, five: { name: 'five', moji: '5οΈβ£', unicodeVersion: '3.0', }, grey_question: { name: 'grey_question', moji: 'β', unicodeVersion: '6.0', }, }; function markupToDomElement(markup) { const div = document.createElement('div'); div.innerHTML = markup; return div.firstElementChild; } function testGlEmojiImageFallback(element, name, src) { expect(element.tagName.toLowerCase()).toBe('img'); expect(element.getAttribute('src')).toBe(src); expect(element.getAttribute('title')).toBe(`:${name}:`); expect(element.getAttribute('alt')).toBe(`:${name}:`); } const defaults = { forceFallback: false, sprite: false, }; function testGlEmojiElement(element, name, unicodeVersion, unicodeMoji, options = {}) { const opts = Object.assign({}, defaults, options); expect(element.tagName.toLowerCase()).toBe('gl-emoji'); expect(element.dataset.name).toBe(name); expect(element.dataset.fallbackSrc.length).toBeGreaterThan(0); expect(element.dataset.unicodeVersion).toBe(unicodeVersion); const fallbackSpriteClass = `emoji-${name}`; if (opts.sprite) { expect(element.dataset.fallbackSpriteClass).toBe(fallbackSpriteClass); } if (opts.forceFallback && opts.sprite) { expect(element.getAttribute('class')).toBe(`emoji-icon ${fallbackSpriteClass}`); } if (opts.forceFallback && !opts.sprite) { // Check for image fallback testGlEmojiImageFallback(element.firstElementChild, name, element.dataset.fallbackSrc); } else { // Otherwise make sure things are still unicode text expect(element.textContent.trim()).toBe(unicodeMoji); } } describe('gl_emoji', () => { describe('glEmojiTag', () => { it('bomb emoji', () => { const emojiKey = 'bomb'; const markup = glEmojiTag(emojiFixtureMap[emojiKey].name); const glEmojiElement = markupToDomElement(markup); testGlEmojiElement( glEmojiElement, emojiFixtureMap[emojiKey].name, emojiFixtureMap[emojiKey].unicodeVersion, emojiFixtureMap[emojiKey].moji, ); }); it('bomb emoji with image fallback', () => { const emojiKey = 'bomb'; const markup = glEmojiTag(emojiFixtureMap[emojiKey].name, { forceFallback: true, }); const glEmojiElement = markupToDomElement(markup); testGlEmojiElement( glEmojiElement, emojiFixtureMap[emojiKey].name, emojiFixtureMap[emojiKey].unicodeVersion, emojiFixtureMap[emojiKey].moji, { forceFallback: true, }, ); }); it('bomb emoji with sprite fallback readiness', () => { const emojiKey = 'bomb'; const markup = glEmojiTag(emojiFixtureMap[emojiKey].name, { sprite: true, }); const glEmojiElement = markupToDomElement(markup); testGlEmojiElement( glEmojiElement, emojiFixtureMap[emojiKey].name, emojiFixtureMap[emojiKey].unicodeVersion, emojiFixtureMap[emojiKey].moji, { sprite: true, }, ); }); it('bomb emoji with sprite fallback', () => { const emojiKey = 'bomb'; const markup = glEmojiTag(emojiFixtureMap[emojiKey].name, { forceFallback: true, sprite: true, }); const glEmojiElement = markupToDomElement(markup); testGlEmojiElement( glEmojiElement, emojiFixtureMap[emojiKey].name, emojiFixtureMap[emojiKey].unicodeVersion, emojiFixtureMap[emojiKey].moji, { forceFallback: true, sprite: true, }, ); }); it('question mark when invalid emoji name given', () => { const name = 'invalid_emoji'; const emojiKey = 'grey_question'; const markup = glEmojiTag(name); const glEmojiElement = markupToDomElement(markup); testGlEmojiElement( glEmojiElement, emojiFixtureMap[emojiKey].name, emojiFixtureMap[emojiKey].unicodeVersion, emojiFixtureMap[emojiKey].moji, ); }); it('question mark with image fallback when invalid emoji name given', () => { const name = 'invalid_emoji'; const emojiKey = 'grey_question'; const markup = glEmojiTag(name, { forceFallback: true, }); const glEmojiElement = markupToDomElement(markup); testGlEmojiElement( glEmojiElement, emojiFixtureMap[emojiKey].name, emojiFixtureMap[emojiKey].unicodeVersion, emojiFixtureMap[emojiKey].moji, { forceFallback: true, }, ); }); }); describe('isFlagEmoji', () => { it('should detect flag_ac', () => { expect(isFlagEmoji('π¦π¨')).toBeTruthy(); }); it('should detect flag_us', () => { expect(isFlagEmoji('πΊπΈ')).toBeTruthy(); }); it('should detect flag_zw', () => { expect(isFlagEmoji('πΏπΌ')).toBeTruthy(); }); it('should not detect flags', () => { expect(isFlagEmoji('π')).toBeFalsy(); }); it('should not detect triangular_flag_on_post', () => { expect(isFlagEmoji('π©')).toBeFalsy(); }); it('should not detect single letter', () => { expect(isFlagEmoji('π¦')).toBeFalsy(); }); it('should not detect >2 letters', () => { expect(isFlagEmoji('π¦π§π¨')).toBeFalsy(); }); }); describe('isKeycapEmoji', () => { it('should detect one(keycap)', () => { expect(isKeycapEmoji('1οΈβ£')).toBeTruthy(); }); it('should detect nine(keycap)', () => { expect(isKeycapEmoji('9οΈβ£')).toBeTruthy(); }); it('should not detect ten(keycap)', () => { expect(isKeycapEmoji('π')).toBeFalsy(); }); it('should not detect hash(keycap)', () => { expect(isKeycapEmoji('#β£')).toBeFalsy(); }); }); describe('isSkinToneComboEmoji', () => { it('should detect hand_splayed_tone5', () => { expect(isSkinToneComboEmoji('ππΏ')).toBeTruthy(); }); it('should not detect hand_splayed', () => { expect(isSkinToneComboEmoji('π')).toBeFalsy(); }); it('should detect lifter_tone1', () => { expect(isSkinToneComboEmoji('ππ»')).toBeTruthy(); }); it('should not detect lifter', () => { expect(isSkinToneComboEmoji('π')).toBeFalsy(); }); it('should detect rowboat_tone4', () => { expect(isSkinToneComboEmoji('π£πΎ')).toBeTruthy(); }); it('should not detect rowboat', () => { expect(isSkinToneComboEmoji('π£')).toBeFalsy(); }); it('should not detect individual tone emoji', () => { expect(isSkinToneComboEmoji('π»')).toBeFalsy(); }); }); describe('isHorceRacingSkinToneComboEmoji', () => { it('should detect horse_racing_tone2', () => { expect(isHorceRacingSkinToneComboEmoji('ππΌ')).toBeTruthy(); }); it('should not detect horse_racing', () => { expect(isHorceRacingSkinToneComboEmoji('π')).toBeFalsy(); }); }); describe('isPersonZwjEmoji', () => { it('should detect couple_mm', () => { expect(isPersonZwjEmoji('π¨ββ€οΈβπ¨')).toBeTruthy(); }); it('should not detect couple_with_heart', () => { expect(isPersonZwjEmoji('π')).toBeFalsy(); }); it('should not detect couplekiss', () => { expect(isPersonZwjEmoji('π')).toBeFalsy(); }); it('should detect family_mmb', () => { expect(isPersonZwjEmoji('π¨βπ¨βπ¦')).toBeTruthy(); }); it('should detect family_mwgb', () => { expect(isPersonZwjEmoji('π¨βπ©βπ§βπ¦')).toBeTruthy(); }); it('should not detect family', () => { expect(isPersonZwjEmoji('πͺ')).toBeFalsy(); }); it('should detect kiss_ww', () => { expect(isPersonZwjEmoji('π©ββ€οΈβπβπ©')).toBeTruthy(); }); it('should not detect girl', () => { expect(isPersonZwjEmoji('π§')).toBeFalsy(); }); it('should not detect girl_tone5', () => { expect(isPersonZwjEmoji('π§πΏ')).toBeFalsy(); }); it('should not detect man', () => { expect(isPersonZwjEmoji('π¨')).toBeFalsy(); }); it('should not detect woman', () => { expect(isPersonZwjEmoji('π©')).toBeFalsy(); }); }); describe('isEmojiUnicodeSupported', () => { it('bomb(6.0) with 6.0 support', () => { const emojiKey = 'bomb'; const unicodeSupportMap = Object.assign({}, emptySupportMap, { '6.0': true, }); const isSupported = isEmojiUnicodeSupported( unicodeSupportMap, emojiFixtureMap[emojiKey].moji, emojiFixtureMap[emojiKey].unicodeVersion, ); expect(isSupported).toBeTruthy(); }); it('bomb(6.0) without 6.0 support', () => { const emojiKey = 'bomb'; const unicodeSupportMap = emptySupportMap; const isSupported = isEmojiUnicodeSupported( unicodeSupportMap, emojiFixtureMap[emojiKey].moji, emojiFixtureMap[emojiKey].unicodeVersion, ); expect(isSupported).toBeFalsy(); }); it('bomb(6.0) without 6.0 but with 9.0 support', () => { const emojiKey = 'bomb'; const unicodeSupportMap = Object.assign({}, emptySupportMap, { '9.0': true, }); const isSupported = isEmojiUnicodeSupported( unicodeSupportMap, emojiFixtureMap[emojiKey].moji, emojiFixtureMap[emojiKey].unicodeVersion, ); expect(isSupported).toBeFalsy(); }); it('construction_worker_tone5(8.0) without skin tone modifier support', () => { const emojiKey = 'construction_worker_tone5'; const unicodeSupportMap = Object.assign({}, emptySupportMap, { skinToneModifier: false, '9.0': true, '8.0': true, '7.0': true, 6.1: true, '6.0': true, 5.2: true, 5.1: true, 4.1: true, '4.0': true, 3.2: true, '3.0': true, 1.1: true, }); const isSupported = isEmojiUnicodeSupported( unicodeSupportMap, emojiFixtureMap[emojiKey].moji, emojiFixtureMap[emojiKey].unicodeVersion, ); expect(isSupported).toBeFalsy(); }); it('use native keycap on >=57 chrome', () => { const emojiKey = 'five'; const unicodeSupportMap = Object.assign({}, emptySupportMap, { '3.0': true, meta: { isChrome: true, chromeVersion: 57, }, }); const isSupported = isEmojiUnicodeSupported( unicodeSupportMap, emojiFixtureMap[emojiKey].moji, emojiFixtureMap[emojiKey].unicodeVersion, ); expect(isSupported).toBeTruthy(); }); it('fallback keycap on <57 chrome', () => { const emojiKey = 'five'; const unicodeSupportMap = Object.assign({}, emptySupportMap, { '3.0': true, meta: { isChrome: true, chromeVersion: 50, }, }); const isSupported = isEmojiUnicodeSupported( unicodeSupportMap, emojiFixtureMap[emojiKey].moji, emojiFixtureMap[emojiKey].unicodeVersion, ); expect(isSupported).toBeFalsy(); }); }); });