1+ import { processFrequentlyUsedEmojis } from '@libs/EmojiUtils' ;
2+ import type { FrequentlyUsedEmoji } from '@src/types/onyx' ;
3+
4+ // Mock the Emojis module
5+ jest . mock ( '@assets/emojis' , ( ) => ( {
6+ emojiCodeTableWithSkinTones : {
7+ // eslint-disable-next-line @typescript-eslint/naming-convention
8+ '😀' : {
9+ code : '😀' ,
10+ name : 'grinning_face' ,
11+ keywords : [ 'face' , 'grin' , 'grinning' ] ,
12+ } ,
13+ // eslint-disable-next-line @typescript-eslint/naming-convention
14+ '😃' : {
15+ code : '😃' ,
16+ name : 'grinning_face_with_big_eyes' ,
17+ keywords : [ 'face' , 'grin' , 'grinning' ] ,
18+ } ,
19+ // eslint-disable-next-line @typescript-eslint/naming-convention
20+ '😄' : {
21+ code : '😄' ,
22+ name : 'grinning_face_with_smiling_eyes' ,
23+ keywords : [ 'face' , 'grin' , 'grinning' ] ,
24+ } ,
25+ // eslint-disable-next-line @typescript-eslint/naming-convention
26+ '👋' : {
27+ code : '👋' ,
28+ name : 'waving_hand' ,
29+ keywords : [ 'hand' , 'wave' , 'waving' ] ,
30+ } ,
31+ // eslint-disable-next-line @typescript-eslint/naming-convention
32+ '👍' : {
33+ code : '👍' ,
34+ name : 'thumbs_up' ,
35+ keywords : [ 'hand' , 'thumb' , 'up' ] ,
36+ } ,
37+ } ,
38+ emojiNameTable : {
39+ // eslint-disable-next-line @typescript-eslint/naming-convention
40+ 'grinning_face' : {
41+ code : '😀' ,
42+ name : 'grinning_face' ,
43+ keywords : [ 'face' , 'grin' , 'grinning' ] ,
44+ } ,
45+ // eslint-disable-next-line @typescript-eslint/naming-convention
46+ 'waving_hand' : {
47+ code : '👋' ,
48+ name : 'waving_hand' ,
49+ keywords : [ 'hand' , 'wave' , 'waving' ] ,
50+ } ,
51+ // eslint-disable-next-line @typescript-eslint/naming-convention
52+ 'thumbs_up' : {
53+ code : '👍' ,
54+ name : 'thumbs_up' ,
55+ keywords : [ 'hand' , 'thumb' , 'up' ] ,
56+ } ,
57+ } ,
58+ } ) ) ;
59+
60+ describe ( 'processFrequentlyUsedEmojis' , ( ) => {
61+ it ( 'should return empty array when input is undefined' , ( ) => {
62+ const result = processFrequentlyUsedEmojis ( undefined ) ;
63+ expect ( result ) . toEqual ( [ ] ) ;
64+ } ) ;
65+
66+ it ( 'should return empty array when input is empty array' , ( ) => {
67+ const result = processFrequentlyUsedEmojis ( [ ] ) ;
68+ expect ( result ) . toEqual ( [ ] ) ;
69+ } ) ;
70+
71+ it ( 'should process valid emoji list correctly' , ( ) => {
72+ const input : FrequentlyUsedEmoji [ ] = [
73+ {
74+ code : '😀' ,
75+ name : 'grinning_face' ,
76+ count : 5 ,
77+ lastUpdatedAt : 1000 ,
78+ } ,
79+ {
80+ code : '👋' ,
81+ name : 'waving_hand' ,
82+ count : 3 ,
83+ lastUpdatedAt : 2000 ,
84+ } ,
85+ ] ;
86+
87+ const result = processFrequentlyUsedEmojis ( input ) ;
88+
89+ expect ( result ) . toHaveLength ( 2 ) ;
90+ expect ( result . at ( 0 ) ) . toEqual ( {
91+ code : '😀' ,
92+ name : 'grinning_face' ,
93+ keywords : [ 'face' , 'grin' , 'grinning' ] ,
94+ count : 5 ,
95+ lastUpdatedAt : 1000 ,
96+ } ) ;
97+ expect ( result . at ( 1 ) ) . toEqual ( {
98+ code : '👋' ,
99+ name : 'waving_hand' ,
100+ keywords : [ 'hand' , 'wave' , 'waving' ] ,
101+ count : 3 ,
102+ lastUpdatedAt : 2000 ,
103+ } ) ;
104+ } ) ;
105+
106+ it ( 'should fill in missing code using name lookup' , ( ) => {
107+ const input : FrequentlyUsedEmoji [ ] = [
108+ {
109+ code : '' ,
110+ name : 'grinning_face' ,
111+ count : 5 ,
112+ lastUpdatedAt : 1000 ,
113+ } ,
114+ ] ;
115+
116+ const result = processFrequentlyUsedEmojis ( input ) ;
117+
118+ expect ( result ) . toHaveLength ( 1 ) ;
119+ expect ( result . at ( 0 ) ) . toEqual ( {
120+ code : '😀' ,
121+ name : 'grinning_face' ,
122+ keywords : [ 'face' , 'grin' , 'grinning' ] ,
123+ count : 5 ,
124+ lastUpdatedAt : 1000 ,
125+ } ) ;
126+ } ) ;
127+
128+ it ( 'should fill in missing name using code lookup' , ( ) => {
129+ const input : FrequentlyUsedEmoji [ ] = [
130+ {
131+ code : '👋' ,
132+ name : '' ,
133+ count : 3 ,
134+ lastUpdatedAt : 2000 ,
135+ } ,
136+ ] ;
137+
138+ const result = processFrequentlyUsedEmojis ( input ) ;
139+
140+ expect ( result ) . toHaveLength ( 1 ) ;
141+ expect ( result . at ( 0 ) ) . toEqual ( {
142+ code : '👋' ,
143+ name : 'waving_hand' ,
144+ keywords : [ 'hand' , 'wave' , 'waving' ] ,
145+ count : 3 ,
146+ lastUpdatedAt : 2000 ,
147+ } ) ;
148+ } ) ;
149+
150+ it ( 'should filter out emojis that do not exist in emojiCodeTableWithSkinTones' , ( ) => {
151+ const input : FrequentlyUsedEmoji [ ] = [
152+ {
153+ code : '😀' ,
154+ name : 'grinning_face' ,
155+ count : 5 ,
156+ lastUpdatedAt : 1000 ,
157+ } ,
158+ {
159+ code : 'invalid_emoji' ,
160+ name : 'invalid_emoji' ,
161+ count : 3 ,
162+ lastUpdatedAt : 2000 ,
163+ } ,
164+ ] ;
165+
166+ const result = processFrequentlyUsedEmojis ( input ) ;
167+
168+ expect ( result ) . toHaveLength ( 1 ) ;
169+ expect ( result . at ( 0 ) ?. code ) . toBe ( '😀' ) ;
170+ } ) ;
171+
172+ it ( 'should merge duplicate emojis and sum their counts' , ( ) => {
173+ const input : FrequentlyUsedEmoji [ ] = [
174+ {
175+ code : '😀' ,
176+ name : 'grinning_face' ,
177+ count : 5 ,
178+ lastUpdatedAt : 1000 ,
179+ } ,
180+ {
181+ code : '😀' ,
182+ name : 'grinning_face' ,
183+ count : 3 ,
184+ lastUpdatedAt : 2000 ,
185+ } ,
186+ ] ;
187+
188+ const result = processFrequentlyUsedEmojis ( input ) ;
189+
190+ expect ( result ) . toHaveLength ( 1 ) ;
191+ expect ( result . at ( 0 ) ) . toEqual ( {
192+ code : '😀' ,
193+ name : 'grinning_face' ,
194+ keywords : [ 'face' , 'grin' , 'grinning' ] ,
195+ count : 8 ,
196+ lastUpdatedAt : 2000 ,
197+ } ) ;
198+ } ) ;
199+
200+ it ( 'should sort by count in descending order' , ( ) => {
201+ const input : FrequentlyUsedEmoji [ ] = [
202+ {
203+ code : '😀' ,
204+ name : 'grinning_face' ,
205+ count : 3 ,
206+ lastUpdatedAt : 1000 ,
207+ } ,
208+ {
209+ code : '👋' ,
210+ name : 'waving_hand' ,
211+ count : 5 ,
212+ lastUpdatedAt : 2000 ,
213+ } ,
214+ {
215+ code : '👍' ,
216+ name : 'thumbs_up' ,
217+ count : 1 ,
218+ lastUpdatedAt : 3000 ,
219+ } ,
220+ ] ;
221+
222+ const result = processFrequentlyUsedEmojis ( input ) ;
223+
224+ expect ( result ) . toHaveLength ( 3 ) ;
225+ expect ( result . at ( 0 ) ?. code ) . toBe ( '👋' ) ;
226+ expect ( result . at ( 1 ) ?. code ) . toBe ( '😀' ) ;
227+ expect ( result . at ( 2 ) ?. code ) . toBe ( '👍' ) ;
228+ } ) ;
229+
230+ it ( 'should sort by lastUpdatedAt in descending order when counts are equal' , ( ) => {
231+ const input : FrequentlyUsedEmoji [ ] = [
232+ {
233+ code : '😀' ,
234+ name : 'grinning_face' ,
235+ count : 5 ,
236+ lastUpdatedAt : 1000 ,
237+ } ,
238+ {
239+ code : '👋' ,
240+ name : 'waving_hand' ,
241+ count : 5 ,
242+ lastUpdatedAt : 3000 ,
243+ } ,
244+ {
245+ code : '👍' ,
246+ name : 'thumbs_up' ,
247+ count : 5 ,
248+ lastUpdatedAt : 2000 ,
249+ } ,
250+ ] ;
251+
252+ const result = processFrequentlyUsedEmojis ( input ) ;
253+
254+ expect ( result ) . toHaveLength ( 3 ) ;
255+ expect ( result . at ( 0 ) ?. code ) . toBe ( '👋' ) ;
256+ expect ( result . at ( 1 ) ?. code ) . toBe ( '👍' ) ;
257+ expect ( result . at ( 2 ) ?. code ) . toBe ( '😀' ) ;
258+ } ) ;
259+
260+ it ( 'should handle emojis with skin tones correctly' , ( ) => {
261+ const input : FrequentlyUsedEmoji [ ] = [
262+ {
263+ code : '😀' ,
264+ name : 'grinning_face' ,
265+ count : 5 ,
266+ lastUpdatedAt : 1000 ,
267+ types : [ '😀' , '😀🏻' , '😀🏼' ] ,
268+ } ,
269+ ] ;
270+
271+ const result = processFrequentlyUsedEmojis ( input ) ;
272+
273+ expect ( result ) . toHaveLength ( 1 ) ;
274+ expect ( result . at ( 0 ) ) . toEqual ( {
275+ code : '😀' ,
276+ name : 'grinning_face' ,
277+ keywords : [ 'face' , 'grin' , 'grinning' ] ,
278+ count : 5 ,
279+ lastUpdatedAt : 1000 ,
280+ types : [ '😀' , '😀🏻' , '😀🏼' ] ,
281+ } ) ;
282+ } ) ;
283+
284+ it ( 'should handle complex scenario with mixed data quality' , ( ) => {
285+ const input : FrequentlyUsedEmoji [ ] = [
286+ {
287+ code : '😀' ,
288+ name : 'grinning_face' ,
289+ count : 5 ,
290+ lastUpdatedAt : 1000 ,
291+ } ,
292+ {
293+ code : '' ,
294+ name : 'waving_hand' ,
295+ count : 3 ,
296+ lastUpdatedAt : 2000 ,
297+ } ,
298+ {
299+ code : '👍' ,
300+ name : '' ,
301+ count : 7 ,
302+ lastUpdatedAt : 3000 ,
303+ } ,
304+ {
305+ code : '😀' ,
306+ name : 'grinning_face' ,
307+ count : 2 ,
308+ lastUpdatedAt : 1500 ,
309+ } ,
310+ {
311+ code : 'invalid_emoji' ,
312+ name : 'invalid_emoji' ,
313+ count : 1 ,
314+ lastUpdatedAt : 4000 ,
315+ } ,
316+ ] ;
317+
318+ const result = processFrequentlyUsedEmojis ( input ) ;
319+
320+ expect ( result ) . toHaveLength ( 3 ) ;
321+
322+ expect ( result . at ( 0 ) ?. code ) . toBe ( '👍' ) ;
323+ expect ( result . at ( 1 ) ?. code ) . toBe ( '😀' ) ;
324+ expect ( result . at ( 2 ) ?. code ) . toBe ( '👋' ) ;
325+ expect ( result . at ( 1 ) ?. count ) . toBe ( 7 ) ;
326+ expect ( result . at ( 1 ) ?. lastUpdatedAt ) . toBe ( 1500 ) ;
327+ } ) ;
328+ } ) ;
0 commit comments