diff --git a/src/app/common/metadata/file-metadata-factory.ts b/src/app/common/metadata/file-metadata-factory.ts
index 431b126e0..41d65afe3 100644
--- a/src/app/common/metadata/file-metadata-factory.ts
+++ b/src/app/common/metadata/file-metadata-factory.ts
@@ -4,7 +4,6 @@ import { BaseFileAccess } from '../io/base-file-access';
import { BaseFileMetadataFactory } from './base-file-metadata-factory';
import { IFileMetadata } from './i-file-metadata';
import { MusicMetadataFileMetadata } from './music-metadata-file-meta-data';
-import { TagLibFileMetadata } from './tag-lib-file-metadata';
@Injectable()
export class FileMetadataFactory implements BaseFileMetadataFactory {
@@ -17,13 +16,13 @@ export class FileMetadataFactory implements BaseFileMetadataFactory {
switch (fileExtension) {
case FileFormats.mp3:
- fileMetadata = new TagLibFileMetadata(path);
+ fileMetadata = new MusicMetadataFileMetadata(path);
break;
case FileFormats.flac:
- fileMetadata = new TagLibFileMetadata(path);
+ fileMetadata = new MusicMetadataFileMetadata(path);
break;
case FileFormats.ogg:
- fileMetadata = new TagLibFileMetadata(path);
+ fileMetadata = new MusicMetadataFileMetadata(path);
break;
case FileFormats.m4a:
fileMetadata = new MusicMetadataFileMetadata(path);
@@ -32,10 +31,10 @@ export class FileMetadataFactory implements BaseFileMetadataFactory {
fileMetadata = new MusicMetadataFileMetadata(path);
break;
case FileFormats.wav:
- fileMetadata = new TagLibFileMetadata(path);
+ fileMetadata = new MusicMetadataFileMetadata(path);
break;
default:
- fileMetadata = new TagLibFileMetadata(path);
+ fileMetadata = new MusicMetadataFileMetadata(path);
break;
}
diff --git a/src/app/components/collection/collection-tracks/collection-tracks-table/collection-tracks-table.component.html b/src/app/components/collection/collection-tracks/collection-tracks-table/collection-tracks-table.component.html
index ca62f7672..045b980c8 100644
--- a/src/app/components/collection/collection-tracks/collection-tracks-table/collection-tracks-table.component.html
+++ b/src/app/components/collection/collection-tracks/collection-tracks-table/collection-tracks-table.component.html
@@ -111,7 +111,7 @@
class="tracks-table-row"
[ngClass]="{ 'selected-item-background-important': track.isSelected }"
(mousedown)="setSelectedTracks($event, track)"
- (dblclick)="playbackService.enqueueAndPlayTracksFromDoubleClick(this.orderedTracks, track)"
+ (dblclick)="playbackService.enqueueAndPlayTracksStartingFromGivenTrack(this.orderedTracks, track)"
(contextmenu)="onTrackContextMenuAsync($event, track)"
>
diff --git a/src/app/components/collection/track-browser/track-browser.component.html b/src/app/components/collection/track-browser/track-browser.component.html
index c7ee5408d..04db7acd9 100644
--- a/src/app/components/collection/track-browser/track-browser.component.html
+++ b/src/app/components/collection/track-browser/track-browser.component.html
@@ -22,7 +22,7 @@
diff --git a/src/app/services/file/file.service.spec.ts b/src/app/services/file/file.service.spec.ts
index 8ea3c4613..1174dfc00 100644
--- a/src/app/services/file/file.service.spec.ts
+++ b/src/app/services/file/file.service.spec.ts
@@ -78,7 +78,7 @@ describe('FileService', () => {
expect(service).toBeDefined();
});
- it('should enqueue all playable tracks found as parameters and play the first track when arguments are received', async () => {
+ it('should enqueue all playable tracks that are found as parameters', async () => {
// Arrange
const service: BaseFileService = createService();
@@ -93,12 +93,13 @@ describe('FileService', () => {
It.is(
(trackModels: TrackModel[]) =>
trackModels.length === 2 && trackModels[0].path === 'file 1.mp3' && trackModels[1].path === 'file 2.ogg'
- )),
+ )
+ ),
Times.once()
);
});
- it('should not enqueue and play anything if parameters are undefined when arguments are received', async () => {
+ it('should not enqueue anything if parameters are undefined when arguments are received', async () => {
// Arrange
const service: BaseFileService = createService();
@@ -110,7 +111,7 @@ describe('FileService', () => {
playbackServiceMock.verify((x) => x.enqueueAndPlayTracks(It.isAny()), Times.never());
});
- it('should not enqueue and play anything if parameters are empty when arguments are received', async () => {
+ it('should not enqueue anything if parameters are empty when arguments are received', async () => {
// Arrange
const service: BaseFileService = createService();
@@ -122,7 +123,7 @@ describe('FileService', () => {
playbackServiceMock.verify((x) => x.enqueueAndPlayTracks(It.isAny()), Times.never());
});
- it('should not enqueue and play anything if there are no playable tracks found as parameters when arguments are received', async () => {
+ it('should not enqueue anything if there are no playable tracks found as parameters when arguments are received', async () => {
// Arrange
const service: BaseFileService = createService();
@@ -163,7 +164,7 @@ describe('FileService', () => {
});
describe('enqueueParameterFilesAsync', () => {
- it('should enqueue all playable tracks found as parameters and play the first track', async () => {
+ it('should enqueue all playable tracks found as parameters', async () => {
// Arrange
applicationMock.setup((x) => x.getParameters()).returns(() => ['file 1.mp3', 'file 2.ogg', 'file 3.bmp']);
const service: BaseFileService = createService();
@@ -184,7 +185,7 @@ describe('FileService', () => {
);
});
- it('should not enqueue and play anything if parameters are undefined', async () => {
+ it('should not enqueue anything if parameters are undefined', async () => {
// Arrange
applicationMock.setup((x) => x.getParameters()).returns(() => undefined);
const service: BaseFileService = createService();
@@ -196,7 +197,7 @@ describe('FileService', () => {
playbackServiceMock.verify((x) => x.enqueueAndPlayTracks(It.isAny()), Times.never());
});
- it('should not enqueue and play anything if parameters are empty', async () => {
+ it('should not enqueue anything if parameters are empty', async () => {
// Arrange
applicationMock.setup((x) => x.getParameters()).returns(() => []);
const service: BaseFileService = createService();
@@ -208,7 +209,7 @@ describe('FileService', () => {
playbackServiceMock.verify((x) => x.enqueueAndPlayTracks(It.isAny()), Times.never());
});
- it('should not enqueue and play anything if there are no playable tracks found as parameters', async () => {
+ it('should not enqueue anything if there are no playable tracks found as parameters', async () => {
// Arrange
applicationMock.setup((x) => x.getParameters()).returns(() => ['file 1.png', 'file 2.mkv', 'file 3.bmp']);
const service: BaseFileService = createService();
diff --git a/src/app/services/playback/base-playback.service.ts b/src/app/services/playback/base-playback.service.ts
index 4effc1bff..37a2bff13 100644
--- a/src/app/services/playback/base-playback.service.ts
+++ b/src/app/services/playback/base-playback.service.ts
@@ -30,6 +30,7 @@ export abstract class BasePlaybackService {
public abstract toggleLoopMode(): void;
public abstract toggleIsShuffled(): void;
public abstract enqueueAndPlayTracks(tracksToEnqueue: TrackModel[]): void;
+ public abstract enqueueAndPlayTracksStartingFromGivenTrack(tracksToEnqueue: TrackModel[], trackToPlay: TrackModel): void;
public abstract enqueueAndPlayArtist(artistToPlay: ArtistModel, artistType: ArtistType): void;
public abstract enqueueAndPlayGenre(genreToPlay: GenreModel): void;
public abstract enqueueAndPlayAlbum(albumToPlay: AlbumModel): void;
diff --git a/src/app/services/playback/playback.service.spec.ts b/src/app/services/playback/playback.service.spec.ts
index c2975430a..cf9fd2c8d 100644
--- a/src/app/services/playback/playback.service.spec.ts
+++ b/src/app/services/playback/playback.service.spec.ts
@@ -7,7 +7,6 @@ import { FileAccess } from '../../common/io/file-access';
import { Logger } from '../../common/logger';
import { MathExtensions } from '../../common/math-extensions';
import { TrackOrdering } from '../../common/ordering/track-ordering';
-import { Shuffler } from '../../common/shuffler';
import { AlbumModel } from '../album/album-model';
import { ArtistModel } from '../artist/artist-model';
import { ArtistType } from '../artist/artist-type';
@@ -34,6 +33,7 @@ describe('PlaybackService', () => {
let trackOrderingMock: IMock;
let fileAccessMock: IMock;
let loggerMock: IMock;
+ let queueMock: IMock;
let progressUpdaterMock: IMock;
let mathExtensionsMock: IMock;
let settingsStub: any;
@@ -43,7 +43,6 @@ describe('PlaybackService', () => {
let subscription: Subscription;
let dateTimeMock: IMock;
let translatorServiceMock: IMock;
- let queue: Queue;
const albumData1: AlbumData = new AlbumData();
albumData1.albumKey = 'albumKey1';
@@ -76,8 +75,7 @@ describe('PlaybackService', () => {
trackOrderingMock = Mock.ofType();
fileAccessMock = Mock.ofType();
loggerMock = Mock.ofType();
- // use an unmocked queue and shuffler to make sure it is working correctly, and to check track orders
- queue = new Queue(new Shuffler(), loggerMock.object);
+ queueMock = Mock.ofType();
progressUpdaterMock = Mock.ofType();
mathExtensionsMock = Mock.ofType();
settingsStub = { volume: 0.6 };
@@ -154,7 +152,7 @@ describe('PlaybackService', () => {
snackBarServiceMock.object,
audioPlayerMock.object,
trackOrderingMock.object,
- queue,
+ queueMock.object,
progressUpdaterMock.object,
mathExtensionsMock.object,
settingsStub,
@@ -306,6 +304,7 @@ describe('PlaybackService', () => {
it('should stop playback on playback finished if a next track is not found', () => {
// Arrange
+ queueMock.setup((x) => x.getNextTrack(It.isAny(), false)).returns(() => undefined);
// Act
playbackFinished.next();
@@ -323,7 +322,7 @@ describe('PlaybackService', () => {
it('should raise an event that playback is stopped on playback finished if a next track is not found', () => {
// Arrange
- service.enqueueAndPlayTracks([trackModel1]);
+ queueMock.setup((x) => x.getNextTrack(It.isAny(), false)).returns(() => undefined);
let playbackIsStopped: boolean = false;
subscription.add(
@@ -341,6 +340,9 @@ describe('PlaybackService', () => {
it('should set the current track to undefined before raising a playback finished event', () => {
// Arrange
+ queueMock.setup((x) => x.getNextTrack(It.isAny(), false)).returns(() => undefined);
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
+
service.enqueueAndPlayTracks(trackModels);
let currentTrack: TrackModel;
@@ -359,7 +361,11 @@ describe('PlaybackService', () => {
it('should play the next track on playback finished if a next track is found', () => {
// Arrange
+ queueMock.setup((x) => x.getNextTrack(It.isObjectWith({ path: 'Path 1' }), false)).returns(() => trackModel2);
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
+
service.enqueueAndPlayTracks(trackModels);
+
audioPlayerMock.reset();
progressUpdaterMock.reset();
@@ -381,6 +387,8 @@ describe('PlaybackService', () => {
service.toggleLoopMode();
}
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
+
service.enqueueAndPlayTracks(trackModels);
audioPlayerMock.reset();
@@ -397,7 +405,11 @@ describe('PlaybackService', () => {
service.toggleLoopMode();
}
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
+ queueMock.setup((x) => x.getNextTrack(It.isObjectWith({ path: 'Path 1' }), false)).returns(() => trackModel2);
+
service.enqueueAndPlayTracks(trackModels);
+
audioPlayerMock.reset();
// Act
@@ -413,7 +425,11 @@ describe('PlaybackService', () => {
service.toggleLoopMode();
}
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
+ queueMock.setup((x) => x.getNextTrack(It.isObjectWith({ path: 'Path 1' }), true)).returns(() => trackModel2);
+
service.enqueueAndPlayTracks(trackModels);
+
audioPlayerMock.reset();
// Act
@@ -429,7 +445,11 @@ describe('PlaybackService', () => {
service.toggleLoopMode();
}
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
+ queueMock.setup((x) => x.getNextTrack(It.isObjectWith({ path: 'Path 1' }), false)).returns(() => trackModel2);
+
service.enqueueAndPlayTracks(trackModels);
+
audioPlayerMock.reset();
// Act
@@ -441,40 +461,31 @@ describe('PlaybackService', () => {
it('should get the next track without wrap around on playback finished if loopMode is None', () => {
// Arrange
- const getNextTrackSpy = jest.spyOn(queue, 'getNextTrack');
- service.enqueueAndPlayTracks([trackModel1, trackModel2]);
// Act
playbackFinished.next();
// Assert
- expect(getNextTrackSpy).toHaveBeenCalledTimes(1);
- expect(getNextTrackSpy).toHaveBeenCalledWith(trackModel1, false);
- expect(service.currentTrack).toBe(trackModel2);
+ queueMock.verify((x) => x.getNextTrack(It.isAny(), false), Times.exactly(1));
});
it('should get the next track with wrap around on playback finished if loopMode is All', () => {
// Arrange
- const getNextTrackSpy = jest.spyOn(queue, 'getNextTrack');
while (service.loopMode !== LoopMode.All) {
service.toggleLoopMode();
}
- // set a queue of 2 tracks and start playing from second track
- service.enqueueAndPlayTracksFromDoubleClick([trackModel1, trackModel2], trackModel2);
// Act
playbackFinished.next();
// Assert
- // should loop back to the first track
- expect(getNextTrackSpy).toHaveBeenCalledTimes(1);
- expect(getNextTrackSpy).toHaveBeenCalledWith(trackModel2, true);
- expect(service.currentTrack).toBe(trackModel1);
+ queueMock.verify((x) => x.getNextTrack(It.isAny(), true), Times.exactly(1));
});
it('should increase play count and date last played for the current track on playback finished', () => {
// Arrange
const trackModelMock: IMock = Mock.ofType();
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModelMock.object);
service.enqueueAndPlayTracks([trackModelMock.object]);
// Act
@@ -487,6 +498,7 @@ describe('PlaybackService', () => {
it('should save play count and date last played for the current track on playback finished', () => {
// Arrange
const trackModelMock: IMock = Mock.ofType();
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModelMock.object);
service.enqueueAndPlayTracks([trackModelMock.object]);
// Act
@@ -498,7 +510,9 @@ describe('PlaybackService', () => {
it('should raise an event, on playback finished, that playback has started, containing the current track and if a next track is being played.', () => {
// Arrange
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
service.enqueueAndPlayTracks(trackModels);
+ queueMock.setup((x) => x.getNextTrack(It.isObjectWith({ path: 'Path 1' }), false)).returns(() => trackModel2);
let receivedTrack: TrackModel;
let isPlayingPreviousTrack: boolean;
@@ -600,23 +614,12 @@ describe('PlaybackService', () => {
while (service.isShuffled !== false) {
service.toggleIsShuffled();
}
- service.enqueueAndPlayTracks(trackModels);
- const shuffleSpy = jest.spyOn(queue, 'shuffle');
// Act
service.toggleIsShuffled();
// Assert
- expect(shuffleSpy).toHaveBeenCalledTimes(1);
- let playbackQueueString: string = '';
- let trackModelString: string = '';
- for (const track of service.playbackQueue.tracks) {
- playbackQueueString = playbackQueueString + track.sortableTitle;
- }
- for (const track of trackModels) {
- trackModelString = trackModelString + track.sortableTitle;
- }
- expect(playbackQueueString).not.toBe(trackModelString);
+ queueMock.verify((x) => x.shuffle(), Times.exactly(1));
});
it('should not unshuffle the queue when shuffle is disabled', () => {
@@ -624,23 +627,12 @@ describe('PlaybackService', () => {
while (service.isShuffled !== false) {
service.toggleIsShuffled();
}
- service.enqueueAndPlayTracks(trackModels);
- const unShuffleSpy = jest.spyOn(queue, 'unShuffle');
// Act
service.toggleIsShuffled();
// Assert
- expect(unShuffleSpy).not.toHaveBeenCalled();
- let playbackQueueString: string = '';
- let trackModelString: string = '';
- for (const track of service.playbackQueue.tracks) {
- playbackQueueString = playbackQueueString + track.sortableTitle;
- }
- for (const track of trackModels) {
- trackModelString = trackModelString + track.sortableTitle;
- }
- expect(playbackQueueString).not.toBe(trackModelString);
+ queueMock.verify((x) => x.unShuffle(), Times.never());
});
it('should disable shuffle when shuffle is enabled', () => {
@@ -658,7 +650,6 @@ describe('PlaybackService', () => {
it('should have shuffled the queue when shuffle is enabled', () => {
// Arrange
- const shuffleSpy = jest.spyOn(queue, 'shuffle');
while (service.isShuffled !== true) {
service.toggleIsShuffled();
}
@@ -667,61 +658,49 @@ describe('PlaybackService', () => {
service.toggleIsShuffled();
// Assert
- expect(shuffleSpy).toHaveBeenCalledTimes(1);
+ queueMock.verify((x) => x.shuffle(), Times.exactly(1));
});
it('should unshuffle the queue when shuffle is enabled', () => {
// Arrange
- const unShuffleSpy = jest.spyOn(queue, 'unShuffle');
while (service.isShuffled !== true) {
service.toggleIsShuffled();
}
- service.enqueueAndPlayTracks(trackModels);
// Act
service.toggleIsShuffled();
// Assert
- expect(unShuffleSpy).toHaveBeenCalledTimes(1);
- let playbackQueueString: string = '';
- let trackModelString: string = '';
- for (const track of service.playbackQueue.tracks) {
- playbackQueueString = playbackQueueString + track.sortableTitle;
- }
- for (const track of trackModels) {
- trackModelString = trackModelString + track.sortableTitle;
- }
- expect(playbackQueueString).toBe(trackModelString);
+ queueMock.verify((x) => x.unShuffle(), Times.exactly(1));
});
});
describe('enqueueAndPlayTracks', () => {
it('should not add tracks to the queue if tracks is undefined', () => {
// Arrange
- const setTracksSpy = jest.spyOn(queue, 'setTracks');
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
// Act
service.enqueueAndPlayTracks(undefined);
// Assert
- expect(setTracksSpy).not.toHaveBeenCalled();
- expect(service.playbackQueue.tracks.length).toBe(0);
+ queueMock.verify((x) => x.setTracks(It.isAny(), It.isAny()), Times.never());
});
it('should not add tracks to the queue if tracks is empty', () => {
// Arrange
- const setTracksSpy = jest.spyOn(queue, 'setTracks');
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
// Act
service.enqueueAndPlayTracks(undefined);
// Assert
- expect(setTracksSpy).not.toHaveBeenCalled();
- expect(service.playbackQueue.tracks.length).toBe(0);
+ queueMock.verify((x) => x.setTracks(It.isAny(), It.isAny()), Times.never());
});
it('should not start playback if tracks is undefined', () => {
// Arrange
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
// Act
service.enqueueAndPlayTracks(undefined);
@@ -736,6 +715,7 @@ describe('PlaybackService', () => {
it('should not start playback if tracks is empty', () => {
// Arrange
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
// Act
service.enqueueAndPlayTracks(undefined);
@@ -750,45 +730,25 @@ describe('PlaybackService', () => {
it('should add tracks to the queue unshuffled if shuffle is disabled', () => {
// Arrange
- const setTracksSpy = jest.spyOn(queue, 'setTracks');
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
// Act
service.enqueueAndPlayTracks(trackModels);
// Assert
- expect(setTracksSpy).toHaveBeenCalledTimes(1);
- expect(setTracksSpy).toHaveBeenCalledWith(trackModels, false);
- let playbackQueueString: string = '';
- let trackModelString: string = '';
- for (const track of service.playbackQueue.tracks) {
- playbackQueueString = playbackQueueString + track.sortableTitle;
- }
- for (const track of trackModels) {
- trackModelString = trackModelString + track.sortableTitle;
- }
- expect(playbackQueueString).toBe(trackModelString);
+ queueMock.verify((x) => x.setTracks(trackModels, false), Times.exactly(1));
});
it('should add tracks to the queue shuffled if shuffle is enabled', () => {
// Arrange
- const setTracksSpy = jest.spyOn(queue, 'setTracks');
service.toggleIsShuffled();
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
// Act
service.enqueueAndPlayTracks(trackModels);
// Assert
- expect(setTracksSpy).toHaveBeenCalledTimes(1);
- expect(setTracksSpy).toHaveBeenCalledWith(trackModels, true);
- let playbackQueueString: string = '';
- let trackModelString: string = '';
- for (const track of service.playbackQueue.tracks) {
- playbackQueueString = playbackQueueString + track.sortableTitle;
- }
- for (const track of trackModels) {
- trackModelString = trackModelString + track.sortableTitle;
- }
- expect(playbackQueueString).not.toBe(trackModelString);
+ queueMock.verify((x) => x.setTracks(trackModels, true), Times.exactly(1));
});
it('should start playback', () => {
@@ -796,6 +756,7 @@ describe('PlaybackService', () => {
audioPlayerMock.reset();
audioPlayerMock.setup((x) => x.stop()).verifiable(Times.once(), ExpectedCallType.InSequence);
audioPlayerMock.setup((x) => x.play(trackModel1.path)).verifiable(Times.once(), ExpectedCallType.InSequence);
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
// Act
service.enqueueAndPlayTracks(trackModels);
@@ -820,6 +781,8 @@ describe('PlaybackService', () => {
})
);
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
+
// Act
service.enqueueAndPlayTracks(trackModels);
@@ -829,6 +792,114 @@ describe('PlaybackService', () => {
});
});
+ describe('enqueueAndPlayTracksStartingFromGivenTrack', () => {
+ it('should not add tracks to the queue if tracks is undefined', () => {
+ // Arrange
+
+ // Act
+ service.enqueueAndPlayTracksStartingFromGivenTrack(undefined, trackModel1);
+
+ // Assert
+ queueMock.verify((x) => x.setTracks(It.isAny(), It.isAny()), Times.never());
+ });
+
+ it('should not add tracks to the queue if tracks is empty', () => {
+ // Arrange
+
+ // Act
+ service.enqueueAndPlayTracksStartingFromGivenTrack(undefined, trackModel1);
+
+ // Assert
+ queueMock.verify((x) => x.setTracks(It.isAny(), It.isAny()), Times.never());
+ });
+
+ it('should not start playback if tracks is undefined', () => {
+ // Arrange
+
+ // Act
+ service.enqueueAndPlayTracksStartingFromGivenTrack(undefined, trackModel1);
+
+ // Assert
+ audioPlayerMock.verify((x) => x.play(It.isAny()), Times.never());
+ progressUpdaterMock.verify((x) => x.startUpdatingProgress(), Times.never());
+ expect(service.isPlaying).toEqual(false);
+ expect(service.canPause).toEqual(false);
+ expect(service.canResume).toEqual(true);
+ });
+
+ it('should not start playback if tracks is empty', () => {
+ // Arrange
+
+ // Act
+ service.enqueueAndPlayTracksStartingFromGivenTrack(undefined, trackModel1);
+
+ // Assert
+ audioPlayerMock.verify((x) => x.play(It.isAny()), Times.never());
+ progressUpdaterMock.verify((x) => x.startUpdatingProgress(), Times.never());
+ expect(service.isPlaying).toEqual(false);
+ expect(service.canPause).toEqual(false);
+ expect(service.canResume).toEqual(true);
+ });
+
+ it('should add tracks to the queue unshuffled if shuffle is disabled', () => {
+ // Arrange
+
+ // Act
+ service.enqueueAndPlayTracksStartingFromGivenTrack(trackModels, trackModel1);
+
+ // Assert
+ queueMock.verify((x) => x.setTracks(trackModels, false), Times.exactly(1));
+ });
+
+ it('should add tracks to the queue shuffled if shuffle is enabled', () => {
+ // Arrange
+ service.toggleIsShuffled();
+
+ // Act
+ service.enqueueAndPlayTracksStartingFromGivenTrack(trackModels, trackModel1);
+
+ // Assert
+ queueMock.verify((x) => x.setTracks(trackModels, true), Times.exactly(1));
+ });
+
+ it('should start playback', () => {
+ // Arrange
+ audioPlayerMock.reset();
+ audioPlayerMock.setup((x) => x.stop()).verifiable(Times.once(), ExpectedCallType.InSequence);
+ audioPlayerMock.setup((x) => x.play(trackModel1.path)).verifiable(Times.once(), ExpectedCallType.InSequence);
+
+ // Act
+ service.enqueueAndPlayTracksStartingFromGivenTrack(trackModels, trackModel1);
+
+ // Assert
+ audioPlayerMock.verifyAll();
+ expect(service.currentTrack).toBe(trackModel1);
+ expect(service.canPause).toBeTruthy();
+ expect(service.canResume).toBeFalsy();
+ expect(service.isPlaying).toBeTruthy();
+ progressUpdaterMock.verify((x) => x.startUpdatingProgress(), Times.exactly(1));
+ });
+
+ it('should raise an event that playback has started, containing the current track and if a next track is being played.', () => {
+ // Arrange
+ let receivedTrack: TrackModel;
+ let isPlayingPreviousTrack: boolean;
+ subscription.add(
+ service.playbackStarted$.subscribe((playbackStarted: PlaybackStarted) => {
+ receivedTrack = playbackStarted.currentTrack;
+ isPlayingPreviousTrack = playbackStarted.isPlayingPreviousTrack;
+ })
+ );
+
+ // Act
+ service.enqueueAndPlayTracksStartingFromGivenTrack(trackModels, trackModel1);
+
+ // Assert
+ expect(receivedTrack).toBe(trackModel1);
+ expect(isPlayingPreviousTrack).toBeFalsy();
+ });
+ });
+
describe('enqueueAndPlayArtist', () => {
it('should not get tracks for the artist if artistToPlay is undefined', () => {
// Arrange
@@ -854,6 +925,7 @@ describe('PlaybackService', () => {
it('should get tracks for the artist if artistToPlay and artistType are not undefined', () => {
// Arrange
const artistToPlay: ArtistModel = new ArtistModel('artist1', translatorServiceMock.object);
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
// Act
service.enqueueAndPlayArtist(artistToPlay, ArtistType.trackArtists);
@@ -865,6 +937,7 @@ describe('PlaybackService', () => {
it('should order tracks for the artist byAlbum', () => {
// Arrange
const artistToPlay: ArtistModel = new ArtistModel('artist1', translatorServiceMock.object);
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
// Act
service.enqueueAndPlayArtist(artistToPlay, ArtistType.trackArtists);
@@ -875,42 +948,14 @@ describe('PlaybackService', () => {
it('should add tracks to the queue ordered by album', () => {
// Arrange
- const setTracksSpy = jest.spyOn(queue, 'setTracks');
- const artistToPlay: ArtistModel = new ArtistModel('artist1', translatorServiceMock.object);
-
- // Act
- service.enqueueAndPlayArtist(artistToPlay, ArtistType.trackArtists);
-
- // Assert
- expect(setTracksSpy).toHaveBeenCalledTimes(1);
- expect(setTracksSpy).toHaveBeenCalledWith(orderedTrackModels, false);
- service.playbackQueue.tracks.forEach((track, index) => {
- expect(track).toBe(orderedTrackModels[index]);
- });
- });
-
- it('should start playback with a random track if shuffle is on', () => {
- // Arrange
- while (service.isShuffled !== true) {
- service.toggleIsShuffled();
- }
const artistToPlay: ArtistModel = new ArtistModel('artist1', translatorServiceMock.object);
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
// Act
service.enqueueAndPlayArtist(artistToPlay, ArtistType.trackArtists);
- // randomness means the test could fail (service could "randomly" assign first track to first position)
- // so we run it a few times
- for (var i = 0; i < 10; i++) {
- if (service.playbackQueue[0] === tracks.tracks[0]) {
- service.enqueueAndPlayArtist(artistToPlay, ArtistType.trackArtists);
- } else {
- break;
- }
- }
-
// Assert
- expect(service.playbackQueue[0]).not.toBe(tracks.tracks[0]);
+ queueMock.verify((x) => x.setTracks(orderedTrackModels, It.isAny()), Times.exactly(1));
});
it('should start playback', () => {
@@ -919,6 +964,7 @@ describe('PlaybackService', () => {
audioPlayerMock.reset();
audioPlayerMock.setup((x) => x.stop()).verifiable(Times.once(), ExpectedCallType.InSequence);
audioPlayerMock.setup((x) => x.play(trackModel2.path)).verifiable(Times.once(), ExpectedCallType.InSequence);
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel2);
// Act
service.enqueueAndPlayArtist(artistToPlay, ArtistType.trackArtists);
@@ -944,6 +990,8 @@ describe('PlaybackService', () => {
})
);
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel2);
+
// Act
service.enqueueAndPlayArtist(artistToPlay, ArtistType.trackArtists);
@@ -956,6 +1004,7 @@ describe('PlaybackService', () => {
describe('enqueueAndPlayGenre', () => {
it('should not get tracks for the genre if genreToPlay is undefined', () => {
// Arrange
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
// Act
service.enqueueAndPlayGenre(undefined);
@@ -967,6 +1016,7 @@ describe('PlaybackService', () => {
it('should get tracks for the genre if genreToPlay is not undefined', () => {
// Arrange
const genreToPlay: GenreModel = new GenreModel('genre1', translatorServiceMock.object);
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
// Act
service.enqueueAndPlayGenre(genreToPlay);
@@ -978,6 +1028,7 @@ describe('PlaybackService', () => {
it('should order tracks for the artist byAlbum', () => {
// Arrange
const genreToPlay: GenreModel = new GenreModel('genre1', translatorServiceMock.object);
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
// Act
service.enqueueAndPlayGenre(genreToPlay);
@@ -988,18 +1039,14 @@ describe('PlaybackService', () => {
it('should add tracks to the queue ordered by album', () => {
// Arrange
- const setTracksSpy = jest.spyOn(queue, 'setTracks');
const genreToPlay: GenreModel = new GenreModel('genre1', translatorServiceMock.object);
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
// Act
service.enqueueAndPlayGenre(genreToPlay);
// Assert
- expect(setTracksSpy).toHaveBeenCalledTimes(1);
- expect(setTracksSpy).toHaveBeenCalledWith(orderedTrackModels, false);
- service.playbackQueue.tracks.forEach((track, index) => {
- expect(track).toBe(orderedTrackModels[index]);
- });
+ queueMock.verify((x) => x.setTracks(orderedTrackModels, It.isAny()), Times.exactly(1));
});
it('should start playback', () => {
@@ -1009,6 +1056,7 @@ describe('PlaybackService', () => {
audioPlayerMock.reset();
audioPlayerMock.setup((x) => x.stop()).verifiable(Times.once(), ExpectedCallType.InSequence);
audioPlayerMock.setup((x) => x.play(trackModel2.path)).verifiable(Times.once(), ExpectedCallType.InSequence);
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel2);
// Act
service.enqueueAndPlayGenre(genreToPlay);
@@ -1022,30 +1070,6 @@ describe('PlaybackService', () => {
progressUpdaterMock.verify((x) => x.startUpdatingProgress(), Times.exactly(1));
});
- it('should start playback with a random track if shuffle is on', () => {
- // Arrange
- while (service.isShuffled !== true) {
- service.toggleIsShuffled();
- }
- const genreToPlay: GenreModel = new GenreModel('genre1', translatorServiceMock.object);
-
- // Act
- service.enqueueAndPlayGenre(genreToPlay);
-
- // randomness means the test could fail (service could "randomly" assign first track to first position)
- // so we run it a few times
- for (var i = 0; i < 10; i++) {
- if (service.playbackQueue[0] === tracks.tracks[0]) {
- service.enqueueAndPlayGenre(genreToPlay);
- } else {
- break;
- }
- }
-
- // Assert
- expect(service.playbackQueue[0]).not.toBe(tracks.tracks[0]);
- });
-
it('should raise an event that playback has started, containing the current track and if a next track is being played.', () => {
// Arrange
const genreToPlay: GenreModel = new GenreModel('genre1', translatorServiceMock.object);
@@ -1060,6 +1084,8 @@ describe('PlaybackService', () => {
})
);
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel2);
+
service.enqueueAndPlayGenre(genreToPlay);
// Assert
@@ -1071,6 +1097,7 @@ describe('PlaybackService', () => {
describe('enqueueAndPlayAlbum', () => {
it('should not get tracks for the album if albumToPlay is undefined', () => {
// Arrange
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
// Act
service.enqueueAndPlayAlbum(undefined);
@@ -1081,6 +1108,7 @@ describe('PlaybackService', () => {
it('should get tracks for the album if albumToPlay is not undefined', () => {
// Arrange
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
// Act
service.enqueueAndPlayAlbum(album1);
@@ -1091,6 +1119,7 @@ describe('PlaybackService', () => {
it('should order tracks for the album byAlbum', () => {
// Arrange
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
// Act
service.enqueueAndPlayAlbum(album1);
@@ -1101,17 +1130,13 @@ describe('PlaybackService', () => {
it('should add tracks to the queue ordered by album', () => {
// Arrange
- const setTracksSpy = jest.spyOn(queue, 'setTracks');
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
// Act
service.enqueueAndPlayAlbum(album1);
// Assert
- expect(setTracksSpy).toHaveBeenCalledTimes(1);
- expect(setTracksSpy).toHaveBeenCalledWith(orderedTrackModels, false);
- service.playbackQueue.tracks.forEach((track, index) => {
- expect(track).toBe(orderedTrackModels[index]);
- });
+ queueMock.verify((x) => x.setTracks(orderedTrackModels, It.isAny()), Times.exactly(1));
});
it('should start playback', () => {
@@ -1119,6 +1144,7 @@ describe('PlaybackService', () => {
audioPlayerMock.reset();
audioPlayerMock.setup((x) => x.stop()).verifiable(Times.once(), ExpectedCallType.InSequence);
audioPlayerMock.setup((x) => x.play(trackModel2.path)).verifiable(Times.once(), ExpectedCallType.InSequence);
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel2);
// Act
service.enqueueAndPlayAlbum(album1);
@@ -1132,29 +1158,6 @@ describe('PlaybackService', () => {
progressUpdaterMock.verify((x) => x.startUpdatingProgress(), Times.exactly(1));
});
- it('should start playback with a random track if shuffle is on', () => {
- // Arrange
- while (service.isShuffled !== true) {
- service.toggleIsShuffled();
- }
-
- // Act
- service.enqueueAndPlayAlbum(album1);
-
- // randomness means the test could fail (service could "randomly" assign first track to first position)
- // so we run it a few times
- for (var i = 0; i < 10; i++) {
- if (service.playbackQueue[0] === tracks.tracks[0]) {
- service.enqueueAndPlayAlbum(album1);
- } else {
- break;
- }
- }
-
- // Assert
- expect(service.playbackQueue[0]).not.toBe(tracks.tracks[0]);
- });
-
it('should raise an event that playback has started, containing the current track and if a next track is being played.', () => {
// Arrange
let receivedTrack: TrackModel;
@@ -1166,6 +1169,8 @@ describe('PlaybackService', () => {
})
);
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel2);
+
// Act
service.enqueueAndPlayAlbum(album1);
@@ -1182,6 +1187,7 @@ describe('PlaybackService', () => {
describe('pause', () => {
it('should pause playback', () => {
// Arrange
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
service.enqueueAndPlayTracks(trackModels);
// Act
@@ -1197,6 +1203,7 @@ describe('PlaybackService', () => {
it('should raise an event that playback is paused.', () => {
// Arrange
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
service.enqueueAndPlayTracks(trackModels);
let playbackIsPaused: boolean = false;
@@ -1217,6 +1224,7 @@ describe('PlaybackService', () => {
describe('resume', () => {
it('should resume playback if playing', () => {
// Arrange
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
service.enqueueAndPlayTracks(trackModels);
audioPlayerMock.reset();
progressUpdaterMock.reset();
@@ -1248,6 +1256,7 @@ describe('PlaybackService', () => {
it('should raise an event that playback is resumed if playing', () => {
// Arrange
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
service.enqueueAndPlayTracks(trackModels);
audioPlayerMock.reset();
progressUpdaterMock.reset();
@@ -1333,6 +1342,7 @@ describe('PlaybackService', () => {
describe('playPrevious', () => {
it('should play the current track if there is a current track and playback lasted for more than 3 seconds', () => {
// Arrange
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
service.enqueueAndPlayTracks(trackModels);
audioPlayerMock.reset();
audioPlayerMock.setup((x) => x.progressSeconds).returns(() => 3.1);
@@ -1351,51 +1361,29 @@ describe('PlaybackService', () => {
it('should play the previous track if found and playback lasted for less then 3 seconds', () => {
// Arrange
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
service.enqueueAndPlayTracks(trackModels);
- // set up a "previous" track by playing next track, otherwise "play previous" will be undefined
- service.playNext();
-
- // reset audio player and progress updater to make sure playback is less than 3s
+ queueMock.setup((x) => x.getPreviousTrack(trackModel1, false)).returns(() => trackModel2);
audioPlayerMock.reset();
audioPlayerMock.setup((x) => x.progressSeconds).returns(() => 2.9);
progressUpdaterMock.reset();
// Act
service.playPrevious();
-
- // Assert
- audioPlayerMock.verify((x) => x.play(trackModel1.path), Times.exactly(1));
- expect(service.isPlaying).toBeTruthy();
- expect(service.canPause).toBeTruthy();
- expect(service.canResume).toBeFalsy();
- progressUpdaterMock.verify((x) => x.startUpdatingProgress(), Times.exactly(1));
- expect(service.currentTrack).toBe(trackModel1);
- });
-
- it('should restart the current track if playback lasted for more than 3 seconds', () => {
- // Arrange
- service.enqueueAndPlayTracks(trackModels);
-
- // reset audio player and progress updater to make sure playback is more than
- audioPlayerMock.reset();
- audioPlayerMock.setup((x) => x.progressSeconds).returns(() => 3.1);
- progressUpdaterMock.reset();
-
- // Act
- service.playPrevious();
-
// Assert
- audioPlayerMock.verify((x) => x.play(trackModel1.path), Times.exactly(1));
+ audioPlayerMock.verify((x) => x.play(trackModel2.path), Times.exactly(1));
expect(service.isPlaying).toBeTruthy();
expect(service.canPause).toBeTruthy();
expect(service.canResume).toBeFalsy();
progressUpdaterMock.verify((x) => x.startUpdatingProgress(), Times.exactly(1));
- expect(service.currentTrack).toBe(trackModel1);
+ expect(service.currentTrack).toBe(trackModel2);
});
it('should stop playback if a previous track was not found and playback lasted for less then 3 seconds', () => {
// Arrange
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
service.enqueueAndPlayTracks(trackModels);
+ queueMock.setup((x) => x.getPreviousTrack(trackModel1, false)).returns(() => undefined);
audioPlayerMock.reset();
audioPlayerMock.setup((x) => x.progressSeconds).returns(() => 2.9);
@@ -1413,7 +1401,9 @@ describe('PlaybackService', () => {
it('should raise an event that playback is stopped if a previous track was not found and playback lasted for less then 3 seconds.', () => {
// Arrange
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
service.enqueueAndPlayTracks(trackModels);
+ queueMock.setup((x) => x.getPreviousTrack(trackModel1, false)).returns(() => undefined);
audioPlayerMock.reset();
audioPlayerMock.setup((x) => x.progressSeconds).returns(() => 2.9);
let playbackIsStopped: boolean = false;
@@ -1433,7 +1423,9 @@ describe('PlaybackService', () => {
it('should set the current track to undefined before raising a stop event', () => {
// Arrange
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
service.enqueueAndPlayTracks(trackModels);
+ queueMock.setup((x) => x.getNextTrack(It.isAny(), false)).returns(() => undefined);
let currentTrack: TrackModel;
subscription.add(
@@ -1451,64 +1443,48 @@ describe('PlaybackService', () => {
it('should get the previous track without wrap around if loopMode is None', () => {
// Arrange
- const getPreviousTrackSpy = jest.spyOn(queue, 'getPreviousTrack');
while (service.loopMode !== LoopMode.None) {
service.toggleLoopMode();
}
- service.enqueueAndPlayTracks([trackModel1, trackModel2]);
// Act
service.playPrevious();
// Assert
- expect(getPreviousTrackSpy).toHaveBeenCalledTimes(1);
- expect(getPreviousTrackSpy).toHaveBeenCalledWith(trackModel1, false);
- expect(service.currentTrack).toBe(undefined);
+ queueMock.verify((x) => x.getPreviousTrack(It.isAny(), false), Times.exactly(1));
});
it('should get the previous track with wrap around if loopMode is All', () => {
// Arrange
- const getPreviousTrackSpy = jest.spyOn(queue, 'getPreviousTrack');
while (service.loopMode !== LoopMode.All) {
service.toggleLoopMode();
}
- service.enqueueAndPlayTracks([trackModel1, trackModel2]);
// Act
service.playPrevious();
// Assert
- expect(getPreviousTrackSpy).toHaveBeenCalledTimes(1);
- expect(getPreviousTrackSpy).toHaveBeenCalledWith(trackModel1, true);
- expect(service.currentTrack).toBe(trackModel2);
+ queueMock.verify((x) => x.getPreviousTrack(It.isAny(), true), Times.exactly(1));
});
it('should get the previous track with wrap around if loopMode is One', () => {
// Arrange
- const getPreviousTrackSpy = jest.spyOn(queue, 'getPreviousTrack');
while (service.loopMode !== LoopMode.One) {
service.toggleLoopMode();
}
- service.enqueueAndPlayTracks([trackModel1, trackModel2]);
- // playPrevious doesn't work on first track, need to have a previous that has been played, so:
- service.playNext(); // play next, should still be track 1
- // do it twice to make sure
- service.playNext(); // play next, should still be track 1
// Act
service.playPrevious();
// Assert
- expect(getPreviousTrackSpy).toHaveBeenCalledTimes(1);
- expect(getPreviousTrackSpy).toHaveBeenCalledWith(undefined, false);
- expect(service.currentTrack).toBe(trackModel1);
+ queueMock.verify((x) => x.getPreviousTrack(It.isAny(), false), Times.exactly(1));
});
it('should raise an event that playback has started, containing the current track and if a previous track is being played.', () => {
// Arrange
- service.enqueueAndPlayTracks([trackModel1, trackModel2]);
- // set up a "previous" track by playing next track, otherwise "play previous" will be undefined
- service.playNext();
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
+ service.enqueueAndPlayTracks(trackModels);
+ queueMock.setup((x) => x.getPreviousTrack(trackModel1, false)).returns(() => trackModel2);
let receivedTrack: TrackModel;
let isPlayingPreviousTrack: boolean;
subscription.add(
@@ -1517,12 +1493,10 @@ describe('PlaybackService', () => {
isPlayingPreviousTrack = playbackStarted.isPlayingPreviousTrack;
})
);
-
// Act
service.playPrevious();
-
// Assert
- expect(receivedTrack).toBe(trackModel1);
+ expect(receivedTrack).toBe(trackModel2);
expect(isPlayingPreviousTrack).toBeTruthy();
});
});
@@ -1530,6 +1504,7 @@ describe('PlaybackService', () => {
describe('playNext', () => {
it('should stop playback if a next track is not found', () => {
// Arrange
+ queueMock.setup((x) => x.getNextTrack(It.isObjectWith({ path: 'Path 1' }), false)).returns(() => undefined);
progressUpdaterMock.reset();
audioPlayerMock.reset();
@@ -1550,7 +1525,11 @@ describe('PlaybackService', () => {
it('should raise an event that playback is stopped if a next track is not found', () => {
// Arrange
- service.enqueueAndPlayTracks([trackModel1]);
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
+ service.enqueueAndPlayTracks(trackModels);
+ queueMock.setup((x) => x.getNextTrack(It.isObjectWith({ path: 'Path 1' }), false)).returns(() => undefined);
+ progressUpdaterMock.reset();
+ audioPlayerMock.reset();
let playbackIsStopped: boolean = false;
subscription.add(
@@ -1568,7 +1547,9 @@ describe('PlaybackService', () => {
it('should set the current track to undefined before raising a stop event', () => {
// Arrange
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
service.enqueueAndPlayTracks(trackModels);
+ queueMock.setup((x) => x.getNextTrack(It.isObjectWith({ path: 'Path 1' }), false)).returns(() => undefined);
progressUpdaterMock.reset();
audioPlayerMock.reset();
let currentTrack: TrackModel;
@@ -1588,7 +1569,9 @@ describe('PlaybackService', () => {
it('should play the next track if found', () => {
// Arrange
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
service.enqueueAndPlayTracks(trackModels);
+ queueMock.setup((x) => x.getNextTrack(It.isObjectWith({ path: 'Path 1' }), false)).returns(() => trackModel2);
progressUpdaterMock.reset();
audioPlayerMock.reset();
@@ -1607,66 +1590,46 @@ describe('PlaybackService', () => {
it('should get the next track without wrap around if loopMode is None', () => {
// Arrange
- const getNextTrackSpy = jest.spyOn(queue, 'getNextTrack');
-
while (service.loopMode !== LoopMode.None) {
service.toggleLoopMode();
}
- service.enqueueAndPlayTracks([trackModel1, trackModel2]);
-
// Act
service.playNext();
// Assert
- expect(getNextTrackSpy).toHaveBeenCalledTimes(1);
- expect(getNextTrackSpy).toHaveBeenCalledWith(trackModel1, false);
- expect(service.currentTrack).toBe(trackModel2);
+ queueMock.verify((x) => x.getNextTrack(It.isAny(), false), Times.exactly(1));
});
-
it('should get the next track with wrap around if loopMode is All', () => {
// Arrange
- const getNextTrackSpy = jest.spyOn(queue, 'getNextTrack');
-
while (service.loopMode !== LoopMode.All) {
service.toggleLoopMode();
}
- service.enqueueAndPlayTracks([trackModel1, trackModel2]);
- service.playNext(); // Move playback to second track (last track in queue)
- getNextTrackSpy.mockClear();
-
// Act
service.playNext();
// Assert
- expect(getNextTrackSpy).toHaveBeenCalledTimes(1);
- expect(getNextTrackSpy).toHaveBeenCalledWith(trackModel2, true);
- expect(service.currentTrack).toBe(trackModel1);
+ queueMock.verify((x) => x.getNextTrack(It.isAny(), true), Times.exactly(1));
});
-
it('should get the next track with wrap around if loopMode is One', () => {
// Arrange
- const getNextTrackSpy = jest.spyOn(queue, 'getNextTrack');
-
while (service.loopMode !== LoopMode.One) {
service.toggleLoopMode();
}
- service.enqueueAndPlayTracks([trackModel1, trackModel2]);
-
// Act
service.playNext();
// Assert
- expect(getNextTrackSpy).toHaveBeenCalledTimes(1);
- expect(getNextTrackSpy).toHaveBeenCalledWith(trackModel1, false);
- expect(service.currentTrack).toBe(trackModel2);
+ queueMock.verify((x) => x.getNextTrack(It.isAny(), false), Times.exactly(1));
});
it('should raise an event that playback has started, containing the current track and if a next track is being played.', () => {
// Arrange
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
service.enqueueAndPlayTracks(trackModels);
+ queueMock.setup((x) => x.getNextTrack(It.isObjectWith({ path: 'Path 1' }), false)).returns(() => trackModel2);
let receivedTrack: TrackModel;
let isPlayingPreviousTrack: boolean;
subscription.add(
@@ -1686,6 +1649,7 @@ describe('PlaybackService', () => {
it('should increase play count and date last played for the current track if progress is more than 80%', () => {
const trackModelMock: IMock = Mock.ofType();
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModelMock.object);
service.enqueueAndPlayTracks([trackModelMock.object]);
progressUpdaterProgressChanged.next(new PlaybackProgress(81, 100));
@@ -1698,6 +1662,7 @@ describe('PlaybackService', () => {
it('should save play count and date last played for the current track if progress is more than 80%', () => {
const trackModelMock: IMock = Mock.ofType();
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModelMock.object);
service.enqueueAndPlayTracks([trackModelMock.object]);
progressUpdaterProgressChanged.next(new PlaybackProgress(81, 100));
@@ -1710,6 +1675,7 @@ describe('PlaybackService', () => {
it('should increase skip count for the current track if progress is less than 80%', () => {
const trackModelMock: IMock = Mock.ofType();
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModelMock.object);
service.enqueueAndPlayTracks([trackModelMock.object]);
progressUpdaterProgressChanged.next(new PlaybackProgress(79, 100));
@@ -1722,6 +1688,7 @@ describe('PlaybackService', () => {
it('should save skip count for the current track if progress is less than 80%', () => {
const trackModelMock: IMock = Mock.ofType();
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModelMock.object);
service.enqueueAndPlayTracks([trackModelMock.object]);
progressUpdaterProgressChanged.next(new PlaybackProgress(79, 100));
@@ -1913,7 +1880,21 @@ describe('PlaybackService', () => {
describe('playbackQueue', () => {
it('should return an empty queue if it has no tracks', () => {
// Arrange
- service.enqueueAndPlayTracks([]);
+ queueMock.reset();
+ queueMock.setup((x) => x.tracks).returns(() => []);
+ queueMock.setup((x) => x.tracksInPlaybackOrder).returns(() => []);
+ service = new PlaybackService(
+ trackServiceMock.object,
+ playlistServiceMock.object,
+ snackBarServiceMock.object,
+ audioPlayerMock.object,
+ trackOrderingMock.object,
+ queueMock.object,
+ progressUpdaterMock.object,
+ mathExtensionsMock.object,
+ settingsStub,
+ loggerMock.object
+ );
// Act
const queue: TrackModels = service.playbackQueue;
@@ -1924,7 +1905,21 @@ describe('PlaybackService', () => {
it('should return the queued tracks if the queue has tracks', () => {
// Arrange
- service.enqueueAndPlayTracks(tracks.tracks);
+ queueMock.reset();
+ queueMock.setup((x) => x.tracks).returns(() => tracks.tracks);
+ queueMock.setup((x) => x.tracksInPlaybackOrder).returns(() => tracks.tracks);
+ service = new PlaybackService(
+ trackServiceMock.object,
+ playlistServiceMock.object,
+ snackBarServiceMock.object,
+ audioPlayerMock.object,
+ trackOrderingMock.object,
+ queueMock.object,
+ progressUpdaterMock.object,
+ mathExtensionsMock.object,
+ settingsStub,
+ loggerMock.object
+ );
// Act
const queue: TrackModels = service.playbackQueue;
@@ -1980,6 +1975,7 @@ describe('PlaybackService', () => {
describe('togglePlayback', () => {
it('should resume playback if paused', () => {
// Arrange
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
service.enqueueAndPlayTracks(trackModels);
audioPlayerMock.reset();
service.pause();
@@ -1995,6 +1991,7 @@ describe('PlaybackService', () => {
it('should pause playback if playing', () => {
// Arrange
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
service.enqueueAndPlayTracks(trackModels);
audioPlayerMock.reset();
@@ -2009,58 +2006,38 @@ describe('PlaybackService', () => {
});
describe('removeFromQueue', () => {
- let removeTracksSpy: jest.SpyInstance;
- beforeEach(() => {
- removeTracksSpy = jest.spyOn(queue, 'removeTracks');
- });
-
it('should not remove tracks when tracksToRemove is undefined', () => {
// Arrange
- service.enqueueAndPlayTracks([trackModel1, trackModel2]);
// Act
service.removeFromQueue(undefined);
// Assert
- expect(removeTracksSpy).not.toHaveBeenCalled();
- expect(service.playbackQueue.tracks[0]).toBe(trackModel1);
- expect(service.playbackQueue.tracks[1]).toBe(trackModel2);
+ queueMock.verify((x) => x.removeTracks(It.isAny()), Times.never());
});
it('should not remove tracks when tracksToRemove is empty', () => {
// Arrange
- service.enqueueAndPlayTracks([trackModel1, trackModel2]);
// Act
service.removeFromQueue([]);
// Assert
- expect(removeTracksSpy).not.toHaveBeenCalled();
- expect(service.playbackQueue.tracks[0]).toBe(trackModel1);
- expect(service.playbackQueue.tracks[1]).toBe(trackModel2);
+ queueMock.verify((x) => x.removeTracks(It.isAny()), Times.never());
});
it('should remove tracks when tracksToRemove has items', () => {
// Arrange
- service.enqueueAndPlayTracks([trackModel1, trackModel2]);
// Act
service.removeFromQueue([trackModel1]);
// Assert
- expect(removeTracksSpy).toHaveBeenCalledTimes(1);
- expect(removeTracksSpy).toHaveBeenCalledWith([trackModel1]);
- expect(service.playbackQueue.tracks[0]).toBe(trackModel2);
- expect(service.playbackQueue.tracks.length).toBe(1);
+ queueMock.verify((x) => x.removeTracks([trackModel1]), Times.once());
});
});
describe('addTracksToQueueAsync', () => {
- let addTracksSpy: jest.SpyInstance;
- beforeEach(() => {
- addTracksSpy = jest.spyOn(queue, 'addTracks');
- });
-
it('should not add tracks to the queue if tracksToAdd is undefined', async () => {
// Arrange
@@ -2068,7 +2045,7 @@ describe('PlaybackService', () => {
await service.addTracksToQueueAsync(undefined);
// Assert
- expect(addTracksSpy).not.toHaveBeenCalled();
+ queueMock.verify((x) => x.addTracks(It.isAny()), Times.never());
snackBarServiceMock.verify((x) => x.singleTrackAddedToPlaybackQueueAsync(), Times.never());
snackBarServiceMock.verify((x) => x.multipleTracksAddedToPlaybackQueueAsync(It.isAny()), Times.never());
});
@@ -2080,7 +2057,7 @@ describe('PlaybackService', () => {
await service.addTracksToQueueAsync([]);
// Assert
- expect(addTracksSpy).not.toHaveBeenCalled();
+ queueMock.verify((x) => x.addTracks(It.isAny()), Times.never());
snackBarServiceMock.verify((x) => x.singleTrackAddedToPlaybackQueueAsync(), Times.never());
snackBarServiceMock.verify((x) => x.multipleTracksAddedToPlaybackQueueAsync(It.isAny()), Times.never());
});
@@ -2092,8 +2069,7 @@ describe('PlaybackService', () => {
await service.addTracksToQueueAsync([trackModel1]);
// Assert
- expect(addTracksSpy).toHaveBeenCalledTimes(1);
- expect(addTracksSpy).toHaveBeenCalledWith([trackModel1]);
+ queueMock.verify((x) => x.addTracks([trackModel1]), Times.once());
snackBarServiceMock.verify((x) => x.singleTrackAddedToPlaybackQueueAsync(), Times.exactly(1));
});
});
@@ -2126,43 +2102,35 @@ describe('PlaybackService', () => {
it('should get tracks for the artist if artistToAdd and artistType are not undefined', async () => {
// Arrange
- const addTracksSpy = jest.spyOn(queue, 'addTracks');
const artistToAdd: ArtistModel = new ArtistModel('artist1', translatorServiceMock.object);
// Act
await service.addArtistToQueueAsync(artistToAdd, ArtistType.trackArtists);
// Assert
- expect(addTracksSpy).toHaveBeenCalledTimes(1);
- expect(addTracksSpy).toHaveBeenCalledWith(orderedTrackModels);
trackServiceMock.verify((x) => x.getTracksForArtists([artistToAdd.displayName], ArtistType.trackArtists), Times.exactly(1));
});
it('should order tracks for the artist byAlbum', async () => {
// Arrange
- const addTracksSpy = jest.spyOn(queue, 'addTracks');
const artistToAdd: ArtistModel = new ArtistModel('artist1', translatorServiceMock.object);
// Act
await service.addArtistToQueueAsync(artistToAdd, ArtistType.trackArtists);
// Assert
- expect(addTracksSpy).toHaveBeenCalledTimes(1);
- expect(addTracksSpy).toHaveBeenCalledWith(orderedTrackModels);
trackOrderingMock.verify((x) => x.getTracksOrderedByAlbum(tracks.tracks), Times.exactly(1));
});
it('should add tracks to the queue ordered by album', async () => {
// Arrange
- const addTracksSpy = jest.spyOn(queue, 'addTracks');
const artistToAdd: ArtistModel = new ArtistModel('artist1', translatorServiceMock.object);
// Act
await service.addArtistToQueueAsync(artistToAdd, ArtistType.trackArtists);
// Assert
- expect(addTracksSpy).toHaveBeenCalledTimes(1);
- expect(addTracksSpy).toHaveBeenCalledWith(orderedTrackModels);
+ queueMock.verify((x) => x.addTracks(orderedTrackModels), Times.exactly(1));
snackBarServiceMock.verify((x) => x.multipleTracksAddedToPlaybackQueueAsync(4), Times.exactly(1));
});
});
@@ -2204,15 +2172,13 @@ describe('PlaybackService', () => {
it('should add tracks to the queue ordered by album', async () => {
// Arrange
- const addTracksSpy = jest.spyOn(queue, 'addTracks');
const genreToAdd: GenreModel = new GenreModel('genre1', translatorServiceMock.object);
// Act
await service.addGenreToQueueAsync(genreToAdd);
// Assert
- expect(addTracksSpy).toHaveBeenCalledTimes(1);
- expect(addTracksSpy).toHaveBeenCalledWith(orderedTrackModels);
+ queueMock.verify((x) => x.addTracks(orderedTrackModels), Times.exactly(1));
snackBarServiceMock.verify((x) => x.multipleTracksAddedToPlaybackQueueAsync(4), Times.exactly(1));
});
});
@@ -2252,14 +2218,12 @@ describe('PlaybackService', () => {
it('should add tracks to the queue ordered by album', () => {
// Arrange
- const addTracksSpy = jest.spyOn(queue, 'addTracks');
// Act
service.addAlbumToQueueAsync(album1);
// Assert
- expect(addTracksSpy).toHaveBeenCalledTimes(1);
- expect(addTracksSpy).toHaveBeenCalledWith(orderedTrackModels);
+ queueMock.verify((x) => x.addTracks(orderedTrackModels), Times.exactly(1));
snackBarServiceMock.verify((x) => x.multipleTracksAddedToPlaybackQueueAsync(4), Times.exactly(1));
});
});
@@ -2281,18 +2245,18 @@ describe('PlaybackService', () => {
it('should not play the next track if there is no track playing', () => {
// Arrange
- const getNextTrackSpy = jest.spyOn(queue, 'getNextTrack');
// Act
service.stopIfPlaying(trackModel2);
// Assert
- expect(getNextTrackSpy).not.toHaveBeenCalled();
+ queueMock.verify((x) => x.getNextTrack(It.isAny(), It.isAny()), Times.never());
audioPlayerMock.verify((x) => x.play(It.isAny()), Times.never());
});
it('should not stop playback if the given track is not playing', () => {
// Arrange
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
service.enqueueAndPlayTracks(trackModels);
audioPlayerMock.reset();
@@ -2305,7 +2269,7 @@ describe('PlaybackService', () => {
it('should not play the next track if the given track is not playing', () => {
// Arrange
- const getNextTrackSpy = jest.spyOn(queue, 'getNextTrack');
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
service.enqueueAndPlayTracks(trackModels);
audioPlayerMock.reset();
@@ -2313,35 +2277,38 @@ describe('PlaybackService', () => {
service.stopIfPlaying(trackModel2);
// Assert
- expect(getNextTrackSpy).not.toHaveBeenCalled();
+ queueMock.verify((x) => x.getNextTrack(It.isAny(), It.isAny()), Times.never());
audioPlayerMock.verify((x) => x.play(It.isAny()), Times.never());
});
it('should stop playback if the given track is playing and it is the only track in the queue', () => {
// Arrange
- const getNextTrackSpy = jest.spyOn(queue, 'getNextTrack');
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
service.enqueueAndPlayTracks([trackModel1]);
audioPlayerMock.reset();
+ queueMock.setup((x) => x.numberOfTracks).returns(() => 1);
// Act
service.stopIfPlaying(trackModel1);
// Assert
- expect(getNextTrackSpy).not.toHaveBeenCalled();
+ queueMock.verify((x) => x.getNextTrack(It.isAny(), It.isAny()), Times.never());
audioPlayerMock.verify((x) => x.stop(), Times.once());
});
it('should play the next track if the given track is playing and it not the only track in the queue', async () => {
// Arrange
- const getNextTrackSpy = jest.spyOn(queue, 'getNextTrack');
+ queueMock.setup((x) => x.getFirstTrack()).returns(() => trackModel1);
service.enqueueAndPlayTracks(trackModels);
audioPlayerMock.reset();
+ queueMock.setup((x) => x.numberOfTracks).returns(() => 4);
+ queueMock.setup((x) => x.getNextTrack(It.isObjectWith({ path: 'Path 1' }), It.isAny())).returns(() => trackModel2);
+
// Act
await service.stopIfPlaying(trackModel1);
// Assert
- expect(getNextTrackSpy).toHaveBeenCalledTimes(1);
- expect(getNextTrackSpy).toHaveBeenCalledWith(trackModel1, false);
+ queueMock.verify((x) => x.getNextTrack(trackModel1, It.isAny()), Times.once());
audioPlayerMock.verify((x) => x.play(trackModel2.path), Times.once());
});
});
diff --git a/src/app/services/playback/playback.service.ts b/src/app/services/playback/playback.service.ts
index e1233160c..f7bfab26d 100644
--- a/src/app/services/playback/playback.service.ts
+++ b/src/app/services/playback/playback.service.ts
@@ -59,9 +59,8 @@ export class PlaybackService implements BasePlaybackService {
const trackModels: TrackModels = new TrackModels();
if (this.queue.tracks != undefined) {
- // add tracks to playback queue in playback order so that they will be loaded in the playback order into the view
- // so that the user can see what is coming next in the queue
- for (const track of this.queue.getTracksInPlaybackOrder()) {
+ // Add tracks to playback queue in playback order so that the user can see what is coming next in the queue
+ for (const track of this.queue.tracksInPlaybackOrder) {
trackModels.addTrack(track);
}
}
@@ -123,11 +122,12 @@ export class PlaybackService implements BasePlaybackService {
}
this.queue.setTracks(tracksToEnqueue, this.isShuffled);
- // play first track in queue (will be a random track if queue is shuffled)
- this.play(this.playbackQueue.tracks[0], false);
+
+ // Play first track in queue (will be a random track if queue is shuffled)
+ this.play(this.queue.getFirstTrack(), false);
}
- public enqueueAndPlayTracksFromDoubleClick(tracksToEnqueue: TrackModel[], trackToPlay: TrackModel): void {
+ public enqueueAndPlayTracksStartingFromGivenTrack(tracksToEnqueue: TrackModel[], trackToPlay: TrackModel): void {
if (tracksToEnqueue == undefined) {
return;
}
@@ -474,15 +474,7 @@ export class PlaybackService implements BasePlaybackService {
private applyVolumeFromSettings(): void {
this._volume = this.settings.volume;
-
- try {
- this.audioPlayer.setVolume(this._volume);
- } catch (error) {
- this.logger.warn('Could not apply volume from settings. Resetting volume to 0.', 'PlaybackService', 'applyVolumeFromSettings');
- this.settings.volume = 0;
- this._volume = 0;
- this.audioPlayer.setVolume(this._volume);
- }
+ this.audioPlayer.setVolume(this._volume);
}
private async notifyOfTracksAddedToPlaybackQueueAsync(numberOfAddedTracks: number): Promise {
diff --git a/src/app/services/playback/queue.spec.ts b/src/app/services/playback/queue.spec.ts
index 46918748e..1c1ec24aa 100644
--- a/src/app/services/playback/queue.spec.ts
+++ b/src/app/services/playback/queue.spec.ts
@@ -8,7 +8,6 @@ import { BaseTranslatorService } from '../translator/base-translator.service';
import { Queue } from './queue';
describe('Queue', () => {
- let queue: Queue;
let shufflerMock: IMock;
let loggerMock: IMock;
let dateTimeMock: IMock;
@@ -19,13 +18,12 @@ describe('Queue', () => {
dateTimeMock = Mock.ofType();
translatorServiceMock = Mock.ofType();
loggerMock = Mock.ofType();
-
- shufflerMock.setup((x) => x.shuffle([0, 1, 2, 3, 4])).returns(() => [3, 2, 4, 0, 1]);
- shufflerMock.setup((x) => x.shuffle([0, 1])).returns(() => [1, 0]);
-
- queue = new Queue(shufflerMock.object, loggerMock.object);
});
+ function createQueue(): Queue {
+ return new Queue(shufflerMock.object, loggerMock.object);
+ }
+
function createTrackModel(path: string): TrackModel {
return new TrackModel(new Track(path), dateTimeMock.object, translatorServiceMock.object);
}
@@ -35,6 +33,7 @@ describe('Queue', () => {
// Arrange
// Act
+ const queue: Queue = createQueue();
// Assert
expect(queue).toBeDefined();
@@ -44,6 +43,7 @@ describe('Queue', () => {
// Arrange
// Act
+ const queue: Queue = createQueue();
// Assert
expect(queue.tracks.length).toEqual(0);
@@ -56,6 +56,8 @@ describe('Queue', () => {
const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2, track3], false);
// Act
@@ -73,6 +75,8 @@ describe('Queue', () => {
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+ const queue: Queue = createQueue();
+
// Act
queue.setTracks([track1, track2, track3], false);
@@ -89,6 +93,8 @@ describe('Queue', () => {
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+ const queue: Queue = createQueue();
+
// Act
queue.setTracks([track1, track2, track3], true);
@@ -105,6 +111,8 @@ describe('Queue', () => {
// Arrange
const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
+ const queue: Queue = createQueue();
+
// Act
// Assert
@@ -115,6 +123,8 @@ describe('Queue', () => {
// Arrange
const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2], false);
// Act
@@ -129,7 +139,8 @@ describe('Queue', () => {
const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
shufflerMock.setup((x) => x.shuffle([0, 1])).returns(() => [1, 0]);
- queue = new Queue(shufflerMock.object, loggerMock.object);
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2], true);
// Act
@@ -156,6 +167,8 @@ describe('Queue', () => {
dateTimeMock.object,
translatorServiceMock.object
);
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2], false);
// Act
@@ -171,7 +184,8 @@ describe('Queue', () => {
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
shufflerMock.setup((x) => x.shuffle([0, 1])).returns(() => [1, 0]);
- queue = new Queue(shufflerMock.object, loggerMock.object);
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2], true);
// Act
@@ -186,6 +200,8 @@ describe('Queue', () => {
const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2, track3], false);
// Act
@@ -201,6 +217,8 @@ describe('Queue', () => {
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
shufflerMock.setup((x) => x.shuffle([0, 1, 2])).returns(() => [1, 2, 0]);
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2, track3], true);
// Act
@@ -215,6 +233,8 @@ describe('Queue', () => {
const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2, track3], false);
// Act
@@ -230,6 +250,8 @@ describe('Queue', () => {
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
shufflerMock.setup((x) => x.shuffle([0, 1, 2])).returns(() => [1, 2, 0]);
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2, track3], true);
// Act
@@ -244,6 +266,8 @@ describe('Queue', () => {
const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2, track3], false);
// Act
@@ -259,6 +283,8 @@ describe('Queue', () => {
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
shufflerMock.setup((x) => x.shuffle([0, 1, 2])).returns(() => [1, 2, 0]);
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2, track3], false);
// Act
@@ -269,11 +295,61 @@ describe('Queue', () => {
});
});
+ describe('getFirstTrack', () => {
+ it('should return undefined if there are no tracks', () => {
+ // Arrange
+ const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
+
+ const queue: Queue = createQueue();
+
+ // Act
+
+ // Assert
+ expect(queue.getNextTrack(track1, false)).toBeUndefined();
+ });
+
+ it('should return the first track when the queue is not shuffled', () => {
+ // Arrange
+ const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
+ const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
+ const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+
+ const queue: Queue = createQueue();
+ queue.setTracks([track1, track2], false);
+
+ // Act
+ const firstTrack: TrackModel = queue.getFirstTrack();
+
+ // Assert
+ expect(firstTrack).toBe(track1);
+ });
+
+ it('should return the first track when the queue is shuffled', () => {
+ // Arrange
+ const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
+ const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
+ const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+
+ shufflerMock.setup((x) => x.shuffle([0, 1])).returns(() => [1, 0]);
+
+ const queue: Queue = createQueue();
+ queue.setTracks([track1, track2], true);
+
+ // Act
+ const firstTrack: TrackModel = queue.getFirstTrack();
+
+ // Assert
+ expect(firstTrack).toBe(track2);
+ });
+ });
+
describe('getNextTrack', () => {
it('should return undefined if there are no tracks', () => {
// Arrange
const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
+ const queue: Queue = createQueue();
+
// Act
// Assert
@@ -285,6 +361,8 @@ describe('Queue', () => {
const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2], false);
// Act
@@ -299,6 +377,8 @@ describe('Queue', () => {
const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
shufflerMock.setup((x) => x.shuffle([0, 1])).returns(() => [1, 0]);
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2], true);
// Act
@@ -313,6 +393,8 @@ describe('Queue', () => {
const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2], false);
// Act
@@ -328,6 +410,8 @@ describe('Queue', () => {
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
shufflerMock.setup((x) => x.shuffle([0, 1])).returns(() => [1, 0]);
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2], true);
// Act
@@ -342,6 +426,8 @@ describe('Queue', () => {
const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2, track3], false);
// Act
@@ -357,6 +443,8 @@ describe('Queue', () => {
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
shufflerMock.setup((x) => x.shuffle([0, 1, 2])).returns(() => [1, 2, 0]);
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2, track3], true);
// Act
@@ -371,6 +459,8 @@ describe('Queue', () => {
const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2, track3], false);
// Act
@@ -386,6 +476,8 @@ describe('Queue', () => {
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
shufflerMock.setup((x) => x.shuffle([0, 1, 2])).returns(() => [1, 2, 0]);
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2, track3], true);
// Act
@@ -400,6 +492,8 @@ describe('Queue', () => {
const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2, track3], false);
// Act
@@ -415,6 +509,8 @@ describe('Queue', () => {
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
shufflerMock.setup((x) => x.shuffle([0, 1, 2])).returns(() => [1, 2, 0]);
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2, track3], true);
// Act
@@ -431,6 +527,8 @@ describe('Queue', () => {
const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2, track3], false);
// Act
@@ -447,6 +545,8 @@ describe('Queue', () => {
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
shufflerMock.setup((x) => x.shuffle([0, 1, 2])).returns(() => [2, 1, 0]);
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2, track3], true);
// Act
@@ -464,6 +564,8 @@ describe('Queue', () => {
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
shufflerMock.setup((x) => x.shuffle([0, 1, 2])).returns(() => [2, 1, 0]);
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2, track3], false);
// Act
@@ -480,6 +582,8 @@ describe('Queue', () => {
const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
shufflerMock.setup((x) => x.shuffle([0, 1, 2])).returns(() => [2, 1, 0]);
+
+ const queue: Queue = createQueue();
queue.setTracks([track1, track2, track3], true);
// Act
@@ -488,259 +592,374 @@ describe('Queue', () => {
// Assert
expect(nextTrack).toBe(track1);
});
+ });
+
+ describe('removeTracks', () => {
+ it('should not remove tracks from queue if tracksToRemove is undefined', () => {
+ // Arrange
+ const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
+ const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
+ const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+
+ const queue: Queue = createQueue();
+ queue.setTracks([track1, track2, track3], false);
+
+ // Act
+ queue.removeTracks(undefined);
+
+ // Assert
+ expect(queue.tracks.length).toEqual(3);
+ expect(queue.tracks[0]).toBe(track1);
+ expect(queue.tracks[1]).toBe(track2);
+ expect(queue.tracks[2]).toBe(track3);
+ });
+
+ it('should not remove tracks from queue if tracksToRemove is empty', () => {
+ // Arrange
+ const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
+ const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
+ const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+
+ const queue: Queue = createQueue();
+ queue.setTracks([track1, track2, track3], false);
+
+ // Act
+ queue.removeTracks([]);
+
+ // Assert
+ expect(queue.tracks.length).toEqual(3);
+ expect(queue.tracks[0]).toBe(track1);
+ expect(queue.tracks[1]).toBe(track2);
+ expect(queue.tracks[2]).toBe(track3);
+ });
+
+ it('should remove tracks from queue if tracksToRemove has items', () => {
+ // Arrange
+ const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
+ const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
+ const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+
+ const queue: Queue = createQueue();
+ queue.setTracks([track1, track2, track3], false);
+
+ // Act
+ queue.removeTracks([track2]);
+
+ // Assert
+ expect(queue.tracks.length).toEqual(2);
+ expect(queue.tracks[0]).toBe(track1);
+ expect(queue.tracks[1]).toBe(track3);
+ });
+
+ it('should not remove tracks from unshuffled playback order if tracksToRemove is undefined', () => {
+ // Arrange
+ const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
+ const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
+ const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+ const track4: TrackModel = createTrackModel('/home/user/Music/Track4.mp3');
+ const track5: TrackModel = createTrackModel('/home/user/Music/Track5.mp3');
+
+ const queue: Queue = createQueue();
+ queue.setTracks([track1, track2, track3, track4, track5], false);
+
+ // Act
+ queue.removeTracks(undefined);
+
+ // Assert
+ expect(queue.getNextTrack(track1, false)).toEqual(track2);
+ expect(queue.getNextTrack(track2, false)).toEqual(track3);
+ expect(queue.getNextTrack(track3, false)).toEqual(track4);
+ expect(queue.getNextTrack(track4, false)).toEqual(track5);
+ });
+
+ it('should not remove tracks from unshuffled playback order if tracksToRemove is empty', () => {
+ // Arrange
+ const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
+ const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
+ const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+ const track4: TrackModel = createTrackModel('/home/user/Music/Track4.mp3');
+ const track5: TrackModel = createTrackModel('/home/user/Music/Track5.mp3');
+ const queue: Queue = createQueue();
+
+ queue.setTracks([track1, track2, track3, track4, track5], false);
+
+ // Act
+ queue.removeTracks([]);
+
+ // Assert
+ expect(queue.getNextTrack(track1, false)).toEqual(track2);
+ expect(queue.getNextTrack(track2, false)).toEqual(track3);
+ expect(queue.getNextTrack(track3, false)).toEqual(track4);
+ expect(queue.getNextTrack(track4, false)).toEqual(track5);
+ });
+
+ it('should remove tracks from unshuffled playback order if tracksToRemove has items', () => {
+ // Arrange
+ const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
+ const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
+ const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+ const track4: TrackModel = createTrackModel('/home/user/Music/Track4.mp3');
+ const track5: TrackModel = createTrackModel('/home/user/Music/Track5.mp3');
+
+ const queue: Queue = createQueue();
+ queue.setTracks([track1, track2, track3, track4, track5], false);
+
+ // Act
+ queue.removeTracks([track2, track4]);
- describe('removeTracks', () => {
- it('should not remove tracks from queue if tracksToRemove is undefined', () => {
- // Arrange
- const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
- const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
- const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
-
- queue.setTracks([track1, track2, track3], false);
-
- // Act
- queue.removeTracks(undefined);
-
- // Assert
- expect(queue.tracks.length).toEqual(3);
- expect(queue.tracks[0]).toBe(track1);
- expect(queue.tracks[1]).toBe(track2);
- expect(queue.tracks[2]).toBe(track3);
- });
-
- it('should not remove tracks from queue if tracksToRemove is empty', () => {
- // Arrange
- const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
- const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
- const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
- queue.setTracks([track1, track2, track3], false);
-
- // Act
- queue.removeTracks([]);
-
- // Assert
- expect(queue.tracks.length).toEqual(3);
- expect(queue.tracks[0]).toBe(track1);
- expect(queue.tracks[1]).toBe(track2);
- expect(queue.tracks[2]).toBe(track3);
- });
-
- it('should remove tracks from queue if tracksToRemove has items', () => {
- // Arrange
- const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
- const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
- const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
- queue.setTracks([track1, track2, track3], false);
-
- // Act
- queue.removeTracks([track2]);
-
- // Assert
- expect(queue.tracks.length).toEqual(2);
- expect(queue.tracks[0]).toBe(track1);
- expect(queue.tracks[1]).toBe(track3);
- });
-
- it('should not remove tracks from unshuffled playback order if tracksToRemove is undefined', () => {
- // Arrange
- const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
- const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
- const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
- const track4: TrackModel = createTrackModel('/home/user/Music/Track4.mp3');
- const track5: TrackModel = createTrackModel('/home/user/Music/Track5.mp3');
- queue.setTracks([track1, track2, track3, track4, track5], false);
-
- // Act
- queue.removeTracks(undefined);
-
- // Assert
- expect(queue.getNextTrack(track1, false)).toEqual(track2);
- expect(queue.getNextTrack(track2, false)).toEqual(track3);
- expect(queue.getNextTrack(track3, false)).toEqual(track4);
- expect(queue.getNextTrack(track4, false)).toEqual(track5);
- });
-
- it('should not remove tracks from unshuffled playback order if tracksToRemove is empty', () => {
- // Arrange
- const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
- const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
- const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
- const track4: TrackModel = createTrackModel('/home/user/Music/Track4.mp3');
- const track5: TrackModel = createTrackModel('/home/user/Music/Track5.mp3');
- queue.setTracks([track1, track2, track3, track4, track5], false);
-
- // Act
- queue.removeTracks([]);
-
- // Assert
- expect(queue.getNextTrack(track1, false)).toEqual(track2);
- expect(queue.getNextTrack(track2, false)).toEqual(track3);
- expect(queue.getNextTrack(track3, false)).toEqual(track4);
- expect(queue.getNextTrack(track4, false)).toEqual(track5);
- });
-
- it('should remove tracks from unshuffled playback order if tracksToRemove has items', () => {
- // Arrange
- const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
- const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
- const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
- const track4: TrackModel = createTrackModel('/home/user/Music/Track4.mp3');
- const track5: TrackModel = createTrackModel('/home/user/Music/Track5.mp3');
- queue.setTracks([track1, track2, track3, track4, track5], false);
-
- // Act
- queue.removeTracks([track2, track4]);
-
- // Assert
- expect(queue.tracks.length).toEqual(3);
- expect(queue.getNextTrack(track1, false)).toEqual(track3);
- expect(queue.getNextTrack(track3, false)).toEqual(track5);
- });
-
- it('should not remove tracks from shuffled playback order if tracksToRemove is undefined', () => {
- // Arrange
- const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
- const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
- const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
- const track4: TrackModel = createTrackModel('/home/user/Music/Track4.mp3');
- const track5: TrackModel = createTrackModel('/home/user/Music/Track5.mp3');
- queue.setTracks([track1, track2, track3, track4, track5], true);
-
- // Act
- queue.removeTracks(undefined);
-
- // Assert
- expect(queue.getNextTrack(track1, false)).toEqual(track2);
- expect(queue.getNextTrack(track2, false)).toEqual(undefined);
- expect(queue.getNextTrack(track3, false)).toEqual(track5);
- expect(queue.getNextTrack(track4, false)).toEqual(track3);
- expect(queue.getNextTrack(track5, false)).toEqual(track1);
- });
-
- it('should not remove tracks from shuffled playback order if tracksToRemove is empty', () => {
- // Arrange
- const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
- const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
- const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
- const track4: TrackModel = createTrackModel('/home/user/Music/Track4.mp3');
- const track5: TrackModel = createTrackModel('/home/user/Music/Track5.mp3');
- queue.setTracks([track1, track2, track3, track4, track5], true);
-
- // Act
- queue.removeTracks([]);
-
- // Assert
- expect(queue.getNextTrack(track1, false)).toEqual(track2);
- expect(queue.getNextTrack(track2, false)).toEqual(undefined);
- expect(queue.getNextTrack(track3, false)).toEqual(track5);
- expect(queue.getNextTrack(track4, false)).toEqual(track3);
- expect(queue.getNextTrack(track5, false)).toEqual(track1);
- });
-
- it('should remove tracks from shuffled playback order if tracksToRemove has items', () => {
- // Arrange
- const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
- const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
- const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
- const track4: TrackModel = createTrackModel('/home/user/Music/Track4.mp3');
- const track5: TrackModel = createTrackModel('/home/user/Music/Track5.mp3');
- queue.setTracks([track1, track2, track3, track4, track5], true);
-
- // Act
- queue.removeTracks([track2, track4]);
-
- // Assert
- expect(queue.tracks.length).toEqual(3);
- expect(queue.getNextTrack(track1, false)).toEqual(undefined);
- expect(queue.getNextTrack(track3, false)).toEqual(track5);
- expect(queue.getNextTrack(track5, false)).toEqual(track1);
- });
- });
-
- describe('addTracks', () => {
- it('should add tracks to the end of queue when not shuffled', () => {
- // Arrange
- const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
- const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
- const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
- const track4: TrackModel = createTrackModel('/home/user/Music/Track4.mp3');
- const track5: TrackModel = createTrackModel('/home/user/Music/Track5.mp3');
- queue.setTracks([track1, track2], false);
-
- // Act
- queue.addTracks([track3, track4, track5]);
-
- // Assert
- expect(queue.tracks.length).toEqual(5);
- expect(queue.tracks[0]).toEqual(track1);
- expect(queue.tracks[1]).toEqual(track2);
- expect(queue.tracks[2]).toEqual(track3);
- expect(queue.tracks[3]).toEqual(track4);
- expect(queue.tracks[4]).toEqual(track5);
- });
-
- it('should add tracks to the end of queue when shuffled', () => {
- // Arrange
- const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
- const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
- const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
- const track4: TrackModel = createTrackModel('/home/user/Music/Track4.mp3');
- const track5: TrackModel = createTrackModel('/home/user/Music/Track5.mp3');
- queue.setTracks([track1, track2], true);
-
- // Act
- queue.addTracks([track3, track4, track5]);
-
- // Assert
- expect(queue.tracks.length).toEqual(5);
- expect(queue.tracks[0]).toEqual(track1);
- expect(queue.tracks[1]).toEqual(track2);
- expect(queue.tracks[2]).toEqual(track3);
- expect(queue.tracks[3]).toEqual(track4);
- expect(queue.tracks[4]).toEqual(track5);
- });
-
- it('should add tracks to the end of unshuffled playback order', () => {
- // Arrange
- const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
- const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
- const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
- const track4: TrackModel = createTrackModel('/home/user/Music/Track4.mp3');
- const track5: TrackModel = createTrackModel('/home/user/Music/Track5.mp3');
- queue.setTracks([track1, track2], false);
-
- // Act
- queue.addTracks([track3, track4, track5]);
-
- // Assert
- expect(queue.tracks.length).toEqual(5);
- expect(queue.getNextTrack(track1, false)).toEqual(track2);
- expect(queue.getNextTrack(track2, false)).toEqual(track3);
- expect(queue.getNextTrack(track3, false)).toEqual(track4);
- expect(queue.getNextTrack(track4, false)).toEqual(track5);
- expect(queue.getNextTrack(track5, false)).toEqual(undefined);
- });
-
- it('should add tracks to the end of shuffled playback order', () => {
- // Arrange
- const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
- const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
- const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
- const track4: TrackModel = createTrackModel('/home/user/Music/Track4.mp3');
- const track5: TrackModel = createTrackModel('/home/user/Music/Track5.mp3');
- queue.setTracks([track1, track2], true);
-
- // Act
- queue.addTracks([track3, track4, track5]);
-
- // Assert
- expect(queue.tracks.length).toEqual(5);
- expect(queue.getNextTrack(track1, false)).toEqual(track3);
- expect(queue.getNextTrack(track2, false)).toEqual(track1);
- expect(queue.getNextTrack(track3, false)).toEqual(track4);
- expect(queue.getNextTrack(track4, false)).toEqual(track5);
- expect(queue.getNextTrack(track5, false)).toEqual(undefined);
- });
+ // Assert
+ expect(queue.tracks.length).toEqual(3);
+ expect(queue.getNextTrack(track1, false)).toEqual(track3);
+ expect(queue.getNextTrack(track3, false)).toEqual(track5);
+ });
+
+ it('should not remove tracks from shuffled playback order if tracksToRemove is undefined', () => {
+ // Arrange
+ const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
+ const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
+ const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+ const track4: TrackModel = createTrackModel('/home/user/Music/Track4.mp3');
+ const track5: TrackModel = createTrackModel('/home/user/Music/Track5.mp3');
+
+ shufflerMock.setup((x) => x.shuffle([0, 1, 2, 3, 4])).returns(() => [3, 2, 4, 0, 1]);
+
+ const queue: Queue = createQueue();
+ queue.setTracks([track1, track2, track3, track4, track5], true);
+
+ // Act
+ queue.removeTracks(undefined);
+
+ // Assert
+ expect(queue.getNextTrack(track1, false)).toEqual(track2);
+ expect(queue.getNextTrack(track2, false)).toEqual(undefined);
+ expect(queue.getNextTrack(track3, false)).toEqual(track5);
+ expect(queue.getNextTrack(track4, false)).toEqual(track3);
+ expect(queue.getNextTrack(track5, false)).toEqual(track1);
+ });
+
+ it('should not remove tracks from shuffled playback order if tracksToRemove is empty', () => {
+ // Arrange
+ const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
+ const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
+ const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+ const track4: TrackModel = createTrackModel('/home/user/Music/Track4.mp3');
+ const track5: TrackModel = createTrackModel('/home/user/Music/Track5.mp3');
+
+ shufflerMock.setup((x) => x.shuffle([0, 1, 2, 3, 4])).returns(() => [3, 2, 4, 0, 1]);
+
+ const queue: Queue = createQueue();
+ queue.setTracks([track1, track2, track3, track4, track5], true);
+
+ // Act
+ queue.removeTracks([]);
+
+ // Assert
+ expect(queue.getNextTrack(track1, false)).toEqual(track2);
+ expect(queue.getNextTrack(track2, false)).toEqual(undefined);
+ expect(queue.getNextTrack(track3, false)).toEqual(track5);
+ expect(queue.getNextTrack(track4, false)).toEqual(track3);
+ expect(queue.getNextTrack(track5, false)).toEqual(track1);
+ });
+
+ it('should remove tracks from shuffled playback order if tracksToRemove has items', () => {
+ // Arrange
+ const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
+ const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
+ const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+ const track4: TrackModel = createTrackModel('/home/user/Music/Track4.mp3');
+ const track5: TrackModel = createTrackModel('/home/user/Music/Track5.mp3');
+
+ shufflerMock.setup((x) => x.shuffle([0, 1, 2, 3, 4])).returns(() => [3, 2, 4, 0, 1]);
+
+ const queue: Queue = createQueue();
+ queue.setTracks([track1, track2, track3, track4, track5], true);
+
+ // Act
+ queue.removeTracks([track2, track4]);
+
+ // Assert
+ expect(queue.tracks.length).toEqual(3);
+ expect(queue.getNextTrack(track1, false)).toEqual(undefined);
+ expect(queue.getNextTrack(track3, false)).toEqual(track5);
+ expect(queue.getNextTrack(track5, false)).toEqual(track1);
+ });
+ });
+
+ describe('addTracks', () => {
+ it('should add tracks to the end of queue when not shuffled', () => {
+ // Arrange
+ const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
+ const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
+ const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+ const track4: TrackModel = createTrackModel('/home/user/Music/Track4.mp3');
+ const track5: TrackModel = createTrackModel('/home/user/Music/Track5.mp3');
+
+ const queue: Queue = createQueue();
+ queue.setTracks([track1, track2], false);
+
+ // Act
+ queue.addTracks([track3, track4, track5]);
+
+ // Assert
+ expect(queue.tracks.length).toEqual(5);
+ expect(queue.tracks[0]).toEqual(track1);
+ expect(queue.tracks[1]).toEqual(track2);
+ expect(queue.tracks[2]).toEqual(track3);
+ expect(queue.tracks[3]).toEqual(track4);
+ expect(queue.tracks[4]).toEqual(track5);
+ });
+
+ it('should add tracks to the end of queue when shuffled', () => {
+ // Arrange
+ const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
+ const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
+ const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+ const track4: TrackModel = createTrackModel('/home/user/Music/Track4.mp3');
+ const track5: TrackModel = createTrackModel('/home/user/Music/Track5.mp3');
+
+ shufflerMock.setup((x) => x.shuffle([0, 1])).returns(() => [1, 0]);
+
+ const queue: Queue = createQueue();
+ queue.setTracks([track1, track2], true);
+
+ // Act
+ queue.addTracks([track3, track4, track5]);
+
+ // Assert
+ expect(queue.tracks.length).toEqual(5);
+ expect(queue.tracks[0]).toEqual(track1);
+ expect(queue.tracks[1]).toEqual(track2);
+ expect(queue.tracks[2]).toEqual(track3);
+ expect(queue.tracks[3]).toEqual(track4);
+ expect(queue.tracks[4]).toEqual(track5);
+ });
+
+ it('should add tracks to the end of unshuffled playback order', () => {
+ // Arrange
+ const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
+ const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
+ const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+ const track4: TrackModel = createTrackModel('/home/user/Music/Track4.mp3');
+ const track5: TrackModel = createTrackModel('/home/user/Music/Track5.mp3');
+
+ const queue: Queue = createQueue();
+ queue.setTracks([track1, track2], false);
+
+ // Act
+ queue.addTracks([track3, track4, track5]);
+
+ // Assert
+ expect(queue.tracks.length).toEqual(5);
+ expect(queue.getNextTrack(track1, false)).toEqual(track2);
+ expect(queue.getNextTrack(track2, false)).toEqual(track3);
+ expect(queue.getNextTrack(track3, false)).toEqual(track4);
+ expect(queue.getNextTrack(track4, false)).toEqual(track5);
+ expect(queue.getNextTrack(track5, false)).toEqual(undefined);
+ });
+
+ it('should add tracks to the end of shuffled playback order', () => {
+ // Arrange
+ const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
+ const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
+ const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+ const track4: TrackModel = createTrackModel('/home/user/Music/Track4.mp3');
+ const track5: TrackModel = createTrackModel('/home/user/Music/Track5.mp3');
+
+ shufflerMock.setup((x) => x.shuffle([0, 1])).returns(() => [1, 0]);
+
+ const queue: Queue = createQueue();
+ queue.setTracks([track1, track2], true);
+
+ // Act
+ queue.addTracks([track3, track4, track5]);
+
+ // Assert
+ expect(queue.tracks.length).toEqual(5);
+ expect(queue.getNextTrack(track1, false)).toEqual(track3);
+ expect(queue.getNextTrack(track2, false)).toEqual(track1);
+ expect(queue.getNextTrack(track3, false)).toEqual(track4);
+ expect(queue.getNextTrack(track4, false)).toEqual(track5);
+ expect(queue.getNextTrack(track5, false)).toEqual(undefined);
+ });
+ });
+
+ describe('tracks', () => {
+ it('should return the tracks in their original order when not shuffled', () => {
+ // Arrange
+ const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
+ const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
+ const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+
+ const queue: Queue = createQueue();
+ queue.setTracks([track1, track2, track3], false);
+
+ // Act
+ const tracks: TrackModel[] = queue.tracks;
+
+ // Assert
+ expect(tracks[0].path).toBe(track1.path);
+ expect(tracks[1].path).toBe(track2.path);
+ expect(tracks[2].path).toBe(track3.path);
+ });
+
+ it('should return the tracks in original order when shuffled', () => {
+ // Arrange
+ const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
+ const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
+ const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+
+ shufflerMock.setup((x) => x.shuffle([0, 1, 2])).returns(() => [1, 2, 0]);
+
+ const queue: Queue = createQueue();
+ queue.setTracks([track1, track2, track3], true);
+
+ // Act
+ const tracks: TrackModel[] = queue.tracks;
+
+ // Assert
+ expect(tracks[0].path).toBe(track1.path);
+ expect(tracks[1].path).toBe(track2.path);
+ expect(tracks[2].path).toBe(track3.path);
+ });
+ });
+
+ describe('tracksInPlaybackOrder', () => {
+ it('should return the tracks in original order when not shuffled', () => {
+ // Arrange
+ const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
+ const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
+ const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+
+ const queue: Queue = createQueue();
+ queue.setTracks([track1, track2, track3], false);
+
+ // Act
+ const tracks: TrackModel[] = queue.tracksInPlaybackOrder;
+
+ // Assert
+ expect(tracks[0].path).toBe(track1.path);
+ expect(tracks[1].path).toBe(track2.path);
+ expect(tracks[2].path).toBe(track3.path);
+ });
+
+ it('should return the tracks in playback order when shuffled', () => {
+ // Arrange
+ const track1: TrackModel = createTrackModel('/home/user/Music/Track1.mp3');
+ const track2: TrackModel = createTrackModel('/home/user/Music/Track2.mp3');
+ const track3: TrackModel = createTrackModel('/home/user/Music/Track3.mp3');
+
+ shufflerMock.setup((x) => x.shuffle([0, 1, 2])).returns(() => [1, 2, 0]);
+
+ const queue: Queue = createQueue();
+ queue.setTracks([track1, track2, track3], true);
+
+ // Act
+ const tracks: TrackModel[] = queue.tracksInPlaybackOrder;
+
+ // Assert
+ expect(tracks[0].path).toBe(track2.path);
+ expect(tracks[1].path).toBe(track3.path);
+ expect(tracks[2].path).toBe(track1.path);
});
});
});
diff --git a/src/app/services/playback/queue.ts b/src/app/services/playback/queue.ts
index 64fd344d8..0ecb98265 100644
--- a/src/app/services/playback/queue.ts
+++ b/src/app/services/playback/queue.ts
@@ -14,18 +14,20 @@ export class Queue {
return this._tracks;
}
- public get numberOfTracks(): number {
- return this._tracks.length;
- }
-
- public getTracksInPlaybackOrder(): TrackModel[] {
+ public get tracksInPlaybackOrder(): TrackModel[] {
let tracksInPlaybackOrder: TrackModel[] = [];
+
for (const trackIndex of this.playbackOrder) {
tracksInPlaybackOrder.push(this._tracks[trackIndex]);
}
+
return tracksInPlaybackOrder;
}
+ public get numberOfTracks(): number {
+ return this._tracks.length;
+ }
+
public setTracks(tracksToSet: TrackModel[], shuffle: boolean): void {
this._tracks = tracksToSet;
@@ -75,6 +77,18 @@ export class Queue {
this.populatePlayBackOrder();
}
+ public getFirstTrack(): TrackModel {
+ if (this.playbackOrder == undefined) {
+ return undefined;
+ }
+
+ if (this.playbackOrder.length === 0) {
+ return undefined;
+ }
+
+ return this._tracks[this.playbackOrder[0]];
+ }
+
public getPreviousTrack(currentTrack: TrackModel, allowWrapAround: boolean): TrackModel {
if (this._tracks.length === 0) {
return undefined;
|