2020-02-22 11:39:32 +08:00
|
|
|
import {
|
|
|
|
ref,
|
|
|
|
effect,
|
|
|
|
reactive,
|
|
|
|
isRef,
|
2020-04-15 08:49:18 +08:00
|
|
|
toRef,
|
2020-02-22 11:39:32 +08:00
|
|
|
toRefs,
|
|
|
|
Ref,
|
|
|
|
isReactive
|
|
|
|
} from '../src/index'
|
2019-08-27 06:17:02 +08:00
|
|
|
import { computed } from '@vue/runtime-dom'
|
2020-04-23 06:00:10 +08:00
|
|
|
import { shallowRef, unref, customRef, triggerRef } from '../src/ref'
|
2019-05-29 22:11:33 +08:00
|
|
|
|
2019-09-30 22:52:50 +08:00
|
|
|
describe('reactivity/ref', () => {
|
2019-05-29 22:11:33 +08:00
|
|
|
it('should hold a value', () => {
|
2019-08-16 21:42:46 +08:00
|
|
|
const a = ref(1)
|
2019-05-29 22:11:33 +08:00
|
|
|
expect(a.value).toBe(1)
|
|
|
|
a.value = 2
|
|
|
|
expect(a.value).toBe(2)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should be reactive', () => {
|
2019-08-16 21:42:46 +08:00
|
|
|
const a = ref(1)
|
2019-05-29 22:11:33 +08:00
|
|
|
let dummy
|
2020-04-23 03:11:01 +08:00
|
|
|
let calls = 0
|
2019-05-29 22:11:33 +08:00
|
|
|
effect(() => {
|
2020-04-23 03:11:01 +08:00
|
|
|
calls++
|
2019-05-29 22:11:33 +08:00
|
|
|
dummy = a.value
|
|
|
|
})
|
2020-04-23 03:11:01 +08:00
|
|
|
expect(calls).toBe(1)
|
2019-05-29 22:11:33 +08:00
|
|
|
expect(dummy).toBe(1)
|
|
|
|
a.value = 2
|
2020-04-23 03:11:01 +08:00
|
|
|
expect(calls).toBe(2)
|
|
|
|
expect(dummy).toBe(2)
|
|
|
|
// same value should not trigger
|
|
|
|
a.value = 2
|
|
|
|
expect(calls).toBe(2)
|
2019-05-29 22:11:33 +08:00
|
|
|
expect(dummy).toBe(2)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('should make nested properties reactive', () => {
|
2019-08-16 21:42:46 +08:00
|
|
|
const a = ref({
|
2019-05-29 22:11:33 +08:00
|
|
|
count: 1
|
|
|
|
})
|
|
|
|
let dummy
|
|
|
|
effect(() => {
|
|
|
|
dummy = a.value.count
|
|
|
|
})
|
|
|
|
expect(dummy).toBe(1)
|
|
|
|
a.value.count = 2
|
|
|
|
expect(dummy).toBe(2)
|
|
|
|
})
|
|
|
|
|
2020-02-21 21:44:41 +08:00
|
|
|
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)
|
|
|
|
})
|
|
|
|
|
2019-08-22 00:01:05 +08:00
|
|
|
it('should work like a normal property when nested in a reactive object', () => {
|
2019-08-16 21:42:46 +08:00
|
|
|
const a = ref(1)
|
|
|
|
const obj = reactive({
|
2019-05-29 22:11:33 +08:00
|
|
|
a,
|
|
|
|
b: {
|
2020-02-22 00:48:39 +08:00
|
|
|
c: a
|
2019-05-29 22:11:33 +08:00
|
|
|
}
|
|
|
|
})
|
2020-01-21 01:02:08 +08:00
|
|
|
|
2019-12-31 00:29:43 +08:00
|
|
|
let dummy1: number
|
|
|
|
let dummy2: number
|
|
|
|
|
2019-05-29 22:11:33 +08:00
|
|
|
effect(() => {
|
2019-05-30 13:35:50 +08:00
|
|
|
dummy1 = obj.a
|
|
|
|
dummy2 = obj.b.c
|
2019-05-29 22:11:33 +08:00
|
|
|
})
|
2019-12-31 00:29:43 +08:00
|
|
|
|
2020-01-21 01:02:08 +08:00
|
|
|
const assertDummiesEqualTo = (val: number) =>
|
2020-02-22 00:48:39 +08:00
|
|
|
[dummy1, dummy2].forEach(dummy => expect(dummy).toBe(val))
|
2019-12-31 00:29:43 +08:00
|
|
|
|
|
|
|
assertDummiesEqualTo(1)
|
2019-05-29 22:11:33 +08:00
|
|
|
a.value++
|
2019-12-31 00:29:43 +08:00
|
|
|
assertDummiesEqualTo(2)
|
2019-05-29 22:11:33 +08:00
|
|
|
obj.a++
|
2019-12-31 00:29:43 +08:00
|
|
|
assertDummiesEqualTo(3)
|
2019-11-07 22:28:49 +08:00
|
|
|
obj.b.c++
|
2019-12-31 00:29:43 +08:00
|
|
|
assertDummiesEqualTo(4)
|
2019-05-29 22:11:33 +08:00
|
|
|
})
|
2019-08-16 21:54:57 +08:00
|
|
|
|
2019-10-10 23:34:42 +08:00
|
|
|
it('should unwrap nested ref in types', () => {
|
|
|
|
const a = ref(0)
|
|
|
|
const b = ref(a)
|
|
|
|
|
|
|
|
expect(typeof (b.value + 1)).toBe('number')
|
|
|
|
})
|
|
|
|
|
2019-08-16 21:54:57 +08:00
|
|
|
it('should unwrap nested values in types', () => {
|
|
|
|
const a = {
|
|
|
|
b: ref(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
const c = ref(a)
|
|
|
|
|
|
|
|
expect(typeof (c.value.b + 1)).toBe('number')
|
|
|
|
})
|
2019-08-23 21:21:29 +08:00
|
|
|
|
2020-02-22 00:48:39 +08:00
|
|
|
it('should NOT unwrap ref types nested inside arrays', () => {
|
2019-10-19 02:54:05 +08:00
|
|
|
const arr = ref([1, ref(1)]).value
|
2020-02-22 00:48:39 +08:00
|
|
|
;(arr[0] as number)++
|
|
|
|
;(arr[1] as Ref<number>).value++
|
2019-10-19 02:54:05 +08:00
|
|
|
|
|
|
|
const arr2 = ref([1, new Map<string, any>(), ref('1')]).value
|
|
|
|
const value = arr2[0]
|
2020-02-22 00:48:39 +08:00
|
|
|
if (isRef(value)) {
|
2019-10-19 02:54:05 +08:00
|
|
|
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')
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2019-11-09 01:52:24 +08:00
|
|
|
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)
|
2020-02-22 00:48:39 +08:00
|
|
|
tupleRef.value[4].value++
|
|
|
|
expect(tupleRef.value[4].value).toBe(1)
|
2019-11-09 01:52:24 +08:00
|
|
|
})
|
|
|
|
|
2020-04-16 21:24:46 +08:00
|
|
|
it('should keep symbols', () => {
|
|
|
|
const customSymbol = Symbol()
|
|
|
|
const obj = {
|
|
|
|
[Symbol.asyncIterator]: { a: 1 },
|
|
|
|
[Symbol.unscopables]: { b: '1' },
|
|
|
|
[customSymbol]: { c: [1, 2, 3] }
|
|
|
|
}
|
|
|
|
|
|
|
|
const objRef = ref(obj)
|
|
|
|
|
|
|
|
expect(objRef.value[Symbol.asyncIterator]).toBe(obj[Symbol.asyncIterator])
|
|
|
|
expect(objRef.value[Symbol.unscopables]).toBe(obj[Symbol.unscopables])
|
|
|
|
expect(objRef.value[customSymbol]).toStrictEqual(obj[customSymbol])
|
|
|
|
})
|
|
|
|
|
2020-02-22 11:39:32 +08:00
|
|
|
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)
|
|
|
|
})
|
|
|
|
|
2020-04-23 03:11:01 +08:00
|
|
|
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
|
2020-04-23 06:00:10 +08:00
|
|
|
triggerRef(sref)
|
2020-04-23 03:11:01 +08:00
|
|
|
expect(dummy).toBe(2)
|
|
|
|
})
|
|
|
|
|
2019-08-27 06:17:02 +08:00
|
|
|
test('isRef', () => {
|
|
|
|
expect(isRef(ref(1))).toBe(true)
|
|
|
|
expect(isRef(computed(() => 1))).toBe(true)
|
2019-08-23 21:21:29 +08:00
|
|
|
|
2019-08-27 06:17:02 +08:00
|
|
|
expect(isRef(0)).toBe(false)
|
2019-10-06 10:32:50 +08:00
|
|
|
expect(isRef(1)).toBe(false)
|
2019-08-27 06:17:02 +08:00
|
|
|
// an object that looks like a ref isn't necessarily a ref
|
|
|
|
expect(isRef({ value: 0 })).toBe(false)
|
|
|
|
})
|
|
|
|
|
2020-04-15 08:49:18 +08:00
|
|
|
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)
|
|
|
|
})
|
|
|
|
|
2019-08-27 06:17:02 +08:00
|
|
|
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)
|
|
|
|
})
|
2020-04-15 08:45:41 +08:00
|
|
|
|
2020-06-12 04:55:56 +08:00
|
|
|
test('toRefs pass a reactivity object', () => {
|
|
|
|
console.warn = jest.fn()
|
|
|
|
const obj = { x: 1 }
|
|
|
|
toRefs(obj)
|
|
|
|
expect(console.warn).toBeCalled()
|
|
|
|
})
|
2020-04-15 08:45:41 +08:00
|
|
|
test('customRef', () => {
|
|
|
|
let value = 1
|
|
|
|
let _trigger: () => void
|
|
|
|
|
|
|
|
const custom = customRef((track, trigger) => ({
|
|
|
|
get() {
|
|
|
|
track()
|
|
|
|
return value
|
|
|
|
},
|
|
|
|
set(newValue: number) {
|
|
|
|
value = newValue
|
|
|
|
_trigger = trigger
|
|
|
|
}
|
|
|
|
}))
|
|
|
|
|
2020-04-15 08:49:18 +08:00
|
|
|
expect(isRef(custom)).toBe(true)
|
|
|
|
|
2020-04-15 08:45:41 +08:00
|
|
|
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)
|
|
|
|
})
|
2019-05-29 22:11:33 +08:00
|
|
|
})
|