-
-
Notifications
You must be signed in to change notification settings - Fork 852
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test: hooks (onAssign, onDelete, onCopy)
- Loading branch information
1 parent
7f50364
commit ab05744
Showing
2 changed files
with
371 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`hooks - onAssign() when draft is an array assign 1`] = ` | ||
Array [ | ||
Array [ | ||
0, | ||
0, | ||
], | ||
] | ||
`; | ||
|
||
exports[`hooks - onAssign() when draft is an array push 1`] = ` | ||
Array [ | ||
Array [ | ||
0, | ||
4, | ||
], | ||
] | ||
`; | ||
|
||
exports[`hooks - onAssign() when draft is an array splice (length += 0) 1`] = ` | ||
Array [ | ||
Array [ | ||
1, | ||
0, | ||
], | ||
] | ||
`; | ||
|
||
exports[`hooks - onAssign() when draft is an array splice (length += 1) 1`] = ` | ||
Array [ | ||
Array [ | ||
1, | ||
0, | ||
], | ||
Array [ | ||
2, | ||
0, | ||
], | ||
Array [ | ||
3, | ||
3, | ||
], | ||
] | ||
`; | ||
|
||
exports[`hooks - onAssign() when draft is an array splice (length -= 1) 1`] = ` | ||
Array [ | ||
Array [ | ||
0, | ||
6, | ||
], | ||
Array [ | ||
1, | ||
3, | ||
], | ||
] | ||
`; | ||
|
||
exports[`hooks - onAssign() when draft is an array unshift 1`] = ` | ||
Array [ | ||
Array [ | ||
0, | ||
0, | ||
], | ||
Array [ | ||
1, | ||
1, | ||
], | ||
] | ||
`; | ||
|
||
exports[`hooks - onAssign() when draft is an object assign 1`] = ` | ||
Array [ | ||
Array [ | ||
"a", | ||
1, | ||
], | ||
Array [ | ||
"c", | ||
1, | ||
], | ||
] | ||
`; | ||
|
||
exports[`hooks - onAssign() when draft is an object nested assignments 1`] = ` | ||
Array [ | ||
Array [ | ||
"c", | ||
2, | ||
], | ||
Array [ | ||
"b", | ||
Object { | ||
"c": 2, | ||
"e": 1, | ||
}, | ||
], | ||
Array [ | ||
"a", | ||
Object { | ||
"b": Object { | ||
"c": 2, | ||
"e": 1, | ||
}, | ||
}, | ||
], | ||
] | ||
`; | ||
|
||
exports[`hooks - onDelete() when draft is an array - length = 0 1`] = `Array []`; | ||
|
||
exports[`hooks - onDelete() when draft is an array - pop 1`] = ` | ||
Array [ | ||
Array [ | ||
"0", | ||
], | ||
] | ||
`; | ||
|
||
exports[`hooks - onDelete() when draft is an array - splice (length -= 1) 1`] = ` | ||
Array [ | ||
Array [ | ||
"2", | ||
], | ||
] | ||
`; | ||
|
||
exports[`hooks - onDelete() when draft is an object - delete 1`] = ` | ||
Array [ | ||
Array [ | ||
"a", | ||
], | ||
Array [ | ||
"c", | ||
], | ||
] | ||
`; | ||
|
||
exports[`hooks - onDelete() when draft is an object - nested deletions 1`] = ` | ||
Array [ | ||
Array [ | ||
"c", | ||
], | ||
] | ||
`; |
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,225 @@ | ||
"use strict" | ||
import {Immer} from "../src/index" | ||
import matchers from "expect/build/matchers" | ||
|
||
describe("hooks -", () => { | ||
let produce, onAssign, onDelete, onCopy | ||
|
||
const reset = () => | ||
({produce, onAssign, onDelete, onCopy} = new Immer({ | ||
autoFreeze: true, | ||
onAssign: defuseProxies(jest.fn().mockName("onAssign")), | ||
onDelete: defuseProxies(jest.fn().mockName("onDelete")), | ||
onCopy: defuseProxies(jest.fn().mockName("onCopy")) | ||
})) | ||
|
||
describe("onAssign()", () => { | ||
beforeEach(reset) | ||
useSharedTests(() => onAssign) | ||
describe("when draft is an object", () => { | ||
test("assign", () => { | ||
produce({a: 0, b: 0, c: 0}, s => { | ||
s.a++ | ||
s.c++ | ||
}) | ||
expectCalls(onAssign) | ||
}) | ||
test("assign (no change)", () => { | ||
produce({a: 0}, s => { | ||
s.a = 0 | ||
}) | ||
expect(onAssign).not.toBeCalled() | ||
}) | ||
test("delete", () => { | ||
produce({a: 1}, s => { | ||
delete s.a | ||
}) | ||
expect(onAssign).not.toBeCalled() | ||
}) | ||
test("nested assignments", () => { | ||
produce({a: {b: {c: 1, d: 1, e: 1}}}, s => { | ||
const {b} = s.a | ||
b.c = 2 | ||
delete b.d | ||
b.e = 1 // no-op | ||
}) | ||
expectCalls(onAssign) | ||
}) | ||
}) | ||
describe("when draft is an array", () => { | ||
test("assign", () => { | ||
produce([1], s => { | ||
s[0] = 0 | ||
}) | ||
expectCalls(onAssign) | ||
}) | ||
test("push", () => { | ||
produce([], s => { | ||
s.push(4) | ||
}) | ||
expectCalls(onAssign) | ||
}) | ||
test("pop", () => { | ||
produce([1], s => { | ||
s.pop() | ||
}) | ||
expect(onAssign).not.toBeCalled() | ||
}) | ||
test("unshift", () => { | ||
produce([1], s => { | ||
s.unshift(0) | ||
}) | ||
expectCalls(onAssign) | ||
}) | ||
test("length = 0", () => { | ||
produce([1], s => { | ||
s.length = 0 | ||
}) | ||
expect(onAssign).not.toBeCalled() | ||
}) | ||
test("splice (length += 1)", () => { | ||
produce([1, 2, 3], s => { | ||
s.splice(1, 1, 0, 0) | ||
}) | ||
expectCalls(onAssign) | ||
}) | ||
test("splice (length += 0)", () => { | ||
produce([1, 2, 3], s => { | ||
s.splice(1, 1, 0) | ||
}) | ||
expectCalls(onAssign) | ||
}) | ||
test("splice (length -= 1)", () => { | ||
produce([1, 2, 3], s => { | ||
s.splice(0, 2, 6) | ||
}) | ||
expectCalls(onAssign) | ||
}) | ||
}) | ||
describe("when a draft is moved into a new object", () => { | ||
it("is called in the right order", () => { | ||
const calls = [] | ||
onAssign.mockImplementation((_, prop) => { | ||
calls.push(prop) | ||
}) | ||
produce({a: {b: 1, c: {}}}, s => { | ||
s.a.b = 0 | ||
s.a.c.d = 1 | ||
s.x = {y: {z: s.a}} | ||
delete s.a | ||
}) | ||
// Sibling properties use enumeration order, which means new | ||
// properties come last among their siblings. The deepest | ||
// properties always come first in their ancestor chain. | ||
expect(calls).toEqual(["b", "d", "c", "x"]) | ||
}) | ||
}) | ||
}) | ||
|
||
describe("onDelete()", () => { | ||
beforeEach(reset) | ||
useSharedTests(() => onDelete) | ||
describe("when draft is an object -", () => { | ||
test("delete", () => { | ||
produce({a: 1, b: 1, c: 1}, s => { | ||
delete s.a | ||
delete s.c | ||
}) | ||
expectCalls(onDelete) | ||
}) | ||
test("delete (no change)", () => { | ||
produce({}, s => { | ||
delete s.a | ||
}) | ||
expect(onDelete).not.toBeCalled() | ||
}) | ||
test("nested deletions", () => { | ||
produce({a: {b: {c: 1}}}, s => { | ||
delete s.a.b.c | ||
}) | ||
expectCalls(onDelete) | ||
}) | ||
}) | ||
describe("when draft is an array -", () => { | ||
test("pop", () => { | ||
produce([1], s => { | ||
s.pop() | ||
}) | ||
expectCalls(onDelete) | ||
}) | ||
test("length = 0", () => { | ||
produce([1], s => { | ||
s.length = 0 | ||
}) | ||
expectCalls(onDelete) | ||
}) | ||
test("splice (length -= 1)", () => { | ||
produce([1, 2, 3], s => { | ||
s.splice(0, 2, 6) | ||
}) | ||
expectCalls(onDelete) | ||
}) | ||
}) | ||
}) | ||
|
||
describe("onCopy()", () => { | ||
beforeEach(reset) | ||
useSharedTests(() => onCopy) | ||
it("is called in the right order", () => { | ||
const calls = [] | ||
onCopy.mockImplementation(s => { | ||
calls.push(s.base) | ||
}) | ||
const base = {a: {b: {c: 1}}} | ||
produce(base, s => { | ||
delete s.a.b.c | ||
}) | ||
expect(calls).toShallowEqual([base.a.b, base.a, base]) | ||
}) | ||
}) | ||
|
||
function useSharedTests(getHook) { | ||
it("is called before the parent is frozen", () => { | ||
const hook = getHook() | ||
hook.mockImplementation(s => { | ||
// Parent object must not be frozen. | ||
expect(Object.isFrozen(s.base)).toBeFalsy() | ||
}) | ||
produce({a: {b: {c: 0}}}, s => { | ||
if (hook == onDelete) delete s.a.b.c | ||
else s.a.b.c = 1 | ||
}) | ||
expect(hook).toHaveBeenCalledTimes(hook == onDelete ? 1 : 3) | ||
}) | ||
} | ||
}) | ||
|
||
// Produce a snapshot of the hook arguments (minus any draft state). | ||
function expectCalls(hook) { | ||
expect( | ||
hook.mock.calls.map(call => { | ||
return call.slice(1) | ||
}) | ||
).toMatchSnapshot() | ||
} | ||
|
||
// For defusing draft proxies. | ||
function defuseProxies(fn) { | ||
return Object.assign((...args) => { | ||
expect(args[0].finalized).toBeTruthy() | ||
args[0].draft = args[0].drafts = null | ||
fn(...args) | ||
}, fn) | ||
} | ||
|
||
expect.extend({ | ||
toShallowEqual(received, expected) { | ||
const match = matchers.toBe(received, expected) | ||
return match.pass || !received || typeof received !== "object" | ||
? match | ||
: !Array.isArray(expected) || | ||
(Array.isArray(received) && received.length === expected.length) | ||
? matchers.toEqual(received, expected) | ||
: match | ||
} | ||
}) |