vue-mobx-finegrained
Version:
Vue 3 composable for a complete MobX state management integration.
615 lines (482 loc) • 19.5 kB
text/typescript
import { describe, it, beforeEach, expect, vi } from 'vitest'
import { AppTestHarness } from './Helpers/AppTestHarness'
import { LoginRegisterPresenter } from "./Helpers/Authentication/LoginRegisterPresenter";
import { LoginRegisterPresenterAuto } from "./Helpers/Authentication/LoginRegisterPresenterAuto";
import { notify } from "../useMobX.js";
import { isReactive } from "vue";
import { ObservableMap, ObservableSet } from "mobx";
let appTestHarness = null
let presenter = null
let vm = null
function wait () {
return setTimeout(() => {})
}
function test (presenterClass, mode = 'manual') {
describe('----- mode: '+mode+ '----', () => {
beforeEach(() => {
appTestHarness = new AppTestHarness()
appTestHarness.init()
presenter = appTestHarness.container.get(presenterClass)
vm = presenter.vm
})
describe('------------------- Object Shadowing ------------------', () => {
if (mode === 'manual') {
it('parent non-observed properties are accessible', () => {
expect(vm.nonObservedProp).toBe('test')
})
it('parent observed properties are accessible', () => {
expect(vm.showValidationWarning).toBe(false)
})
it('presenter observed properties accessible', () => {
expect(vm.email).toBe('test@test.ca')
})
it('grandparent properties are accessible', () => {
expect(vm.grandParentProp).toBe(1)
})
}
it('presenter non-observed properties accessible', () => {
expect(vm.testNonObservable).toBe(1)
})
it('presenter non-observed simple properties reassignable', () => {
vm.testNonObservable = 2
expect(presenter.testNonObservable).toBe(2)
})
it('presenter non-observed object properties reassignable', () => {
vm.testNonObservableObject = { test: 1 }
expect(presenter.testNonObservableObject.test).toBe(1)
})
it('vm non-observed object properties are reassignable', () => {
presenter.testNonObservableObject = { test: 1 }
expect(vm.testNonObservableObject.test).toBe(1)
})
it('presenter non-observed object sub-properties are reassignable', () => {
vm.testNonObservableObject.prop.prop.prop = 3
expect(presenter.testNonObservableObject.prop.prop.prop).toBe(3)
})
it('presenter non-observed object sub-properties are reassignable', () => {
vm.testNonObservableObject.prop.prop.prop = 3
expect(presenter.testNonObservableObject.prop.prop.prop).toBe(3)
})
})
describe('------------------- MobX Reactivity ------------------', () => {
describe('mobX observable state propagation to vue', () => {
describe('mobX change primitive', () => {
it('mobX change string', () => {
presenter._string = 'hello'
expect(vm._string).toBe('hello')
})
it('mobX change boolean', () => {
presenter._boolean = false
expect(vm._boolean).toBe(false)
})
it('mobX change number', () => {
presenter._number = 2
expect(vm._number).toBe(2)
})
})
describe('mobX change array', () => {
describe('mobX change array at root', () => {
it('mobX push to array root', () => {
presenter._array.push(1)
presenter._array.push(2)
expect(vm._array[0]).toBe(1)
expect(vm._array[1]).toBe(2)
})
it('mobX splice array at root', () => {
presenter._arraySplice.splice(1, 0, 2)
expect(vm._arraySplice[1]).toBe(2)
})
it('mobX unshift array at root', () => {
presenter._arrayUnshift.unshift(0)
expect(vm._arrayUnshift[0]).toBe(0)
})
it('mobX pop array at root', () => {
presenter._arrayPop.pop()
expect(vm._arrayPop[vm._arrayPop.length - 1]).toBe(1)
})
it('mobX shift array at root', () => {
presenter._arrayShift.shift()
expect(vm._arrayShift[0]).toBe(2)
})
})
describe('mobX change deep nested array', () => {
it('mobX push to nested array', () => {
presenter._narray[0][0].push(1)
presenter._narray[0][0].push(2)
expect(vm._narray[0][0][0]).toBe(1)
expect(vm._narray[0][0][1]).toBe(2)
})
it('mobX splice nested array', () => {
presenter._narraySplice[0][0].splice(1, 0, 2)
expect(vm._narraySplice[0][0][1]).toBe(2)
})
it('mobX unshift nested array', () => {
presenter._narrayUnshift[0][0].unshift(0)
expect(vm._narrayUnshift[0][0][0]).toBe(0)
})
it('mobX pop nested array', () => {
presenter._narrayPop[0][0].pop()
expect(vm._narrayPop[0][0][vm._narrayPop.length - 1]).toBe(1)
})
it('mobX shift nested array', () => {
presenter._narrayShift[0][0].shift()
expect(vm._narrayShift[0][0][0]).toBe(2)
})
})
})
describe('mobX change object', () => {
describe('mobX change root props', () => {
it('mobX add object prop to root', () => {
presenter._object.prop = 1
expect(vm._object.prop).toBe(1)
})
it('mobX update object prop at root', () => {
presenter._objectUpdate.prop = 2
expect(vm._objectUpdate.prop).toBe(2)
})
it('mobX delete object prop at root', () => {
delete presenter._objectDelete.prop
expect(vm._objectDelete.prop).toBeUndefined()
})
})
describe('mobX change deep nested props', () => {
it('mobX add nested object prop', () => {
presenter._nobject.nest.nest.prop = 1
expect(vm._nobject.nest.nest.prop).toBe(1)
})
it('mobX update nested object prop', () => {
presenter._nobjectUpdate.nest.nest.prop = 2
expect(vm._nobjectUpdate.nest.nest.prop).toBe(2)
})
it('mobX delete nested object prop', () => {
delete presenter._nobjectDelete.nest.nest.prop
expect(vm._nobjectDelete.nest.nest.prop).toBeUndefined()
})
})
})
describe('mobX object/array reassignment & maintain reactivity', () => {
it('mobX reassign array at root', async () => {
presenter._arrayReassign = [{ newProp: 1 }]
expect(vm._arrayReassign[0].newProp).toBe(1)
vm._arrayReassign[0].newProp = 2
await wait()
expect(presenter._arrayReassign[0].newProp).toBe(2)
})
it('mobX reassign whole observable object at root', async () => {
presenter._objectReassign = { prop: { prop: true } }
expect(vm._objectReassign.prop.prop).toBe(true)
vm._objectReassign.prop.prop = false
await wait()
expect(presenter._objectReassign.prop.prop).toBe(false)
})
})
})
describe('mobX getter propagation', () => {
describe('mobX getter propagation for array', () => {
it('mobX push to array root', () => {
presenter._getterArray.push({ prop: 1, prop2: 2 })
expect(vm.getterArray[0].prop).toBe(1)
})
it('mobX change object in array', () => {
presenter._getterNestedArray[0].prop = 3
presenter._getterNestedArray[0].prop2 = 3
expect(vm.getterNestedArray[0].prop).toBe(3)
expect(vm.getterNestedArray[0].prop2).toBe(3)
})
it('mobX change nested object in array & notify manually', () => {
presenter._getterNestedArray[0].prop3.prop = 4
presenter._getterNestedArray[0].prop3 = notify(presenter._getterNestedArray[0].prop3)
expect(vm.getterNestedArray[0].prop3.prop).toBe(4)
})
})
describe('mobX getter propagation for object', () => {
it('mobX add property to object', () => {
presenter._getterObject.prop2 = 3
presenter._getterObject = notify(presenter._getterObject)
expect(vm.getterObject.prop2).toBe(3)
})
it('mobX update property on nested object & notify manually', () => {
presenter._getterNestedObject.prop3.prop = 3
presenter._getterNestedObject = notify(presenter._getterNestedObject)
expect(vm.getterNestedObject.prop3.prop).toBe(3)
})
})
})
describe('mobX Map/Set propagation', () => {
describe('mobX Map propagation', () => {
it('mobX set key & value into a Map', async () => {
presenter.mapObject.set(1, 'foo')
expect(vm.mapObject.get(1)).toBe('foo')
})
it('mobX delete from Map', () => {
presenter.mapObject.set(1, 'foo')
presenter.mapObject.delete(1)
expect(vm.mapObject.get(1)).toBeUndefined()
})
})
describe('mobX Set propagation', () => {
it('mobX add to Set', async () => {
const obj = { prop: 1, prop2: 2 }
presenter.setObject.add(obj)
vm.setObject.forEach(o => {
expect(o).toEqual(obj)
})
})
it('mobX delete from Set', () => {
const obj = { prop: 1, prop2: 2 }
presenter.setObject.add(obj)
presenter.setObject.forEach(o => {
presenter.setObject.delete(o)
})
let i = 0;
vm.setObject.forEach(o => {
i++
})
expect(i).toBe(0)
})
})
})
describe('mobx object/array reassignment & maintain reactivity', async () => {
it('mobx reassign array at root', async () => {
presenter._arrayReassign = [{ newProp: 1 }]
expect(vm._arrayReassign[0].newProp).toBe(1)
vm._arrayReassign[0].newProp = 2
expect(vm._arrayReassign[0].newProp).toBe(2)
})
it('mobx reassign whole observable object at root', async () => {
presenter._objectReassign = { prop: { prop: true } }
expect(vm._objectReassign.prop.prop).toBe(true)
vm._objectReassign.prop.prop = false
expect(vm._objectReassign.prop.prop).toBe(false)
})
it('mobx reassign Map', async () => {
presenter.mapObject = new Map()
presenter.mapObject.set('prop', 1)
expect(vm.mapObject.get('prop')).toBe(1)
vm.mapObject.set('prop', 2)
await wait()
expect(presenter.mapObject.get('prop')).toBe(2)
})
it('mobx reassign Set', async () => {
presenter.setObject = new Set()
presenter.setObject.add(1)
presenter.setObject.add(2)
presenter.setObject.add(3)
let i = 0, value
const iterator = vm.setObject.entries();
for (const entry of iterator) {
i++
value = entry
}
expect(i).toBe(3)
expect(value[0]).toBe(3)
})
})
it('--------------------- Vue Reactivity ---------------------', () => {})
describe('vue state propagation to mobX', () => {
describe('vue change primitive', () => {
it('vue change string', async () => {
vm._string = 'hello'
await wait()
expect(presenter._string).toBe('hello')
})
it('vue change boolean', async () => {
vm._boolean = false
await wait()
expect(presenter._boolean).toBe(false)
})
it('vue change number', async () => {
vm._number = 2
await wait()
expect(presenter._number).toBe(2)
})
})
describe('vue change array', async () => {
describe('vue change array at root', async () => {
it('vue push to array root', async () => {
vm._array.push(1)
vm._array.push(2)
await wait()
expect(presenter._array[0]).toBe(1)
expect(presenter._array[1]).toBe(2)
})
it('vue splice array at root', async () => {
vm._arraySplice.splice(1, 0, 2)
await wait()
expect(presenter._arraySplice[1]).toBe(2)
})
it('vue unshift array at root', async () => {
vm._arrayUnshift.unshift(0)
await wait()
expect(presenter._arrayUnshift[0]).toBe(0)
})
it('vue pop array at root', async () => {
vm._arrayPop.pop()
await wait()
expect(presenter._arrayPop[presenter._arrayPop.length - 1]).toBe(1)
})
it('vue shift array at root', async () => {
vm._arrayShift.shift()
await wait()
expect(presenter._arrayShift[0]).toBe(2)
})
})
describe('vue change deep nested array', async () => {
it('vue push to nested array', async () => {
vm._narray[0][0].push(1)
vm._narray[0][0].push(2)
await wait()
expect(presenter._narray[0][0][0]).toBe(1)
expect(presenter._narray[0][0][1]).toBe(2)
})
it('vue splice nested array', async () => {
vm._narraySplice[0][0].splice(1, 0, 2)
await wait()
expect(presenter._narraySplice[0][0][1]).toBe(2)
})
it('vue unshift nested array', async () => {
vm._narrayUnshift[0][0].unshift(0)
await wait()
expect(presenter._narrayUnshift[0][0][0]).toBe(0)
})
it('vue pop nested array', async () => {
vm._narrayPop[0][0].pop()
await wait()
expect(presenter._narrayPop[0][0][presenter._narrayPop.length - 1]).toBe(1)
})
it('vue shift nested array', async () => {
vm._narrayShift[0][0].shift()
await wait()
expect(presenter._narrayShift[0][0][0]).toBe(2)
})
})
})
describe('vue change object', async () => {
describe('vue change root props', async () => {
it('vue add object prop to root', async () => {
vm._object.prop = 1
await wait()
expect(presenter._object.prop).toBe(1)
})
it('vue update object prop at root', async () => {
vm._objectUpdate.prop = 2
await wait()
expect(presenter._objectUpdate.prop).toBe(2)
})
it('vue delete object prop at root', async () => {
delete vm._objectDelete.prop
await wait()
expect(presenter._objectDelete.prop).toBeUndefined()
})
})
describe('vue change deep nested props', async () => {
it('vue add nested object prop', async () => {
vm._nobject.nest.nest.prop = 1
await wait()
expect(presenter._nobject.nest.nest.prop).toBe(1)
})
it('vue update nested object prop', async () => {
vm._nobjectUpdate.nest.nest.prop = 2
await wait()
expect(presenter._nobjectUpdate.nest.nest.prop).toBe(2)
})
it('vue delete nested object prop', async () => {
delete vm._nobjectDelete.nest.nest.prop
await wait()
expect(presenter._nobjectDelete.nest.nest.prop).toBeUndefined()
})
})
})
describe('vue object/array reassignment & maintain reactivity', async () => {
it('vue reassign array at root', async () => {
vm._arrayReassign = [{ newProp: 1 }]
await wait()
expect(presenter._arrayReassign[0].newProp).toBe(1)
presenter._arrayReassign[0].newProp = 2
expect(vm._arrayReassign[0].newProp).toBe(2)
})
it('vue reassign whole observable object at root', async () => {
vm._objectReassign = { prop: { prop: true } }
await wait()
expect(presenter._objectReassign.prop.prop).toBe(true)
presenter._objectReassign.prop.prop = false
expect(vm._objectReassign.prop.prop).toBe(false)
})
it('vue reassign Map', async () => {
vm.mapObject = new ObservableMap([[
'prop', 1
]])
await wait()
expect(presenter.mapObject.get('prop')).toBe(1)
presenter.mapObject.set('prop', 2)
expect(vm.mapObject.get('prop')).toBe(2)
})
it('vue reassign Set', async () => {
vm.setObject = new ObservableSet([1, 2])
await wait()
presenter.setObject.add(3)
let i = 0
presenter.setObject.forEach(el => {
i++
if (i === 3) {
expect(el).toBe(3)
}
})
let j = 0;
vm.setObject.forEach(el => {
j++
if (j === 3) {
expect(el).toBe(3)
}
})
})
})
describe('vue Map/Set propagation', () => {
describe('vue Map propagation', () => {
it('vue add to Map', async () => {
const obj = { prop: 1, prop2: 2 }
vm.mapObject.set(1, 'bar')
await wait()
expect(presenter.mapObject.get(1)).toBe('bar')
presenter.mapObject.set(1, 'foo')
expect(vm.mapObject.get(1)).toBe('foo')
expect(isReactive(vm.mapObject)).toBe(true)
})
it('vue delete from Map', async () => {
vm.mapObject.set(1, 'foo')
expect(isReactive(vm.mapObject)).toBe(true)
await wait()
expect(presenter.mapObject.get(1)).toBe('foo')
vm.mapObject.delete(1)
await wait()
expect(presenter.mapObject.get(1)).toBeUndefined()
})
})
describe('vue Set propagation', () => {
it('vue add to Set', async () => {
const obj = { prop: 1, prop2: 2 }
vm.setObject.add(obj)
presenter.setObject.forEach(o => {
expect(o).toEqual(obj)
})
})
it('vue delete from Set', () => {
const obj = { prop: 1, prop2: 2 }
vm.setObject.add(obj)
vm.setObject.forEach(o => {
vm.setObject.delete(o)
})
let i = 0;
presenter.setObject.forEach(o => {
i++
})
expect(i).toBe(0)
})
})
})
})
})
})
}
test(LoginRegisterPresenter, 'manual')
test(LoginRegisterPresenterAuto, 'auto')