fix(test): album store tests

This commit is contained in:
Phan An 2022-07-22 23:56:13 +02:00
parent 9a4e680c4d
commit 2e3105e309
No known key found for this signature in database
GPG key ID: A81E4477F0BB6FDC
11 changed files with 141 additions and 44 deletions

View file

@ -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 () {

View file

@ -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
}

View file

@ -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}."`)
}

View file

@ -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)
})
}
}

View file

@ -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
},

View file

@ -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
},

View file

@ -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 () {

View file

@ -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[]) {

View file

@ -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')
},
/**

View file

@ -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
},

View file

@ -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