dd48d1d59d
- 更新 App 主题配置 - 优化反馈页面 - 更新认证和通知测试用例
208 lines
6.2 KiB
Dart
208 lines
6.2 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import 'package:meeyao_qianwen/features/notifications/data/models/notification_item.dart';
|
|
import 'package:meeyao_qianwen/features/notifications/data/models/notification_list_result.dart';
|
|
import 'package:meeyao_qianwen/features/notifications/data/models/notification_payload.dart';
|
|
import 'package:meeyao_qianwen/features/notifications/data/repositories/notification_repository.dart';
|
|
import 'package:meeyao_qianwen/features/notifications/presentation/bloc/notification_bloc.dart';
|
|
|
|
class _FakeNotificationRepository implements NotificationRepository {
|
|
final List<NotificationItem> items = [];
|
|
int unreadCount = 0;
|
|
int markAllReadCallCount = 0;
|
|
bool failMarkRead = false;
|
|
bool failMarkAllRead = false;
|
|
|
|
@override
|
|
Future<NotificationListResult> listNotifications({
|
|
int limit = 20,
|
|
String? cursor,
|
|
String locale = 'zh',
|
|
}) async {
|
|
return NotificationListResult(
|
|
items: items,
|
|
hasMore: false,
|
|
nextCursor: null,
|
|
);
|
|
}
|
|
|
|
@override
|
|
Future<int> getUnreadCount() async => unreadCount;
|
|
|
|
@override
|
|
Future<NotificationItem> markRead({
|
|
required String notificationId,
|
|
String locale = 'zh',
|
|
}) async {
|
|
if (failMarkRead) {
|
|
throw Exception('Mark read failed');
|
|
}
|
|
final idx = items.indexWhere((i) => i.id == notificationId);
|
|
if (idx == -1) {
|
|
throw Exception('Not found');
|
|
}
|
|
items[idx] = items[idx].copyWith(isRead: true);
|
|
unreadCount = unreadCount > 0 ? unreadCount - 1 : 0;
|
|
return items[idx];
|
|
}
|
|
|
|
@override
|
|
Future<int> markAllRead() async {
|
|
if (failMarkAllRead) {
|
|
throw Exception('Mark all read failed');
|
|
}
|
|
markAllReadCallCount++;
|
|
final count = unreadCount;
|
|
for (int i = 0; i < items.length; i++) {
|
|
items[i] = items[i].copyWith(isRead: true);
|
|
}
|
|
unreadCount = 0;
|
|
return count;
|
|
}
|
|
}
|
|
|
|
NotificationItem makeItem({
|
|
String id = 'item-1',
|
|
String notificationId = 'notif-1',
|
|
bool isRead = false,
|
|
}) {
|
|
return NotificationItem(
|
|
id: id,
|
|
notificationId: notificationId,
|
|
type: 'system',
|
|
title: 'Test',
|
|
body: 'Body',
|
|
payload: const NotificationPayloadNone(),
|
|
isRead: isRead,
|
|
createdAt: DateTime(2026, 4, 10),
|
|
);
|
|
}
|
|
|
|
void main() {
|
|
group('NotificationBloc', () {
|
|
late _FakeNotificationRepository fakeRepo;
|
|
late NotificationBloc bloc;
|
|
|
|
setUp(() {
|
|
fakeRepo = _FakeNotificationRepository();
|
|
bloc = NotificationBloc(repository: fakeRepo);
|
|
});
|
|
|
|
tearDown(() {
|
|
bloc.dispose();
|
|
});
|
|
|
|
test('initial state has zero unreadCount', () {
|
|
expect(bloc.state.unreadCount, 0);
|
|
expect(bloc.state.items, isEmpty);
|
|
});
|
|
|
|
test('RefreshUnreadCount updates unreadCount', () async {
|
|
fakeRepo.unreadCount = 5;
|
|
await bloc.handleEvent(RefreshUnreadCount());
|
|
expect(bloc.state.unreadCount, 5);
|
|
});
|
|
|
|
test('MarkNotificationRead marks item as read', () async {
|
|
fakeRepo.items.add(makeItem(id: 'n1', isRead: false));
|
|
await bloc.handleEvent(LoadNotifications());
|
|
await bloc.handleEvent(MarkNotificationRead(notificationId: 'n1'));
|
|
expect(bloc.state.items.first.isRead, true);
|
|
expect(bloc.state.unreadCount, 0);
|
|
});
|
|
|
|
test(
|
|
'MarkNotificationRead does not update state when request fails',
|
|
() async {
|
|
fakeRepo.items.add(makeItem(id: 'n1', isRead: false));
|
|
fakeRepo.unreadCount = 1;
|
|
fakeRepo.failMarkRead = true;
|
|
await bloc.handleEvent(LoadNotifications());
|
|
await bloc.handleEvent(RefreshUnreadCount());
|
|
|
|
await bloc.handleEvent(MarkNotificationRead(notificationId: 'n1'));
|
|
expect(bloc.state.items.first.isRead, false);
|
|
expect(bloc.state.unreadCount, 1);
|
|
},
|
|
);
|
|
|
|
test('MarkAllNotificationsRead marks all as read', () async {
|
|
fakeRepo.items.addAll([
|
|
makeItem(id: 'n1', isRead: false),
|
|
makeItem(id: 'n2', isRead: false),
|
|
]);
|
|
fakeRepo.unreadCount = 2;
|
|
await bloc.handleEvent(LoadNotifications());
|
|
|
|
await bloc.handleEvent(MarkAllNotificationsRead());
|
|
expect(bloc.state.unreadCount, 0);
|
|
expect(bloc.state.items.every((i) => i.isRead), true);
|
|
});
|
|
|
|
test(
|
|
'MarkAllNotificationsRead does not update state when request fails',
|
|
() async {
|
|
fakeRepo.items.addAll([
|
|
makeItem(id: 'n1', isRead: false),
|
|
makeItem(id: 'n2', isRead: false),
|
|
]);
|
|
fakeRepo.unreadCount = 2;
|
|
fakeRepo.failMarkAllRead = true;
|
|
await bloc.handleEvent(LoadNotifications());
|
|
await bloc.handleEvent(RefreshUnreadCount());
|
|
|
|
await bloc.handleEvent(MarkAllNotificationsRead());
|
|
expect(bloc.state.unreadCount, 2);
|
|
expect(bloc.state.items.every((i) => !i.isRead), true);
|
|
},
|
|
);
|
|
|
|
test(
|
|
'NotificationCreatedEvent adds item and increments unreadCount',
|
|
() async {
|
|
final item = makeItem(id: 'new-1', isRead: false);
|
|
bloc.handleEvent(NotificationCreatedEvent(item: item));
|
|
expect(bloc.state.items.length, 1);
|
|
expect(bloc.state.unreadCount, 1);
|
|
},
|
|
);
|
|
|
|
test(
|
|
'NotificationCreatedEvent for read item does not increment unreadCount',
|
|
() async {
|
|
final item = makeItem(id: 'new-1', isRead: true);
|
|
bloc.handleEvent(NotificationCreatedEvent(item: item));
|
|
expect(bloc.state.items.length, 1);
|
|
expect(bloc.state.unreadCount, 0);
|
|
},
|
|
);
|
|
|
|
test(
|
|
'NotificationReadUpdatedEvent updates existing item to read',
|
|
() async {
|
|
fakeRepo.items.add(makeItem(id: 'n1', isRead: false));
|
|
fakeRepo.unreadCount = 1;
|
|
await bloc.handleEvent(LoadNotifications());
|
|
|
|
bloc.handleEvent(
|
|
NotificationReadUpdatedEvent(notificationId: 'n1', isRead: true),
|
|
);
|
|
expect(bloc.state.items.first.isRead, true);
|
|
expect(bloc.state.unreadCount, 0);
|
|
},
|
|
);
|
|
|
|
test('NotificationRevokedEvent removes item', () async {
|
|
fakeRepo.items.add(
|
|
makeItem(id: 'n1', notificationId: 'notif-1', isRead: false),
|
|
);
|
|
fakeRepo.unreadCount = 1;
|
|
await bloc.handleEvent(LoadNotifications());
|
|
|
|
bloc.handleEvent(NotificationRevokedEvent(notificationId: 'notif-1'));
|
|
expect(bloc.state.items, isEmpty);
|
|
expect(bloc.state.unreadCount, 0);
|
|
});
|
|
});
|
|
}
|