mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-05 11:58:32 +00:00
Add unit tests for voice messages (#1869)
This commit is contained in:
@@ -650,6 +650,7 @@
|
|||||||
C0DC02E2B91DC76A4D1A0E7F /* OnboardingScreenBackgroundImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F3450F4C32D73532DBBC1A2 /* OnboardingScreenBackgroundImage.swift */; };
|
C0DC02E2B91DC76A4D1A0E7F /* OnboardingScreenBackgroundImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F3450F4C32D73532DBBC1A2 /* OnboardingScreenBackgroundImage.swift */; };
|
||||||
C11939FDC40716C4387275A4 /* NotificationSettingsEditScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8544F7058D31DBEB8DBFF0F5 /* NotificationSettingsEditScreenViewModelTests.swift */; };
|
C11939FDC40716C4387275A4 /* NotificationSettingsEditScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8544F7058D31DBEB8DBFF0F5 /* NotificationSettingsEditScreenViewModelTests.swift */; };
|
||||||
C13128AAA787A4C2CBE4EE82 /* MessageForwardingScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC10CCC8D68B863E20660DBC /* MessageForwardingScreenViewModelProtocol.swift */; };
|
C13128AAA787A4C2CBE4EE82 /* MessageForwardingScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC10CCC8D68B863E20660DBC /* MessageForwardingScreenViewModelProtocol.swift */; };
|
||||||
|
C1429699A6A5BB09A25775C1 /* AudioPlayerStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89233612A8632AD7E2803620 /* AudioPlayerStateTests.swift */; };
|
||||||
C19085A284D54A166A64A86C /* AudioPlayerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA88C615D8BFCCEF0D2FEAC9 /* AudioPlayerState.swift */; };
|
C19085A284D54A166A64A86C /* AudioPlayerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA88C615D8BFCCEF0D2FEAC9 /* AudioPlayerState.swift */; };
|
||||||
C1910A16BDF131FECA77BE22 /* EmojiPickerScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEA38B9851CFCC4D67F5587D /* EmojiPickerScreenCoordinator.swift */; };
|
C1910A16BDF131FECA77BE22 /* EmojiPickerScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEA38B9851CFCC4D67F5587D /* EmojiPickerScreenCoordinator.swift */; };
|
||||||
C1A5C386319835FB0C77736B /* ReportContentScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A16CD2C62CB7DB78A4238485 /* ReportContentScreenCoordinator.swift */; };
|
C1A5C386319835FB0C77736B /* ReportContentScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A16CD2C62CB7DB78A4238485 /* ReportContentScreenCoordinator.swift */; };
|
||||||
@@ -1315,6 +1316,7 @@
|
|||||||
8872E9C5E91E9F2BFC4EBCCA /* AlignedScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlignedScrollView.swift; sourceTree = "<group>"; };
|
8872E9C5E91E9F2BFC4EBCCA /* AlignedScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlignedScrollView.swift; sourceTree = "<group>"; };
|
||||||
8896CDD20CA2D87EA3B848A1 /* RoomNotificationSettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsScreen.swift; sourceTree = "<group>"; };
|
8896CDD20CA2D87EA3B848A1 /* RoomNotificationSettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsScreen.swift; sourceTree = "<group>"; };
|
||||||
889DEDD63C68ABDA8AD29812 /* VoiceMessageMediaManagerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageMediaManagerProtocol.swift; sourceTree = "<group>"; };
|
889DEDD63C68ABDA8AD29812 /* VoiceMessageMediaManagerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageMediaManagerProtocol.swift; sourceTree = "<group>"; };
|
||||||
|
89233612A8632AD7E2803620 /* AudioPlayerStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayerStateTests.swift; sourceTree = "<group>"; };
|
||||||
892E29C98C4E8182C9037F84 /* TimelineStyler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStyler.swift; sourceTree = "<group>"; };
|
892E29C98C4E8182C9037F84 /* TimelineStyler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStyler.swift; sourceTree = "<group>"; };
|
||||||
893777A4997BBDB68079D4F5 /* ArrayTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayTests.swift; sourceTree = "<group>"; };
|
893777A4997BBDB68079D4F5 /* ArrayTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayTests.swift; sourceTree = "<group>"; };
|
||||||
8977176AB534AA41630395BC /* LegalInformationScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalInformationScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
8977176AB534AA41630395BC /* LegalInformationScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalInformationScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
@@ -2760,6 +2762,7 @@
|
|||||||
893777A4997BBDB68079D4F5 /* ArrayTests.swift */,
|
893777A4997BBDB68079D4F5 /* ArrayTests.swift */,
|
||||||
AF25E364AE85090A70AE4644 /* AttributedStringBuilderTests.swift */,
|
AF25E364AE85090A70AE4644 /* AttributedStringBuilderTests.swift */,
|
||||||
37CA26F55123E36B50DB0B3A /* AttributedStringTests.swift */,
|
37CA26F55123E36B50DB0B3A /* AttributedStringTests.swift */,
|
||||||
|
89233612A8632AD7E2803620 /* AudioPlayerStateTests.swift */,
|
||||||
6DFCAA239095A116976E32C4 /* BackgroundTaskTests.swift */,
|
6DFCAA239095A116976E32C4 /* BackgroundTaskTests.swift */,
|
||||||
EFFD3200F9960D4996159F10 /* BugReportServiceTests.swift */,
|
EFFD3200F9960D4996159F10 /* BugReportServiceTests.swift */,
|
||||||
7AB7ED3A898B07976F3AA90F /* BugReportViewModelTests.swift */,
|
7AB7ED3A898B07976F3AA90F /* BugReportViewModelTests.swift */,
|
||||||
@@ -4550,6 +4553,7 @@
|
|||||||
3EC698F80DDEEFA273857841 /* ArrayTests.swift in Sources */,
|
3EC698F80DDEEFA273857841 /* ArrayTests.swift in Sources */,
|
||||||
90DF83A6A347F7EE7EDE89EE /* AttributedStringBuilderTests.swift in Sources */,
|
90DF83A6A347F7EE7EDE89EE /* AttributedStringBuilderTests.swift in Sources */,
|
||||||
5100F53E6884A15F9BA07CC3 /* AttributedStringTests.swift in Sources */,
|
5100F53E6884A15F9BA07CC3 /* AttributedStringTests.swift in Sources */,
|
||||||
|
C1429699A6A5BB09A25775C1 /* AudioPlayerStateTests.swift in Sources */,
|
||||||
0F9E38A75337D0146652ACAB /* BackgroundTaskTests.swift in Sources */,
|
0F9E38A75337D0146652ACAB /* BackgroundTaskTests.swift in Sources */,
|
||||||
7F61F9ACD5EC9E845EF3EFBF /* BugReportServiceTests.swift in Sources */,
|
7F61F9ACD5EC9E845EF3EFBF /* BugReportServiceTests.swift in Sources */,
|
||||||
C7CFDB4929DDD9A3B5BA085D /* BugReportViewModelTests.swift in Sources */,
|
C7CFDB4929DDD9A3B5BA085D /* BugReportViewModelTests.swift in Sources */,
|
||||||
|
|||||||
@@ -115,6 +115,94 @@ class AnalyticsClientMock: AnalyticsClientProtocol {
|
|||||||
updateUserPropertiesClosure?(userProperties)
|
updateUserPropertiesClosure?(userProperties)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
class AudioPlayerMock: AudioPlayerProtocol {
|
||||||
|
var actions: AnyPublisher<AudioPlayerAction, Never> {
|
||||||
|
get { return underlyingActions }
|
||||||
|
set(value) { underlyingActions = value }
|
||||||
|
}
|
||||||
|
var underlyingActions: AnyPublisher<AudioPlayerAction, Never>!
|
||||||
|
var mediaSource: MediaSourceProxy?
|
||||||
|
var currentTime: TimeInterval {
|
||||||
|
get { return underlyingCurrentTime }
|
||||||
|
set(value) { underlyingCurrentTime = value }
|
||||||
|
}
|
||||||
|
var underlyingCurrentTime: TimeInterval!
|
||||||
|
var url: URL?
|
||||||
|
var state: MediaPlayerState {
|
||||||
|
get { return underlyingState }
|
||||||
|
set(value) { underlyingState = value }
|
||||||
|
}
|
||||||
|
var underlyingState: MediaPlayerState!
|
||||||
|
|
||||||
|
//MARK: - load
|
||||||
|
|
||||||
|
var loadMediaSourceUsingCallsCount = 0
|
||||||
|
var loadMediaSourceUsingCalled: Bool {
|
||||||
|
return loadMediaSourceUsingCallsCount > 0
|
||||||
|
}
|
||||||
|
var loadMediaSourceUsingReceivedArguments: (mediaSource: MediaSourceProxy, url: URL)?
|
||||||
|
var loadMediaSourceUsingReceivedInvocations: [(mediaSource: MediaSourceProxy, url: URL)] = []
|
||||||
|
var loadMediaSourceUsingClosure: ((MediaSourceProxy, URL) -> Void)?
|
||||||
|
|
||||||
|
func load(mediaSource: MediaSourceProxy, using url: URL) {
|
||||||
|
loadMediaSourceUsingCallsCount += 1
|
||||||
|
loadMediaSourceUsingReceivedArguments = (mediaSource: mediaSource, url: url)
|
||||||
|
loadMediaSourceUsingReceivedInvocations.append((mediaSource: mediaSource, url: url))
|
||||||
|
loadMediaSourceUsingClosure?(mediaSource, url)
|
||||||
|
}
|
||||||
|
//MARK: - play
|
||||||
|
|
||||||
|
var playCallsCount = 0
|
||||||
|
var playCalled: Bool {
|
||||||
|
return playCallsCount > 0
|
||||||
|
}
|
||||||
|
var playClosure: (() -> Void)?
|
||||||
|
|
||||||
|
func play() {
|
||||||
|
playCallsCount += 1
|
||||||
|
playClosure?()
|
||||||
|
}
|
||||||
|
//MARK: - pause
|
||||||
|
|
||||||
|
var pauseCallsCount = 0
|
||||||
|
var pauseCalled: Bool {
|
||||||
|
return pauseCallsCount > 0
|
||||||
|
}
|
||||||
|
var pauseClosure: (() -> Void)?
|
||||||
|
|
||||||
|
func pause() {
|
||||||
|
pauseCallsCount += 1
|
||||||
|
pauseClosure?()
|
||||||
|
}
|
||||||
|
//MARK: - stop
|
||||||
|
|
||||||
|
var stopCallsCount = 0
|
||||||
|
var stopCalled: Bool {
|
||||||
|
return stopCallsCount > 0
|
||||||
|
}
|
||||||
|
var stopClosure: (() -> Void)?
|
||||||
|
|
||||||
|
func stop() {
|
||||||
|
stopCallsCount += 1
|
||||||
|
stopClosure?()
|
||||||
|
}
|
||||||
|
//MARK: - seek
|
||||||
|
|
||||||
|
var seekToCallsCount = 0
|
||||||
|
var seekToCalled: Bool {
|
||||||
|
return seekToCallsCount > 0
|
||||||
|
}
|
||||||
|
var seekToReceivedProgress: Double?
|
||||||
|
var seekToReceivedInvocations: [Double] = []
|
||||||
|
var seekToClosure: ((Double) async -> Void)?
|
||||||
|
|
||||||
|
func seek(to progress: Double) async {
|
||||||
|
seekToCallsCount += 1
|
||||||
|
seekToReceivedProgress = progress
|
||||||
|
seekToReceivedInvocations.append(progress)
|
||||||
|
await seekToClosure?(progress)
|
||||||
|
}
|
||||||
|
}
|
||||||
class BugReportServiceMock: BugReportServiceProtocol {
|
class BugReportServiceMock: BugReportServiceProtocol {
|
||||||
var isRunning: Bool {
|
var isRunning: Bool {
|
||||||
get { return underlyingIsRunning }
|
get { return underlyingIsRunning }
|
||||||
@@ -226,6 +314,89 @@ class CompletionSuggestionServiceMock: CompletionSuggestionServiceProtocol {
|
|||||||
setSuggestionTriggerClosure?(suggestionTrigger)
|
setSuggestionTriggerClosure?(suggestionTrigger)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
class MediaPlayerMock: MediaPlayerProtocol {
|
||||||
|
var mediaSource: MediaSourceProxy?
|
||||||
|
var currentTime: TimeInterval {
|
||||||
|
get { return underlyingCurrentTime }
|
||||||
|
set(value) { underlyingCurrentTime = value }
|
||||||
|
}
|
||||||
|
var underlyingCurrentTime: TimeInterval!
|
||||||
|
var url: URL?
|
||||||
|
var state: MediaPlayerState {
|
||||||
|
get { return underlyingState }
|
||||||
|
set(value) { underlyingState = value }
|
||||||
|
}
|
||||||
|
var underlyingState: MediaPlayerState!
|
||||||
|
|
||||||
|
//MARK: - load
|
||||||
|
|
||||||
|
var loadMediaSourceUsingCallsCount = 0
|
||||||
|
var loadMediaSourceUsingCalled: Bool {
|
||||||
|
return loadMediaSourceUsingCallsCount > 0
|
||||||
|
}
|
||||||
|
var loadMediaSourceUsingReceivedArguments: (mediaSource: MediaSourceProxy, url: URL)?
|
||||||
|
var loadMediaSourceUsingReceivedInvocations: [(mediaSource: MediaSourceProxy, url: URL)] = []
|
||||||
|
var loadMediaSourceUsingClosure: ((MediaSourceProxy, URL) -> Void)?
|
||||||
|
|
||||||
|
func load(mediaSource: MediaSourceProxy, using url: URL) {
|
||||||
|
loadMediaSourceUsingCallsCount += 1
|
||||||
|
loadMediaSourceUsingReceivedArguments = (mediaSource: mediaSource, url: url)
|
||||||
|
loadMediaSourceUsingReceivedInvocations.append((mediaSource: mediaSource, url: url))
|
||||||
|
loadMediaSourceUsingClosure?(mediaSource, url)
|
||||||
|
}
|
||||||
|
//MARK: - play
|
||||||
|
|
||||||
|
var playCallsCount = 0
|
||||||
|
var playCalled: Bool {
|
||||||
|
return playCallsCount > 0
|
||||||
|
}
|
||||||
|
var playClosure: (() -> Void)?
|
||||||
|
|
||||||
|
func play() {
|
||||||
|
playCallsCount += 1
|
||||||
|
playClosure?()
|
||||||
|
}
|
||||||
|
//MARK: - pause
|
||||||
|
|
||||||
|
var pauseCallsCount = 0
|
||||||
|
var pauseCalled: Bool {
|
||||||
|
return pauseCallsCount > 0
|
||||||
|
}
|
||||||
|
var pauseClosure: (() -> Void)?
|
||||||
|
|
||||||
|
func pause() {
|
||||||
|
pauseCallsCount += 1
|
||||||
|
pauseClosure?()
|
||||||
|
}
|
||||||
|
//MARK: - stop
|
||||||
|
|
||||||
|
var stopCallsCount = 0
|
||||||
|
var stopCalled: Bool {
|
||||||
|
return stopCallsCount > 0
|
||||||
|
}
|
||||||
|
var stopClosure: (() -> Void)?
|
||||||
|
|
||||||
|
func stop() {
|
||||||
|
stopCallsCount += 1
|
||||||
|
stopClosure?()
|
||||||
|
}
|
||||||
|
//MARK: - seek
|
||||||
|
|
||||||
|
var seekToCallsCount = 0
|
||||||
|
var seekToCalled: Bool {
|
||||||
|
return seekToCallsCount > 0
|
||||||
|
}
|
||||||
|
var seekToReceivedProgress: Double?
|
||||||
|
var seekToReceivedInvocations: [Double] = []
|
||||||
|
var seekToClosure: ((Double) async -> Void)?
|
||||||
|
|
||||||
|
func seek(to progress: Double) async {
|
||||||
|
seekToCallsCount += 1
|
||||||
|
seekToReceivedProgress = progress
|
||||||
|
seekToReceivedInvocations.append(progress)
|
||||||
|
await seekToClosure?(progress)
|
||||||
|
}
|
||||||
|
}
|
||||||
class NotificationCenterMock: NotificationCenterProtocol {
|
class NotificationCenterMock: NotificationCenterProtocol {
|
||||||
|
|
||||||
//MARK: - post
|
//MARK: - post
|
||||||
@@ -1914,4 +2085,32 @@ class UserNotificationCenterMock: UserNotificationCenterProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
class VoiceMessageMediaManagerMock: VoiceMessageMediaManagerProtocol {
|
||||||
|
|
||||||
|
//MARK: - loadVoiceMessageFromSource
|
||||||
|
|
||||||
|
var loadVoiceMessageFromSourceBodyThrowableError: Error?
|
||||||
|
var loadVoiceMessageFromSourceBodyCallsCount = 0
|
||||||
|
var loadVoiceMessageFromSourceBodyCalled: Bool {
|
||||||
|
return loadVoiceMessageFromSourceBodyCallsCount > 0
|
||||||
|
}
|
||||||
|
var loadVoiceMessageFromSourceBodyReceivedArguments: (source: MediaSourceProxy, body: String?)?
|
||||||
|
var loadVoiceMessageFromSourceBodyReceivedInvocations: [(source: MediaSourceProxy, body: String?)] = []
|
||||||
|
var loadVoiceMessageFromSourceBodyReturnValue: URL!
|
||||||
|
var loadVoiceMessageFromSourceBodyClosure: ((MediaSourceProxy, String?) async throws -> URL)?
|
||||||
|
|
||||||
|
func loadVoiceMessageFromSource(_ source: MediaSourceProxy, body: String?) async throws -> URL {
|
||||||
|
if let error = loadVoiceMessageFromSourceBodyThrowableError {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
loadVoiceMessageFromSourceBodyCallsCount += 1
|
||||||
|
loadVoiceMessageFromSourceBodyReceivedArguments = (source: source, body: body)
|
||||||
|
loadVoiceMessageFromSourceBodyReceivedInvocations.append((source: source, body: body))
|
||||||
|
if let loadVoiceMessageFromSourceBodyClosure = loadVoiceMessageFromSourceBodyClosure {
|
||||||
|
return try await loadVoiceMessageFromSourceBodyClosure(source, body)
|
||||||
|
} else {
|
||||||
|
return loadVoiceMessageFromSourceBodyReturnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// swiftlint:enable all
|
// swiftlint:enable all
|
||||||
|
|||||||
@@ -30,3 +30,6 @@ enum AudioPlayerAction {
|
|||||||
protocol AudioPlayerProtocol: MediaPlayerProtocol {
|
protocol AudioPlayerProtocol: MediaPlayerProtocol {
|
||||||
var actions: AnyPublisher<AudioPlayerAction, Never> { get }
|
var actions: AnyPublisher<AudioPlayerAction, Never> { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sourcery: AutoMockable
|
||||||
|
extension AudioPlayerProtocol { }
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ class AudioPlayerState: ObservableObject {
|
|||||||
audioPlayer != nil
|
audioPlayer != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isPublishingProgress: Bool {
|
||||||
|
cancellableTimer != nil
|
||||||
|
}
|
||||||
|
|
||||||
init(duration: Double, waveform: Waveform? = nil, progress: Double = 0.0) {
|
init(duration: Double, waveform: Waveform? = nil, progress: Double = 0.0) {
|
||||||
self.duration = duration
|
self.duration = duration
|
||||||
self.waveform = waveform ?? Waveform(data: [])
|
self.waveform = waveform ?? Waveform(data: [])
|
||||||
@@ -133,6 +137,7 @@ class AudioPlayerState: ObservableObject {
|
|||||||
|
|
||||||
private func stopPublishProgress() {
|
private func stopPublishProgress() {
|
||||||
cancellableTimer?.cancel()
|
cancellableTimer?.cancel()
|
||||||
|
cancellableTimer = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
private func restoreAudioPlayerState(audioPlayer: AudioPlayerProtocol) async {
|
private func restoreAudioPlayerState(audioPlayer: AudioPlayerProtocol) async {
|
||||||
|
|||||||
@@ -37,3 +37,6 @@ protocol MediaPlayerProtocol {
|
|||||||
func stop()
|
func stop()
|
||||||
func seek(to progress: Double) async
|
func seek(to progress: Double) async
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sourcery: AutoMockable
|
||||||
|
extension MediaPlayerProtocol { }
|
||||||
|
|||||||
@@ -19,3 +19,6 @@ import Foundation
|
|||||||
protocol VoiceMessageMediaManagerProtocol {
|
protocol VoiceMessageMediaManagerProtocol {
|
||||||
func loadVoiceMessageFromSource(_ source: MediaSourceProxy, body: String?) async throws -> URL
|
func loadVoiceMessageFromSource(_ source: MediaSourceProxy, body: String?) async throws -> URL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sourcery: AutoMockable
|
||||||
|
extension VoiceMessageMediaManagerProtocol { }
|
||||||
|
|||||||
@@ -0,0 +1,221 @@
|
|||||||
|
//
|
||||||
|
// 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 ElementX
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
class AudioPlayerStateTests: XCTestCase {
|
||||||
|
private var audioPlayerState: AudioPlayerState!
|
||||||
|
private var audioPlayerMock: AudioPlayerMock!
|
||||||
|
|
||||||
|
private var audioPlayerActionsSubject: PassthroughSubject<AudioPlayerAction, Never>!
|
||||||
|
private var audioPlayerActions: AnyPublisher<AudioPlayerAction, Never> {
|
||||||
|
audioPlayerActionsSubject.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
|
private var audioPlayerSeekCallsSubject: PassthroughSubject<Double, Never>!
|
||||||
|
private var audioPlayerSeekCalls: AnyPublisher<Double, Never> {
|
||||||
|
audioPlayerSeekCallsSubject.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func buildAudioPlayerMock() -> AudioPlayerMock {
|
||||||
|
let audioPlayerMock = AudioPlayerMock()
|
||||||
|
audioPlayerMock.underlyingActions = audioPlayerActions
|
||||||
|
audioPlayerMock.currentTime = 0.0
|
||||||
|
audioPlayerMock.seekToClosure = { [audioPlayerSeekCallsSubject] progress in
|
||||||
|
audioPlayerSeekCallsSubject?.send(progress)
|
||||||
|
}
|
||||||
|
return audioPlayerMock
|
||||||
|
}
|
||||||
|
|
||||||
|
override func setUp() async throws {
|
||||||
|
audioPlayerActionsSubject = .init()
|
||||||
|
audioPlayerSeekCallsSubject = .init()
|
||||||
|
audioPlayerState = AudioPlayerState(duration: 10.0)
|
||||||
|
audioPlayerMock = buildAudioPlayerMock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAttach() async throws {
|
||||||
|
audioPlayerState.attachAudioPlayer(audioPlayerMock)
|
||||||
|
|
||||||
|
XCTAssert(audioPlayerState.isAttached)
|
||||||
|
XCTAssertEqual(audioPlayerState.playbackState, .loading)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDetach() async throws {
|
||||||
|
audioPlayerState.attachAudioPlayer(audioPlayerMock)
|
||||||
|
|
||||||
|
audioPlayerState.detachAudioPlayer()
|
||||||
|
XCTAssert(audioPlayerMock.stopCalled)
|
||||||
|
XCTAssertFalse(audioPlayerState.isAttached)
|
||||||
|
XCTAssertEqual(audioPlayerState.playbackState, .stopped)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testReportError() async throws {
|
||||||
|
XCTAssertEqual(audioPlayerState.playbackState, .stopped)
|
||||||
|
audioPlayerState.reportError(AudioPlayerError.genericError)
|
||||||
|
XCTAssertEqual(audioPlayerState.playbackState, .error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUpdateProgress() async throws {
|
||||||
|
audioPlayerState.attachAudioPlayer(audioPlayerMock)
|
||||||
|
|
||||||
|
// If we try to set a negative progress, the new progress must be 0.0
|
||||||
|
do {
|
||||||
|
await audioPlayerState.updateState(progress: -5.0)
|
||||||
|
XCTAssertEqual(audioPlayerState.progress, 0.0)
|
||||||
|
XCTAssertEqual(audioPlayerMock.seekToReceivedProgress, 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we try to set a progress > 1.0, the new progress must be 1.0
|
||||||
|
do {
|
||||||
|
await audioPlayerState.updateState(progress: 1.5)
|
||||||
|
XCTAssertEqual(audioPlayerState.progress, 1.0)
|
||||||
|
XCTAssertEqual(audioPlayerMock.seekToReceivedProgress, 1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
await audioPlayerState.updateState(progress: 0.4)
|
||||||
|
XCTAssertEqual(audioPlayerState.progress, 0.4)
|
||||||
|
XCTAssertEqual(audioPlayerMock.seekToReceivedProgress, 0.4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testHandlingAudioPlayerActionDidStartLoading() async throws {
|
||||||
|
audioPlayerState.attachAudioPlayer(audioPlayerMock)
|
||||||
|
|
||||||
|
let deferred = deferFulfillment(audioPlayerActions) { action in
|
||||||
|
switch action {
|
||||||
|
case .didStartLoading:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
audioPlayerActionsSubject.send(.didStartLoading)
|
||||||
|
try await deferred.fulfill()
|
||||||
|
XCTAssertEqual(audioPlayerState.playbackState, .loading)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testHandlingAudioPlayerActionDidFinishLoading() async throws {
|
||||||
|
let originalStateProgress = 0.4
|
||||||
|
await audioPlayerState.updateState(progress: originalStateProgress)
|
||||||
|
audioPlayerState.attachAudioPlayer(audioPlayerMock)
|
||||||
|
|
||||||
|
let deferred = deferFulfillment(audioPlayerActions) { action in
|
||||||
|
switch action {
|
||||||
|
case .didFinishLoading:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The progress should be restored
|
||||||
|
let deferedProgress = deferFulfillment(audioPlayerSeekCalls) { progress in
|
||||||
|
progress == originalStateProgress
|
||||||
|
}
|
||||||
|
|
||||||
|
audioPlayerActionsSubject.send(.didFinishLoading)
|
||||||
|
try await deferred.fulfill()
|
||||||
|
try await deferedProgress.fulfill()
|
||||||
|
|
||||||
|
// The state is expected to be .readyToPlay
|
||||||
|
XCTAssertEqual(audioPlayerState.playbackState, .readyToPlay)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testHandlingAudioPlayerActionDidStartPlaying() async throws {
|
||||||
|
audioPlayerState.attachAudioPlayer(audioPlayerMock)
|
||||||
|
|
||||||
|
let deferred = deferFulfillment(audioPlayerActions) { action in
|
||||||
|
switch action {
|
||||||
|
case .didStartPlaying:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
audioPlayerActionsSubject.send(.didStartPlaying)
|
||||||
|
try await deferred.fulfill()
|
||||||
|
XCTAssertEqual(audioPlayerState.playbackState, .playing)
|
||||||
|
XCTAssert(audioPlayerState.isPublishingProgress)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testHandlingAudioPlayerActionDidPausePlaying() async throws {
|
||||||
|
await audioPlayerState.updateState(progress: 0.4)
|
||||||
|
audioPlayerState.attachAudioPlayer(audioPlayerMock)
|
||||||
|
|
||||||
|
let deferred = deferFulfillment(audioPlayerActions) { action in
|
||||||
|
switch action {
|
||||||
|
case .didPausePlaying:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
audioPlayerActionsSubject.send(.didPausePlaying)
|
||||||
|
try await deferred.fulfill()
|
||||||
|
XCTAssertEqual(audioPlayerState.playbackState, .stopped)
|
||||||
|
XCTAssertEqual(audioPlayerState.progress, 0.4)
|
||||||
|
XCTAssertFalse(audioPlayerState.isPublishingProgress)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testHandlingAudioPlayerActionsidStopPlaying() async throws {
|
||||||
|
await audioPlayerState.updateState(progress: 0.4)
|
||||||
|
audioPlayerState.attachAudioPlayer(audioPlayerMock)
|
||||||
|
|
||||||
|
let deferred = deferFulfillment(audioPlayerActions) { action in
|
||||||
|
switch action {
|
||||||
|
case .didStopPlaying:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
audioPlayerActionsSubject.send(.didStopPlaying)
|
||||||
|
try await deferred.fulfill()
|
||||||
|
XCTAssertEqual(audioPlayerState.playbackState, .stopped)
|
||||||
|
XCTAssertEqual(audioPlayerState.progress, 0.4)
|
||||||
|
XCTAssertFalse(audioPlayerState.isPublishingProgress)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAudioPlayerActionsDidFinishPlaying() async throws {
|
||||||
|
await audioPlayerState.updateState(progress: 0.4)
|
||||||
|
audioPlayerState.attachAudioPlayer(audioPlayerMock)
|
||||||
|
|
||||||
|
let deferred = deferFulfillment(audioPlayerActions) { action in
|
||||||
|
switch action {
|
||||||
|
case .didFinishPlaying:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
audioPlayerActionsSubject.send(.didFinishPlaying)
|
||||||
|
try await deferred.fulfill()
|
||||||
|
XCTAssertEqual(audioPlayerState.playbackState, .stopped)
|
||||||
|
// Progress should be reset to 0
|
||||||
|
XCTAssertEqual(audioPlayerState.progress, 0.0)
|
||||||
|
XCTAssertFalse(audioPlayerState.isPublishingProgress)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user