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>
|
<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>
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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)
|
emit('updated', mutatedPlaylist)
|
||||||
}
|
}
|
||||||
|
|
||||||
const cancel = () => emit('cancelled')
|
const cancel = () => {
|
||||||
|
mutatedPlaylist.name = playlist.value.name
|
||||||
|
emit('cancelled')
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue