mirror of
https://github.com/koel/koel
synced 2025-02-17 13:58:28 +00:00
refactor: use provide/inject and composable for screen logics
This commit is contained in:
parent
aea0fabe73
commit
e89d0f93ca
58 changed files with 223 additions and 175 deletions
|
@ -32,7 +32,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('plays all', async () => {
|
||||
const songs = factory<Song[]>('song', 10)
|
||||
const songs = factory<Song>('song', 10)
|
||||
const fetchMock = this.mock(songStore, 'fetchForAlbum').mockResolvedValue(songs)
|
||||
const playMock = this.mock(playbackService, 'queueAndPlay')
|
||||
|
||||
|
@ -45,7 +45,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('shuffles all', async () => {
|
||||
const songs = factory<Song[]>('song', 10)
|
||||
const songs = factory<Song>('song', 10)
|
||||
const fetchMock = this.mock(songStore, 'fetchForAlbum').mockResolvedValue(songs)
|
||||
const playMock = this.mock(playbackService, 'queueAndPlay')
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('plays', async () => {
|
||||
const songs = factory<Song[]>('song', 3)
|
||||
const songs = factory<Song>('song', 3)
|
||||
const fetchMock = this.mock(songStore, 'fetchForAlbum').mockResolvedValue(songs)
|
||||
const playMock = this.mock(playbackService, 'queueAndPlay')
|
||||
const { getByTitle } = await this.renderComponent()
|
||||
|
|
|
@ -8,12 +8,12 @@ new class extends UnitTestCase {
|
|||
protected test () {
|
||||
it('displays the tracks', async () => {
|
||||
const album = factory<Album>('album')
|
||||
const fetchMock = this.mock(songStore, 'fetchForAlbum').mockResolvedValue(factory<Song[]>('song', 5))
|
||||
const fetchMock = this.mock(songStore, 'fetchForAlbum').mockResolvedValue(factory<Song>('song', 5))
|
||||
|
||||
const { queryAllByTestId } = this.render(AlbumTrackList, {
|
||||
props: {
|
||||
album,
|
||||
tracks: factory<AlbumTrack[]>('album-track', 3)
|
||||
tracks: factory<AlbumTrack>('album-track', 3)
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import AlbumTrackListItem from './AlbumTrackListItem.vue'
|
|||
|
||||
new class extends UnitTestCase {
|
||||
private renderComponent (matchedSong?: Song) {
|
||||
const songsToMatchAgainst = factory<Song[]>('song', 10)
|
||||
const songsToMatchAgainst = factory<Song>('song', 10)
|
||||
const album = factory<Album>('album')
|
||||
|
||||
const track = factory<AlbumTrack>('album-track', {
|
||||
|
|
|
@ -60,7 +60,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('shuffles', async () => {
|
||||
const songs = factory<Song[]>('song', 16)
|
||||
const songs = factory<Song>('song', 16)
|
||||
const fetchMock = this.mock(songStore, 'fetchForArtist').mockResolvedValue(songs)
|
||||
const playMock = this.mock(playbackService, 'queueAndPlay')
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('plays all', async () => {
|
||||
const songs = factory<Song[]>('song', 10)
|
||||
const songs = factory<Song>('song', 10)
|
||||
const fetchMock = this.mock(songStore, 'fetchForArtist').mockResolvedValue(songs)
|
||||
const playMock = this.mock(playbackService, 'queueAndPlay')
|
||||
|
||||
|
@ -45,7 +45,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('shuffles all', async () => {
|
||||
const songs = factory<Song[]>('song', 10)
|
||||
const songs = factory<Song>('song', 10)
|
||||
const fetchMock = this.mock(songStore, 'fetchForArtist').mockResolvedValue(songs)
|
||||
const playMock = this.mock(playbackService, 'queueAndPlay')
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('plays', async () => {
|
||||
const songs = factory<Song[]>('song', 3)
|
||||
const songs = factory<Song>('song', 3)
|
||||
const fetchMock = this.mock(songStore, 'fetchForArtist').mockResolvedValue(songs)
|
||||
const playMock = this.mock(playbackService, 'queueAndPlay')
|
||||
const { getByTitle } = await this.renderComponent()
|
||||
|
|
|
@ -4,13 +4,13 @@ import factory from '@/__tests__/factory'
|
|||
import { eventBus } from '@/utils'
|
||||
import { albumStore, preferenceStore } from '@/stores'
|
||||
import UnitTestCase from '@/__tests__/UnitTestCase'
|
||||
import MainContent from '@/components/layout/main-wrapper/MainContent.vue'
|
||||
import AlbumArtOverlay from '@/components/ui/AlbumArtOverlay.vue'
|
||||
import MainContent from './MainContent.vue'
|
||||
|
||||
new class extends UnitTestCase {
|
||||
protected test () {
|
||||
it('has a translucent overlay per album', async () => {
|
||||
this.mock(albumStore, 'fetchThumbnail').mockResolvedValue('https://foo/bar.jpg')
|
||||
this.mock(albumStore, 'fetchThumbnail').mockResolvedValue('http://localhost/foo.jpg')
|
||||
|
||||
const { getByTestId } = this.render(MainContent, {
|
||||
global: {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { ref } from 'vue'
|
||||
import { expect, it } from 'vitest'
|
||||
import factory from '@/__tests__/factory'
|
||||
import UnitTestCase from '@/__tests__/UnitTestCase'
|
||||
import { albumStore, preferenceStore } from '@/stores'
|
||||
import { eventBus } from '@/utils'
|
||||
import { fireEvent, waitFor } from '@testing-library/vue'
|
||||
import { ActiveScreenKey } from '@/symbols'
|
||||
import AlbumListScreen from './AlbumListScreen.vue'
|
||||
|
||||
new class extends UnitTestCase {
|
||||
|
@ -12,8 +13,14 @@ new class extends UnitTestCase {
|
|||
}
|
||||
|
||||
private renderComponent () {
|
||||
albumStore.state.albums = factory<Album[]>('album', 9)
|
||||
return this.render(AlbumListScreen)
|
||||
albumStore.state.albums = factory<Album>('album', 9)
|
||||
return this.render(AlbumListScreen, {
|
||||
global: {
|
||||
provide: {
|
||||
[<symbol>ActiveScreenKey]: ref('Albums')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
protected test () {
|
||||
|
@ -25,7 +32,6 @@ new class extends UnitTestCase {
|
|||
preferenceStore.albumsViewMode = mode
|
||||
|
||||
const { getByTestId } = this.renderComponent()
|
||||
eventBus.emit('ACTIVATE_SCREEN', 'Albums')
|
||||
|
||||
await waitFor(() => expect(getByTestId('album-list').classList.contains(`as-${mode}`)).toBe(true))
|
||||
})
|
||||
|
|
|
@ -27,9 +27,8 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, toRef, watch } from 'vue'
|
||||
import { eventBus } from '@/utils'
|
||||
import { albumStore, preferenceStore as preferences } from '@/stores'
|
||||
import { useInfiniteScroll } from '@/composables'
|
||||
import { useInfiniteScroll, useScreen } from '@/composables'
|
||||
|
||||
import AlbumCard from '@/components/album/AlbumCard.vue'
|
||||
import AlbumCardSkeleton from '@/components/ui/skeletons/ArtistAlbumCardSkeleton.vue'
|
||||
|
@ -64,11 +63,11 @@ const fetchAlbums = async () => {
|
|||
loading.value = false
|
||||
}
|
||||
|
||||
eventBus.on('ACTIVATE_SCREEN', async (screen: ScreenName) => {
|
||||
if (screen === 'Albums' && !initialized) {
|
||||
useScreen('Albums').onScreenActivated(async () => {
|
||||
if (!initialized) {
|
||||
viewMode.value = preferences.albumsViewMode || 'thumbnails'
|
||||
await makeScrollable()
|
||||
initialized = true
|
||||
await makeScrollable()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -26,7 +26,7 @@ new class extends UnitTestCase {
|
|||
|
||||
const resolveAlbumMock = this.mock(albumStore, 'resolve').mockResolvedValue(album)
|
||||
|
||||
const songs = factory<Song[]>('song', 13)
|
||||
const songs = factory<Song>('song', 13)
|
||||
const fetchSongsMock = this.mock(songStore, 'fetchForAlbum').mockResolvedValue(songs)
|
||||
|
||||
const rendered = this.render(AlbumScreen, {
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { ref } from 'vue'
|
||||
import { expect, it } from 'vitest'
|
||||
import factory from '@/__tests__/factory'
|
||||
import UnitTestCase from '@/__tests__/UnitTestCase'
|
||||
import { commonStore, queueStore, songStore } from '@/stores'
|
||||
import { fireEvent, waitFor } from '@testing-library/vue'
|
||||
import { eventBus } from '@/utils'
|
||||
import { playbackService } from '@/services'
|
||||
import router from '@/router'
|
||||
import { ActiveScreenKey } from '@/symbols'
|
||||
import AllSongsScreen from './AllSongsScreen.vue'
|
||||
|
||||
new class extends UnitTestCase {
|
||||
|
@ -19,12 +20,13 @@ new class extends UnitTestCase {
|
|||
global: {
|
||||
stubs: {
|
||||
SongList: this.stub('song-list')
|
||||
},
|
||||
provide: {
|
||||
[<symbol>ActiveScreenKey]: ref('Songs')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
eventBus.emit('ACTIVATE_SCREEN', 'Songs')
|
||||
|
||||
await waitFor(() => expect(fetchMock).toHaveBeenCalledWith('title', 'asc', 1))
|
||||
return rendered
|
||||
}
|
||||
|
|
|
@ -36,10 +36,10 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, toRef } from 'vue'
|
||||
import { eventBus, pluralize, secondsToHis } from '@/utils'
|
||||
import { pluralize, secondsToHis } from '@/utils'
|
||||
import { commonStore, queueStore, songStore } from '@/stores'
|
||||
import { playbackService } from '@/services'
|
||||
import { useSongList } from '@/composables'
|
||||
import { useScreen, useSongList } from '@/composables'
|
||||
import router from '@/router'
|
||||
|
||||
import ScreenHeader from '@/components/ui/ScreenHeader.vue'
|
||||
|
@ -102,10 +102,10 @@ const playAll = async (shuffle: boolean) => {
|
|||
await router.go('queue')
|
||||
}
|
||||
|
||||
eventBus.on('ACTIVATE_SCREEN', async (screen: ScreenName) => {
|
||||
if (screen === 'Songs' && !initialized) {
|
||||
await fetchSongs()
|
||||
useScreen('Songs').onScreenActivated(async () => {
|
||||
if (!initialized) {
|
||||
initialized = true
|
||||
await fetchSongs()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { ref } from 'vue'
|
||||
import { expect, it } from 'vitest'
|
||||
import factory from '@/__tests__/factory'
|
||||
import UnitTestCase from '@/__tests__/UnitTestCase'
|
||||
import { artistStore, preferenceStore } from '@/stores'
|
||||
import { eventBus } from '@/utils'
|
||||
import { fireEvent, waitFor } from '@testing-library/vue'
|
||||
import { ActiveScreenKey } from '@/symbols'
|
||||
import ArtistListScreen from './ArtistListScreen.vue'
|
||||
|
||||
new class extends UnitTestCase {
|
||||
|
@ -12,8 +13,14 @@ new class extends UnitTestCase {
|
|||
}
|
||||
|
||||
private renderComponent () {
|
||||
artistStore.state.artists = factory<Artist[]>('artist', 9)
|
||||
return this.render(ArtistListScreen)
|
||||
artistStore.state.artists = factory<Artist>('artist', 9)
|
||||
return this.render(ArtistListScreen, {
|
||||
global: {
|
||||
provide: {
|
||||
[<symbol>ActiveScreenKey]: ref('Artists')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
protected test () {
|
||||
|
@ -21,11 +28,10 @@ new class extends UnitTestCase {
|
|||
expect(this.renderComponent().getAllByTestId('artist-card')).toHaveLength(9)
|
||||
})
|
||||
|
||||
it.each<[ArtistAlbumViewMode]>([['list'], ['thumbnails']])('sets layout from preferences', async (mode) => {
|
||||
it.each<[ArtistAlbumViewMode]>([['list'], ['thumbnails']])('sets layout:%s from preferences', async (mode) => {
|
||||
preferenceStore.artistsViewMode = mode
|
||||
|
||||
const { getByTestId } = this.renderComponent()
|
||||
eventBus.emit('ACTIVATE_SCREEN', 'Artists')
|
||||
const { getByTestId, html } = this.renderComponent()
|
||||
|
||||
await waitFor(() => expect(getByTestId('artist-list').classList.contains(`as-${mode}`)).toBe(true))
|
||||
})
|
||||
|
|
|
@ -27,9 +27,8 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, toRef, watch } from 'vue'
|
||||
import { eventBus } from '@/utils'
|
||||
import { artistStore, preferenceStore as preferences } from '@/stores'
|
||||
import { useInfiniteScroll } from '@/composables'
|
||||
import { useInfiniteScroll, useScreen } from '@/composables'
|
||||
|
||||
import ArtistCard from '@/components/artist/ArtistCard.vue'
|
||||
import ArtistCardSkeleton from '@/components/ui/skeletons/ArtistAlbumCardSkeleton.vue'
|
||||
|
@ -64,11 +63,11 @@ const fetchArtists = async () => {
|
|||
loading.value = false
|
||||
}
|
||||
|
||||
eventBus.on('ACTIVATE_SCREEN', async (screen: ScreenName) => {
|
||||
if (screen === 'Artists' && !initialized) {
|
||||
useScreen('Artists').onScreenActivated(async () => {
|
||||
if (!initialized) {
|
||||
viewMode.value = preferences.artistsViewMode || 'thumbnails'
|
||||
await makeScrollable()
|
||||
initialized = true
|
||||
await makeScrollable()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -25,7 +25,7 @@ new class extends UnitTestCase {
|
|||
|
||||
const resolveArtistMock = this.mock(artistStore, 'resolve').mockResolvedValue(artist)
|
||||
|
||||
const songs = factory<Song[]>('song', 13)
|
||||
const songs = factory<Song>('song', 13)
|
||||
const fetchSongsMock = this.mock(songStore, 'fetchForArtist').mockResolvedValue(songs)
|
||||
|
||||
const rendered = this.render(ArtistScreen, {
|
||||
|
|
|
@ -1,17 +1,23 @@
|
|||
import { ref } from 'vue'
|
||||
import { waitFor } from '@testing-library/vue'
|
||||
import { expect, it } from 'vitest'
|
||||
import factory from '@/__tests__/factory'
|
||||
import UnitTestCase from '@/__tests__/UnitTestCase'
|
||||
import { favoriteStore } from '@/stores'
|
||||
import { ActiveScreenKey } from '@/symbols'
|
||||
import FavoritesScreen from './FavoritesScreen.vue'
|
||||
import { eventBus } from '@/utils'
|
||||
|
||||
new class extends UnitTestCase {
|
||||
private async renderComponent () {
|
||||
const fetchMock = this.mock(favoriteStore, 'fetch')
|
||||
const rendered = this.render(FavoritesScreen)
|
||||
const rendered = this.render(FavoritesScreen, {
|
||||
global: {
|
||||
provide: {
|
||||
[<symbol>ActiveScreenKey]: ref('Favorites')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
eventBus.emit('ACTIVATE_SCREEN', 'Favorites')
|
||||
await waitFor(() => expect(fetchMock).toHaveBeenCalled())
|
||||
|
||||
return rendered
|
||||
|
|
|
@ -60,10 +60,10 @@
|
|||
<script lang="ts" setup>
|
||||
import { faHeartBroken } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faHeart } from '@fortawesome/free-regular-svg-icons'
|
||||
import { eventBus, pluralize } from '@/utils'
|
||||
import { pluralize } from '@/utils'
|
||||
import { commonStore, favoriteStore } from '@/stores'
|
||||
import { downloadService } from '@/services'
|
||||
import { useSongList } from '@/composables'
|
||||
import { useScreen, useSongList } from '@/composables'
|
||||
import { nextTick, ref, toRef } from 'vue'
|
||||
|
||||
import ScreenHeader from '@/components/ui/ScreenHeader.vue'
|
||||
|
@ -106,10 +106,10 @@ const fetchSongs = async () => {
|
|||
sort()
|
||||
}
|
||||
|
||||
eventBus.on('ACTIVATE_SCREEN', async (screen: ScreenName) => {
|
||||
if (screen === 'Favorites' && !initialized) {
|
||||
await fetchSongs()
|
||||
useScreen('Favorites').onScreenActivated(async () => {
|
||||
if (!initialized) {
|
||||
initialized = true
|
||||
await fetchSongs()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -1,18 +1,31 @@
|
|||
import { ref } from 'vue'
|
||||
import { expect, it } from 'vitest'
|
||||
import UnitTestCase from '@/__tests__/UnitTestCase'
|
||||
import HomeScreen from './HomeScreen.vue'
|
||||
import { commonStore } from '@/stores'
|
||||
import { ActiveScreenKey } from '@/symbols'
|
||||
import HomeScreen from './HomeScreen.vue'
|
||||
|
||||
new class extends UnitTestCase {
|
||||
private renderComponent () {
|
||||
return this.render(HomeScreen, {
|
||||
global: {
|
||||
provide: {
|
||||
[<symbol>ActiveScreenKey]: ref('Home')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
protected test () {
|
||||
it('renders an empty state if no songs found', () => {
|
||||
commonStore.state.song_length = 0
|
||||
this.render(HomeScreen).getByTestId('screen-empty-state')
|
||||
this.renderComponent().getByTestId('screen-empty-state')
|
||||
})
|
||||
|
||||
it('renders overview components if applicable', () => {
|
||||
commonStore.state.song_length = 100
|
||||
const { getByTestId, queryByTestId } = this.render(HomeScreen)
|
||||
|
||||
const { getByTestId, queryByTestId } = this.renderComponent()
|
||||
|
||||
;[
|
||||
'most-played-songs',
|
||||
|
@ -21,7 +34,7 @@ new class extends UnitTestCase {
|
|||
'recently-added-songs',
|
||||
'most-played-artists',
|
||||
'most-played-albums'
|
||||
].forEach(getByTestId)
|
||||
].forEach(id => getByTestId(id))
|
||||
|
||||
expect(queryByTestId('screen-empty-state')).toBeNull()
|
||||
})
|
||||
|
|
|
@ -37,9 +37,9 @@
|
|||
import { faVolumeOff } from '@fortawesome/free-solid-svg-icons'
|
||||
import { sample } from 'lodash'
|
||||
import { computed, ref } from 'vue'
|
||||
import { eventBus, noop } from '@/utils'
|
||||
import { noop } from '@/utils'
|
||||
import { commonStore, overviewStore, userStore } from '@/stores'
|
||||
import { useAuthorization, useInfiniteScroll } from '@/composables'
|
||||
import { useAuthorization, useInfiniteScroll, useScreen } from '@/composables'
|
||||
|
||||
import MostPlayedSongs from '@/components/screens/home/MostPlayedSongs.vue'
|
||||
import RecentlyPlayedSongs from '@/components/screens/home/RecentlyPlayedSongs.vue'
|
||||
|
@ -72,8 +72,8 @@ const libraryEmpty = computed(() => commonStore.state.song_length === 0)
|
|||
const loading = ref(false)
|
||||
let initialized = false
|
||||
|
||||
eventBus.on('ACTIVATE_SCREEN', async (view: ScreenName) => {
|
||||
if (view === 'Home' && !initialized) {
|
||||
useScreen('Home').onScreenActivated(async () => {
|
||||
if (!initialized) {
|
||||
loading.value = true
|
||||
await overviewStore.init()
|
||||
initialized = true
|
||||
|
|
|
@ -24,7 +24,7 @@ new class extends UnitTestCase {
|
|||
|
||||
protected test () {
|
||||
it('renders the playlist', async () => {
|
||||
const { getByTestId, queryByTestId } = (await this.renderComponent(factory<Song[]>('song', 10))).rendered
|
||||
const { getByTestId, queryByTestId } = (await this.renderComponent(factory<Song>('song', 10))).rendered
|
||||
|
||||
await waitFor(() => {
|
||||
getByTestId('song-list')
|
||||
|
@ -43,7 +43,7 @@ new class extends UnitTestCase {
|
|||
|
||||
it('downloads the playlist', async () => {
|
||||
const downloadMock = this.mock(downloadService, 'fromPlaylist')
|
||||
const { getByText } = (await this.renderComponent(factory<Song[]>('song', 10))).rendered
|
||||
const { getByText } = (await this.renderComponent(factory<Song>('song', 10))).rendered
|
||||
|
||||
await this.tick()
|
||||
await fireEvent.click(getByText('Download All'))
|
||||
|
|
|
@ -21,7 +21,7 @@ new class extends UnitTestCase {
|
|||
|
||||
protected test () {
|
||||
it('renders the queue', () => {
|
||||
const { queryByTestId } = this.renderComponent(factory<Song[]>('song', 3))
|
||||
const { queryByTestId } = this.renderComponent(factory<Song>('song', 3))
|
||||
|
||||
expect(queryByTestId('song-list')).toBeTruthy()
|
||||
expect(queryByTestId('screen-empty-state')).toBeNull()
|
||||
|
@ -49,7 +49,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('Shuffles all', async () => {
|
||||
const songs = factory<Song[]>('song', 3)
|
||||
const songs = factory<Song>('song', 3)
|
||||
const { getByTitle } = this.renderComponent(songs)
|
||||
const playMock = this.mock(playbackService, 'queueAndPlay')
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { faCoffee } from '@fortawesome/free-solid-svg-icons'
|
||||
import { computed, ref, toRef, toRefs } from 'vue'
|
||||
import { computed, ref, toRef } from 'vue'
|
||||
import { eventBus, logger, pluralize, requireInjection } from '@/utils'
|
||||
import { commonStore, queueStore, songStore } from '@/stores'
|
||||
import { playbackService } from '@/services'
|
||||
|
@ -64,9 +64,6 @@ import SongListSkeleton from '@/components/ui/skeletons/SongListSkeleton.vue'
|
|||
const dialog = requireInjection(DialogBoxKey)
|
||||
const controlConfig: Partial<SongListControlsConfig> = { clearQueue: true }
|
||||
|
||||
const props = defineProps<{ song?: string }>()
|
||||
const { song: queuedSongId } = toRefs(props)
|
||||
|
||||
const {
|
||||
SongList,
|
||||
SongListControls,
|
||||
|
@ -108,7 +105,7 @@ const onPressEnter = () => selectedSongs.value.length && playbackService.play(se
|
|||
const onReorder = (target: Song) => queueStore.move(selectedSongs.value, target)
|
||||
|
||||
eventBus.on('SONG_QUEUED_FROM_ROUTE', async (id: string) => {
|
||||
let song: Song
|
||||
let song: Song | undefined
|
||||
|
||||
try {
|
||||
loading.value = true
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { ref } from 'vue'
|
||||
import { expect, it } from 'vitest'
|
||||
import factory from '@/__tests__/factory'
|
||||
import UnitTestCase from '@/__tests__/UnitTestCase'
|
||||
import { recentlyPlayedStore } from '@/stores'
|
||||
import { eventBus } from '@/utils'
|
||||
import { waitFor } from '@testing-library/vue'
|
||||
import { ActiveScreenKey } from '@/symbols'
|
||||
import RecentlyPlayedScreen from './RecentlyPlayedScreen.vue'
|
||||
|
||||
new class extends UnitTestCase {
|
||||
|
@ -15,12 +16,13 @@ new class extends UnitTestCase {
|
|||
global: {
|
||||
stubs: {
|
||||
SongList: this.stub('song-list')
|
||||
},
|
||||
provide: {
|
||||
[<symbol>ActiveScreenKey]: ref('RecentlyPlayed')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
eventBus.emit('ACTIVATE_SCREEN', 'RecentlyPlayed')
|
||||
|
||||
await waitFor(() => expect(fetchMock).toHaveBeenCalled())
|
||||
|
||||
return rendered
|
||||
|
@ -28,7 +30,7 @@ new class extends UnitTestCase {
|
|||
|
||||
protected test () {
|
||||
it('displays the songs', async () => {
|
||||
const { queryByTestId } = await this.renderComponent(factory<Song[]>('song', 3))
|
||||
const { queryByTestId } = await this.renderComponent(factory<Song>('song', 3))
|
||||
|
||||
expect(queryByTestId('song-list')).toBeTruthy()
|
||||
expect(queryByTestId('screen-empty-state')).toBeNull()
|
||||
|
|
|
@ -37,9 +37,9 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { faClock } from '@fortawesome/free-regular-svg-icons'
|
||||
import { eventBus, pluralize } from '@/utils'
|
||||
import { pluralize } from '@/utils'
|
||||
import { recentlyPlayedStore } from '@/stores'
|
||||
import { useSongList } from '@/composables'
|
||||
import { useScreen, useSongList } from '@/composables'
|
||||
import { ref, toRef } from 'vue'
|
||||
|
||||
import ScreenHeader from '@/components/ui/ScreenHeader.vue'
|
||||
|
@ -69,14 +69,12 @@ const {
|
|||
let initialized = false
|
||||
let loading = ref(false)
|
||||
|
||||
eventBus.on({
|
||||
ACTIVATE_SCREEN: async (screen: ScreenName) => {
|
||||
if (screen === 'RecentlyPlayed' && !initialized) {
|
||||
loading.value = true
|
||||
await recentlyPlayedStore.fetch()
|
||||
loading.value = false
|
||||
initialized = true
|
||||
}
|
||||
useScreen('RecentlyPlayed').onScreenActivated(async () => {
|
||||
if (!initialized) {
|
||||
loading.value = true
|
||||
initialized = true
|
||||
await recentlyPlayedStore.fetch()
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -7,7 +7,7 @@ import MostPlayedAlbums from './MostPlayedAlbums.vue'
|
|||
new class extends UnitTestCase {
|
||||
protected test () {
|
||||
it('displays the albums', () => {
|
||||
overviewStore.state.mostPlayedAlbums = factory<Album[]>('album', 6)
|
||||
overviewStore.state.mostPlayedAlbums = factory<Album>('album', 6)
|
||||
expect(this.render(MostPlayedAlbums).getAllByTestId('album-card')).toHaveLength(6)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import MostPlayedArtists from './MostPlayedArtists.vue'
|
|||
new class extends UnitTestCase {
|
||||
protected test () {
|
||||
it('displays the artists', () => {
|
||||
overviewStore.state.mostPlayedArtists = factory<Artist[]>('artist', 6)
|
||||
overviewStore.state.mostPlayedArtists = factory<Artist>('artist', 6)
|
||||
expect(this.render(MostPlayedArtists).getAllByTestId('artist-card')).toHaveLength(6)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import MostPlayedSongs from './MostPlayedSongs.vue'
|
|||
new class extends UnitTestCase {
|
||||
protected test () {
|
||||
it('displays the songs', () => {
|
||||
overviewStore.state.mostPlayedSongs = factory<Song[]>('song', 6)
|
||||
overviewStore.state.mostPlayedSongs = factory<Song>('song', 6)
|
||||
expect(this.render(MostPlayedSongs).getAllByTestId('song-card')).toHaveLength(6)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import RecentlyAddedAlbums from './RecentlyAddedAlbums.vue'
|
|||
new class extends UnitTestCase {
|
||||
protected test () {
|
||||
it('displays the albums', () => {
|
||||
overviewStore.state.recentlyAddedAlbums = factory<Album[]>('album', 6)
|
||||
overviewStore.state.recentlyAddedAlbums = factory<Album>('album', 6)
|
||||
expect(this.render(RecentlyAddedAlbums).getAllByTestId('album-card')).toHaveLength(6)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import RecentlyAddedSongs from './RecentlyAddedSongs.vue'
|
|||
new class extends UnitTestCase {
|
||||
protected test () {
|
||||
it('displays the songs', () => {
|
||||
overviewStore.state.recentlyAddedSongs = factory<Song[]>('song', 6)
|
||||
overviewStore.state.recentlyAddedSongs = factory<Song>('song', 6)
|
||||
expect(this.render(RecentlyAddedSongs).getAllByTestId('song-card')).toHaveLength(6)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import router from '@/router'
|
|||
new class extends UnitTestCase {
|
||||
protected test () {
|
||||
it('displays the songs', () => {
|
||||
recentlyPlayedStore.excerptState.songs = factory<Song[]>('song', 6)
|
||||
recentlyPlayedStore.excerptState.songs = factory<Song>('song', 6)
|
||||
expect(this.render(RecentlyPlayedSongs).getAllByTestId('song-card')).toHaveLength(6)
|
||||
})
|
||||
|
||||
|
|
|
@ -62,7 +62,8 @@ new class extends UnitTestCase {
|
|||
['to bottom', 'queue-bottom', 'queue']
|
||||
])('queues songs %s', async (_: string, testId: string, queueMethod: MethodOf<typeof queueStore>) => {
|
||||
queueStore.state.songs = factory<Song>('song', 5)
|
||||
queueStore.state.current = queueStore.state.songs[1]
|
||||
queueStore.state.songs[2].playback_state = 'Playing'
|
||||
|
||||
const mock = this.mock(queueStore, queueMethod)
|
||||
const { getByTestId } = this.renderComponent()
|
||||
|
||||
|
|
|
@ -90,7 +90,7 @@ const { songs, showing, config } = toRefs(props)
|
|||
|
||||
const newPlaylistName = ref('')
|
||||
const queue = toRef(queueStore.state, 'songs')
|
||||
const currentSong = toRef(queueStore.state, 'current')
|
||||
const currentSong = queueStore.current
|
||||
|
||||
const allPlaylists = toRef(playlistStore.state, 'playlists')
|
||||
const playlists = computed(() => allPlaylists.value.filter(playlist => !playlist.is_smart))
|
||||
|
|
|
@ -38,7 +38,7 @@ new class extends UnitTestCase {
|
|||
title: 'Rocket to Heaven',
|
||||
artist_name: 'Led Zeppelin',
|
||||
album_name: 'IV',
|
||||
album_cover: 'https://example.co/album.jpg'
|
||||
album_cover: 'http://localhost/album.jpg'
|
||||
}))
|
||||
|
||||
expect(html()).toMatchSnapshot()
|
||||
|
|
|
@ -72,6 +72,7 @@ const currentSong = queueStore.current
|
|||
const onlyOneSongSelected = computed(() => songs.value.length === 1)
|
||||
const firstSongPlaying = computed(() => songs.value.length ? songs.value[0].playback_state === 'Playing' : false)
|
||||
const normalPlaylists = computed(() => playlists.value.filter(playlist => !playlist.is_smart))
|
||||
|
||||
const { isAdmin } = useAuthorization()
|
||||
|
||||
const doPlayback = () => trigger(() => {
|
||||
|
|
|
@ -9,7 +9,7 @@ import SongListControls from './SongListControls.vue'
|
|||
|
||||
new class extends UnitTestCase {
|
||||
private renderComponent (selectedSongCount = 1, config: Partial<SongListControlsConfig> = {}) {
|
||||
const songs = factory<Song[]>('song', 5)
|
||||
const songs = factory<Song>('song', 5)
|
||||
|
||||
return this.render(SongListControls, {
|
||||
props: {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
exports[`edits a single song 1`] = `
|
||||
<div class="edit-song" data-testid="edit-song-form" tabindex="0" data-v-210b4214="">
|
||||
<form data-v-210b4214="">
|
||||
<header data-v-210b4214=""><span class="cover" style="background-image: url(https://example.co/album.jpg);" data-v-210b4214=""></span>
|
||||
<header data-v-210b4214=""><span class="cover" style="background-image: url(http://localhost/album.jpg);" data-v-210b4214=""></span>
|
||||
<div class="meta" data-v-210b4214="">
|
||||
<h1 class="" data-v-210b4214="">Rocket to Heaven</h1>
|
||||
<h2 data-testid="displayed-artist-name" class="" data-v-210b4214="">Led Zeppelin</h2>
|
||||
|
|
|
@ -23,7 +23,7 @@ new class extends UnitTestCase {
|
|||
|
||||
protected test () {
|
||||
it('fetches and displays the album thumbnail', async () => {
|
||||
const fetchMock = this.mock(albumStore, 'fetchThumbnail').mockResolvedValue('https://localhost/thumb.jpg')
|
||||
const fetchMock = this.mock(albumStore, 'fetchThumbnail').mockResolvedValue('http://localhost/thumb.jpg')
|
||||
|
||||
const { html } = await this.renderComponent()
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('plays album', async () => {
|
||||
const songs = factory<Song[]>('song', 10)
|
||||
const songs = factory<Song>('song', 10)
|
||||
const fetchMock = this.mock(songStore, 'fetchForAlbum').mockResolvedValue(songs)
|
||||
const playMock = this.mock(playbackService, 'queueAndPlay')
|
||||
const { getByRole } = this.renderForAlbum()
|
||||
|
@ -61,7 +61,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('queues album', async () => {
|
||||
const songs = factory<Song[]>('song', 10)
|
||||
const songs = factory<Song>('song', 10)
|
||||
const fetchMock = this.mock(songStore, 'fetchForAlbum').mockResolvedValue(songs)
|
||||
const queueMock = this.mock(queueStore, 'queue')
|
||||
const { getByRole } = this.renderForAlbum()
|
||||
|
@ -75,7 +75,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('plays artist', async () => {
|
||||
const songs = factory<Song[]>('song', 10)
|
||||
const songs = factory<Song>('song', 10)
|
||||
const fetchMock = this.mock(songStore, 'fetchForArtist').mockResolvedValue(songs)
|
||||
const playMock = this.mock(playbackService, 'queueAndPlay')
|
||||
const { getByRole } = this.renderForArtist()
|
||||
|
@ -89,7 +89,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('queues artist', async () => {
|
||||
const songs = factory<Song[]>('song', 10)
|
||||
const songs = factory<Song>('song', 10)
|
||||
const fetchMock = this.mock(songStore, 'fetchForArtist').mockResolvedValue(songs)
|
||||
const queueMock = this.mock(queueStore, 'queue')
|
||||
const { getByRole } = this.renderForArtist()
|
||||
|
|
|
@ -14,10 +14,10 @@ new class extends UnitTestCase {
|
|||
|
||||
const searchMock = this.mock(youTubeService, 'searchVideosBySong').mockResolvedValueOnce({
|
||||
nextPageToken: 'foo',
|
||||
items: factory<YouTubeVideo[]>('video', 5)
|
||||
items: factory<YouTubeVideo>('video', 5)
|
||||
}).mockResolvedValueOnce({
|
||||
nextPageToken: 'bar',
|
||||
items: factory<YouTubeVideo[]>('video', 3)
|
||||
items: factory<YouTubeVideo>('video', 3)
|
||||
})
|
||||
|
||||
const { getAllByTestId, getByRole } = this.render(YouTubeVideoList, {
|
||||
|
|
|
@ -3,3 +3,5 @@
|
|||
exports[`displays nothing if fetching fails 1`] = `<div style="background-image: none;" data-testid="album-art-overlay" data-v-75d06710=""></div>`;
|
||||
|
||||
exports[`fetches and displays the album thumbnail 1`] = `<div style="background-image: url(https://localhost/thumb.jpg);" data-testid="album-art-overlay" data-v-75d06710=""></div>`;
|
||||
|
||||
exports[`fetches and displays the album thumbnail 2`] = `<div style="background-image: url(http://localhost/thumb.jpg);" data-testid="album-art-overlay" data-v-75d06710=""></div>`;
|
||||
|
|
|
@ -7,3 +7,4 @@ export * from './useNewVersionNotification'
|
|||
export * from './useThirdPartyServices'
|
||||
export * from './useDragAndDrop'
|
||||
export * from './useUpload'
|
||||
export * from './useScreen'
|
||||
|
|
|
@ -5,5 +5,8 @@ export const useAuthorization = () => {
|
|||
const currentUser = toRef(userStore.state, 'current')
|
||||
const isAdmin = computed(() => currentUser.value?.is_admin)
|
||||
|
||||
return { currentUser, isAdmin }
|
||||
return {
|
||||
currentUser,
|
||||
isAdmin
|
||||
}
|
||||
}
|
||||
|
|
16
resources/assets/js/composables/useScreen.ts
Normal file
16
resources/assets/js/composables/useScreen.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { ref, watch } from 'vue'
|
||||
import { requireInjection } from '@/utils'
|
||||
import { ActiveScreenKey } from '@/symbols'
|
||||
|
||||
export const useScreen = (currentScreen: ScreenName) => {
|
||||
const activeScreen = requireInjection(ActiveScreenKey, ref('Home'))
|
||||
|
||||
const onScreenActivated = (cb: Closure) => watch(activeScreen, screen => screen === currentScreen && cb(), {
|
||||
immediate: true
|
||||
})
|
||||
|
||||
return {
|
||||
activeScreen,
|
||||
onScreenActivated
|
||||
}
|
||||
}
|
|
@ -20,11 +20,7 @@ import SongListControls from '@/components/song/SongListControls.vue'
|
|||
import ThumbnailStack from '@/components/ui/ThumbnailStack.vue'
|
||||
import { provideReadonly } from '@/utils'
|
||||
|
||||
export const useSongList = (
|
||||
songs: Ref<Song[]>,
|
||||
type: SongListType,
|
||||
config: Partial<SongListConfig> = {}
|
||||
) => {
|
||||
export const useSongList = (songs: Ref<Song[]>, type: SongListType, config: Partial<SongListConfig> = {}) => {
|
||||
const songList = ref<InstanceType<typeof SongList>>()
|
||||
|
||||
const isPhone = isMobile.phone
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import isMobile from 'ismobilejs'
|
||||
import { computed, toRef } from 'vue'
|
||||
import { computed, ref, toRef } from 'vue'
|
||||
import { useAuthorization } from '@/composables/useAuthorization'
|
||||
import { settingStore } from '@/stores'
|
||||
import { acceptedMediaTypes, UploadFile } from '@/config'
|
||||
|
@ -11,7 +11,7 @@ import router from '@/router'
|
|||
export const useUpload = () => {
|
||||
const { isAdmin } = useAuthorization()
|
||||
|
||||
const activeScreen = requireInjection(ActiveScreenKey)
|
||||
const activeScreen = requireInjection(ActiveScreenKey, ref('Home'))
|
||||
const toaster = requireInjection(MessageToasterKey)
|
||||
const mediaPath = toRef(settingStore.state, 'media_path')
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('removes albums by IDs', () => {
|
||||
const albums = factory<Album[]>('album', 3)
|
||||
const albums = factory<Album>('album', 3)
|
||||
albums.forEach(album => albumStore.vault.set(album.id, album))
|
||||
albumStore.state.albums = albums
|
||||
|
||||
|
@ -50,32 +50,32 @@ new class extends UnitTestCase {
|
|||
albumStore.syncWithVault(album)
|
||||
|
||||
expect(albumStore.vault.size).toBe(1)
|
||||
expect(albumStore.vault.get(album.id).name).toBe('V')
|
||||
expect(albumStore.vault.get(album.id)?.name).toBe('V')
|
||||
})
|
||||
|
||||
it('uploads a cover for an album', async () => {
|
||||
const album = factory<Album>('album')
|
||||
albumStore.syncWithVault(album)
|
||||
const songsInAlbum = factory<Song[]>('song', 3, { album_id: album.id })
|
||||
const putMock = this.mock(httpService, 'put').mockResolvedValue({ coverUrl: 'https://foo/cover.jpg' })
|
||||
const songsInAlbum = factory<Song>('song', 3, { album_id: album.id })
|
||||
const putMock = this.mock(httpService, 'put').mockResolvedValue({ coverUrl: 'http://localhost/cover.jpg' })
|
||||
this.mock(songStore, 'byAlbum', songsInAlbum)
|
||||
|
||||
await albumStore.uploadCover(album, 'data://cover')
|
||||
|
||||
expect(album.cover).toBe('https://foo/cover.jpg')
|
||||
expect(album.cover).toBe('http://localhost/cover.jpg')
|
||||
expect(putMock).toHaveBeenCalledWith(`album/${album.id}/cover`, { cover: 'data://cover' })
|
||||
expect(albumStore.byId(album.id).cover).toBe('https://foo/cover.jpg')
|
||||
songsInAlbum.forEach(song => expect(song.album_cover).toBe('https://foo/cover.jpg'))
|
||||
expect(albumStore.byId(album.id)?.cover).toBe('http://localhost/cover.jpg')
|
||||
songsInAlbum.forEach(song => expect(song.album_cover).toBe('http://localhost/cover.jpg'))
|
||||
})
|
||||
|
||||
it('fetches an album thumbnail', async () => {
|
||||
const getMock = this.mock(httpService, 'get').mockResolvedValue({ thumbnailUrl: 'https://foo/thumbnail.jpg' })
|
||||
const getMock = this.mock(httpService, 'get').mockResolvedValue({ thumbnailUrl: 'http://localhost/thumbnail.jpg' })
|
||||
const album = factory<Album>('album')
|
||||
|
||||
const url = await albumStore.fetchThumbnail(album.id)
|
||||
|
||||
expect(getMock).toHaveBeenCalledWith(`album/${album.id}/thumbnail`)
|
||||
expect(url).toBe('https://foo/thumbnail.jpg')
|
||||
expect(url).toBe('http://localhost/thumbnail.jpg')
|
||||
})
|
||||
|
||||
it('resolves an album', async () => {
|
||||
|
@ -91,7 +91,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('paginates', async () => {
|
||||
const albums = factory<Album[]>('album', 3)
|
||||
const albums = factory<Album>('album', 3)
|
||||
|
||||
this.mock(httpService, 'get').mockResolvedValueOnce({
|
||||
data: albums,
|
||||
|
|
|
@ -10,7 +10,7 @@ export const albumStore = {
|
|||
vault: new Map<number, UnwrapNestedRefs<Album>>(),
|
||||
|
||||
state: reactive({
|
||||
albums: []
|
||||
albums: [] as Album[]
|
||||
}),
|
||||
|
||||
byId (id: number) {
|
||||
|
@ -48,7 +48,7 @@ export const albumStore = {
|
|||
songStore.byAlbum(album).forEach(song => song.album_cover = album.cover)
|
||||
|
||||
// sync to vault
|
||||
this.byId(album.id).cover = album.cover
|
||||
this.byId(album.id)!.cover = album.cover
|
||||
|
||||
return album.cover
|
||||
},
|
||||
|
|
|
@ -20,7 +20,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('removes artists by IDs', () => {
|
||||
const artists = factory<Artist[]>('artist', 3)
|
||||
const artists = factory<Artist>('artist', 3)
|
||||
artists.forEach(artist => artistStore.vault.set(artist.id, artist))
|
||||
artistStore.state.artists = artists
|
||||
|
||||
|
@ -64,19 +64,19 @@ new class extends UnitTestCase {
|
|||
artistStore.syncWithVault(artist)
|
||||
|
||||
expect(artistStore.vault.size).toBe(1)
|
||||
expect(artistStore.vault.get(artist.id).name).toBe('Pink Floyd')
|
||||
expect(artistStore.vault.get(artist.id)?.name).toBe('Pink Floyd')
|
||||
})
|
||||
|
||||
it('uploads an image for an artist', async () => {
|
||||
const artist = factory<Artist>('artist')
|
||||
artistStore.syncWithVault(artist)
|
||||
const putMock = this.mock(httpService, 'put').mockResolvedValue({ imageUrl: 'https://foo/img.jpg' })
|
||||
const putMock = this.mock(httpService, 'put').mockResolvedValue({ imageUrl: 'http://localhost/img.jpg' })
|
||||
|
||||
await artistStore.uploadImage(artist, 'data://image')
|
||||
|
||||
expect(artist.image).toBe('https://foo/img.jpg')
|
||||
expect(artist.image).toBe('http://localhost/img.jpg')
|
||||
expect(putMock).toHaveBeenCalledWith(`artist/${artist.id}/image`, { image: 'data://image' })
|
||||
expect(artistStore.byId(artist.id).image).toBe('https://foo/img.jpg')
|
||||
expect(artistStore.byId(artist.id)?.image).toBe('http://localhost/img.jpg')
|
||||
})
|
||||
|
||||
it('resolves an artist', async () => {
|
||||
|
@ -92,7 +92,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('paginates', async () => {
|
||||
const artists = factory<Artist[]>('artist', 3)
|
||||
const artists = factory<Artist>('artist', 3)
|
||||
|
||||
this.mock(httpService, 'get').mockResolvedValueOnce({
|
||||
data: artists,
|
||||
|
|
|
@ -10,7 +10,7 @@ export const artistStore = {
|
|||
vault: new Map<number, UnwrapNestedRefs<Artist>>(),
|
||||
|
||||
state: reactive({
|
||||
artists: []
|
||||
artists: [] as Artist[]
|
||||
}),
|
||||
|
||||
byId (id: number) {
|
||||
|
@ -38,7 +38,7 @@ export const artistStore = {
|
|||
artist.image = (await httpService.put<{ imageUrl: string }>(`artist/${artist.id}/image`, { image })).imageUrl
|
||||
|
||||
// sync to vault
|
||||
this.byId(artist.id).image = artist.image
|
||||
this.byId(artist.id)!.image = artist.image
|
||||
|
||||
return artist.image
|
||||
},
|
||||
|
|
|
@ -29,7 +29,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('adds songs', () => {
|
||||
const songs = factory<Song[]>('song', 3)
|
||||
const songs = factory<Song>('song', 3)
|
||||
favoriteStore.add(songs)
|
||||
expect(favoriteStore.state.songs).toEqual(songs)
|
||||
|
||||
|
@ -39,14 +39,14 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('removes songs', () => {
|
||||
const songs = factory<Song[]>('song', 3)
|
||||
const songs = factory<Song>('song', 3)
|
||||
favoriteStore.state.songs = songs
|
||||
favoriteStore.remove(songs)
|
||||
expect(favoriteStore.state.songs).toEqual([])
|
||||
})
|
||||
|
||||
it('likes several songs', async () => {
|
||||
const songs = factory<Song[]>('song', 3)
|
||||
const songs = factory<Song>('song', 3)
|
||||
const addMock = this.mock(favoriteStore, 'add')
|
||||
const postMock = this.mock(httpService, 'post')
|
||||
|
||||
|
@ -57,7 +57,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('unlikes several songs', async () => {
|
||||
const songs = factory<Song[]>('song', 3)
|
||||
const songs = factory<Song>('song', 3)
|
||||
const removeMock = this.mock(favoriteStore, 'remove')
|
||||
const postMock = this.mock(httpService, 'post')
|
||||
|
||||
|
@ -68,7 +68,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('fetches favorites', async () => {
|
||||
const songs = factory<Song[]>('song', 3)
|
||||
const songs = factory<Song>('song', 3)
|
||||
const getMock = this.mock(httpService, 'get').mockResolvedValue(songs)
|
||||
|
||||
await favoriteStore.fetch()
|
||||
|
|
|
@ -25,12 +25,12 @@ new class extends UnitTestCase {
|
|||
const artistSyncMock = this.mock(artistStore, 'syncWithVault')
|
||||
const refreshMock = this.mock(overviewStore, 'refresh')
|
||||
|
||||
const mostPlayedSongs = factory<Song[]>('song', 7)
|
||||
const mostPlayedAlbums = factory<Album[]>('album', 6)
|
||||
const mostPlayedArtists = factory<Artist[]>('artist', 6)
|
||||
const recentlyAddedSongs = factory<Song[]>('song', 9)
|
||||
const recentlyAddedAlbums = factory<Album[]>('album', 6)
|
||||
const recentlyPlayedSongs = factory<Song[]>('song', 9)
|
||||
const mostPlayedSongs = factory<Song>('song', 7)
|
||||
const mostPlayedAlbums = factory<Album>('album', 6)
|
||||
const mostPlayedArtists = factory<Artist>('artist', 6)
|
||||
const recentlyAddedSongs = factory<Song>('song', 9)
|
||||
const recentlyAddedAlbums = factory<Album>('album', 6)
|
||||
const recentlyPlayedSongs = factory<Song>('song', 9)
|
||||
|
||||
const getMock = this.mock(httpService, 'get').mockResolvedValueOnce({
|
||||
most_played_songs: mostPlayedSongs,
|
||||
|
@ -52,12 +52,12 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('refreshes the store', () => {
|
||||
const mostPlayedSongs = factory<Song[]>('song', 7)
|
||||
const mostPlayedAlbums = factory<Album[]>('album', 6)
|
||||
const mostPlayedArtists = factory<Artist[]>('artist', 6)
|
||||
const recentlyAddedSongs = factory<Song[]>('song', 9)
|
||||
const recentlyAddedAlbums = factory<Album[]>('album', 6)
|
||||
const recentlyPlayedSongs = factory<Song[]>('song', 9)
|
||||
const mostPlayedSongs = factory<Song>('song', 7)
|
||||
const mostPlayedAlbums = factory<Album>('album', 6)
|
||||
const mostPlayedArtists = factory<Artist>('artist', 6)
|
||||
const recentlyAddedSongs = factory<Song>('song', 9)
|
||||
const recentlyAddedAlbums = factory<Album>('album', 6)
|
||||
const recentlyPlayedSongs = factory<Song>('song', 9)
|
||||
|
||||
const mostPlayedSongsMock = this.mock(songStore, 'getMostPlayed', mostPlayedSongs)
|
||||
const mostPlayedAlbumsMock = this.mock(albumStore, 'getMostPlayed', mostPlayedAlbums)
|
||||
|
|
|
@ -7,12 +7,12 @@ import { recentlyPlayedStore } from '@/stores'
|
|||
|
||||
export const overviewStore = {
|
||||
state: reactive({
|
||||
recentlyPlayed: [],
|
||||
recentlyAddedSongs: [],
|
||||
recentlyAddedAlbums: [],
|
||||
mostPlayedSongs: [],
|
||||
mostPlayedAlbums: [],
|
||||
mostPlayedArtists: []
|
||||
recentlyPlayed: [] as Song[],
|
||||
recentlyAddedSongs: [] as Song[],
|
||||
recentlyAddedAlbums: [] as Album[],
|
||||
mostPlayedSongs: [] as Song[],
|
||||
mostPlayedAlbums: [] as Album[],
|
||||
mostPlayedArtists: [] as Artist[]
|
||||
}),
|
||||
|
||||
async init () {
|
||||
|
|
|
@ -11,7 +11,7 @@ let songs
|
|||
new class extends UnitTestCase {
|
||||
protected beforeEach () {
|
||||
super.beforeEach(() => {
|
||||
songs = factory<Song[]>('song', 3)
|
||||
songs = factory<Song>('song', 3)
|
||||
queueStore.state.songs = reactive(songs)
|
||||
})
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('replaces the whole queue', () => {
|
||||
const newSongs = factory<Song[]>('song', 2)
|
||||
const newSongs = factory<Song>('song', 2)
|
||||
queueStore.replaceQueueWith(newSongs)
|
||||
|
||||
expect(queueStore.all).toEqual(newSongs)
|
||||
|
@ -89,7 +89,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('fetches random songs to queue', async () => {
|
||||
const songs = factory<Song[]>('song', 3)
|
||||
const songs = factory<Song>('song', 3)
|
||||
const getMock = this.mock(httpService, 'get').mockResolvedValue(songs)
|
||||
const syncMock = this.mock(songStore, 'syncWithVault', songs)
|
||||
|
||||
|
@ -101,7 +101,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('fetches random songs to queue with a custom order', async () => {
|
||||
const songs = factory<Song[]>('song', 3)
|
||||
const songs = factory<Song>('song', 3)
|
||||
const getMock = this.mock(httpService, 'get').mockResolvedValue(songs)
|
||||
const syncMock = this.mock(songStore, 'syncWithVault', songs)
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import { recentlyPlayedStore } from '@/stores/recentlyPlayedStore'
|
|||
new class extends UnitTestCase {
|
||||
protected test () {
|
||||
it('fetches the recently played songs', async () => {
|
||||
const songs = factory<Song[]>('song', 3)
|
||||
const songs = factory<Song>('song', 3)
|
||||
const getMock = this.mock(httpService, 'get').mockResolvedValue(songs)
|
||||
const syncMock = this.mock(songStore, 'syncWithVault', songs)
|
||||
|
||||
|
@ -21,7 +21,7 @@ new class extends UnitTestCase {
|
|||
|
||||
it('fetches when attempting to add a new song and the state is empty', async () => {
|
||||
recentlyPlayedStore.state.songs = []
|
||||
const songs = factory<Song[]>('song', 3)
|
||||
const songs = factory<Song>('song', 3)
|
||||
const fetchMock = this.mock(recentlyPlayedStore, 'fetch').mockResolvedValue(songs)
|
||||
|
||||
await recentlyPlayedStore.add(factory<Song>('song'))
|
||||
|
@ -31,7 +31,7 @@ new class extends UnitTestCase {
|
|||
|
||||
it('adds a song to the state', async () => {
|
||||
const newSong = factory<Song>('song')
|
||||
const songs = factory<Song[]>('song', 10)
|
||||
const songs = factory<Song>('song', 10)
|
||||
const exceptSongs = songs.slice(0, 7)
|
||||
|
||||
// We don't want to keep the reference to the original songs
|
||||
|
@ -45,7 +45,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('deduplicates when adding a song to the state', async () => {
|
||||
const songs = factory<Song[]>('song', 10)
|
||||
const songs = factory<Song>('song', 10)
|
||||
const newSong = songs[1]
|
||||
const exceptSongs = songs.slice(0, 7)
|
||||
|
||||
|
|
|
@ -25,9 +25,9 @@ new class extends UnitTestCase {
|
|||
protected test () {
|
||||
it('performs an excerpt search', async () => {
|
||||
const result: ExcerptSearchResult = {
|
||||
songs: factory<Song[]>('song', 3),
|
||||
albums: factory<Album[]>('album', 3),
|
||||
artists: factory<Artist[]>('artist', 3)
|
||||
songs: factory<Song>('song', 3),
|
||||
albums: factory<Album>('album', 3),
|
||||
artists: factory<Artist>('artist', 3)
|
||||
}
|
||||
|
||||
const getMock = this.mock(httpService, 'get').mockResolvedValue(result)
|
||||
|
@ -48,7 +48,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('performs a song search', async () => {
|
||||
const songs = factory<Song[]>('song', 3)
|
||||
const songs = factory<Song>('song', 3)
|
||||
|
||||
const getMock = this.mock(httpService, 'get').mockResolvedValue(songs)
|
||||
const syncMock = this.mock(songStore, 'syncWithVault', songs)
|
||||
|
@ -62,7 +62,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('resets the song result state', () => {
|
||||
searchStore.state.songs = factory<Song[]>('song', 3)
|
||||
searchStore.state.songs = factory<Song>('song', 3)
|
||||
searchStore.resetSongResultState()
|
||||
expect(searchStore.state.songs).toEqual([])
|
||||
})
|
||||
|
|
|
@ -73,7 +73,7 @@ new class extends UnitTestCase {
|
|||
|
||||
it('matches a song', () => {
|
||||
const song = factory<Song>('song', { title: 'An amazing song' })
|
||||
const songs = [song, ...factory<Song[]>('song', 3)]
|
||||
const songs = [song, ...factory<Song>('song', 3)]
|
||||
|
||||
expect(songStore.match('An amazing song', songs)).toEqual(song)
|
||||
expect(songStore.match('An Amazing Song', songs)).toEqual(song)
|
||||
|
@ -103,24 +103,24 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('updates songs', async () => {
|
||||
const songs = factory<Song[]>('song', 3)
|
||||
const songs = factory<Song>('song', 3)
|
||||
|
||||
const result: SongUpdateResult = {
|
||||
songs: factory<Song[]>('song', 3),
|
||||
albums: factory<Album[]>('album', 2),
|
||||
artists: factory<Artist[]>('artist', 2),
|
||||
songs: factory<Song>('song', 3),
|
||||
albums: factory<Album>('album', 2),
|
||||
artists: factory<Artist>('artist', 2),
|
||||
removed: {
|
||||
albums: [{
|
||||
id: 10,
|
||||
artist_id: 3,
|
||||
name: 'Removed Album',
|
||||
cover: 'https://example.com/removed-album.jpg',
|
||||
cover: 'http://localhost/removed-album.jpg',
|
||||
created_at: '2020-01-01'
|
||||
}],
|
||||
artists: [{
|
||||
id: 42,
|
||||
name: 'Removed Artist',
|
||||
image: 'https://example.com/removed-artist.jpg',
|
||||
image: 'http://localhost/removed-artist.jpg',
|
||||
created_at: '2020-01-01'
|
||||
}]
|
||||
}
|
||||
|
@ -220,7 +220,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('fetches for album', async () => {
|
||||
const songs = factory<Song[]>('song', 3)
|
||||
const songs = factory<Song>('song', 3)
|
||||
const album = factory<Album>('album', { id: 42 })
|
||||
const getMock = this.mock(httpService, 'get').mockResolvedValueOnce(songs)
|
||||
const syncMock = this.mock(songStore, 'syncWithVault')
|
||||
|
@ -232,7 +232,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('fetches for artist', async () => {
|
||||
const songs = factory<Song[]>('song', 3)
|
||||
const songs = factory<Song>('song', 3)
|
||||
const artist = factory<Artist>('artist', { id: 42 })
|
||||
const getMock = this.mock(httpService, 'get').mockResolvedValueOnce(songs)
|
||||
const syncMock = this.mock(songStore, 'syncWithVault')
|
||||
|
@ -244,7 +244,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('fetches for playlist', async () => {
|
||||
const songs = factory<Song[]>('song', 3)
|
||||
const songs = factory<Song>('song', 3)
|
||||
const playlist = factory<Playlist>('playlist', { id: 42 })
|
||||
const getMock = this.mock(httpService, 'get').mockResolvedValueOnce(songs)
|
||||
const syncMock = this.mock(songStore, 'syncWithVault')
|
||||
|
@ -256,7 +256,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('paginates', async () => {
|
||||
const songs = factory<Song[]>('song', 3)
|
||||
const songs = factory<Song>('song', 3)
|
||||
|
||||
const getMock = this.mock(httpService, 'get').mockResolvedValueOnce({
|
||||
data: songs,
|
||||
|
|
|
@ -34,7 +34,7 @@ new class extends UnitTestCase {
|
|||
})
|
||||
|
||||
it('fetches users', async () => {
|
||||
const users = factory<User[]>('user', 3)
|
||||
const users = factory<User>('user', 3)
|
||||
const getMock = this.mock(httpService, 'get').mockResolvedValue(users)
|
||||
|
||||
await userStore.fetch()
|
||||
|
|
Loading…
Add table
Reference in a new issue