Expose iPhone playback controls
This commit is contained in:
@@ -1,4 +1,10 @@
|
||||
import Foundation
|
||||
#if canImport(SwiftUI)
|
||||
import SwiftUI
|
||||
#endif
|
||||
#if canImport(UIKit)
|
||||
import UIKit
|
||||
#endif
|
||||
import XCTest
|
||||
import VelodyDomain
|
||||
import VelodyNetworking
|
||||
@@ -22,6 +28,128 @@ final class iPhoneLibraryViewModelPlaybackTests: XCTestCase {
|
||||
XCTAssertEqual(NowPlayingCardLayoutMetrics.artworkHeight(for: 520), 176)
|
||||
}
|
||||
|
||||
func testNowPlayingControlsLayoutFitsCommonIPhoneCardWidths() {
|
||||
let cases: [(name: String, availableWidth: CGFloat, expectedStyle: NowPlayingCardLayoutMetrics.ControlsLayout.Style)] = [
|
||||
("iPhone 16e", 311, .regular),
|
||||
("iPhone 17", 321, .regular),
|
||||
("iPhone 17 Pro Max", 358, .regular),
|
||||
]
|
||||
|
||||
for testCase in cases {
|
||||
let layout = NowPlayingCardLayoutMetrics.controlsLayout(for: testCase.availableWidth)
|
||||
XCTAssertLessThanOrEqual(
|
||||
layout.firstRowWidth,
|
||||
testCase.availableWidth,
|
||||
"\(testCase.name) control row overflowed the card width"
|
||||
)
|
||||
XCTAssertGreaterThan(layout.primaryButtonSize, layout.secondaryButtonSize)
|
||||
XCTAssertEqual(layout.style, testCase.expectedStyle)
|
||||
}
|
||||
}
|
||||
|
||||
#if canImport(UIKit)
|
||||
func testNowPlayingViewRendersCommonIPhoneLayouts() async throws {
|
||||
let captures: [(name: String, size: CGSize)] = [
|
||||
("iphone-16e", CGSize(width: 375, height: 812)),
|
||||
("iphone-17", CGSize(width: 393, height: 852)),
|
||||
("iphone-17-pro-max", CGSize(width: 430, height: 932)),
|
||||
]
|
||||
let outputDirectory = URL(fileURLWithPath: NSTemporaryDirectory())
|
||||
.appendingPathComponent("velody-now-playing-layout", isDirectory: true)
|
||||
|
||||
try? FileManager.default.removeItem(at: outputDirectory)
|
||||
try FileManager.default.createDirectory(at: outputDirectory, withIntermediateDirectories: true)
|
||||
|
||||
for capture in captures {
|
||||
let viewModel = try await makeNowPlayingScreenshotViewModel()
|
||||
let image = try XCTUnwrap(
|
||||
renderNowPlayingLayoutScreenshot(viewModel: viewModel, size: capture.size)
|
||||
)
|
||||
|
||||
XCTAssertEqual(image.size.width, capture.size.width)
|
||||
XCTAssertEqual(image.size.height, capture.size.height)
|
||||
|
||||
let url = outputDirectory.appendingPathComponent("\(capture.name).png")
|
||||
let pngData = try XCTUnwrap(image.pngData())
|
||||
try pngData.write(to: url)
|
||||
print("Saved now playing screenshot to \(url.path)")
|
||||
}
|
||||
}
|
||||
|
||||
private func makeNowPlayingScreenshotViewModel() async throws -> iPhoneLibraryViewModel {
|
||||
let firstTrack = makeRemoteTrack(
|
||||
trackId: "remote-layout-first",
|
||||
assetId: "asset-layout-first",
|
||||
title: "Northern Lights"
|
||||
)
|
||||
let secondTrack = makeRemoteTrack(
|
||||
trackId: "remote-layout-second",
|
||||
assetId: "asset-layout-second",
|
||||
title: "Horizons"
|
||||
)
|
||||
let thirdTrack = makeRemoteTrack(
|
||||
trackId: "remote-layout-third",
|
||||
assetId: "asset-layout-third",
|
||||
title: "Coastline"
|
||||
)
|
||||
let favoriteStore = InMemoryFavoriteTrackStore(
|
||||
tracks: [
|
||||
FavoriteTrackRecord(
|
||||
remoteTrackId: secondTrack.trackId,
|
||||
favoritedAt: Date(timeIntervalSince1970: 3_000)
|
||||
),
|
||||
]
|
||||
)
|
||||
let viewModel = makeViewModel(
|
||||
remoteTracks: [firstTrack, secondTrack, thirdTrack],
|
||||
downloadStates: [
|
||||
makeDownloadedState(for: firstTrack),
|
||||
makeDownloadedState(for: secondTrack),
|
||||
makeDownloadedState(for: thirdTrack),
|
||||
],
|
||||
favoriteTrackStore: favoriteStore,
|
||||
audioFiles: [
|
||||
localFilePath(for: firstTrack): Data([0x1]),
|
||||
localFilePath(for: secondTrack): Data([0x2]),
|
||||
localFilePath(for: thirdTrack): Data([0x3]),
|
||||
]
|
||||
)
|
||||
|
||||
await viewModel.loadIfNeeded()
|
||||
viewModel.togglePlayback(trackID: secondTrack.trackId)
|
||||
viewModel.cycleRepeatMode()
|
||||
|
||||
return viewModel
|
||||
}
|
||||
|
||||
private func renderNowPlayingLayoutScreenshot(
|
||||
viewModel: iPhoneLibraryViewModel,
|
||||
size: CGSize
|
||||
) -> UIImage? {
|
||||
let rootView = iPhoneLibraryView(viewModel: viewModel)
|
||||
.frame(width: size.width, height: size.height)
|
||||
let hostingController = UIHostingController(rootView: rootView)
|
||||
let window = UIWindow(frame: CGRect(origin: .zero, size: size))
|
||||
|
||||
window.rootViewController = hostingController
|
||||
window.isHidden = false
|
||||
hostingController.view.frame = window.bounds
|
||||
hostingController.view.backgroundColor = .systemGroupedBackground
|
||||
hostingController.view.setNeedsLayout()
|
||||
hostingController.view.layoutIfNeeded()
|
||||
window.layoutIfNeeded()
|
||||
RunLoop.main.run(until: Date(timeIntervalSinceNow: 0.05))
|
||||
|
||||
let renderer = UIGraphicsImageRenderer(size: size)
|
||||
let image = renderer.image { _ in
|
||||
hostingController.view.drawHierarchy(in: hostingController.view.bounds, afterScreenUpdates: true)
|
||||
}
|
||||
|
||||
window.isHidden = true
|
||||
return image
|
||||
}
|
||||
#endif
|
||||
|
||||
func testNowPlayingCardShowsTitleArtistAndArtwork() async throws {
|
||||
let artwork = RemoteArtwork(
|
||||
artworkId: "artwork-midnight",
|
||||
@@ -137,6 +265,85 @@ final class iPhoneLibraryViewModelPlaybackTests: XCTestCase {
|
||||
XCTAssertEqual(viewModel.nowPlaying.duration, 245)
|
||||
}
|
||||
|
||||
func testNextTrackUpdatesNowPlayingMetadata() async throws {
|
||||
let firstTrack = makeRemoteTrack(
|
||||
trackId: "remote-next-first",
|
||||
assetId: "asset-next-first",
|
||||
title: "Arrival"
|
||||
)
|
||||
let secondTrack = makeRemoteTrack(
|
||||
trackId: "remote-next-second",
|
||||
assetId: "asset-next-second",
|
||||
title: "Departure"
|
||||
)
|
||||
let player = TestPlayer()
|
||||
let viewModel = makeViewModel(
|
||||
remoteTracks: [firstTrack, secondTrack],
|
||||
downloadStates: [
|
||||
makeDownloadedState(for: firstTrack),
|
||||
makeDownloadedState(for: secondTrack),
|
||||
],
|
||||
audioFiles: [
|
||||
localFilePath(for: firstTrack): Data([0x1]),
|
||||
localFilePath(for: secondTrack): Data([0x2]),
|
||||
],
|
||||
player: player
|
||||
)
|
||||
|
||||
await viewModel.loadIfNeeded()
|
||||
viewModel.togglePlayback(trackID: firstTrack.trackId)
|
||||
viewModel.nextTrack()
|
||||
|
||||
let card = try XCTUnwrap(viewModel.nowPlayingCard)
|
||||
XCTAssertEqual(viewModel.nowPlaying.trackID, secondTrack.trackId)
|
||||
XCTAssertEqual(card.trackID, secondTrack.trackId)
|
||||
XCTAssertEqual(card.title, secondTrack.title)
|
||||
XCTAssertEqual(card.artist, secondTrack.artist)
|
||||
XCTAssertEqual(viewModel.nowPlaying.playbackState, .playing)
|
||||
XCTAssertTrue(viewModel.canGoPrevious)
|
||||
XCTAssertFalse(viewModel.canGoNext)
|
||||
}
|
||||
|
||||
func testPreviousTrackUpdatesNowPlayingMetadata() async throws {
|
||||
let firstTrack = makeRemoteTrack(
|
||||
trackId: "remote-previous-first",
|
||||
assetId: "asset-previous-first",
|
||||
title: "Early Bird"
|
||||
)
|
||||
let secondTrack = makeRemoteTrack(
|
||||
trackId: "remote-previous-second",
|
||||
assetId: "asset-previous-second",
|
||||
title: "Night Owl"
|
||||
)
|
||||
let player = TestPlayer()
|
||||
let viewModel = makeViewModel(
|
||||
remoteTracks: [firstTrack, secondTrack],
|
||||
downloadStates: [
|
||||
makeDownloadedState(for: firstTrack),
|
||||
makeDownloadedState(for: secondTrack),
|
||||
],
|
||||
audioFiles: [
|
||||
localFilePath(for: firstTrack): Data([0x1]),
|
||||
localFilePath(for: secondTrack): Data([0x2]),
|
||||
],
|
||||
player: player
|
||||
)
|
||||
|
||||
await viewModel.loadIfNeeded()
|
||||
viewModel.togglePlayback(trackID: firstTrack.trackId)
|
||||
viewModel.nextTrack()
|
||||
viewModel.previousTrack()
|
||||
|
||||
let card = try XCTUnwrap(viewModel.nowPlayingCard)
|
||||
XCTAssertEqual(viewModel.nowPlaying.trackID, firstTrack.trackId)
|
||||
XCTAssertEqual(card.trackID, firstTrack.trackId)
|
||||
XCTAssertEqual(card.title, firstTrack.title)
|
||||
XCTAssertEqual(card.artist, firstTrack.artist)
|
||||
XCTAssertEqual(viewModel.nowPlaying.playbackState, .playing)
|
||||
XCTAssertFalse(viewModel.canGoPrevious)
|
||||
XCTAssertTrue(viewModel.canGoNext)
|
||||
}
|
||||
|
||||
func testSearchDoesNotClearNowPlaying() async throws {
|
||||
let firstTrack = makeRemoteTrack(
|
||||
trackId: "remote-first-search",
|
||||
@@ -217,7 +424,210 @@ final class iPhoneLibraryViewModelPlaybackTests: XCTestCase {
|
||||
XCTAssertEqual(viewModel.nowPlaying.duration, 245)
|
||||
}
|
||||
|
||||
func testRelaunchRestoresMetadataButDoesNotAutoplay() {
|
||||
func testShufflePreservesCurrentTrackAndUpdatesState() async throws {
|
||||
let firstTrack = makeRemoteTrack(
|
||||
trackId: "remote-shuffle-first",
|
||||
assetId: "asset-shuffle-first",
|
||||
title: "Northbound"
|
||||
)
|
||||
let secondTrack = makeRemoteTrack(
|
||||
trackId: "remote-shuffle-second",
|
||||
assetId: "asset-shuffle-second",
|
||||
title: "Southbound"
|
||||
)
|
||||
let thirdTrack = makeRemoteTrack(
|
||||
trackId: "remote-shuffle-third",
|
||||
assetId: "asset-shuffle-third",
|
||||
title: "Westbound"
|
||||
)
|
||||
let player = TestPlayer()
|
||||
let viewModel = makeViewModel(
|
||||
remoteTracks: [firstTrack, secondTrack, thirdTrack],
|
||||
downloadStates: [
|
||||
makeDownloadedState(for: firstTrack),
|
||||
makeDownloadedState(for: secondTrack),
|
||||
makeDownloadedState(for: thirdTrack),
|
||||
],
|
||||
audioFiles: [
|
||||
localFilePath(for: firstTrack): Data([0x1]),
|
||||
localFilePath(for: secondTrack): Data([0x2]),
|
||||
localFilePath(for: thirdTrack): Data([0x3]),
|
||||
],
|
||||
player: player
|
||||
)
|
||||
|
||||
await viewModel.loadIfNeeded()
|
||||
viewModel.togglePlayback(trackID: secondTrack.trackId)
|
||||
|
||||
let originalTrackIDs = Set([firstTrack.trackId, secondTrack.trackId, thirdTrack.trackId])
|
||||
viewModel.toggleShuffle()
|
||||
|
||||
XCTAssertTrue(viewModel.isShuffleEnabled)
|
||||
XCTAssertEqual(viewModel.nowPlaying.trackID, secondTrack.trackId)
|
||||
XCTAssertEqual(viewModel.nowPlaying.queueTrackIDs.first, secondTrack.trackId)
|
||||
XCTAssertEqual(Set(viewModel.nowPlaying.queueTrackIDs), originalTrackIDs)
|
||||
XCTAssertEqual(Set(viewModel.nowPlaying.queueTrackIDs).count, originalTrackIDs.count)
|
||||
}
|
||||
|
||||
func testRepeatCyclesCorrectly() async throws {
|
||||
let track = makeRemoteTrack(
|
||||
trackId: "remote-repeat-cycle",
|
||||
assetId: "asset-repeat-cycle",
|
||||
title: "Loop Study"
|
||||
)
|
||||
let viewModel = makeViewModel(
|
||||
remoteTracks: [track],
|
||||
downloadStates: [makeDownloadedState(for: track)],
|
||||
audioFiles: [localFilePath(for: track): Data([0x1])]
|
||||
)
|
||||
|
||||
await viewModel.loadIfNeeded()
|
||||
viewModel.togglePlayback(trackID: track.trackId)
|
||||
|
||||
XCTAssertEqual(viewModel.repeatMode, .off)
|
||||
|
||||
viewModel.cycleRepeatMode()
|
||||
XCTAssertEqual(viewModel.repeatMode, .one)
|
||||
|
||||
viewModel.cycleRepeatMode()
|
||||
XCTAssertEqual(viewModel.repeatMode, .all)
|
||||
|
||||
viewModel.cycleRepeatMode()
|
||||
XCTAssertEqual(viewModel.repeatMode, .off)
|
||||
}
|
||||
|
||||
func testNextTrackRespectsRepeatOne() async throws {
|
||||
let firstTrack = makeRemoteTrack(
|
||||
trackId: "remote-repeat-one-first",
|
||||
assetId: "asset-repeat-one-first",
|
||||
title: "Single Orbit"
|
||||
)
|
||||
let secondTrack = makeRemoteTrack(
|
||||
trackId: "remote-repeat-one-second",
|
||||
assetId: "asset-repeat-one-second",
|
||||
title: "Outer Ring"
|
||||
)
|
||||
let player = TestPlayer()
|
||||
let viewModel = makeViewModel(
|
||||
remoteTracks: [firstTrack, secondTrack],
|
||||
downloadStates: [
|
||||
makeDownloadedState(for: firstTrack),
|
||||
makeDownloadedState(for: secondTrack),
|
||||
],
|
||||
audioFiles: [
|
||||
localFilePath(for: firstTrack): Data([0x1]),
|
||||
localFilePath(for: secondTrack): Data([0x2]),
|
||||
],
|
||||
player: player
|
||||
)
|
||||
|
||||
await viewModel.loadIfNeeded()
|
||||
viewModel.togglePlayback(trackID: firstTrack.trackId)
|
||||
viewModel.seekPlayback(to: 37)
|
||||
viewModel.cycleRepeatMode()
|
||||
viewModel.nextTrack()
|
||||
|
||||
XCTAssertEqual(viewModel.repeatMode, .one)
|
||||
XCTAssertEqual(viewModel.nowPlaying.trackID, firstTrack.trackId)
|
||||
XCTAssertEqual(viewModel.nowPlaying.title, firstTrack.title)
|
||||
XCTAssertEqual(viewModel.nowPlaying.currentTime, 0)
|
||||
XCTAssertEqual(viewModel.nowPlaying.playbackState, .playing)
|
||||
}
|
||||
|
||||
func testQueueControlsPreserveFavoriteState() async throws {
|
||||
let firstTrack = makeRemoteTrack(
|
||||
trackId: "remote-favorite-queue-first",
|
||||
assetId: "asset-favorite-queue-first",
|
||||
title: "Anchor Point"
|
||||
)
|
||||
let secondTrack = makeRemoteTrack(
|
||||
trackId: "remote-favorite-queue-second",
|
||||
assetId: "asset-favorite-queue-second",
|
||||
title: "Signal Path"
|
||||
)
|
||||
let thirdTrack = makeRemoteTrack(
|
||||
trackId: "remote-favorite-queue-third",
|
||||
assetId: "asset-favorite-queue-third",
|
||||
title: "Safe Harbor"
|
||||
)
|
||||
let favoriteStore = InMemoryFavoriteTrackStore(
|
||||
tracks: [
|
||||
FavoriteTrackRecord(
|
||||
remoteTrackId: firstTrack.trackId,
|
||||
favoritedAt: Date(timeIntervalSince1970: 1_000)
|
||||
),
|
||||
FavoriteTrackRecord(
|
||||
remoteTrackId: thirdTrack.trackId,
|
||||
favoritedAt: Date(timeIntervalSince1970: 2_000)
|
||||
),
|
||||
]
|
||||
)
|
||||
let viewModel = makeViewModel(
|
||||
remoteTracks: [firstTrack, secondTrack, thirdTrack],
|
||||
downloadStates: [
|
||||
makeDownloadedState(for: firstTrack),
|
||||
makeDownloadedState(for: secondTrack),
|
||||
makeDownloadedState(for: thirdTrack),
|
||||
],
|
||||
favoriteTrackStore: favoriteStore,
|
||||
audioFiles: [
|
||||
localFilePath(for: firstTrack): Data([0x1]),
|
||||
localFilePath(for: secondTrack): Data([0x2]),
|
||||
localFilePath(for: thirdTrack): Data([0x3]),
|
||||
]
|
||||
)
|
||||
|
||||
await viewModel.loadIfNeeded()
|
||||
viewModel.togglePlayback(trackID: secondTrack.trackId)
|
||||
viewModel.toggleShuffle()
|
||||
viewModel.nextTrack()
|
||||
viewModel.previousTrack()
|
||||
|
||||
XCTAssertTrue(try XCTUnwrap(remoteRow(in: viewModel, trackID: firstTrack.trackId)).isFavorite)
|
||||
XCTAssertFalse(try XCTUnwrap(remoteRow(in: viewModel, trackID: secondTrack.trackId)).isFavorite)
|
||||
XCTAssertTrue(try XCTUnwrap(remoteRow(in: viewModel, trackID: thirdTrack.trackId)).isFavorite)
|
||||
XCTAssertTrue(try XCTUnwrap(offlineRow(in: viewModel, trackID: firstTrack.trackId)).isFavorite)
|
||||
XCTAssertFalse(try XCTUnwrap(offlineRow(in: viewModel, trackID: secondTrack.trackId)).isFavorite)
|
||||
XCTAssertTrue(try XCTUnwrap(offlineRow(in: viewModel, trackID: thirdTrack.trackId)).isFavorite)
|
||||
}
|
||||
|
||||
func testQueueControlsPreserveOfflineAndDownloadedState() async throws {
|
||||
let firstTrack = makeRemoteTrack(
|
||||
trackId: "remote-offline-first",
|
||||
assetId: "asset-offline-first",
|
||||
title: "Stored Echo"
|
||||
)
|
||||
let secondTrack = makeRemoteTrack(
|
||||
trackId: "remote-offline-second",
|
||||
assetId: "asset-offline-second",
|
||||
title: "Airplane Mode"
|
||||
)
|
||||
let viewModel = makeViewModel(
|
||||
remoteTracks: [firstTrack, secondTrack],
|
||||
downloadStates: [
|
||||
makeDownloadedState(for: firstTrack),
|
||||
makeDownloadedState(for: secondTrack),
|
||||
],
|
||||
audioFiles: [
|
||||
localFilePath(for: firstTrack): Data([0x1, 0x2]),
|
||||
localFilePath(for: secondTrack): Data([0x3, 0x4]),
|
||||
]
|
||||
)
|
||||
|
||||
await viewModel.loadIfNeeded()
|
||||
viewModel.togglePlayback(trackID: firstTrack.trackId)
|
||||
viewModel.toggleShuffle()
|
||||
viewModel.nextTrack()
|
||||
viewModel.previousTrack()
|
||||
|
||||
XCTAssertEqual(try XCTUnwrap(remoteRow(in: viewModel, trackID: firstTrack.trackId)).status, .downloaded)
|
||||
XCTAssertEqual(try XCTUnwrap(remoteRow(in: viewModel, trackID: secondTrack.trackId)).status, .downloaded)
|
||||
XCTAssertEqual(try XCTUnwrap(offlineRow(in: viewModel, trackID: firstTrack.trackId)).statusBadgeTitle, "Downloaded")
|
||||
XCTAssertEqual(try XCTUnwrap(offlineRow(in: viewModel, trackID: secondTrack.trackId)).statusBadgeTitle, "Downloaded")
|
||||
XCTAssertEqual(viewModel.availableOfflineTracks.map(\.id), [firstTrack.trackId, secondTrack.trackId])
|
||||
}
|
||||
|
||||
func testRelaunchRestoresMetadataShuffleAndRepeatWithoutAutoplay() {
|
||||
let suiteName = "de.diyaa.velody.tests.\(UUID().uuidString)"
|
||||
let defaults = UserDefaults(suiteName: suiteName)!
|
||||
defer {
|
||||
@@ -228,46 +638,64 @@ final class iPhoneLibraryViewModelPlaybackTests: XCTestCase {
|
||||
userDefaults: defaults,
|
||||
storageKey: "playback"
|
||||
)
|
||||
let track = LibraryTrack(
|
||||
id: "remote-restore",
|
||||
let firstTrack = LibraryTrack(
|
||||
id: "remote-restore-first",
|
||||
title: "Restore Point",
|
||||
artist: "Velody Artist",
|
||||
durationSeconds: 245,
|
||||
localFilePath: "/in-memory/asset-restore.mp3",
|
||||
remoteTrackId: "remote-restore"
|
||||
localFilePath: "/in-memory/asset-restore-first.mp3",
|
||||
remoteTrackId: "remote-restore-first"
|
||||
)
|
||||
let secondTrack = LibraryTrack(
|
||||
id: "remote-restore-second",
|
||||
title: "Resume Thread",
|
||||
artist: "Velody Artist",
|
||||
durationSeconds: 188,
|
||||
localFilePath: "/in-memory/asset-restore-second.mp3",
|
||||
remoteTrackId: "remote-restore-second"
|
||||
)
|
||||
|
||||
let firstEngine = FakePlaybackEngine()
|
||||
firstEngine.durationByPath[track.localFilePath] = 245
|
||||
firstEngine.durationByPath[firstTrack.localFilePath] = 245
|
||||
firstEngine.durationByPath[secondTrack.localFilePath] = 188
|
||||
let firstPlayer = iPhonePlaybackControllerPlayer(
|
||||
controller: PlaybackController(
|
||||
engine: firstEngine,
|
||||
sessionStore: sessionStore
|
||||
)
|
||||
)
|
||||
firstPlayer.setCatalogTracks([track])
|
||||
firstPlayer.play(trackID: track.id)
|
||||
firstPlayer.setCatalogTracks([firstTrack, secondTrack])
|
||||
firstPlayer.play(trackID: secondTrack.id)
|
||||
firstPlayer.toggleShuffle()
|
||||
firstPlayer.cycleRepeatMode()
|
||||
firstPlayer.cycleRepeatMode()
|
||||
firstEngine.currentTime = 48
|
||||
firstPlayer.pause()
|
||||
|
||||
let storedSession = sessionStore.loadSession()
|
||||
XCTAssertEqual(storedSession?.currentTrackID, track.id)
|
||||
XCTAssertEqual(storedSession?.currentTrackID, secondTrack.id)
|
||||
XCTAssertEqual(storedSession?.currentTime, 48)
|
||||
XCTAssertEqual(storedSession?.queueTrackIDs, [])
|
||||
XCTAssertEqual(storedSession?.queueTrackIDs, [secondTrack.id, firstTrack.id])
|
||||
XCTAssertEqual(storedSession?.isShuffleEnabled, true)
|
||||
XCTAssertEqual(storedSession?.repeatMode, .all)
|
||||
|
||||
let secondEngine = FakePlaybackEngine()
|
||||
secondEngine.durationByPath[track.localFilePath] = 245
|
||||
secondEngine.durationByPath[firstTrack.localFilePath] = 245
|
||||
secondEngine.durationByPath[secondTrack.localFilePath] = 188
|
||||
let relaunchedPlayer = iPhonePlaybackControllerPlayer(
|
||||
controller: PlaybackController(
|
||||
engine: secondEngine,
|
||||
sessionStore: sessionStore
|
||||
)
|
||||
)
|
||||
relaunchedPlayer.setCatalogTracks([track])
|
||||
relaunchedPlayer.setCatalogTracks([firstTrack, secondTrack])
|
||||
|
||||
XCTAssertEqual(relaunchedPlayer.state.trackID, track.id)
|
||||
XCTAssertEqual(relaunchedPlayer.state.title, track.title)
|
||||
XCTAssertEqual(relaunchedPlayer.state.artist, track.artist)
|
||||
XCTAssertEqual(relaunchedPlayer.state.trackID, secondTrack.id)
|
||||
XCTAssertEqual(relaunchedPlayer.state.title, secondTrack.title)
|
||||
XCTAssertEqual(relaunchedPlayer.state.artist, secondTrack.artist)
|
||||
XCTAssertEqual(relaunchedPlayer.state.queueTrackIDs, [secondTrack.id, firstTrack.id])
|
||||
XCTAssertTrue(relaunchedPlayer.state.isShuffleEnabled)
|
||||
XCTAssertEqual(relaunchedPlayer.state.repeatMode, .all)
|
||||
XCTAssertEqual(relaunchedPlayer.state.currentTime, 48)
|
||||
XCTAssertEqual(relaunchedPlayer.state.playbackState, .paused)
|
||||
XCTAssertFalse(relaunchedPlayer.state.isPlaying)
|
||||
|
||||
Reference in New Issue
Block a user