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 video from './video'
|
||||
import playlist from './playlist'
|
||||
import user from './user'
|
||||
import user, { states as userStates } from './user'
|
||||
|
||||
factory
|
||||
.define('artist', (faker: Faker): Artist => artist(faker))
|
||||
|
@ -13,6 +13,6 @@ factory
|
|||
.define('song', (faker: Faker): Song => song(faker))
|
||||
.define('video', (faker: Faker): YouTubeVideo => video(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
|
||||
|
|
|
@ -9,3 +9,9 @@ export default (faker: Faker): User => ({
|
|||
avatar: 'https://gravatar.com/foo',
|
||||
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>
|
||||
<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">
|
||||
<i class="fa fa-bars"></i>
|
||||
</span>
|
||||
<span class="magnifier" role="button" title="Show or hide the search form" @click="toggleSearchForm">
|
||||
<i class="fa fa-search"></i>
|
||||
</span>
|
||||
<search-form/>
|
||||
<SearchForm v-if="showSearchForm"/>
|
||||
<div class="header-right">
|
||||
<user-badge/>
|
||||
<UserBadge/>
|
||||
<button
|
||||
class="about control"
|
||||
data-testid="about-btn"
|
||||
|
@ -17,33 +17,29 @@
|
|||
type="button"
|
||||
@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!
|
||||
</span>
|
||||
<i v-else class="fa fa-info-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineAsyncComponent, toRef } from 'vue'
|
||||
import { defineAsyncComponent, ref } from 'vue'
|
||||
import { eventBus } from '@/utils'
|
||||
import { app as appConfig } from '@/config'
|
||||
import { commonStore, userStore } from '@/stores'
|
||||
import { useNewVersionNotification } from '@/composables'
|
||||
import isMobile from 'ismobilejs'
|
||||
|
||||
const SearchForm = defineAsyncComponent(() => import('@/components/ui/SearchForm.vue'))
|
||||
const UserBadge = defineAsyncComponent(() => import('@/components/user/UserBadge.vue'))
|
||||
|
||||
const user = toRef(userStore.state, 'current')
|
||||
const state = commonStore.state
|
||||
|
||||
const showSearchForm = ref(!isMobile.any)
|
||||
const { shouldNotifyNewVersion, latestVersion } = useNewVersionNotification()
|
||||
|
||||
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')
|
||||
</script>
|
||||
|
||||
|
@ -89,7 +85,7 @@ const showAboutDialog = () => eventBus.emit('MODAL_SHOW_ABOUT_KOEL')
|
|||
@media only screen and (max-width: 667px) {
|
||||
display: flex;
|
||||
align-content: stretch;
|
||||
justify-content: flext-start;
|
||||
justify-content: flex-start;
|
||||
|
||||
.hamburger, .magnifier {
|
||||
display: inline-block;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="side search" id="searchForm" :class="{ showing }" role="search">
|
||||
<div id="searchForm" class="side search" data-testid="search-form" role="search">
|
||||
<input
|
||||
ref="input"
|
||||
v-model="q"
|
||||
|
@ -16,16 +16,13 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import isMobile from 'ismobilejs'
|
||||
import { ref } from 'vue'
|
||||
import { debounce } from 'lodash'
|
||||
|
||||
import { eventBus } from '@/utils'
|
||||
import router from '@/router'
|
||||
|
||||
const input = ref<HTMLInputElement>()
|
||||
const q = ref('')
|
||||
const showing = ref(!isMobile.phone)
|
||||
|
||||
const onInput = debounce(() => {
|
||||
const _q = q.value.trim()
|
||||
|
@ -35,8 +32,6 @@ const onInput = debounce(() => {
|
|||
const goToSearchScreen = () => router.go('/search')
|
||||
|
||||
eventBus.on({
|
||||
'TOGGLE_SEARCH_FORM': () => (showing.value = !showing.value),
|
||||
|
||||
FOCUS_SEARCH_FIELD () {
|
||||
input.value?.focus()
|
||||
input.value?.select()
|
||||
|
@ -56,19 +51,14 @@ eventBus.on({
|
|||
}
|
||||
|
||||
@media only screen and (max-width: 667px) {
|
||||
z-index: -1;
|
||||
z-index: 100;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
background: var(--color-bg-primary);
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
top: 0;
|
||||
|
||||
&.showing {
|
||||
top: var(--header-height);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, .1);
|
||||
z-index: 100;
|
||||
}
|
||||
top: var(--header-height);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, .1);
|
||||
|
||||
input[type="search"] {
|
||||
width: 100%;
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
export const app = {
|
||||
name: 'Koel'
|
||||
}
|
|
@ -4,7 +4,6 @@ export type EventName =
|
|||
| 'LOAD_MAIN_CONTENT'
|
||||
| 'LOG_OUT'
|
||||
| 'TOGGLE_SIDEBAR'
|
||||
| 'TOGGLE_SEARCH_FORM'
|
||||
| 'SHOW_OVERLAY'
|
||||
| 'HIDE_OVERLAY'
|
||||
| 'FOCUS_SEARCH_FIELD'
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
export * from './app'
|
||||
export * from './events'
|
||||
export * from './upload.types'
|
||||
export * from './acceptedMediaTypes'
|
||||
|
|
|
@ -15,7 +15,6 @@ import {
|
|||
} from '@/stores'
|
||||
|
||||
import { audioService, socketService } from '@/services'
|
||||
import { app } from '@/config'
|
||||
import router from '@/router'
|
||||
|
||||
/**
|
||||
|
@ -156,7 +155,7 @@ export const playbackService = {
|
|||
return
|
||||
}
|
||||
|
||||
document.title = `${song.title} ♫ ${app.name}`
|
||||
document.title = `${song.title} ♫ Koel`
|
||||
this.player!.media.setAttribute('title', `${song.artist.name} - ${song.title}`)
|
||||
|
||||
if (queueStore.current) {
|
||||
|
@ -332,7 +331,7 @@ export const playbackService = {
|
|||
},
|
||||
|
||||
stop () {
|
||||
document.title = app.name
|
||||
document.title = 'Koel'
|
||||
this.getPlayer().pause()
|
||||
this.getPlayer().seek(0)
|
||||
|
||||
|
|
Loading…
Reference in a new issue