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

View file

@ -1,7 +1,7 @@
<template>
<ContextMenuBase extra-class="playlist-item-menu" ref="base">
<li @click="editPlaylist" :data-testid="`playlist-context-menu-edit-${playlist.id}`">Edit</li>
<li @click="deletePlaylist" :data-testid="`playlist-context-menu-delete-${playlist.id}`">Delete</li>
<ContextMenuBase ref="base" extra-class="playlist-item-menu">
<li :data-testid="`playlist-context-menu-edit-${playlist.id}`" @click="editPlaylist">Edit</li>
<li :data-testid="`playlist-context-menu-delete-${playlist.id}`" @click="deletePlaylist">Delete</li>
</ContextMenuBase>
</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)
}
const cancel = () => emit('cancelled')
const cancel = () => {
mutatedPlaylist.name = playlist.value.name
emit('cancelled')
}
</script>

View file

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

View file

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

View file

@ -2,7 +2,7 @@
<FormBase>
<div @keydown.esc="maybeClose">
<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>
<h1>New Smart Playlist</h1>
</header>
@ -10,18 +10,18 @@
<div>
<div class="form-row">
<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 class="form-row rules">
<RuleGroup
v-for="(group, index) in collectedRuleGroups"
:key="group.id"
:group="group"
:isFirstGroup="index === 0"
:key="group.id"
@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
</Btn>
</div>
@ -29,7 +29,7 @@
<footer>
<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>
</form>
</div>

View file

@ -2,7 +2,7 @@
<FormBase>
<div @keydown.esc="maybeClose">
<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>
<h1>Edit Smart Playlist</h1>
</header>
@ -10,18 +10,18 @@
<div>
<div class="form-row">
<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 class="form-row rules">
<RuleGroup
v-for="(group, index) in mutatedPlaylist.rules"
:isFirstGroup="index === 0"
:key="group.id"
:group="group"
:isFirstGroup="index === 0"
@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
</Btn>
</div>
@ -29,7 +29,7 @@
<footer>
<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>
</form>
</div>

View file

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

View file

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

View file

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