// NoteRender: web-side FEP-9098 emoji + FEP-044f quote rendering (mirrors Shaer). import { test } from 'node:test'; import assert from 'node:assert/strict'; import { emojiMap, emojiHtml, emojiName, parseQuote, escapeHtml } from '../src/services/NoteRender.js'; test('emojiMap normalises both the tag-array and the map form', () => { const fromTags = emojiMap(JSON.stringify([{ type: 'Emoji', name: ':wave:', icon: { url: 'https://s/w.png' } }])); assert.equal(fromTags[':wave:'], 'https://s/w.png'); const fromMap = emojiMap(JSON.stringify({ ':blob:': 'https://s/b.png' })); assert.equal(fromMap[':blob:'], 'https://s/b.png'); assert.deepEqual(emojiMap(null), {}); assert.deepEqual(emojiMap('not json'), {}); }); test('emojiHtml swaps known shortcodes for , leaves tags/attributes/code alone', () => { const html = '

hi :wave: link

:wave:'; const out = emojiHtml(html, JSON.stringify({ ':wave:': 'https://s/w.png' })); assert.ok(out.includes(':wave:')); // code untouched assert.equal((out.match(/ { assert.equal(emojiHtml('

plain

', null), '

plain

'); }); test('emojiName escapes the name then renders its emoji', () => { const out = emojiName('Laura :bongoCat: ', JSON.stringify({ ':bongoCat:': 'https://s/c.png' })); assert.ok(out.includes('<b>')); // HTML-escaped assert.ok(out.includes(') { assert.equal(emojiName('A & B ', null), 'A & B <x>'); }); test('escapeHtml escapes the five entities', () => { assert.equal(escapeHtml(`&<>"'`), '&<>"''); }); test('parseQuote returns the object only with a url', () => { assert.equal(parseQuote(JSON.stringify({ url: 'https://s/q', author: { name: 'A' } })).url, 'https://s/q'); assert.equal(parseQuote(JSON.stringify({ author: { name: 'A' } })), null); // no url assert.equal(parseQuote(null), null); assert.equal(parseQuote('nope'), null); });