@@ -104,6 +104,12 @@ void main() {
104
104
await tester.enterText (contentInputFinder, content);
105
105
}
106
106
107
+ Future <void > tapSendButton (WidgetTester tester) async {
108
+ connection.prepare (json: SendMessageResult (id: 123 ).toJson ());
109
+ await tester.tap (find.byIcon (ZulipIcons .send));
110
+ await tester.pump (Duration .zero);
111
+ }
112
+
107
113
group ('ComposeContentController' , () {
108
114
group ('insertPadded' , () {
109
115
// Like `parseMarkedText` in test/model/autocomplete_test.dart,
@@ -206,6 +212,107 @@ void main() {
206
212
});
207
213
});
208
214
215
+ group ('length validation' , () {
216
+ final channel = eg.stream ();
217
+
218
+ /// String where there are [n] Unicode code points,
219
+ /// >[n] UTF-16 code units, and <[n] "characters" a.k.a. grapheme clusters.
220
+ String makeStringWithCodePoints (int n) {
221
+ assert (n >= 5 );
222
+ const graphemeCluster = '👨👩👦' ;
223
+ assert (graphemeCluster.runes.length == 5 );
224
+ assert (graphemeCluster.length == 8 );
225
+ assert (graphemeCluster.characters.length == 1 );
226
+
227
+ final result =
228
+ graphemeCluster * (n ~ / 5 )
229
+ + 'a' * (n % 5 );
230
+ assert (result.runes.length == n);
231
+
232
+ return result;
233
+ }
234
+
235
+ group ('content' , () {
236
+ Future <void > prepareWithContent (WidgetTester tester, String content) async {
237
+ TypingNotifier .debugEnable = false ;
238
+ addTearDown (TypingNotifier .debugReset);
239
+
240
+ final narrow = ChannelNarrow (channel.streamId);
241
+ await prepareComposeBox (tester, narrow: narrow, streams: [channel]);
242
+ await enterTopic (tester, narrow: narrow, topic: 'some topic' );
243
+ await enterContent (tester, content);
244
+ }
245
+
246
+ Future <void > checkErrorResponse (WidgetTester tester) async {
247
+ await tester.tap (find.byWidget (checkErrorDialog (tester,
248
+ expectedTitle: 'Message not sent' ,
249
+ expectedMessage: 'Message length shouldn\' t be greater than 10000 characters.' )));
250
+ }
251
+
252
+ testWidgets ('too-long content is rejected' , (tester) async {
253
+ await prepareWithContent (tester,
254
+ makeStringWithCodePoints (kMaxMessageLengthCodePoints + 1 ));
255
+ await tapSendButton (tester);
256
+ await checkErrorResponse (tester);
257
+ });
258
+
259
+ // TODO(#1238) unskip
260
+ // testWidgets('max-length content not rejected', (tester) async {
261
+ // await prepareWithContent(tester,
262
+ // makeStringWithCodePoints(kMaxMessageLengthCodePoints));
263
+ // await tapSendButton(tester);
264
+ // checkNoErrorDialog(tester);
265
+ // });
266
+
267
+ // TODO(#1238) replace with above commented-out test
268
+ testWidgets ('some content not rejected' , (tester) async {
269
+ await prepareWithContent (tester, 'a' * kMaxMessageLengthCodePoints);
270
+ await tapSendButton (tester);
271
+ checkNoErrorDialog (tester);
272
+ });
273
+ });
274
+
275
+ group ('topic' , () {
276
+ Future <void > prepareWithTopic (WidgetTester tester, String topic) async {
277
+ TypingNotifier .debugEnable = false ;
278
+ addTearDown (TypingNotifier .debugReset);
279
+
280
+ final narrow = ChannelNarrow (channel.streamId);
281
+ await prepareComposeBox (tester, narrow: narrow, streams: [channel]);
282
+ await enterTopic (tester, narrow: narrow, topic: topic);
283
+ await enterContent (tester, 'some content' );
284
+ }
285
+
286
+ Future <void > checkErrorResponse (WidgetTester tester) async {
287
+ await tester.tap (find.byWidget (checkErrorDialog (tester,
288
+ expectedTitle: 'Message not sent' ,
289
+ expectedMessage: 'Topic length shouldn\' t be greater than 60 characters.' )));
290
+ }
291
+
292
+ testWidgets ('too-long topic is rejected' , (tester) async {
293
+ await prepareWithTopic (tester,
294
+ makeStringWithCodePoints (kMaxTopicLengthCodePoints + 1 ));
295
+ await tapSendButton (tester);
296
+ await checkErrorResponse (tester);
297
+ });
298
+
299
+ // TODO(#1238) unskip
300
+ // testWidgets('max-length topic not rejected', (tester) async {
301
+ // await prepareWithTopic(tester,
302
+ // makeStringWithCodePoints(kMaxTopicLengthCodePoints));
303
+ // await tapSendButton(tester);
304
+ // checkNoErrorDialog(tester);
305
+ // });
306
+
307
+ // TODO(#1238) replace with above commented-out test
308
+ testWidgets ('some topic not rejected' , (tester) async {
309
+ await prepareWithTopic (tester, 'a' * kMaxTopicLengthCodePoints);
310
+ await tapSendButton (tester);
311
+ checkNoErrorDialog (tester);
312
+ });
313
+ });
314
+ });
315
+
209
316
group ('ComposeBox textCapitalization' , () {
210
317
void checkComposeBoxTextFields (WidgetTester tester, {
211
318
required bool expectTopicTextField,
0 commit comments