Files
mobx-utils/test/deepObserve.ts
2020-04-10 14:23:14 +02:00

135 lines
3.1 KiB
TypeScript

import { deepObserve } from "../src/mobx-utils"
import { observable, $mobx, IObjectDidChange } from "mobx"
import * as cloneDeepWith from "lodash.clonedeepwith"
function cleanChange(change, includeObject = true) {
return cloneDeepWith(change, (value, key) => {
if (key === $mobx) return null
if (key === "object" && !includeObject) return null
})
}
function assertChanges<T>(x: T, fn: (x: T) => void) {
const target = observable(x)
const events: any[] = []
const d = deepObserve(target, (change, path) => {
events.push([path, cleanChange(change, false)])
})
fn(target)
expect(events).toMatchSnapshot()
}
test("not throwing on primitive value changes", () => {
// deepObserve uses a WeakMap to track what is being observed.
// WeakMaps can only contain objects as their keys, not primitive values.
// Certain JS runtimes throw when passing a non-object to #get, #set or #has.
// deepObserve should not throw on primitive value changes, but still observe them.
expect(() => {
const x = observable({ a: 1 })
const d = deepObserve(x, (change: any) => {
expect(change.oldValue).toBe(1)
expect(change.newValue).toBe(2)
})
x.a = 2
}).not.toThrow()
})
test("basic & dispose", () => {
const x = observable({ a: 1, b: { z: 3 } })
const events: any[] = []
const d = deepObserve(x, (change, path) => {
events.push([path, cleanChange(change)])
})
x.a = 2
x.b.z = 4
d()
x.a = 3
x.b.z = 5
expect(events).toMatchSnapshot()
})
test("deep", () => {
assertChanges(
{
a: {
b: {
c: 3,
},
},
},
(x) => {
x.a.b.c = 4
}
)
})
test("add", () => {
assertChanges({}, (x: any) => {
x.a = 3
})
})
test("delete", () => {
assertChanges({ x: 1 }, (x) => {
delete x.x
})
})
test("cleanup", () => {
const a = observable({ b: 1 })
const x = observable({ a })
const events: any[] = []
const d = deepObserve(x, (change, path) => {
events.push([path, cleanChange(change)])
})
a.b = 2
delete x.a
a.b = 3 // should not be visible
expect(events).toMatchSnapshot()
})
test("throw on double entry", () => {
const a = observable({ b: 1 })
const x = observable({ a })
const events: any[] = []
const d = deepObserve(x, (change, path) => {
events.push([path, cleanChange(change)])
})
expect(() => {
;(x as any).b = a
}).toThrow("trying to assign it to '/b', but it already exists at '/a'")
})
test("array", () => {
assertChanges([1, 2, { x: 3 }], (ar: any) => {
ar.splice(1, 1, { x: 1 }, { x: 2 })
ar[1].x = "a"
ar[2].x = "b"
ar[3].x = "c"
ar.splice(0, 3)
ar.push({ x: "B" })
ar[0].x = "A"
})
})
test("map", () => {
assertChanges({}, (o: any) => {
const x = observable.map({})
o.x = x
x.set("a", { a: 1 })
x.get("a").a = 2
x.set("a", 3)
x.delete("a")
})
})