mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-05 20:09:04 +00:00
Move MockTimelineController signalling to a new SignallingTimelineItemProviderMock
This allows for testing more of a flows and using a real TimelineController in a lot of the UI tests
This commit is contained in:
@@ -845,6 +845,7 @@
|
|||||||
8A5064CAC8E5F3B18645621D /* CallNotificationRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD6E082B0507FB28F966516A /* CallNotificationRoomTimelineView.swift */; };
|
8A5064CAC8E5F3B18645621D /* CallNotificationRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD6E082B0507FB28F966516A /* CallNotificationRoomTimelineView.swift */; };
|
||||||
8A83D715940378B9BA9F739E /* RoomInviterLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB58E4E8D6D634C246AD5C2 /* RoomInviterLabel.swift */; };
|
8A83D715940378B9BA9F739E /* RoomInviterLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB58E4E8D6D634C246AD5C2 /* RoomInviterLabel.swift */; };
|
||||||
8AA84EF202F2EFC8453A97BD /* SecureBackupRecoveryKeyScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 645E027C112740573D27765C /* SecureBackupRecoveryKeyScreenModels.swift */; };
|
8AA84EF202F2EFC8453A97BD /* SecureBackupRecoveryKeyScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 645E027C112740573D27765C /* SecureBackupRecoveryKeyScreenModels.swift */; };
|
||||||
|
8AAE2EB68802E837F568163C /* SignallingTimelineItemProviderMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE61568A225F7AA068583AA7 /* SignallingTimelineItemProviderMock.swift */; };
|
||||||
8AB8ED1051216546CB35FA0E /* UserSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E5E9C044BEB7C70B1378E91 /* UserSession.swift */; };
|
8AB8ED1051216546CB35FA0E /* UserSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E5E9C044BEB7C70B1378E91 /* UserSession.swift */; };
|
||||||
8AEE47A14223423806A7653A /* LiveLocationSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2FEFA393FC7D2870263012 /* LiveLocationSession.swift */; };
|
8AEE47A14223423806A7653A /* LiveLocationSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2FEFA393FC7D2870263012 /* LiveLocationSession.swift */; };
|
||||||
8B1D5CE017EEC734CF5FE130 /* Encodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260004737C573A56FA01E86E /* Encodable.swift */; };
|
8B1D5CE017EEC734CF5FE130 /* Encodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 260004737C573A56FA01E86E /* Encodable.swift */; };
|
||||||
@@ -2995,6 +2996,7 @@
|
|||||||
ED60E4D2CD678E1EBF16F77A /* BlockedUsersScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockedUsersScreen.swift; sourceTree = "<group>"; };
|
ED60E4D2CD678E1EBF16F77A /* BlockedUsersScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockedUsersScreen.swift; sourceTree = "<group>"; };
|
||||||
EDDE826EAB1BAB80C1104980 /* SpaceFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceFlowCoordinator.swift; sourceTree = "<group>"; };
|
EDDE826EAB1BAB80C1104980 /* SpaceFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceFlowCoordinator.swift; sourceTree = "<group>"; };
|
||||||
EE378083653EF0C9B5E9D580 /* EmoteRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmoteRoomTimelineItemContent.swift; sourceTree = "<group>"; };
|
EE378083653EF0C9B5E9D580 /* EmoteRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmoteRoomTimelineItemContent.swift; sourceTree = "<group>"; };
|
||||||
|
EE61568A225F7AA068583AA7 /* SignallingTimelineItemProviderMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignallingTimelineItemProviderMock.swift; sourceTree = "<group>"; };
|
||||||
EE8BCD14EFED23459A43FDFF /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
|
EE8BCD14EFED23459A43FDFF /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
EEAB5662310AE73D93815134 /* JoinRoomScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
EEAB5662310AE73D93815134 /* JoinRoomScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
EED6D8956E554CEDFD4FE00D /* LocationSharingScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationSharingScreenViewModelTests.swift; sourceTree = "<group>"; };
|
EED6D8956E554CEDFD4FE00D /* LocationSharingScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationSharingScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||||
@@ -3839,6 +3841,7 @@
|
|||||||
7396A6491DEC64FCB66F60A1 /* RoomThreadListServiceProxyMock.swift */,
|
7396A6491DEC64FCB66F60A1 /* RoomThreadListServiceProxyMock.swift */,
|
||||||
4AB29A2D95D3469B5F016655 /* SecureBackupControllerMock.swift */,
|
4AB29A2D95D3469B5F016655 /* SecureBackupControllerMock.swift */,
|
||||||
248649EBA5BC33DB93698734 /* SessionVerificationControllerProxyMock.swift */,
|
248649EBA5BC33DB93698734 /* SessionVerificationControllerProxyMock.swift */,
|
||||||
|
EE61568A225F7AA068583AA7 /* SignallingTimelineItemProviderMock.swift */,
|
||||||
7DB2A5FF1E68BA580A20D405 /* SpaceRoomListProxyMock.swift */,
|
7DB2A5FF1E68BA580A20D405 /* SpaceRoomListProxyMock.swift */,
|
||||||
EF36FC3DE25B20B7FA91F1FD /* SpaceServiceProxyMock.swift */,
|
EF36FC3DE25B20B7FA91F1FD /* SpaceServiceProxyMock.swift */,
|
||||||
B0F5CC38803B8382D2C63222 /* TimelineControllerFactoryMock.swift */,
|
B0F5CC38803B8382D2C63222 /* TimelineControllerFactoryMock.swift */,
|
||||||
@@ -8979,6 +8982,7 @@
|
|||||||
1C8BC70A18060677E295A846 /* ShareToMapsAppActivity.swift in Sources */,
|
1C8BC70A18060677E295A846 /* ShareToMapsAppActivity.swift in Sources */,
|
||||||
274CE3C986841D15FD530BF5 /* ShimmerModifier.swift in Sources */,
|
274CE3C986841D15FD530BF5 /* ShimmerModifier.swift in Sources */,
|
||||||
5038E69A5E6A89DE1A345E04 /* ShouldScrollOnKeyboardDidShow.swift in Sources */,
|
5038E69A5E6A89DE1A345E04 /* ShouldScrollOnKeyboardDidShow.swift in Sources */,
|
||||||
|
8AAE2EB68802E837F568163C /* SignallingTimelineItemProviderMock.swift in Sources */,
|
||||||
77920AFA8091AC6B9F190C90 /* Signposter.swift in Sources */,
|
77920AFA8091AC6B9F190C90 /* Signposter.swift in Sources */,
|
||||||
2E6AD068D7767BDC33626F1C /* SnapshotableGlassEffect.swift in Sources */,
|
2E6AD068D7767BDC33626F1C /* SnapshotableGlassEffect.swift in Sources */,
|
||||||
F08F7BC07CA9AEF5CD157918 /* Snapshotting.swift in Sources */,
|
F08F7BC07CA9AEF5CD157918 /* Snapshotting.swift in Sources */,
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2025 Element Creations Ltd.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
|
// Please see LICENSE files in the repository root for full details.
|
||||||
|
//
|
||||||
|
|
||||||
|
// periphery:ignore:all
|
||||||
|
|
||||||
|
import Combine
|
||||||
|
import Foundation
|
||||||
|
import MatrixRustSDK
|
||||||
|
|
||||||
|
/// A ``TimelineItemProviderMock`` subclass that optionally listens for ``UITestsSignalling`` signals
|
||||||
|
/// to drive pagination and incoming message simulation.
|
||||||
|
@MainActor
|
||||||
|
class SignallingTimelineItemProviderMock: TimelineItemProviderMock {
|
||||||
|
private let stateSubject: CurrentValueSubject<([TimelineItemProxy], TimelinePaginationState), Never>
|
||||||
|
|
||||||
|
/// An array of item arrays that will be prepended in order on each paginate signal.
|
||||||
|
var backPaginationResponses: [[TimelineItemProxy]] = []
|
||||||
|
/// An array of items that will be appended in order on each incoming message signal.
|
||||||
|
var incomingItems: [TimelineItemProxy] = []
|
||||||
|
|
||||||
|
private var client: UITestsSignalling.Client?
|
||||||
|
private var signalCancellable: AnyCancellable?
|
||||||
|
|
||||||
|
init(itemProxies: [TimelineItemProxy] = [],
|
||||||
|
paginationState: TimelinePaginationState = .init(backward: .idle, forward: .endReached),
|
||||||
|
listenForSignals: Bool = false) {
|
||||||
|
stateSubject = CurrentValueSubject((itemProxies, paginationState))
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.itemProxies = itemProxies
|
||||||
|
|
||||||
|
underlyingPaginationState = paginationState
|
||||||
|
underlyingKind = .live
|
||||||
|
underlyingUpdatePublisher = stateSubject.eraseToAnyPublisher()
|
||||||
|
underlyingMembershipChangePublisher = Empty().eraseToAnyPublisher()
|
||||||
|
|
||||||
|
guard listenForSignals else { return }
|
||||||
|
|
||||||
|
do {
|
||||||
|
let client = try UITestsSignalling.Client(mode: .app)
|
||||||
|
signalCancellable = client.signals.sink { [weak self] signal in
|
||||||
|
Task { @MainActor [weak self] in
|
||||||
|
guard let self else { return }
|
||||||
|
do {
|
||||||
|
try self.handleSignal(signal)
|
||||||
|
} catch {
|
||||||
|
MXLog.error(error.localizedDescription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.client = client
|
||||||
|
} catch {
|
||||||
|
fatalError("Failure setting up signalling: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
|
private func handleSignal(_ signal: UITestsSignal) throws {
|
||||||
|
switch signal {
|
||||||
|
case .timeline(.paginate):
|
||||||
|
try simulateBackPagination()
|
||||||
|
case .timeline(.incomingMessage):
|
||||||
|
try simulateIncomingItem()
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func simulateIncomingItem() throws {
|
||||||
|
guard !incomingItems.isEmpty else { return }
|
||||||
|
let item = incomingItems.removeFirst()
|
||||||
|
let (items, paginationState) = stateSubject.value
|
||||||
|
let newItems = items + [item]
|
||||||
|
itemProxies = newItems
|
||||||
|
stateSubject.send((newItems, paginationState))
|
||||||
|
try client?.send(.success)
|
||||||
|
}
|
||||||
|
|
||||||
|
func inject(_ proxies: [TimelineItemProxy]) {
|
||||||
|
let (items, paginationState) = stateSubject.value
|
||||||
|
let newItems = items + proxies
|
||||||
|
itemProxies = newItems
|
||||||
|
stateSubject.send((newItems, paginationState))
|
||||||
|
}
|
||||||
|
|
||||||
|
func replaceLastItem(with proxy: TimelineItemProxy) {
|
||||||
|
var (items, paginationState) = stateSubject.value
|
||||||
|
guard !items.isEmpty else { return }
|
||||||
|
items[items.index(before: items.endIndex)] = proxy
|
||||||
|
itemProxies = items
|
||||||
|
stateSubject.send((items, paginationState))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func simulateBackPagination() throws {
|
||||||
|
let (items, _) = stateSubject.value
|
||||||
|
|
||||||
|
guard !backPaginationResponses.isEmpty else {
|
||||||
|
let newState = TimelinePaginationState(backward: .endReached, forward: .endReached)
|
||||||
|
underlyingPaginationState = newState
|
||||||
|
stateSubject.send((items, newState))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let newItems = backPaginationResponses.removeFirst()
|
||||||
|
let newPaginationState = TimelinePaginationState(backward: backPaginationResponses.isEmpty ? .endReached : .idle,
|
||||||
|
forward: .endReached)
|
||||||
|
itemProxies = newItems + items
|
||||||
|
underlyingPaginationState = newPaginationState
|
||||||
|
stateSubject.send((newItems + items, newPaginationState))
|
||||||
|
try client?.send(.success)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,11 +8,13 @@
|
|||||||
|
|
||||||
import Combine
|
import Combine
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import MatrixRustSDK
|
||||||
|
|
||||||
extension TimelineProxyMock {
|
extension TimelineProxyMock {
|
||||||
struct Configuration {
|
struct Configuration {
|
||||||
var isAutoUpdating = false
|
var isAutoUpdating = false
|
||||||
var timelineStartReached = false
|
var timelineStartReached = false
|
||||||
|
var timelineItemProvider: TimelineItemProviderProtocol?
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
@@ -20,13 +22,18 @@ extension TimelineProxyMock {
|
|||||||
self.init()
|
self.init()
|
||||||
|
|
||||||
sendMessageEventContentReturnValue = .success(())
|
sendMessageEventContentReturnValue = .success(())
|
||||||
|
sendMessageHtmlInReplyToEventIDIntentionalMentionsReturnValue = .success(())
|
||||||
|
editNewContentReturnValue = .success(())
|
||||||
|
buildMessageContentForHtmlIntentionalMentionsReturnValue = RoomMessageEventContentWithoutRelation(noHandle: .init())
|
||||||
paginateBackwardsRequestSizeReturnValue = .success(())
|
paginateBackwardsRequestSizeReturnValue = .success(())
|
||||||
paginateForwardsRequestSizeReturnValue = .success(())
|
paginateForwardsRequestSizeReturnValue = .success(())
|
||||||
sendReadReceiptForTypeReturnValue = .success(())
|
sendReadReceiptForTypeReturnValue = .success(())
|
||||||
createPollQuestionAnswersPollKindReturnValue = .success(())
|
createPollQuestionAnswersPollKindReturnValue = .success(())
|
||||||
editPollOriginalQuestionAnswersPollKindReturnValue = .success(())
|
editPollOriginalQuestionAnswersPollKindReturnValue = .success(())
|
||||||
|
|
||||||
if configuration.isAutoUpdating {
|
if let provider = configuration.timelineItemProvider {
|
||||||
|
underlyingTimelineItemProvider = provider
|
||||||
|
} else if configuration.isAutoUpdating {
|
||||||
underlyingTimelineItemProvider = AutoUpdatingTimelineItemProviderMock()
|
underlyingTimelineItemProvider = AutoUpdatingTimelineItemProviderMock()
|
||||||
} else {
|
} else {
|
||||||
let timelineItemProvider = TimelineItemProviderMock()
|
let timelineItemProvider = TimelineItemProviderMock()
|
||||||
|
|||||||
+12
-77
@@ -18,8 +18,6 @@ class MockTimelineController: TimelineControllerProtocol {
|
|||||||
|
|
||||||
/// An array of timeline item arrays that will be inserted in order for each back pagination request.
|
/// An array of timeline item arrays that will be inserted in order for each back pagination request.
|
||||||
var backPaginationResponses: [[RoomTimelineItemProtocol]] = []
|
var backPaginationResponses: [[RoomTimelineItemProtocol]] = []
|
||||||
/// An array of timeline items that will be appended in order when ``simulateIncomingItems()`` is called.
|
|
||||||
var incomingItems: [RoomTimelineItemProtocol] = []
|
|
||||||
|
|
||||||
var roomProxy: JoinedRoomProxyProtocol?
|
var roomProxy: JoinedRoomProxyProtocol?
|
||||||
var timelineProxy: TimelineProxyProtocol?
|
var timelineProxy: TimelineProxyProtocol?
|
||||||
@@ -46,8 +44,6 @@ class MockTimelineController: TimelineControllerProtocol {
|
|||||||
|
|
||||||
var timelineItemsTimestamp: [TimelineItemIdentifier: Date] = [:]
|
var timelineItemsTimestamp: [TimelineItemIdentifier: Date] = [:]
|
||||||
|
|
||||||
private var client: UITestsSignalling.Client?
|
|
||||||
|
|
||||||
static var mediaGallery: MockTimelineController {
|
static var mediaGallery: MockTimelineController {
|
||||||
MockTimelineController(timelineKind: .media(.mediaFilesScreen), timelineItems: (0..<5).reduce([]) { partialResult, _ in
|
MockTimelineController(timelineKind: .media(.mediaFilesScreen), timelineItems: (0..<5).reduce([]) { partialResult, _ in
|
||||||
partialResult + [RoomTimelineItemFixtures.separator] + RoomTimelineItemFixtures.mediaChunk
|
partialResult + [RoomTimelineItemFixtures.separator] + RoomTimelineItemFixtures.mediaChunk
|
||||||
@@ -61,7 +57,6 @@ class MockTimelineController: TimelineControllerProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init(timelineKind: TimelineKind = .live,
|
init(timelineKind: TimelineKind = .live,
|
||||||
listenForSignals: Bool = false,
|
|
||||||
timelineItems: [RoomTimelineItemProtocol] = RoomTimelineItemFixtures.default,
|
timelineItems: [RoomTimelineItemProtocol] = RoomTimelineItemFixtures.default,
|
||||||
timelineProxy: TimelineProxyProtocol? = nil) {
|
timelineProxy: TimelineProxyProtocol? = nil) {
|
||||||
self.timelineKind = timelineKind
|
self.timelineKind = timelineKind
|
||||||
@@ -70,14 +65,6 @@ class MockTimelineController: TimelineControllerProtocol {
|
|||||||
|
|
||||||
callbacks.send(.paginationState(paginationState))
|
callbacks.send(.paginationState(paginationState))
|
||||||
callbacks.send(.isLive(true))
|
callbacks.send(.isLive(true))
|
||||||
|
|
||||||
guard listenForSignals else { return }
|
|
||||||
|
|
||||||
do {
|
|
||||||
try startListening()
|
|
||||||
} catch {
|
|
||||||
fatalError("Failure setting up signalling: \(error)")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private(set) var focusOnEventCallCount = 0
|
private(set) var focusOnEventCallCount = 0
|
||||||
@@ -96,14 +83,21 @@ class MockTimelineController: TimelineControllerProtocol {
|
|||||||
private(set) var paginateBackwardsCallCount = 0
|
private(set) var paginateBackwardsCallCount = 0
|
||||||
func paginateBackwards(requestSize: UInt16) async -> Result<Void, TimelineControllerError> {
|
func paginateBackwards(requestSize: UInt16) async -> Result<Void, TimelineControllerError> {
|
||||||
paginateBackwardsCallCount += 1
|
paginateBackwardsCallCount += 1
|
||||||
|
|
||||||
paginationState = TimelinePaginationState(backward: .paginating, forward: .endReached)
|
paginationState = TimelinePaginationState(backward: .paginating, forward: .endReached)
|
||||||
|
simulateBackPagination()
|
||||||
if client == nil {
|
return .success(())
|
||||||
try? await simulateBackPagination()
|
}
|
||||||
|
|
||||||
|
private func simulateBackPagination() {
|
||||||
|
defer {
|
||||||
|
paginationState = TimelinePaginationState(backward: backPaginationResponses.isEmpty ? .endReached : .idle,
|
||||||
|
forward: .endReached)
|
||||||
}
|
}
|
||||||
|
|
||||||
return .success(())
|
guard !backPaginationResponses.isEmpty else { return }
|
||||||
|
|
||||||
|
let newItems = backPaginationResponses.removeFirst()
|
||||||
|
timelineItems.insert(contentsOf: newItems, at: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
private(set) var paginateForwardsCallCount = 0
|
private(set) var paginateForwardsCallCount = 0
|
||||||
@@ -328,63 +322,4 @@ class MockTimelineController: TimelineControllerProtocol {
|
|||||||
}
|
}
|
||||||
return .success(())
|
return .success(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - UI Test signalling
|
|
||||||
|
|
||||||
/// The cancellable used for UI Tests signalling.
|
|
||||||
private var signalCancellable: AnyCancellable?
|
|
||||||
|
|
||||||
/// Allows the simulation of server responses by listening for signals from UI tests.
|
|
||||||
private func startListening() throws {
|
|
||||||
let client = try UITestsSignalling.Client(mode: .app)
|
|
||||||
|
|
||||||
signalCancellable = client.signals.sink { [weak self] signal in
|
|
||||||
Task {
|
|
||||||
do {
|
|
||||||
try await self?.handleSignal(signal)
|
|
||||||
} catch {
|
|
||||||
MXLog.error(error.localizedDescription)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.client = client
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handles a UI test signal as necessary.
|
|
||||||
private func handleSignal(_ signal: UITestsSignal) async throws {
|
|
||||||
switch signal {
|
|
||||||
case .timeline(.paginate):
|
|
||||||
try await simulateBackPagination()
|
|
||||||
case .timeline(.incomingMessage):
|
|
||||||
try await simulateIncomingItem()
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Appends the next incoming item to the `timelineItems` array.
|
|
||||||
private func simulateIncomingItem() async throws {
|
|
||||||
guard !incomingItems.isEmpty else { return }
|
|
||||||
|
|
||||||
let incomingItem = incomingItems.removeFirst()
|
|
||||||
timelineItems.append(incomingItem)
|
|
||||||
|
|
||||||
try client?.send(.success)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prepends the next chunk of items to the `timelineItems` array.
|
|
||||||
private func simulateBackPagination() async throws {
|
|
||||||
defer {
|
|
||||||
paginationState = TimelinePaginationState(backward: backPaginationResponses.isEmpty ? .endReached : .idle,
|
|
||||||
forward: .endReached)
|
|
||||||
}
|
|
||||||
|
|
||||||
guard !backPaginationResponses.isEmpty else { return }
|
|
||||||
|
|
||||||
let newItems = backPaginationResponses.removeFirst()
|
|
||||||
timelineItems.insert(contentsOf: newItems, at: 0)
|
|
||||||
|
|
||||||
try client?.send(.success)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -373,12 +373,14 @@ class MockScreen: Identifiable {
|
|||||||
case .roomSmallTimelineIncomingAndSmallPagination:
|
case .roomSmallTimelineIncomingAndSmallPagination:
|
||||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||||
|
|
||||||
let timelineController = MockTimelineController(listenForSignals: true)
|
let itemProvider = SignallingTimelineItemProviderMock(itemProxies: RoomTimelineItemFixtures.smallChunkProxies,
|
||||||
timelineController.timelineItems = RoomTimelineItemFixtures.smallChunk
|
listenForSignals: true)
|
||||||
timelineController.backPaginationResponses = [RoomTimelineItemFixtures.singleMessageChunk]
|
itemProvider.backPaginationResponses = [RoomTimelineItemFixtures.singleMessageChunkProxies]
|
||||||
timelineController.incomingItems = [RoomTimelineItemFixtures.incomingMessage]
|
itemProvider.incomingItems = [RoomTimelineItemFixtures.incomingMessageProxy]
|
||||||
|
let roomProxy = JoinedRoomProxyMock(.init(name: "Small timeline", avatarURL: .mockMXCAvatar))
|
||||||
|
let timelineController = makeSignallingTimelineController(itemProvider: itemProvider, roomProxy: roomProxy)
|
||||||
let parameters = RoomScreenCoordinatorParameters(userSession: UserSessionMock(.init()),
|
let parameters = RoomScreenCoordinatorParameters(userSession: UserSessionMock(.init()),
|
||||||
roomProxy: JoinedRoomProxyMock(.init(name: "Small timeline", avatarURL: .mockMXCAvatar)),
|
roomProxy: roomProxy,
|
||||||
timelineController: timelineController,
|
timelineController: timelineController,
|
||||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||||
emojiProvider: EmojiProvider(appSettings: appSettings),
|
emojiProvider: EmojiProvider(appSettings: appSettings),
|
||||||
@@ -398,11 +400,13 @@ class MockScreen: Identifiable {
|
|||||||
case .roomSmallTimelineLargePagination:
|
case .roomSmallTimelineLargePagination:
|
||||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||||
|
|
||||||
let timelineController = MockTimelineController(listenForSignals: true)
|
let itemProvider = SignallingTimelineItemProviderMock(itemProxies: RoomTimelineItemFixtures.smallChunkProxies,
|
||||||
timelineController.timelineItems = RoomTimelineItemFixtures.smallChunk
|
listenForSignals: true)
|
||||||
timelineController.backPaginationResponses = [RoomTimelineItemFixtures.largeChunk]
|
itemProvider.backPaginationResponses = [RoomTimelineItemFixtures.largeChunkProxies]
|
||||||
|
let roomProxy = JoinedRoomProxyMock(.init(name: "Small timeline, paginating", avatarURL: .mockMXCAvatar))
|
||||||
|
let timelineController = makeSignallingTimelineController(itemProvider: itemProvider, roomProxy: roomProxy)
|
||||||
let parameters = RoomScreenCoordinatorParameters(userSession: UserSessionMock(.init()),
|
let parameters = RoomScreenCoordinatorParameters(userSession: UserSessionMock(.init()),
|
||||||
roomProxy: JoinedRoomProxyMock(.init(name: "Small timeline, paginating", avatarURL: .mockMXCAvatar)),
|
roomProxy: roomProxy,
|
||||||
timelineController: timelineController,
|
timelineController: timelineController,
|
||||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||||
emojiProvider: EmojiProvider(appSettings: appSettings),
|
emojiProvider: EmojiProvider(appSettings: appSettings),
|
||||||
@@ -421,11 +425,13 @@ class MockScreen: Identifiable {
|
|||||||
case .roomLayoutTop:
|
case .roomLayoutTop:
|
||||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||||
|
|
||||||
let timelineController = MockTimelineController(listenForSignals: true)
|
let itemProvider = SignallingTimelineItemProviderMock(itemProxies: RoomTimelineItemFixtures.largeChunkProxies,
|
||||||
timelineController.timelineItems = RoomTimelineItemFixtures.largeChunk
|
listenForSignals: true)
|
||||||
timelineController.backPaginationResponses = [RoomTimelineItemFixtures.largeChunk]
|
itemProvider.backPaginationResponses = [RoomTimelineItemFixtures.largeChunkProxies]
|
||||||
|
let roomProxy = JoinedRoomProxyMock(.init(name: "Large timeline", avatarURL: .mockMXCAvatar))
|
||||||
|
let timelineController = makeSignallingTimelineController(itemProvider: itemProvider, roomProxy: roomProxy)
|
||||||
let parameters = RoomScreenCoordinatorParameters(userSession: UserSessionMock(.init()),
|
let parameters = RoomScreenCoordinatorParameters(userSession: UserSessionMock(.init()),
|
||||||
roomProxy: JoinedRoomProxyMock(.init(name: "Large timeline", avatarURL: .mockMXCAvatar)),
|
roomProxy: roomProxy,
|
||||||
timelineController: timelineController,
|
timelineController: timelineController,
|
||||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||||
emojiProvider: EmojiProvider(appSettings: appSettings),
|
emojiProvider: EmojiProvider(appSettings: appSettings),
|
||||||
@@ -445,12 +451,14 @@ class MockScreen: Identifiable {
|
|||||||
case .roomLayoutMiddle:
|
case .roomLayoutMiddle:
|
||||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||||
|
|
||||||
let timelineController = MockTimelineController(listenForSignals: true)
|
let itemProvider = SignallingTimelineItemProviderMock(itemProxies: RoomTimelineItemFixtures.largeChunkProxies,
|
||||||
timelineController.timelineItems = RoomTimelineItemFixtures.largeChunk
|
listenForSignals: true)
|
||||||
timelineController.backPaginationResponses = [RoomTimelineItemFixtures.largeChunk]
|
itemProvider.backPaginationResponses = [RoomTimelineItemFixtures.largeChunkProxies]
|
||||||
timelineController.incomingItems = [RoomTimelineItemFixtures.incomingMessage]
|
itemProvider.incomingItems = [RoomTimelineItemFixtures.incomingMessageProxy]
|
||||||
|
let roomProxy = JoinedRoomProxyMock(.init(name: "Large timeline", avatarURL: .mockMXCAvatar))
|
||||||
|
let timelineController = makeSignallingTimelineController(itemProvider: itemProvider, roomProxy: roomProxy)
|
||||||
let parameters = RoomScreenCoordinatorParameters(userSession: UserSessionMock(.init()),
|
let parameters = RoomScreenCoordinatorParameters(userSession: UserSessionMock(.init()),
|
||||||
roomProxy: JoinedRoomProxyMock(.init(name: "Large timeline", avatarURL: .mockMXCAvatar)),
|
roomProxy: roomProxy,
|
||||||
timelineController: timelineController,
|
timelineController: timelineController,
|
||||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||||
emojiProvider: EmojiProvider(appSettings: appSettings),
|
emojiProvider: EmojiProvider(appSettings: appSettings),
|
||||||
@@ -470,11 +478,13 @@ class MockScreen: Identifiable {
|
|||||||
case .roomLayoutBottom:
|
case .roomLayoutBottom:
|
||||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||||
|
|
||||||
let timelineController = MockTimelineController(listenForSignals: true)
|
let itemProvider = SignallingTimelineItemProviderMock(itemProxies: RoomTimelineItemFixtures.largeChunkProxies,
|
||||||
timelineController.timelineItems = RoomTimelineItemFixtures.largeChunk
|
listenForSignals: true)
|
||||||
timelineController.incomingItems = [RoomTimelineItemFixtures.incomingMessage]
|
itemProvider.incomingItems = [RoomTimelineItemFixtures.incomingMessageProxy]
|
||||||
|
let roomProxy = JoinedRoomProxyMock(.init(name: "Large timeline", avatarURL: .mockMXCAvatar))
|
||||||
|
let timelineController = makeSignallingTimelineController(itemProvider: itemProvider, roomProxy: roomProxy)
|
||||||
let parameters = RoomScreenCoordinatorParameters(userSession: UserSessionMock(.init()),
|
let parameters = RoomScreenCoordinatorParameters(userSession: UserSessionMock(.init()),
|
||||||
roomProxy: JoinedRoomProxyMock(.init(name: "Large timeline", avatarURL: .mockMXCAvatar)),
|
roomProxy: roomProxy,
|
||||||
timelineController: timelineController,
|
timelineController: timelineController,
|
||||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||||
emojiProvider: EmojiProvider(appSettings: appSettings),
|
emojiProvider: EmojiProvider(appSettings: appSettings),
|
||||||
@@ -532,7 +542,6 @@ class MockScreen: Identifiable {
|
|||||||
|
|
||||||
let timelineController = MockTimelineController()
|
let timelineController = MockTimelineController()
|
||||||
timelineController.timelineItems = RoomTimelineItemFixtures.disclosedPolls
|
timelineController.timelineItems = RoomTimelineItemFixtures.disclosedPolls
|
||||||
timelineController.incomingItems = []
|
|
||||||
let parameters = RoomScreenCoordinatorParameters(userSession: UserSessionMock(.init()),
|
let parameters = RoomScreenCoordinatorParameters(userSession: UserSessionMock(.init()),
|
||||||
roomProxy: JoinedRoomProxyMock(.init(name: "Polls timeline", avatarURL: .mockMXCAvatar)),
|
roomProxy: JoinedRoomProxyMock(.init(name: "Polls timeline", avatarURL: .mockMXCAvatar)),
|
||||||
timelineController: timelineController,
|
timelineController: timelineController,
|
||||||
@@ -556,7 +565,6 @@ class MockScreen: Identifiable {
|
|||||||
|
|
||||||
let timelineController = MockTimelineController()
|
let timelineController = MockTimelineController()
|
||||||
timelineController.timelineItems = RoomTimelineItemFixtures.undisclosedPolls
|
timelineController.timelineItems = RoomTimelineItemFixtures.undisclosedPolls
|
||||||
timelineController.incomingItems = []
|
|
||||||
let parameters = RoomScreenCoordinatorParameters(userSession: UserSessionMock(.init()),
|
let parameters = RoomScreenCoordinatorParameters(userSession: UserSessionMock(.init()),
|
||||||
roomProxy: JoinedRoomProxyMock(.init(name: "Polls timeline", avatarURL: .mockMXCAvatar)),
|
roomProxy: JoinedRoomProxyMock(.init(name: "Polls timeline", avatarURL: .mockMXCAvatar)),
|
||||||
timelineController: timelineController,
|
timelineController: timelineController,
|
||||||
@@ -580,7 +588,6 @@ class MockScreen: Identifiable {
|
|||||||
|
|
||||||
let timelineController = MockTimelineController()
|
let timelineController = MockTimelineController()
|
||||||
timelineController.timelineItems = RoomTimelineItemFixtures.outgoingPolls
|
timelineController.timelineItems = RoomTimelineItemFixtures.outgoingPolls
|
||||||
timelineController.incomingItems = []
|
|
||||||
let parameters = RoomScreenCoordinatorParameters(userSession: UserSessionMock(.init()),
|
let parameters = RoomScreenCoordinatorParameters(userSession: UserSessionMock(.init()),
|
||||||
roomProxy: JoinedRoomProxyMock(.init(name: "Polls timeline", avatarURL: .mockMXCAvatar)),
|
roomProxy: JoinedRoomProxyMock(.init(name: "Polls timeline", avatarURL: .mockMXCAvatar)),
|
||||||
timelineController: timelineController,
|
timelineController: timelineController,
|
||||||
@@ -618,6 +625,7 @@ class MockScreen: Identifiable {
|
|||||||
} else {
|
} else {
|
||||||
.mockRooms
|
.mockRooms
|
||||||
}
|
}
|
||||||
|
|
||||||
let clientProxy = ClientProxyMock(.init(userID: "@mock:client.com",
|
let clientProxy = ClientProxyMock(.init(userID: "@mock:client.com",
|
||||||
deviceID: "MOCKCLIENT",
|
deviceID: "MOCKCLIENT",
|
||||||
roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(roomSummaries))),
|
roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(roomSummaries))),
|
||||||
@@ -633,6 +641,13 @@ class MockScreen: Identifiable {
|
|||||||
let appMediator = AppMediatorMock(.init())
|
let appMediator = AppMediatorMock(.init())
|
||||||
appMediator.underlyingWindowManager = windowManager
|
appMediator.underlyingWindowManager = windowManager
|
||||||
|
|
||||||
|
let itemProvider = SignallingTimelineItemProviderMock(itemProxies: RoomTimelineItemFixtures.largeChunkProxies)
|
||||||
|
|
||||||
|
let timelineController = makeSignallingTimelineController(itemProvider: itemProvider,
|
||||||
|
roomProxy: JoinedRoomProxyMock(.init()))
|
||||||
|
|
||||||
|
let timelineControllerFactory = TimelineControllerFactoryMock(.init(timelineController: timelineController))
|
||||||
|
|
||||||
let flowCoordinator = UserSessionFlowCoordinator(isNewLogin: false,
|
let flowCoordinator = UserSessionFlowCoordinator(isNewLogin: false,
|
||||||
navigationRootCoordinator: navigationRootCoordinator,
|
navigationRootCoordinator: navigationRootCoordinator,
|
||||||
appLockService: AppLockService(keychainController: KeychainControllerMock(),
|
appLockService: AppLockService(keychainController: KeychainControllerMock(),
|
||||||
@@ -640,7 +655,7 @@ class MockScreen: Identifiable {
|
|||||||
flowParameters: CommonFlowParameters(userSession: UserSessionMock(.init(clientProxy: clientProxy)),
|
flowParameters: CommonFlowParameters(userSession: UserSessionMock(.init(clientProxy: clientProxy)),
|
||||||
bugReportService: BugReportServiceMock(.init()),
|
bugReportService: BugReportServiceMock(.init()),
|
||||||
elementCallService: ElementCallServiceMock(.init()),
|
elementCallService: ElementCallServiceMock(.init()),
|
||||||
timelineControllerFactory: TimelineControllerFactoryMock(.init()),
|
timelineControllerFactory: timelineControllerFactory,
|
||||||
emojiProvider: EmojiProvider(appSettings: appSettings),
|
emojiProvider: EmojiProvider(appSettings: appSettings),
|
||||||
linkMetadataProvider: LinkMetadataProvider(),
|
linkMetadataProvider: LinkMetadataProvider(),
|
||||||
appMediator: appMediator,
|
appMediator: appMediator,
|
||||||
@@ -852,4 +867,40 @@ class MockScreen: Identifiable {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
|
private func makeSignallingTimelineController(itemProvider: SignallingTimelineItemProviderMock,
|
||||||
|
roomProxy: JoinedRoomProxyMock) -> TimelineController {
|
||||||
|
let timelineProxy = TimelineProxyMock(.init(timelineItemProvider: itemProvider))
|
||||||
|
|
||||||
|
var pendingEditText: String?
|
||||||
|
|
||||||
|
timelineProxy.sendMessageHtmlInReplyToEventIDIntentionalMentionsClosure = { [weak itemProvider] message, _, _, _ in
|
||||||
|
itemProvider?.inject([.mockOwnMessage(message)])
|
||||||
|
return .success(())
|
||||||
|
}
|
||||||
|
|
||||||
|
timelineProxy.buildMessageContentForHtmlIntentionalMentionsClosure = { text, _, _ in
|
||||||
|
pendingEditText = text
|
||||||
|
return RoomMessageEventContentWithoutRelation(noHandle: .init())
|
||||||
|
}
|
||||||
|
|
||||||
|
timelineProxy.editNewContentClosure = { [weak itemProvider] _, _ in
|
||||||
|
if let text = pendingEditText {
|
||||||
|
itemProvider?.replaceLastItem(with: .mockOwnMessage(text, isEdited: true))
|
||||||
|
pendingEditText = nil
|
||||||
|
}
|
||||||
|
return .success(())
|
||||||
|
}
|
||||||
|
|
||||||
|
return TimelineController(roomProxy: roomProxy,
|
||||||
|
timelineProxy: timelineProxy,
|
||||||
|
initialFocussedEventID: nil,
|
||||||
|
timelineItemFactory: RoomTimelineItemFactory(userID: "@alice:matrix.org",
|
||||||
|
attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()),
|
||||||
|
stateEventStringBuilder: RoomStateEventStringBuilder(userID: "@alice:matrix.org")),
|
||||||
|
mediaProvider: MediaProviderMock(.init()),
|
||||||
|
appSettings: appSettings)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user