feat(test): add PlaylistNameEditor component tests

This commit is contained in:
Phan An 2022-05-08 20:18:27 +02:00
parent 1a26ad1ac1
commit 3f93d68f95
No known key found for this signature in database
GPG key ID: A81E4477F0BB6FDC
12 changed files with 103 additions and 71 deletions

View file

@ -1,29 +0,0 @@
import Component from '@/components/playlist/PlaylistNameEditor.vue'
import factory from '@/__tests__/factory'
import { playlistStore } from '@/stores'
import { mock } from '@/__tests__/__helpers__'
import { shallow } from '@/__tests__/adapter'
describe('components/playlist/PlaylistNameEditor', () => {
let playlist: Playlist
beforeEach(() => {
playlist = factory<Playlist>('playlist', {
id: 99,
name: 'Foo'
})
})
afterEach(() => {
jest.resetModules()
jest.clearAllMocks()
})
it('updates a playlist', () => {
const updateStub = mock(playlistStore, 'update')
const wrapper = shallow(Component, {
propsData: { playlist }
})
wrapper.find('[type=text]').setValue('Bar').input().blur()
expect(updateStub).toHaveBeenCalledWith(expect.objectContaining({ id: 99, name: 'Bar' }))
})
})

View file

@ -1,7 +1,7 @@
<template> <template>
<ContextMenuBase extra-class="playlist-menu" ref="base"> <ContextMenuBase ref="base" extra-class="playlist-menu">
<li @click="createPlaylist" data-testid="playlist-context-menu-create-simple">New Playlist</li> <li data-testid="playlist-context-menu-create-simple" @click="createPlaylist">New Playlist</li>
<li @click="createSmartPlaylist" data-testid="playlist-context-menu-create-smart">New Smart Playlist</li> <li data-testid="playlist-context-menu-create-smart" @click="createSmartPlaylist">New Smart Playlist</li>
</ContextMenuBase> </ContextMenuBase>
</template> </template>

View file

@ -1,7 +1,7 @@
<template> <template>
<ContextMenuBase extra-class="playlist-item-menu" ref="base"> <ContextMenuBase ref="base" extra-class="playlist-item-menu">
<li @click="editPlaylist" :data-testid="`playlist-context-menu-edit-${playlist.id}`">Edit</li> <li :data-testid="`playlist-context-menu-edit-${playlist.id}`" @click="editPlaylist">Edit</li>
<li @click="deletePlaylist" :data-testid="`playlist-context-menu-delete-${playlist.id}`">Delete</li> <li :data-testid="`playlist-context-menu-delete-${playlist.id}`" @click="deletePlaylist">Delete</li>
</ContextMenuBase> </ContextMenuBase>
</template> </template>

View file

@ -0,0 +1,63 @@
import factory from '@/__tests__/factory'
import PlaylistNameEditor from '@/components/playlist/PlaylistNameEditor.vue'
import { beforeEach, expect, it } from 'vitest'
import { mockHelper, render } from '@/__tests__/__helpers__'
import { cleanup, fireEvent } from '@testing-library/vue'
import { playlistStore } from '@/stores'
beforeEach(() => {
mockHelper.restoreAllMocks()
cleanup()
})
const setup = () => {
const updateMock = mockHelper.mock(playlistStore, 'update')
const { getByTestId } = render(PlaylistNameEditor, {
props: {
playlist: factory<Playlist>('playlist', {
id: 99,
name: 'Foo'
})
}
})
return {
updateMock,
input: getByTestId<HTMLInputElement>('inline-playlist-name-input')
}
}
it('updates a playlist name on blur', async () => {
const { updateMock, input } = setup()
await fireEvent.update(input, 'Bar')
await fireEvent.blur(input)
expect(updateMock).toHaveBeenCalledWith(expect.objectContaining({
id: 99,
name: 'Bar'
}))
})
it('updates a playlist name on enter', async () => {
const { updateMock, input } = setup()
await fireEvent.update(input, 'Bar')
await fireEvent.keyUp(input, { key: 'Enter' })
expect(updateMock).toHaveBeenCalledWith(expect.objectContaining({
id: 99,
name: 'Bar'
}))
})
it('cancels updating on esc', async () => {
const { updateMock, input } = setup()
await fireEvent.update(input, 'Bar')
await fireEvent.keyUp(input, { key: 'Esc' })
expect(input.value).toBe('Foo')
expect(updateMock).not.toHaveBeenCalled()
})

View file

@ -51,5 +51,8 @@ const update = async () => {
emit('updated', mutatedPlaylist) emit('updated', mutatedPlaylist)
} }
const cancel = () => emit('cancelled') const cancel = () => {
mutatedPlaylist.name = playlist.value.name
emit('cancelled')
}
</script> </script>

View file

@ -1,26 +1,26 @@
<template> <template>
<li <li
@dblclick.prevent="makeEditable" :class="['playlist', type, editing ? 'editing' : '', playlist.is_smart ? 'smart' : '']"
:class="['playlist', type, editing ? 'editing' : '', playlist.is_smart ? 'smart' : '']"> @dblclick.prevent="makeEditable">
<a <a
v-koel-droppable:[contentEditable]="handleDrop"
:class="{ active }" :class="{ active }"
:href="url" :href="url"
@contextmenu.prevent="openContextMenu" @contextmenu.prevent="openContextMenu"
v-koel-droppable:[contentEditable]="handleDrop"
>{{ playlist.name }}</a> >{{ playlist.name }}</a>
<NameEditor <NameEditor
v-if="nameEditable && editing"
:playlist="playlist" :playlist="playlist"
@cancelled="cancelEditing" @cancelled="cancelEditing"
@updated="onPlaylistNameUpdated" @updated="onPlaylistNameUpdated"
v-if="nameEditable && editing"
/> />
<ContextMenu <ContextMenu
v-if="hasContextMenu" v-if="hasContextMenu"
v-show="showingContextMenu" v-show="showingContextMenu"
:playlist="playlist"
ref="contextMenu" ref="contextMenu"
:playlist="playlist"
@edit="makeEditable" @edit="makeEditable"
/> />
</li> </li>

View file

@ -3,34 +3,34 @@
<h1>Playlists <h1>Playlists
<i <i
:class="{ creating }" :class="{ creating }"
@click.prevent="toggleContextMenu"
class="fa fa-plus-circle control create" class="fa fa-plus-circle control create"
data-testid="sidebar-create-playlist-btn"
role="button" role="button"
title="Create a new playlist" title="Create a new playlist"
data-testid="sidebar-create-playlist-btn" @click.prevent="toggleContextMenu"
></i> ></i>
</h1> </h1>
<form v-if="creating" @submit.prevent="createPlaylist" name="create-simple-playlist-form" class="create"> <form v-if="creating" @submit.prevent="createPlaylist" name="create-simple-playlist-form" class="create">
<input <input
@keyup.esc.prevent="creating = false" v-model="newName"
placeholder="↵ to save" v-koel-focus
name="name" name="name"
placeholder="↵ to save"
required required
type="text" type="text"
v-koel-focus @keyup.esc.prevent="creating = false"
v-model="newName"
> >
</form> </form>
<ul> <ul>
<PlaylistItem type="favorites" :playlist="{ name: 'Favorites', songs: favorites }"/> <PlaylistItem :playlist="{ name: 'Favorites', songs: favorites }" type="favorites"/>
<PlaylistItem type="recently-played" :playlist="{ name: 'Recently Played', songs: [] }"/> <PlaylistItem :playlist="{ name: 'Recently Played', songs: [] }" type="recently-played"/>
<PlaylistItem <PlaylistItem
:playlist="playlist"
:key="playlist.id"
type="playlist"
v-for="playlist in playlists" v-for="playlist in playlists"
:key="playlist.id"
:playlist="playlist"
type="playlist"
/> />
</ul> </ul>

View file

@ -2,7 +2,7 @@
<FormBase> <FormBase>
<div @keydown.esc="maybeClose"> <div @keydown.esc="maybeClose">
<SoundBar v-if="loading"/> <SoundBar v-if="loading"/>
<form @submit.prevent="submit" v-else data-testid="create-smart-playlist-form"> <form v-else data-testid="create-smart-playlist-form" @submit.prevent="submit">
<header> <header>
<h1>New Smart Playlist</h1> <h1>New Smart Playlist</h1>
</header> </header>
@ -10,18 +10,18 @@
<div> <div>
<div class="form-row"> <div class="form-row">
<label>Name</label> <label>Name</label>
<input type="text" v-model="name" name="name" v-koel-focus required> <input v-model="name" v-koel-focus name="name" required type="text">
</div> </div>
<div class="form-row rules"> <div class="form-row rules">
<RuleGroup <RuleGroup
v-for="(group, index) in collectedRuleGroups"
:key="group.id"
:group="group" :group="group"
:isFirstGroup="index === 0" :isFirstGroup="index === 0"
:key="group.id"
@input="onGroupChanged" @input="onGroupChanged"
v-for="(group, index) in collectedRuleGroups"
/> />
<Btn @click.prevent="addGroup" class="btn-add-group" green small uppercase> <Btn class="btn-add-group" green small uppercase @click.prevent="addGroup">
<i class="fa fa-plus"></i> Group <i class="fa fa-plus"></i> Group
</Btn> </Btn>
</div> </div>
@ -29,7 +29,7 @@
<footer> <footer>
<Btn type="submit">Save</Btn> <Btn type="submit">Save</Btn>
<Btn class="btn-cancel" @click.prevent="maybeClose" white>Cancel</Btn> <Btn class="btn-cancel" white @click.prevent="maybeClose">Cancel</Btn>
</footer> </footer>
</form> </form>
</div> </div>

View file

@ -2,7 +2,7 @@
<FormBase> <FormBase>
<div @keydown.esc="maybeClose"> <div @keydown.esc="maybeClose">
<SoundBar v-if="loading"/> <SoundBar v-if="loading"/>
<form @submit.prevent="submit" v-else data-testid="edit-smart-playlist-form"> <form v-else data-testid="edit-smart-playlist-form" @submit.prevent="submit">
<header> <header>
<h1>Edit Smart Playlist</h1> <h1>Edit Smart Playlist</h1>
</header> </header>
@ -10,18 +10,18 @@
<div> <div>
<div class="form-row"> <div class="form-row">
<label>Name</label> <label>Name</label>
<input type="text" v-model="mutatedPlaylist.name" name="name" v-koel-focus required> <input v-model="mutatedPlaylist.name" v-koel-focus name="name" required type="text">
</div> </div>
<div class="form-row rules"> <div class="form-row rules">
<RuleGroup <RuleGroup
v-for="(group, index) in mutatedPlaylist.rules" v-for="(group, index) in mutatedPlaylist.rules"
:isFirstGroup="index === 0"
:key="group.id" :key="group.id"
:group="group" :group="group"
:isFirstGroup="index === 0"
@input="onGroupChanged" @input="onGroupChanged"
/> />
<Btn @click.prevent="addGroup" class="btn-add-group" green small uppercase> <Btn class="btn-add-group" green small uppercase @click.prevent="addGroup">
<i class="fa fa-plus"></i> Group <i class="fa fa-plus"></i> Group
</Btn> </Btn>
</div> </div>
@ -29,7 +29,7 @@
<footer> <footer>
<Btn type="submit">Save</Btn> <Btn type="submit">Save</Btn>
<Btn white class="btn-cancel" @click.prevent="maybeClose">Cancel</Btn> <Btn class="btn-cancel" white @click.prevent="maybeClose">Cancel</Btn>
</footer> </footer>
</form> </form>
</div> </div>

View file

@ -40,7 +40,6 @@ const mutatedRule = Object.assign({}, rule.value)
const selectedModel = ref<SmartPlaylistModel>() const selectedModel = ref<SmartPlaylistModel>()
const selectedOperator = ref<SmartPlaylistOperator>() const selectedOperator = ref<SmartPlaylistOperator>()
const inputValues = ref([])
const model = models.find(m => m.name === mutatedRule.model.name) const model = models.find(m => m.name === mutatedRule.model.name)

View file

@ -1,5 +1,5 @@
<template> <template>
<input :type="type" v-model="value" name="value[]" required> <input v-model="value" :type="type" name="value[]" required>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>

View file

@ -92,12 +92,8 @@ const close = () => {
shown.value = false shown.value = false
} }
eventBus.on('CONTEXT_MENU_OPENED', target => { // ensure there's only one context menu at any time
// ensure there's only one context menu at any time eventBus.on('CONTEXT_MENU_OPENED', target => target === el || close())
if (target !== el) {
close()
}
})
defineExpose({ open, close, shown }) defineExpose({ open, close, shown })
</script> </script>