1
1
import 'dart:convert' ;
2
+ import 'dart:io' ;
2
3
import 'dart:typed_data' ;
3
4
4
5
import 'package:checks/checks.dart' ;
5
6
import 'package:collection/collection.dart' ;
6
7
import 'package:fake_async/fake_async.dart' ;
7
8
import 'package:firebase_messaging/firebase_messaging.dart' ;
9
+ import 'package:flutter/foundation.dart' ;
8
10
import 'package:flutter/material.dart' ;
9
11
import 'package:flutter_local_notifications/flutter_local_notifications.dart' hide Message, Person;
10
12
import 'package:flutter_test/flutter_test.dart' ;
13
+ import 'package:http/http.dart' as http;
14
+ import 'package:http/testing.dart' as http_testing;
11
15
import 'package:zulip/api/model/model.dart' ;
12
16
import 'package:zulip/api/notifications.dart' ;
13
17
import 'package:zulip/host/android_notifications.dart' ;
@@ -25,6 +29,7 @@ import 'package:zulip/widgets/theme.dart';
25
29
import '../fake_async.dart' ;
26
30
import '../model/binding.dart' ;
27
31
import '../example_data.dart' as eg;
32
+ import '../test_images.dart' ;
28
33
import '../test_navigation.dart' ;
29
34
import '../widgets/message_list_checks.dart' ;
30
35
import '../widgets/page_checks.dart' ;
@@ -79,6 +84,15 @@ void main() {
79
84
TestZulipBinding .ensureInitialized ();
80
85
final zulipLocalizations = GlobalLocalizations .zulipLocalizations;
81
86
87
+ final mockHttpClient = http_testing.MockClient (
88
+ (request) async => http.Response .bytes (kSolidBlueAvatar, HttpStatus .ok));
89
+
90
+ void Function () run (Future <void > Function (FakeAsync async ) callback) {
91
+ return () => http.runWithClient (() {
92
+ return awaitFakeAsync (callback);
93
+ }, () => mockHttpClient);
94
+ }
95
+
82
96
Future <void > init () async {
83
97
addTearDown (testBinding.reset);
84
98
testBinding.firebaseMessagingInitialToken = '012abc' ;
@@ -135,7 +149,9 @@ void main() {
135
149
..text.equals (messageData.content)
136
150
..timestampMs.equals (messageData.time * 1000 )
137
151
..person.which ((it) => it.isNotNull ()
138
- ..iconBitmap.which ((it) => isLast ? it.isNotNull () : it.isNull ())
152
+ ..iconBitmap.which ((it) => isLast
153
+ ? it.isNotNull ().deepEquals (kSolidBlueAvatar)
154
+ : it.isNull ())
139
155
..key.equals (expectedSenderKey)
140
156
..name.equals (messageData.senderFullName));
141
157
});
@@ -221,7 +237,7 @@ void main() {
221
237
async .flushMicrotasks ();
222
238
}
223
239
224
- test ('stream message' , () => awaitFakeAsync ((async ) async {
240
+ test ('stream message' , run ((async ) async {
225
241
await init ();
226
242
final stream = eg.stream ();
227
243
final message = eg.streamMessage (stream: stream);
@@ -231,7 +247,7 @@ void main() {
231
247
expectedTagComponent: 'stream:${message .streamId }:${message .topic }' );
232
248
}));
233
249
234
- test ('stream message: multiple messages, same topic' , () => awaitFakeAsync ((async ) async {
250
+ test ('stream message: multiple messages, same topic' , run ((async ) async {
235
251
await init ();
236
252
final stream = eg.stream ();
237
253
const topic = 'topic 1' ;
@@ -267,7 +283,67 @@ void main() {
267
283
expectedTagComponent: expectedTagComponent);
268
284
}));
269
285
270
- test ('stream message: stream name omitted' , () => awaitFakeAsync ((async ) async {
286
+ test ('stream message: multiple messages, different topics' , run ((async ) async {
287
+ await init ();
288
+ final stream = eg.stream ();
289
+ const topicA = 'topic A' ;
290
+ const topicB = 'topic B' ;
291
+ final message1 = eg.streamMessage (topic: topicA, stream: stream);
292
+ final data1 = messageFcmMessage (message1, streamName: stream.name);
293
+ final message2 = eg.streamMessage (topic: topicB, stream: stream);
294
+ final data2 = messageFcmMessage (message2, streamName: stream.name);
295
+ final message3 = eg.streamMessage (topic: topicA, stream: stream);
296
+ final data3 = messageFcmMessage (message3, streamName: stream.name);
297
+
298
+ await receiveFcmMessage (async , data1);
299
+ checkNotification (data1,
300
+ messageStyleMessages: [data1],
301
+ expectedIsGroupConversation: true ,
302
+ expectedTitle: '#${stream .name } > $topicA ' ,
303
+ expectedTagComponent: 'stream:${stream .streamId }:$topicA ' );
304
+
305
+ await receiveFcmMessage (async , data2);
306
+ checkNotification (data2,
307
+ messageStyleMessages: [data2],
308
+ expectedIsGroupConversation: true ,
309
+ expectedTitle: '#${stream .name } > $topicB ' ,
310
+ expectedTagComponent: 'stream:${stream .streamId }:$topicB ' );
311
+
312
+ await receiveFcmMessage (async , data3);
313
+ checkNotification (data3,
314
+ messageStyleMessages: [data1, data3],
315
+ expectedIsGroupConversation: true ,
316
+ expectedTitle: '#${stream .name } > $topicA ' ,
317
+ expectedTagComponent: 'stream:${stream .streamId }:$topicA ' );
318
+ }));
319
+
320
+ test ('stream message: conversation stays same when stream is renamed' , run ((async ) async {
321
+ await init ();
322
+ var stream = eg.stream (streamId: 1 , name: 'Before' );
323
+ const topic = 'topic' ;
324
+ final message1 = eg.streamMessage (topic: topic, stream: stream);
325
+ final data1 = messageFcmMessage (message1, streamName: stream.name);
326
+
327
+ await receiveFcmMessage (async , data1);
328
+ checkNotification (data1,
329
+ messageStyleMessages: [data1],
330
+ expectedIsGroupConversation: true ,
331
+ expectedTitle: '#Before > $topic ' ,
332
+ expectedTagComponent: 'stream:${stream .streamId }:$topic ' );
333
+
334
+ stream = eg.stream (streamId: 1 , name: 'After' );
335
+ final message2 = eg.streamMessage (topic: topic, stream: stream);
336
+ final data2 = messageFcmMessage (message2, streamName: stream.name);
337
+
338
+ await receiveFcmMessage (async , data2);
339
+ checkNotification (data2,
340
+ messageStyleMessages: [data1, data2],
341
+ expectedIsGroupConversation: true ,
342
+ expectedTitle: '#After > $topic ' ,
343
+ expectedTagComponent: 'stream:${stream .streamId }:$topic ' );
344
+ }));
345
+
346
+ test ('stream message: stream name omitted' , () => run ((async ) async {
271
347
await init ();
272
348
final stream = eg.stream ();
273
349
final message = eg.streamMessage (stream: stream);
@@ -277,7 +353,7 @@ void main() {
277
353
expectedTagComponent: 'stream:${message .streamId }:${message .topic }' );
278
354
}));
279
355
280
- test ('group DM: 3 users' , () => awaitFakeAsync ((async ) async {
356
+ test ('group DM: 3 users' , () => run ((async ) async {
281
357
await init ();
282
358
final message = eg.dmMessage (from: eg.thirdUser, to: [eg.otherUser, eg.selfUser]);
283
359
await checkNotifications (async , messageFcmMessage (message),
@@ -286,7 +362,7 @@ void main() {
286
362
expectedTagComponent: 'dm:${message .allRecipientIds .join ("," )}' );
287
363
}));
288
364
289
- test ('group DM: more than 3 users' , () => awaitFakeAsync ((async ) async {
365
+ test ('group DM: more than 3 users' , () => run ((async ) async {
290
366
await init ();
291
367
final message = eg.dmMessage (from: eg.thirdUser,
292
368
to: [eg.otherUser, eg.selfUser, eg.fourthUser]);
@@ -296,7 +372,31 @@ void main() {
296
372
expectedTagComponent: 'dm:${message .allRecipientIds .join ("," )}' );
297
373
}));
298
374
299
- test ('1:1 DM' , () => awaitFakeAsync ((async ) async {
375
+ test ('group DM: title updates with latest sender' , () => run ((async ) async {
376
+ await init ();
377
+ final message1 = eg.dmMessage (from: eg.otherUser, to: [eg.selfUser, eg.thirdUser]);
378
+ final data1 = messageFcmMessage (message1);
379
+ final message2 = eg.dmMessage (from: eg.thirdUser, to: [eg.selfUser, eg.otherUser]);
380
+ final data2 = messageFcmMessage (message2);
381
+
382
+ final expectedTagComponent = 'dm:${message1 .allRecipientIds .join ("," )}' ;
383
+
384
+ await receiveFcmMessage (async , data1);
385
+ checkNotification (data1,
386
+ messageStyleMessages: [data1],
387
+ expectedIsGroupConversation: true ,
388
+ expectedTitle: "${eg .otherUser .fullName } to you and 1 other" ,
389
+ expectedTagComponent: expectedTagComponent);
390
+
391
+ await receiveFcmMessage (async , data2);
392
+ checkNotification (data2,
393
+ messageStyleMessages: [data1, data2],
394
+ expectedIsGroupConversation: true ,
395
+ expectedTitle: "${eg .thirdUser .fullName } to you and 1 other" ,
396
+ expectedTagComponent: expectedTagComponent);
397
+ }));
398
+
399
+ test ('1:1 DM' , () => run ((async ) async {
300
400
await init ();
301
401
final message = eg.dmMessage (from: eg.otherUser, to: [eg.selfUser]);
302
402
await checkNotifications (async , messageFcmMessage (message),
@@ -305,7 +405,61 @@ void main() {
305
405
expectedTagComponent: 'dm:${message .allRecipientIds .join ("," )}' );
306
406
}));
307
407
308
- test ('self-DM' , () => awaitFakeAsync ((async ) async {
408
+ test ('1:1 DM: title updates when sender name changes' , () => run ((async ) async {
409
+ await init ();
410
+ final otherUser = eg.user (fullName: 'Before' );
411
+ final message1 = eg.dmMessage (from: otherUser, to: [eg.selfUser]);
412
+ final data1 = messageFcmMessage (message1);
413
+
414
+ final expectedTagComponent = 'dm:${message1 .allRecipientIds .join ("," )}' ;
415
+
416
+ await receiveFcmMessage (async , data1);
417
+ checkNotification (data1,
418
+ messageStyleMessages: [data1],
419
+ expectedIsGroupConversation: false ,
420
+ expectedTitle: 'Before' ,
421
+ expectedTagComponent: expectedTagComponent);
422
+
423
+ otherUser.fullName = 'After' ;
424
+ final message2 = eg.dmMessage (from: otherUser, to: [eg.selfUser]);
425
+ final data2 = messageFcmMessage (message2);
426
+
427
+ await receiveFcmMessage (async , data2);
428
+ checkNotification (data2,
429
+ messageStyleMessages: [data1, data2],
430
+ expectedIsGroupConversation: false ,
431
+ expectedTitle: 'After' ,
432
+ expectedTagComponent: expectedTagComponent);
433
+ }));
434
+
435
+ test ('1:1 DM: conversation stays same when sender email changes' , () => run ((async ) async {
436
+ await init ();
437
+ final otherUser
= eg.
user (email
: '[email protected] ' );
438
+ final message1 = eg.dmMessage (from: otherUser, to: [eg.selfUser]);
439
+ final data1 = messageFcmMessage (message1);
440
+
441
+ final expectedTagComponent = 'dm:${message1 .allRecipientIds .join ("," )}' ;
442
+
443
+ await receiveFcmMessage (async , data1);
444
+ checkNotification (data1,
445
+ messageStyleMessages: [data1],
446
+ expectedIsGroupConversation: false ,
447
+ expectedTitle: otherUser.fullName,
448
+ expectedTagComponent: expectedTagComponent);
449
+
450
+ otherUser.email
= '[email protected] ' ;
451
+ final message2 = eg.dmMessage (from: otherUser, to: [eg.selfUser]);
452
+ final data2 = messageFcmMessage (message2);
453
+
454
+ await receiveFcmMessage (async , data2);
455
+ checkNotification (data2,
456
+ messageStyleMessages: [data1, data2],
457
+ expectedIsGroupConversation: false ,
458
+ expectedTitle: otherUser.fullName,
459
+ expectedTagComponent: expectedTagComponent);
460
+ }));
461
+
462
+ test ('self-DM' , () => run ((async ) async {
309
463
await init ();
310
464
final message = eg.dmMessage (from: eg.selfUser, to: []);
311
465
await checkNotifications (async , messageFcmMessage (message),
0 commit comments