Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • funkwhale/funkwhale
  • Luclu7/funkwhale
  • mbothorel/funkwhale
  • EorlBruder/funkwhale
  • tcit/funkwhale
  • JocelynDelalande/funkwhale
  • eneiluj/funkwhale
  • reg/funkwhale
  • ButterflyOfFire/funkwhale
  • m4sk1n/funkwhale
  • wxcafe/funkwhale
  • andybalaam/funkwhale
  • jcgruenhage/funkwhale
  • pblayo/funkwhale
  • joshuaboniface/funkwhale
  • n3ddy/funkwhale
  • gegeweb/funkwhale
  • tohojo/funkwhale
  • emillumine/funkwhale
  • Te-k/funkwhale
  • asaintgenis/funkwhale
  • anoadragon453/funkwhale
  • Sakada/funkwhale
  • ilianaw/funkwhale
  • l4p1n/funkwhale
  • pnizet/funkwhale
  • dante383/funkwhale
  • interfect/funkwhale
  • akhardya/funkwhale
  • svfusion/funkwhale
  • noplanman/funkwhale
  • nykopol/funkwhale
  • roipoussiere/funkwhale
  • Von/funkwhale
  • aurieh/funkwhale
  • icaria36/funkwhale
  • floreal/funkwhale
  • paulwalko/funkwhale
  • comradekingu/funkwhale
  • FurryJulie/funkwhale
  • Legolars99/funkwhale
  • Vierkantor/funkwhale
  • zachhats/funkwhale
  • heyjake/funkwhale
  • sn0w/funkwhale
  • jvoisin/funkwhale
  • gordon/funkwhale
  • Alexander/funkwhale
  • bignose/funkwhale
  • qasim.ali/funkwhale
  • fakegit/funkwhale
  • Kxze/funkwhale
  • stenstad/funkwhale
  • creak/funkwhale
  • Kaze/funkwhale
  • Tixie/funkwhale
  • IISergII/funkwhale
  • lfuelling/funkwhale
  • nhaddag/funkwhale
  • yoasif/funkwhale
  • ifischer/funkwhale
  • keslerm/funkwhale
  • flupe/funkwhale
  • petitminion/funkwhale
  • ariasuni/funkwhale
  • ollie/funkwhale
  • ngaumont/funkwhale
  • techknowlogick/funkwhale
  • Shleeble/funkwhale
  • theflyingfrog/funkwhale
  • jonatron/funkwhale
  • neobrain/funkwhale
  • eorn/funkwhale
  • KokaKiwi/funkwhale
  • u1-liquid/funkwhale
  • marzzzello/funkwhale
  • sirenwatcher/funkwhale
  • newer027/funkwhale
  • codl/funkwhale
  • Zwordi/funkwhale
  • gisforgabriel/funkwhale
  • iuriatan/funkwhale
  • simon/funkwhale
  • bheesham/funkwhale
  • zeoses/funkwhale
  • accraze/funkwhale
  • meliurwen/funkwhale
  • divadsn/funkwhale
  • Etua/funkwhale
  • sdrik/funkwhale
  • Soran/funkwhale
  • kuba-orlik/funkwhale
  • cristianvogel/funkwhale
  • Forceu/funkwhale
  • jeff/funkwhale
  • der_scheibenhacker/funkwhale
  • owlnical/funkwhale
  • jovuit/funkwhale
  • SilverFox15/funkwhale
  • phw/funkwhale
  • mayhem/funkwhale
  • sridhar/funkwhale
  • stromlin/funkwhale
  • rrrnld/funkwhale
  • nitaibezerra/funkwhale
  • jaller94/funkwhale
  • pcouy/funkwhale
  • eduxstad/funkwhale
  • codingHahn/funkwhale
  • captain/funkwhale
  • polyedre/funkwhale
  • leishenailong/funkwhale
  • ccritter/funkwhale
  • lnceballosz/funkwhale
  • fpiesche/funkwhale
  • Fanyx/funkwhale
  • markusblogde/funkwhale
  • Firobe/funkwhale
  • devilcius/funkwhale
  • freaktechnik/funkwhale
  • blopware/funkwhale
  • cone/funkwhale
  • thanksd/funkwhale
  • vachan-maker/funkwhale
  • bbenti/funkwhale
  • tarator/funkwhale
  • prplecake/funkwhale
  • DMarzal/funkwhale
  • lullis/funkwhale
  • hanacgr/funkwhale
  • albjeremias/funkwhale
  • xeruf/funkwhale
  • llelite/funkwhale
  • RoiArthurB/funkwhale
  • cloo/funkwhale
  • nztvar/funkwhale
  • Keunes/funkwhale
  • petitminion/funkwhale-petitminion
  • m-idler/funkwhale
  • SkyLeite/funkwhale
140 results
Select Git revision
Show changes
Commits on Source (3)
Showing
with 1813 additions and 17 deletions
...@@ -212,6 +212,8 @@ test_front: ...@@ -212,6 +212,8 @@ test_front:
- front/dist/ - front/dist/
reports: reports:
junit: front/test-results.xml junit: front/test-results.xml
cobertura: front/coverage/cobertura-coverage.xml
coverage: '/Statements\s*:\s\d+\.\d+%\s\(\s\d+\/\d+\s\)/'
tags: tags:
- docker - docker
......
...@@ -9,8 +9,8 @@ ...@@ -9,8 +9,8 @@
"build": "vite build", "build": "vite build",
"build:deployment": "vite build --base /front/", "build:deployment": "vite build --base /front/",
"serve": "vite preview", "serve": "vite preview",
"test:unit": "true", "test:unit": "vitest run --dom --coverage",
"lint": "eslint --ext .js,.vue src", "lint": "eslint --ext .js,.vue src tests",
"fix-fomantic-css": "scripts/fix-fomantic-css.sh", "fix-fomantic-css": "scripts/fix-fomantic-css.sh",
"i18n-compile": "scripts/i18n-compile.sh", "i18n-compile": "scripts/i18n-compile.sh",
"i18n-extract": "scripts/i18n-extract.sh", "i18n-extract": "scripts/i18n-extract.sh",
...@@ -45,7 +45,9 @@ ...@@ -45,7 +45,9 @@
"vuex-router-sync": "5.0.0" "vuex-router-sync": "5.0.0"
}, },
"devDependencies": { "devDependencies": {
"@vue/test-utils": "1",
"autoprefixer": "10.4.2", "autoprefixer": "10.4.2",
"c8": "^7.11.0",
"easygettext": "2.17.0", "easygettext": "2.17.0",
"eslint": "8.9.0", "eslint": "8.9.0",
"eslint-config-standard": "16.0.3", "eslint-config-standard": "16.0.3",
...@@ -55,8 +57,11 @@ ...@@ -55,8 +57,11 @@
"eslint-plugin-promise": "6.0.0", "eslint-plugin-promise": "6.0.0",
"eslint-plugin-vue": "7.20.0", "eslint-plugin-vue": "7.20.0",
"glob-all": "3.2.1", "glob-all": "3.2.1",
"happy-dom": "^2.41.0",
"moxios": "^0.4.0",
"vite": "2.8.4", "vite": "2.8.4",
"vite-plugin-vue2": "1.9.3", "vite-plugin-vue2": "1.9.3",
"vitest": "^0.5.9",
"vue-template-compiler": "2.6.14" "vue-template-compiler": "2.6.14"
}, },
"eslintConfig": { "eslintConfig": {
......
...@@ -132,7 +132,6 @@ export default { ...@@ -132,7 +132,6 @@ export default {
actions: { actions: {
// Send a request to the login URL and save the returned JWT // Send a request to the login URL and save the returned JWT
login ({ commit, dispatch }, { next, credentials, onError }) { login ({ commit, dispatch }, { next, credentials, onError }) {
const router = require('@/router').default
const form = new FormData() const form = new FormData()
Object.keys(credentials).forEach((k) => { Object.keys(credentials).forEach((k) => {
form.set(k, credentials[k]) form.set(k, credentials[k])
...@@ -142,7 +141,7 @@ export default { ...@@ -142,7 +141,7 @@ export default {
// commit('token', response.data.token) // commit('token', response.data.token)
dispatch('fetchProfile').then(() => { dispatch('fetchProfile').then(() => {
// Redirect to a specified route // Redirect to a specified route
router.push(next) this.$router.push(next)
}) })
}, response => { }, response => {
logger.default.error('Error while logging in', response.data) logger.default.error('Error while logging in', response.data)
......
{
"env": {
"mocha": true
},
"globals": {
"expect": true,
"sinon": true
}
}
import { describe, it, expect } from 'vitest'
import { toLinearVolumeScale, toLogarithmicVolumeScale } from '@/audio/volume'
describe('store/auth', () => {
describe('toLinearVolumeScale', () => {
it('it should return real 0', () => {
expect(toLinearVolumeScale(0.0)).to.equal(0.0)
})
it('it should return full volume', () => {
expect(toLogarithmicVolumeScale(1.0)).to.be.closeTo(1.0, 0.001)
})
})
describe('toLogarithmicVolumeScale', () => {
it('it should return real 0', () => {
expect(toLogarithmicVolumeScale(0.0)).to.equal(0.0)
})
it('it should return full volume', () => {
expect(toLogarithmicVolumeScale(1.0)).to.be.closeTo(1.0, 0.001)
})
})
})
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import Username from '@/components/common/Username.vue'
describe('Username', () => {
it('displays username', () => {
const wrapper = mount(Username, {
propsData: {
username: 'Hello'
}
})
expect(wrapper.text()).to.equal('Hello')
})
})
import { describe, it, expect } from 'vitest'
import PasswordInput from '@/components/forms/PasswordInput.vue'
import { shallowMount } from '@vue/test-utils'
describe('PasswordInput', () => {
const password = 'password'
const wrapper = shallowMount(PasswordInput, {
mocks: {
$pgettext: () => 'dummy',
$store: {
commit: () => { }
}
}
})
wrapper.setProps({ value: password, copyButton: true })
it('password input has passed value', () => {
const inputElement = wrapper.find('input')
expect(inputElement.element.value).to.equal(password)
})
// it('copy password function called', () => {
// const spy = sandbox.spy()
// wrapper.setMethods({
// copyPassword: spy
// })
// sandbox.stub(PasswordInput.methods, '_copyStringToClipboard').callsFake()
// const copyButton = wrapper.findAll('button').at(1)
// copyButton.trigger('click')
// sandbox.assert.calledOnce(spy)
// })
})
import { describe, it, expect } from 'vitest'
import moment from 'moment'
import { truncate, ago, capitalize, year } from '@/filters'
describe('filters', () => {
describe('truncate', () => {
it('leave strings as it if correct size', () => {
const input = 'Hello world'
const output = truncate(input, 100)
expect(output).to.equal(input)
})
it('returns shorter string with character', () => {
const input = 'Hello world'
const output = truncate(input, 5)
expect(output).to.equal('Hello…')
})
it('custom ellipsis', () => {
const input = 'Hello world'
const output = truncate(input, 5, ' pouet')
expect(output).to.equal('Hello pouet')
})
})
describe('ago', () => {
it('works', () => {
const input = new Date()
const output = ago(input)
const expected = moment(input).calendar(input, {
sameDay: 'LT',
nextDay: 'L',
nextWeek: 'L',
lastDay: 'L',
lastWeek: 'L',
sameElse: 'L'
})
expect(output).to.equal(expected)
})
})
describe('year', () => {
it('works', () => {
const input = '2017-07-13'
const output = year(input)
expect(output).to.equal(2017)
})
})
describe('capitalize', () => {
it('works', () => {
const input = 'hello world'
const output = capitalize(input)
expect(output).to.equal('Hello world')
})
})
})
import { describe, it, expect } from 'vitest'
import { normalizeQuery, parseTokens, compileTokens } from '@/search'
describe('search', () => {
it('normalizeQuery returns correct tokens', () => {
const input = 'this is a "search query" yeah'
const output = normalizeQuery(input)
expect(output).to.deep.equal(['this', 'is', 'a', 'search query', 'yeah'])
})
it('parseTokens can extract fields and values from tokens', () => {
const input = ['unhandled', 'key:value', 'status:pending', 'title:"some title"', 'anotherunhandled']
const output = parseTokens(input)
const expected = [
{
field: null,
value: 'unhandled'
},
{
field: 'key',
value: 'value'
},
{
field: 'status',
value: 'pending'
},
{
field: 'title',
value: 'some title'
},
{
field: null,
value: 'anotherunhandled'
}
]
expect(output).to.deep.equal(expected)
})
it('compileTokens returns proper query string', () => {
const input = [
{
field: null,
value: 'unhandled'
},
{
field: 'key',
value: 'value'
},
{
field: 'status',
value: 'pending'
},
{
field: 'title',
value: 'some title'
},
{
field: null,
value: 'anotherunhandled'
}
]
const expected = 'unhandled key:value status:pending title:"some title" anotherunhandled'
const output = compileTokens(input)
expect(output).to.deep.equal(expected)
})
})
import { describe, it, expect } from 'vitest'
import store from '@/store/auth'
import { testAction } from '../../utils'
describe('store/auth', () => {
describe('mutations', () => {
it('profile', () => {
const state = {}
store.mutations.profile(state, {})
expect(state.profile).to.deep.equal({})
})
it('username', () => {
const state = {}
store.mutations.username(state, 'world')
expect(state.username).to.equal('world')
})
it('authenticated true', () => {
const state = {}
store.mutations.authenticated(state, true)
expect(state.authenticated).to.equal(true)
})
it('authenticated false', () => {
const state = {
username: 'dummy',
token: 'dummy',
profile: 'dummy',
availablePermissions: 'dummy'
}
store.mutations.authenticated(state, false)
expect(state.authenticated).to.equal(false)
expect(state.username).to.equal(null)
expect(state.token).to.equal(null)
expect(state.profile).to.equal(null)
expect(state.availablePermissions).to.deep.equal({})
})
it('token null', () => {
const state = {}
store.mutations.token(state, null)
expect(state.token).to.equal(null)
})
it('token real', () => {
const state = {}
const token = 'eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJodHRwczovL2p3dC1pZHAuZXhhbXBsZS5jb20iLCJzdWIiOiJtYWlsdG86bWlrZUBleGFtcGxlLmNvbSIsIm5iZiI6MTUxNTUzMzQyOSwiZXhwIjoxNTE1NTM3MDI5LCJpYXQiOjE1MTU1MzM0MjksImp0aSI6ImlkMTIzNDU2IiwidHlwIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9yZWdpc3RlciJ9.'
store.mutations.token(state, token)
expect(state.token).to.equal(token)
})
it('permissions', () => {
const state = { availablePermissions: {} }
store.mutations.permission(state, { key: 'admin', status: true })
expect(state.availablePermissions).to.deep.equal({ admin: true })
})
})
describe('getters', () => {
it('header', () => {
const state = { oauth: { accessToken: 'helloworld' } }
expect(store.getters.header(state)).to.equal('Bearer helloworld')
})
})
describe('actions', () => {
it('logout', () => {
testAction({
action: store.actions.logout,
params: { state: {} },
expectedMutations: [
{ type: 'auth/reset', payload: null, options: { root: true } },
{ type: 'favorites/reset', payload: null, options: { root: true } },
{ type: 'player/reset', payload: null, options: { root: true } },
{ type: 'playlists/reset', payload: null, options: { root: true } },
{ type: 'queue/reset', payload: null, options: { root: true } },
{ type: 'radios/reset', payload: null, options: { root: true } }
]
})
})
it('check jwt null', () => {
testAction({
action: store.actions.check,
params: { state: {} },
expectedMutations: [
{ type: 'authenticated', payload: false },
{ type: 'authenticated', payload: true }
],
expectedActions: [
{ type: 'fetchProfile' }
]
})
})
it('fetchProfile', () => {
const profile = {
username: 'bob',
permissions: {
admin: true
}
}
testAction({
action: store.actions.fetchProfile,
expectedMutations: [
{ type: 'authenticated', payload: true },
{ type: 'profile', payload: profile },
{ type: 'username', payload: profile.username },
{ type: 'permission', payload: { key: 'admin', status: true } }
],
expectedActions: [
{ type: 'ui/initSettings', payload: { root: true } },
{ type: 'updateProfile', payload: profile },
{ type: 'ui/fetchUnreadNotifications', payload: null },
{ type: 'favorites/fetch', payload: null, options: { root: true } },
{ type: 'channels/fetchSubscriptions', payload: null, options: { root: true } },
{ type: 'libraries/fetchFollows', payload: null, options: { root: true } },
{ type: 'moderation/fetchContentFilters', payload: null, options: { root: true } },
{ type: 'playlists/fetchOwn', payload: null, options: { root: true } }
]
})
})
})
})
import { describe, it, expect } from 'vitest'
import store from '@/store/favorites'
import { testAction } from '../../utils'
describe('store/favorites', () => {
describe('mutations', () => {
it('track true', () => {
const state = { tracks: [] }
store.mutations.track(state, { id: 1, value: true })
expect(state.tracks).to.deep.equal([1])
expect(state.count).to.deep.equal(1)
})
it('track false', () => {
const state = { tracks: [1] }
store.mutations.track(state, { id: 1, value: false })
expect(state.tracks).to.deep.equal([])
expect(state.count).to.deep.equal(0)
})
})
describe('getters', () => {
it('isFavorite true', () => {
const state = { tracks: [1] }
expect(store.getters.isFavorite(state)(1)).to.equal(true)
})
it('isFavorite false', () => {
const state = { tracks: [] }
expect(store.getters.isFavorite(state)(1)).to.equal(false)
})
})
describe('actions', () => {
it('toggle true', () => {
testAction({
action: store.actions.toggle,
payload: 1,
params: { getters: { isFavorite: () => false } },
expectedActions: [
{ type: 'set', payload: { id: 1, value: true } }
]
})
})
it('toggle true', () => {
testAction({
action: store.actions.toggle,
payload: 1,
params: { getters: { isFavorite: () => true } },
expectedActions: [
{ type: 'set', payload: { id: 1, value: false } }
]
})
})
})
})
import { describe, it, expect } from 'vitest'
import store from '@/store/instance'
import { testAction } from '../../utils'
describe('store/instance', () => {
describe('mutations', () => {
it('settings', () => {
const state = { settings: { users: { upload_quota: { value: 1 } } } }
const settings = { users: { registration_enabled: { value: true } } }
store.mutations.settings(state, settings)
expect(state.settings).to.deep.equal({
users: { upload_quota: { value: 1 }, registration_enabled: { value: true } }
})
})
it('instanceUrl', () => {
const state = { instanceUrl: null, knownInstances: ['http://test2/', 'http://test/'] }
store.mutations.instanceUrl(state, 'http://test')
expect(state).to.deep.equal({
instanceUrl: 'http://test/', // trailing slash added
knownInstances: ['http://test/', 'http://test2/']
})
})
})
describe('actions', () => {
it('fetchSettings', () => {
testAction({
action: store.actions.fetchSettings,
payload: null,
expectedMutations: [
{
type: 'settings',
payload: {
users: {
upload_quota: {
section: 'users',
name: 'upload_quota',
value: 1
},
registration_enabled: {
section: 'users',
name: 'registration_enabled',
value: false
}
}
}
}
]
})
})
})
})
import { describe, it, expect } from 'vitest'
import store from '@/store/player'
import { testAction } from '../../utils'
describe('store/player', () => {
describe('mutations', () => {
it('set volume', () => {
const state = { volume: 0 }
store.mutations.volume(state, 0.9)
expect(state.volume).to.equal(0.9)
})
it('set volume max 1', () => {
const state = { volume: 0 }
store.mutations.volume(state, 2)
expect(state.volume).to.equal(1)
})
it('set volume min to 0', () => {
const state = { volume: 0.5 }
store.mutations.volume(state, -2)
expect(state.volume).to.equal(0)
})
it('increment volume', () => {
const state = { volume: 0 }
store.mutations.incrementVolume(state, 0.1)
expect(state.volume).to.equal(0.1)
})
it('increment volume max 1', () => {
const state = { volume: 0 }
store.mutations.incrementVolume(state, 2)
expect(state.volume).to.equal(1)
})
it('increment volume min to 0', () => {
const state = { volume: 0.5 }
store.mutations.incrementVolume(state, -2)
expect(state.volume).to.equal(0)
})
it('set duration', () => {
const state = { duration: 42 }
store.mutations.duration(state, 14)
expect(state.duration).to.equal(14)
})
it('set errored', () => {
const state = { errored: false }
store.mutations.errored(state, true)
expect(state.errored).to.equal(true)
})
it('set looping', () => {
const state = { looping: 1 }
store.mutations.looping(state, 2)
expect(state.looping).to.equal(2)
})
it('set playing', () => {
const state = { playing: false }
store.mutations.playing(state, true)
expect(state.playing).to.equal(true)
})
it('set current time', () => {
const state = { currentTime: 1 }
store.mutations.currentTime(state, 2)
expect(state.currentTime).to.equal(2)
})
it('toggle looping from 0', () => {
const state = { looping: 0 }
store.mutations.toggleLooping(state)
expect(state.looping).to.equal(1)
})
it('toggle looping from 1', () => {
const state = { looping: 1 }
store.mutations.toggleLooping(state)
expect(state.looping).to.equal(2)
})
it('toggle looping from 2', () => {
const state = { looping: 2 }
store.mutations.toggleLooping(state)
expect(state.looping).to.equal(0)
})
it('increment error count', () => {
const state = { errorCount: 0 }
store.mutations.incrementErrorCount(state)
expect(state.errorCount).to.equal(1)
})
it('reset error count', () => {
const state = { errorCount: 10 }
store.mutations.resetErrorCount(state)
expect(state.errorCount).to.equal(0)
})
})
describe('getters', () => {
it('durationFormatted', () => {
const state = { duration: 12.51 }
expect(store.getters.durationFormatted(state)).to.equal('0:13')
})
it('currentTimeFormatted', () => {
const state = { currentTime: 12.51 }
expect(store.getters.currentTimeFormatted(state)).to.equal('0:13')
})
it('progress', () => {
const state = { currentTime: 4, duration: 10 }
expect(store.getters.progress(state)).to.equal(40)
})
})
describe('actions', () => {
it('incrementVolume', () => {
testAction({
action: store.actions.incrementVolume,
payload: 0.2,
params: { state: { volume: 0.7 } },
expectedMutations: [
{ type: 'volume', payload: 0.7 + 0.2 }
]
})
})
it('toggle playback false', () => {
testAction({
action: store.actions.togglePlayback,
params: { state: { playing: false } },
expectedMutations: [
{ type: 'playing', payload: true }
]
})
})
it('toggle playback true', () => {
testAction({
action: store.actions.togglePlayback,
params: { state: { playing: true } },
expectedMutations: [
{ type: 'playing', payload: false }
]
})
})
it('resume playback', () => {
testAction({
action: store.actions.resumePlayback,
params: { state: {} },
expectedMutations: [
{ type: 'playing', payload: true }
]
})
})
it('pause playback', () => {
testAction({
action: store.actions.pausePlayback,
expectedMutations: [
{ type: 'playing', payload: false }
]
})
})
it('trackEnded', () => {
testAction({
action: store.actions.trackEnded,
payload: { test: 'track' },
params: { rootState: { queue: { currentIndex: 0, tracks: [1, 2] } } },
expectedActions: [
{ type: 'queue/next', payload: null, options: { root: true } }
]
})
})
it('trackEnded calls populateQueue if last', () => {
testAction({
action: store.actions.trackEnded,
payload: { test: 'track' },
params: { rootState: { queue: { currentIndex: 1, tracks: [1, 2] } } },
expectedActions: [
{ type: 'radios/populateQueue', payload: null, options: { root: true } },
{ type: 'queue/next', payload: null, options: { root: true } }
]
})
})
it('trackErrored', () => {
testAction({
action: store.actions.trackErrored,
payload: { test: 'track' },
params: { state: { errorCount: 0, maxConsecutiveErrors: 5 } },
expectedMutations: [
{ type: 'errored', payload: true },
{ type: 'incrementErrorCount' }
],
expectedActions: [
{ type: 'queue/next', payload: null, options: { root: true } }
]
})
})
it('updateProgress', () => {
testAction({
action: store.actions.updateProgress,
payload: 1,
expectedMutations: [
{ type: 'currentTime', payload: 1 }
]
})
})
it('mute', () => {
testAction({
action: store.actions.mute,
params: { state: { volume: 0.7, tempVolume: 0 } },
expectedMutations: [
{ type: 'tempVolume', payload: 0.7 },
{ type: 'volume', payload: 0 }
]
})
})
it('unmute', () => {
testAction({
action: store.actions.unmute,
params: { state: { volume: 0, tempVolume: 0.8 } },
expectedMutations: [
{ type: 'volume', payload: 0.8 }
]
})
})
})
})
import { describe, it, expect } from 'vitest'
import store from '@/store/playlists'
import { testAction } from '../../utils'
describe('store/playlists', () => {
describe('mutations', () => {
it('set playlists', () => {
const state = { playlists: [] }
store.mutations.playlists(state, [{ id: 1, name: 'test' }])
expect(state.playlists).to.deep.equal([{ id: 1, name: 'test' }])
})
})
describe('actions', () => {
it('fetchOwn does nothing with no user', () => {
testAction({
action: store.actions.fetchOwn,
payload: null,
params: { state: { playlists: [] }, rootState: { auth: { profile: {} } } },
expectedMutations: []
})
})
})
})
import { it, describe, expect } from 'vitest'
import store from '@/store/queue'
import { testAction } from '../../utils'
describe('store/queue', () => {
describe('mutations', () => {
it('currentIndex', () => {
const state = {}
store.mutations.currentIndex(state, 2)
expect(state.currentIndex).to.equal(2)
})
it('ended', () => {
const state = {}
store.mutations.ended(state, false)
expect(state.ended).to.equal(false)
})
it('tracks', () => {
const state = {}
store.mutations.tracks(state, [1, 2])
expect(state.tracks).to.deep.equal([1, 2])
})
it('splice', () => {
const state = { tracks: [1, 2, 3] }
store.mutations.splice(state, { start: 1, size: 2 })
expect(state.tracks).to.deep.equal([1])
})
it('insert', () => {
const state = { tracks: [1, 3] }
store.mutations.insert(state, { track: 2, index: 1 })
expect(state.tracks).to.deep.equal([1, 2, 3])
})
it('reorder before', () => {
const state = { currentIndex: 3 }
store.mutations.reorder(state, { oldIndex: 2, newIndex: 1 })
expect(state.currentIndex).to.equal(3)
})
it('reorder from after to before', () => {
const state = { currentIndex: 3 }
store.mutations.reorder(state, { oldIndex: 4, newIndex: 1 })
expect(state.currentIndex).to.equal(4)
})
it('reorder after', () => {
const state = { currentIndex: 3 }
store.mutations.reorder(state, { oldIndex: 4, newIndex: 5 })
expect(state.currentIndex).to.equal(3)
})
it('reorder before to after', () => {
const state = { currentIndex: 3 }
store.mutations.reorder(state, { oldIndex: 1, newIndex: 5 })
expect(state.currentIndex).to.equal(2)
})
it('reorder current', () => {
const state = { currentIndex: 3 }
store.mutations.reorder(state, { oldIndex: 3, newIndex: 1 })
expect(state.currentIndex).to.equal(1)
})
})
describe('getters', () => {
it('currentTrack', () => {
const state = { tracks: [1, 2, 3], currentIndex: 2 }
expect(store.getters.currentTrack(state)).to.equal(3)
})
it('hasNext true', () => {
const state = { tracks: [1, 2, 3], currentIndex: 1 }
expect(store.getters.hasNext(state)).to.equal(true)
})
it('hasNext false', () => {
const state = { tracks: [1, 2, 3], currentIndex: 2 }
expect(store.getters.hasNext(state)).to.equal(false)
})
})
describe('actions', () => {
it('append at end', () => {
testAction({
action: store.actions.append,
payload: { track: 4 },
params: { state: { tracks: [1, 2, 3] } },
expectedMutations: [
{ type: 'insert', payload: { track: 4, index: 3 } }
]
})
})
it('append at index', () => {
testAction({
action: store.actions.append,
payload: { track: 2, index: 1 },
params: { state: { tracks: [1, 3] } },
expectedMutations: [
{ type: 'insert', payload: { track: 2, index: 1 } }
]
})
})
it('appendMany', () => {
const tracks = [{ title: 1 }, { title: 2 }]
testAction({
action: store.actions.appendMany,
payload: { tracks: tracks },
params: { state: { tracks: [] } },
expectedActions: [
{ type: 'append', payload: { track: tracks[0], index: 0 } },
{ type: 'append', payload: { track: tracks[1], index: 1 } }
]
})
})
it('appendMany at index', () => {
const tracks = [{ title: 1 }, { title: 2 }]
testAction({
action: store.actions.appendMany,
payload: { tracks: tracks, index: 1 },
params: { state: { tracks: [1, 2] } },
expectedActions: [
{ type: 'append', payload: { track: tracks[0], index: 1 } },
{ type: 'append', payload: { track: tracks[1], index: 2 } }
]
})
})
it('cleanTrack after current', () => {
testAction({
action: store.actions.cleanTrack,
payload: 3,
params: { state: { currentIndex: 2, tracks: [1, 2, 3, 4, 5] } },
expectedMutations: [
{ type: 'splice', payload: { start: 3, size: 1 } }
]
})
})
it('cleanTrack before current', () => {
testAction({
action: store.actions.cleanTrack,
payload: 1,
params: { state: { currentIndex: 2, tracks: [] } },
expectedMutations: [
{ type: 'splice', payload: { start: 1, size: 1 } },
{ type: 'currentIndex', payload: 1 }
]
})
})
it('cleanTrack current', () => {
testAction({
action: store.actions.cleanTrack,
payload: 2,
params: { state: { currentIndex: 2, tracks: [] } },
expectedMutations: [
{ type: 'splice', payload: { start: 2, size: 1 } },
{ type: 'currentIndex', payload: 2 }
],
expectedActions: [
{ type: 'player/stop', payload: null, options: { root: true } }
]
})
})
it('cleanTrack current is last', () => {
testAction({
action: store.actions.cleanTrack,
payload: 5,
params: { state: { currentIndex: 5, tracks: [1, 2, 3, 4, 5] } },
expectedMutations: [
{ type: 'splice', payload: { start: 5, size: 1 } },
{ type: 'currentIndex', payload: 4 }
],
expectedActions: [
{ type: 'player/stop', payload: null, options: { root: true } }
]
})
})
it('previous when at beginning', () => {
testAction({
action: store.actions.previous,
params: { state: { currentIndex: 0 } },
expectedActions: [
{ type: 'currentIndex', payload: 0 }
]
})
})
it('previous after less than 3 seconds of playback', () => {
testAction({
action: store.actions.previous,
params: { state: { currentIndex: 1 }, rootState: { player: { currentTime: 1 } } },
expectedActions: [
{ type: 'currentIndex', payload: 0 }
]
})
})
it('previous after more than 3 seconds of playback', () => {
testAction({
action: store.actions.previous,
params: { state: { currentIndex: 1 }, rootState: { player: { currentTime: 3 } } },
expectedActions: [
{ type: 'currentIndex', payload: 1 }
]
})
})
it('next on last track when looping on queue', () => {
testAction({
action: store.actions.next,
params: { state: { tracks: [1, 2], currentIndex: 1 }, rootState: { player: { looping: 2 } } },
expectedActions: [
{ type: 'currentIndex', payload: 0 }
]
})
})
it('next track when last track', () => {
testAction({
action: store.actions.next,
params: { state: { tracks: [1, 2], currentIndex: 1 }, rootState: { player: { looping: 0 } } },
expectedMutations: [
{ type: 'ended', payload: true }
]
})
})
it('next track when not last track', () => {
testAction({
action: store.actions.next,
params: { state: { tracks: [1, 2], currentIndex: 0 }, rootState: { player: { looping: 0 } } },
expectedActions: [
{ type: 'currentIndex', payload: 1 }
]
})
})
it('currentIndex', () => {
testAction({
action: store.actions.currentIndex,
payload: 1,
params: { state: { tracks: [1, 2], currentIndex: 0 }, rootState: { radios: { running: false } } },
expectedMutations: [
{ type: 'ended', payload: false },
{ type: 'player/currentTime', payload: 0, options: { root: true } },
{ type: 'currentIndex', payload: 1 }
]
})
})
it('currentIndex with radio and many tracks remaining', () => {
testAction({
action: store.actions.currentIndex,
payload: 1,
params: { state: { tracks: [1, 2, 3, 4], currentIndex: 0 }, rootState: { radios: { running: true } } },
expectedMutations: [
{ type: 'ended', payload: false },
{ type: 'player/currentTime', payload: 0, options: { root: true } },
{ type: 'currentIndex', payload: 1 }
]
})
})
it('currentIndex with radio and less than two tracks remaining', () => {
testAction({
action: store.actions.currentIndex,
payload: 1,
params: { state: { tracks: [1, 2, 3], currentIndex: 0 }, rootState: { radios: { running: true } } },
expectedMutations: [
{ type: 'ended', payload: false },
{ type: 'player/currentTime', payload: 0, options: { root: true } },
{ type: 'currentIndex', payload: 1 }
],
expectedActions: [
{ type: 'radios/populateQueue', payload: null, options: { root: true } }
]
})
})
it('clean', () => {
testAction({
action: store.actions.clean,
expectedMutations: [
{ type: 'tracks', payload: [] },
{ type: 'ended', payload: true }
],
expectedActions: [
{ type: 'radios/stop', payload: null, options: { root: true } },
{ type: 'player/stop', payload: null, options: { root: true } },
{ type: 'currentIndex', payload: -1 }
]
})
})
// it('shuffle', () => {
// let _shuffle = sandbox.stub(_, 'shuffle')
// let tracks = ['a', 'b', 'c', 'd', 'e']
// let shuffledTracks = ['a', 'b', 'e', 'd', 'c']
// _shuffle.returns(shuffledTracks)
// testAction({
// action: store.actions.shuffle,
// params: {state: {currentIndex: 1, tracks: tracks}},
// expectedMutations: [
// { type: 'tracks', payload: [] }
// ],
// expectedActions: [
// { type: 'appendMany', payload: {tracks: shuffledTracks} },
// { type: 'currentIndex', payload: {tracks: shuffledTracks} }
// ]
// })
// })
})
})
import { describe, it, expect } from 'vitest'
import store from '@/store/radios'
import { testAction } from '../../utils'
describe('store/radios', () => {
describe('mutations', () => {
it('current', () => {
const state = {}
store.mutations.current(state, 1)
expect(state.current).to.equal(1)
})
it('running', () => {
const state = {}
store.mutations.running(state, false)
expect(state.running).to.equal(false)
})
})
describe('actions', () => {
it('start', () => {
testAction({
action: store.actions.start,
payload: { type: 'favorites', objectId: 0, customRadioId: null },
expectedMutations: [
{
type: 'current',
payload: {
type: 'favorites',
objectId: 0,
customRadioId: null,
session: 2
}
},
{ type: 'running', payload: true }
],
expectedActions: [
{ type: 'populateQueue', payload: true }
]
})
})
it('stop', () => {
return testAction({
action: store.actions.stop,
params: { state: {} },
expectedMutations: [
{ type: 'current', payload: null },
{ type: 'running', payload: false }
]
})
})
it('populateQueue', () => {
return testAction({
action: store.actions.populateQueue,
params: {
state: { running: true, current: { session: 1 } },
rootState: { player: { errorCount: 0, maxConsecutiveErrors: 5 } }
},
expectedActions: [
{ type: 'queue/append', payload: { track: { id: 1 } }, options: { root: true } }
]
})
})
it('populateQueue does nothing when not running', () => {
testAction({
action: store.actions.populateQueue,
params: { state: { running: false } },
expectedActions: []
})
})
it('populateQueue does nothing when too much errors', () => {
return testAction({
action: store.actions.populateQueue,
payload: { test: 'track' },
params: {
rootState: { player: { errorCount: 5, maxConsecutiveErrors: 5 } },
state: { running: true }
},
expectedActions: []
})
})
})
})
import { describe, it, expect } from 'vitest'
import { parseAPIErrors } from '@/utils'
describe('utils', () => {
describe('parseAPIErrors', () => {
it('handles flat structure', () => {
const input = { old_password: ['Invalid password'] }
const expected = ['Invalid password']
const output = parseAPIErrors(input)
expect(output).to.deep.equal(expected)
})
it('handles flat structure with multiple errors per field', () => {
const input = { old_password: ['Invalid password', 'Too short'] }
const expected = ['Invalid password', 'Too short']
const output = parseAPIErrors(input)
expect(output).to.deep.equal(expected)
})
it('translate field name', () => {
const input = { old_password: ['This field is required'] }
const expected = ['Old Password: This field is required']
const output = parseAPIErrors(input)
expect(output).to.deep.equal(expected)
})
it('handle nested fields', () => {
const input = { summary: { text: ['Ensure this field has no more than 5000 characters.'] } }
const expected = ['Summary - Text: Ensure this field has no more than 5000 characters.']
const output = parseAPIErrors(input)
expect(output).to.deep.equal(expected)
})
})
})
// helper for testing action with expected mutations
import Vue from 'vue'
import { expect } from 'chai'
export const render = (Component, propsData) => {
const Constructor = Vue.extend(Component)
return new Constructor({ propsData: propsData }).$mount()
}
export const testAction = ({ action, payload, params, expectedMutations, expectedActions }, done) => {
let mutationsCount = 0
let actionsCount = 0
if (!expectedMutations) {
expectedMutations = []
}
if (!expectedActions) {
expectedActions = []
}
// mock commit
const commit = (type, payload) => {
const mutation = expectedMutations[mutationsCount]
expect(mutation.type).to.equal(type)
if (payload) {
expect(mutation.payload).to.deep.equal(payload)
}
mutationsCount++
}
// mock dispatch
const dispatch = (type, payload, options) => {
const a = expectedActions[actionsCount]
if (!a) {
throw Error(`Unexecpted action ${type}`)
}
expect(a.type).to.equal(type)
if (payload) {
expect(a.payload).to.deep.equal(payload)
}
if (a.options) {
expect(options).to.deep.equal(a.options)
}
actionsCount++
}
const end = function () {
// check if no mutations should have been dispatched
if (expectedMutations.length === 0) {
expect(mutationsCount).to.equal(0)
}
if (expectedActions.length === 0) {
expect(actionsCount).to.equal(0)
}
}
// call the action with mocked store and arguments
const promise = action({ commit, dispatch, ...params }, payload)
if (promise) {
promise.then(end)
return promise
} else {
return end()
}
}
...@@ -31,4 +31,11 @@ export default defineConfig({ ...@@ -31,4 +31,11 @@ export default defineConfig({
"@": path.resolve(__dirname, "./src"), "@": path.resolve(__dirname, "./src"),
}, },
}, },
test: {
coverage: {
all: true,
src: ["src"],
reporter: [ "text-summary", "text", "cobertura" ]
}
}
}) })
This diff is collapsed.