@@ -15,13 +15,16 @@ import 'package:zulip/model/localizations.dart';
15
15
import 'package:zulip/model/narrow.dart' ;
16
16
import 'package:zulip/model/store.dart' ;
17
17
import 'package:zulip/model/typing_status.dart' ;
18
+ import 'package:zulip/widgets/app.dart' ;
18
19
import 'package:zulip/widgets/compose_box.dart' ;
20
+ import 'package:zulip/widgets/page.dart' ;
19
21
20
22
import '../api/fake_api.dart' ;
21
23
import '../example_data.dart' as eg;
22
24
import '../flutter_checks.dart' ;
23
25
import '../model/binding.dart' ;
24
26
import '../model/test_store.dart' ;
27
+ import '../model/typing_status_test.dart' ;
25
28
import '../stdlib_checks.dart' ;
26
29
import 'dialog_checks.dart' ;
27
30
import 'test_app.dart' ;
@@ -201,6 +204,128 @@ void main() {
201
204
});
202
205
});
203
206
207
+ group ('ComposeBox typing notification' , () {
208
+ const narrow = TopicNarrow (123 , 'some topic' );
209
+
210
+ // This uses a high feature level to test with the latest version of the
211
+ // setTypingNotifier API.
212
+ final account = eg.account (
213
+ user: eg.selfUser, zulipFeatureLevel: eg.futureZulipFeatureLevel);
214
+
215
+ final contentInputFinder = find.byWidgetPredicate (
216
+ (widget) => widget is TextField && widget.controller is ComposeContentController );
217
+ final topicInputFinder = find.byWidgetPredicate (
218
+ (widget) => widget is TextField && widget.controller is ComposeTopicController );
219
+
220
+ void checkTypingRequest (TypingOp op, SendableNarrow narrow) =>
221
+ checkSetTypingStatusRequests (connection, [(op, narrow)]);
222
+
223
+ Future <void > checkStartTyping (WidgetTester tester, SendableNarrow narrow) async {
224
+ connection.prepare (json: {});
225
+ await tester.enterText (contentInputFinder, 'hello world' );
226
+ checkTypingRequest (TypingOp .start, narrow);
227
+ }
228
+
229
+ testWidgets ('smoke TopicNarrow' , (tester) async {
230
+ await prepareComposeBox (tester, narrow: narrow, account: account);
231
+
232
+ await checkStartTyping (tester, narrow);
233
+
234
+ connection.prepare (json: {});
235
+ await tester.pump (store.typingNotifier.typingStoppedWaitPeriod);
236
+ checkTypingRequest (TypingOp .stop, narrow);
237
+ });
238
+
239
+ testWidgets ('smoke DmNarrow' , (tester) async {
240
+ final narrow = DmNarrow .withUsers (
241
+ [eg.otherUser.userId], selfUserId: eg.selfUser.userId);
242
+ await prepareComposeBox (tester, narrow: narrow, account: account);
243
+
244
+ await checkStartTyping (tester, narrow);
245
+
246
+ connection.prepare (json: {});
247
+ await tester.pump (store.typingNotifier.typingStoppedWaitPeriod);
248
+ checkTypingRequest (TypingOp .stop, narrow);
249
+ });
250
+
251
+ testWidgets ('smoke ChannelNarrow' , (tester) async {
252
+ const narrow = ChannelNarrow (123 );
253
+ final destinationNarrow = TopicNarrow (narrow.streamId, 'test topic' );
254
+ await prepareComposeBox (tester, narrow: narrow, account: account);
255
+
256
+ await tester.enterText (topicInputFinder, destinationNarrow.topic);
257
+ // Remove an irrelevant topic request.
258
+ check (connection.takeRequests ()).single
259
+ ..method.equals ('GET' )
260
+ ..url.path.equals ('/api/v1/users/me/123/topics' );
261
+
262
+ await checkStartTyping (tester, destinationNarrow);
263
+
264
+ connection.prepare (json: {});
265
+ await tester.pump (store.typingNotifier.typingStoppedWaitPeriod);
266
+ checkTypingRequest (TypingOp .stop, destinationNarrow);
267
+ });
268
+
269
+ testWidgets ('clearing text stops typing notification' , (tester) async {
270
+ await prepareComposeBox (tester, narrow: narrow, account: account);
271
+
272
+ await checkStartTyping (tester, narrow);
273
+
274
+ connection.prepare (json: {});
275
+ await tester.enterText (contentInputFinder, '' );
276
+ checkTypingRequest (TypingOp .stop, narrow);
277
+ });
278
+
279
+ Future <void > prepareComposeBoxWithNavigation (WidgetTester tester) async {
280
+ addTearDown (testBinding.reset);
281
+ await testBinding.globalStore.add (account, eg.initialSnapshot (
282
+ zulipFeatureLevel: account.zulipFeatureLevel));
283
+
284
+ await tester.pumpWidget (const ZulipApp ());
285
+ await tester.pump ();
286
+ final navigator = await ZulipApp .navigator;
287
+ navigator.push (MaterialAccountWidgetRoute (
288
+ accountId: account.id, page: const ComposeBox (narrow: narrow)));
289
+ await tester.pumpAndSettle ();
290
+
291
+ store = await testBinding.globalStore.perAccount (account.id);
292
+ connection = store.connection as FakeApiConnection ;
293
+ }
294
+
295
+ testWidgets ('navigating away stops typing notification' , (tester) async {
296
+ await prepareComposeBoxWithNavigation (tester);
297
+
298
+ await checkStartTyping (tester, narrow);
299
+
300
+ connection.prepare (json: {});
301
+ (await ZulipApp .navigator).pop ();
302
+ await tester.pump (Duration .zero);
303
+ checkTypingRequest (TypingOp .stop, narrow);
304
+ });
305
+
306
+ testWidgets ('unfocusing content input stops typing notification' , (tester) async {
307
+ const narrow = ChannelNarrow (123 );
308
+ final destinationNarrow = TopicNarrow (narrow.streamId, 'test topic' );
309
+ await prepareComposeBox (tester, narrow: narrow, account: account);
310
+
311
+ await tester.enterText (topicInputFinder, destinationNarrow.topic);
312
+ // Remove an irrelevant topic request.
313
+ check (connection.takeRequests ()).single
314
+ ..method.equals ('GET' )
315
+ ..url.path.equals ('/api/v1/users/me/123/topics' );
316
+
317
+ connection.prepare (json: {});
318
+ await tester.enterText (contentInputFinder, 'hello world' );
319
+ checkTypingRequest (TypingOp .start, destinationNarrow);
320
+
321
+ connection.prepare (json: {});
322
+ // Move focus to the topic input
323
+ await tester.tap (topicInputFinder);
324
+ await tester.pump (Duration .zero);
325
+ checkTypingRequest (TypingOp .stop, destinationNarrow);
326
+ });
327
+ });
328
+
204
329
group ('message-send request response' , () {
205
330
Future <void > setupAndTapSend (WidgetTester tester, {
206
331
required void Function (int messageId) prepareResponse,
0 commit comments