feat(test): add ExtraPanel component tests

This commit is contained in:
Phan An 2022-05-06 12:28:02 +02:00
parent e51d8de337
commit c85564bf0a
No known key found for this signature in database
GPG key ID: A81E4477F0BB6FDC
7 changed files with 93 additions and 134 deletions

View file

@ -1,61 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/layout/ExtraPanel does not have a YouTube tab if not using YouTube 1`] = `
<section id="extra" data-testid="extra-panel" class="text-secondary showing">
<div class="tabs">
<div role="tablist" class="clear"><button aria-selected="true" aria-controls="extraPanelLyrics" id="extraTabLyrics" role="tab">
Lyrics
</button> <button aria-controls="extraPanelArtist" id="extraTabArtist" role="tab">
Artist
</button> <button aria-controls="extraPanelAlbum" id="extraTabAlbum" role="tab">
Album
</button>
<!---->
</div>
<div class="panes">
<div aria-labelledby="extraTabLyrics" id="extraPanelLyrics" role="tabpanel" tabindex="0">
<lyricspane-stub></lyricspane-stub>
</div>
<div aria-labelledby="extraTabArtist" id="extraPanelArtist" role="tabpanel" tabindex="0" style="display: none;">
<!---->
</div>
<div aria-labelledby="extraTabAlbum" id="extraPanelAlbum" role="tabpanel" tabindex="0" style="display: none;">
<!---->
</div>
<div aria-labelledby="extraTabYouTube" id="extraPanelYouTube" role="tabpanel" tabindex="0" style="display: none;">
<!---->
</div>
</div>
</div>
</section>
`;
exports[`components/layout/ExtraPanel renders properly 1`] = `
<section id="extra" data-testid="extra-panel" class="text-secondary showing">
<div class="tabs">
<div role="tablist" class="clear"><button aria-selected="true" aria-controls="extraPanelLyrics" id="extraTabLyrics" role="tab">
Lyrics
</button> <button aria-controls="extraPanelArtist" id="extraTabArtist" role="tab">
Artist
</button> <button aria-controls="extraPanelAlbum" id="extraTabAlbum" role="tab">
Album
</button>
<!---->
</div>
<div class="panes">
<div aria-labelledby="extraTabLyrics" id="extraPanelLyrics" role="tabpanel" tabindex="0">
<lyrics-pane-stub></lyrics-pane-stub>
</div>
<div aria-labelledby="extraTabArtist" id="extraPanelArtist" role="tabpanel" tabindex="0" style="display: none;">
<!---->
</div>
<div aria-labelledby="extraTabAlbum" id="extraPanelAlbum" role="tabpanel" tabindex="0" style="display: none;">
<!---->
</div>
<div aria-labelledby="extraTabYouTube" id="extraPanelYouTube" role="tabpanel" tabindex="0" style="display: none;">
<!---->
</div>
</div>
</div>
</section>
`;

View file

@ -1,54 +0,0 @@
import Component from '@/components/layout/main-wrapper/ExtraPanel.vue'
import factory from '@/__tests__/factory'
import { eventBus } from '@/utils'
import { songInfo } from '@/services'
import { mock } from '@/__tests__/__helpers__'
import { shallow, Wrapper } from '@/__tests__/adapter'
const shallowComponent = (data: Record<string, any> = {}): Wrapper => shallow(Component, {
stubs: ['lyrics-pane', 'artist-info', 'album-info', 'you-tube-video-list'],
data: () => data
})
describe('components/layout/ExtraPanel', () => {
afterEach(() => {
jest.resetModules()
jest.clearAllMocks()
})
it('renders properly', () => {
expect(shallowComponent()).toMatchSnapshot()
})
it('does not have a YouTube tab if not using YouTube', async () => {
const wrapper = shallowComponent({
sharedState: {
useYouTube: false
}
})
await wrapper.vm.$nextTick()
expect(shallow(Component)).toMatchSnapshot()
})
it('has a YouTube tab if using YouTube', async () => {
const wrapper = shallowComponent({
sharedState: {
useYouTube: true
}
})
await wrapper.vm.$nextTick()
expect(wrapper.has('#extraTabYouTube')).toBe(true)
})
it.each<[string]>([['#extraTabLyrics'], ['#extraTabAlbum'], ['#extraTabArtist']])('switches to "%s" tab', selector => {
expect(shallowComponent().click(selector).find('[aria-selected=true]').is(selector)).toBe(true)
})
it('fetch song info when a new song is played', () => {
shallowComponent()
const song = factory('song')
const m = mock(songInfo, 'fetch', song)
eventBus.emit('SONG_STARTED', song)
expect(m).toHaveBeenCalledWith(song)
})
})

View file

@ -2,7 +2,7 @@ import Component from '@/components/song/SongEditForm.vue'
import Typeahead from '@/components/ui/typeahead.vue' import Typeahead from '@/components/ui/typeahead.vue'
import factory from '@/__tests__/factory' import factory from '@/__tests__/factory'
import { songStore } from '@/stores' import { songStore } from '@/stores'
import { songInfo } from '@/services/info' import { songInfoService } from '@/services/info'
import { mock } from '@/__tests__/__helpers__' import { mock } from '@/__tests__/__helpers__'
import { mount } from '@/__tests__/adapter' import { mount } from '@/__tests__/adapter'
@ -37,7 +37,7 @@ describe('components/song/edit-form', () => {
it('fetches song information on demand', () => { it('fetches song information on demand', () => {
const song = factory('song', { infoRetrieved: false }) const song = factory('song', { infoRetrieved: false })
const fetchMock = mock(songInfo, 'fetch') const fetchMock = mock(songInfoService, 'fetch')
mount(Component, { mount(Component, {
propsData: { songs: song } propsData: { songs: song }
}) })

View file

@ -0,0 +1,65 @@
import { beforeEach, expect, it } from 'vitest'
import { cleanup, fireEvent } from '@testing-library/vue'
import { mockHelper, render } from '@/__tests__/__helpers__'
import ExtraPanel from './ExtraPanel.vue'
import LyricsPane from '@/__tests__/Stub.vue'
import AlbumInfo from '@/__tests__/Stub.vue'
import ArtistInfo from '@/__tests__/Stub.vue'
import YouTubeVideoList from '@/__tests__/Stub.vue'
import factory from '@/__tests__/factory'
import { commonStore } from '@/stores'
import { songInfoService } from '@/services'
import { eventBus } from '@/utils'
const renderComponent = () => {
return render(ExtraPanel, {
props: {
song: factory<Song>('song')
},
global: {
stubs: {
LyricsPane,
AlbumInfo,
ArtistInfo,
YouTubeVideoList
}
}
})
}
beforeEach(() => {
cleanup()
mockHelper.restoreAllMocks()
})
it('has a YouTube tab if using YouTube ', () => {
commonStore.state.useYouTube = true
const { getByTestId } = renderComponent()
getByTestId('extra-tab-youtube')
})
it('does not have a YouTube tab if not using YouTube', async () => {
commonStore.state.useYouTube = false
const { queryByTestId } = renderComponent()
expect(await queryByTestId('extra-tab-youtube')).toBe(null)
})
it.each([['extra-tab-lyrics'], ['extra-tab-album'], ['extra-tab-artist']])('switches to "%s" tab', async (testId) => {
const { getByTestId, container } = renderComponent()
await fireEvent.click(getByTestId(testId))
expect(container.querySelector('[aria-selected=true]')).toBe(getByTestId(testId))
})
it('fetches song info when a new song is played', () => {
renderComponent()
const song = factory<Song>('song')
const mock = mockHelper.mock(songInfoService, 'fetch', song)
eventBus.emit('SONG_STARTED', song)
expect(mock).toHaveBeenCalledWith(song)
})

View file

@ -6,6 +6,7 @@
id="extraTabLyrics" id="extraTabLyrics"
:aria-selected="currentTab === 'Lyrics'" :aria-selected="currentTab === 'Lyrics'"
aria-controls="extraPanelLyrics" aria-controls="extraPanelLyrics"
data-testid="extra-tab-lyrics"
role="tab" role="tab"
@click.prevent="currentTab = 'Lyrics'" @click.prevent="currentTab = 'Lyrics'"
> >
@ -15,6 +16,7 @@
id="extraTabArtist" id="extraTabArtist"
:aria-selected="currentTab === 'Artist'" :aria-selected="currentTab === 'Artist'"
aria-controls="extraPanelArtist" aria-controls="extraPanelArtist"
data-testid="extra-tab-artist"
role="tab" role="tab"
@click.prevent="currentTab = 'Artist'" @click.prevent="currentTab = 'Artist'"
> >
@ -24,6 +26,7 @@
id="extraTabAlbum" id="extraTabAlbum"
:aria-selected="currentTab === 'Album'" :aria-selected="currentTab === 'Album'"
aria-controls="extraPanelAlbum" aria-controls="extraPanelAlbum"
data-testid="extra-tab-album"
role="tab" role="tab"
@click.prevent="currentTab = 'Album'" @click.prevent="currentTab = 'Album'"
> >
@ -34,6 +37,7 @@
id="extraTabYouTube" id="extraTabYouTube"
:aria-selected="currentTab === 'YouTube'" :aria-selected="currentTab === 'YouTube'"
aria-controls="extraPanelYouTube" aria-controls="extraPanelYouTube"
data-testid="extra-tab-youtube"
role="tab" role="tab"
title="YouTube" title="YouTube"
@click.prevent="currentTab = 'YouTube'" @click.prevent="currentTab = 'YouTube'"
@ -92,7 +96,7 @@ import isMobile from 'ismobilejs'
import { computed, defineAsyncComponent, ref, toRef, watch } from 'vue' import { computed, defineAsyncComponent, ref, toRef, watch } from 'vue'
import { $, eventBus } from '@/utils' import { $, eventBus } from '@/utils'
import { preferenceStore as preferences } from '@/stores' import { preferenceStore as preferences } from '@/stores'
import { songInfo } from '@/services' import { songInfoService } from '@/services'
import { useThirdPartyServices } from '@/composables' import { useThirdPartyServices } from '@/composables'
type Tab = 'Lyrics' | 'Artist' | 'Album' | 'YouTube' type Tab = 'Lyrics' | 'Artist' | 'Album' | 'YouTube'
@ -122,7 +126,7 @@ watch(showing, (showingExtraPanel) => {
const fetchSongInfo = async (_song: Song) => { const fetchSongInfo = async (_song: Song) => {
try { try {
song.value = await songInfo.fetch(_song) song.value = await songInfoService.fetch(_song)
} catch (err) { } catch (err) {
song.value = _song song.value = _song
throw err throw err

View file

@ -14,22 +14,22 @@
<div class="tabs"> <div class="tabs">
<div class="clear" role="tablist"> <div class="clear" role="tablist">
<button <button
:aria-selected="currentView === 'details'"
@click.prevent="currentView = 'details'"
aria-controls="editSongPanelDetails"
id="editSongTabDetails" id="editSongTabDetails"
:aria-selected="currentView === 'details'"
aria-controls="editSongPanelDetails"
role="tab" role="tab"
@click.prevent="currentView = 'details'"
> >
Details Details
</button> </button>
<button <button
@click.prevent="currentView = 'lyrics'"
v-if="editingOnlyOneSong" v-if="editingOnlyOneSong"
id="editSongTabLyrics"
:aria-selected="currentView === 'lyrics'" :aria-selected="currentView === 'lyrics'"
aria-controls="editSongPanelLyrics" aria-controls="editSongPanelLyrics"
id="editSongTabLyrics"
role="tab"
data-testid="edit-song-lyrics-tab" data-testid="edit-song-lyrics-tab"
role="tab"
@click.prevent="currentView = 'lyrics'"
> >
Lyrics Lyrics
</button> </button>
@ -43,7 +43,7 @@
role="tabpanel" role="tabpanel"
tabindex="0" tabindex="0"
> >
<div class="form-row" v-if="editingOnlyOneSong"> <div v-if="editingOnlyOneSong" class="form-row">
<label>Title</label> <label>Title</label>
<input v-model="formData.title" v-koel-focus name="title" title="Title" type="text"> <input v-model="formData.title" v-koel-focus name="title" title="Title" type="text">
</div> </div>
@ -122,7 +122,7 @@ import { computed, defineAsyncComponent, nextTick, reactive, ref, toRef, toRefs
import { isEqual, union } from 'lodash' import { isEqual, union } from 'lodash'
import { alerts, arrayify, br2nl, defaultCover, pluralize } from '@/utils' import { alerts, arrayify, br2nl, defaultCover, pluralize } from '@/utils'
import { songInfo } from '@/services/info' import { songInfoService } from '@/services/info'
import { albumStore, artistStore, songStore } from '@/stores' import { albumStore, artistStore, songStore } from '@/stores'
interface EditFormData { interface EditFormData {
@ -235,7 +235,7 @@ const open = async () => {
loading.value = true loading.value = true
try { try {
await songInfo.fetch(firstSong) await songInfoService.fetch(firstSong)
formData.lyrics = br2nl(firstSong.lyrics) formData.lyrics = br2nl(firstSong.lyrics)
formData.track = firstSong.track || null formData.track = firstSong.track || null
} catch (e) { } catch (e) {

View file

@ -1,4 +1,4 @@
import { httpService, albumInfoService, artistInfoService } from '..' import { albumInfoService, artistInfoService, httpService } from '..'
interface SongInfoResponse { interface SongInfoResponse {
artist_info: ArtistInfo, artist_info: ArtistInfo,
@ -10,14 +10,19 @@ interface SongInfoResponse {
lyrics: string lyrics: string
} }
export const songInfo = { export const songInfoService = {
fetch: async (song: Song): Promise<Song> => { fetch: async (song: Song) => {
if (!song.infoRetrieved) { if (!song.infoRetrieved) {
const { lyrics, artist_info, album_info, youtube } = await httpService.get<SongInfoResponse>(`song/${song.id}/info`) const {
lyrics,
artist_info: artistInfo,
album_info: albumInfo,
youtube
} = await httpService.get<SongInfoResponse>(`song/${song.id}/info`)
song.lyrics = lyrics song.lyrics = lyrics
artist_info && artistInfoService.merge(song.artist, artist_info) artistInfo && artistInfoService.merge(song.artist, artistInfo)
album_info && albumInfoService.merge(song.album, album_info) albumInfo && albumInfoService.merge(song.album, albumInfo)
song.youtube = youtube song.youtube = youtube
song.infoRetrieved = true song.infoRetrieved = true
} }