@@ -78,6 +78,8 @@ void main() {
78
78
});
79
79
});
80
80
81
+ final popularCandidates = EmojiStore .popularEmojiCandidates;
82
+
81
83
Condition <Object ?> isUnicodeCandidate (String ? emojiCode, List <String >? names) {
82
84
return (it_) {
83
85
final it = it_.isA <EmojiCandidate >();
@@ -108,6 +110,9 @@ void main() {
108
110
..aliases.isEmpty ();
109
111
}
110
112
113
+ List <Condition <Object ?>> arePopularCandidates = popularCandidates.map (
114
+ (c) => isUnicodeCandidate (c.emojiCode, null )).toList ();
115
+
111
116
group ('allEmojiCandidates' , () {
112
117
// TODO test emojiDisplay of candidates matches emojiDisplayFor
113
118
@@ -123,6 +128,40 @@ void main() {
123
128
return store;
124
129
}
125
130
131
+ test ('popular emoji appear even when no server emoji data' , () {
132
+ final store = prepare (unicodeEmoji: null );
133
+ check (store.allEmojiCandidates ()).deepEquals ([
134
+ ...arePopularCandidates,
135
+ isZulipCandidate (),
136
+ ]);
137
+ });
138
+
139
+ test ('popular emoji appear in their canonical order' , () {
140
+ // In the server's emoji data, have the popular emoji in a permuted order,
141
+ // and interspersed with other emoji.
142
+ final store = prepare (unicodeEmoji: {
143
+ '1f603' : ['smiley' ],
144
+ for (final candidate in popularCandidates.skip (3 ))
145
+ candidate.emojiCode: [candidate.emojiName, ...candidate.aliases],
146
+ '1f34a' : ['orange' , 'tangerine' , 'mandarin' ],
147
+ for (final candidate in popularCandidates.take (3 ))
148
+ candidate.emojiCode: [candidate.emojiName, ...candidate.aliases],
149
+ '1f516' : ['bookmark' ],
150
+ });
151
+ // In the allEmojiCandidates result, the popular emoji come first
152
+ // and are in their canonical order, even though the other Unicode emoji
153
+ // are in the same order they were given in.
154
+ check (store.allEmojiCandidates ()).deepEquals ([
155
+ for (final candidate in popularCandidates)
156
+ isUnicodeCandidate (candidate.emojiCode,
157
+ [candidate.emojiName, ...candidate.aliases]),
158
+ isUnicodeCandidate ('1f603' , ['smiley' ]),
159
+ isUnicodeCandidate ('1f34a' , ['orange' , 'tangerine' , 'mandarin' ]),
160
+ isUnicodeCandidate ('1f516' , ['bookmark' ]),
161
+ isZulipCandidate (),
162
+ ]);
163
+ });
164
+
126
165
test ('realm emoji overrides Unicode emoji' , () {
127
166
final store = prepare (realmEmoji: {
128
167
'1' : eg.realmEmojiItem (emojiCode: '1' , emojiName: 'smiley' ),
@@ -131,6 +170,7 @@ void main() {
131
170
'1f603' : ['smiley' ],
132
171
});
133
172
check (store.allEmojiCandidates ()).deepEquals ([
173
+ ...arePopularCandidates,
134
174
isUnicodeCandidate ('1f516' , ['bookmark' ]),
135
175
isRealmCandidate (emojiCode: '1' , emojiName: 'smiley' ),
136
176
isZulipCandidate (),
@@ -144,6 +184,7 @@ void main() {
144
184
'1f34a' : ['orange' , 'tangerine' , 'mandarin' ],
145
185
});
146
186
check (store.allEmojiCandidates ()).deepEquals ([
187
+ ...arePopularCandidates,
147
188
isUnicodeCandidate ('1f34a' , ['orange' , 'mandarin' ]),
148
189
isRealmCandidate (emojiCode: '1' , emojiName: 'tangerine' ),
149
190
isZulipCandidate (),
@@ -157,6 +198,7 @@ void main() {
157
198
'1f34a' : ['orange' , 'tangerine' , 'mandarin' ],
158
199
});
159
200
check (store.allEmojiCandidates ()).deepEquals ([
201
+ ...arePopularCandidates,
160
202
isUnicodeCandidate ('1f34a' , ['tangerine' , 'mandarin' ]),
161
203
isRealmCandidate (emojiCode: '1' , emojiName: 'orange' ),
162
204
isZulipCandidate (),
@@ -166,13 +208,15 @@ void main() {
166
208
test ('updates on setServerEmojiData' , () {
167
209
final store = prepare ();
168
210
check (store.allEmojiCandidates ()).deepEquals ([
211
+ ...arePopularCandidates,
169
212
isZulipCandidate (),
170
213
]);
171
214
172
215
store.setServerEmojiData (ServerEmojiData (codeToNames: {
173
216
'1f516' : ['bookmark' ],
174
217
}));
175
218
check (store.allEmojiCandidates ()).deepEquals ([
219
+ ...arePopularCandidates,
176
220
isUnicodeCandidate ('1f516' , ['bookmark' ]),
177
221
isZulipCandidate (),
178
222
]);
@@ -181,13 +225,15 @@ void main() {
181
225
test ('updates on RealmEmojiUpdateEvent' , () {
182
226
final store = prepare ();
183
227
check (store.allEmojiCandidates ()).deepEquals ([
228
+ ...arePopularCandidates,
184
229
isZulipCandidate (),
185
230
]);
186
231
187
232
store.handleEvent (RealmEmojiUpdateEvent (id: 1 , realmEmoji: {
188
233
'1' : eg.realmEmojiItem (emojiCode: '1' , emojiName: 'happy' ),
189
234
}));
190
235
check (store.allEmojiCandidates ()).deepEquals ([
236
+ ...arePopularCandidates,
191
237
isRealmCandidate (emojiCode: '1' , emojiName: 'happy' ),
192
238
isZulipCandidate (),
193
239
]);
@@ -220,6 +266,9 @@ void main() {
220
266
isZulipCandidate ());
221
267
}
222
268
269
+ List <Condition <Object ?>> arePopularResults = popularCandidates.map (
270
+ (c) => isUnicodeResult (emojiCode: c.emojiCode)).toList ();
271
+
223
272
PerAccountStore prepare ({
224
273
Map <String , String > realmEmoji = const {},
225
274
Map <String , List <String >>? unicodeEmoji,
@@ -245,6 +294,7 @@ void main() {
245
294
await Future (() {});
246
295
check (done).isTrue ();
247
296
check (view.results).deepEquals ([
297
+ ...arePopularResults,
248
298
isRealmResult (emojiName: 'happy' ),
249
299
isZulipResult (),
250
300
isUnicodeResult (names: ['bookmark' ]),
@@ -286,6 +336,45 @@ void main() {
286
336
return view.results;
287
337
}
288
338
339
+ test ('results preserve order of popular emoji within each rank' , () async {
340
+ // In other words, the sorting by rank is a stable sort.
341
+
342
+ // Full results list matches allEmojiCandidates.
343
+ check (prepare ().allEmojiCandidates ())
344
+ .deepEquals ([...arePopularCandidates, isZulipCandidate ()]);
345
+ check (await resultsOf ('' ))
346
+ .deepEquals ([...arePopularResults, isZulipResult ()]);
347
+
348
+ // Same list written out explicitly, for comparison with the cases below.
349
+ check (await resultsOf ('' )).deepEquals ([
350
+ isUnicodeResult (names: ['+1' , 'thumbs_up' , 'like' ]),
351
+ isUnicodeResult (names: ['tada' ]),
352
+ isUnicodeResult (names: ['smile' ]),
353
+ isUnicodeResult (names: ['heart' , 'love' , 'love_you' ]),
354
+ isUnicodeResult (names: ['working_on_it' , 'hammer_and_wrench' , 'tools' ]),
355
+ isUnicodeResult (names: ['octopus' ]),
356
+ isZulipResult (),
357
+ ]);
358
+
359
+ check (await resultsOf ('t' )).deepEquals ([
360
+ // prefix
361
+ isUnicodeResult (names: ['+1' , 'thumbs_up' , 'like' ]),
362
+ isUnicodeResult (names: ['tada' ]),
363
+ isUnicodeResult (names: ['working_on_it' , 'hammer_and_wrench' , 'tools' ]),
364
+ // other
365
+ isUnicodeResult (names: ['heart' , 'love' , 'love_you' ]),
366
+ isUnicodeResult (names: ['octopus' ]),
367
+ ]);
368
+
369
+ check (await resultsOf ('h' )).deepEquals ([
370
+ // prefix
371
+ isUnicodeResult (names: ['heart' , 'love' , 'love_you' ]),
372
+ isUnicodeResult (names: ['working_on_it' , 'hammer_and_wrench' , 'tools' ]),
373
+ // other
374
+ isUnicodeResult (names: ['+1' , 'thumbs_up' , 'like' ]),
375
+ ]);
376
+ });
377
+
289
378
test ('results end-to-end' , () async {
290
379
// (See more detailed rank tests below, on EmojiAutocompleteQuery.)
291
380
@@ -294,6 +383,7 @@ void main() {
294
383
295
384
// Empty query -> base ordering.
296
385
check (await resultsOf ('' , unicodeEmoji: unicodeEmoji)).deepEquals ([
386
+ ...arePopularResults,
297
387
isZulipResult (),
298
388
isUnicodeResult (names: ['notebook' ]),
299
389
isUnicodeResult (names: ['bookmark' ]),
0 commit comments