Add notification badge count and MainTabView with notification tab FRE-4740 FRE-4739
- Add getUnreadCount() endpoint to NotificationsServiceProtocol - Add NotificationUnreadCountResponse model - Add badgeCount and fetchUnreadCount() to NotificationsViewModel - Update markAsRead/markAllAsRead to decrement badge count - Create MainTabView with Home, Challenges, Clubs, Notifications tabs - Add unread badge on notification tab using .badge() modifier - Support injected ViewModel in NotificationsView for shared state - Add badge count tests to NotificationServiceTests - Fetch unread count on app launch and tab switch Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -10,6 +10,8 @@ final class MockNotificationsService: NotificationsServiceProtocol {
|
||||
var markAllCalled = false
|
||||
var listCallCount = 0
|
||||
var listError: Error?
|
||||
var mockUnreadCount: Int = 0
|
||||
var getUnreadCountCallCount = 0
|
||||
|
||||
func list(params: NotificationListParams = NotificationListParams()) async throws -> [NotificationItem] {
|
||||
listCallCount += 1
|
||||
@@ -26,6 +28,11 @@ final class MockNotificationsService: NotificationsServiceProtocol {
|
||||
func markAllAsRead() async throws {
|
||||
markAllCalled = true
|
||||
}
|
||||
|
||||
func getUnreadCount() async throws -> Int {
|
||||
getUnreadCountCallCount += 1
|
||||
return mockUnreadCount
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Helper: Sample Notifications
|
||||
@@ -216,6 +223,91 @@ final class NotificationServiceTests: XCTestCase {
|
||||
XCTAssertEqual(mock.listCallCount, 1)
|
||||
XCTAssertEqual(viewModel.notifications.count, 1)
|
||||
}
|
||||
|
||||
// MARK: - Badge Count
|
||||
|
||||
@MainActor
|
||||
func testFetchUnreadCountSetsBadgeCount() async {
|
||||
let mock = MockNotificationsService()
|
||||
mock.mockUnreadCount = 5
|
||||
|
||||
let viewModel = NotificationsViewModel(notificationsService: mock)
|
||||
await viewModel.fetchUnreadCount()
|
||||
|
||||
XCTAssertEqual(viewModel.badgeCount, 5)
|
||||
XCTAssertEqual(mock.getUnreadCountCallCount, 1)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func testFetchUnreadCountZero() async {
|
||||
let mock = MockNotificationsService()
|
||||
mock.mockUnreadCount = 0
|
||||
|
||||
let viewModel = NotificationsViewModel(notificationsService: mock)
|
||||
await viewModel.fetchUnreadCount()
|
||||
|
||||
XCTAssertEqual(viewModel.badgeCount, 0)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func testMarkAsReadDecrementsBadgeCount() async {
|
||||
let mock = MockNotificationsService()
|
||||
let unread = NotificationItem.sample(id: "1", isRead: false)
|
||||
|
||||
let viewModel = NotificationsViewModel(notificationsService: mock)
|
||||
viewModel.notifications = [unread]
|
||||
viewModel.badgeCount = 3
|
||||
|
||||
await viewModel.markAsRead(id: "1")
|
||||
|
||||
XCTAssertEqual(viewModel.badgeCount, 2)
|
||||
XCTAssertTrue(viewModel.notifications.first?.isRead == true)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func testMarkAsReadBadgeCountDoesNotGoBelowZero() async {
|
||||
let mock = MockNotificationsService()
|
||||
let unread = NotificationItem.sample(id: "1", isRead: false)
|
||||
|
||||
let viewModel = NotificationsViewModel(notificationsService: mock)
|
||||
viewModel.notifications = [unread]
|
||||
viewModel.badgeCount = 0
|
||||
|
||||
await viewModel.markAsRead(id: "1")
|
||||
|
||||
XCTAssertEqual(viewModel.badgeCount, 0)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func testMarkAllAsReadResetsBadgeCount() async {
|
||||
let mock = MockNotificationsService()
|
||||
let unread1 = NotificationItem.sample(id: "1", isRead: false)
|
||||
let unread2 = NotificationItem.sample(id: "2", isRead: false)
|
||||
|
||||
let viewModel = NotificationsViewModel(notificationsService: mock)
|
||||
viewModel.notifications = [unread1, unread2]
|
||||
viewModel.badgeCount = 7
|
||||
|
||||
await viewModel.markAllAsRead()
|
||||
|
||||
XCTAssertEqual(viewModel.badgeCount, 0)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func testFetchNotificationsUpdatesBadgeCount() async {
|
||||
let mock = MockNotificationsService()
|
||||
mock.notifications = [
|
||||
NotificationItem.sample(id: "1", isRead: false),
|
||||
NotificationItem.sample(id: "2", isRead: true),
|
||||
NotificationItem.sample(id: "3", isRead: false),
|
||||
]
|
||||
|
||||
let viewModel = NotificationsViewModel(notificationsService: mock)
|
||||
viewModel.badgeCount = 0
|
||||
await viewModel.fetchNotifications()
|
||||
|
||||
XCTAssertEqual(viewModel.badgeCount, 2)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - NotificationModelTests
|
||||
|
||||
Reference in New Issue
Block a user