@@ -1366,24 +1366,174 @@ void main() {
1366
1366
of: find.byType (MessageItem ),
1367
1367
matching: find.text (content, findRichText: true )).hitTestable ();
1368
1368
1369
- testWidgets ('sent message appear in message list after debounce timeout' , (tester) async {
1370
- await setupMessageListPage (tester,
1371
- narrow: eg.topicNarrow (stream.streamId, 'topic' ), streams: [stream],
1372
- messages: []);
1369
+ Finder messageIsntSentErrorFinder = find.text (
1370
+ 'MESSAGE ISN\' T SENT. CHECK YOUR CONNECTION.' ).hitTestable ();
1373
1371
1374
- connection.prepare (json: SendMessageResult (id: 1 ).toJson ());
1372
+ Future <void > sendMessageAndSucceed (WidgetTester tester, {
1373
+ Duration delay = Duration .zero,
1374
+ }) async {
1375
+ connection.prepare (json: SendMessageResult (id: 1 ).toJson (), delay: delay);
1376
+ await tester.enterText (contentInputFinder, content);
1377
+ await tester.tap (find.byIcon (ZulipIcons .send));
1378
+ await tester.pump (Duration .zero);
1379
+ }
1380
+
1381
+ Future <void > sendMessageAndFail (WidgetTester tester) async {
1382
+ // Send a message and fail. Dismiss the error dialog as it pops up.
1383
+ connection.prepare (apiException: eg.apiBadRequest ());
1375
1384
await tester.enterText (contentInputFinder, content);
1376
1385
await tester.tap (find.byIcon (ZulipIcons .send));
1377
1386
await tester.pump (Duration .zero);
1387
+ await tester.tap (find.byWidget (
1388
+ checkErrorDialog (tester, expectedTitle: 'Message not sent' )));
1389
+ await tester.pump ();
1390
+ check (outboxMessageFinder).findsOne ();
1391
+ check (messageIsntSentErrorFinder).findsOne ();
1392
+ }
1393
+
1394
+ testWidgets ('sent message appear in message list after debounce timeout' , (tester) async {
1395
+ await setupMessageListPage (tester,
1396
+ narrow: eg.topicNarrow (stream.streamId, 'topic' ), streams: [stream],
1397
+ messages: []);
1398
+ await sendMessageAndSucceed (tester);
1378
1399
check (outboxMessageFinder).findsNothing ();
1379
1400
1380
1401
await tester.pump (kLocalEchoDebounceDuration);
1381
1402
check (outboxMessageFinder).findsOne ();
1403
+ check (find.descendant (
1404
+ of: find.byType (MessageItem ),
1405
+ matching: find.byType (LinearProgressIndicator ))).findsOne ();
1382
1406
1383
1407
await store.handleEvent (eg.messageEvent (
1384
1408
eg.streamMessage (stream: stream, topic: 'topic' ),
1385
1409
localMessageId: store.outboxMessages.keys.single));
1386
1410
});
1411
+
1412
+ testWidgets ('failed to send message, retrieve the content to compose box' , (tester) async {
1413
+ await setupMessageListPage (tester,
1414
+ narrow: eg.topicNarrow (stream.streamId, 'topic' ), streams: [stream],
1415
+ messages: []);
1416
+ await sendMessageAndFail (tester);
1417
+
1418
+ final controller = tester.state <ComposeBoxState >(find.byType (ComposeBox )).controller;
1419
+ check (controller.content).text.isNotNull ().isEmpty ();
1420
+
1421
+ // Tap the message. This should put its content back into the compose box
1422
+ // and remove it.
1423
+ await tester.tap (outboxMessageFinder);
1424
+ await tester.pump ();
1425
+ check (outboxMessageFinder).findsNothing ();
1426
+ check (controller.content).text.equals ('$content \n\n ' );
1427
+
1428
+ await tester.pump (kLocalEchoDebounceDuration);
1429
+ });
1430
+
1431
+ testWidgets ('message sent, reaches wait period time limit and fail, retrieve the content to compose box, then message event arrives' , (tester) async {
1432
+ await setupMessageListPage (tester,
1433
+ narrow: eg.topicNarrow (stream.streamId, 'topic' ), streams: [stream],
1434
+ messages: []);
1435
+ await sendMessageAndSucceed (tester);
1436
+ await tester.pump (kSendMessageRetryWaitPeriod);
1437
+ check (messageIsntSentErrorFinder).findsOne ();
1438
+ final localMessageId = store.outboxMessages.keys.single;
1439
+
1440
+ final controller = tester.state <ComposeBoxState >(find.byType (ComposeBox )).controller;
1441
+ check (controller.content).text.isNotNull ().isEmpty ();
1442
+
1443
+ // Tap the message. This should put its content back into the compose box
1444
+ // and remove it.
1445
+ await tester.tap (outboxMessageFinder);
1446
+ await tester.pump ();
1447
+ check (outboxMessageFinder).findsNothing ();
1448
+ check (controller.content).text.equals ('$content \n\n ' );
1449
+ check (store.outboxMessages).isEmpty ();
1450
+
1451
+ // While `localMessageId` is no longer in store, there should be no error
1452
+ // when a message event refers to it.
1453
+ await store.handleEvent (eg.messageEvent (
1454
+ eg.streamMessage (stream: stream, topic: 'topic' ),
1455
+ localMessageId: localMessageId));
1456
+ });
1457
+
1458
+
1459
+ testWidgets ('tapping does nothing if compose box is not offered' , (tester) async {
1460
+ final messages = [eg.streamMessage (stream: stream, topic: 'some topic' )];
1461
+ await setupMessageListPage (tester,
1462
+ narrow: const CombinedFeedNarrow (), streams: [stream], subscriptions: [eg.subscription (stream)],
1463
+ messages: messages);
1464
+
1465
+ // Navigate to a message list page in a topic narrow,
1466
+ // which has a compose box.
1467
+ connection.prepare (json:
1468
+ eg.newestGetMessagesResult (foundOldest: true , messages: messages).toJson ());
1469
+ await tester.tap (find.text ('some topic' ));
1470
+ await tester.pump (); // handle tap
1471
+ await tester.pump (); // wait for navigation
1472
+ await sendMessageAndFail (tester);
1473
+
1474
+ // Navigate back to the message list page without a compose box,
1475
+ // where the failed to send message should still be visible.
1476
+ await tester.pageBack ();
1477
+ await tester.pump (); // handle tap
1478
+ await tester.pump (); // wait for navigation
1479
+ check (contentInputFinder).findsNothing ();
1480
+ check (outboxMessageFinder).findsOne ();
1481
+ check (messageIsntSentErrorFinder).findsOne ();
1482
+
1483
+ // Tap the failed to send message.
1484
+ // This should not remove it from the message list.
1485
+ await tester.tap (outboxMessageFinder);
1486
+ await tester.pump ();
1487
+ check (outboxMessageFinder).findsOne ();
1488
+ });
1489
+
1490
+ testWidgets ('tapping does nothing if message is still being sent' , (tester) async {
1491
+ await setupMessageListPage (tester,
1492
+ narrow: eg.topicNarrow (stream.streamId, 'topic' ), streams: [stream],
1493
+ messages: []);
1494
+ final controller = tester.state <ComposeBoxState >(find.byType (ComposeBox )).controller;
1495
+
1496
+ // Send a message and wait until the debounce timer expires but before
1497
+ // the message is successfully sent.
1498
+ await sendMessageAndSucceed (tester,
1499
+ delay: kLocalEchoDebounceDuration + Duration (seconds: 1 ));
1500
+ await tester.pump (kLocalEchoDebounceDuration);
1501
+ check (controller.content).text.isNotNull ().isEmpty ();
1502
+
1503
+ await tester.tap (outboxMessageFinder);
1504
+ await tester.pump ();
1505
+ check (outboxMessageFinder).findsOne ();
1506
+ check (controller.content).text.isNotNull ().isEmpty ();
1507
+
1508
+ // Wait till the send request completes. The outbox message should
1509
+ // remain visible because the message event didn't arrive.
1510
+ await tester.pump (Duration (seconds: 1 ));
1511
+ check (outboxMessageFinder).findsOne ();
1512
+ check (controller.content).text.isNotNull ().isEmpty ();
1513
+
1514
+ // Dispose pending timers from the message store.
1515
+ store.dispose ();
1516
+ });
1517
+
1518
+ testWidgets ('tapping does nothing if message was successfully sent and before message event arrives' , (tester) async {
1519
+ await setupMessageListPage (tester,
1520
+ narrow: eg.topicNarrow (stream.streamId, 'topic' ), streams: [stream],
1521
+ messages: []);
1522
+ final controller = tester.state <ComposeBoxState >(find.byType (ComposeBox )).controller;
1523
+
1524
+ // Send a message and wait until the debounce timer expires.
1525
+ await sendMessageAndSucceed (tester);
1526
+ await tester.pump (kLocalEchoDebounceDuration);
1527
+ check (controller.content).text.isNotNull ().isEmpty ();
1528
+
1529
+ await tester.tap (outboxMessageFinder);
1530
+ await tester.pump ();
1531
+ check (outboxMessageFinder).findsOne ();
1532
+ check (controller.content).text.isNotNull ().isEmpty ();
1533
+
1534
+ // Dispose pending timers from the message store.
1535
+ store.dispose ();
1536
+ });
1387
1537
});
1388
1538
1389
1539
group ('Starred messages' , () {
0 commit comments