Replace the ServiceLocator with direct dependency injection (#5548)

Removes the ServiceLocator singleton and instead directly injects the dependencies it was holding for more control over instantiation of the various component subtrees and better visibility over how they're being used.
This commit is contained in:
Michael
2026-05-14 01:45:49 -05:00
committed by GitHub
parent f70182d09a
commit ff5dcb040d
108 changed files with 971 additions and 729 deletions
+56 -46
View File
@@ -14,19 +14,13 @@ import Testing
@MainActor
final class TimelineViewModelTests {
var userIndicatorControllerMock: UserIndicatorControllerMock!
var cancellables = Set<AnyCancellable>()
init() async throws {
AppSettings.resetAllSettings()
cancellables.removeAll()
userIndicatorControllerMock = UserIndicatorControllerMock.default
}
deinit {
userIndicatorControllerMock = nil
}
// MARK: - Message Grouping
@Test
@@ -313,15 +307,17 @@ final class TimelineViewModelTests {
timelineController.timelineItems = items
timelineController.roomProxy = roomProxy
let appSettings = AppSettings()
let viewModel = TimelineViewModel(roomProxy: roomProxy,
timelineController: timelineController,
userSession: UserSessionMock(.init()),
mediaPlayerProvider: MediaPlayerProviderMock(),
userIndicatorController: userIndicatorControllerMock,
userIndicatorController: UserIndicatorControllerMock.default,
appMediator: AppMediatorMock.default,
appSettings: ServiceLocator.shared.settings,
analyticsService: ServiceLocator.shared.analytics,
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
appSettings: appSettings,
analyticsService: .mock(settings: appSettings),
emojiProvider: EmojiProvider(appSettings: appSettings),
linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
return (viewModel, roomProxy, timelineProxy, timelineController)
@@ -337,6 +333,8 @@ final class TimelineViewModelTests {
addReadReceipts: receipts)
let id = message.id
let appSettings = AppSettings()
// When showing them in a timeline.
let timelineController = MockTimelineController()
timelineController.timelineItems = [message]
@@ -344,11 +342,11 @@ final class TimelineViewModelTests {
timelineController: timelineController,
userSession: UserSessionMock(.init()),
mediaPlayerProvider: MediaPlayerProviderMock(),
userIndicatorController: userIndicatorControllerMock,
userIndicatorController: UserIndicatorControllerMock.default,
appMediator: AppMediatorMock.default,
appSettings: ServiceLocator.shared.settings,
analyticsService: ServiceLocator.shared.analytics,
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
appSettings: appSettings,
analyticsService: .mock(settings: appSettings),
emojiProvider: EmojiProvider(appSettings: appSettings),
linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
@@ -362,6 +360,8 @@ final class TimelineViewModelTests {
@Test
func showManageUserAsAdmin() async throws {
let appSettings = AppSettings()
let viewModel = TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "",
members: [RoomMemberProxyMock.mockAdmin,
RoomMemberProxyMock.mockAlice],
@@ -369,11 +369,11 @@ final class TimelineViewModelTests {
timelineController: MockTimelineController(),
userSession: UserSessionMock(.init()),
mediaPlayerProvider: MediaPlayerProviderMock(),
userIndicatorController: userIndicatorControllerMock,
userIndicatorController: UserIndicatorControllerMock.default,
appMediator: AppMediatorMock.default,
appSettings: ServiceLocator.shared.settings,
analyticsService: ServiceLocator.shared.analytics,
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
appSettings: appSettings,
analyticsService: .mock(settings: appSettings),
emojiProvider: EmojiProvider(appSettings: appSettings),
linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
@@ -399,6 +399,8 @@ final class TimelineViewModelTests {
@Test
func showDetailsForAnAdmin() async throws {
let appSettings = AppSettings()
let viewModel = TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "",
members: [RoomMemberProxyMock.mockAdmin,
RoomMemberProxyMock.mockAlice],
@@ -406,11 +408,11 @@ final class TimelineViewModelTests {
timelineController: MockTimelineController(),
userSession: UserSessionMock(.init()),
mediaPlayerProvider: MediaPlayerProviderMock(),
userIndicatorController: userIndicatorControllerMock,
userIndicatorController: UserIndicatorControllerMock.default,
appMediator: AppMediatorMock.default,
appSettings: ServiceLocator.shared.settings,
analyticsService: ServiceLocator.shared.analytics,
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
appSettings: appSettings,
analyticsService: .mock(settings: appSettings),
emojiProvider: EmojiProvider(appSettings: appSettings),
linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
@@ -436,6 +438,8 @@ final class TimelineViewModelTests {
@Test
func showDetailsForABannedUser() async throws {
let appSettings = AppSettings()
let viewModel = TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "",
members: [RoomMemberProxyMock.mockAdmin,
RoomMemberProxyMock.mockBanned[0]],
@@ -443,11 +447,11 @@ final class TimelineViewModelTests {
timelineController: MockTimelineController(),
userSession: UserSessionMock(.init()),
mediaPlayerProvider: MediaPlayerProviderMock(),
userIndicatorController: userIndicatorControllerMock,
userIndicatorController: UserIndicatorControllerMock.default,
appMediator: AppMediatorMock.default,
appSettings: ServiceLocator.shared.settings,
analyticsService: ServiceLocator.shared.analytics,
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
appSettings: appSettings,
analyticsService: .mock(settings: appSettings),
emojiProvider: EmojiProvider(appSettings: appSettings),
linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
@@ -476,6 +480,8 @@ final class TimelineViewModelTests {
@Test
func pinnedEvents() async throws {
let appSettings = AppSettings()
var configuration = JoinedRoomProxyMockConfiguration(name: "",
pinnedEventIDs: .init(["test1"]))
let roomProxyMock = JoinedRoomProxyMock(configuration)
@@ -486,11 +492,11 @@ final class TimelineViewModelTests {
timelineController: MockTimelineController(),
userSession: UserSessionMock(.init()),
mediaPlayerProvider: MediaPlayerProviderMock(),
userIndicatorController: userIndicatorControllerMock,
userIndicatorController: UserIndicatorControllerMock.default,
appMediator: AppMediatorMock.default,
appSettings: ServiceLocator.shared.settings,
analyticsService: ServiceLocator.shared.analytics,
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
appSettings: appSettings,
analyticsService: .mock(settings: appSettings),
emojiProvider: EmojiProvider(appSettings: appSettings),
linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
#expect(configuration.pinnedEventIDs == viewModel.context.viewState.pinnedEventIDs)
@@ -505,6 +511,8 @@ final class TimelineViewModelTests {
@Test
func canUserPinEvents() async throws {
let appSettings = AppSettings()
let configuration = JoinedRoomProxyMockConfiguration(name: "",
powerLevelsConfiguration: .init(canUserPin: true))
let roomProxyMock = JoinedRoomProxyMock(configuration)
@@ -515,11 +523,11 @@ final class TimelineViewModelTests {
timelineController: MockTimelineController(),
userSession: UserSessionMock(.init()),
mediaPlayerProvider: MediaPlayerProviderMock(),
userIndicatorController: userIndicatorControllerMock,
userIndicatorController: UserIndicatorControllerMock.default,
appMediator: AppMediatorMock.default,
appSettings: ServiceLocator.shared.settings,
analyticsService: ServiceLocator.shared.analytics,
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
appSettings: appSettings,
analyticsService: .mock(settings: appSettings),
emojiProvider: EmojiProvider(appSettings: appSettings),
linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
@@ -580,18 +588,20 @@ final class TimelineViewModelTests {
private func makeViewModel(roomProxy: JoinedRoomProxyProtocol? = nil,
focussedEventID: String? = nil,
timelineController: TimelineControllerProtocol) -> TimelineViewModel {
TimelineViewModel(roomProxy: roomProxy ?? JoinedRoomProxyMock(.init(name: "")),
focussedEventID: focussedEventID,
timelineController: timelineController,
userSession: UserSessionMock(.init()),
mediaPlayerProvider: MediaPlayerProviderMock(),
userIndicatorController: userIndicatorControllerMock,
appMediator: AppMediatorMock.default,
appSettings: ServiceLocator.shared.settings,
analyticsService: ServiceLocator.shared.analytics,
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
let appSettings = AppSettings()
return TimelineViewModel(roomProxy: roomProxy ?? JoinedRoomProxyMock(.init(name: "")),
focussedEventID: focussedEventID,
timelineController: timelineController,
userSession: UserSessionMock(.init()),
mediaPlayerProvider: MediaPlayerProviderMock(),
userIndicatorController: UserIndicatorControllerMock.default,
appMediator: AppMediatorMock.default,
appSettings: appSettings,
analyticsService: .mock(settings: appSettings),
emojiProvider: EmojiProvider(appSettings: appSettings),
linkMetadataProvider: LinkMetadataProvider(),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
}
}