@@ -16,6 +16,7 @@ import 'package:zulip/widgets/message_list.dart';
1616import 'package:zulip/widgets/page.dart' ;
1717import 'package:zulip/widgets/store.dart' ;
1818
19+ import 'flutter_checks.dart' ;
1920import 'model/binding.dart' ;
2021import 'example_data.dart' as eg;
2122import 'test_navigation.dart' ;
@@ -185,72 +186,67 @@ void main() {
185186 group ('NotificationDisplayManager open' , () {
186187 late List <Route <dynamic >> pushedRoutes;
187188
188- Future <void > prepare (WidgetTester tester) async {
189+ Future <void > prepare (WidgetTester tester, { bool early = false } ) async {
189190 await init ();
190191 pushedRoutes = [];
191192 final testNavObserver = TestNavigatorObserver ()
192193 ..onPushed = (route, prevRoute) => pushedRoutes.add (route);
193194 await tester.pumpWidget (ZulipApp (navigatorObservers: [testNavObserver]));
195+ if (early) {
196+ check (pushedRoutes).isEmpty ();
197+ return ;
198+ }
194199 await tester.pump ();
195200 check (pushedRoutes).length.equals (1 );
196201 pushedRoutes.clear ();
197202 }
198203
199- void openNotification (Account account, Message message) {
204+ Future < void > openNotification (Account account, Message message) async {
200205 final fcmMessage = messageFcmMessage (message, account: account);
201206 testBinding.notifications.receiveNotificationResponse (NotificationResponse (
202207 notificationResponseType: NotificationResponseType .selectedNotification,
203208 payload: jsonEncode (fcmMessage)));
209+ await null ; // let _navigateForNotification find navigator
204210 }
205211
206- void checkOpenedMessageList ({ required int expectedAccountId, required Narrow expectedNarrow} ) {
207- check (pushedRoutes).single .isA <WidgetRoute >().page
212+ void matchesNavigation ( Subject < Route > route, Account account, Message message ) {
213+ route .isA <WidgetRoute >().page
208214 .isA <PerAccountStoreWidget >()
209- ..accountId.equals (expectedAccountId )
215+ ..accountId.equals (account.id )
210216 ..child.isA <MessageListPage >()
211- .narrow.equals (expectedNarrow);
212- pushedRoutes. clear ( );
217+ .narrow.equals (SendableNarrow . ofMessage (message,
218+ selfUserId : account.userId) );
213219 }
214220
215- void checkOpenNotification (Account account, Message message) {
216- openNotification (account, message);
217- checkOpenedMessageList (
218- expectedAccountId: account.id,
219- expectedNarrow: SendableNarrow .ofMessage (message,
220- selfUserId: account.userId));
221+ Future <void > checkOpenNotification (Account account, Message message) async {
222+ await openNotification (account, message);
223+ matchesNavigation (check (pushedRoutes).single, account, message);
224+ pushedRoutes.clear ();
221225 }
222226
223227 testWidgets ('stream message' , (tester) async {
224228 testBinding.globalStore.insertAccount (eg.selfAccount.toCompanion (false ));
225229 await prepare (tester);
226- checkOpenNotification (eg.selfAccount, eg.streamMessage ());
230+ await checkOpenNotification (eg.selfAccount, eg.streamMessage ());
227231 });
228232
229233 testWidgets ('direct message' , (tester) async {
230234 testBinding.globalStore.insertAccount (eg.selfAccount.toCompanion (false ));
231235 await prepare (tester);
232- checkOpenNotification (eg.selfAccount,
236+ await checkOpenNotification (eg.selfAccount,
233237 eg.dmMessage (from: eg.otherUser, to: [eg.selfUser]));
234238 });
235239
236- testWidgets ('no widgets in tree' , (tester) async {
237- await init ();
238- final message = eg.dmMessage (from: eg.otherUser, to: [eg.selfUser]);
239-
240- openNotification (eg.selfAccount, message);
241- // nothing happened, but nothing blew up
242- });
243-
244240 testWidgets ('no accounts' , (tester) async {
245241 await prepare (tester);
246- openNotification (eg.selfAccount, eg.streamMessage ());
242+ await openNotification (eg.selfAccount, eg.streamMessage ());
247243 check (pushedRoutes).isEmpty ();
248244 });
249245
250246 testWidgets ('mismatching account' , (tester) async {
251247 testBinding.globalStore.insertAccount (eg.selfAccount.toCompanion (false ));
252248 await prepare (tester);
253- openNotification (eg.otherAccount, eg.streamMessage ());
249+ await openNotification (eg.otherAccount, eg.streamMessage ());
254250 check (pushedRoutes).isEmpty ();
255251 });
256252
@@ -268,10 +264,52 @@ void main() {
268264 }
269265 await prepare (tester);
270266
271- checkOpenNotification (accounts[0 ], eg.streamMessage ());
272- checkOpenNotification (accounts[1 ], eg.streamMessage ());
273- checkOpenNotification (accounts[2 ], eg.streamMessage ());
274- checkOpenNotification (accounts[3 ], eg.streamMessage ());
267+ await checkOpenNotification (accounts[0 ], eg.streamMessage ());
268+ await checkOpenNotification (accounts[1 ], eg.streamMessage ());
269+ await checkOpenNotification (accounts[2 ], eg.streamMessage ());
270+ await checkOpenNotification (accounts[3 ], eg.streamMessage ());
271+ });
272+
273+ testWidgets ('wait for app to become ready' , (tester) async {
274+ testBinding.globalStore.insertAccount (eg.selfAccount.toCompanion (false ));
275+ await prepare (tester, early: true );
276+ final message = eg.streamMessage ();
277+ await openNotification (eg.selfAccount, message);
278+ // The app should still not be ready (or else this test won't work right).
279+ check (ZulipApp .ready.value).isFalse ();
280+ check (ZulipApp .navigatorKey.currentState).isNull ();
281+ // And the openNotification hasn't caused any navigation yet.
282+ check (pushedRoutes).isEmpty ();
283+
284+ // Now let the GlobalStore get loaded and the app's main UI get mounted.
285+ await tester.pump ();
286+ // The navigator first pushes the home route…
287+ check (pushedRoutes).length.equals (2 );
288+ check (pushedRoutes[0 ]).settings.name.equals ("/" );
289+ // … and then the one the notification leads to.
290+ matchesNavigation (check (pushedRoutes[1 ]), eg.selfAccount, message);
291+ });
292+
293+ testWidgets ('at app launch' , (tester) async {
294+ // Set up a value for `getNotificationLaunchDetails` to return.
295+ final account = eg.selfAccount;
296+ final message = eg.streamMessage ();
297+ final response = NotificationResponse (
298+ notificationResponseType: NotificationResponseType .selectedNotification,
299+ payload: jsonEncode (messageFcmMessage (message, account: account)));
300+ testBinding.notifications.appLaunchDetails =
301+ NotificationAppLaunchDetails (true , notificationResponse: response);
302+
303+ // Now start the app.
304+ testBinding.globalStore.insertAccount (account.toCompanion (false ));
305+ await prepare (tester, early: true );
306+ check (pushedRoutes).isEmpty (); // GlobalStore hasn't loaded yet
307+
308+ // Once the app is ready, we navigate to the conversation.
309+ await tester.pump ();
310+ check (pushedRoutes).length.equals (2 );
311+ check (pushedRoutes[0 ]).settings.name.equals ("/" );
312+ matchesNavigation (check (pushedRoutes[1 ]), account, message);
275313 });
276314 });
277315}
0 commit comments