@@ -51,6 +51,15 @@ void main() {
51
51
await tester.pumpAndSettle ();
52
52
}
53
53
54
+ List <StreamMessage > generateStreamMessages ({
55
+ required ZulipStream stream,
56
+ required int count,
57
+ required List <MessageFlag > flags,
58
+ }) {
59
+ return List .generate (count, (index) => eg.streamMessage (
60
+ stream: stream, topic: '${stream .name } topic $index ' , flags: flags));
61
+ }
62
+
54
63
/// Set up an inbox view with lots of interesting content.
55
64
Future <void > setupVarious (WidgetTester tester) async {
56
65
final stream1 = eg.stream (streamId: 1 , name: 'stream 1' );
@@ -61,12 +70,16 @@ void main() {
61
70
await setupPage (tester,
62
71
streams: [stream1, stream2],
63
72
subscriptions: [sub1, sub2],
64
- users: [eg.selfUser, eg.otherUser, eg.thirdUser],
73
+ users: [eg.selfUser, eg.otherUser, eg.thirdUser, eg.fourthUser ],
65
74
unreadMessages: [
66
75
eg.streamMessage (stream: stream1, topic: 'specific topic' , flags: []),
76
+ ...generateStreamMessages (stream: stream1, count: 5 , flags: []),
67
77
eg.streamMessage (stream: stream2, flags: []),
78
+ ...generateStreamMessages (stream: stream2, count: 40 , flags: []),
68
79
eg.dmMessage (from: eg.otherUser, to: [eg.selfUser], flags: []),
69
80
eg.dmMessage (from: eg.otherUser, to: [eg.selfUser, eg.thirdUser], flags: []),
81
+ eg.dmMessage (from: eg.thirdUser, to: [eg.selfUser], flags: []),
82
+ eg.dmMessage (from: eg.fourthUser, to: [eg.selfUser], flags: []),
70
83
]);
71
84
}
72
85
@@ -252,6 +265,28 @@ void main() {
252
265
|| widget.icon == ZulipIcons .arrow_right))));
253
266
}
254
267
268
+ Future <void > dragUntilInvisible (
269
+ WidgetTester tester,
270
+ FinderBase <Element > finder,
271
+ FinderBase <Element > view,
272
+ Offset moveStep, {
273
+ int maxIteration = 50 ,
274
+ Duration duration = const Duration (milliseconds: 50 ),
275
+ }) {
276
+ return TestAsyncUtils .guard <void >(() async {
277
+ final iteration = maxIteration;
278
+ while (maxIteration > 0 && finder.evaluate ().isNotEmpty) {
279
+ await tester.drag (view, moveStep);
280
+ await tester.pump (duration);
281
+ maxIteration -= 1 ;
282
+ }
283
+ if (maxIteration <= 0 && finder.evaluate ().isNotEmpty) {
284
+ throw StateError (
285
+ 'Finder is still visible after $iteration iterations. Consider increasing the number of iterations.' );
286
+ }
287
+ });
288
+ }
289
+
255
290
group ('all-DMs section' , () {
256
291
Future <void > tapCollapseIcon (WidgetTester tester) async {
257
292
final headerRow = findAllDmsHeaderRow (tester);
@@ -310,6 +345,30 @@ void main() {
310
345
checkAppearsUncollapsed (tester, findSectionContent);
311
346
});
312
347
348
+ testWidgets ('collapse all-DMs section after scroll' , (tester) async {
349
+ await setupVarious (tester);
350
+
351
+ final listFinder = find.byType (Scrollable );
352
+ final findSectionContent =
353
+ find.text (eg.otherUser.fullName).hitTestable ();
354
+
355
+ // Scroll the [StickyHeaderListView] enough so that
356
+ // the [_AllDmsSection] shows a sticky header
357
+ await dragUntilInvisible (
358
+ tester, findSectionContent, listFinder, const Offset (0 , - 50 ));
359
+
360
+ Widget ? headerRow = findAllDmsHeaderRow (tester);
361
+ // Check that the header is present (which in this case
362
+ // is a sticky one as we've scrolled enough)
363
+ check (headerRow).isNotNull ();
364
+
365
+ await tapCollapseIcon (tester);
366
+ // Check that the header is still visible even after
367
+ // collapsing the section
368
+ headerRow = findAllDmsHeaderRow (tester);
369
+ check (headerRow).isNotNull ();
370
+ });
371
+
313
372
// TODO check it remains collapsed even if you scroll far away and back
314
373
315
374
// TODO check that it's always uncollapsed when it appears after being
@@ -385,6 +444,45 @@ void main() {
385
444
checkAppearsUncollapsed (tester, 1 , findSectionContent);
386
445
});
387
446
447
+ testWidgets ('collapse stream section after scroll' , (tester) async {
448
+ await setupVarious (tester);
449
+
450
+ final topicFinder = find.text ('stream 1 topic 2' ).hitTestable ();
451
+ final listFinder = find.byType (Scrollable );
452
+
453
+ // Scroll the [StickyHeaderListView] enough so that
454
+ // the [_StreamSection] shows a sticky header
455
+ await dragUntilInvisible (
456
+ tester, topicFinder, listFinder, const Offset (0 , - 100 ));
457
+
458
+ Widget ? headerRow = findStreamHeaderRow (tester, 1 );
459
+ // Check that the header is present (which in this case
460
+ // is a sticky one as we've scrolled enough)
461
+ check (headerRow).isNotNull ();
462
+
463
+ await tapCollapseIcon (tester, 1 );
464
+ // Check that the header is still visible even after
465
+ // collapsing the section
466
+ headerRow = findStreamHeaderRow (tester, 1 );
467
+ check (headerRow).isNotNull ();
468
+ });
469
+
470
+ testWidgets ('collapse stream section in the middle of screen' , (tester) async {
471
+ await setupVarious (tester);
472
+
473
+ Widget ? headerRow = findStreamHeaderRow (tester, 1 );
474
+ final rectBeforeTap = tester.getRect (find.byWidget (headerRow! ));
475
+
476
+ check (headerRow).isNotNull ();
477
+
478
+ await tapCollapseIcon (tester, 1 );
479
+
480
+ headerRow = findStreamHeaderRow (tester, 1 );
481
+ final rectAfterTap = tester.getRect (find.byWidget (headerRow! ));
482
+
483
+ check (rectAfterTap).equals (rectBeforeTap);
484
+ });
485
+
388
486
// TODO check it remains collapsed even if you scroll far away and back
389
487
390
488
// TODO check that it's always uncollapsed when it appears after being
0 commit comments