mirror of
https://github.com/koel/koel
synced 2024-11-10 06:34:14 +00:00
test: add FooterPlayerControls component tests
This commit is contained in:
parent
4e2351c85e
commit
4ddb6c6a44
11 changed files with 68 additions and 24 deletions
|
@ -91,11 +91,11 @@ context('Queuing', { scrollBehavior: false }, () => {
|
|||
cy.$shuffleSeveralSongs()
|
||||
cy.get('#queueWrapper .song-item:nth-child(1)').should('have.class', 'playing')
|
||||
|
||||
cy.findByTestId('play-next-btn').click({ force: true })
|
||||
cy.findByTitle('Play next song').click({ force: true })
|
||||
cy.get('#queueWrapper .song-item:nth-child(2)').should('have.class', 'playing')
|
||||
cy.$assertPlaying()
|
||||
|
||||
cy.findByTestId('play-prev-btn').click({ force: true })
|
||||
cy.findByTitle('Play previous song').click({ force: true })
|
||||
cy.get('#queueWrapper .song-item:nth-child(1)').should('have.class', 'playing')
|
||||
cy.$assertPlaying()
|
||||
})
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { vi } from 'vitest'
|
||||
import { noop } from '@/utils'
|
||||
|
||||
declare type Procedure = (...args: any[]) => any;
|
||||
declare type Methods<T> = { [K in keyof T]: T[K] extends Procedure ? K : never; }[keyof T] & (string | symbol);
|
||||
declare type Methods<T> = { [K in keyof T]: T[K] extends Closure ? K : never; }[keyof T] & (string | symbol);
|
||||
|
||||
export const mockHelper = {
|
||||
backup: new Map(),
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
import { beforeEach, expect, it } from 'vitest'
|
||||
import { cleanup, fireEvent } from '@testing-library/vue'
|
||||
import { mockHelper, render } from '@/__tests__/__helpers__'
|
||||
import { playbackService } from '@/services'
|
||||
import factory from '@/__tests__/factory'
|
||||
import FooterPlayerControls from './FooterPlayerControls.vue'
|
||||
|
||||
beforeEach(() => {
|
||||
cleanup()
|
||||
mockHelper.restoreAllMocks()
|
||||
})
|
||||
|
||||
declare type PlaybackMethod = {
|
||||
[K in keyof typeof playbackService]:
|
||||
typeof playbackService[K] extends Closure ? K : never;
|
||||
}[keyof typeof playbackService]
|
||||
|
||||
it.each<[string, string, PlaybackMethod]>([
|
||||
['plays next song', 'Play next song', 'playNext'],
|
||||
['plays previous song', 'Play previous song', 'playPrev'],
|
||||
['plays/resumes current song', 'Play or resume', 'toggle']
|
||||
])('%s', async (_: string, title: string, method: PlaybackMethod) => {
|
||||
const mock = mockHelper.mock(playbackService, method)
|
||||
|
||||
const { getByTitle } = render(FooterPlayerControls, {
|
||||
props: {
|
||||
song: factory<Song>('song')
|
||||
}
|
||||
})
|
||||
|
||||
await fireEvent.click(getByTitle(title))
|
||||
expect(mock).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('pauses the current song', async () => {
|
||||
const mock = mockHelper.mock(playbackService, 'toggle')
|
||||
|
||||
const { getByTitle } = render(FooterPlayerControls, {
|
||||
props: {
|
||||
song: factory<Song>('song', {
|
||||
playbackState: 'Playing'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
await fireEvent.click(getByTitle('Pause'))
|
||||
expect(mock).toHaveBeenCalled()
|
||||
})
|
|
@ -2,7 +2,6 @@
|
|||
<div class="side player-controls">
|
||||
<i
|
||||
class="prev fa fa-step-backward control"
|
||||
data-testid="play-prev-btn"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
title="Play previous song"
|
||||
|
@ -14,7 +13,6 @@
|
|||
<span
|
||||
v-if="shouldShowPlayButton"
|
||||
class="play"
|
||||
data-testid="play-btn"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
title="Play or resume"
|
||||
|
@ -37,7 +35,6 @@
|
|||
|
||||
<i
|
||||
class="next fa fa-step-forward control"
|
||||
data-testid="play-next-btn"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
title="Play next song"
|
||||
|
|
|
@ -6,7 +6,7 @@ import { alerts, pluralize } from '@/utils'
|
|||
* Includes the methods trigger-able on a song (context) menu.
|
||||
* Each component including this mixin must have a `songs` array as either data, prop, or computed.
|
||||
*/
|
||||
export const useSongMenuMethods = (songs: Ref<Song[]>, close: TAnyFunction) => {
|
||||
export const useSongMenuMethods = (songs: Ref<Song[]>, close: Closure) => {
|
||||
const queueSongsAfterCurrent = () => {
|
||||
queueStore.queueAfterCurrent(songs.value)
|
||||
close()
|
||||
|
|
|
@ -34,7 +34,7 @@ const router = {
|
|||
playbackService.queueAndPlay([song])
|
||||
}
|
||||
})
|
||||
} as { [path: string]: TAnyFunction },
|
||||
} as { [path: string]: Closure },
|
||||
|
||||
init () {
|
||||
this.loadState()
|
||||
|
|
|
@ -35,7 +35,7 @@ export const socketService = {
|
|||
return this
|
||||
},
|
||||
|
||||
listen (eventName: string, cb: TAnyFunction) {
|
||||
listen (eventName: string, cb: Closure) {
|
||||
this.channel && this.channel.bind(`client-${eventName}.${userStore.current.id}`, data => cb(data))
|
||||
return this
|
||||
}
|
||||
|
|
14
resources/assets/js/types.d.ts
vendored
14
resources/assets/js/types.d.ts
vendored
|
@ -17,18 +17,18 @@ declare module '*.svg' {
|
|||
export default value
|
||||
}
|
||||
|
||||
declare type TAnyFunction = (...args: Array<unknown | any>) => unknown | any
|
||||
declare type Closure = (...args: Array<unknown | any>) => unknown | any
|
||||
|
||||
declare module 'alertify.js' {
|
||||
function alert (msg: string): void
|
||||
|
||||
function confirm (msg: string, okFunc: TAnyFunction, cancelFunc?: TAnyFunction): void
|
||||
function confirm (msg: string, okFunc: Closure, cancelFunc?: Closure): void
|
||||
|
||||
function success (msg: string, cb?: TAnyFunction): void
|
||||
function success (msg: string, cb?: Closure): void
|
||||
|
||||
function error (msg: string, cb?: TAnyFunction): void
|
||||
function error (msg: string, cb?: Closure): void
|
||||
|
||||
function log (msg: string, cb?: TAnyFunction): void
|
||||
function log (msg: string, cb?: Closure): void
|
||||
|
||||
function logPosition (position: string): void
|
||||
|
||||
|
@ -101,7 +101,7 @@ interface Window {
|
|||
}
|
||||
|
||||
interface FileSystemDirectoryReader {
|
||||
readEntries (successCallback: TAnyFunction, errorCallback?: TAnyFunction): FileSystemEntry[]
|
||||
readEntries (successCallback: Closure, errorCallback?: Closure): FileSystemEntry[]
|
||||
}
|
||||
|
||||
interface FileSystemEntry {
|
||||
|
@ -113,7 +113,7 @@ interface FileSystemEntry {
|
|||
|
||||
createReader (): FileSystemDirectoryReader
|
||||
|
||||
file (successCallback: TAnyFunction): void
|
||||
file (successCallback: Closure): void
|
||||
}
|
||||
|
||||
interface AlbumTrack {
|
||||
|
|
|
@ -6,7 +6,7 @@ export const $ = {
|
|||
addClass: (el: Element | null, className: string) => el?.classList.add(className),
|
||||
removeClass: (el: Element | null, className: string) => el?.classList.remove(className),
|
||||
|
||||
scrollTo (el: Element, to: number, duration: number, cb?: TAnyFunction) {
|
||||
scrollTo (el: Element, to: number, duration: number, cb?: Closure) {
|
||||
if (duration <= 0 || !el) {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -9,21 +9,21 @@ const encodeEntities = (str: string) => str.replace(/&/g, '&')
|
|||
export const alerts = {
|
||||
alert: (msg: string) => alertify.alert(encodeEntities(msg)),
|
||||
|
||||
confirm: (msg: string, okFunc: TAnyFunction, cancelFunc?: TAnyFunction) => {
|
||||
confirm: (msg: string, okFunc: Closure, cancelFunc?: Closure) => {
|
||||
alertify.confirm(msg, okFunc, cancelFunc)
|
||||
},
|
||||
|
||||
log: (msg: string, type: logType = 'log', cb?: TAnyFunction) => {
|
||||
log: (msg: string, type: logType = 'log', cb?: Closure) => {
|
||||
alertify.logPosition('top right')
|
||||
alertify.closeLogOnClick(true)
|
||||
alertify[type](encodeEntities(msg), cb)
|
||||
},
|
||||
|
||||
success (msg: string, cb?: TAnyFunction) {
|
||||
success (msg: string, cb?: Closure) {
|
||||
this.log(msg, 'success', cb)
|
||||
},
|
||||
|
||||
error (msg: string, cb?: TAnyFunction) {
|
||||
error (msg: string, cb?: Closure) {
|
||||
this.log(msg, 'error', cb)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { EventName } from '@/config'
|
|||
export const eventBus = {
|
||||
all: new Map(),
|
||||
|
||||
on (name: EventName | EventName[] | Partial<{ [K in EventName]: TAnyFunction }>, callback?: TAnyFunction) {
|
||||
on (name: EventName | EventName[] | Partial<{ [K in EventName]: Closure }>, callback?: Closure) {
|
||||
if (Array.isArray(name)) {
|
||||
name.forEach(k => this.on(k, callback))
|
||||
return
|
||||
|
@ -26,7 +26,7 @@ export const eventBus = {
|
|||
|
||||
emit (name: EventName, ...args: any) {
|
||||
if (this.all.has(name)) {
|
||||
this.all.get(name).forEach((cb: TAnyFunction) => cb(...args))
|
||||
this.all.get(name).forEach((cb: Closure) => cb(...args))
|
||||
} else {
|
||||
console.warn(`Event ${name} is not listened by any component`)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue