@@ -13,13 +13,17 @@ import 'package:zulip/api/route/messages.dart';
13
13
import 'package:zulip/model/localizations.dart' ;
14
14
import 'package:zulip/model/narrow.dart' ;
15
15
import 'package:zulip/model/store.dart' ;
16
+ import 'package:zulip/model/typing_status.dart' ;
17
+ import 'package:zulip/widgets/app.dart' ;
16
18
import 'package:zulip/widgets/compose_box.dart' ;
19
+ import 'package:zulip/widgets/page.dart' ;
17
20
18
21
import '../api/fake_api.dart' ;
19
22
import '../example_data.dart' as eg;
20
23
import '../flutter_checks.dart' ;
21
24
import '../model/binding.dart' ;
22
25
import '../model/test_store.dart' ;
26
+ import '../model/typing_status_test.dart' ;
23
27
import '../stdlib_checks.dart' ;
24
28
import 'dialog_checks.dart' ;
25
29
import 'test_app.dart' ;
@@ -53,6 +57,22 @@ void main() {
53
57
return controllerKey;
54
58
}
55
59
60
+ Future <void > prepareComposeBoxWithNavigation (WidgetTester tester,
61
+ {required Narrow narrow}) async {
62
+ addTearDown (testBinding.reset);
63
+ await testBinding.globalStore.add (eg.selfAccount, eg.initialSnapshot ());
64
+
65
+ await tester.pumpWidget (const ZulipApp ());
66
+ await tester.pump ();
67
+ final navigator = await ZulipApp .navigator;
68
+ navigator.push (MaterialAccountWidgetRoute (
69
+ accountId: eg.selfAccount.id, page: ComposeBox (narrow: narrow)));
70
+ await tester.pumpAndSettle ();
71
+
72
+ store = await testBinding.globalStore.perAccount (eg.selfAccount.id);
73
+ connection = store.connection as FakeApiConnection ;
74
+ }
75
+
56
76
group ('ComposeContentController' , () {
57
77
group ('insertPadded' , () {
58
78
// Like `parseMarkedText` in test/model/autocomplete_test.dart,
@@ -155,6 +175,101 @@ void main() {
155
175
});
156
176
});
157
177
178
+ group ('ComposeBox typing notification' , () {
179
+ void checkTypingRequest (TypingOp op, SendableNarrow narrow) =>
180
+ checkSetTypingStatusRequests (connection, [(op, narrow)]);
181
+
182
+ final testCases = [
183
+ const TopicNarrow (123 , 'some topic' ),
184
+ DmNarrow .withUsers ([eg.otherUser.userId], selfUserId: eg.selfUser.userId),
185
+ ];
186
+
187
+ final contentInputFinder = find.byWidgetPredicate (
188
+ (widget) => widget is TextField && widget.controller is ComposeContentController );
189
+ final topicInputFinder = find.byWidgetPredicate (
190
+ (widget) => widget is TextField && widget.controller is ComposeTopicController );
191
+
192
+ for (final narrow in testCases) {
193
+ testWidgets ('smoke $narrow ' , (tester) async {
194
+ await prepareComposeBox (tester, narrow: narrow);
195
+
196
+ connection.prepare (json: {});
197
+ await tester.enterText (contentInputFinder, 'hello world' );
198
+ checkTypingRequest (TypingOp .start, narrow);
199
+
200
+ connection.prepare (json: {});
201
+ tester.pumpAndSettle (store.typingNotifier.typingStoppedWaitPeriod);
202
+ checkTypingRequest (TypingOp .stop, narrow);
203
+ });
204
+
205
+ testWidgets ('$narrow : clearing text' , (tester) async {
206
+ await prepareComposeBox (tester, narrow: narrow);
207
+
208
+ connection.prepare (json: {});
209
+ await tester.enterText (contentInputFinder, 'hello world' );
210
+ checkTypingRequest (TypingOp .start, narrow);
211
+
212
+ connection.prepare (json: {});
213
+ await tester.enterText (contentInputFinder, '' );
214
+ checkTypingRequest (TypingOp .stop, narrow);
215
+ });
216
+
217
+ testWidgets ('$narrow : unfocusing content input stops typing notification' , (tester) async {
218
+ await prepareComposeBoxWithNavigation (tester, narrow: narrow);
219
+
220
+ connection.prepare (json: {});
221
+ await tester.enterText (contentInputFinder, 'hello world' );
222
+ checkTypingRequest (TypingOp .start, narrow);
223
+
224
+ connection.prepare (json: {});
225
+ (await ZulipApp .navigator).pop ();
226
+ tester.pump (Duration .zero);
227
+ checkTypingRequest (TypingOp .stop, narrow);
228
+ });
229
+ }
230
+
231
+ testWidgets ('smoke ChannelNarrow' , (tester) async {
232
+ const narrow = ChannelNarrow (123 );
233
+ await prepareComposeBox (tester, narrow: narrow);
234
+
235
+ await tester.enterText (topicInputFinder, 'test topic' );
236
+ // Clean an irrelevant topic request.
237
+ check (connection.takeRequests ()).single
238
+ ..method.equals ('GET' )
239
+ ..url.path.equals ('/api/v1/users/me/123/topics' );
240
+
241
+ connection.prepare (json: {});
242
+ final typingNarrow = TopicNarrow (narrow.streamId, 'test topic' );
243
+ await tester.enterText (contentInputFinder, 'hello world' );
244
+ checkTypingRequest (TypingOp .start, typingNarrow);
245
+
246
+ connection.prepare (json: {});
247
+ tester.pumpAndSettle (store.typingNotifier.typingStoppedWaitPeriod);
248
+ checkTypingRequest (TypingOp .stop, typingNarrow);
249
+ });
250
+
251
+ testWidgets ('unfocusing content input stops typing notification' , (tester) async {
252
+ const narrow = ChannelNarrow (123 );
253
+ await prepareComposeBox (tester, narrow: narrow);
254
+
255
+ await tester.enterText (topicInputFinder, 'topic' );
256
+ // Clean an irrelevant topic request.
257
+ check (connection.takeRequests ()).single
258
+ ..method.equals ('GET' )
259
+ ..url.path.equals ('/api/v1/users/me/123/topics' );
260
+
261
+ connection.prepare (json: {});
262
+ await tester.enterText (contentInputFinder, 'hello world' );
263
+ checkTypingRequest (TypingOp .start, TopicNarrow (narrow.streamId, 'topic' ));
264
+
265
+ connection.prepare (json: {});
266
+ await tester.tap (topicInputFinder);
267
+ checkTypingRequest (TypingOp .stop, TopicNarrow (narrow.streamId, 'topic' ));
268
+ // Wait for the idle timer to be cancelled.
269
+ await tester.pump (Duration .zero);
270
+ });
271
+ });
272
+
158
273
group ('ComposeBox textCapitalization' , () {
159
274
void checkComposeBoxTextFields (WidgetTester tester, {
160
275
required GlobalKey <ComposeBoxController > controllerKey,
@@ -198,6 +313,9 @@ void main() {
198
313
Future <void > setupAndTapSend (WidgetTester tester, {
199
314
required void Function (int messageId) prepareResponse,
200
315
}) async {
316
+ DebugTypingNotifier .debugEnable = false ;
317
+ addTearDown (DebugTypingNotifier .debugReset);
318
+
201
319
final zulipLocalizations = GlobalLocalizations .zulipLocalizations;
202
320
await prepareComposeBox (tester, narrow: const TopicNarrow (123 , 'some topic' ));
203
321
@@ -263,6 +381,9 @@ void main() {
263
381
264
382
group ('attach from media library' , () {
265
383
testWidgets ('success' , (tester) async {
384
+ DebugTypingNotifier .debugEnable = false ;
385
+ addTearDown (DebugTypingNotifier .debugReset);
386
+
266
387
final controllerKey = await prepareComposeBox (tester, narrow: ChannelNarrow (eg.stream ().streamId));
267
388
final composeBoxController = controllerKey.currentState! ;
268
389
@@ -319,6 +440,9 @@ void main() {
319
440
320
441
group ('attach from camera' , () {
321
442
testWidgets ('success' , (tester) async {
443
+ DebugTypingNotifier .debugEnable = false ;
444
+ addTearDown (DebugTypingNotifier .debugReset);
445
+
322
446
final controllerKey = await prepareComposeBox (tester, narrow: ChannelNarrow (eg.stream ().streamId));
323
447
final composeBoxController = controllerKey.currentState! ;
324
448
0 commit comments