@@ -8,13 +8,19 @@ import 'package:flutter_test/flutter_test.dart';
8
8
import 'package:url_launcher/url_launcher.dart' ;
9
9
import 'package:zulip/api/core.dart' ;
10
10
import 'package:zulip/model/content.dart' ;
11
+ import 'package:zulip/model/narrow.dart' ;
11
12
import 'package:zulip/widgets/content.dart' ;
13
+ import 'package:zulip/widgets/message_list.dart' ;
14
+ import 'package:zulip/widgets/page.dart' ;
12
15
import 'package:zulip/widgets/store.dart' ;
13
16
14
17
import '../example_data.dart' as eg;
15
18
import '../model/binding.dart' ;
16
19
import '../test_images.dart' ;
20
+ import '../test_navigation.dart' ;
17
21
import 'dialog_checks.dart' ;
22
+ import 'message_list_checks.dart' ;
23
+ import 'page_checks.dart' ;
18
24
19
25
void main () {
20
26
TestZulipBinding .ensureInitialized ();
@@ -158,6 +164,53 @@ void main() {
158
164
});
159
165
});
160
166
167
+ group ('LinkNode on internal links' , () {
168
+ Future <List <Route <dynamic >>> prepareContent (WidgetTester tester, {
169
+ required String html,
170
+ }) async {
171
+ await testBinding.globalStore.add (eg.selfAccount, eg.initialSnapshot (
172
+ streams: [eg.stream (streamId: 1 , name: 'check' )],
173
+ ));
174
+ addTearDown (testBinding.reset);
175
+ final pushedRoutes = < Route <dynamic >> [];
176
+ final testNavObserver = TestNavigatorObserver ()
177
+ ..onPushed = (route, prevRoute) => pushedRoutes.add (route);
178
+ await tester.pumpWidget (GlobalStoreWidget (child: MaterialApp (
179
+ navigatorObservers: [testNavObserver],
180
+ home: PerAccountStoreWidget (accountId: eg.selfAccount.id,
181
+ child: BlockContentList (nodes: parseContent (html).nodes)))));
182
+ await tester.pump (); // global store
183
+ await tester.pump (); // per-account store
184
+ // `tester.pumpWidget` introduces an initial route, remove so
185
+ // consumers only have newly pushed routes.
186
+ assert (pushedRoutes.length == 1 );
187
+ pushedRoutes.removeLast ();
188
+ return pushedRoutes;
189
+ }
190
+
191
+ testWidgets ('valid internal links are navigated to within app' , (tester) async {
192
+ final pushedRoutes = await prepareContent (tester,
193
+ html: '<p><a href="/#narrow/stream/1-check">stream</a></p>' );
194
+
195
+ await tester.tap (find.text ('stream' ));
196
+ check (testBinding.takeLaunchUrlCalls ()).isEmpty ();
197
+ check (pushedRoutes).single.isA <WidgetRoute >()
198
+ .page.isA <MessageListPage >().narrow.equals (const StreamNarrow (1 ));
199
+ });
200
+
201
+ testWidgets ('invalid internal links are opened in browser' , (tester) async {
202
+ // Link is invalid due to `topic` operator missing an operand.
203
+ final pushedRoutes = await prepareContent (tester,
204
+ html: '<p><a href="/#narrow/stream/1-check/topic">invalid</a></p>' );
205
+
206
+ await tester.tap (find.text ('invalid' ));
207
+ final expectedUrl = eg.realmUrl.resolve ('/#narrow/stream/1-check/topic' );
208
+ check (testBinding.takeLaunchUrlCalls ())
209
+ .single.equals ((url: expectedUrl, mode: LaunchMode .externalApplication));
210
+ check (pushedRoutes).isEmpty ();
211
+ });
212
+ });
213
+
161
214
group ('UnicodeEmoji' , () {
162
215
Future <void > prepareContent (WidgetTester tester, String html) async {
163
216
await tester.pumpWidget (MaterialApp (home: BlockContentList (nodes: parseContent (html).nodes)));
0 commit comments