chore: 完成v6.7.0版本迭代更新
本次更新涵盖多个功能模块的优化与新增: 1. 新增Wi-Fi直连配对方式与协作画布模块 2. 完成设备管理重命名API与前端适配 3. 优化日签卡片空数据保护与UI细节 4. 新增剪贴板工具与每日运势会话 5. 修复应用锁恢复、语音消息录制等已知问题 6. 完善文件传输统计与配对逻辑 7. 更新安卓权限配置与iOS隐私描述 8. 新增自动化测试脚本与文档 9. 清理旧版审计报告与测试文件
This commit is contained in:
592
test/file_transfer/delivery_receipt_test.dart
Normal file
592
test/file_transfer/delivery_receipt_test.dart
Normal file
@@ -0,0 +1,592 @@
|
||||
// ============================================================
|
||||
// 闲言APP — 送达回执集成测试
|
||||
// 创建时间: 2026-05-14
|
||||
// 更新时间: 2026-05-14
|
||||
// 作用: 送达回执集成测试
|
||||
// 上次更新: 初始创建
|
||||
// ============================================================
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:xianyan/features/file_transfer/models/transfer_message.dart';
|
||||
import 'package:xianyan/features/file_transfer/presentation/widgets/receipt_indicator.dart';
|
||||
import 'package:xianyan/features/file_transfer/services/delivery_receipt_service.dart';
|
||||
import 'package:xianyan/features/file_transfer/services/signaling_service.dart';
|
||||
|
||||
class MockSignalingService extends Mock implements SignalingService {}
|
||||
|
||||
class FakeSignalingMessage extends Fake implements SignalingMessage {}
|
||||
|
||||
void main() {
|
||||
setUpAll(() {
|
||||
registerFallbackValue(FakeSignalingMessage());
|
||||
});
|
||||
|
||||
group('DeliveryStatus', () {
|
||||
test('enum values have correct id/label/emoji', () {
|
||||
expect(DeliveryStatus.sending.id, 'sending');
|
||||
expect(DeliveryStatus.sending.label, '发送中');
|
||||
expect(DeliveryStatus.sending.emoji, '📤');
|
||||
|
||||
expect(DeliveryStatus.sent.id, 'sent');
|
||||
expect(DeliveryStatus.sent.label, '已发送');
|
||||
expect(DeliveryStatus.sent.emoji, '✓');
|
||||
|
||||
expect(DeliveryStatus.delivered.id, 'delivered');
|
||||
expect(DeliveryStatus.delivered.label, '已送达');
|
||||
expect(DeliveryStatus.delivered.emoji, '✓✓');
|
||||
|
||||
expect(DeliveryStatus.read.id, 'read');
|
||||
expect(DeliveryStatus.read.label, '已读');
|
||||
expect(DeliveryStatus.read.emoji, '✓✓');
|
||||
});
|
||||
|
||||
test('fromId returns correct status', () {
|
||||
expect(DeliveryStatus.fromId('sending'), DeliveryStatus.sending);
|
||||
expect(DeliveryStatus.fromId('sent'), DeliveryStatus.sent);
|
||||
expect(DeliveryStatus.fromId('delivered'), DeliveryStatus.delivered);
|
||||
expect(DeliveryStatus.fromId('read'), DeliveryStatus.read);
|
||||
});
|
||||
|
||||
test('fromId returns sending for unknown id', () {
|
||||
expect(DeliveryStatus.fromId('unknown'), DeliveryStatus.sending);
|
||||
expect(DeliveryStatus.fromId(''), DeliveryStatus.sending);
|
||||
});
|
||||
|
||||
test('tryFromId returns correct status', () {
|
||||
expect(DeliveryStatus.tryFromId('sending'), DeliveryStatus.sending);
|
||||
expect(DeliveryStatus.tryFromId('sent'), DeliveryStatus.sent);
|
||||
expect(DeliveryStatus.tryFromId('delivered'), DeliveryStatus.delivered);
|
||||
expect(DeliveryStatus.tryFromId('read'), DeliveryStatus.read);
|
||||
});
|
||||
|
||||
test('tryFromId returns null for null or unknown id', () {
|
||||
expect(DeliveryStatus.tryFromId(null), isNull);
|
||||
expect(DeliveryStatus.tryFromId('unknown'), isNull);
|
||||
expect(DeliveryStatus.tryFromId(''), isNull);
|
||||
});
|
||||
|
||||
test('isRead is true only for read status', () {
|
||||
expect(DeliveryStatus.sending.isRead, false);
|
||||
expect(DeliveryStatus.sent.isRead, false);
|
||||
expect(DeliveryStatus.delivered.isRead, false);
|
||||
expect(DeliveryStatus.read.isRead, true);
|
||||
});
|
||||
|
||||
test('isDelivered is true for delivered and read', () {
|
||||
expect(DeliveryStatus.sending.isDelivered, false);
|
||||
expect(DeliveryStatus.sent.isDelivered, false);
|
||||
expect(DeliveryStatus.delivered.isDelivered, true);
|
||||
expect(DeliveryStatus.read.isDelivered, true);
|
||||
});
|
||||
|
||||
test('values contains all four statuses', () {
|
||||
expect(DeliveryStatus.values.length, 4);
|
||||
expect(DeliveryStatus.values, containsAll([
|
||||
DeliveryStatus.sending,
|
||||
DeliveryStatus.sent,
|
||||
DeliveryStatus.delivered,
|
||||
DeliveryStatus.read,
|
||||
]));
|
||||
});
|
||||
});
|
||||
|
||||
group('DeliveryReceiptEvent', () {
|
||||
test('fields are set correctly', () {
|
||||
final now = DateTime.now();
|
||||
final event = DeliveryReceiptEvent(
|
||||
messageId: 'msg-001',
|
||||
status: DeliveryStatus.delivered,
|
||||
fromDeviceId: 'device-A',
|
||||
timestamp: now,
|
||||
);
|
||||
|
||||
expect(event.messageId, 'msg-001');
|
||||
expect(event.status, DeliveryStatus.delivered);
|
||||
expect(event.fromDeviceId, 'device-A');
|
||||
expect(event.timestamp, now);
|
||||
});
|
||||
|
||||
test('toString contains messageId and status id', () {
|
||||
final event = DeliveryReceiptEvent(
|
||||
messageId: 'msg-002',
|
||||
status: DeliveryStatus.read,
|
||||
fromDeviceId: 'device-B',
|
||||
timestamp: DateTime.now(),
|
||||
);
|
||||
|
||||
final str = event.toString();
|
||||
expect(str, contains('msg-002'));
|
||||
expect(str, contains('read'));
|
||||
});
|
||||
});
|
||||
|
||||
group('DeliveryReceiptService', () {
|
||||
late MockSignalingService mockSignaling;
|
||||
late DeliveryReceiptService service;
|
||||
late StreamController<SignalingMessage> messageController;
|
||||
|
||||
setUp(() {
|
||||
mockSignaling = MockSignalingService();
|
||||
messageController = StreamController<SignalingMessage>.broadcast();
|
||||
when(() => mockSignaling.onMessage).thenAnswer((_) => messageController.stream);
|
||||
when(() => mockSignaling.deviceId).thenReturn('device-A');
|
||||
service = DeliveryReceiptService(mockSignaling);
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await service.dispose();
|
||||
await messageController.close();
|
||||
});
|
||||
|
||||
test('startListening subscribes to signaling messages', () {
|
||||
service.startListening();
|
||||
verify(() => mockSignaling.onMessage).called(1);
|
||||
});
|
||||
|
||||
test('stopListening cancels subscription', () {
|
||||
service.startListening();
|
||||
service.stopListening();
|
||||
verify(() => mockSignaling.onMessage).called(greaterThanOrEqualTo(1));
|
||||
});
|
||||
|
||||
test('sendDeliveryAck sends correct signaling message', () {
|
||||
service.sendDeliveryAck(
|
||||
targetId: 'device-B',
|
||||
messageId: 'msg-001',
|
||||
status: DeliveryStatus.delivered,
|
||||
);
|
||||
|
||||
verify(() => mockSignaling.sendCustomMessage(any(
|
||||
that: isA<SignalingMessage>()
|
||||
.having((m) => m.type, 'type', SignalingMessageType.deliveryAck)
|
||||
.having((m) => m.to, 'to', 'device-B')
|
||||
.having((m) => m.from, 'from', 'device-A')
|
||||
.having((m) => m.payload!['messageId'], 'messageId', 'msg-001')
|
||||
.having((m) => m.payload!['status'], 'status', 'delivered')))).called(1);
|
||||
});
|
||||
|
||||
test('sendDeliveryAck includes timestamp in payload', () {
|
||||
service.sendDeliveryAck(
|
||||
targetId: 'device-B',
|
||||
messageId: 'msg-001',
|
||||
status: DeliveryStatus.read,
|
||||
);
|
||||
|
||||
verify(() => mockSignaling.sendCustomMessage(any(
|
||||
that: isA<SignalingMessage>()
|
||||
.having((m) => m.payload!['timestamp'], 'timestamp', isA<int>())))).called(1);
|
||||
});
|
||||
|
||||
test('markAsDelivered sends delivered status', () async {
|
||||
await service.markAsDelivered('device-B', 'msg-002');
|
||||
|
||||
verify(() => mockSignaling.sendCustomMessage(any(
|
||||
that: isA<SignalingMessage>()
|
||||
.having((m) => m.payload!['messageId'], 'messageId', 'msg-002')
|
||||
.having((m) => m.payload!['status'], 'status', 'delivered')))).called(1);
|
||||
});
|
||||
|
||||
test('markAsRead sends read status', () async {
|
||||
await service.markAsRead('device-B', 'msg-003');
|
||||
|
||||
verify(() => mockSignaling.sendCustomMessage(any(
|
||||
that: isA<SignalingMessage>()
|
||||
.having((m) => m.payload!['messageId'], 'messageId', 'msg-003')
|
||||
.having((m) => m.payload!['status'], 'status', 'read')))).called(1);
|
||||
});
|
||||
|
||||
test('receiptStream emits event on valid deliveryAck message', () async {
|
||||
service.startListening();
|
||||
|
||||
final events = <DeliveryReceiptEvent>[];
|
||||
service.receiptStream.listen(events.add);
|
||||
|
||||
final ts = DateTime.now().millisecondsSinceEpoch;
|
||||
messageController.add(SignalingMessage(
|
||||
type: SignalingMessageType.deliveryAck,
|
||||
from: 'device-B',
|
||||
to: 'device-A',
|
||||
payload: {
|
||||
'messageId': 'msg-100',
|
||||
'status': 'delivered',
|
||||
'timestamp': ts,
|
||||
},
|
||||
));
|
||||
|
||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||
|
||||
expect(events.length, 1);
|
||||
expect(events[0].messageId, 'msg-100');
|
||||
expect(events[0].status, DeliveryStatus.delivered);
|
||||
expect(events[0].fromDeviceId, 'device-B');
|
||||
});
|
||||
|
||||
test('receiptStream emits read status correctly', () async {
|
||||
service.startListening();
|
||||
|
||||
final events = <DeliveryReceiptEvent>[];
|
||||
service.receiptStream.listen(events.add);
|
||||
|
||||
messageController.add(SignalingMessage(
|
||||
type: SignalingMessageType.deliveryAck,
|
||||
from: 'device-C',
|
||||
to: 'device-A',
|
||||
payload: {
|
||||
'messageId': 'msg-200',
|
||||
'status': 'read',
|
||||
'timestamp': DateTime.now().millisecondsSinceEpoch,
|
||||
},
|
||||
));
|
||||
|
||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||
|
||||
expect(events.length, 1);
|
||||
expect(events[0].status, DeliveryStatus.read);
|
||||
});
|
||||
|
||||
test('receiptStream ignores non-deliveryAck messages', () async {
|
||||
service.startListening();
|
||||
|
||||
final events = <DeliveryReceiptEvent>[];
|
||||
service.receiptStream.listen(events.add);
|
||||
|
||||
messageController.add(const SignalingMessage(
|
||||
type: SignalingMessageType.textMessage,
|
||||
from: 'device-B',
|
||||
to: 'device-A',
|
||||
payload: {'text': 'hello'},
|
||||
));
|
||||
|
||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||
|
||||
expect(events, isEmpty);
|
||||
});
|
||||
|
||||
test('receiptStream ignores deliveryAck with null payload', () async {
|
||||
service.startListening();
|
||||
|
||||
final events = <DeliveryReceiptEvent>[];
|
||||
service.receiptStream.listen(events.add);
|
||||
|
||||
messageController.add(const SignalingMessage(
|
||||
type: SignalingMessageType.deliveryAck,
|
||||
from: 'device-B',
|
||||
to: 'device-A',
|
||||
));
|
||||
|
||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||
|
||||
expect(events, isEmpty);
|
||||
});
|
||||
|
||||
test('receiptStream ignores deliveryAck with empty messageId', () async {
|
||||
service.startListening();
|
||||
|
||||
final events = <DeliveryReceiptEvent>[];
|
||||
service.receiptStream.listen(events.add);
|
||||
|
||||
messageController.add(const SignalingMessage(
|
||||
type: SignalingMessageType.deliveryAck,
|
||||
from: 'device-B',
|
||||
to: 'device-A',
|
||||
payload: {
|
||||
'messageId': '',
|
||||
'status': 'delivered',
|
||||
'timestamp': 0,
|
||||
},
|
||||
));
|
||||
|
||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||
|
||||
expect(events, isEmpty);
|
||||
});
|
||||
|
||||
test('receiptStream ignores deliveryAck with empty status', () async {
|
||||
service.startListening();
|
||||
|
||||
final events = <DeliveryReceiptEvent>[];
|
||||
service.receiptStream.listen(events.add);
|
||||
|
||||
messageController.add(const SignalingMessage(
|
||||
type: SignalingMessageType.deliveryAck,
|
||||
from: 'device-B',
|
||||
to: 'device-A',
|
||||
payload: {
|
||||
'messageId': 'msg-300',
|
||||
'status': '',
|
||||
'timestamp': 0,
|
||||
},
|
||||
));
|
||||
|
||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||
|
||||
expect(events, isEmpty);
|
||||
});
|
||||
|
||||
test('receiptStream handles missing timestamp gracefully', () async {
|
||||
service.startListening();
|
||||
|
||||
final events = <DeliveryReceiptEvent>[];
|
||||
service.receiptStream.listen(events.add);
|
||||
|
||||
messageController.add(const SignalingMessage(
|
||||
type: SignalingMessageType.deliveryAck,
|
||||
from: 'device-B',
|
||||
to: 'device-A',
|
||||
payload: {
|
||||
'messageId': 'msg-400',
|
||||
'status': 'sent',
|
||||
},
|
||||
));
|
||||
|
||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||
|
||||
expect(events.length, 1);
|
||||
expect(events[0].messageId, 'msg-400');
|
||||
expect(events[0].status, DeliveryStatus.sent);
|
||||
expect(events[0].timestamp, DateTime.fromMillisecondsSinceEpoch(0));
|
||||
});
|
||||
|
||||
test('receiptStream handles unknown status id as sending', () async {
|
||||
service.startListening();
|
||||
|
||||
final events = <DeliveryReceiptEvent>[];
|
||||
service.receiptStream.listen(events.add);
|
||||
|
||||
messageController.add(SignalingMessage(
|
||||
type: SignalingMessageType.deliveryAck,
|
||||
from: 'device-B',
|
||||
to: 'device-A',
|
||||
payload: {
|
||||
'messageId': 'msg-500',
|
||||
'status': 'unknown_status',
|
||||
'timestamp': DateTime.now().millisecondsSinceEpoch,
|
||||
},
|
||||
));
|
||||
|
||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||
|
||||
expect(events.length, 1);
|
||||
expect(events[0].status, DeliveryStatus.sending);
|
||||
});
|
||||
|
||||
test('multiple receipts are emitted in order', () async {
|
||||
service.startListening();
|
||||
|
||||
final events = <DeliveryReceiptEvent>[];
|
||||
service.receiptStream.listen(events.add);
|
||||
|
||||
final ts = DateTime.now().millisecondsSinceEpoch;
|
||||
messageController.add(SignalingMessage(
|
||||
type: SignalingMessageType.deliveryAck,
|
||||
from: 'device-B',
|
||||
payload: {'messageId': 'msg-1', 'status': 'sent', 'timestamp': ts},
|
||||
));
|
||||
messageController.add(SignalingMessage(
|
||||
type: SignalingMessageType.deliveryAck,
|
||||
from: 'device-B',
|
||||
payload: {'messageId': 'msg-2', 'status': 'delivered', 'timestamp': ts},
|
||||
));
|
||||
messageController.add(SignalingMessage(
|
||||
type: SignalingMessageType.deliveryAck,
|
||||
from: 'device-B',
|
||||
payload: {'messageId': 'msg-3', 'status': 'read', 'timestamp': ts},
|
||||
));
|
||||
|
||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||
|
||||
expect(events.length, 3);
|
||||
expect(events[0].status, DeliveryStatus.sent);
|
||||
expect(events[1].status, DeliveryStatus.delivered);
|
||||
expect(events[2].status, DeliveryStatus.read);
|
||||
});
|
||||
|
||||
test('dispose closes receipt stream', () async {
|
||||
service.startListening();
|
||||
await service.dispose();
|
||||
|
||||
expect(service.receiptStream.isBroadcast, isTrue);
|
||||
});
|
||||
|
||||
test('stopListening then startListening re-subscribes', () async {
|
||||
service.startListening();
|
||||
service.stopListening();
|
||||
service.startListening();
|
||||
|
||||
final events = <DeliveryReceiptEvent>[];
|
||||
service.receiptStream.listen(events.add);
|
||||
|
||||
messageController.add(SignalingMessage(
|
||||
type: SignalingMessageType.deliveryAck,
|
||||
from: 'device-B',
|
||||
payload: {
|
||||
'messageId': 'msg-restart',
|
||||
'status': 'delivered',
|
||||
'timestamp': DateTime.now().millisecondsSinceEpoch,
|
||||
},
|
||||
));
|
||||
|
||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||
|
||||
expect(events.length, 1);
|
||||
expect(events[0].messageId, 'msg-restart');
|
||||
});
|
||||
});
|
||||
|
||||
group('TransferMessage delivery status', () {
|
||||
TransferMessage createMessage({DeliveryStatus? status}) {
|
||||
return TransferMessage(
|
||||
id: 'msg-001',
|
||||
sessionId: 'session-1',
|
||||
type: TransferMessageType.text,
|
||||
content: 'Hello',
|
||||
isRemote: false,
|
||||
timestamp: DateTime.now(),
|
||||
deliveryStatus: status,
|
||||
);
|
||||
}
|
||||
|
||||
test('deliveryStatus defaults to null', () {
|
||||
final msg = createMessage();
|
||||
expect(msg.deliveryStatus, isNull);
|
||||
});
|
||||
|
||||
test('deliveryStatus can be set to sending', () {
|
||||
final msg = createMessage(status: DeliveryStatus.sending);
|
||||
expect(msg.deliveryStatus, DeliveryStatus.sending);
|
||||
});
|
||||
|
||||
test('deliveryStatus can be set to read', () {
|
||||
final msg = createMessage(status: DeliveryStatus.read);
|
||||
expect(msg.deliveryStatus, DeliveryStatus.read);
|
||||
expect(msg.deliveryStatus!.isRead, true);
|
||||
});
|
||||
|
||||
test('copyWith updates deliveryStatus', () {
|
||||
final msg = createMessage(status: DeliveryStatus.sending);
|
||||
final updated = msg.copyWith(deliveryStatus: DeliveryStatus.delivered);
|
||||
expect(updated.deliveryStatus, DeliveryStatus.delivered);
|
||||
expect(msg.deliveryStatus, DeliveryStatus.sending);
|
||||
});
|
||||
|
||||
test('toJson includes deliveryStatus', () {
|
||||
final msg = createMessage(status: DeliveryStatus.delivered);
|
||||
final json = msg.toJson();
|
||||
expect(json['deliveryStatus'], 'delivered');
|
||||
});
|
||||
|
||||
test('fromJson restores deliveryStatus', () {
|
||||
final msg = createMessage(status: DeliveryStatus.read);
|
||||
final json = msg.toJson();
|
||||
final restored = TransferMessage.fromJson(json);
|
||||
expect(restored.deliveryStatus, DeliveryStatus.read);
|
||||
});
|
||||
|
||||
test('fromJson handles null deliveryStatus', () {
|
||||
final json = {
|
||||
'id': 'msg-1',
|
||||
'sessionId': 's1',
|
||||
'type': 'text',
|
||||
'content': 'hi',
|
||||
'isRemote': false,
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
};
|
||||
final restored = TransferMessage.fromJson(json);
|
||||
expect(restored.deliveryStatus, isNull);
|
||||
});
|
||||
|
||||
test('deliveredAt and readAt fields', () {
|
||||
final deliveredTime = DateTime(2026, 5, 14, 10, 30);
|
||||
final readTime = DateTime(2026, 5, 14, 10, 31);
|
||||
final msg = createMessage().copyWith(
|
||||
deliveryStatus: DeliveryStatus.read,
|
||||
deliveredAt: deliveredTime,
|
||||
readAt: readTime,
|
||||
);
|
||||
expect(msg.deliveredAt, deliveredTime);
|
||||
expect(msg.readAt, readTime);
|
||||
|
||||
final json = msg.toJson();
|
||||
expect(json['deliveredAt'], deliveredTime.toIso8601String());
|
||||
expect(json['readAt'], readTime.toIso8601String());
|
||||
|
||||
final restored = TransferMessage.fromJson(json);
|
||||
expect(restored.deliveredAt!.millisecondsSinceEpoch,
|
||||
deliveredTime.millisecondsSinceEpoch);
|
||||
expect(restored.readAt!.millisecondsSinceEpoch,
|
||||
readTime.millisecondsSinceEpoch);
|
||||
});
|
||||
|
||||
test('delivery status progression via copyWith', () {
|
||||
var msg = createMessage(status: DeliveryStatus.sending);
|
||||
expect(msg.deliveryStatus, DeliveryStatus.sending);
|
||||
|
||||
msg = msg.copyWith(deliveryStatus: DeliveryStatus.sent);
|
||||
expect(msg.deliveryStatus, DeliveryStatus.sent);
|
||||
|
||||
msg = msg.copyWith(deliveryStatus: DeliveryStatus.delivered);
|
||||
expect(msg.deliveryStatus, DeliveryStatus.delivered);
|
||||
expect(msg.deliveryStatus!.isDelivered, true);
|
||||
expect(msg.deliveryStatus!.isRead, false);
|
||||
|
||||
msg = msg.copyWith(deliveryStatus: DeliveryStatus.read);
|
||||
expect(msg.deliveryStatus, DeliveryStatus.read);
|
||||
expect(msg.deliveryStatus!.isDelivered, true);
|
||||
expect(msg.deliveryStatus!.isRead, true);
|
||||
});
|
||||
});
|
||||
|
||||
group('ReceiptIndicator widget', () {
|
||||
Widget buildWidget(DeliveryStatus? status) {
|
||||
return CupertinoApp(
|
||||
home: Center(
|
||||
child: ReceiptIndicator(status: status),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('renders SizedBox.shrink when status is null', (tester) async {
|
||||
await tester.pumpWidget(buildWidget(null));
|
||||
expect(find.byType(SizedBox), findsOneWidget);
|
||||
expect(find.byType(Row), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('renders sending status with emoji and label', (tester) async {
|
||||
await tester.pumpWidget(buildWidget(DeliveryStatus.sending));
|
||||
expect(find.byType(Row), findsOneWidget);
|
||||
expect(find.text('⏳'), findsOneWidget);
|
||||
expect(find.text('发送中'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('renders sent status', (tester) async {
|
||||
await tester.pumpWidget(buildWidget(DeliveryStatus.sent));
|
||||
expect(find.text('✓'), findsOneWidget);
|
||||
expect(find.text('已发送'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('renders delivered status', (tester) async {
|
||||
await tester.pumpWidget(buildWidget(DeliveryStatus.delivered));
|
||||
expect(find.text('✓✓'), findsOneWidget);
|
||||
expect(find.text('已送达'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('renders read status', (tester) async {
|
||||
await tester.pumpWidget(buildWidget(DeliveryStatus.read));
|
||||
expect(find.text('✓✓'), findsOneWidget);
|
||||
expect(find.text('已读'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('switching status updates display', (tester) async {
|
||||
await tester.pumpWidget(buildWidget(DeliveryStatus.sending));
|
||||
expect(find.text('发送中'), findsOneWidget);
|
||||
|
||||
await tester.pumpWidget(buildWidget(DeliveryStatus.read));
|
||||
expect(find.text('已读'), findsOneWidget);
|
||||
expect(find.text('发送中'), findsNothing);
|
||||
});
|
||||
});
|
||||
}
|
||||
1007
test/file_transfer/resume_transfer_test.dart
Normal file
1007
test/file_transfer/resume_transfer_test.dart
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user