Skip to content

Commit

Permalink
Add Thread List View Model test coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
nuno-vieira committed Oct 15, 2024
1 parent b7dea0a commit 163349b
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,12 @@ open class ChatThreadListViewModel: ObservableObject, ChatThreadListControllerDe
/// Re-fetches the threads. If the initial query failed, it will load the initial page.
/// If on the other hand it was a new page that failed, it will re-fetch that page.
public func retryLoadThreads() {
if failedToLoadThreads {
loadThreads()
if failedToLoadMoreThreads {
loadMoreThreads()
return
}

loadMoreThreads()
loadThreads()
}

/// Called when the view appears on screen.
Expand All @@ -115,7 +115,8 @@ open class ChatThreadListViewModel: ObservableObject, ChatThreadListControllerDe

/// Loads the initial page of threads.
public func loadThreads() {
isLoading = threadListController.threads.isEmpty == true
let isEmpty = threadListController.threads.isEmpty
isLoading = isEmpty
failedToLoadThreads = false
isReloading = !isEmpty
preselectThreadIfNeeded()
Expand Down
4 changes: 4 additions & 0 deletions StreamChatSwiftUI.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,7 @@
ADE0F5622CB8556F0053B8B9 /* ChatThreadListFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADE0F5612CB8556F0053B8B9 /* ChatThreadListFooterView.swift */; };
ADE0F5642CB9609E0053B8B9 /* ChatThreadListHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADE0F5632CB9609E0053B8B9 /* ChatThreadListHeaderView.swift */; };
ADE0F5662CB962470053B8B9 /* ActionBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADE0F5652CB962470053B8B9 /* ActionBannerView.swift */; };
ADE442EE2CBDAAAA0066CDF7 /* ChatThreadListViewModel_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADE442ED2CBDAAAA0066CDF7 /* ChatThreadListViewModel_Tests.swift */; };
ADE442F02CBDAAB70066CDF7 /* ChatThreadListView_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADE442EF2CBDAAB70066CDF7 /* ChatThreadListView_Tests.swift */; };
ADE442F22CBDAAC40066CDF7 /* ChatThreadListItemView_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADE442F12CBDAAC40066CDF7 /* ChatThreadListItemView_Tests.swift */; };
C14A465B284665B100EF498E /* SDKIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C14A465A284665B100EF498E /* SDKIdentifier.swift */; };
Expand Down Expand Up @@ -1111,6 +1112,7 @@
ADE0F5612CB8556F0053B8B9 /* ChatThreadListFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatThreadListFooterView.swift; sourceTree = "<group>"; };
ADE0F5632CB9609E0053B8B9 /* ChatThreadListHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatThreadListHeaderView.swift; sourceTree = "<group>"; };
ADE0F5652CB962470053B8B9 /* ActionBannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionBannerView.swift; sourceTree = "<group>"; };
ADE442ED2CBDAAAA0066CDF7 /* ChatThreadListViewModel_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatThreadListViewModel_Tests.swift; sourceTree = "<group>"; };
ADE442EF2CBDAAB70066CDF7 /* ChatThreadListView_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatThreadListView_Tests.swift; sourceTree = "<group>"; };
ADE442F12CBDAAC40066CDF7 /* ChatThreadListItemView_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatThreadListItemView_Tests.swift; sourceTree = "<group>"; };
C14A465A284665B100EF498E /* SDKIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDKIdentifier.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2268,6 +2270,7 @@
ADE442EC2CBDAA320066CDF7 /* ChatThreadList */ = {
isa = PBXGroup;
children = (
ADE442ED2CBDAAAA0066CDF7 /* ChatThreadListViewModel_Tests.swift */,
ADE442EF2CBDAAB70066CDF7 /* ChatThreadListView_Tests.swift */,
ADE442F12CBDAAC40066CDF7 /* ChatThreadListItemView_Tests.swift */,
);
Expand Down Expand Up @@ -2966,6 +2969,7 @@
84C94D0727578BF2007FE2B9 /* RandomDispatchQueue.swift in Sources */,
847110B628611033004A46D6 /* MessageActions_Tests.swift in Sources */,
84C94D1227578BF2007FE2B9 /* JSONEncoder+Extensions.swift in Sources */,
ADE442EE2CBDAAAA0066CDF7 /* ChatThreadListViewModel_Tests.swift in Sources */,
84E04797284A444E00BAFA17 /* WebSocketPingControllerMock.swift in Sources */,
8423C34C277DDD250092DCF1 /* MuteCommandHandler_Tests.swift in Sources */,
84C94D1127578BF2007FE2B9 /* ChannelId.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
//
// Copyright © 2024 Stream.io Inc. All rights reserved.
//

@testable import StreamChat
@testable import StreamChatSwiftUI
@testable import StreamChatTestTools
import XCTest

class ChatThreadListViewModel_Tests: StreamChatTestCase {

func test_viewDidAppear_thenLoadsThreads() {
let mockThreadListController = ChatThreadListController_Mock.mock(
query: .init(watch: true)
)
let viewModel = ChatThreadListViewModel(
threadListController: mockThreadListController
)

viewModel.viewDidAppear()
XCTAssertEqual(mockThreadListController.synchronize_callCount, 1)
}

func test_viewDidAppear_whenAlreadyLoadedThreads_thenDoesNotLoadsThreads() {
let mockThreadListController = ChatThreadListController_Mock.mock(
query: .init(watch: true)
)
let viewModel = ChatThreadListViewModel(
threadListController: mockThreadListController
)

viewModel.viewDidAppear()
mockThreadListController.synchronize_completion?(nil)
viewModel.viewDidAppear()

XCTAssertEqual(mockThreadListController.synchronize_callCount, 1)
}

func test_loadThreads_whenInitialEmptyData_whenSuccess() {
let mockThreadListController = ChatThreadListController_Mock.mock(
query: .init(watch: true)
)
mockThreadListController.threads_mock = []
let viewModel = ChatThreadListViewModel(
threadListController: mockThreadListController
)

viewModel.loadThreads()

XCTAssertEqual(viewModel.isLoading, true)
XCTAssertEqual(viewModel.isReloading, false)
XCTAssertEqual(viewModel.failedToLoadThreads, false)
XCTAssertEqual(viewModel.hasLoadedThreads, false)

mockThreadListController.threads_mock = [.mock()]
mockThreadListController.synchronize_completion?(nil)

XCTAssertEqual(viewModel.isLoading, false)
XCTAssertEqual(viewModel.isReloading, false)
XCTAssertEqual(viewModel.failedToLoadThreads, false)
XCTAssertEqual(viewModel.hasLoadedThreads, true)
XCTAssertEqual(viewModel.isEmpty, false)
}

func test_loadThreads_whenCacheAvailable_whenSuccess() {
let mockThreadListController = ChatThreadListController_Mock.mock(
query: .init(watch: true)
)
mockThreadListController.threads_mock = [.mock()]
let viewModel = ChatThreadListViewModel(
threadListController: mockThreadListController
)

viewModel.loadThreads()

XCTAssertEqual(viewModel.isLoading, false)
XCTAssertEqual(viewModel.isReloading, true)
XCTAssertEqual(viewModel.failedToLoadThreads, false)
XCTAssertEqual(viewModel.hasLoadedThreads, false)

mockThreadListController.threads_mock = [.mock(), .mock()]
mockThreadListController.synchronize_completion?(nil)

XCTAssertEqual(viewModel.isLoading, false)
XCTAssertEqual(viewModel.isReloading, false)
XCTAssertEqual(viewModel.failedToLoadThreads, false)
XCTAssertEqual(viewModel.hasLoadedThreads, true)
XCTAssertEqual(viewModel.isEmpty, false)
}

func test_loadThreads_whenError() {
let mockThreadListController = ChatThreadListController_Mock.mock(
query: .init(watch: true)
)
mockThreadListController.threads_mock = []
let viewModel = ChatThreadListViewModel(
threadListController: mockThreadListController
)

viewModel.loadThreads()
mockThreadListController.threads_mock = [.mock()]
mockThreadListController.synchronize_completion?(ClientError("ERROR"))

XCTAssertEqual(viewModel.isLoading, false)
XCTAssertEqual(viewModel.isReloading, false)
XCTAssertEqual(viewModel.failedToLoadThreads, true)
XCTAssertEqual(viewModel.failedToLoadMoreThreads, false)
XCTAssertEqual(viewModel.hasLoadedThreads, false)
}

func test_didAppearThread_whenInsideThreshold_thenLoadMoreThreads() {
let mockThreadListController = ChatThreadListController_Mock.mock(
query: .init(watch: true)
)
let viewModel = ChatThreadListViewModel(
threadListController: mockThreadListController
)
let mockedThreads: [ChatThread] = [
.mock(), .mock(), .mock(), .mock(), .mock(), .mock(), .mock()
]
mockedThreads.forEach { thread in
viewModel.threads.append(thread)
}

XCTAssertEqual(viewModel.isLoadingMoreThreads, false)

viewModel.didAppearThread(at: 5)

XCTAssertEqual(viewModel.isLoadingMoreThreads, true)
}

func test_didAppearThread_whenNotInThreshold_thenDoNotLoadMoreThreads() {
let mockThreadListController = ChatThreadListController_Mock.mock(
query: .init(watch: true)
)
let viewModel = ChatThreadListViewModel(
threadListController: mockThreadListController
)
let mockedThreads: [ChatThread] = [
.mock(), .mock(), .mock(), .mock(), .mock(), .mock(), .mock()
]
mockedThreads.forEach { thread in
viewModel.threads.append(thread)
}

XCTAssertEqual(viewModel.isLoadingMoreThreads, false)

viewModel.didAppearThread(at: 0)

XCTAssertEqual(viewModel.isLoadingMoreThreads, false)
}

func test_didReceiveThreadMessageNewEvent() {
let mockThreadListController = ChatThreadListController_Mock.mock(
query: .init(watch: true)
)
let viewModel = ChatThreadListViewModel(
threadListController: mockThreadListController
)
let eventController = mockThreadListController.client.eventsController()

// 2 Events
viewModel.eventsController(
eventController,
didReceiveEvent: ThreadMessageNewEvent(
message: .mock(parentMessageId: .unique),
channel: .mock(cid: .unique),
unreadCount: .noUnread,
createdAt: .unique
)
)
viewModel.eventsController(
eventController,
didReceiveEvent: ThreadMessageNewEvent(
message: .mock(parentMessageId: .unique),
channel: .mock(cid: .unique),
unreadCount: .noUnread,
createdAt: .unique
)
)

XCTAssertEqual(viewModel.newThreadsCount, 2)
XCTAssertTrue(viewModel.hasNewThreads)
}
}

0 comments on commit 163349b

Please sign in to comment.