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

178 lines
3.7 KiB
TypeScript

import { ref, effect, reactive, isRef, toRefs, Ref } from '../src/index'
import { computed } from '@vue/runtime-dom'
describe('reactivity/ref', () => {
it('should hold a value', () => {
const a = ref(1)
expect(a.value).toBe(1)
a.value = 2
expect(a.value).toBe(2)
})
it('should be reactive', () => {
const a = ref(1)
let dummy
effect(() => {
dummy = a.value
})
expect(dummy).toBe(1)
a.value = 2
expect(dummy).toBe(2)
})
it('should make nested properties reactive', () => {
const a = ref({
count: 1
})
let dummy
effect(() => {
dummy = a.value.count
})
expect(dummy).toBe(1)
a.value.count = 2
expect(dummy).toBe(2)
})
it('should work like a normal property when nested in a reactive object', () => {
const a = ref(1)
const obj = reactive({
a,
b: {
c: a,
d: [a]
}
})
let dummy1: number
let dummy2: number
let dummy3: number
effect(() => {
dummy1 = obj.a
dummy2 = obj.b.c
dummy3 = obj.b.d[0]
})
const assertDummiesEqualTo = (val: number) =>
[dummy1, dummy2, dummy3].forEach(dummy => expect(dummy).toBe(val))
assertDummiesEqualTo(1)
a.value++
assertDummiesEqualTo(2)
obj.a++
assertDummiesEqualTo(3)
obj.b.c++
assertDummiesEqualTo(4)
obj.b.d[0]++
assertDummiesEqualTo(5)
})
it('should unwrap nested ref in types', () => {
const a = ref(0)
const b = ref(a)
expect(typeof (b.value + 1)).toBe('number')
})
it('should unwrap nested values in types', () => {
const a = {
b: ref(0)
}
const c = ref(a)
expect(typeof (c.value.b + 1)).toBe('number')
})
it('should properly unwrap ref types nested inside arrays', () => {
const arr = ref([1, ref(1)]).value
// should unwrap to number[]
arr[0]++
arr[1]++
const arr2 = ref([1, new Map<string, any>(), ref('1')]).value
const value = arr2[0]
if (typeof value === 'string') {
value + 'foo'
} else if (typeof value === 'number') {
value + 1
} else {
// should narrow down to Map type
// and not contain any Ref type
value.has('foo')
}
})
it('should keep tuple types', () => {
const tuple: [number, string, { a: number }, () => number, Ref<number>] = [
0,
'1',
{ a: 1 },
() => 0,
ref(0)
]
const tupleRef = ref(tuple)
tupleRef.value[0]++
expect(tupleRef.value[0]).toBe(1)
tupleRef.value[1] += '1'
expect(tupleRef.value[1]).toBe('11')
tupleRef.value[2].a++
expect(tupleRef.value[2].a).toBe(2)
expect(tupleRef.value[3]()).toBe(0)
tupleRef.value[4]++
expect(tupleRef.value[4]).toBe(1)
})
test('isRef', () => {
expect(isRef(ref(1))).toBe(true)
expect(isRef(computed(() => 1))).toBe(true)
expect(isRef(0)).toBe(false)
expect(isRef(1)).toBe(false)
// an object that looks like a ref isn't necessarily a ref
expect(isRef({ value: 0 })).toBe(false)
})
test('toRefs', () => {
const a = reactive({
x: 1,
y: 2
})
const { x, y } = toRefs(a)
expect(isRef(x)).toBe(true)
expect(isRef(y)).toBe(true)
expect(x.value).toBe(1)
expect(y.value).toBe(2)
// source -> proxy
a.x = 2
a.y = 3
expect(x.value).toBe(2)
expect(y.value).toBe(3)
// proxy -> source
x.value = 3
y.value = 4
expect(a.x).toBe(3)
expect(a.y).toBe(4)
// reactivity
let dummyX, dummyY
effect(() => {
dummyX = x.value
dummyY = y.value
})
expect(dummyX).toBe(x.value)
expect(dummyY).toBe(y.value)
// mutating source should trigger effect using the proxy refs
a.x = 4
a.y = 5
expect(dummyX).toBe(4)
expect(dummyY).toBe(5)
})
})