vue3-yuanma/packages/reactivity/__tests__/computed.spec.ts

202 lines
4.8 KiB
TypeScript
Raw Normal View History

import {
computed,
reactive,
effect,
ref,
WritableComputedRef,
isReadonly
} from '../src'
2018-09-21 06:03:59 +08:00
2019-08-20 21:58:10 +08:00
describe('reactivity/computed', () => {
2018-09-21 06:03:59 +08:00
it('should return updated value', () => {
const value = reactive<{ foo?: number }>({})
2018-09-21 06:03:59 +08:00
const cValue = computed(() => value.foo)
2019-05-29 18:47:29 +08:00
expect(cValue.value).toBe(undefined)
2018-09-21 06:03:59 +08:00
value.foo = 1
2019-05-29 18:47:29 +08:00
expect(cValue.value).toBe(1)
2018-09-21 06:03:59 +08:00
})
it('should compute lazily', () => {
const value = reactive<{ foo?: number }>({})
2018-09-21 06:03:59 +08:00
const getter = jest.fn(() => value.foo)
const cValue = computed(getter)
// lazy
expect(getter).not.toHaveBeenCalled()
2019-05-29 18:47:29 +08:00
expect(cValue.value).toBe(undefined)
2018-09-21 06:03:59 +08:00
expect(getter).toHaveBeenCalledTimes(1)
// should not compute again
2019-05-29 18:47:29 +08:00
cValue.value
2018-09-21 06:03:59 +08:00
expect(getter).toHaveBeenCalledTimes(1)
// should not compute until needed
value.foo = 1
expect(getter).toHaveBeenCalledTimes(1)
// now it should compute
2019-05-29 18:47:29 +08:00
expect(cValue.value).toBe(1)
2018-09-21 06:03:59 +08:00
expect(getter).toHaveBeenCalledTimes(2)
// should not compute again
2019-05-29 18:47:29 +08:00
cValue.value
2018-09-21 06:03:59 +08:00
expect(getter).toHaveBeenCalledTimes(2)
})
2018-11-14 00:03:35 +08:00
it('should trigger effect', () => {
const value = reactive<{ foo?: number }>({})
2018-09-21 06:03:59 +08:00
const cValue = computed(() => value.foo)
let dummy
2018-11-14 00:03:35 +08:00
effect(() => {
2019-05-29 18:47:29 +08:00
dummy = cValue.value
2018-09-21 06:03:59 +08:00
})
expect(dummy).toBe(undefined)
value.foo = 1
expect(dummy).toBe(1)
})
it('should work when chained', () => {
const value = reactive({ foo: 0 })
2018-09-21 06:03:59 +08:00
const c1 = computed(() => value.foo)
2019-05-29 18:47:29 +08:00
const c2 = computed(() => c1.value + 1)
expect(c2.value).toBe(1)
expect(c1.value).toBe(0)
2018-09-21 06:03:59 +08:00
value.foo++
2019-05-29 18:47:29 +08:00
expect(c2.value).toBe(2)
expect(c1.value).toBe(1)
2018-09-21 06:03:59 +08:00
})
2018-11-14 00:03:35 +08:00
it('should trigger effect when chained', () => {
const value = reactive({ foo: 0 })
2018-09-21 06:03:59 +08:00
const getter1 = jest.fn(() => value.foo)
const getter2 = jest.fn(() => {
2019-05-29 18:47:29 +08:00
return c1.value + 1
2018-09-21 06:03:59 +08:00
})
const c1 = computed(getter1)
const c2 = computed(getter2)
let dummy
2018-11-14 00:03:35 +08:00
effect(() => {
2019-05-29 18:47:29 +08:00
dummy = c2.value
2018-09-21 06:03:59 +08:00
})
expect(dummy).toBe(1)
expect(getter1).toHaveBeenCalledTimes(1)
expect(getter2).toHaveBeenCalledTimes(1)
value.foo++
expect(dummy).toBe(2)
// should not result in duplicate calls
expect(getter1).toHaveBeenCalledTimes(2)
expect(getter2).toHaveBeenCalledTimes(2)
})
2018-11-14 00:03:35 +08:00
it('should trigger effect when chained (mixed invocations)', () => {
const value = reactive({ foo: 0 })
2018-09-21 06:03:59 +08:00
const getter1 = jest.fn(() => value.foo)
const getter2 = jest.fn(() => {
2019-05-29 18:47:29 +08:00
return c1.value + 1
2018-09-21 06:03:59 +08:00
})
const c1 = computed(getter1)
const c2 = computed(getter2)
let dummy
2018-11-14 00:03:35 +08:00
effect(() => {
2019-05-29 18:47:29 +08:00
dummy = c1.value + c2.value
2018-09-21 06:03:59 +08:00
})
expect(dummy).toBe(1)
expect(getter1).toHaveBeenCalledTimes(1)
expect(getter2).toHaveBeenCalledTimes(1)
value.foo++
expect(dummy).toBe(3)
// should not result in duplicate calls
expect(getter1).toHaveBeenCalledTimes(2)
expect(getter2).toHaveBeenCalledTimes(2)
})
it('should no longer update when stopped', () => {
const value = reactive<{ foo?: number }>({})
2018-09-21 06:03:59 +08:00
const cValue = computed(() => value.foo)
let dummy
2018-11-14 00:03:35 +08:00
effect(() => {
2019-05-29 18:47:29 +08:00
dummy = cValue.value
2018-09-21 06:03:59 +08:00
})
expect(dummy).toBe(undefined)
value.foo = 1
expect(dummy).toBe(1)
cValue.effect.stop()
2018-09-21 06:03:59 +08:00
value.foo = 2
expect(dummy).toBe(1)
})
2019-06-06 15:19:04 +08:00
it('should support setter', () => {
const n = ref(1)
const plusOne = computed({
get: () => n.value + 1,
set: val => {
n.value = val - 1
}
})
expect(plusOne.value).toBe(2)
n.value++
expect(plusOne.value).toBe(3)
plusOne.value = 0
expect(n.value).toBe(-1)
})
it('should trigger effect w/ setter', () => {
const n = ref(1)
const plusOne = computed({
get: () => n.value + 1,
set: val => {
n.value = val - 1
}
})
let dummy
effect(() => {
dummy = n.value
})
expect(dummy).toBe(1)
plusOne.value = 0
expect(dummy).toBe(-1)
2019-06-06 15:19:04 +08:00
})
it('should warn if trying to set a readonly computed', () => {
const n = ref(1)
const plusOne = computed(() => n.value + 1)
;(plusOne as WritableComputedRef<number>).value++ // Type cast to prevent TS from preventing the error
expect(
'Write operation failed: computed value is readonly'
).toHaveBeenWarnedLast()
})
it('should be readonly', () => {
let a = { a: 1 }
const x = computed(() => a)
expect(isReadonly(x)).toBe(true)
expect(isReadonly(x.value)).toBe(false)
expect(isReadonly(x.value.a)).toBe(false)
const z = computed<typeof a>({
get() {
return a
},
set(v) {
a = v
}
})
expect(isReadonly(z)).toBe(false)
expect(isReadonly(z.value.a)).toBe(false)
})
it('should expose value when stopped', () => {
const x = computed(() => 1)
x.effect.stop()
expect(x.value).toBe(1)
})
2018-09-21 06:03:59 +08:00
})