]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
test: better collection coverage + tests for immutable
authorEvan You <yyx990803@gmail.com>
Thu, 20 Sep 2018 20:18:22 +0000 (16:18 -0400)
committerEvan You <yyx990803@gmail.com>
Thu, 20 Sep 2018 20:18:22 +0000 (16:18 -0400)
packages/observer/__tests__/collections/Map.spec.ts
packages/observer/__tests__/collections/Set.spec.ts
packages/observer/__tests__/collections/WeakMap.spec.ts
packages/observer/__tests__/collections/WeakSet.spec.ts
packages/observer/__tests__/immutable.spec.ts
packages/observer/src/autorun.ts
packages/observer/src/baseHandlers.ts
packages/observer/src/collectionHandlers.ts
packages/observer/src/index.ts

index c9d2de24ec3cd75d7ffe6401837246bdbbe8832f..20f97874de3a882b505da682b6daa5736297a05d 100644 (file)
@@ -206,5 +206,34 @@ describe('observer/collections', () => {
       map.delete('key')
       expect(dummy).toBe(undefined)
     })
+
+    it('should not pollute original Map with Proxies', () => {
+      const map = new Map()
+      const observed = observable(map)
+      const value = observable({})
+      observed.set('key', value)
+      expect(map.get('key')).not.toBe(value)
+      expect(map.get('key')).toBe(unwrap(value))
+    })
+
+    it('should return observable versions of contained values', () => {
+      const observed = observable(new Map())
+      const value = {}
+      observed.set('key', value)
+      const wrapped = observed.get('key')
+      expect(isObservable(wrapped)).toBe(true)
+      expect(unwrap(wrapped)).toBe(value)
+    })
+
+    it('should observed nested data', () => {
+      const observed = observable(new Map())
+      observed.set('key', { a: 1 })
+      let dummy
+      autorun(() => {
+        dummy = observed.get('key').a
+      })
+      observed.get('key').a = 2
+      expect(dummy).toBe(2)
+    })
   })
 })
index 5c6b3f9e9218da9d2648bd551f005e67755aa64a..159e5e01b825947225649fb445fb6b3a7dc8fa33 100644 (file)
 import { observable, autorun, isObservable, unwrap } from '../../src'
 
-describe('Set', () => {
-  it('instanceof', () => {
-    const original = new Set()
-    const observed = observable(original)
-    expect(isObservable(observed)).toBe(true)
-    expect(original instanceof Set).toBe(true)
-    expect(observed instanceof Set).toBe(true)
-  })
+describe('observer/collections', () => {
+  describe('Set', () => {
+    it('instanceof', () => {
+      const original = new Set()
+      const observed = observable(original)
+      expect(isObservable(observed)).toBe(true)
+      expect(original instanceof Set).toBe(true)
+      expect(observed instanceof Set).toBe(true)
+    })
 
-  it('should observe mutations', () => {
-    let dummy
-    const set = observable(new Set())
-    autorun(() => (dummy = set.has('value')))
+    it('should observe mutations', () => {
+      let dummy
+      const set = observable(new Set())
+      autorun(() => (dummy = set.has('value')))
 
-    expect(dummy).toBe(false)
-    set.add('value')
-    expect(dummy).toBe(true)
-    set.delete('value')
-    expect(dummy).toBe(false)
-  })
-
-  it('should observe for of iteration', () => {
-    let dummy
-    const set = observable(new Set())
-    autorun(() => {
-      dummy = 0
-      for (let num of set) {
-        dummy += num
-      }
+      expect(dummy).toBe(false)
+      set.add('value')
+      expect(dummy).toBe(true)
+      set.delete('value')
+      expect(dummy).toBe(false)
     })
 
-    expect(dummy).toBe(0)
-    set.add(2)
-    set.add(1)
-    expect(dummy).toBe(3)
-    set.delete(2)
-    expect(dummy).toBe(1)
-    set.clear()
-    expect(dummy).toBe(0)
-  })
+    it('should observe for of iteration', () => {
+      let dummy
+      const set = observable(new Set())
+      autorun(() => {
+        dummy = 0
+        for (let num of set) {
+          dummy += num
+        }
+      })
 
-  it('should observe forEach iteration', () => {
-    let dummy: any
-    const set = observable(new Set())
-    autorun(() => {
-      dummy = 0
-      set.forEach(num => (dummy += num))
+      expect(dummy).toBe(0)
+      set.add(2)
+      set.add(1)
+      expect(dummy).toBe(3)
+      set.delete(2)
+      expect(dummy).toBe(1)
+      set.clear()
+      expect(dummy).toBe(0)
     })
 
-    expect(dummy).toBe(0)
-    set.add(2)
-    set.add(1)
-    expect(dummy).toBe(3)
-    set.delete(2)
-    expect(dummy).toBe(1)
-    set.clear()
-    expect(dummy).toBe(0)
-  })
+    it('should observe forEach iteration', () => {
+      let dummy: any
+      const set = observable(new Set())
+      autorun(() => {
+        dummy = 0
+        set.forEach(num => (dummy += num))
+      })
 
-  it('should observe values iteration', () => {
-    let dummy
-    const set = observable(new Set())
-    autorun(() => {
-      dummy = 0
-      for (let num of set.values()) {
-        dummy += num
-      }
+      expect(dummy).toBe(0)
+      set.add(2)
+      set.add(1)
+      expect(dummy).toBe(3)
+      set.delete(2)
+      expect(dummy).toBe(1)
+      set.clear()
+      expect(dummy).toBe(0)
     })
 
-    expect(dummy).toBe(0)
-    set.add(2)
-    set.add(1)
-    expect(dummy).toBe(3)
-    set.delete(2)
-    expect(dummy).toBe(1)
-    set.clear()
-    expect(dummy).toBe(0)
-  })
+    it('should observe values iteration', () => {
+      let dummy
+      const set = observable(new Set())
+      autorun(() => {
+        dummy = 0
+        for (let num of set.values()) {
+          dummy += num
+        }
+      })
 
-  it('should observe keys iteration', () => {
-    let dummy
-    const set = observable(new Set())
-    autorun(() => {
-      dummy = 0
-      for (let num of set.keys()) {
-        dummy += num
-      }
+      expect(dummy).toBe(0)
+      set.add(2)
+      set.add(1)
+      expect(dummy).toBe(3)
+      set.delete(2)
+      expect(dummy).toBe(1)
+      set.clear()
+      expect(dummy).toBe(0)
     })
 
-    expect(dummy).toBe(0)
-    set.add(2)
-    set.add(1)
-    expect(dummy).toBe(3)
-    set.delete(2)
-    expect(dummy).toBe(1)
-    set.clear()
-    expect(dummy).toBe(0)
-  })
+    it('should observe keys iteration', () => {
+      let dummy
+      const set = observable(new Set())
+      autorun(() => {
+        dummy = 0
+        for (let num of set.keys()) {
+          dummy += num
+        }
+      })
 
-  it('should observe entries iteration', () => {
-    let dummy
-    const set = observable(new Set())
-    autorun(() => {
-      dummy = 0
-      // eslint-disable-next-line no-unused-vars
-      for (let [key, num] of set.entries()) {
-        key
-        dummy += num
-      }
+      expect(dummy).toBe(0)
+      set.add(2)
+      set.add(1)
+      expect(dummy).toBe(3)
+      set.delete(2)
+      expect(dummy).toBe(1)
+      set.clear()
+      expect(dummy).toBe(0)
     })
 
-    expect(dummy).toBe(0)
-    set.add(2)
-    set.add(1)
-    expect(dummy).toBe(3)
-    set.delete(2)
-    expect(dummy).toBe(1)
-    set.clear()
-    expect(dummy).toBe(0)
-  })
+    it('should observe entries iteration', () => {
+      let dummy
+      const set = observable(new Set())
+      autorun(() => {
+        dummy = 0
+        // eslint-disable-next-line no-unused-vars
+        for (let [key, num] of set.entries()) {
+          key
+          dummy += num
+        }
+      })
 
-  it('should be triggered by clearing', () => {
-    let dummy
-    const set = observable(new Set())
-    autorun(() => (dummy = set.has('key')))
+      expect(dummy).toBe(0)
+      set.add(2)
+      set.add(1)
+      expect(dummy).toBe(3)
+      set.delete(2)
+      expect(dummy).toBe(1)
+      set.clear()
+      expect(dummy).toBe(0)
+    })
 
-    expect(dummy).toBe(false)
-    set.add('key')
-    expect(dummy).toBe(true)
-    set.clear()
-    expect(dummy).toBe(false)
-  })
+    it('should be triggered by clearing', () => {
+      let dummy
+      const set = observable(new Set())
+      autorun(() => (dummy = set.has('key')))
 
-  it('should not observe custom property mutations', () => {
-    let dummy
-    const set: any = observable(new Set())
-    autorun(() => (dummy = set.customProp))
+      expect(dummy).toBe(false)
+      set.add('key')
+      expect(dummy).toBe(true)
+      set.clear()
+      expect(dummy).toBe(false)
+    })
 
-    expect(dummy).toBe(undefined)
-    set.customProp = 'Hello World'
-    expect(dummy).toBe(undefined)
-  })
+    it('should not observe custom property mutations', () => {
+      let dummy
+      const set: any = observable(new Set())
+      autorun(() => (dummy = set.customProp))
 
-  it('should observe size mutations', () => {
-    let dummy
-    const set = observable(new Set())
-    autorun(() => (dummy = set.size))
-
-    expect(dummy).toBe(0)
-    set.add('value')
-    set.add('value2')
-    expect(dummy).toBe(2)
-    set.delete('value')
-    expect(dummy).toBe(1)
-    set.clear()
-    expect(dummy).toBe(0)
-  })
+      expect(dummy).toBe(undefined)
+      set.customProp = 'Hello World'
+      expect(dummy).toBe(undefined)
+    })
 
-  it('should not observe non value changing mutations', () => {
-    let dummy
-    const set = observable(new Set())
-    const setSpy = jest.fn(() => (dummy = set.has('value')))
-    autorun(setSpy)
-
-    expect(dummy).toBe(false)
-    expect(setSpy).toHaveBeenCalledTimes(1)
-    set.add('value')
-    expect(dummy).toBe(true)
-    expect(setSpy).toHaveBeenCalledTimes(2)
-    set.add('value')
-    expect(dummy).toBe(true)
-    expect(setSpy).toHaveBeenCalledTimes(2)
-    set.delete('value')
-    expect(dummy).toBe(false)
-    expect(setSpy).toHaveBeenCalledTimes(3)
-    set.delete('value')
-    expect(dummy).toBe(false)
-    expect(setSpy).toHaveBeenCalledTimes(3)
-    set.clear()
-    expect(dummy).toBe(false)
-    expect(setSpy).toHaveBeenCalledTimes(3)
-  })
+    it('should observe size mutations', () => {
+      let dummy
+      const set = observable(new Set())
+      autorun(() => (dummy = set.size))
+
+      expect(dummy).toBe(0)
+      set.add('value')
+      set.add('value2')
+      expect(dummy).toBe(2)
+      set.delete('value')
+      expect(dummy).toBe(1)
+      set.clear()
+      expect(dummy).toBe(0)
+    })
 
-  it('should not observe raw data', () => {
-    let dummy
-    const set = observable(new Set())
-    autorun(() => (dummy = unwrap(set).has('value')))
+    it('should not observe non value changing mutations', () => {
+      let dummy
+      const set = observable(new Set())
+      const setSpy = jest.fn(() => (dummy = set.has('value')))
+      autorun(setSpy)
+
+      expect(dummy).toBe(false)
+      expect(setSpy).toHaveBeenCalledTimes(1)
+      set.add('value')
+      expect(dummy).toBe(true)
+      expect(setSpy).toHaveBeenCalledTimes(2)
+      set.add('value')
+      expect(dummy).toBe(true)
+      expect(setSpy).toHaveBeenCalledTimes(2)
+      set.delete('value')
+      expect(dummy).toBe(false)
+      expect(setSpy).toHaveBeenCalledTimes(3)
+      set.delete('value')
+      expect(dummy).toBe(false)
+      expect(setSpy).toHaveBeenCalledTimes(3)
+      set.clear()
+      expect(dummy).toBe(false)
+      expect(setSpy).toHaveBeenCalledTimes(3)
+    })
 
-    expect(dummy).toBe(false)
-    set.add('value')
-    expect(dummy).toBe(false)
-  })
+    it('should not observe raw data', () => {
+      let dummy
+      const set = observable(new Set())
+      autorun(() => (dummy = unwrap(set).has('value')))
+
+      expect(dummy).toBe(false)
+      set.add('value')
+      expect(dummy).toBe(false)
+    })
 
-  it('should not observe raw iterations', () => {
-    let dummy = 0
-    const set = observable(new Set())
-    autorun(() => {
-      dummy = 0
-      for (let [num] of unwrap(set).entries()) {
-        dummy += num
-      }
-      for (let num of unwrap(set).keys()) {
-        dummy += num
-      }
-      for (let num of unwrap(set).values()) {
-        dummy += num
-      }
-      unwrap(set).forEach(num => {
-        dummy += num
+    it('should not observe raw iterations', () => {
+      let dummy = 0
+      const set = observable(new Set())
+      autorun(() => {
+        dummy = 0
+        for (let [num] of unwrap(set).entries()) {
+          dummy += num
+        }
+        for (let num of unwrap(set).keys()) {
+          dummy += num
+        }
+        for (let num of unwrap(set).values()) {
+          dummy += num
+        }
+        unwrap(set).forEach(num => {
+          dummy += num
+        })
+        for (let num of unwrap(set)) {
+          dummy += num
+        }
       })
-      for (let num of unwrap(set)) {
-        dummy += num
-      }
+
+      expect(dummy).toBe(0)
+      set.add(2)
+      set.add(3)
+      expect(dummy).toBe(0)
+      set.delete(2)
+      expect(dummy).toBe(0)
     })
 
-    expect(dummy).toBe(0)
-    set.add(2)
-    set.add(3)
-    expect(dummy).toBe(0)
-    set.delete(2)
-    expect(dummy).toBe(0)
-  })
+    it('should not be triggered by raw mutations', () => {
+      let dummy
+      const set = observable(new Set())
+      autorun(() => (dummy = set.has('value')))
+
+      expect(dummy).toBe(false)
+      unwrap(set).add('value')
+      expect(dummy).toBe(false)
+      dummy = true
+      unwrap(set).delete('value')
+      expect(dummy).toBe(true)
+      unwrap(set).clear()
+      expect(dummy).toBe(true)
+    })
 
-  it('should not be triggered by raw mutations', () => {
-    let dummy
-    const set = observable(new Set())
-    autorun(() => (dummy = set.has('value')))
-
-    expect(dummy).toBe(false)
-    unwrap(set).add('value')
-    expect(dummy).toBe(false)
-    dummy = true
-    unwrap(set).delete('value')
-    expect(dummy).toBe(true)
-    unwrap(set).clear()
-    expect(dummy).toBe(true)
-  })
+    it('should not observe raw size mutations', () => {
+      let dummy
+      const set = observable(new Set())
+      autorun(() => (dummy = unwrap(set).size))
 
-  it('should not observe raw size mutations', () => {
-    let dummy
-    const set = observable(new Set())
-    autorun(() => (dummy = unwrap(set).size))
+      expect(dummy).toBe(0)
+      set.add('value')
+      expect(dummy).toBe(0)
+    })
 
-    expect(dummy).toBe(0)
-    set.add('value')
-    expect(dummy).toBe(0)
-  })
+    it('should not be triggered by raw size mutations', () => {
+      let dummy
+      const set = observable(new Set())
+      autorun(() => (dummy = set.size))
 
-  it('should not be triggered by raw size mutations', () => {
-    let dummy
-    const set = observable(new Set())
-    autorun(() => (dummy = set.size))
+      expect(dummy).toBe(0)
+      unwrap(set).add('value')
+      expect(dummy).toBe(0)
+    })
 
-    expect(dummy).toBe(0)
-    unwrap(set).add('value')
-    expect(dummy).toBe(0)
-  })
+    it('should support objects as key', () => {
+      let dummy
+      const key = {}
+      const set = observable(new Set())
+      const setSpy = jest.fn(() => (dummy = set.has(key)))
+      autorun(setSpy)
 
-  it('should support objects as key', () => {
-    let dummy
-    const key = {}
-    const set = observable(new Set())
-    const setSpy = jest.fn(() => (dummy = set.has(key)))
-    autorun(setSpy)
+      expect(dummy).toBe(false)
+      expect(setSpy).toHaveBeenCalledTimes(1)
 
-    expect(dummy).toBe(false)
-    expect(setSpy).toHaveBeenCalledTimes(1)
+      set.add({})
+      expect(dummy).toBe(false)
+      expect(setSpy).toHaveBeenCalledTimes(1)
 
-    set.add({})
-    expect(dummy).toBe(false)
-    expect(setSpy).toHaveBeenCalledTimes(1)
+      set.add(key)
+      expect(dummy).toBe(true)
+      expect(setSpy).toHaveBeenCalledTimes(2)
+    })
 
-    set.add(key)
-    expect(dummy).toBe(true)
-    expect(setSpy).toHaveBeenCalledTimes(2)
+    it('should not pollute original Set with Proxies', () => {
+      const set = new Set()
+      const observed = observable(set)
+      const value = observable({})
+      observed.add(value)
+      expect(observed.has(value)).toBe(true)
+      expect(set.has(value)).toBe(false)
+    })
   })
 })
index db97dd0b113cfb0aa34e1f886760dcacdb54d894..f20a1961d91d38401329af3a9e4cb5e2f34ae180 100644 (file)
 import { observable, autorun, unwrap, isObservable } from '../../src'
 
-describe('observer/collections/WeakMap', () => {
-  test('instanceof', () => {
-    const original = new WeakMap()
-    const observed = observable(original)
-    expect(isObservable(observed)).toBe(true)
-    expect(original instanceof WeakMap).toBe(true)
-    expect(observed instanceof WeakMap).toBe(true)
-  })
+describe('observer/collections', () => {
+  describe('WeakMap', () => {
+    test('instanceof', () => {
+      const original = new WeakMap()
+      const observed = observable(original)
+      expect(isObservable(observed)).toBe(true)
+      expect(original instanceof WeakMap).toBe(true)
+      expect(observed instanceof WeakMap).toBe(true)
+    })
 
-  it('should observe mutations', () => {
-    let dummy
-    const key = {}
-    const map = observable(new WeakMap())
-    autorun(() => {
-      dummy = map.get(key)
+    it('should observe mutations', () => {
+      let dummy
+      const key = {}
+      const map = observable(new WeakMap())
+      autorun(() => {
+        dummy = map.get(key)
+      })
+
+      expect(dummy).toBe(undefined)
+      map.set(key, 'value')
+      expect(dummy).toBe('value')
+      map.set(key, 'value2')
+      expect(dummy).toBe('value2')
+      map.delete(key)
+      expect(dummy).toBe(undefined)
     })
 
-    expect(dummy).toBe(undefined)
-    map.set(key, 'value')
-    expect(dummy).toBe('value')
-    map.set(key, 'value2')
-    expect(dummy).toBe('value2')
-    map.delete(key)
-    expect(dummy).toBe(undefined)
-  })
+    it('should not observe custom property mutations', () => {
+      let dummy
+      const map: any = observable(new WeakMap())
+      autorun(() => (dummy = map.customProp))
 
-  it('should not observe custom property mutations', () => {
-    let dummy
-    const map: any = observable(new Map())
-    autorun(() => (dummy = map.customProp))
+      expect(dummy).toBe(undefined)
+      map.customProp = 'Hello World'
+      expect(dummy).toBe(undefined)
+    })
 
-    expect(dummy).toBe(undefined)
-    map.customProp = 'Hello World'
-    expect(dummy).toBe(undefined)
-  })
+    it('should not observe non value changing mutations', () => {
+      let dummy
+      const key = {}
+      const map = observable(new WeakMap())
+      const mapSpy = jest.fn(() => (dummy = map.get(key)))
+      autorun(mapSpy)
+
+      expect(dummy).toBe(undefined)
+      expect(mapSpy).toHaveBeenCalledTimes(1)
+      map.set(key, 'value')
+      expect(dummy).toBe('value')
+      expect(mapSpy).toHaveBeenCalledTimes(2)
+      map.set(key, 'value')
+      expect(dummy).toBe('value')
+      expect(mapSpy).toHaveBeenCalledTimes(2)
+      map.delete(key)
+      expect(dummy).toBe(undefined)
+      expect(mapSpy).toHaveBeenCalledTimes(3)
+      map.delete(key)
+      expect(dummy).toBe(undefined)
+      expect(mapSpy).toHaveBeenCalledTimes(3)
+    })
 
-  it('should not observe non value changing mutations', () => {
-    let dummy
-    const key = {}
-    const map = observable(new Map())
-    const mapSpy = jest.fn(() => (dummy = map.get(key)))
-    autorun(mapSpy)
+    it('should not observe raw data', () => {
+      let dummy
+      const key = {}
+      const map = observable(new WeakMap())
+      autorun(() => (dummy = unwrap(map).get(key)))
 
-    expect(dummy).toBe(undefined)
-    expect(mapSpy).toHaveBeenCalledTimes(1)
-    map.set(key, 'value')
-    expect(dummy).toBe('value')
-    expect(mapSpy).toHaveBeenCalledTimes(2)
-    map.set(key, 'value')
-    expect(dummy).toBe('value')
-    expect(mapSpy).toHaveBeenCalledTimes(2)
-    map.delete(key)
-    expect(dummy).toBe(undefined)
-    expect(mapSpy).toHaveBeenCalledTimes(3)
-    map.delete(key)
-    expect(dummy).toBe(undefined)
-    expect(mapSpy).toHaveBeenCalledTimes(3)
-    map.clear()
-    expect(dummy).toBe(undefined)
-    expect(mapSpy).toHaveBeenCalledTimes(3)
-  })
+      expect(dummy).toBe(undefined)
+      map.set(key, 'Hello')
+      expect(dummy).toBe(undefined)
+      map.delete(key)
+      expect(dummy).toBe(undefined)
+    })
 
-  it('should not observe raw data', () => {
-    let dummy
-    const key = {}
-    const map = observable(new Map())
-    autorun(() => (dummy = unwrap(map).get('key')))
+    it('should not pollute original Map with Proxies', () => {
+      const map = new WeakMap()
+      const observed = observable(map)
+      const key = {}
+      const value = observable({})
+      observed.set(key, value)
+      expect(map.get(key)).not.toBe(value)
+      expect(map.get(key)).toBe(unwrap(value))
+    })
 
-    expect(dummy).toBe(undefined)
-    map.set(key, 'Hello')
-    expect(dummy).toBe(undefined)
-    map.delete(key)
-    expect(dummy).toBe(undefined)
+    it('should return observable versions of contained values', () => {
+      const observed = observable(new WeakMap())
+      const key = {}
+      const value = {}
+      observed.set(key, value)
+      const wrapped = observed.get(key)
+      expect(isObservable(wrapped)).toBe(true)
+      expect(unwrap(wrapped)).toBe(value)
+    })
+
+    it('should observed nested data', () => {
+      const observed = observable(new Map())
+      const key = {}
+      observed.set(key, { a: 1 })
+      let dummy
+      autorun(() => {
+        dummy = observed.get(key).a
+      })
+      observed.get(key).a = 2
+      expect(dummy).toBe(2)
+    })
   })
 })
index 1efd9b3b509e1f011f17e067d045bb1f71417c49..23b0534239a1c4cc19a050374c9a2df4906b17cc 100644 (file)
@@ -1,79 +1,90 @@
 import { observable, isObservable, autorun, unwrap } from '../../src'
 
-describe('WeakSet', () => {
-  it('instanceof', () => {
-    const original = new Set()
-    const observed = observable(original)
-    expect(isObservable(observed)).toBe(true)
-    expect(original instanceof Set).toBe(true)
-    expect(observed instanceof Set).toBe(true)
-  })
+describe('observer/collections', () => {
+  describe('WeakSet', () => {
+    it('instanceof', () => {
+      const original = new Set()
+      const observed = observable(original)
+      expect(isObservable(observed)).toBe(true)
+      expect(original instanceof Set).toBe(true)
+      expect(observed instanceof Set).toBe(true)
+    })
 
-  it('should observe mutations', () => {
-    let dummy
-    const value = {}
-    const set = observable(new WeakSet())
-    autorun(() => (dummy = set.has(value)))
+    it('should observe mutations', () => {
+      let dummy
+      const value = {}
+      const set = observable(new WeakSet())
+      autorun(() => (dummy = set.has(value)))
 
-    expect(dummy).toBe(false)
-    set.add(value)
-    expect(dummy).toBe(true)
-    set.delete(value)
-    expect(dummy).toBe(false)
-  })
+      expect(dummy).toBe(false)
+      set.add(value)
+      expect(dummy).toBe(true)
+      set.delete(value)
+      expect(dummy).toBe(false)
+    })
 
-  it('should not observe custom property mutations', () => {
-    let dummy
-    const set: any = observable(new WeakSet())
-    autorun(() => (dummy = set.customProp))
+    it('should not observe custom property mutations', () => {
+      let dummy
+      const set: any = observable(new WeakSet())
+      autorun(() => (dummy = set.customProp))
 
-    expect(dummy).toBe(undefined)
-    set.customProp = 'Hello World'
-    expect(dummy).toBe(undefined)
-  })
+      expect(dummy).toBe(undefined)
+      set.customProp = 'Hello World'
+      expect(dummy).toBe(undefined)
+    })
 
-  it('should not observe non value changing mutations', () => {
-    let dummy
-    const value = {}
-    const set = observable(new WeakSet())
-    const setSpy = jest.fn(() => (dummy = set.has(value)))
-    autorun(setSpy)
+    it('should not observe non value changing mutations', () => {
+      let dummy
+      const value = {}
+      const set = observable(new WeakSet())
+      const setSpy = jest.fn(() => (dummy = set.has(value)))
+      autorun(setSpy)
 
-    expect(dummy).toBe(false)
-    expect(setSpy).toHaveBeenCalledTimes(1)
-    set.add(value)
-    expect(dummy).toBe(true)
-    expect(setSpy).toHaveBeenCalledTimes(2)
-    set.add(value)
-    expect(dummy).toBe(true)
-    expect(setSpy).toHaveBeenCalledTimes(2)
-    set.delete(value)
-    expect(dummy).toBe(false)
-    expect(setSpy).toHaveBeenCalledTimes(3)
-    set.delete(value)
-    expect(dummy).toBe(false)
-    expect(setSpy).toHaveBeenCalledTimes(3)
-  })
+      expect(dummy).toBe(false)
+      expect(setSpy).toHaveBeenCalledTimes(1)
+      set.add(value)
+      expect(dummy).toBe(true)
+      expect(setSpy).toHaveBeenCalledTimes(2)
+      set.add(value)
+      expect(dummy).toBe(true)
+      expect(setSpy).toHaveBeenCalledTimes(2)
+      set.delete(value)
+      expect(dummy).toBe(false)
+      expect(setSpy).toHaveBeenCalledTimes(3)
+      set.delete(value)
+      expect(dummy).toBe(false)
+      expect(setSpy).toHaveBeenCalledTimes(3)
+    })
 
-  it('should not observe raw data', () => {
-    const value = {}
-    let dummy
-    const set = observable(new WeakSet())
-    autorun(() => (dummy = unwrap(set).has(value)))
+    it('should not observe raw data', () => {
+      const value = {}
+      let dummy
+      const set = observable(new WeakSet())
+      autorun(() => (dummy = unwrap(set).has(value)))
 
-    expect(dummy).toBe(false)
-    set.add(value)
-    expect(dummy).toBe(false)
-  })
+      expect(dummy).toBe(false)
+      set.add(value)
+      expect(dummy).toBe(false)
+    })
+
+    it('should not be triggered by raw mutations', () => {
+      const value = {}
+      let dummy
+      const set = observable(new WeakSet())
+      autorun(() => (dummy = set.has(value)))
 
-  it('should not be triggered by raw mutations', () => {
-    const value = {}
-    let dummy
-    const set = observable(new WeakSet())
-    autorun(() => (dummy = set.has(value)))
+      expect(dummy).toBe(false)
+      unwrap(set).add(value)
+      expect(dummy).toBe(false)
+    })
 
-    expect(dummy).toBe(false)
-    unwrap(set).add(value)
-    expect(dummy).toBe(false)
+    it('should not pollute original Set with Proxies', () => {
+      const set = new WeakSet()
+      const observed = observable(set)
+      const value = observable({})
+      observed.add(value)
+      expect(observed.has(value)).toBe(true)
+      expect(set.has(value)).toBe(false)
+    })
   })
 })
index 64d6e2aa52c9d30df4d0980a93bb9357df72c57c..4ce9dbf1bd3856a6b146738c081a6a2f69b6077c 100644 (file)
@@ -1 +1,339 @@
-describe('observer/immutable', () => {})
+import {
+  observable,
+  immutable,
+  unwrap,
+  isObservable,
+  isImmutable,
+  markNonReactive,
+  markImmutable,
+  lock,
+  unlock,
+  autorun
+} from '../src'
+
+describe('observer/immutable', () => {
+  let warn: any
+
+  beforeEach(() => {
+    warn = jest.spyOn(console, 'warn')
+    warn.mockImplementation(() => {})
+  })
+
+  afterEach(() => {
+    warn.mockRestore()
+  })
+
+  describe('Object', () => {
+    it('should make nested values immutable', () => {
+      const original = { foo: 1, bar: { baz: 2 } }
+      const observed = immutable(original)
+      expect(observed).not.toBe(original)
+      expect(isObservable(observed)).toBe(true)
+      expect(isImmutable(observed)).toBe(true)
+      expect(isObservable(original)).toBe(false)
+      expect(isImmutable(original)).toBe(false)
+      expect(isObservable(observed.bar)).toBe(true)
+      expect(isImmutable(observed.bar)).toBe(true)
+      expect(isObservable(original.bar)).toBe(false)
+      expect(isImmutable(original.bar)).toBe(false)
+      // get
+      expect(observed.foo).toBe(1)
+      // has
+      expect('foo' in observed).toBe(true)
+      // ownKeys
+      expect(Object.keys(observed)).toEqual(['foo', 'bar'])
+    })
+
+    it('should not allow mutation', () => {
+      const observed = immutable({ foo: 1, bar: { baz: 2 } })
+      observed.foo = 2
+      expect(observed.foo).toBe(1)
+      expect(warn).toHaveBeenCalledTimes(1)
+      observed.bar.baz = 3
+      expect(observed.bar.baz).toBe(2)
+      expect(warn).toHaveBeenCalledTimes(2)
+    })
+
+    it('should allow mutation when unlocked', () => {
+      const observed = immutable({ foo: 1, bar: { baz: 2 } })
+      unlock()
+      observed.foo = 2
+      observed.bar.baz = 3
+      lock()
+      expect(observed.foo).toBe(2)
+      expect(observed.bar.baz).toBe(3)
+      expect(warn).not.toHaveBeenCalled()
+    })
+
+    it('should not trigger autoruns when locked', () => {
+      const observed = immutable({ a: 1 })
+      let dummy
+      autorun(() => {
+        dummy = observed.a
+      })
+      expect(dummy).toBe(1)
+      observed.a = 2
+      expect(observed.a).toBe(1)
+      expect(dummy).toBe(1)
+    })
+
+    it('should trigger autoruns when unlocked', () => {
+      const observed = immutable({ a: 1 })
+      let dummy
+      autorun(() => {
+        dummy = observed.a
+      })
+      expect(dummy).toBe(1)
+      unlock()
+      observed.a = 2
+      lock()
+      expect(observed.a).toBe(2)
+      expect(dummy).toBe(2)
+    })
+  })
+
+  describe('Array', () => {
+    it('should make nested values immutable', () => {
+      const original: any[] = [{ foo: 1 }]
+      const observed = immutable(original)
+      expect(observed).not.toBe(original)
+      expect(isObservable(observed)).toBe(true)
+      expect(isImmutable(observed)).toBe(true)
+      expect(isObservable(original)).toBe(false)
+      expect(isImmutable(original)).toBe(false)
+      expect(isObservable(observed[0])).toBe(true)
+      expect(isImmutable(observed[0])).toBe(true)
+      expect(isObservable(original[0])).toBe(false)
+      expect(isImmutable(original[0])).toBe(false)
+      // get
+      expect(observed[0].foo).toBe(1)
+      // has
+      expect(0 in observed).toBe(true)
+      // ownKeys
+      expect(Object.keys(observed)).toEqual(['0'])
+    })
+
+    it('should not allow mutation', () => {
+      const observed: any = immutable([{ foo: 1 }])
+      observed[0] = 1
+      expect(observed[0]).not.toBe(1)
+      expect(warn).toHaveBeenCalledTimes(1)
+      observed[0].foo = 2
+      expect(observed[0].foo).toBe(1)
+      expect(warn).toHaveBeenCalledTimes(2)
+
+      // should block length mutation
+      observed.length = 0
+      expect(observed.length).toBe(1)
+      expect(observed[0].foo).toBe(1)
+      expect(warn).toHaveBeenCalledTimes(3)
+
+      // mutation methods invoke set/length internally and thus are blocked as well
+      observed.push(2)
+      expect(observed.length).toBe(1)
+      // push triggers two warnings on [1] and .length
+      expect(warn).toHaveBeenCalledTimes(5)
+    })
+
+    it('should allow mutation when unlocked', () => {
+      const observed: any[] = immutable([{ foo: 1, bar: { baz: 2 } }])
+      unlock()
+      observed[1] = 2
+      observed.push(3)
+      observed[0].foo = 2
+      observed[0].bar.baz = 3
+      lock()
+      expect(observed.length).toBe(3)
+      expect(observed[1]).toBe(2)
+      expect(observed[2]).toBe(3)
+      expect(observed[0].foo).toBe(2)
+      expect(observed[0].bar.baz).toBe(3)
+      expect(warn).not.toHaveBeenCalled()
+    })
+
+    it('should not trigger autoruns when locked', () => {
+      const observed = immutable([{ a: 1 }])
+      let dummy
+      autorun(() => {
+        dummy = observed[0].a
+      })
+      expect(dummy).toBe(1)
+      observed[0].a = 2
+      expect(observed[0].a).toBe(1)
+      expect(dummy).toBe(1)
+      observed[0] = { a: 2 }
+      expect(observed[0].a).toBe(1)
+      expect(dummy).toBe(1)
+    })
+
+    it('should trigger autoruns when unlocked', () => {
+      const observed = immutable([{ a: 1 }])
+      let dummy
+      autorun(() => {
+        dummy = observed[0].a
+      })
+      expect(dummy).toBe(1)
+
+      unlock()
+
+      observed[0].a = 2
+      expect(observed[0].a).toBe(2)
+      expect(dummy).toBe(2)
+
+      observed[0] = { a: 3 }
+      expect(observed[0].a).toBe(3)
+      expect(dummy).toBe(3)
+
+      observed.unshift({ a: 4 })
+      expect(observed[0].a).toBe(4)
+      expect(dummy).toBe(4)
+      lock()
+    })
+  })
+  ;[Map, WeakMap].forEach((Collection: any) => {
+    describe(Collection.name, () => {
+      test('should make nested values immutable', () => {
+        const key1 = {}
+        const key2 = {}
+        const original = new Collection([[key1, {}], [key2, {}]])
+        const observed = immutable(original)
+        expect(observed).not.toBe(original)
+        expect(isObservable(observed)).toBe(true)
+        expect(isImmutable(observed)).toBe(true)
+        expect(isObservable(original)).toBe(false)
+        expect(isImmutable(original)).toBe(false)
+        expect(isObservable(observed.get(key1))).toBe(true)
+        expect(isImmutable(observed.get(key1))).toBe(true)
+        expect(isObservable(original.get(key1))).toBe(false)
+        expect(isImmutable(original.get(key1))).toBe(false)
+      })
+
+      test('should not allow mutation & not trigger autorun', () => {
+        const map = immutable(new Collection())
+        const key = {}
+        let dummy
+        autorun(() => {
+          dummy = map.get(key)
+        })
+        expect(dummy).toBeUndefined()
+        map.set(key, 1)
+        expect(dummy).toBeUndefined()
+        expect(map.has(key)).toBe(false)
+        expect(warn).toHaveBeenCalledTimes(1)
+      })
+
+      test('should allow mutation & trigger autorun when unlocked', () => {
+        const map = immutable(new Collection())
+        const key = {}
+        let dummy
+        autorun(() => {
+          dummy = map.get(key)
+        })
+        expect(dummy).toBeUndefined()
+        unlock()
+        map.set(key, 1)
+        lock()
+        expect(dummy).toBe(1)
+        expect(map.get(key)).toBe(1)
+        expect(warn).not.toHaveBeenCalled()
+      })
+    })
+  })
+  ;[Set, WeakSet].forEach((Collection: any) => {
+    describe(Collection.name, () => {
+      test('should make nested values immutable', () => {
+        const key1 = {}
+        const key2 = {}
+        const original = new Collection([key1, key2])
+        const observed = immutable(original)
+        expect(observed).not.toBe(original)
+        expect(isObservable(observed)).toBe(true)
+        expect(isImmutable(observed)).toBe(true)
+        expect(isObservable(original)).toBe(false)
+        expect(isImmutable(original)).toBe(false)
+        expect(observed.has(observable(key1))).toBe(true)
+        expect(original.has(observable(key1))).toBe(false)
+      })
+
+      test('should not allow mutation & not trigger autorun', () => {
+        const set = immutable(new Collection())
+        const key = {}
+        let dummy
+        autorun(() => {
+          dummy = set.has(key)
+        })
+        expect(dummy).toBe(false)
+        set.add(key)
+        expect(dummy).toBe(false)
+        expect(set.has(key)).toBe(false)
+        expect(warn).toHaveBeenCalledTimes(1)
+      })
+
+      test('should allow mutation & trigger autorun when unlocked', () => {
+        const set = immutable(new Collection())
+        const key = {}
+        let dummy
+        autorun(() => {
+          dummy = set.has(key)
+        })
+        expect(dummy).toBe(false)
+        unlock()
+        set.add(key)
+        lock()
+        expect(dummy).toBe(true)
+        expect(set.has(key)).toBe(true)
+        expect(warn).not.toHaveBeenCalled()
+      })
+    })
+  })
+
+  test('calling observable on an immutable should return immutable', () => {
+    const a = immutable()
+    const b = observable(a)
+    expect(isImmutable(b)).toBe(true)
+    // should point to same original
+    expect(unwrap(a)).toBe(unwrap(b))
+  })
+
+  test('calling immutable on an observable should return immutable', () => {
+    const a = observable()
+    const b = immutable(a)
+    expect(isImmutable(b)).toBe(true)
+    // should point to same original
+    expect(unwrap(a)).toBe(unwrap(b))
+  })
+
+  test('observing already observed value should return same Proxy', () => {
+    const original = { foo: 1 }
+    const observed = immutable(original)
+    const observed2 = immutable(observed)
+    expect(observed2).toBe(observed)
+  })
+
+  test('observing the same value multiple times should return same Proxy', () => {
+    const original = { foo: 1 }
+    const observed = immutable(original)
+    const observed2 = immutable(original)
+    expect(observed2).toBe(observed)
+  })
+
+  test('markNonReactive', () => {
+    const obj = immutable({
+      foo: { a: 1 },
+      bar: markNonReactive({ b: 2 })
+    })
+    expect(isObservable(obj.foo)).toBe(true)
+    expect(isObservable(obj.bar)).toBe(false)
+  })
+
+  test('markImmutable', () => {
+    const obj = observable({
+      foo: { a: 1 },
+      bar: markImmutable({ b: 2 })
+    })
+    expect(isObservable(obj.foo)).toBe(true)
+    expect(isObservable(obj.bar)).toBe(true)
+    expect(isImmutable(obj.foo)).toBe(false)
+    expect(isImmutable(obj.bar)).toBe(true)
+  })
+})
index b4a1cb6cb6c77f5e310ae747ab60d48b3143c3a1..c0c9aa826e71ff356953702b2811ee8b34b22a69 100644 (file)
@@ -116,7 +116,9 @@ export function trigger(
     })
   } else {
     // schedule runs for SET | ADD | DELETE
-    addRunners(runners, depsMap.get(key as string | symbol))
+    if (key !== void 0) {
+      addRunners(runners, depsMap.get(key as string | symbol))
+    }
     // also run for iteration key on ADD | DELETE
     if (type === OperationTypes.ADD || type === OperationTypes.DELETE) {
       const iterationKey = Array.isArray(target) ? 'length' : ITERATE_KEY
index 15240ac7dd5aad412c45392a54670270ea624398..dc0010c0920e6f783f2ca8a0d12dfad1de5ae55e 100644 (file)
@@ -15,14 +15,14 @@ function get(
   target: any,
   key: string | symbol,
   receiver: any,
-  toObsevable: (t: any) => any
+  toObservable: (t: any) => any
 ) {
   const res = Reflect.get(target, key, receiver)
   if (typeof key === 'symbol' && builtInSymbols.has(key)) {
     return res
   }
   track(target, OperationTypes.GET, key)
-  return res !== null && typeof res === 'object' ? toObsevable(res) : res
+  return res !== null && typeof res === 'object' ? toObservable(res) : res
 }
 
 function set(
@@ -96,7 +96,10 @@ export const immutableHandlers: ProxyHandler<any> = {
   set(target: any, key: string | symbol, value: any, receiver: any): boolean {
     if (LOCKED) {
       if (__DEV__) {
-        console.warn(`Set operation failed: target is immutable.`, target)
+        console.warn(
+          `Set operation on key "${key as any}" failed: target is immutable.`,
+          target
+        )
       }
       return true
     } else {
@@ -107,7 +110,10 @@ export const immutableHandlers: ProxyHandler<any> = {
   deleteProperty(target: any, key: string | symbol): boolean {
     if (LOCKED) {
       if (__DEV__) {
-        console.warn(`Delete operation failed: target is immutable.`, target)
+        console.warn(
+          `Delete operation on key "${key as any}" failed: target is immutable.`,
+          target
+        )
       }
       return true
     } else {
index 113ec8a360a5022c8147bb81a23c03ec4b0c2277..bf5452661d6158624942b6a67024c3db5a2bc468 100644 (file)
@@ -1,18 +1,24 @@
-import { unwrap } from './index'
+import { unwrap, observable, immutable } from './index'
 import { track, trigger } from './autorun'
 import { OperationTypes } from './operations'
+import { LOCKED } from './lock'
 
-function makeInstrumentedMethod(method: string | symbol, type: OperationTypes) {
-  return function(...args: any[]) {
-    const target = unwrap(this)
-    const proto: any = Reflect.getPrototypeOf(target)
-    track(target, type, args[0])
-    return proto[method].apply(target, args)
-  }
+function get(target: any, key: any, toObservable: (t: any) => any): any {
+  target = unwrap(target)
+  key = unwrap(key)
+  const proto: any = Reflect.getPrototypeOf(target)
+  track(target, OperationTypes.GET, key)
+  const res = proto.get.call(target, key)
+  return res !== null && typeof res === 'object' ? toObservable(res) : res
 }
 
-const get = makeInstrumentedMethod('get', OperationTypes.GET)
-const has = makeInstrumentedMethod('has', OperationTypes.HAS)
+function has(key: any): boolean {
+  const target = unwrap(this)
+  key = unwrap(key)
+  const proto: any = Reflect.getPrototypeOf(target)
+  track(target, OperationTypes.HAS, key)
+  return proto.has.call(target, key)
+}
 
 function size(target: any) {
   target = unwrap(target)
@@ -21,115 +27,135 @@ function size(target: any) {
   return Reflect.get(proto, 'size', target)
 }
 
-function makeWarning(type: OperationTypes) {
-  return function() {
+function add(value: any) {
+  value = unwrap(value)
+  const target = unwrap(this)
+  const proto: any = Reflect.getPrototypeOf(this)
+  const hadKey = proto.has.call(target, value)
+  const result = proto.add.call(target, value)
+  if (!hadKey) {
     if (__DEV__) {
-      console.warn(
-        `${type} operation failed: target is immutable.`,
-        unwrap(this)
-      )
+      trigger(target, OperationTypes.ADD, value, { value })
+    } else {
+      trigger(target, OperationTypes.ADD, value)
     }
   }
+  return result
 }
 
-const mutableInstrumentations: any = {
-  get,
-  has,
-
-  get size() {
-    return size(this)
-  },
-
-  add(key: any) {
-    const target = unwrap(this)
-    const proto: any = Reflect.getPrototypeOf(this)
-    const hadKey = proto.has.call(target, key)
-    const result = proto.add.apply(target, arguments)
-    if (!hadKey) {
-      if (__DEV__) {
-        trigger(target, OperationTypes.ADD, key, { value: key })
+function set(key: any, value: any) {
+  value = unwrap(value)
+  const target = unwrap(this)
+  const proto: any = Reflect.getPrototypeOf(this)
+  const hadKey = proto.has.call(target, key)
+  const oldValue = proto.get.call(target, key)
+  const result = proto.set.call(target, key, value)
+  if (value !== oldValue) {
+    if (__DEV__) {
+      const extraInfo = { oldValue, newValue: value }
+      if (!hadKey) {
+        trigger(target, OperationTypes.ADD, key, extraInfo)
       } else {
+        trigger(target, OperationTypes.SET, key, extraInfo)
+      }
+    } else {
+      if (!hadKey) {
         trigger(target, OperationTypes.ADD, key)
+      } else {
+        trigger(target, OperationTypes.SET, key)
       }
     }
-    return result
-  },
+  }
+  return result
+}
 
-  set(key: any, value: any) {
-    const target = unwrap(this)
-    const proto: any = Reflect.getPrototypeOf(this)
-    const hadKey = proto.has.call(target, key)
-    const oldValue = proto.get.call(target, key)
-    const result = proto.set.apply(target, arguments)
-    if (value !== oldValue) {
-      if (__DEV__) {
-        const extraInfo = { oldValue, newValue: value }
-        if (!hadKey) {
-          trigger(target, OperationTypes.ADD, key, extraInfo)
-        } else {
-          trigger(target, OperationTypes.SET, key, extraInfo)
-        }
-      } else {
-        if (!hadKey) {
-          trigger(target, OperationTypes.ADD, key)
-        } else {
-          trigger(target, OperationTypes.SET, key)
-        }
-      }
+function deleteEntry(key: any) {
+  const target = unwrap(this)
+  const proto: any = Reflect.getPrototypeOf(this)
+  const hadKey = proto.has.call(target, key)
+  const oldValue = proto.get ? proto.get.call(target, key) : undefined
+  // forward the operation before queueing reactions
+  const result = proto.delete.call(target, key)
+  if (hadKey) {
+    if (__DEV__) {
+      trigger(target, OperationTypes.DELETE, key, { oldValue })
+    } else {
+      trigger(target, OperationTypes.DELETE, key)
     }
-    return result
-  },
+  }
+  return result
+}
 
-  delete(key: any) {
-    const target = unwrap(this)
-    const proto: any = Reflect.getPrototypeOf(this)
-    const hadKey = proto.has.call(target, key)
-    const oldValue = proto.get ? proto.get.call(target, key) : undefined
-    // forward the operation before queueing reactions
-    const result = proto.delete.apply(target, arguments)
-    if (hadKey) {
-      if (__DEV__) {
-        trigger(target, OperationTypes.DELETE, key, { oldValue })
-      } else {
-        trigger(target, OperationTypes.DELETE, key)
-      }
+function clear() {
+  const target = unwrap(this)
+  const proto: any = Reflect.getPrototypeOf(this)
+  const hadItems = target.size !== 0
+  const oldTarget = target instanceof Map ? new Map(target) : new Set(target)
+  // forward the operation before queueing reactions
+  const result = proto.clear.call(target)
+  if (hadItems) {
+    if (__DEV__) {
+      trigger(target, OperationTypes.CLEAR, void 0, { oldTarget })
+    } else {
+      trigger(target, OperationTypes.CLEAR)
     }
-    return result
-  },
+  }
+  return result
+}
 
-  clear() {
-    const target = unwrap(this)
-    const proto: any = Reflect.getPrototypeOf(this)
-    const hadItems = target.size !== 0
-    const oldTarget = target instanceof Map ? new Map(target) : new Set(target)
-    // forward the operation before queueing reactions
-    const result = proto.clear.apply(target, arguments)
-    if (hadItems) {
+function makeImmutableMethod(method: Function, type: OperationTypes): Function {
+  return function(...args: any[]) {
+    if (LOCKED) {
       if (__DEV__) {
-        trigger(target, OperationTypes.CLEAR, void 0, { oldTarget })
-      } else {
-        trigger(target, OperationTypes.CLEAR)
+        const key = args[0] ? `on key "${args[0]}"` : ``
+        console.warn(
+          `${type} operation ${key}failed: target is immutable.`,
+          unwrap(this)
+        )
       }
+      return type === OperationTypes.DELETE ? false : this
+    } else {
+      return method.apply(this, args)
     }
-    return result
   }
 }
 
-const immutableInstrumentations: any = {
-  get,
+const mutableInstrumentations: any = {
+  get(key: any) {
+    return get(this, key, observable)
+  },
+  get size() {
+    return size(this)
+  },
   has,
+  add,
+  set,
+  delete: deleteEntry,
+  clear
+}
+
+const immutableInstrumentations: any = {
+  get(key: any) {
+    return get(this, key, immutable)
+  },
   get size() {
     return size(this)
   },
-  add: makeWarning(OperationTypes.ADD),
-  set: makeWarning(OperationTypes.SET),
-  delete: makeWarning(OperationTypes.DELETE),
-  clear: makeWarning(OperationTypes.CLEAR)
+  has,
+  add: makeImmutableMethod(add, OperationTypes.ADD),
+  set: makeImmutableMethod(set, OperationTypes.SET),
+  delete: makeImmutableMethod(deleteEntry, OperationTypes.DELETE),
+  clear: makeImmutableMethod(clear, OperationTypes.CLEAR)
 }
-;['forEach', 'keys', 'values', 'entries', Symbol.iterator].forEach(key => {
-  mutableInstrumentations[key] = immutableInstrumentations[
-    key
-  ] = makeInstrumentedMethod(key, OperationTypes.ITERATE)
+;['forEach', 'keys', 'values', 'entries', Symbol.iterator].forEach(method => {
+  mutableInstrumentations[method] = immutableInstrumentations[
+    method
+  ] = function(...args: any[]) {
+    const target = unwrap(this)
+    const proto: any = Reflect.getPrototypeOf(target)
+    track(target, OperationTypes.ITERATE)
+    return proto[method].apply(target, args)
+  }
 })
 
 function getInstrumented(
index 379595398f18a14a67118f0f15683311071e03ef..ca181a56a4833b80b58c195fb1d8b7f994c3c073 100644 (file)
@@ -105,7 +105,9 @@ function createObservable(
   observed = new Proxy(target, handlers)
   toProxy.set(target, observed)
   toRaw.set(observed, target)
-  targetMap.set(target, new Map())
+  if (!targetMap.has(target)) {
+    targetMap.set(target, new Map())
+  }
   return observed
 }