mirror of
https://github.com/koel/koel
synced 2024-11-24 21:23:06 +00:00
feat(test): add AppHeader component tests
This commit is contained in:
parent
93c02a6b58
commit
93073814ca
9 changed files with 84 additions and 37 deletions
|
@ -5,7 +5,7 @@ import album from './album'
|
||||||
import song from './song'
|
import song from './song'
|
||||||
import video from './video'
|
import video from './video'
|
||||||
import playlist from './playlist'
|
import playlist from './playlist'
|
||||||
import user from './user'
|
import user, { states as userStates } from './user'
|
||||||
|
|
||||||
factory
|
factory
|
||||||
.define('artist', (faker: Faker): Artist => artist(faker))
|
.define('artist', (faker: Faker): Artist => artist(faker))
|
||||||
|
@ -13,6 +13,6 @@ factory
|
||||||
.define('song', (faker: Faker): Song => song(faker))
|
.define('song', (faker: Faker): Song => song(faker))
|
||||||
.define('video', (faker: Faker): YouTubeVideo => video(faker))
|
.define('video', (faker: Faker): YouTubeVideo => video(faker))
|
||||||
.define('playlist', (faker: Faker): Playlist => playlist(faker))
|
.define('playlist', (faker: Faker): Playlist => playlist(faker))
|
||||||
.define('user', (faker: Faker): User => user(faker))
|
.define('user', (faker: Faker): User => user(faker), userStates)
|
||||||
|
|
||||||
export default factory
|
export default factory
|
||||||
|
|
|
@ -9,3 +9,9 @@ export default (faker: Faker): User => ({
|
||||||
avatar: 'https://gravatar.com/foo',
|
avatar: 'https://gravatar.com/foo',
|
||||||
preferences: {}
|
preferences: {}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const states = {
|
||||||
|
admin: {
|
||||||
|
is_admin: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
61
resources/assets/js/components/layout/AppHeader.spec.ts
Normal file
61
resources/assets/js/components/layout/AppHeader.spec.ts
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import { beforeEach, expect, it } from 'vitest'
|
||||||
|
import { mockHelper, render } from '@/__tests__/__helpers__'
|
||||||
|
import { cleanup, fireEvent, queryAllByTestId } from '@testing-library/vue'
|
||||||
|
import { eventBus } from '@/utils'
|
||||||
|
import { nextTick } from 'vue'
|
||||||
|
import isMobile from 'ismobilejs'
|
||||||
|
import AppHeader from './AppHeader.vue'
|
||||||
|
import SearchForm from '@/components/ui/SearchForm.vue'
|
||||||
|
import compareVersions from 'compare-versions'
|
||||||
|
import { userStore } from '@/stores'
|
||||||
|
import factory from '@/__tests__/factory'
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cleanup()
|
||||||
|
mockHelper.restoreAllMocks()
|
||||||
|
isMobile.any = false
|
||||||
|
})
|
||||||
|
|
||||||
|
it('toggles sidebar (mobile only)', async () => {
|
||||||
|
isMobile.any = true
|
||||||
|
const { getByTitle } = render(AppHeader)
|
||||||
|
const mock = mockHelper.mock(eventBus, 'emit')
|
||||||
|
|
||||||
|
await fireEvent.click(getByTitle('Show or hide the sidebar'))
|
||||||
|
|
||||||
|
expect(mock).toHaveBeenCalledWith('TOGGLE_SIDEBAR')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('toggles search form (mobile only)', async () => {
|
||||||
|
isMobile.any = true
|
||||||
|
|
||||||
|
const { getByTitle, getByTestId, queryByTestId } = render(AppHeader, {
|
||||||
|
global: {
|
||||||
|
stubs: {
|
||||||
|
SearchForm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(await queryByTestId('search-form')).toBe(null)
|
||||||
|
|
||||||
|
await fireEvent.click(getByTitle('Show or hide the search form'))
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
getByTestId('search-form')
|
||||||
|
})
|
||||||
|
|
||||||
|
it.each([[true, true, true], [false, true, false], [true, false, false], [false, false, false]])(
|
||||||
|
'announces a new version if applicable',
|
||||||
|
async (hasNewVersion, isAdmin, announcing) => {
|
||||||
|
mockHelper.mock(compareVersions, 'compare', hasNewVersion)
|
||||||
|
|
||||||
|
userStore.state.current = factory<User>('user', {
|
||||||
|
is_admin: isAdmin
|
||||||
|
})
|
||||||
|
|
||||||
|
const { queryAllByTestId } = render(AppHeader)
|
||||||
|
|
||||||
|
expect(await queryAllByTestId('new-version')).toHaveLength(announcing ? 1 : 0)
|
||||||
|
}
|
||||||
|
)
|
|
@ -1,15 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<header id="mainHeader">
|
<header id="mainHeader">
|
||||||
<h1 class="brand" v-once>{{ appConfig.name }}</h1>
|
<h1 class="brand">Koel</h1>
|
||||||
<span class="hamburger" role="button" title="Show or hide the sidebar" @click="toggleSidebar">
|
<span class="hamburger" role="button" title="Show or hide the sidebar" @click="toggleSidebar">
|
||||||
<i class="fa fa-bars"></i>
|
<i class="fa fa-bars"></i>
|
||||||
</span>
|
</span>
|
||||||
<span class="magnifier" role="button" title="Show or hide the search form" @click="toggleSearchForm">
|
<span class="magnifier" role="button" title="Show or hide the search form" @click="toggleSearchForm">
|
||||||
<i class="fa fa-search"></i>
|
<i class="fa fa-search"></i>
|
||||||
</span>
|
</span>
|
||||||
<search-form/>
|
<SearchForm v-if="showSearchForm"/>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<user-badge/>
|
<UserBadge/>
|
||||||
<button
|
<button
|
||||||
class="about control"
|
class="about control"
|
||||||
data-testid="about-btn"
|
data-testid="about-btn"
|
||||||
|
@ -17,33 +17,29 @@
|
||||||
type="button"
|
type="button"
|
||||||
@click.prevent="showAboutDialog"
|
@click.prevent="showAboutDialog"
|
||||||
>
|
>
|
||||||
<span v-if="shouldNotifyNewVersion" class="new-version" data-test="new-version-available">
|
<span v-if="shouldNotifyNewVersion" class="new-version" data-testid="new-version">
|
||||||
{{ latestVersion }} available!
|
{{ latestVersion }} available!
|
||||||
</span>
|
</span>
|
||||||
<i v-else class="fa fa-info-circle"></i>
|
<i v-else class="fa fa-info-circle"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent, toRef } from 'vue'
|
import { defineAsyncComponent, ref } from 'vue'
|
||||||
import { eventBus } from '@/utils'
|
import { eventBus } from '@/utils'
|
||||||
import { app as appConfig } from '@/config'
|
|
||||||
import { commonStore, userStore } from '@/stores'
|
|
||||||
import { useNewVersionNotification } from '@/composables'
|
import { useNewVersionNotification } from '@/composables'
|
||||||
|
import isMobile from 'ismobilejs'
|
||||||
|
|
||||||
const SearchForm = defineAsyncComponent(() => import('@/components/ui/SearchForm.vue'))
|
const SearchForm = defineAsyncComponent(() => import('@/components/ui/SearchForm.vue'))
|
||||||
const UserBadge = defineAsyncComponent(() => import('@/components/user/UserBadge.vue'))
|
const UserBadge = defineAsyncComponent(() => import('@/components/user/UserBadge.vue'))
|
||||||
|
|
||||||
const user = toRef(userStore.state, 'current')
|
const showSearchForm = ref(!isMobile.any)
|
||||||
const state = commonStore.state
|
|
||||||
|
|
||||||
const { shouldNotifyNewVersion, latestVersion } = useNewVersionNotification()
|
const { shouldNotifyNewVersion, latestVersion } = useNewVersionNotification()
|
||||||
|
|
||||||
const toggleSidebar = () => eventBus.emit('TOGGLE_SIDEBAR')
|
const toggleSidebar = () => eventBus.emit('TOGGLE_SIDEBAR')
|
||||||
const toggleSearchForm = () => eventBus.emit('TOGGLE_SEARCH_FORM')
|
const toggleSearchForm = () => (showSearchForm.value = !showSearchForm.value)
|
||||||
const showAboutDialog = () => eventBus.emit('MODAL_SHOW_ABOUT_KOEL')
|
const showAboutDialog = () => eventBus.emit('MODAL_SHOW_ABOUT_KOEL')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -89,7 +85,7 @@ const showAboutDialog = () => eventBus.emit('MODAL_SHOW_ABOUT_KOEL')
|
||||||
@media only screen and (max-width: 667px) {
|
@media only screen and (max-width: 667px) {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-content: stretch;
|
align-content: stretch;
|
||||||
justify-content: flext-start;
|
justify-content: flex-start;
|
||||||
|
|
||||||
.hamburger, .magnifier {
|
.hamburger, .magnifier {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="side search" id="searchForm" :class="{ showing }" role="search">
|
<div id="searchForm" class="side search" data-testid="search-form" role="search">
|
||||||
<input
|
<input
|
||||||
ref="input"
|
ref="input"
|
||||||
v-model="q"
|
v-model="q"
|
||||||
|
@ -16,16 +16,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import isMobile from 'ismobilejs'
|
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { debounce } from 'lodash'
|
import { debounce } from 'lodash'
|
||||||
|
|
||||||
import { eventBus } from '@/utils'
|
import { eventBus } from '@/utils'
|
||||||
import router from '@/router'
|
import router from '@/router'
|
||||||
|
|
||||||
const input = ref<HTMLInputElement>()
|
const input = ref<HTMLInputElement>()
|
||||||
const q = ref('')
|
const q = ref('')
|
||||||
const showing = ref(!isMobile.phone)
|
|
||||||
|
|
||||||
const onInput = debounce(() => {
|
const onInput = debounce(() => {
|
||||||
const _q = q.value.trim()
|
const _q = q.value.trim()
|
||||||
|
@ -35,8 +32,6 @@ const onInput = debounce(() => {
|
||||||
const goToSearchScreen = () => router.go('/search')
|
const goToSearchScreen = () => router.go('/search')
|
||||||
|
|
||||||
eventBus.on({
|
eventBus.on({
|
||||||
'TOGGLE_SEARCH_FORM': () => (showing.value = !showing.value),
|
|
||||||
|
|
||||||
FOCUS_SEARCH_FIELD () {
|
FOCUS_SEARCH_FIELD () {
|
||||||
input.value?.focus()
|
input.value?.focus()
|
||||||
input.value?.select()
|
input.value?.select()
|
||||||
|
@ -56,19 +51,14 @@ eventBus.on({
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 667px) {
|
@media only screen and (max-width: 667px) {
|
||||||
z-index: -1;
|
z-index: 100;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
background: var(--color-bg-primary);
|
background: var(--color-bg-primary);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
top: 0;
|
|
||||||
|
|
||||||
&.showing {
|
|
||||||
top: var(--header-height);
|
top: var(--header-height);
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, .1);
|
border-bottom: 1px solid rgba(255, 255, 255, .1);
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="search"] {
|
input[type="search"] {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
export const app = {
|
|
||||||
name: 'Koel'
|
|
||||||
}
|
|
|
@ -4,7 +4,6 @@ export type EventName =
|
||||||
| 'LOAD_MAIN_CONTENT'
|
| 'LOAD_MAIN_CONTENT'
|
||||||
| 'LOG_OUT'
|
| 'LOG_OUT'
|
||||||
| 'TOGGLE_SIDEBAR'
|
| 'TOGGLE_SIDEBAR'
|
||||||
| 'TOGGLE_SEARCH_FORM'
|
|
||||||
| 'SHOW_OVERLAY'
|
| 'SHOW_OVERLAY'
|
||||||
| 'HIDE_OVERLAY'
|
| 'HIDE_OVERLAY'
|
||||||
| 'FOCUS_SEARCH_FIELD'
|
| 'FOCUS_SEARCH_FIELD'
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
export * from './app'
|
|
||||||
export * from './events'
|
export * from './events'
|
||||||
export * from './upload.types'
|
export * from './upload.types'
|
||||||
export * from './acceptedMediaTypes'
|
export * from './acceptedMediaTypes'
|
||||||
|
|
|
@ -15,7 +15,6 @@ import {
|
||||||
} from '@/stores'
|
} from '@/stores'
|
||||||
|
|
||||||
import { audioService, socketService } from '@/services'
|
import { audioService, socketService } from '@/services'
|
||||||
import { app } from '@/config'
|
|
||||||
import router from '@/router'
|
import router from '@/router'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -156,7 +155,7 @@ export const playbackService = {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
document.title = `${song.title} ♫ ${app.name}`
|
document.title = `${song.title} ♫ Koel`
|
||||||
this.player!.media.setAttribute('title', `${song.artist.name} - ${song.title}`)
|
this.player!.media.setAttribute('title', `${song.artist.name} - ${song.title}`)
|
||||||
|
|
||||||
if (queueStore.current) {
|
if (queueStore.current) {
|
||||||
|
@ -332,7 +331,7 @@ export const playbackService = {
|
||||||
},
|
},
|
||||||
|
|
||||||
stop () {
|
stop () {
|
||||||
document.title = app.name
|
document.title = 'Koel'
|
||||||
this.getPlayer().pause()
|
this.getPlayer().pause()
|
||||||
this.getPlayer().seek(0)
|
this.getPlayer().seek(0)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue