@@ -14,8 +14,10 @@ import 'package:zulip/widgets/message_list.dart';
1414import 'package:zulip/widgets/topic_list.dart' ;
1515
1616import '../api/fake_api.dart' ;
17+ import '../api/route/route_checks.dart' ;
1718import '../example_data.dart' as eg;
1819import '../model/binding.dart' ;
20+ import '../model/store_checks.dart' ;
1921import '../model/test_store.dart' ;
2022import '../stdlib_checks.dart' ;
2123import 'test_app.dart' ;
@@ -124,7 +126,7 @@ void main() {
124126 check (find.byType (CircularProgressIndicator )).findsNothing ();
125127 });
126128
127- testWidgets (' fetch again when navigating away and back' , (tester) async {
129+ testWidgets ("don't fetch again when navigating away and back" , (tester) async {
128130 addTearDown (testBinding.reset);
129131 await testBinding.globalStore.add (eg.selfAccount, eg.initialSnapshot ());
130132 final store = await testBinding.globalStore.perAccount (eg.selfAccount.id);
@@ -145,20 +147,25 @@ void main() {
145147 await tester.tap (find.byIcon (ZulipIcons .topics));
146148 await tester.pump ();
147149 await tester.pump (Duration .zero);
150+ check (connection.takeRequests ())
151+ ..length.equals (2 ) // one for the messages request, another for the topics
152+ ..last.which ((last) => last
153+ .isA< http.Request > ()
154+ ..method.equals ('GET' )
155+ ..url.path.equals ('/api/v1/users/me/${channel .streamId }/topics' ));
148156 check (find.text ('topic A' )).findsOne ();
149157
150158 // … go back to the message list page…
151159 await tester.pageBack ();
152160 await tester.pump ();
153161
154- // … then back to the topic-list page, expecting to fetch again.
155- connection.prepare (json: GetChannelTopicsResult (
156- topics: [eg.getChannelTopicsEntry (name: 'topic B' )]).toJson ());
162+ // … then back to the topic-list page, expecting not to fetch again but
163+ // use existing data which is kept up-to-date anyway.
157164 await tester.tap (find.byIcon (ZulipIcons .topics));
158165 await tester.pump ();
159166 await tester.pump (Duration .zero);
160- check (find. text ( 'topic A' )).findsNothing ();
161- check (find.text ('topic B ' )).findsOne ();
167+ check (connection. takeRequests ( )).isEmpty ();
168+ check (find.text ('topic A ' )).findsOne ();
162169 });
163170
164171 Finder topicItemFinder = find.descendant (
@@ -169,6 +176,18 @@ void main() {
169176 of: topicItemFinder.at (index),
170177 matching: finder);
171178
179+ testWidgets ('sort topics by maxId' , (tester) async {
180+ await prepare (tester, topics: [
181+ eg.getChannelTopicsEntry (name: 'A' , maxId: 3 ),
182+ eg.getChannelTopicsEntry (name: 'B' , maxId: 2 ),
183+ eg.getChannelTopicsEntry (name: 'C' , maxId: 4 ),
184+ ]);
185+
186+ check (findInTopicItemAt (0 , find.text ('C' ))).findsOne ();
187+ check (findInTopicItemAt (1 , find.text ('A' ))).findsOne ();
188+ check (findInTopicItemAt (2 , find.text ('B' ))).findsOne ();
189+ });
190+
172191 testWidgets ('show topic action sheet' , (tester) async {
173192 final channel = eg.stream ();
174193 await prepare (tester, channel: channel,
@@ -190,16 +209,70 @@ void main() {
190209 });
191210 });
192211
193- testWidgets ('sort topics by maxId' , (tester) async {
194- await prepare (tester, topics: [
195- eg.getChannelTopicsEntry (name: 'A' , maxId: 3 ),
196- eg.getChannelTopicsEntry (name: 'B' , maxId: 2 ),
197- eg.getChannelTopicsEntry (name: 'C' , maxId: 4 ),
198- ]);
212+ testWidgets ('show topic action sheet before and after moves' , (tester) async {
213+ final channel = eg.stream ();
214+ final message = eg.streamMessage (id: 100 , stream: channel, topic: 'foo' );
215+ await prepare (tester, channel: channel,
216+ topics: [eg.getChannelTopicsEntry (name: 'foo' , maxId: 100 )],
217+ messages: [message]);
218+ check (store).getChannelTopics (channel.streamId).isNotNull ().single
219+ .maxId.equals (100 );
220+ check (topicItemFinder).findsOne ();
221+
222+ // Before the move, "foo"'s maxId is known to be accurate. This makes
223+ // topic actions that require `someMessageIdInTopic` available.
224+ await tester.longPress (find.text ('foo' ));
225+ await tester.pump (Duration (milliseconds: 150 )); // bottom-sheet animation
226+ check (find.text ('Mark as resolved' )).findsOne ();
227+ await tester.tap (find.text ('Cancel' ));
199228
200- check (findInTopicItemAt (0 , find.text ('C' ))).findsOne ();
201- check (findInTopicItemAt (1 , find.text ('A' ))).findsOne ();
202- check (findInTopicItemAt (2 , find.text ('B' ))).findsOne ();
229+ await store.handleEvent (eg.updateMessageEventMoveFrom (
230+ origMessages: [message],
231+ newTopicStr: 'bar' ));
232+ await tester.pump ();
233+ check (topicItemFinder).findsExactly (2 );
234+
235+ // After the move, the message with maxId moved away from "foo". The topic
236+ // actions that require `someMessageIdInTopic` are no longer available.
237+ await tester.longPress (find.text ('foo' ));
238+ await tester.pump (Duration (milliseconds: 150 )); // bottom-sheet animation
239+ check (find.text ('Mark as resolved' )).findsNothing ();
240+ await tester.tap (find.text ('Cancel' ));
241+ await tester.pump ();
242+
243+ // Topic actions that require `someMessageIdInTopic` are available
244+ // for "bar", the new topic that the message moved to.
245+ await tester.longPress (find.text ('bar' ));
246+ await tester.pump (Duration (milliseconds: 150 )); // bottom-sheet animation
247+ check (find.text ('Mark as resolved' )).findsOne ();
248+ });
249+
250+ // event handling is more thoroughly tested in test/model/channel_test.dart
251+ testWidgets ('smoke resolve topic from topic action sheet' , (tester) async {
252+ final channel = eg.stream ();
253+ final messages = List .generate (10 , (i) =>
254+ eg.streamMessage (id: 100 + i, stream: channel, topic: 'foo' ));
255+
256+ await prepare (tester, channel: channel,
257+ topics: [eg.getChannelTopicsEntry (maxId: 109 , name: 'foo' )],
258+ messages: messages);
259+ await tester.longPress (topicItemFinder);
260+ await tester.pump (Duration (milliseconds: 150 )); // bottom-sheet animation
261+ check (findInTopicItemAt (0 , find.byIcon (ZulipIcons .check))).findsNothing ();
262+ check (findInTopicItemAt (0 , find.text ('foo' ))).findsOne ();
263+
264+ connection.prepare (json: {});
265+ await tester.tap (find.text ('Mark as resolved' ));
266+ await tester.pump ();
267+ await tester.pump (Duration .zero);
268+
269+ await store.handleEvent (eg.updateMessageEventMoveFrom (
270+ origMessages: messages,
271+ newTopic: eg.t ('foo' ).resolve (),
272+ propagateMode: PropagateMode .changeAll));
273+ await tester.pump ();
274+ check (findInTopicItemAt (0 , find.byIcon (ZulipIcons .check))).findsOne ();
275+ check (findInTopicItemAt (0 , find.text ('foo' ))).findsOne ();
203276 });
204277
205278 testWidgets ('resolved and unresolved topics' , (tester) async {
0 commit comments