pleroma-fe/test/unit/specs/lib/persisted_state.spec.js
2025-03-12 21:40:45 -04:00

307 lines
8.9 KiB
JavaScript

import { setActivePinia, createPinia, defineStore } from 'pinia'
import { createApp } from 'vue'
import { flushPromises } from '@vue/test-utils'
import { piniaPersistPlugin } from 'src/lib/persisted_state.js'
const app = createApp({})
const getMockStorage = () => {
let state = {}
return {
getItem: vi.fn(async key => {
console.log('get:', key, state[key])
return state[key]
}),
setItem: vi.fn(async (key, value) => {
console.log('set:', key, value)
state[key] = value
}),
_clear: () => {
state = {}
}
}
}
let mockStorage
beforeEach(() => {
mockStorage = getMockStorage()
const pinia = createPinia().use(piniaPersistPlugin({ storage: mockStorage }))
app.use(pinia)
setActivePinia(pinia)
})
describe('piniaPersistPlugin', () => {
describe('initial state', () => {
test('it does not load anything if it is not enabled', async () => {
await mockStorage.setItem('pinia-local-test', { a: 3 })
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2 })
})
const test = useTestStore()
await test.$persistLoaded
expect(test.a).to.eql(1)
expect(test.b).to.eql(2)
})
test('$persistLoaded rejects if getItem() throws', async () => {
const error = new Error('unable to get storage')
mockStorage.getItem = vi.fn(async () => {
throw error
})
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2, c: { d: 4, e: 5 } }),
persist: {}
})
const test = useTestStore()
await expect(test.$persistLoaded).rejects.toThrowError(error)
})
test('it loads from pinia storage', async () => {
await mockStorage.setItem('pinia-local-test', { a: 3, c: { d: 0 } })
await mockStorage.setItem('vuex-lz', { test: { a: 4 } })
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2, c: { d: 4, e: 5 } }),
persist: {}
})
const test = useTestStore()
await test.$persistLoaded
expect(test.a).to.eql(3)
expect(test.b).to.eql(2)
expect(test.c.d).to.eql(0)
expect(test.c.e).to.eql(5)
})
test('it loads from vuex storage as fallback', async () => {
await mockStorage.setItem('vuex-lz', { test: { a: 4, c: { d: 0 } } })
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2, c: { d: 4, e: 5 } }),
persist: {}
})
const test = useTestStore()
await test.$persistLoaded
expect(test.a).to.eql(4)
expect(test.b).to.eql(2)
expect(test.c.d).to.eql(0)
expect(test.c.e).to.eql(5)
})
test('it loads from vuex storage and writes it into pinia storage', async () => {
await mockStorage.setItem('vuex-lz', { test: { a: 4, c: { d: 0 } } })
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2, c: { d: 4, e: 5 } }),
persist: {
afterLoad (state) {
return {
...state,
a: 5
}
}
}
})
const test = useTestStore()
await test.$persistLoaded
expect(await mockStorage.getItem('pinia-local-test')).to.eql({ a: 4, c: { d: 0 } })
expect(test.a).to.eql(5)
expect(test.b).to.eql(2)
expect(test.c.d).to.eql(0)
expect(test.c.e).to.eql(5)
})
test('it does not modify state if there is nothing to load', async () => {
await mockStorage.setItem('vuex-lz', { test2: { a: 4 } })
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2 }),
persist: {}
})
const test = useTestStore()
await test.$persistLoaded
expect(test.a).to.eql(1)
expect(test.b).to.eql(2)
})
})
describe('paths', () => {
test('it saves everything if paths is unspecified', async () => {
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2 }),
persist: {}
})
const test = useTestStore()
await test.$persistLoaded
test.$patch({ a: 3 })
await flushPromises()
expect(await mockStorage.getItem('pinia-local-test')).to.eql({ a: 3, b: 2 })
})
test('it saves only specified paths', async () => {
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2, c: { d: 4, e: 5 } }),
persist: {
paths: ['a', 'c.d']
}
})
const test = useTestStore()
await test.$persistLoaded
test.$patch({ a: 3 })
await flushPromises()
expect(await mockStorage.getItem('pinia-local-test')).to.eql({ a: 3, c: { d: 4 } })
})
})
test('it only saves after load', async () => {
const onSaveError = vi.fn()
const onSaveSuccess = vi.fn()
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2 }),
persist: {
onSaveSuccess,
onSaveError
}
})
const test = useTestStore()
test.$patch({ a: 3 })
expect(await mockStorage.getItem('pinia-local-test')).to.eql(undefined)
// NOTE: it should not even have tried to save, because the subscribe function
// is called only after loading the initial state.
expect(mockStorage.setItem).not.toHaveBeenCalled()
// this asserts that it has not called setState() in persistCurrentState()
expect(onSaveError).not.toHaveBeenCalled()
expect(onSaveSuccess).not.toHaveBeenCalled()
await test.$persistLoaded
test.$patch({ a: 4 })
expect(await mockStorage.getItem('pinia-local-test')).to.eql({ a: 4, b: 2 })
})
describe('saveImmediatelyActions', () => {
test('it should only persist state after specified actions', async () => {
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2 }),
actions: {
increaseA () {
++this.a
},
increaseB () {
++this.b
}
},
persist: {
saveImmediatelyActions: ['increaseA']
}
})
const test = useTestStore()
await test.$persistLoaded
await test.increaseA()
expect(await mockStorage.getItem('pinia-local-test')).to.eql({ a: 2, b: 2 })
await test.increaseB()
expect(await mockStorage.getItem('pinia-local-test')).to.eql({ a: 2, b: 2 })
await test.increaseA()
expect(await mockStorage.getItem('pinia-local-test')).to.eql({ a: 3, b: 3 })
})
})
describe('onSaveSuccess / onSaveError', () => {
test('onSaveSuccess is called after setState', async () => {
const onSaveSuccess = vi.fn()
const onSaveError = vi.fn()
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2 }),
persist: {
onSaveSuccess,
onSaveError
}
})
const test = useTestStore()
await test.$persistLoaded
test.$patch({ a: 3 })
await flushPromises()
expect(onSaveSuccess).toHaveBeenCalledTimes(1)
expect(onSaveError).toHaveBeenCalledTimes(0)
test.$patch({ a: 4 })
await flushPromises()
expect(onSaveSuccess).toHaveBeenCalledTimes(2)
expect(onSaveError).toHaveBeenCalledTimes(0)
})
test('onSaveError is called after setState fails', async () => {
mockStorage.setItem = vi.fn(async () => {
throw new Error('cannot save')
})
const onSaveSuccess = vi.fn()
const onSaveError = vi.fn()
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2 }),
persist: {
onSaveSuccess,
onSaveError
}
})
const test = useTestStore()
await test.$persistLoaded
await test.$patch({ a: 3 })
expect(onSaveSuccess).toHaveBeenCalledTimes(0)
expect(onSaveError).toHaveBeenCalledTimes(1)
await test.$patch({ a: 4 })
expect(onSaveSuccess).toHaveBeenCalledTimes(0)
expect(onSaveError).toHaveBeenCalledTimes(2)
})
})
describe('afterLoad', () => {
test('it is called with the saved state object', async () => {
await mockStorage.setItem('pinia-local-test', { a: 2 })
const afterLoad = vi.fn(async orig => {
return { a: orig.a + 1 }
})
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2 }),
persist: {
afterLoad
}
})
const test = useTestStore()
await test.$persistLoaded
expect(afterLoad).toHaveBeenCalledTimes(1)
expect(afterLoad).toHaveBeenCalledWith({ a: 2 })
expect(test.a).to.eql(3)
})
test('it is called with empty object if there is no saved state', async () => {
const afterLoad = vi.fn(async () => {
return { a: 3 }
})
const useTestStore = defineStore('test', {
state: () => ({ a: 1, b: 2 }),
persist: {
afterLoad
}
})
const test = useTestStore()
await test.$persistLoaded
expect(afterLoad).toHaveBeenCalledTimes(1)
expect(afterLoad).toHaveBeenCalledWith({})
expect(test.a).to.eql(3)
})
})
})