@@ -10,12 +10,19 @@ import 'package:video_player_platform_interface/video_player_platform_interface.
10
10
import 'package:video_player/video_player.dart' ;
11
11
import 'package:zulip/api/model/model.dart' ;
12
12
import 'package:zulip/model/localizations.dart' ;
13
+ import 'package:zulip/model/narrow.dart' ;
14
+ import 'package:zulip/model/store.dart' ;
13
15
import 'package:zulip/widgets/app.dart' ;
14
16
import 'package:zulip/widgets/content.dart' ;
15
17
import 'package:zulip/widgets/lightbox.dart' ;
18
+ import 'package:zulip/widgets/message_list.dart' ;
19
+ import 'package:zulip/widgets/page.dart' ;
16
20
21
+ import '../api/fake_api.dart' ;
17
22
import '../example_data.dart' as eg;
18
23
import '../model/binding.dart' ;
24
+ import '../model/content_test.dart' ;
25
+ import '../model/test_store.dart' ;
19
26
import '../test_images.dart' ;
20
27
import 'dialog_checks.dart' ;
21
28
import 'test_app.dart' ;
@@ -197,6 +204,37 @@ class FakeVideoPlayerPlatform extends Fake
197
204
void main () {
198
205
TestZulipBinding .ensureInitialized ();
199
206
207
+ late PerAccountStore store;
208
+ late FakeApiConnection connection;
209
+
210
+ Future <void > setupMessageListPage (WidgetTester tester, {
211
+ Narrow narrow = const CombinedFeedNarrow (),
212
+ List <Message >? messages,
213
+ List <ZulipStream >? streams,
214
+
215
+ }) async {
216
+ addTearDown (testBinding.reset);
217
+ final stream = streams? .first ?? eg.stream (streamId: eg.defaultStreamMessageStreamId);
218
+ final subscription = eg.subscription (stream);
219
+ await testBinding.globalStore.add (eg.selfAccount, eg.initialSnapshot (
220
+ streams: streams ?? [stream], subscriptions: [subscription]));
221
+ store = await testBinding.globalStore.perAccount (eg.selfAccount.id);
222
+ connection = store.connection as FakeApiConnection ;
223
+
224
+ await store.addUser (eg.selfUser);
225
+
226
+ prepareBoringImageHttpClient ();
227
+
228
+ connection.prepare (json:
229
+ eg.newestGetMessagesResult (foundOldest: true , messages: messages ?? []).toJson ());
230
+
231
+ await tester.pumpWidget (TestZulipApp (accountId: eg.selfAccount.id,
232
+ child: MessageListPage (initNarrow: narrow)));
233
+
234
+ await tester.pumpAndSettle ();
235
+ debugNetworkImageHttpClientProvider = null ;
236
+ }
237
+
200
238
group ('_ImageLightboxPage' , () {
201
239
final src = Uri .parse ('https://chat.example/lightbox-image.png' );
202
240
@@ -207,14 +245,19 @@ void main() {
207
245
addTearDown (testBinding.reset);
208
246
await testBinding.globalStore.add (eg.selfAccount, eg.initialSnapshot ());
209
247
210
- // ZulipApp instead of TestZulipApp because we need the navigator to push
211
- // the lightbox route. The lightbox page works together with the route;
212
- // it takes the route's entrance animation.
213
- await tester.pumpWidget (const ZulipApp ());
248
+ // ZulipApp instead of TestZulipApp because we need:
249
+ // 1. The navigator to push the lightbox route. The lightbox page works
250
+ // together with the route; it takes the route's entrance animation.
251
+ // 2. The PageRoot widget to provide context for Hero animations between
252
+ // the message list and lightbox.
253
+ await tester.pumpWidget (PageRoot (
254
+ child: const ZulipApp ()
255
+ ));
214
256
await tester.pump ();
215
257
final navigator = await ZulipApp .navigator;
216
258
unawaited (navigator.push (getImageLightboxRoute (
217
259
accountId: eg.selfAccount.id,
260
+ pageContext: PageRoot .contextOf (navigator.context),
218
261
message: message ?? eg.streamMessage (),
219
262
src: src,
220
263
thumbnailUrl: thumbnailUrl,
@@ -558,4 +601,51 @@ void main() {
558
601
check (platform.position).equals (position);
559
602
});
560
603
});
561
- }
604
+
605
+ group ('LightboxHero' , () {
606
+ testWidgets ('no hero animation occurs between different message list pages for same image' , (tester) async {
607
+ final channel = eg.stream (streamId: eg.defaultStreamMessageStreamId);
608
+ final message = eg.streamMessage (stream: channel,
609
+ contentMarkdown: ContentExample .spoilerHeaderHasImage.html, topic: 'test topic' );
610
+ await setupMessageListPage (tester, narrow: const CombinedFeedNarrow (),
611
+ streams: [channel], messages: [message]);
612
+
613
+ connection.prepare (json:
614
+ eg.newestGetMessagesResult (foundOldest: true , messages: [message]).toJson ());
615
+
616
+ final imageFinder = find.byType (RealmContentNetworkImage ).first;
617
+ final initialImageRect = tester.getRect (imageFinder);
618
+
619
+ await tester.tap (find.descendant (
620
+ of: find.byType (StreamMessageRecipientHeader ),
621
+ matching: find.text ('test topic' )));
622
+ await tester.pump ();
623
+ await tester.pump (const Duration (milliseconds: 150 ));
624
+
625
+ final imageInTransition = tester.getRect (imageFinder);
626
+ check (imageInTransition.top).equals (initialImageRect.top);
627
+ check (imageInTransition.left).equals (initialImageRect.left);
628
+ await tester.pumpAndSettle ();
629
+ });
630
+
631
+ testWidgets ('hero animation occurs when opening lightbox from message list' , (tester) async {
632
+ final channel = eg.stream (streamId: eg.defaultStreamMessageStreamId);
633
+ final message = eg.streamMessage (stream: channel,
634
+ contentMarkdown: ContentExample .spoilerHeaderHasImage.html);
635
+ await setupMessageListPage (tester, narrow: const CombinedFeedNarrow (),
636
+ streams: [channel], messages: [message]);
637
+
638
+ final imageFinder = find.byType (RealmContentNetworkImage );
639
+ final initialImageRect = tester.getRect (imageFinder);
640
+
641
+ await tester.tap (imageFinder);
642
+ await tester.pump ();
643
+ await tester.pump (const Duration (milliseconds: 150 ));
644
+
645
+ final imageInTransition = tester.getRect (imageFinder);
646
+ check (imageInTransition.top != initialImageRect.top).isTrue ();
647
+ check (imageInTransition.left != initialImageRect.left).isTrue ();
648
+ await tester.pumpAndSettle ();
649
+ });
650
+ });
651
+ }
0 commit comments