| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 |
- import {
- ref,
- effect,
- reactive,
- isRef,
- toRef,
- toRefs,
- Ref,
- isReactive
- } from '../src/index'
- import { computed } from '@vue/runtime-dom'
- import { shallowRef, unref, customRef, triggerRef } from '../src/ref'
- 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
- let calls = 0
- effect(() => {
- calls++
- dummy = a.value
- })
- expect(calls).toBe(1)
- expect(dummy).toBe(1)
- a.value = 2
- expect(calls).toBe(2)
- expect(dummy).toBe(2)
- // same value should not trigger
- a.value = 2
- expect(calls).toBe(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 without initial value', () => {
- const a = ref()
- let dummy
- effect(() => {
- dummy = a.value
- })
- expect(dummy).toBe(undefined)
- a.value = 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
- }
- })
- let dummy1: number
- let dummy2: number
- effect(() => {
- dummy1 = obj.a
- dummy2 = obj.b.c
- })
- const assertDummiesEqualTo = (val: number) =>
- [dummy1, dummy2].forEach(dummy => expect(dummy).toBe(val))
- assertDummiesEqualTo(1)
- a.value++
- assertDummiesEqualTo(2)
- obj.a++
- assertDummiesEqualTo(3)
- obj.b.c++
- assertDummiesEqualTo(4)
- })
- 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 NOT unwrap ref types nested inside arrays', () => {
- const arr = ref([1, ref(3)]).value
- expect(isRef(arr[0])).toBe(false)
- expect(isRef(arr[1])).toBe(true)
- expect((arr[1] as Ref).value).toBe(3)
- })
- it('should unwrap ref types as props of arrays', () => {
- const arr = [ref(0)]
- const symbolKey = Symbol('')
- arr['' as any] = ref(1)
- arr[symbolKey as any] = ref(2)
- const arrRef = ref(arr).value
- expect(isRef(arrRef[0])).toBe(true)
- expect(isRef(arrRef['' as any])).toBe(false)
- expect(isRef(arrRef[symbolKey as any])).toBe(false)
- expect(arrRef['' as any]).toBe(1)
- expect(arrRef[symbolKey as any]).toBe(2)
- })
- 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].value++
- expect(tupleRef.value[4].value).toBe(1)
- })
- it('should keep symbols', () => {
- const customSymbol = Symbol()
- const obj = {
- [Symbol.asyncIterator]: ref(1),
- [Symbol.hasInstance]: { a: ref('a') },
- [Symbol.isConcatSpreadable]: { b: ref(true) },
- [Symbol.iterator]: [ref(1)],
- [Symbol.match]: new Set<Ref<number>>(),
- [Symbol.matchAll]: new Map<number, Ref<string>>(),
- [Symbol.replace]: { arr: [ref('a')] },
- [Symbol.search]: { set: new Set<Ref<number>>() },
- [Symbol.species]: { map: new Map<number, Ref<string>>() },
- [Symbol.split]: new WeakSet<Ref<boolean>>(),
- [Symbol.toPrimitive]: new WeakMap<Ref<boolean>, string>(),
- [Symbol.toStringTag]: { weakSet: new WeakSet<Ref<boolean>>() },
- [Symbol.unscopables]: { weakMap: new WeakMap<Ref<boolean>, string>() },
- [customSymbol]: { arr: [ref(1)] }
- }
- const objRef = ref(obj)
- const keys: (keyof typeof obj)[] = [
- Symbol.asyncIterator,
- Symbol.hasInstance,
- Symbol.isConcatSpreadable,
- Symbol.iterator,
- Symbol.match,
- Symbol.matchAll,
- Symbol.replace,
- Symbol.search,
- Symbol.species,
- Symbol.split,
- Symbol.toPrimitive,
- Symbol.toStringTag,
- Symbol.unscopables,
- customSymbol
- ]
- keys.forEach(key => {
- expect(objRef.value[key]).toStrictEqual(obj[key])
- })
- })
- test('unref', () => {
- expect(unref(1)).toBe(1)
- expect(unref(ref(1))).toBe(1)
- })
- test('shallowRef', () => {
- const sref = shallowRef({ a: 1 })
- expect(isReactive(sref.value)).toBe(false)
- let dummy
- effect(() => {
- dummy = sref.value.a
- })
- expect(dummy).toBe(1)
- sref.value = { a: 2 }
- expect(isReactive(sref.value)).toBe(false)
- expect(dummy).toBe(2)
- })
- test('shallowRef force trigger', () => {
- const sref = shallowRef({ a: 1 })
- let dummy
- effect(() => {
- dummy = sref.value.a
- })
- expect(dummy).toBe(1)
- sref.value.a = 2
- expect(dummy).toBe(1) // should not trigger yet
- // force trigger
- triggerRef(sref)
- expect(dummy).toBe(2)
- })
- 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('toRef', () => {
- const a = reactive({
- x: 1
- })
- const x = toRef(a, 'x')
- expect(isRef(x)).toBe(true)
- expect(x.value).toBe(1)
- // source -> proxy
- a.x = 2
- expect(x.value).toBe(2)
- // proxy -> source
- x.value = 3
- expect(a.x).toBe(3)
- // reactivity
- let dummyX
- effect(() => {
- dummyX = x.value
- })
- expect(dummyX).toBe(x.value)
- // mutating source should trigger effect using the proxy refs
- a.x = 4
- expect(dummyX).toBe(4)
- // should keep ref
- const r = { x: ref(1) }
- expect(toRef(r, 'x')).toBe(r.x)
- })
- 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)
- })
- test('toRefs should warn on plain object', () => {
- toRefs({})
- expect(`toRefs() expects a reactive object`).toHaveBeenWarned()
- })
- test('toRefs should warn on plain array', () => {
- toRefs([])
- expect(`toRefs() expects a reactive object`).toHaveBeenWarned()
- })
- test('toRefs reactive array', () => {
- const arr = reactive(['a', 'b', 'c'])
- const refs = toRefs(arr)
- expect(Array.isArray(refs)).toBe(true)
- refs[0].value = '1'
- expect(arr[0]).toBe('1')
- arr[1] = '2'
- expect(refs[1].value).toBe('2')
- })
- test('customRef', () => {
- let value = 1
- let _trigger: () => void
- const custom = customRef((track, trigger) => ({
- get() {
- track()
- return value
- },
- set(newValue: number) {
- value = newValue
- _trigger = trigger
- }
- }))
- expect(isRef(custom)).toBe(true)
- let dummy
- effect(() => {
- dummy = custom.value
- })
- expect(dummy).toBe(1)
- custom.value = 2
- // should not trigger yet
- expect(dummy).toBe(1)
- _trigger!()
- expect(dummy).toBe(2)
- })
- test('should not trigger when setting value to same proxy', () => {
- const obj = reactive({ count: 0 })
- const a = ref(obj)
- const spy1 = jest.fn(() => a.value)
- effect(spy1)
- a.value = obj
- expect(spy1).toBeCalledTimes(1)
- const b = shallowRef(obj)
- const spy2 = jest.fn(() => b.value)
- effect(spy2)
- b.value = obj
- expect(spy2).toBeCalledTimes(1)
- })
- })
|