import { test } from 'node:test'; import assert from 'node:assert/strict'; import { MatrixClient, MatrixEvent } from 'matrix-js-sdk'; import { addRecentEmoji, getRecentEmojis, IRecentEmojiContent } from './recent-emoji'; import { AccountDataEvent } from '../../types/matrix/accountData'; import { emojis, loadEmojiData } from './emoji'; // A Map-backed MatrixClient stub supporting get/setAccountData. const createMx = () => { const store = new Map(); const mx = { getAccountData: (type: string): MatrixEvent | undefined => { if (!store.has(type)) return undefined; const content = store.get(type); return { getContent: () => content } as unknown as MatrixEvent; }, setAccountData: (type: string, content: unknown) => { store.set(type, content); return Promise.resolve({}); }, }; return { mx: mx as unknown as MatrixClient, store }; }; const getStored = (store: Map): IRecentEmojiContent['recent_emoji'] => (store.get(AccountDataEvent.ElementRecentEmoji) as IRecentEmojiContent | undefined)?.recent_emoji; // Emoji data is now loaded lazily; populate `emojis` before the round trips. await loadEmojiData(); // Pick two real unicode emojis to drive add->get round trips. const u1 = emojis[0].unicode; const u2 = emojis[1].unicode; test('getRecentEmojis returns [] when there is no account data', () => { const { mx } = createMx(); assert.deepEqual(getRecentEmojis(mx), []); }); test('getRecentEmojis returns [] when content is not an array', () => { const { mx, store } = createMx(); store.set(AccountDataEvent.ElementRecentEmoji, { recent_emoji: 'nope' }); assert.deepEqual(getRecentEmojis(mx), []); }); test('addRecentEmoji creates an entry with count 1', () => { const { mx, store } = createMx(); addRecentEmoji(mx, u1); assert.deepEqual(getStored(store), [[u1, 1]]); }); test('adding the same emoji again increments its count and keeps it at front', () => { const { mx, store } = createMx(); addRecentEmoji(mx, u1); addRecentEmoji(mx, u1); assert.deepEqual(getStored(store), [[u1, 2]]); }); test('a newly added emoji is promoted to the front', () => { const { mx, store } = createMx(); addRecentEmoji(mx, u1); addRecentEmoji(mx, u2); assert.deepEqual(getStored(store), [ [u2, 1], [u1, 1], ]); }); test('re-adding an existing emoji promotes it to front and increments', () => { const { mx, store } = createMx(); addRecentEmoji(mx, u1); addRecentEmoji(mx, u2); addRecentEmoji(mx, u1); assert.deepEqual(getStored(store), [ [u1, 2], [u2, 1], ]); }); test('list is capped at 100 entries', () => { const { mx, store } = createMx(); for (let i = 0; i < 120; i += 1) { addRecentEmoji(mx, `:emoji-${i}:`); } const stored = getStored(store)!; assert.equal(stored.length, 100); // most recently added is at the front assert.equal(stored[0][0], ':emoji-119:'); }); test('getRecentEmojis resolves stored unicodes to known emoji objects', () => { const { mx } = createMx(); addRecentEmoji(mx, u1); addRecentEmoji(mx, u2); const recent = getRecentEmojis(mx); assert.equal(recent.length, 2); assert.equal(recent[0].unicode, u2); assert.equal(recent[1].unicode, u1); }); test('getRecentEmojis skips unicodes with no matching emoji definition', () => { const { mx } = createMx(); addRecentEmoji(mx, ':not-a-real-emoji:'); addRecentEmoji(mx, u1); const recent = getRecentEmojis(mx); assert.equal(recent.length, 1); assert.equal(recent[0].unicode, u1); }); test('getRecentEmojis respects the limit argument (slice before resolve)', () => { const { mx } = createMx(); addRecentEmoji(mx, u1); addRecentEmoji(mx, u2); const recent = getRecentEmojis(mx, 1); assert.equal(recent.length, 1); assert.equal(recent[0].unicode, u2); });