mirror of
https://github.com/koel/koel
synced 2024-11-28 06:50:27 +00:00
fix(test): album store tests
This commit is contained in:
parent
9a4e680c4d
commit
2e3105e309
11 changed files with 141 additions and 44 deletions
|
@ -8,7 +8,7 @@ import AlbumListScreen from './AlbumListScreen.vue'
|
|||
|
||||
new class extends UnitTestCase {
|
||||
protected beforeEach () {
|
||||
super.beforeEach(() => this.mock(albumStore, 'fetch'))
|
||||
super.beforeEach(() => this.mock(albumStore, 'paginate'))
|
||||
}
|
||||
|
||||
private renderComponent () {
|
||||
|
|
|
@ -53,7 +53,7 @@ const fetchAlbums = async () => {
|
|||
if (loading || !moreAlbumsAvailable.value) return
|
||||
|
||||
loading = true
|
||||
page.value = await albumStore.fetch(page.value!)
|
||||
page.value = await albumStore.paginate(page.value!)
|
||||
loading = false
|
||||
}
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { faFile } from '@fortawesome/free-regular-svg-icons'
|
||||
import { difference } from 'lodash'
|
||||
import { differenceBy } from 'lodash'
|
||||
import { ref, toRef } from 'vue'
|
||||
import { alerts, eventBus, pluralize } from '@/utils'
|
||||
import { commonStore, playlistStore, songStore } from '@/stores'
|
||||
|
@ -109,7 +109,7 @@ const removeSelected = () => {
|
|||
if (!selectedSongs.value.length || playlist.value.is_smart) return
|
||||
|
||||
playlistStore.removeSongs(playlist.value!, selectedSongs.value)
|
||||
songs.value = difference(songs.value, selectedSongs.value)
|
||||
songs.value = differenceBy(songs.value, selectedSongs.value, 'id')
|
||||
alerts.success(`Removed ${pluralize(selectedSongs.value.length, 'song')} from "${playlist.value!.name}."`)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,111 @@
|
|||
import { expect, it } from 'vitest'
|
||||
import UnitTestCase from '@/__tests__/UnitTestCase'
|
||||
import { albumStore, artistStore } from '@/stores'
|
||||
import { albumStore, songStore } from '@/stores'
|
||||
import factory from '@/__tests__/factory'
|
||||
import { httpService } from '@/services'
|
||||
|
||||
new class extends UnitTestCase {
|
||||
protected afterEach () {
|
||||
super.afterEach(() => {
|
||||
artistStore.state.artists = []
|
||||
protected beforeEach () {
|
||||
super.beforeEach(() => {
|
||||
albumStore.vault.clear()
|
||||
albumStore.state.albums = []
|
||||
})
|
||||
}
|
||||
|
||||
protected test () {
|
||||
it('gets an album by ID', () => {
|
||||
const album = factory<Album>('album')
|
||||
albumStore.vault.set(album.id, album)
|
||||
expect(albumStore.byId(album.id)).toEqual(album)
|
||||
})
|
||||
|
||||
it('removes albums by IDs', () => {
|
||||
const albums = factory<Album[]>('album', 3)
|
||||
albums.forEach(album => albumStore.vault.set(album.id, album))
|
||||
albumStore.state.albums = albums
|
||||
|
||||
albumStore.removeByIds([albums[0].id, albums[1].id])
|
||||
|
||||
expect(albumStore.vault.size).toBe(1)
|
||||
expect(albumStore.vault.has(albums[0].id)).toBe(false)
|
||||
expect(albumStore.vault.has(albums[1].id)).toBe(false)
|
||||
expect(albumStore.state.albums).toEqual([albums[2]])
|
||||
})
|
||||
|
||||
it('identifies an unknown album', () => {
|
||||
const album = factory.states('unknown')<Album>('album')
|
||||
|
||||
expect(albumStore.isUnknown(album)).toBe(true)
|
||||
expect(albumStore.isUnknown(album.id)).toBe(true)
|
||||
expect(albumStore.isUnknown(factory<Album>('album'))).toBe(false)
|
||||
})
|
||||
|
||||
it('syncs albums with the vault', () => {
|
||||
const album = factory<Album>('album', { name: 'IV' })
|
||||
|
||||
albumStore.syncWithVault(album)
|
||||
expect(albumStore.vault.get(album.id)).toEqual(album)
|
||||
|
||||
album.name = 'V'
|
||||
albumStore.syncWithVault(album)
|
||||
|
||||
expect(albumStore.vault.size).toBe(1)
|
||||
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' })
|
||||
this.mock(songStore, 'byAlbum', songsInAlbum)
|
||||
|
||||
await albumStore.uploadCover(album, 'data://cover')
|
||||
|
||||
expect(album.cover).toBe('https://foo/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'))
|
||||
})
|
||||
|
||||
it('fetches an album thumbnail', async () => {
|
||||
const getMock = this.mock(httpService, 'get').mockResolvedValue({ thumbnailUrl: 'https://foo/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')
|
||||
})
|
||||
|
||||
it('resolves an album', async () => {
|
||||
const album = factory<Album>('album')
|
||||
const getMock = this.mock(httpService, 'get').mockResolvedValueOnce(album)
|
||||
|
||||
expect(await albumStore.resolve(album.id)).toEqual(album)
|
||||
expect(getMock).toHaveBeenCalledWith(`albums/${album.id}`)
|
||||
|
||||
// next call shouldn't make another request
|
||||
expect(await albumStore.resolve(album.id)).toEqual(album)
|
||||
expect(getMock).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('paginates', async () => {
|
||||
const albums = factory<Album[]>('album', 3)
|
||||
|
||||
this.mock(httpService, 'get').mockResolvedValueOnce({
|
||||
data: albums,
|
||||
links: {
|
||||
next: '/albums?page=2'
|
||||
},
|
||||
meta: {
|
||||
current_page: 1
|
||||
}
|
||||
})
|
||||
|
||||
expect(await albumStore.paginate(1)).toEqual(2)
|
||||
expect(albumStore.state.albums).toEqual(albums)
|
||||
expect(albumStore.vault.size).toBe(3)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { reactive } from 'vue'
|
||||
import { difference, orderBy, take, union } from 'lodash'
|
||||
import { differenceBy, merge, orderBy, take, unionBy } from 'lodash'
|
||||
import { Cache, httpService } from '@/services'
|
||||
import { arrayify } from '@/utils'
|
||||
import { songStore } from '@/stores'
|
||||
|
@ -18,10 +18,25 @@ export const albumStore = {
|
|||
},
|
||||
|
||||
removeByIds (ids: number[]) {
|
||||
this.state.albums = difference(this.state.albums, ids.map(id => this.byId(id)))
|
||||
this.state.albums = differenceBy(this.state.albums, ids.map(id => this.byId(id)), 'id')
|
||||
ids.forEach(id => this.vault.delete(id))
|
||||
},
|
||||
|
||||
isUnknown: (album: Album | number) => {
|
||||
if (typeof album === 'number') return album === UNKNOWN_ALBUM_ID
|
||||
return album.id === UNKNOWN_ALBUM_ID
|
||||
},
|
||||
|
||||
syncWithVault (albums: Album | Album[]) {
|
||||
return arrayify(albums).map(album => {
|
||||
let local = this.vault.get(album.id)
|
||||
local = reactive(local ? merge(local, album) : album)
|
||||
this.vault.set(album.id, local)
|
||||
|
||||
return local
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Upload a cover for an album.
|
||||
*
|
||||
|
@ -45,21 +60,6 @@ export const albumStore = {
|
|||
return (await httpService.get<{ thumbnailUrl: string }>(`album/${id}/thumbnail`)).thumbnailUrl
|
||||
},
|
||||
|
||||
isUnknown: (album: Album | number) => {
|
||||
if (typeof album === 'number') return album === UNKNOWN_ALBUM_ID
|
||||
return album.id === UNKNOWN_ALBUM_ID
|
||||
},
|
||||
|
||||
syncWithVault (albums: Album | Album[]) {
|
||||
return arrayify(albums).map(album => {
|
||||
let local = this.vault.get(album.id)
|
||||
local = reactive(local ? Object.assign(local, album) : album)
|
||||
this.vault.set(album.id, local)
|
||||
|
||||
return local
|
||||
})
|
||||
},
|
||||
|
||||
async resolve (id: number) {
|
||||
let album = this.byId(id)
|
||||
|
||||
|
@ -71,9 +71,9 @@ export const albumStore = {
|
|||
return album
|
||||
},
|
||||
|
||||
async fetch (page: number) {
|
||||
async paginate (page: number) {
|
||||
const resource = await httpService.get<PaginatorResource>(`albums?page=${page}`)
|
||||
this.state.albums = union(this.state.albums, this.syncWithVault(resource.data))
|
||||
this.state.albums = unionBy(this.state.albums, this.syncWithVault(resource.data), 'id')
|
||||
|
||||
return resource.links.next ? ++resource.meta.current_page : null
|
||||
},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { reactive } from 'vue'
|
||||
import { difference, orderBy, take, union } from 'lodash'
|
||||
import { differenceBy, orderBy, take, union, unionBy } from 'lodash'
|
||||
import { Cache, httpService } from '@/services'
|
||||
import { arrayify } from '@/utils'
|
||||
|
||||
|
@ -18,7 +18,7 @@ export const artistStore = {
|
|||
},
|
||||
|
||||
removeByIds (ids: number[]) {
|
||||
this.state.artists = difference(this.state.artists, ids.map(id => this.byId(id)))
|
||||
this.state.artists = differenceBy(this.state.artists, ids.map(id => this.byId(id)), 'id')
|
||||
ids.forEach(id => this.vault.delete(id))
|
||||
},
|
||||
|
||||
|
@ -36,7 +36,7 @@ export const artistStore = {
|
|||
|
||||
async uploadImage (artist: Artist, image: string) {
|
||||
artist.image = (await httpService.put<{ imageUrl: string }>(`artist/${artist.id}/image`, { image })).imageUrl
|
||||
|
||||
|
||||
// sync to vault
|
||||
this.byId(artist.id).image = artist.image
|
||||
|
||||
|
@ -66,7 +66,7 @@ export const artistStore = {
|
|||
|
||||
async fetch (page: number) {
|
||||
const resource = await httpService.get<PaginatorResource>(`artists?page=${page}`)
|
||||
this.state.artists = union(this.state.artists, this.syncWithVault(resource.data))
|
||||
this.state.artists = unionBy(this.state.artists, this.syncWithVault(resource.data), 'id')
|
||||
|
||||
return resource.links.next ? ++resource.meta.current_page : null
|
||||
},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { reactive } from 'vue'
|
||||
import { difference, union } from 'lodash'
|
||||
import { differenceBy, union, unionBy } from 'lodash'
|
||||
import { httpService } from '@/services'
|
||||
import { arrayify } from '@/utils'
|
||||
import { songStore } from '@/stores'
|
||||
|
@ -21,11 +21,11 @@ export const favoriteStore = {
|
|||
},
|
||||
|
||||
add (songs: Song | Song[]) {
|
||||
this.state.songs = union(this.state.songs, arrayify(songs))
|
||||
this.state.songs = unionBy(this.state.songs, arrayify(songs), 'id')
|
||||
},
|
||||
|
||||
remove (songs: Song | Song[]) {
|
||||
this.state.songs = difference(this.state.songs, arrayify(songs))
|
||||
this.state.songs = differenceBy(this.state.songs, arrayify(songs), 'id')
|
||||
},
|
||||
|
||||
clear () {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { difference, orderBy } from 'lodash'
|
||||
import { differenceBy, orderBy } from 'lodash'
|
||||
import { reactive } from 'vue'
|
||||
|
||||
import { logger } from '@/utils'
|
||||
|
@ -55,7 +55,7 @@ export const playlistStore = {
|
|||
|
||||
async delete (playlist: Playlist) {
|
||||
await httpService.delete(`playlists/${playlist.id}`)
|
||||
this.state.playlists = difference(this.state.playlists, [playlist])
|
||||
this.state.playlists = differenceBy(this.state.playlists, [playlist], 'id')
|
||||
},
|
||||
|
||||
async addSongs (playlist: Playlist, songs: Song[]) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { reactive } from 'vue'
|
||||
import { difference, shuffle, union } from 'lodash'
|
||||
import { differenceBy, shuffle, union, unionBy } from 'lodash'
|
||||
import { arrayify } from '@/utils'
|
||||
import { httpService } from '@/services'
|
||||
import { songStore } from '@/stores'
|
||||
|
@ -60,7 +60,7 @@ export const queueStore = {
|
|||
*/
|
||||
queue (songs: Song | Song[]) {
|
||||
this.unqueue(songs)
|
||||
this.all = union(this.all, arrayify(songs))
|
||||
this.all = unionBy(this.all, arrayify(songs), 'id')
|
||||
},
|
||||
|
||||
queueIfNotQueued (song: Song) {
|
||||
|
@ -70,7 +70,7 @@ export const queueStore = {
|
|||
},
|
||||
|
||||
queueToTop (songs: Song | Song[]) {
|
||||
this.all = union(arrayify(songs), this.all)
|
||||
this.all = unionBy(arrayify(songs), this.all, 'id')
|
||||
},
|
||||
|
||||
replaceQueueWith (songs: Song | Song[]) {
|
||||
|
@ -92,7 +92,7 @@ export const queueStore = {
|
|||
},
|
||||
|
||||
unqueue (songs: Song | Song[]) {
|
||||
this.all = difference(this.all, arrayify(songs))
|
||||
this.all = differenceBy(this.all, arrayify(songs), 'id')
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import isMobile from 'ismobilejs'
|
||||
import slugify from 'slugify'
|
||||
import { orderBy, take, union } from 'lodash'
|
||||
import { orderBy, take, union, unionBy } from 'lodash'
|
||||
import { reactive, watch } from 'vue'
|
||||
import { arrayify, eventBus, secondsToHis, use } from '@/utils'
|
||||
import { authService, Cache, httpService } from '@/services'
|
||||
|
@ -216,7 +216,7 @@ export const songStore = {
|
|||
`songs?page=${page}&sort=${sortField}&order=${sortOrder}`
|
||||
)
|
||||
|
||||
this.state.songs = union(this.state.songs, this.syncWithVault(resource.data))
|
||||
this.state.songs = unionBy(this.state.songs, this.syncWithVault(resource.data), 'id')
|
||||
|
||||
return resource.links.next ? ++resource.meta.current_page : null
|
||||
},
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { difference } from 'lodash'
|
||||
import { differenceBy } from 'lodash'
|
||||
import { httpService } from '@/services'
|
||||
import { reactive } from 'vue'
|
||||
import { arrayify } from '@/utils'
|
||||
|
@ -84,7 +84,7 @@ export const userStore = {
|
|||
|
||||
async destroy (user: User) {
|
||||
await httpService.delete(`user/${user.id}`)
|
||||
this.state.users = difference(this.state.users, [user])
|
||||
this.state.users = differenceBy(this.state.users, [user], 'id')
|
||||
this.vault.delete(user.id)
|
||||
|
||||
// Mama, just killed a man
|
||||
|
|
Loading…
Reference in a new issue