@@ -96,6 +96,23 @@ void main() {
96
96
..url.path.equals ('/api/v1/users/me/${narrow .streamId }/topics' );
97
97
}
98
98
99
+ /// Set the content input's text to [content] , using [WidgetTester.enterText] .
100
+ Future <void > enterContent (WidgetTester tester, {
101
+ required ChannelNarrow narrow,
102
+ required String content,
103
+ }) async {
104
+ final contentInputFinder = find.byWidgetPredicate (
105
+ (widget) => widget is TextField && widget.controller is ComposeContentController );
106
+
107
+ await tester.enterText (contentInputFinder, content);
108
+ }
109
+
110
+ Future <void > tapSendButton (WidgetTester tester) async {
111
+ connection.prepare (json: SendMessageResult (id: 123 ).toJson ());
112
+ await tester.tap (find.byIcon (ZulipIcons .send));
113
+ await tester.pump (Duration .zero);
114
+ }
115
+
99
116
group ('ComposeContentController' , () {
100
117
group ('insertPadded' , () {
101
118
// Like `parseMarkedText` in test/model/autocomplete_test.dart,
@@ -198,6 +215,95 @@ void main() {
198
215
});
199
216
});
200
217
218
+ group ('length validation' , () {
219
+ final channel = eg.stream ();
220
+
221
+ /// String where the number of Unicode code points is [n]
222
+ /// and the number of UTF-16 code units is higher.
223
+ String makeStringWithCodePoints (int n) {
224
+ assert (n >= 5 );
225
+ const graphemeCluster = '👨👩👦' ;
226
+ assert (graphemeCluster.runes.length == 5 );
227
+ assert (graphemeCluster.length == 8 );
228
+ assert (graphemeCluster.characters.length == 1 );
229
+
230
+ final result =
231
+ graphemeCluster * (n ~ / 5 )
232
+ + 'a' * (n % 5 );
233
+ assert (result.runes.length == n);
234
+
235
+ return result;
236
+ }
237
+
238
+ group ('content' , () {
239
+ void doTest (String description, {required String content, required bool expectError}) {
240
+ testWidgets (description, (tester) async {
241
+ TypingNotifier .debugEnable = false ;
242
+ addTearDown (TypingNotifier .debugReset);
243
+
244
+ final narrow = ChannelNarrow (channel.streamId);
245
+ await prepareComposeBox (tester, narrow: narrow, streams: [channel]);
246
+ await enterTopic (tester, narrow: narrow, topic: 'some topic' );
247
+ await enterContent (tester, narrow: narrow, content: content);
248
+
249
+ await tapSendButton (tester);
250
+ if (expectError) {
251
+ await tester.tap (find.byWidget (checkErrorDialog (tester,
252
+ expectedTitle: 'Message not sent' ,
253
+ expectedMessage: 'Message length shouldn\' t be greater than 10000 characters.' )));
254
+ } else {
255
+ checkNoErrorDialog (tester);
256
+ }
257
+ });
258
+ }
259
+
260
+ doTest ('too-long content is rejected' ,
261
+ content: makeStringWithCodePoints (kMaxMessageLengthCodePoints + 1 ), expectError: true );
262
+
263
+ // TODO(#1238) unskip
264
+ // doTest('max-length content not rejected',
265
+ // content: makeStringWithCodePoints(kMaxMessageLengthCodePoints), expectError: false);
266
+
267
+ // TODO(#1238) replace with above commented-out test
268
+ doTest ('some content not rejected' ,
269
+ content: 'a' * kMaxMessageLengthCodePoints, expectError: false );
270
+ });
271
+
272
+ group ('topic' , () {
273
+ void doTest (String description, {required String topic, required bool expectError}) {
274
+ testWidgets (description, (tester) async {
275
+ TypingNotifier .debugEnable = false ;
276
+ addTearDown (TypingNotifier .debugReset);
277
+
278
+ final narrow = ChannelNarrow (channel.streamId);
279
+ await prepareComposeBox (tester, narrow: narrow, streams: [channel]);
280
+ await enterTopic (tester, narrow: narrow, topic: topic);
281
+ await enterContent (tester, narrow: narrow, content: 'some content' );
282
+
283
+ await tapSendButton (tester);
284
+ if (expectError) {
285
+ await tester.tap (find.byWidget (checkErrorDialog (tester,
286
+ expectedTitle: 'Message not sent' ,
287
+ expectedMessage: 'Topic length shouldn\' t be greater than 60 characters.' )));
288
+ } else {
289
+ checkNoErrorDialog (tester);
290
+ }
291
+ });
292
+ }
293
+
294
+ doTest ('too-long topic is rejected' ,
295
+ topic: makeStringWithCodePoints (kMaxTopicLengthCodePoints + 1 ), expectError: true );
296
+
297
+ // TODO(#1238) unskip
298
+ // doTest('max-length topic not rejected',
299
+ // topic: makeStringWithCodePoints(kMaxTopicLengthCodePoints), expectError: false);
300
+
301
+ // TODO(#1238) replace with above commented-out test
302
+ doTest ('some topic not rejected' ,
303
+ topic: 'a' * kMaxTopicLengthCodePoints, expectError: false );
304
+ });
305
+ });
306
+
201
307
group ('ComposeBox textCapitalization' , () {
202
308
void checkComposeBoxTextFields (WidgetTester tester, {
203
309
required bool expectTopicTextField,
0 commit comments