@@ -177,6 +177,77 @@ void main() {
177
177
178
178
debugNetworkImageHttpClientProvider = null ;
179
179
});
180
+
181
+ testWidgets ('user options appear in the correct rendering order and do not scroll down' , (tester) async {
182
+ final user1 = eg.user (userId: 1 , fullName: 'Aaditya' , avatarUrl: 'user1.png' );
183
+ final user2 = eg.user (userId: 2 , fullName: 'Alya' , avatarUrl: 'user2.png' );
184
+ final user3 = eg.user (userId: 3 , fullName: 'Aman' , avatarUrl: 'user3.png' );
185
+ final user4 = eg.user (userId: 4 , fullName: 'Anders' , avatarUrl: 'user4.png' );
186
+ final user5 = eg.user (userId: 5 , fullName: 'Anthony' , avatarUrl: 'user5.png' );
187
+ final user6 = eg.user (userId: 6 , fullName: 'Apoorva' , avatarUrl: 'user6.png' );
188
+ final user7 = eg.user (userId: 7 , fullName: 'Asif' , avatarUrl: 'user7.png' );
189
+ final user8 = eg.user (userId: 8 , fullName: 'Asim' , avatarUrl: 'user8.png' );
190
+
191
+ final composeInputFinder = await setupToComposeInput (tester, users: [user1,user2,user3,user4,user5,user6,user7,user8]);
192
+ final store = await testBinding.globalStore.perAccount (eg.selfAccount.id);
193
+
194
+ final expectedUserSequence = [user1,user2,user3,user4,user5,user6,user7,user8];
195
+ // Options are filtered correctly for query
196
+ // TODO(#226): Remove this extra edit when this bug is fixed.
197
+ await tester.enterText (composeInputFinder, 'hello @' );
198
+ await tester.pumpAndSettle ();
199
+ await tester.enterText (composeInputFinder, 'hello @A' );
200
+ await tester.pumpAndSettle ();
201
+ //only first seven users render initially, 8th user has to be accessed by scrolling up
202
+ for (int i = 0 ;i < 7 ;i++ ){
203
+ final user = expectedUserSequence[i];
204
+ checkUserShown (user, store, expected: true );
205
+ }
206
+ for (int i = 7 ; i < expectedUserSequence.length;i++ ){
207
+ final user = expectedUserSequence[i];
208
+ checkUserShown (user, store, expected: false );
209
+ }
210
+ final listViewFinder = find.byType (ListView );
211
+ expect (listViewFinder, findsOneWidget, reason: 'ListView should be rendered' );
212
+
213
+ final positions = expectedUserSequence.asMap ().entries
214
+ .where ((entry) {
215
+ final index = entry.key;
216
+ return index < 7 ; // Filter only visible users.
217
+ })
218
+ .map ((entry) {
219
+ final index = entry.key;
220
+ final user = entry.value;
221
+ final finder = find.text (user.fullName);
222
+ expect (finder, findsOneWidget, reason: 'Each user option should be rendered (index: $index )' );
223
+ return tester.getTopLeft (finder).dy;
224
+ }).toList ();
225
+
226
+ final initialScrollOffset = tester.getTopLeft (listViewFinder).dy;
227
+ await tester.drag (listViewFinder, const Offset (0 , - 50 ));
228
+ await tester.pumpAndSettle ();
229
+ final scrollOffsetAfterDragDown = tester.getTopLeft (listViewFinder).dy;
230
+
231
+ expect (scrollOffsetAfterDragDown, initialScrollOffset,
232
+ reason: 'ListView should not scroll down because it is already at the bottom' );
233
+
234
+ for (int i = 0 ;i < 6 ;i++ ){
235
+ expect (positions[i] > positions[i + 1 ], isTrue,
236
+ reason: '${expectedUserSequence [i + 1 ]} should appear above ${expectedUserSequence [i ]} because of reverse order' );
237
+ }
238
+
239
+ await tester.drag (listViewFinder, const Offset (0 , 200 )); //should be capped at prev position
240
+ await tester.pumpAndSettle ();
241
+
242
+ checkUserShown (user8, store, expected: true );
243
+ checkUserShown (user1, store, expected: false );
244
+
245
+ //8th user should be above 7th user
246
+ expect (tester.getTopLeft (find.text (user8.fullName)).dy < tester.getTopLeft (find.text (user7.fullName)).dy,
247
+ isTrue, reason: "8th user should be above 7th user" );
248
+
249
+ debugNetworkImageHttpClientProvider = null ;
250
+ });
180
251
});
181
252
182
253
group ('emoji' , () {
@@ -247,6 +318,101 @@ void main() {
247
318
debugNetworkImageHttpClientProvider = null ;
248
319
});
249
320
321
+ testWidgets ('emoji options appear in the correct rendering order and do not scroll down' , (tester) async {
322
+ final composeInputFinder = await setupToComposeInput (tester);
323
+ final store = await testBinding.globalStore.perAccount (eg.selfAccount.id);
324
+
325
+ store.setServerEmojiData (
326
+ ServerEmojiData (
327
+ codeToNames: {
328
+ '1f4a4' : ['zzz' , 'sleepy' ], // Unicode emoji for "zzz"
329
+ '1f52a' : ['biohazard' ],
330
+ '1f92a' : ['zany_face' ],
331
+ '1f993' : ['zebra' ],
332
+ '0030-fe0f-20e3' : ['zero' ],
333
+ '1f9d0' : ['zombie' ],
334
+ },
335
+ ),
336
+ );
337
+ await store.handleEvent (
338
+ RealmEmojiUpdateEvent (
339
+ id: 1 ,
340
+ realmEmoji: {
341
+ '1' : eg.realmEmojiItem (emojiCode: '1' , emojiName: 'buzzing' ),
342
+ },
343
+ ),
344
+ );
345
+
346
+ const zulipOptionLabel = 'zulip' ;
347
+ const zanyFaceOptionLabel = 'zany_face' ;
348
+ const zebraOptionLabel = 'zebra' ;
349
+ const zzzOptionLabel = 'zzz, sleepy' ;
350
+ const unicodeGlyph = '💤' ;
351
+ const zombieOptionLabel = 'zombie' ;
352
+ const zeroOptionLabel = 'zero' ;
353
+ const buzzingOptionLabel = 'buzzing' ;
354
+ const biohazardOptionLabel = 'biohazard' ;
355
+
356
+ // Adjust the order so the best match appears last
357
+ final emojiSequence = [
358
+ zulipOptionLabel,
359
+ zzzOptionLabel,
360
+ unicodeGlyph,
361
+ zanyFaceOptionLabel,
362
+ zebraOptionLabel,
363
+ zeroOptionLabel,
364
+ zombieOptionLabel,
365
+ buzzingOptionLabel,
366
+ // biohazardOptionLabel, this won't be rendered in the list initally since it is the 7th option.
367
+ ];
368
+
369
+ await tester.enterText (composeInputFinder, 'hi :' );
370
+ await tester.enterText (composeInputFinder, 'hi :z' );
371
+ await tester.pumpAndSettle ();
372
+
373
+ final listViewFinder = find.byType (ListView );
374
+ expect (listViewFinder, findsOneWidget, reason: 'ListView should be rendered' );
375
+
376
+ final positions = emojiSequence.map ((icon) {
377
+ final finder = find.text (icon);
378
+ expect (finder, findsOneWidget, reason: 'Each emoji option should be rendered' );
379
+ return tester.getTopLeft (finder).dy;
380
+ }).toList ();
381
+
382
+ for (int i = 0 ; i < positions.length - 1 ; i++ ) {
383
+ expect (positions[i] > positions[i + 1 ], isTrue,
384
+ reason: '${emojiSequence [i + 1 ]} should appear above ${emojiSequence [i ]} because of reverse order' );
385
+ }
386
+
387
+ final initialScrollOffset = tester.getTopLeft (listViewFinder).dy;
388
+ await tester.drag (listViewFinder, const Offset (0 , - 50 ));
389
+ await tester.pumpAndSettle ();
390
+ final scrollOffsetAfterDragDown = tester.getTopLeft (listViewFinder).dy;
391
+
392
+ expect (scrollOffsetAfterDragDown, initialScrollOffset,
393
+ reason: 'ListView should not scroll down because it is already at the bottom' );
394
+
395
+ final biohazardFinder = find.text (biohazardOptionLabel);
396
+ expect (biohazardFinder, findsNothing, reason: 'The biohazard emoji should not be visible before scrolling up' );
397
+
398
+ // Scroll up
399
+ await tester.drag (listViewFinder, const Offset (0 , 50 ));
400
+ await tester.pumpAndSettle ();
401
+
402
+ expect (biohazardFinder, findsOneWidget, reason: 'The biohazard emoji should be visible after scrolling up' );
403
+
404
+ final firstEmojiPositionAfterScrollUp = tester.getTopLeft (find.text (emojiSequence[0 ])).dy;
405
+ // print(firstEmojiPositionAfterScrollUp);
406
+ // print(positions[0]);
407
+ expect (firstEmojiPositionAfterScrollUp >= positions[0 ], isTrue,
408
+ reason: 'Scrolling up should reveal other emoji matches' );
409
+
410
+ expect (tester.getTopLeft (find.text (biohazardOptionLabel)).runtimeType,const Offset (1 ,2 ).runtimeType, reason: "7th best result should only be visible on scrolling up" );
411
+
412
+ debugNetworkImageHttpClientProvider = null ;
413
+
414
+ });
415
+
250
416
testWidgets ('text emoji means just show text' , (tester) async {
251
417
final composeInputFinder = await setupToComposeInput (tester);
252
418
final store = await testBinding.globalStore.perAccount (eg.selfAccount.id);
0 commit comments