-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
318 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
284 changes: 284 additions & 0 deletions
284
test/unit/features/composition-api/reactivity/reactive.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,284 @@ | ||
import { ref, isRef } from 'vca/reactivity/ref' | ||
import { reactive, isReactive, toRaw, markRaw } from 'vca/reactivity/reactive' | ||
import { effect } from 'vca/reactivity/effect' | ||
import { set } from 'core/observer' | ||
// TODO import { computed } from 'vca/reactivity/computed' | ||
|
||
describe('reactivity/reactive', () => { | ||
test('Object', () => { | ||
const original = { foo: 1 } | ||
const observed = reactive(original) | ||
// @discrepancy Vue 2 does not create proxy objects | ||
// expect(observed).not.toBe(original) | ||
expect(isReactive(observed)).toBe(true) | ||
// @discrepancy Vue 2 does not create proxy objects | ||
// expect(isReactive(original)).toBe(false) | ||
// get | ||
expect(observed.foo).toBe(1) | ||
// has | ||
expect('foo' in observed).toBe(true) | ||
// ownKeys | ||
expect(Object.keys(observed)).toEqual(['foo']) | ||
}) | ||
|
||
test('proto', () => { | ||
const obj = {} | ||
const reactiveObj = reactive(obj) | ||
expect(isReactive(reactiveObj)).toBe(true) | ||
// read prop of reactiveObject will cause reactiveObj[prop] to be reactive | ||
// @ts-ignore | ||
const prototype = reactiveObj['__proto__'] | ||
const otherObj = { data: ['a'] } | ||
expect(isReactive(otherObj)).toBe(false) | ||
const reactiveOther = reactive(otherObj) | ||
expect(isReactive(reactiveOther)).toBe(true) | ||
expect(reactiveOther.data[0]).toBe('a') | ||
}) | ||
|
||
test('nested reactives', () => { | ||
const original = { | ||
nested: { | ||
foo: 1 | ||
}, | ||
array: [{ bar: 2 }] | ||
} | ||
const observed = reactive(original) | ||
expect(isReactive(observed.nested)).toBe(true) | ||
expect(isReactive(observed.array)).toBe(true) | ||
expect(isReactive(observed.array[0])).toBe(true) | ||
}) | ||
|
||
// @discrepancy Vue 2 does not support collections | ||
// test('observing subtypes of IterableCollections(Map, Set)', () => { | ||
// // subtypes of Map | ||
// class CustomMap extends Map {} | ||
// const cmap = reactive(new CustomMap()) | ||
|
||
// expect(cmap instanceof Map).toBe(true) | ||
// expect(isReactive(cmap)).toBe(true) | ||
|
||
// cmap.set('key', {}) | ||
// expect(isReactive(cmap.get('key'))).toBe(true) | ||
|
||
// // subtypes of Set | ||
// class CustomSet extends Set {} | ||
// const cset = reactive(new CustomSet()) | ||
|
||
// expect(cset instanceof Set).toBe(true) | ||
// expect(isReactive(cset)).toBe(true) | ||
|
||
// let dummy | ||
// effect(() => (dummy = cset.has('value'))) | ||
// expect(dummy).toBe(false) | ||
// cset.add('value') | ||
// expect(dummy).toBe(true) | ||
// cset.delete('value') | ||
// expect(dummy).toBe(false) | ||
// }) | ||
|
||
// test('observing subtypes of WeakCollections(WeakMap, WeakSet)', () => { | ||
// // subtypes of WeakMap | ||
// class CustomMap extends WeakMap {} | ||
// const cmap = reactive(new CustomMap()) | ||
|
||
// expect(cmap instanceof WeakMap).toBe(true) | ||
// expect(isReactive(cmap)).toBe(true) | ||
|
||
// const key = {} | ||
// cmap.set(key, {}) | ||
// expect(isReactive(cmap.get(key))).toBe(true) | ||
|
||
// // subtypes of WeakSet | ||
// class CustomSet extends WeakSet {} | ||
// const cset = reactive(new CustomSet()) | ||
|
||
// expect(cset instanceof WeakSet).toBe(true) | ||
// expect(isReactive(cset)).toBe(true) | ||
|
||
// let dummy | ||
// effect(() => (dummy = cset.has(key))) | ||
// expect(dummy).toBe(false) | ||
// cset.add(key) | ||
// expect(dummy).toBe(true) | ||
// cset.delete(key) | ||
// expect(dummy).toBe(false) | ||
// }) | ||
|
||
test('observed value should proxy mutations to original (Object)', () => { | ||
const original: any = { foo: 1 } | ||
const observed = reactive(original) | ||
// set | ||
observed.bar = 1 | ||
expect(observed.bar).toBe(1) | ||
expect(original.bar).toBe(1) | ||
// delete | ||
delete observed.foo | ||
expect('foo' in observed).toBe(false) | ||
expect('foo' in original).toBe(false) | ||
}) | ||
|
||
test('original value change should reflect in observed value (Object)', () => { | ||
const original: any = { foo: 1 } | ||
const observed = reactive(original) | ||
// set | ||
original.bar = 1 | ||
expect(original.bar).toBe(1) | ||
expect(observed.bar).toBe(1) | ||
// delete | ||
delete original.foo | ||
expect('foo' in original).toBe(false) | ||
expect('foo' in observed).toBe(false) | ||
}) | ||
|
||
test('setting a property with an unobserved value should wrap with reactive', () => { | ||
const observed = reactive<{ foo?: object }>({}) | ||
const raw = {} | ||
set(observed, 'foo', raw) | ||
// @discrepancy not a proxy | ||
// expect(observed.foo).not.toBe(raw) | ||
expect(isReactive(observed.foo)).toBe(true) | ||
}) | ||
|
||
test('observing already observed value should return same Proxy', () => { | ||
const original = { foo: 1 } | ||
const observed = reactive(original) | ||
const observed2 = reactive(observed) | ||
expect(observed2).toBe(observed) | ||
}) | ||
|
||
test('observing the same value multiple times should return same Proxy', () => { | ||
const original = { foo: 1 } | ||
const observed = reactive(original) | ||
const observed2 = reactive(original) | ||
expect(observed2).toBe(observed) | ||
}) | ||
|
||
test('should not pollute original object with Proxies', () => { | ||
const original: any = { foo: 1 } | ||
const original2 = { bar: 2 } | ||
const observed = reactive(original) | ||
const observed2 = reactive(original2) | ||
observed.bar = observed2 | ||
expect(observed.bar).toBe(observed2) | ||
expect(original.bar).toBe(original2) | ||
}) | ||
|
||
test('toRaw', () => { | ||
const original = { foo: 1 } | ||
const observed = reactive(original) | ||
expect(toRaw(observed)).toBe(original) | ||
expect(toRaw(original)).toBe(original) | ||
}) | ||
|
||
test('toRaw on object using reactive as prototype', () => { | ||
const original = reactive({}) | ||
const obj = Object.create(original) | ||
const raw = toRaw(obj) | ||
expect(raw).toBe(obj) | ||
expect(raw).not.toBe(toRaw(original)) | ||
}) | ||
|
||
test('should not unwrap Ref<T>', () => { | ||
const observedNumberRef = reactive(ref(1)) | ||
const observedObjectRef = reactive(ref({ foo: 1 })) | ||
|
||
expect(isRef(observedNumberRef)).toBe(true) | ||
expect(isRef(observedObjectRef)).toBe(true) | ||
}) | ||
|
||
// TODO | ||
// test('should unwrap computed refs', () => { | ||
// // readonly | ||
// const a = computed(() => 1) | ||
// // writable | ||
// const b = computed({ | ||
// get: () => 1, | ||
// set: () => {} | ||
// }) | ||
// const obj = reactive({ a, b }) | ||
// // check type | ||
// obj.a + 1 | ||
// obj.b + 1 | ||
// expect(typeof obj.a).toBe(`number`) | ||
// expect(typeof obj.b).toBe(`number`) | ||
// }) | ||
|
||
test('should allow setting property from a ref to another ref', () => { | ||
const foo = ref(0) | ||
const bar = ref(1) | ||
const observed = reactive({ a: foo }) | ||
let dummy | ||
effect(() => { | ||
dummy = observed.a | ||
}) | ||
expect(dummy).toBe(0) | ||
|
||
// @ts-ignore | ||
observed.a = bar | ||
expect(dummy).toBe(1) | ||
|
||
bar.value++ | ||
expect(dummy).toBe(2) | ||
}) | ||
|
||
test('non-observable values', () => { | ||
const assertValue = (value: any) => { | ||
reactive(value) | ||
expect( | ||
`value cannot be made reactive: ${String(value)}` | ||
).toHaveBeenWarnedLast() | ||
} | ||
|
||
// number | ||
assertValue(1) | ||
// string | ||
assertValue('foo') | ||
// boolean | ||
assertValue(false) | ||
// null | ||
assertValue(null) | ||
// undefined | ||
assertValue(undefined) | ||
// symbol | ||
const s = Symbol() | ||
assertValue(s) | ||
|
||
// built-ins should work and return same value | ||
const p = Promise.resolve() | ||
expect(reactive(p)).toBe(p) | ||
const r = new RegExp('') | ||
expect(reactive(r)).toBe(r) | ||
const d = new Date() | ||
expect(reactive(d)).toBe(d) | ||
}) | ||
|
||
test('markRaw', () => { | ||
const obj = reactive({ | ||
foo: { a: 1 }, | ||
bar: markRaw({ b: 2 }) | ||
}) | ||
expect(isReactive(obj.foo)).toBe(true) | ||
expect(isReactive(obj.bar)).toBe(false) | ||
}) | ||
|
||
test('should not observe non-extensible objects', () => { | ||
const obj = reactive({ | ||
foo: Object.preventExtensions({ a: 1 }), | ||
// sealed or frozen objects are considered non-extensible as well | ||
bar: Object.freeze({ a: 1 }), | ||
baz: Object.seal({ a: 1 }) | ||
}) | ||
expect(isReactive(obj.foo)).toBe(false) | ||
expect(isReactive(obj.bar)).toBe(false) | ||
expect(isReactive(obj.baz)).toBe(false) | ||
}) | ||
|
||
test('should not observe objects with __v_skip', () => { | ||
const original = { | ||
foo: 1, | ||
__v_skip: true | ||
} | ||
const observed = reactive(original) | ||
expect(isReactive(observed)).toBe(false) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters