diff --git a/RiotSwiftUI/Modules/Room/PollHistory/Service/MatrixSDK/PollHistoryService.swift b/RiotSwiftUI/Modules/Room/PollHistory/Service/MatrixSDK/PollHistoryService.swift index 575832053..6a7499248 100644 --- a/RiotSwiftUI/Modules/Room/PollHistory/Service/MatrixSDK/PollHistoryService.swift +++ b/RiotSwiftUI/Modules/Room/PollHistory/Service/MatrixSDK/PollHistoryService.swift @@ -43,7 +43,7 @@ final class PollHistoryService: PollHistoryServiceProtocol { init(room: MXRoom, chunkSizeInDays: UInt) { self.room = room self.chunkSizeInDays = chunkSizeInDays - self.timeline = MXRoomEventTimeline(room: room, andInitialEventId: nil) + timeline = MXRoomEventTimeline(room: room, andInitialEventId: nil) targetTimestamp = Date().addingTimeInterval(-TimeInterval(chunkSizeInDays) * Constants.oneDayInSeconds) setup(timeline: timeline) } diff --git a/RiotSwiftUI/Modules/Room/PollHistory/Service/Mock/MockPollHistoryService.swift b/RiotSwiftUI/Modules/Room/PollHistory/Service/Mock/MockPollHistoryService.swift index be6474cb3..7f412b94e 100644 --- a/RiotSwiftUI/Modules/Room/PollHistory/Service/Mock/MockPollHistoryService.swift +++ b/RiotSwiftUI/Modules/Room/PollHistory/Service/Mock/MockPollHistoryService.swift @@ -1,4 +1,4 @@ -// +// // Copyright 2023 New Vector Ltd // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,12 +17,14 @@ import Combine final class MockPollHistoryService: PollHistoryServiceProtocol { + var updatesPublisher: AnyPublisher = Empty().eraseToAnyPublisher() var updates: AnyPublisher { - Empty().eraseToAnyPublisher() + updatesPublisher } + var updatesErrorsPublisher: AnyPublisher = Empty().eraseToAnyPublisher() var updatesErrors: AnyPublisher { - Empty().eraseToAnyPublisher() + updatesErrorsPublisher } lazy var nextPublisher: AnyPublisher = (activePollsData + pastPollsData) @@ -37,13 +39,13 @@ final class MockPollHistoryService: PollHistoryServiceProtocol { private extension MockPollHistoryService { var activePollsData: [TimelinePollDetails] { - (1..<10) + (1...10) .map { index in TimelinePollDetails(id: "a\(index)", question: "Do you like the active poll number \(index)?", answerOptions: [], closed: false, - startDate: .init(), + startDate: .init().addingTimeInterval(TimeInterval(-index) * 3600 * 24), totalAnswerCount: 30, type: .disclosed, eventType: .started, @@ -54,13 +56,13 @@ private extension MockPollHistoryService { } var pastPollsData: [TimelinePollDetails] { - (1..<10) + (1...10) .map { index in TimelinePollDetails(id: "p\(index)", question: "Do you like the active poll number \(index)?", answerOptions: [.init(id: "id", text: "Yes, of course!", count: 20, winner: true, selected: true)], closed: true, - startDate: .init(), + startDate: .init().addingTimeInterval(TimeInterval(-index) * 3600 * 24), totalAnswerCount: 30, type: .disclosed, eventType: .started, diff --git a/RiotSwiftUI/Modules/Room/PollHistory/Test/UI/PollHistoryUITests.swift b/RiotSwiftUI/Modules/Room/PollHistory/Test/UI/PollHistoryUITests.swift index b6d062370..bd08d10c2 100644 --- a/RiotSwiftUI/Modules/Room/PollHistory/Test/UI/PollHistoryUITests.swift +++ b/RiotSwiftUI/Modules/Room/PollHistory/Test/UI/PollHistoryUITests.swift @@ -17,7 +17,7 @@ import RiotSwiftUI import XCTest -class PollHistoryUITests: MockScreenTestCase { +final class PollHistoryUITests: MockScreenTestCase { func testActivePollHistoryHasContent() { app.goToScreenWithIdentifier(MockPollHistoryScreenState.active.title) let title = app.navigationBars.firstMatch.identifier @@ -65,4 +65,15 @@ class PollHistoryUITests: MockScreenTestCase { XCTAssertEqual(selectedSegment.value as? String, VectorL10n.accessibilitySelected) XCTAssertFalse(winningOption.exists) } + + func testLoaderIsShowing() { + app.goToScreenWithIdentifier(MockPollHistoryScreenState.loading.title) + let title = app.navigationBars.firstMatch.identifier + let loadingText = app.staticTexts["PollHistory.loadingText"] + let items = app.staticTexts["PollListItem.title"] + + XCTAssertEqual(title, VectorL10n.pollHistoryTitle) + XCTAssertFalse(items.exists) + XCTAssertTrue(loadingText.exists) + } } diff --git a/RiotSwiftUI/Modules/Room/PollHistory/Test/Unit/PollHistoryViewModelTests.swift b/RiotSwiftUI/Modules/Room/PollHistory/Test/Unit/PollHistoryViewModelTests.swift new file mode 100644 index 000000000..f5694a7bb --- /dev/null +++ b/RiotSwiftUI/Modules/Room/PollHistory/Test/Unit/PollHistoryViewModelTests.swift @@ -0,0 +1,90 @@ +// +// Copyright 2023 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Combine +@testable import RiotSwiftUI +import XCTest + +final class PollHistoryViewModelTests: XCTestCase { + private var viewModel: PollHistoryViewModel! + private var pollHistoryService: MockPollHistoryService = .init() + + override func setUpWithError() throws { + pollHistoryService = .init() + viewModel = .init(mode: .active, pollService: pollHistoryService) + } + + func testEmitsContentOnLanding() throws { + XCTAssert(viewModel.state.polls == nil) + viewModel.process(viewAction: .viewAppeared) + XCTAssertFalse(try polls.isEmpty) + } + + func testLoadingState() throws { + XCTAssertFalse(viewModel.state.isLoading) + viewModel.process(viewAction: .viewAppeared) + XCTAssertFalse(viewModel.state.isLoading) + XCTAssertFalse(try polls.isEmpty) + } + + func testLoadingStateIsTrueWhileLoading() { + XCTAssertFalse(viewModel.state.isLoading) + pollHistoryService.nextPublisher = Empty(completeImmediately: false, outputType: TimelinePollDetails.self, failureType: Error.self).eraseToAnyPublisher() + viewModel.process(viewAction: .viewAppeared) + XCTAssertTrue(viewModel.state.isLoading) + } + + func testUpdatesAreHandled() throws { + let mockUpdates: PassthroughSubject = .init() + pollHistoryService.updatesPublisher = mockUpdates.eraseToAnyPublisher() + viewModel.process(viewAction: .viewAppeared) + + var firstPoll = try XCTUnwrap(try polls.first) + XCTAssertEqual(firstPoll.question, "Do you like the active poll number 9?") + firstPoll.question = "foo" + + mockUpdates.send(firstPoll) + + let updatedPoll = try XCTUnwrap(viewModel.state.polls?.first) + XCTAssertEqual(updatedPoll.question, "foo") + } + + func testSegmentsAreUpdated() throws { + viewModel.process(viewAction: .viewAppeared) + XCTAssertFalse(try polls.isEmpty) + XCTAssertTrue(try polls.allSatisfy { !$0.closed }) + + viewModel.state.bindings.mode = .past + viewModel.process(viewAction: .segmentDidChange) + + XCTAssertTrue(try polls.allSatisfy(\.closed)) + } + + func testPollsAreReverseOrdered() throws { + viewModel.process(viewAction: .viewAppeared) + + let pollDates = try polls.map(\.startDate) + XCTAssertEqual(pollDates, pollDates.sorted(by: { $0 > $1 })) + } +} + +private extension PollHistoryViewModelTests { + var polls: [TimelinePollDetails] { + get throws { + try XCTUnwrap(viewModel.state.polls) + } + } +} diff --git a/RiotSwiftUI/Modules/Room/PollHistory/View/PollHistory.swift b/RiotSwiftUI/Modules/Room/PollHistory/View/PollHistory.swift index fcd133ee4..cf6038bcd 100644 --- a/RiotSwiftUI/Modules/Room/PollHistory/View/PollHistory.swift +++ b/RiotSwiftUI/Modules/Room/PollHistory/View/PollHistory.swift @@ -80,7 +80,7 @@ struct PollHistory: View { } Button { - #warning("handle action") + #warning("handle action in next ticket") } label: { Text("Load more polls") } diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/Test/Unit/TimelinePollViewModelTests.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/Test/Unit/TimelinePollViewModelTests.swift index cd806da54..a36a7d092 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/Test/Unit/TimelinePollViewModelTests.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/Test/Unit/TimelinePollViewModelTests.swift @@ -29,9 +29,11 @@ class TimelinePollViewModelTests: XCTestCase { TimelinePollAnswerOption(id: "2", text: "2", count: 1, winner: false, selected: false), TimelinePollAnswerOption(id: "3", text: "3", count: 1, winner: false, selected: false)] - let timelinePoll = TimelinePollDetails(question: "Question", + let timelinePoll = TimelinePollDetails(id: "poll-id", + question: "Question", answerOptions: answerOptions, closed: false, + startDate: .init(), totalAnswerCount: 3, type: .disclosed, eventType: .started,