Refactor theTimelineItemIdentifier handling; stop relying on optional EventOrTransactionIds and be explicit when setting composer modes from the draft service.

This commit is contained in:
Stefan Ceriu
2024-10-16 14:07:54 +03:00
parent 3d797ccabb
commit 35d49c4d85
56 changed files with 313 additions and 298 deletions
@@ -38,7 +38,7 @@ class AudioPlayerStateTests: XCTestCase {
override func setUp() async throws {
audioPlayerActionsSubject = .init()
audioPlayerSeekCallsSubject = .init()
audioPlayerState = AudioPlayerState(id: .timelineItemIdentifier(.random), title: "", duration: Self.audioDuration)
audioPlayerState = AudioPlayerState(id: .timelineItemIdentifier(.randomEvent), title: "", duration: Self.audioDuration)
audioPlayerMock = buildAudioPlayerMock()
audioPlayerMock.seekToClosure = { [weak self] progress in
self?.audioPlayerMock.currentTime = Self.audioDuration * progress
@@ -162,7 +162,7 @@ class AudioPlayerStateTests: XCTestCase {
func testHandlingAudioPlayerActionDidFinishLoading() async throws {
audioPlayerMock.duration = 10.0
audioPlayerState = AudioPlayerState(id: .timelineItemIdentifier(.random), title: "", duration: 0)
audioPlayerState = AudioPlayerState(id: .timelineItemIdentifier(.randomEvent), title: "", duration: 0)
audioPlayerState.attachAudioPlayer(audioPlayerMock)
let deferred = deferFulfillment(audioPlayerState.$playbackState) { action in
@@ -41,14 +41,14 @@ class ComposerToolbarViewModelTests: XCTestCase {
}
func testComposerFocus() {
viewModel.process(timelineAction: .setMode(mode: .edit(originalItemId: TimelineItemIdentifier(uniqueID: "mock"))))
viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventId(eventId: "mock"), source: .timeline)))
XCTAssertTrue(viewModel.state.bindings.composerFocused)
viewModel.process(timelineAction: .removeFocus)
XCTAssertFalse(viewModel.state.bindings.composerFocused)
}
func testComposerMode() {
let mode: ComposerMode = .edit(originalItemId: TimelineItemIdentifier(uniqueID: "mock"))
let mode: ComposerMode = .edit(originalEventOrTransactionID: .eventId(eventId: "mock"), source: .timeline)
viewModel.process(timelineAction: .setMode(mode: mode))
XCTAssertEqual(viewModel.state.composerMode, mode)
viewModel.process(timelineAction: .clear)
@@ -56,7 +56,7 @@ class ComposerToolbarViewModelTests: XCTestCase {
}
func testComposerModeIsPublished() {
let mode: ComposerMode = .edit(originalItemId: TimelineItemIdentifier(uniqueID: "mock"))
let mode: ComposerMode = .edit(originalEventOrTransactionID: .eventId(eventId: "mock"), source: .timeline)
let expectation = expectation(description: "Composer mode is published")
let cancellable = viewModel
.context
@@ -236,7 +236,7 @@ class ComposerToolbarViewModelTests: XCTestCase {
}
viewModel.context.composerFormattingEnabled = false
viewModel.process(timelineAction: .setMode(mode: .edit(originalItemId: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "testID")))))
viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventId(eventId: "testID"), source: .timeline)))
viewModel.context.plainComposerText = .init(string: "Hello world!")
viewModel.saveDraft()
@@ -257,7 +257,7 @@ class ComposerToolbarViewModelTests: XCTestCase {
}
viewModel.context.composerFormattingEnabled = false
viewModel.process(timelineAction: .setMode(mode: .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "testID")),
viewModel.process(timelineAction: .setMode(mode: .reply(eventID: "testID",
replyDetails: .loaded(sender: .init(id: ""),
eventID: "testID",
eventContent: .message(.text(.init(body: "reply text")))),
@@ -282,7 +282,7 @@ class ComposerToolbarViewModelTests: XCTestCase {
}
viewModel.context.composerFormattingEnabled = false
viewModel.process(timelineAction: .setMode(mode: .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "testID")),
viewModel.process(timelineAction: .setMode(mode: .reply(eventID: "testID",
replyDetails: .loaded(sender: .init(id: ""),
eventID: "testID",
eventContent: .message(.text(.init(body: "reply text")))),
@@ -395,7 +395,7 @@ class ComposerToolbarViewModelTests: XCTestCase {
await fulfillment(of: [expectation], timeout: 10)
XCTAssertFalse(viewModel.context.composerFormattingEnabled)
XCTAssertEqual(viewModel.state.composerMode, .edit(originalItemId: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: "testID"))))
XCTAssertEqual(viewModel.state.composerMode, .edit(originalEventOrTransactionID: .eventId(eventId: "testID"), source: .draftService))
XCTAssertEqual(viewModel.context.plainComposerText, NSAttributedString(string: "Hello world!"))
}
@@ -429,13 +429,13 @@ class ComposerToolbarViewModelTests: XCTestCase {
await fulfillment(of: [draftExpectation], timeout: 10)
XCTAssertFalse(viewModel.context.composerFormattingEnabled)
// Testing the loading state first
XCTAssertEqual(viewModel.state.composerMode, .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: testEventID)),
XCTAssertEqual(viewModel.state.composerMode, .reply(eventID: testEventID,
replyDetails: .loading(eventID: testEventID),
isThread: false))
XCTAssertEqual(viewModel.context.plainComposerText, NSAttributedString(string: text))
await fulfillment(of: [loadReplyExpectation], timeout: 10)
XCTAssertEqual(viewModel.state.composerMode, .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: testEventID)),
XCTAssertEqual(viewModel.state.composerMode, .reply(eventID: testEventID,
replyDetails: loadedReply,
isThread: true))
}
@@ -469,7 +469,7 @@ class ComposerToolbarViewModelTests: XCTestCase {
await fulfillment(of: [draftExpectation], timeout: 10)
XCTAssertFalse(viewModel.context.composerFormattingEnabled)
// Testing the loading state first
XCTAssertEqual(viewModel.state.composerMode, .reply(itemID: .init(uniqueID: "", eventOrTransactionID: .eventId(eventId: testEventID)),
XCTAssertEqual(viewModel.state.composerMode, .reply(eventID: testEventID,
replyDetails: .loading(eventID: testEventID),
isThread: false))
XCTAssertEqual(viewModel.context.plainComposerText, NSAttributedString(string: text))
@@ -483,7 +483,7 @@ class ComposerToolbarViewModelTests: XCTestCase {
func testSaveVolatileDraftWhenEditing() {
viewModel.context.composerFormattingEnabled = false
viewModel.context.plainComposerText = .init(string: "Hello world!")
viewModel.process(timelineAction: .setMode(mode: .edit(originalItemId: .random)))
viewModel.process(timelineAction: .setMode(mode: .edit(originalEventOrTransactionID: .eventId(eventId: UUID().uuidString), source: .timeline)))
let draft = draftServiceMock.saveVolatileDraftReceivedDraft
XCTAssertNotNil(draft)
+6 -6
View File
@@ -116,7 +116,7 @@ class LoggingTests: XCTestCase {
func validateTimelineContentIsRedacted() throws {
// Given timeline items that contain text
let textAttributedString = "TextAttributed"
let textMessage = TextRoomTimelineItem(id: .random,
let textMessage = TextRoomTimelineItem(id: .randomEvent,
timestamp: "",
isOutgoing: false,
isEditable: false,
@@ -125,7 +125,7 @@ class LoggingTests: XCTestCase {
sender: .init(id: "sender"),
content: .init(body: "TextString", formattedBody: AttributedString(textAttributedString)))
let noticeAttributedString = "NoticeAttributed"
let noticeMessage = NoticeRoomTimelineItem(id: .random,
let noticeMessage = NoticeRoomTimelineItem(id: .randomEvent,
timestamp: "",
isOutgoing: false,
isEditable: false,
@@ -134,7 +134,7 @@ class LoggingTests: XCTestCase {
sender: .init(id: "sender"),
content: .init(body: "NoticeString", formattedBody: AttributedString(noticeAttributedString)))
let emoteAttributedString = "EmoteAttributed"
let emoteMessage = EmoteRoomTimelineItem(id: .random,
let emoteMessage = EmoteRoomTimelineItem(id: .randomEvent,
timestamp: "",
isOutgoing: false,
isEditable: false,
@@ -142,7 +142,7 @@ class LoggingTests: XCTestCase {
isThreaded: false,
sender: .init(id: "sender"),
content: .init(body: "EmoteString", formattedBody: AttributedString(emoteAttributedString)))
let imageMessage = ImageRoomTimelineItem(id: .init(uniqueID: "myimagemessage"),
let imageMessage = ImageRoomTimelineItem(id: .randomEvent,
timestamp: "",
isOutgoing: false,
isEditable: false,
@@ -153,7 +153,7 @@ class LoggingTests: XCTestCase {
caption: "ImageString",
source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/gif"),
thumbnailSource: nil))
let videoMessage = VideoRoomTimelineItem(id: .random,
let videoMessage = VideoRoomTimelineItem(id: .randomEvent,
timestamp: "",
isOutgoing: false,
isEditable: false,
@@ -165,7 +165,7 @@ class LoggingTests: XCTestCase {
duration: 0,
source: nil,
thumbnailSource: nil))
let fileMessage = FileRoomTimelineItem(id: .random,
let fileMessage = FileRoomTimelineItem(id: .randomEvent,
timestamp: "",
isOutgoing: false,
isEditable: false,
@@ -60,7 +60,7 @@ class MediaPlayerProviderTests: XCTestCase {
}
func testPlayerStates() async throws {
let audioPlayerStateId = AudioPlayerStateIdentifier.timelineItemIdentifier(.random)
let audioPlayerStateId = AudioPlayerStateIdentifier.timelineItemIdentifier(.randomEvent)
// By default, there should be no player state
XCTAssertNil(mediaPlayerProvider.playerState(for: audioPlayerStateId))
@@ -76,7 +76,7 @@ class MediaPlayerProviderTests: XCTestCase {
let audioPlayer = AudioPlayerMock()
audioPlayer.actions = PassthroughSubject<AudioPlayerAction, Never>().eraseToAnyPublisher()
let audioPlayerStates = Array(repeating: AudioPlayerState(id: .timelineItemIdentifier(.random), title: "", duration: 0), count: 10)
let audioPlayerStates = Array(repeating: AudioPlayerState(id: .timelineItemIdentifier(.randomEvent), title: "", duration: 0), count: 10)
for audioPlayerState in audioPlayerStates {
mediaPlayerProvider.register(audioPlayerState: audioPlayerState)
audioPlayerState.attachAudioPlayer(audioPlayer)
@@ -95,7 +95,7 @@ class MediaPlayerProviderTests: XCTestCase {
let audioPlayer = AudioPlayerMock()
audioPlayer.actions = PassthroughSubject<AudioPlayerAction, Never>().eraseToAnyPublisher()
let audioPlayerStates = Array(repeating: AudioPlayerState(id: .timelineItemIdentifier(.random), title: "", duration: 0), count: 10)
let audioPlayerStates = Array(repeating: AudioPlayerState(id: .timelineItemIdentifier(.randomEvent), title: "", duration: 0), count: 10)
for audioPlayerState in audioPlayerStates {
mediaPlayerProvider.register(audioPlayerState: audioPlayerState)
audioPlayerState.attachAudioPlayer(audioPlayer)
@@ -12,7 +12,7 @@ import XCTest
@MainActor
class MessageForwardingScreenViewModelTests: XCTestCase {
let forwardingItem = MessageForwardingItem(id: .init(uniqueID: "t1", eventOrTransactionID: .eventId(eventId: "t1")),
let forwardingItem = MessageForwardingItem(id: .event(uniqueID: "t1", eventOrTransactionID: .eventId(eventId: "t1")),
roomID: "1",
content: .init(noPointer: .init()))
var viewModel: MessageForwardingScreenViewModelProtocol!
@@ -61,7 +61,7 @@ class ResolveVerifiedUserSendFailureScreenViewModelTests: XCTestCase {
private func makeViewModel(with failure: TimelineItemSendFailure.VerifiedUser) -> ResolveVerifiedUserSendFailureScreenViewModel {
ResolveVerifiedUserSendFailureScreenViewModel(failure: failure,
itemID: .random,
itemID: .randomEvent,
roomProxy: roomProxy,
userIndicatorController: UserIndicatorControllerMock())
}
@@ -11,7 +11,7 @@ import XCTest
final class TextBasedRoomTimelineTests: XCTestCase {
func testTextRoomTimelineItemWhitespaceEnd() {
let timestamp = "Now"
let timelineItem = TextRoomTimelineItem(id: .random,
let timelineItem = TextRoomTimelineItem(id: .randomEvent,
timestamp: timestamp,
isOutgoing: true,
isEditable: true,
@@ -24,7 +24,7 @@ final class TextBasedRoomTimelineTests: XCTestCase {
func testTextRoomTimelineItemWhitespaceEndLonger() {
let timestamp = "10:00 AM"
let timelineItem = TextRoomTimelineItem(id: .random,
let timelineItem = TextRoomTimelineItem(id: .randomEvent,
timestamp: timestamp,
isOutgoing: true,
isEditable: true,
@@ -37,7 +37,7 @@ final class TextBasedRoomTimelineTests: XCTestCase {
func testTextRoomTimelineItemWhitespaceEndWithEdit() {
let timestamp = "Now"
var timelineItem = TextRoomTimelineItem(id: .random,
var timelineItem = TextRoomTimelineItem(id: .randomEvent,
timestamp: timestamp,
isOutgoing: true,
isEditable: true,
@@ -52,7 +52,7 @@ final class TextBasedRoomTimelineTests: XCTestCase {
func testTextRoomTimelineItemWhitespaceEndWithEditAndAlert() {
let timestamp = "Now"
var timelineItem = TextRoomTimelineItem(id: .random,
var timelineItem = TextRoomTimelineItem(id: .randomEvent,
timestamp: timestamp,
isOutgoing: true,
isEditable: true,
@@ -423,7 +423,7 @@ class TimelineViewModelTests: XCTestCase {
private extension TextRoomTimelineItem {
init(text: String, sender: String, addReactions: Bool = false, addReadReceipts: [ReadReceipt] = []) {
let reactions = addReactions ? [AggregatedReaction(accountOwnerID: "bob", key: "🦄", senders: [ReactionSender(id: sender, timestamp: Date())])] : []
self.init(id: .random,
self.init(id: .randomEvent,
timestamp: "10:47 am",
isOutgoing: sender == "bob",
isEditable: sender == "bob",
@@ -437,13 +437,13 @@ private extension TextRoomTimelineItem {
private extension SeparatorRoomTimelineItem {
init(uniqueID: String) {
self.init(id: .init(uniqueID: uniqueID), text: "")
self.init(id: .virtual(uniqueID: uniqueID), text: "")
}
}
private extension TextRoomTimelineItem {
init(eventID: String) {
self.init(id: .init(uniqueID: UUID().uuidString, eventOrTransactionID: .eventId(eventId: eventID)),
self.init(id: .event(uniqueID: UUID().uuidString, eventOrTransactionID: .eventId(eventId: eventID)),
timestamp: "",
isOutgoing: false,
isEditable: false,