mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 06:55:59 +00:00
Finish migration of UnitTests target from XCTestCase to Swift Testing (#5129)
* Initial plan * Migrate 3 test files from XCTest to Swift Testing - MediaUploadPreviewScreenViewModelTests: @MainActor @Suite struct with init(), BundleFinder class for Bundle(for:), mutating test/setup functions, [self] capture replacing [weak self] in closures - NotificationManagerTests: @MainActor @Suite final class with init()/deinit, expectation/fulfillment(of:) replaced with confirmation(...), test_ prefix stripped - NotificationSettingsScreenViewModelTests: @MainActor @Suite struct with init() throws, non-optional stored properties, test prefix stripped Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Migrate 3 XCTest files to Swift Testing - NotificationSettingsEditScreenViewModelTests: @MainActor @Suite struct with init() throws, mutating test methods - TimelineViewModelTests: @MainActor @Suite final class with init() async throws + deinit - AttributedStringBuilderTests: @Suite struct with init() async throws All XCT assertions replaced with #expect/#require/Issue.record Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Migrate 4 test files from XCTest to Swift Testing - TimelineMediaPreviewViewModelTests: @Suite struct, mutating @Test funcs, testLoadingItem renamed to loadingItem (called internally by other tests) - ServerConfirmationScreenViewModelTests: @Suite final class with init()/deinit - CompletionSuggestionServiceTests: @Suite struct with init() - RoomFlowCoordinatorTests: @Suite final class with deinit Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Migrate 4 test files from XCTest to Swift Testing - VoiceMessageRecorderTests: @Suite struct with init() async throws, added BundleFinder class for Bundle lookup, migrated all assertions - SpaceScreenViewModelTests: @Suite struct, private mutating setupViewModel, all test funcs mutating, XCTestExpectation → confirmation - RoomNotificationSettingsScreenViewModelTests: @Suite struct with init() throws, cancellable tests marked mutating - JoinRoomScreenViewModelTests: @Suite final class with init()/deinit, XCTestExpectation → confirmation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Migrate 6 test files from XCTestCase to Swift Testing Co-authored-by: pixlwave <6060466+pixlwave@users.noreply.github.com> * Fix trailing blank line in RoomPollsHistoryScreenViewModelTests Co-authored-by: pixlwave <6060466+pixlwave@users.noreply.github.com> * Migrate 3 test files from XCTest to Swift Testing - MediaUploadingPreprocessorTests: @Suite final class with init()/deinit, removed executionTimeAllowance, XCTAssertEqual(accuracy:) → abs(Double) - SecurityAndPrivacyScreenViewModelTests: @MainActor @Suite final class, 5 expectation+fulfillment → await confirmation(...) - CreateRoomViewModelTests: @MainActor @Suite final class, 4 expectation+fulfillment → await confirmation(...) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Migrate RoomScreenViewModelTests and RoomDetailsScreenViewModelTests to Swift Testing - Replace XCTest with Testing framework - RoomScreenViewModelTests: final class with init() async throws + deinit - RoomDetailsScreenViewModelTests: struct with init() and mutating funcs - Convert XCT assertions to #expect / Issue.record - Convert XCTestExpectation patterns to confirmation { confirm in } - Strip 'test' prefix from all test function names Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Migrate ComposerToolbarViewModelTests from XCTest to Swift Testing - Replace import XCTest with import Testing - Convert XCTestCase class to @MainActor @Suite final class - Replace setUp()/tearDown() with init()/deinit - Strip 'test' prefix from all 41 test method names and add @Test - Replace XCTAssert* with #expect()/#require() - Replace try XCTUnwrap() with try #require() - Convert expectation+wait patterns to deferFulfillment with PassthroughSubject - Convert isInverted expectation to boolean flag checked after await - Use deferFulfillment on $viewState for state-transition tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address comments with Copilot. * Fix the failing tests. * Fixed flaky tests (#5137) resolved flaky tests * Tweaks and fixes. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: pixlwave <6060466+pixlwave@users.noreply.github.com> Co-authored-by: Doug <douglase@element.io> Co-authored-by: Mauro <34335419+Velin92@users.noreply.github.com>
This commit is contained in:
@@ -11,10 +11,11 @@ import Combine
|
||||
import MatrixRustSDK
|
||||
import QuickLook
|
||||
import SwiftUI
|
||||
import XCTest
|
||||
import Testing
|
||||
|
||||
@Suite
|
||||
@MainActor
|
||||
class TimelineMediaPreviewViewModelTests: XCTestCase {
|
||||
struct TimelineMediaPreviewViewModelTests {
|
||||
var viewModel: TimelineMediaPreviewViewModel!
|
||||
var context: TimelineMediaPreviewViewModel.Context {
|
||||
viewModel.context
|
||||
@@ -24,49 +25,52 @@ class TimelineMediaPreviewViewModelTests: XCTestCase {
|
||||
var photoLibraryManager: PhotoLibraryManagerMock!
|
||||
var timelineController: MockTimelineController!
|
||||
|
||||
func testLoadingItem() async throws {
|
||||
@Test
|
||||
mutating func loadingItem() async throws {
|
||||
// Given a fresh view model.
|
||||
setupViewModel()
|
||||
XCTAssertFalse(mediaProvider.loadFileFromSourceFilenameCalled)
|
||||
XCTAssertEqual(context.viewState.currentItem, .media(context.viewState.dataSource.previewItems[0]))
|
||||
XCTAssertNotNil(context.viewState.currentItemActions)
|
||||
#expect(!mediaProvider.loadFileFromSourceFilenameCalled)
|
||||
#expect(context.viewState.currentItem == .media(context.viewState.dataSource.previewItems[0]))
|
||||
#expect(context.viewState.currentItemActions != nil)
|
||||
|
||||
// When the preview controller sets the current item.
|
||||
try await loadInitialItem()
|
||||
|
||||
// Then the view model should load the item and update its view state.
|
||||
XCTAssertTrue(mediaProvider.loadFileFromSourceFilenameCalled)
|
||||
XCTAssertEqual(context.viewState.currentItem, .media(context.viewState.dataSource.previewItems[0]))
|
||||
XCTAssertNotNil(context.viewState.currentItemActions)
|
||||
#expect(mediaProvider.loadFileFromSourceFilenameCalled)
|
||||
#expect(context.viewState.currentItem == .media(context.viewState.dataSource.previewItems[0]))
|
||||
#expect(context.viewState.currentItemActions != nil)
|
||||
}
|
||||
|
||||
func testLoadingItemFailure() async throws {
|
||||
@Test
|
||||
mutating func loadingItemFailure() async throws {
|
||||
// Given a fresh view model.
|
||||
setupViewModel()
|
||||
guard case let .media(mediaItem) = context.viewState.currentItem else {
|
||||
XCTFail("There should be a current item")
|
||||
Issue.record("There should be a current item")
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertFalse(mediaProvider.loadFileFromSourceFilenameCalled)
|
||||
XCTAssertEqual(mediaItem, context.viewState.dataSource.previewItems[0])
|
||||
XCTAssertNil(mediaItem.downloadError)
|
||||
#expect(!mediaProvider.loadFileFromSourceFilenameCalled)
|
||||
#expect(mediaItem == context.viewState.dataSource.previewItems[0])
|
||||
#expect(mediaItem.downloadError == nil)
|
||||
|
||||
// When the preview controller sets an item that fails to load.
|
||||
mediaProvider.loadFileFromSourceFilenameClosure = { _, _ in .failure(.failedRetrievingFile) }
|
||||
let failure = deferFailure(viewModel.state.previewControllerDriver, timeout: 1) { $0.isItemLoaded }
|
||||
let failure = deferFailure(viewModel.state.previewControllerDriver, timeout: .seconds(1)) { $0.isItemLoaded }
|
||||
context.send(viewAction: .updateCurrentItem(.media(context.viewState.dataSource.previewItems[0])))
|
||||
try await failure.fulfill()
|
||||
|
||||
// Then the view model should load the item and update its view state.
|
||||
XCTAssertTrue(mediaProvider.loadFileFromSourceFilenameCalled)
|
||||
XCTAssertEqual(mediaItem, context.viewState.dataSource.previewItems[0])
|
||||
XCTAssertNotNil(mediaItem.downloadError)
|
||||
#expect(mediaProvider.loadFileFromSourceFilenameCalled)
|
||||
#expect(mediaItem == context.viewState.dataSource.previewItems[0])
|
||||
#expect(mediaItem.downloadError != nil)
|
||||
}
|
||||
|
||||
func testSwipingBetweenItems() async throws {
|
||||
@Test
|
||||
mutating func swipingBetweenItems() async throws {
|
||||
// Given a view model with a loaded item.
|
||||
try await testLoadingItem()
|
||||
try await loadingItem()
|
||||
|
||||
// When swiping to another item.
|
||||
let deferred = deferFulfillment(viewModel.state.previewControllerDriver) { $0.isItemLoaded }
|
||||
@@ -74,41 +78,43 @@ class TimelineMediaPreviewViewModelTests: XCTestCase {
|
||||
try await deferred.fulfill()
|
||||
|
||||
// Then the view model should load the item and update its view state.
|
||||
XCTAssertEqual(mediaProvider.loadFileFromSourceFilenameCallsCount, 2)
|
||||
XCTAssertEqual(context.viewState.currentItem, .media(context.viewState.dataSource.previewItems[1]))
|
||||
#expect(mediaProvider.loadFileFromSourceFilenameCallsCount == 2)
|
||||
#expect(context.viewState.currentItem == .media(context.viewState.dataSource.previewItems[1]))
|
||||
|
||||
// When swiping back to the first item.
|
||||
let failure = deferFailure(viewModel.state.previewControllerDriver, timeout: 1) { $0.isItemLoaded }
|
||||
let failure = deferFailure(viewModel.state.previewControllerDriver, timeout: .seconds(1)) { $0.isItemLoaded }
|
||||
context.send(viewAction: .updateCurrentItem(.media(context.viewState.dataSource.previewItems[0])))
|
||||
try await failure.fulfill()
|
||||
|
||||
// Then the view model should not need to load the item, but should still update its view state.
|
||||
XCTAssertEqual(mediaProvider.loadFileFromSourceFilenameCallsCount, 2)
|
||||
XCTAssertEqual(context.viewState.currentItem, .media(context.viewState.dataSource.previewItems[0]))
|
||||
#expect(mediaProvider.loadFileFromSourceFilenameCallsCount == 2)
|
||||
#expect(context.viewState.currentItem == .media(context.viewState.dataSource.previewItems[0]))
|
||||
}
|
||||
|
||||
func testLoadingMoreItems() async throws {
|
||||
@Test
|
||||
mutating func loadingMoreItems() async throws {
|
||||
// Given a view model with a loaded item.
|
||||
try await testLoadingItem()
|
||||
XCTAssertEqual(timelineController.paginateBackwardsCallCount, 0)
|
||||
try await loadingItem()
|
||||
#expect(timelineController.paginateBackwardsCallCount == 0)
|
||||
|
||||
// When swiping to a "loading more" item and there are more media items to load.
|
||||
timelineController.paginationState = .init(backward: .idle, forward: .endReached)
|
||||
timelineController.backPaginationResponses.append(RoomTimelineItemFixtures.mediaChunk)
|
||||
let failure = deferFailure(viewModel.state.previewControllerDriver, timeout: 1) { $0.isItemLoaded }
|
||||
let failure = deferFailure(viewModel.state.previewControllerDriver, timeout: .seconds(1)) { $0.isItemLoaded }
|
||||
context.send(viewAction: .updateCurrentItem(.loading(.paginatingBackwards)))
|
||||
try await failure.fulfill()
|
||||
|
||||
// Then there should no longer be a media preview and instead of loading any media, a pagination request should be made.
|
||||
XCTAssertEqual(mediaProvider.loadFileFromSourceFilenameCallsCount, 1)
|
||||
XCTAssertEqual(context.viewState.currentItem, .loading(.paginatingBackwards)) // Note: This item only changes when the preview controller handles the new items.
|
||||
XCTAssertEqual(timelineController.paginateBackwardsCallCount, 1)
|
||||
#expect(mediaProvider.loadFileFromSourceFilenameCallsCount == 1)
|
||||
#expect(context.viewState.currentItem == .loading(.paginatingBackwards)) // Note: This item only changes when the preview controller handles the new items.
|
||||
#expect(timelineController.paginateBackwardsCallCount == 1)
|
||||
}
|
||||
|
||||
func testPagination() async throws {
|
||||
@Test
|
||||
mutating func pagination() async throws {
|
||||
// Given a view model with a loaded item.
|
||||
try await testLoadingItem()
|
||||
XCTAssertEqual(context.viewState.dataSource.previewItems.count, 3)
|
||||
try await loadingItem()
|
||||
#expect(context.viewState.dataSource.previewItems.count == 3)
|
||||
|
||||
// When more items are added via a back pagination.
|
||||
let deferred = deferFulfillment(context.viewState.dataSource.previewItemsPaginationPublisher) { _ in true }
|
||||
@@ -118,22 +124,23 @@ class TimelineMediaPreviewViewModelTests: XCTestCase {
|
||||
|
||||
// And the preview controller attempts to update the current item (now at a new index in the array but it hasn't changed in the data source).
|
||||
mediaProvider.loadFileFromSourceFilenameClosure = { _, _ in .failure(.failedRetrievingFile) }
|
||||
let failure = deferFailure(viewModel.state.previewControllerDriver, timeout: 1) { $0.isItemLoaded }
|
||||
let failure = deferFailure(viewModel.state.previewControllerDriver, timeout: .seconds(1)) { $0.isItemLoaded }
|
||||
context.send(viewAction: .updateCurrentItem(.media(context.viewState.dataSource.previewItems[3])))
|
||||
try await failure.fulfill()
|
||||
|
||||
// Then the current item shouldn't need to be reloaded.
|
||||
XCTAssertEqual(context.viewState.dataSource.previewItems.count, 6)
|
||||
XCTAssertEqual(mediaProvider.loadFileFromSourceFilenameCallsCount, 1)
|
||||
#expect(context.viewState.dataSource.previewItems.count == 6)
|
||||
#expect(mediaProvider.loadFileFromSourceFilenameCallsCount == 1)
|
||||
}
|
||||
|
||||
func testViewInRoomTimeline() async throws {
|
||||
@Test
|
||||
mutating func viewInRoomTimeline() async throws {
|
||||
// Given a view model with a loaded item.
|
||||
try await testLoadingItem()
|
||||
try await loadingItem()
|
||||
|
||||
// When choosing to view the current item in the timeline.
|
||||
guard case let .media(mediaItem) = context.viewState.currentItem else {
|
||||
XCTFail("There should be a current item.")
|
||||
Issue.record("There should be a current item.")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -144,13 +151,14 @@ class TimelineMediaPreviewViewModelTests: XCTestCase {
|
||||
try await deferred.fulfill()
|
||||
}
|
||||
|
||||
func testRedactConfirmation() async throws {
|
||||
@Test
|
||||
mutating func redactConfirmation() async throws {
|
||||
// Given a view model with a loaded item.
|
||||
try await testLoadingItem()
|
||||
XCTAssertNil(context.redactConfirmationItem)
|
||||
XCTAssertFalse(timelineController.redactCalled)
|
||||
try await loadingItem()
|
||||
#expect(context.redactConfirmationItem == nil)
|
||||
#expect(!timelineController.redactCalled)
|
||||
guard case let .media(mediaItem) = context.viewState.currentItem else {
|
||||
XCTFail("There should be a current item.")
|
||||
Issue.record("There should be a current item.")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -161,17 +169,17 @@ class TimelineMediaPreviewViewModelTests: XCTestCase {
|
||||
// Then the details sheet should be presented.
|
||||
let action = try await deferredDriver.fulfill()
|
||||
guard case let .showItemDetails(mediaDetailsItem) = action else {
|
||||
XCTFail("The action should include the media item.")
|
||||
Issue.record("The action should include the media item.")
|
||||
return
|
||||
}
|
||||
XCTAssertEqual(.media(mediaDetailsItem), context.viewState.currentItem)
|
||||
#expect(.media(mediaDetailsItem) == context.viewState.currentItem)
|
||||
|
||||
// When choosing to redact the item.
|
||||
context.send(viewAction: .menuAction(.redact, item: mediaItem))
|
||||
|
||||
// Then the confirmation sheet should be presented.
|
||||
XCTAssertEqual(context.redactConfirmationItem, mediaItem)
|
||||
XCTAssertFalse(timelineController.redactCalled)
|
||||
#expect(context.redactConfirmationItem == mediaItem)
|
||||
#expect(!timelineController.redactCalled)
|
||||
|
||||
// When confirming the redaction.
|
||||
let deferred = deferFulfillment(viewModel.actions) { $0 == .dismiss }
|
||||
@@ -179,37 +187,39 @@ class TimelineMediaPreviewViewModelTests: XCTestCase {
|
||||
|
||||
// Then the item should be redacted and the view should be dismissed.
|
||||
try await deferred.fulfill()
|
||||
XCTAssertTrue(timelineController.redactCalled)
|
||||
#expect(timelineController.redactCalled)
|
||||
}
|
||||
|
||||
func testSaveImage() async throws {
|
||||
@Test
|
||||
mutating func saveImage() async throws {
|
||||
// Given a view model with a loaded image.
|
||||
try await testLoadingItem()
|
||||
try await loadingItem()
|
||||
guard case let .media(mediaItem) = context.viewState.currentItem else {
|
||||
XCTFail("There should be a current item")
|
||||
Issue.record("There should be a current item")
|
||||
return
|
||||
}
|
||||
XCTAssertEqual(mediaItem.contentType, "JPEG image")
|
||||
#expect(mediaItem.contentType == "JPEG image")
|
||||
|
||||
// When choosing to save the image.
|
||||
context.send(viewAction: .menuAction(.save, item: mediaItem))
|
||||
try await Task.sleep(for: .seconds(0.5))
|
||||
|
||||
// Then the image should be saved as a photo to the user's photo library.
|
||||
XCTAssertTrue(photoLibraryManager.addResourceAtCalled)
|
||||
XCTAssertEqual(photoLibraryManager.addResourceAtReceivedArguments?.type, .photo)
|
||||
XCTAssertEqual(photoLibraryManager.addResourceAtReceivedArguments?.url, mediaItem.fileHandle?.url)
|
||||
#expect(photoLibraryManager.addResourceAtCalled)
|
||||
#expect(photoLibraryManager.addResourceAtReceivedArguments?.type == .photo)
|
||||
#expect(photoLibraryManager.addResourceAtReceivedArguments?.url == mediaItem.fileHandle?.url)
|
||||
}
|
||||
|
||||
func testSaveImageWithoutAuthorization() async throws {
|
||||
@Test
|
||||
mutating func saveImageWithoutAuthorization() async throws {
|
||||
// Given a view model with a loaded image where the user has denied access to the photo library.
|
||||
setupViewModel(photoLibraryAuthorizationDenied: true)
|
||||
try await loadInitialItem()
|
||||
guard case let .media(mediaItem) = context.viewState.currentItem else {
|
||||
XCTFail("There should be a current item")
|
||||
Issue.record("There should be a current item")
|
||||
return
|
||||
}
|
||||
XCTAssertEqual(mediaItem.contentType, "JPEG image")
|
||||
#expect(mediaItem.contentType == "JPEG image")
|
||||
|
||||
// When choosing to save the image.
|
||||
let deferred = deferFulfillment(context.viewState.previewControllerDriver) { $0.isAuthorizationRequired }
|
||||
@@ -217,38 +227,40 @@ class TimelineMediaPreviewViewModelTests: XCTestCase {
|
||||
|
||||
// Then the user should be prompted to allow access.
|
||||
try await deferred.fulfill()
|
||||
XCTAssertTrue(photoLibraryManager.addResourceAtCalled)
|
||||
#expect(photoLibraryManager.addResourceAtCalled)
|
||||
}
|
||||
|
||||
func testSaveVideo() async throws {
|
||||
@Test
|
||||
mutating func saveVideo() async throws {
|
||||
// Given a view model with a loaded video.
|
||||
setupViewModel(initialItemIndex: 1)
|
||||
try await loadInitialItem()
|
||||
guard case let .media(mediaItem) = context.viewState.currentItem else {
|
||||
XCTFail("There should be a current item")
|
||||
Issue.record("There should be a current item")
|
||||
return
|
||||
}
|
||||
XCTAssertEqual(mediaItem.contentType, "MPEG-4 movie")
|
||||
#expect(mediaItem.contentType == "MPEG-4 movie")
|
||||
|
||||
// When choosing to save the video.
|
||||
context.send(viewAction: .menuAction(.save, item: mediaItem))
|
||||
try await Task.sleep(for: .seconds(0.5))
|
||||
|
||||
// Then the video should be saved as a video in the user's photo library.
|
||||
XCTAssertTrue(photoLibraryManager.addResourceAtCalled)
|
||||
XCTAssertEqual(photoLibraryManager.addResourceAtReceivedArguments?.type, .video)
|
||||
XCTAssertEqual(photoLibraryManager.addResourceAtReceivedArguments?.url, mediaItem.fileHandle?.url)
|
||||
#expect(photoLibraryManager.addResourceAtCalled)
|
||||
#expect(photoLibraryManager.addResourceAtReceivedArguments?.type == .video)
|
||||
#expect(photoLibraryManager.addResourceAtReceivedArguments?.url == mediaItem.fileHandle?.url)
|
||||
}
|
||||
|
||||
func testSaveFile() async throws {
|
||||
@Test
|
||||
mutating func saveFile() async throws {
|
||||
// Given a view model with a loaded file.
|
||||
setupViewModel(initialItemIndex: 2)
|
||||
try await loadInitialItem()
|
||||
guard case let .media(mediaItem) = context.viewState.currentItem else {
|
||||
XCTFail("There should be a current item")
|
||||
Issue.record("There should be a current item")
|
||||
return
|
||||
}
|
||||
XCTAssertEqual(mediaItem.contentType, "PDF document")
|
||||
#expect(mediaItem.contentType == "PDF document")
|
||||
|
||||
// When choosing to save the file.
|
||||
let deferred = deferFulfillment(context.viewState.previewControllerDriver) { $0.isExportFile }
|
||||
@@ -256,13 +268,13 @@ class TimelineMediaPreviewViewModelTests: XCTestCase {
|
||||
let exportAction = try await deferred.fulfill()
|
||||
|
||||
guard case let .exportFile(file) = exportAction else {
|
||||
XCTFail("Unexpected action")
|
||||
Issue.record("Unexpected action")
|
||||
return
|
||||
}
|
||||
|
||||
// Then the binding should be set for the user to export the file to their specified location.
|
||||
XCTAssertFalse(photoLibraryManager.addResourceAtCalled)
|
||||
XCTAssertEqual(file.url, mediaItem.fileHandle?.url)
|
||||
#expect(!photoLibraryManager.addResourceAtCalled)
|
||||
#expect(file.url == mediaItem.fileHandle?.url)
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
@@ -272,14 +284,14 @@ class TimelineMediaPreviewViewModelTests: XCTestCase {
|
||||
let initialItem = context.viewState.dataSource.previewController(QLPreviewController(),
|
||||
previewItemAt: context.viewState.dataSource.initialItemIndex)
|
||||
guard let initialPreviewItem = initialItem as? TimelineMediaPreviewItem.Media else {
|
||||
XCTFail("The initial item should be a media preview.")
|
||||
Issue.record("The initial item should be a media preview.")
|
||||
return
|
||||
}
|
||||
context.send(viewAction: .updateCurrentItem(.media(initialPreviewItem)))
|
||||
try await deferred.fulfill()
|
||||
}
|
||||
|
||||
private func setupViewModel(initialItemIndex: Int = 0, photoLibraryAuthorizationDenied: Bool = false) {
|
||||
private mutating func setupViewModel(initialItemIndex: Int = 0, photoLibraryAuthorizationDenied: Bool = false) {
|
||||
let initialItems = makeItems()
|
||||
timelineController = MockTimelineController(timelineKind: .media(.mediaFilesScreen))
|
||||
timelineController.timelineItems = initialItems
|
||||
|
||||
Reference in New Issue
Block a user