mirror of
https://github.com/koel/koel
synced 2024-11-24 21:23:06 +00:00
feat(test): add PlaylistNameEditor component tests
This commit is contained in:
parent
1a26ad1ac1
commit
3f93d68f95
12 changed files with 103 additions and 71 deletions
|
@ -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' }))
|
||||
})
|
||||
})
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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()
|
||||
})
|
|
@ -51,5 +51,8 @@ const update = async () => {
|
|||
emit('updated', mutatedPlaylist)
|
||||
}
|
||||
|
||||
const cancel = () => emit('cancelled')
|
||||
const cancel = () => {
|
||||
mutatedPlaylist.name = playlist.value.name
|
||||
emit('cancelled')
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue