mirror of
https://github.com/zhigang1992/mobx-utils.git
synced 2026-01-13 17:22:21 +08:00
135 lines
3.1 KiB
TypeScript
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")
|
|
})
|
|
})
|