Added the recording flow reporting and command listening on the RecordingViewModel view model in the Recording package target.
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
import Foundation
|
||||
import Recording
|
||||
|
||||
/// A reporting service that records the lifecycle reports it receives, in call order, along with the values of the last report.
|
||||
@MainActor
|
||||
final class ReportingMock: Reporting {
|
||||
|
||||
// MARK: Properties
|
||||
|
||||
/// The names of the reports received by the service, in call order.
|
||||
private(set) var reports: [String] = []
|
||||
|
||||
/// The anchor received with the last started or resumed report, or `nil` when none arrived yet.
|
||||
private(set) var lastAnchor: Date?
|
||||
|
||||
/// The elapsed recording time received with the last paused or processing report, or `nil` when none arrived yet.
|
||||
private(set) var lastElapsed: TimeInterval?
|
||||
|
||||
// MARK: Methods
|
||||
|
||||
func started(
|
||||
at anchor: Date
|
||||
) async {
|
||||
reports.append("started")
|
||||
lastAnchor = anchor
|
||||
}
|
||||
|
||||
func paused(
|
||||
elapsed: TimeInterval
|
||||
) async {
|
||||
reports.append("paused")
|
||||
lastElapsed = elapsed
|
||||
}
|
||||
|
||||
func resumed(
|
||||
anchor: Date
|
||||
) async {
|
||||
reports.append("resumed")
|
||||
lastAnchor = anchor
|
||||
}
|
||||
|
||||
func processing(
|
||||
elapsed: TimeInterval
|
||||
) async {
|
||||
reports.append("processing")
|
||||
lastElapsed = elapsed
|
||||
}
|
||||
|
||||
func ended() async {
|
||||
reports.append("ended")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import Commanding
|
||||
import SwiftUI
|
||||
import Testing
|
||||
|
||||
@@ -764,6 +765,199 @@ struct RecordingViewModelTests {
|
||||
|
||||
}
|
||||
|
||||
// MARK: Reporter
|
||||
|
||||
@MainActor
|
||||
@Suite("Reporter")
|
||||
struct Reporter {
|
||||
|
||||
@Test
|
||||
func `reports the start of a new recording with a current anchor`() async throws {
|
||||
let reporter = ReportingMock()
|
||||
let model = Model(reporter: reporter)
|
||||
|
||||
let before = Date.now
|
||||
|
||||
model.pressedMain()
|
||||
|
||||
try await Task.sleep(for: .seconds(0.1))
|
||||
|
||||
#expect(reporter.reports == ["started"])
|
||||
#expect(reporter.lastAnchor ?? .distantPast >= before)
|
||||
#expect(reporter.lastAnchor ?? .distantFuture <= .now)
|
||||
|
||||
model.pressedMain()
|
||||
}
|
||||
|
||||
@Test
|
||||
func `reports a pause with the elapsed recording time`() async throws {
|
||||
let reporter = ReportingMock()
|
||||
let model = Model(reporter: reporter)
|
||||
|
||||
model.pressedMain()
|
||||
|
||||
try await Task.sleep(for: .seconds(1.2))
|
||||
|
||||
model.pressedMain()
|
||||
|
||||
try await Task.sleep(for: .seconds(0.1))
|
||||
|
||||
#expect(reporter.reports == ["started", "paused"])
|
||||
#expect(reporter.lastElapsed ?? 0 >= 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
func `reports a resumption with an anchor moved back by the elapsed recording time`() async throws {
|
||||
let reporter = ReportingMock()
|
||||
let model = Model(reporter: reporter)
|
||||
|
||||
model.pressedMain()
|
||||
|
||||
try await Task.sleep(for: .seconds(1.2))
|
||||
|
||||
model.pressedMain()
|
||||
model.pressedMain()
|
||||
|
||||
try await Task.sleep(for: .seconds(0.1))
|
||||
|
||||
#expect(reporter.reports == ["started", "paused", "resumed"])
|
||||
#expect(reporter.lastAnchor ?? .distantFuture <= Date(timeIntervalSinceNow: -1))
|
||||
|
||||
model.pressedMain()
|
||||
}
|
||||
|
||||
@Test
|
||||
func `reports the processing and end of a sent recording`() async throws {
|
||||
let reporter = ReportingMock()
|
||||
let model = Model(
|
||||
transcribe: TranscribingMock(),
|
||||
reporter: reporter
|
||||
)
|
||||
|
||||
model.drive(to: .processing)
|
||||
|
||||
try await Task.sleep(for: .seconds(0.5))
|
||||
|
||||
#expect(reporter.reports == ["started", "paused", "processing", "ended"])
|
||||
}
|
||||
|
||||
@Test
|
||||
func `reports the end of a discarded recording`() async throws {
|
||||
let reporter = ReportingMock()
|
||||
let model = Model(reporter: reporter)
|
||||
|
||||
model.drive(to: .paused)
|
||||
|
||||
model.pressedDiscard()
|
||||
|
||||
try await Task.sleep(for: .seconds(0.1))
|
||||
|
||||
#expect(reporter.reports == ["started", "paused", "ended"])
|
||||
}
|
||||
|
||||
@Test
|
||||
func `reports a pause when the capture is interrupted`() async throws {
|
||||
let capturer = CapturingMock()
|
||||
let reporter = ReportingMock()
|
||||
let model = Model(
|
||||
capturer: capturer,
|
||||
reporter: reporter
|
||||
)
|
||||
|
||||
model.pressedMain()
|
||||
|
||||
try await Task.sleep(for: .seconds(0.1))
|
||||
|
||||
capturer.interrupt()
|
||||
|
||||
try await Task.sleep(for: .seconds(0.1))
|
||||
|
||||
#expect(reporter.reports == ["started", "paused"])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: Commands
|
||||
|
||||
@MainActor
|
||||
@Suite("Commands")
|
||||
struct Commands {
|
||||
|
||||
@Test
|
||||
func `a toggle command pauses the ongoing recording`() async throws {
|
||||
let capturer = CapturingMock()
|
||||
let commander = RecordingCommander()
|
||||
let model = Model(
|
||||
capturer: capturer,
|
||||
commander: commander
|
||||
)
|
||||
|
||||
model.pressedMain()
|
||||
|
||||
try await Task.sleep(for: .seconds(0.1))
|
||||
|
||||
commander.send(.toggle)
|
||||
|
||||
try await Task.sleep(for: .seconds(0.1))
|
||||
|
||||
#expect(model.state == .paused)
|
||||
#expect(capturer.calls == ["start", "pause"])
|
||||
}
|
||||
|
||||
@Test
|
||||
func `a toggle command resumes a paused recording`() async throws {
|
||||
let capturer = CapturingMock()
|
||||
let commander = RecordingCommander()
|
||||
let model = Model(
|
||||
capturer: capturer,
|
||||
commander: commander
|
||||
)
|
||||
|
||||
model.drive(to: .paused)
|
||||
|
||||
try await Task.sleep(for: .seconds(0.1))
|
||||
|
||||
commander.send(.toggle)
|
||||
|
||||
try await Task.sleep(for: .seconds(0.1))
|
||||
|
||||
#expect(model.state == .recording)
|
||||
#expect(capturer.calls == ["start", "pause", "resume"])
|
||||
|
||||
model.pressedMain()
|
||||
}
|
||||
|
||||
@Test
|
||||
func `a discard command throws a paused recording away`() async throws {
|
||||
let commander = RecordingCommander()
|
||||
let model = Model(commander: commander)
|
||||
|
||||
model.drive(to: .paused)
|
||||
|
||||
try await Task.sleep(for: .seconds(0.1))
|
||||
|
||||
commander.send(.discard)
|
||||
|
||||
try await Task.sleep(for: .seconds(0.1))
|
||||
|
||||
#expect(model.state == .notRecording)
|
||||
#expect(model.elapsedSeconds == 0)
|
||||
}
|
||||
|
||||
@Test
|
||||
func `commands are ignored before the first recording starts`() async throws {
|
||||
let commander = RecordingCommander()
|
||||
let model = Model(commander: commander)
|
||||
|
||||
commander.send(.toggle)
|
||||
|
||||
try await Task.sleep(for: .seconds(0.1))
|
||||
|
||||
#expect(model.state == .notRecording)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
Reference in New Issue
Block a user