Skip to content

Commit

Permalink
✅ test: Chagne list now returns 'none' changes
Browse files Browse the repository at this point in the history
  • Loading branch information
joebobmiles committed Jul 12, 2021
1 parent ce2bdb0 commit 9ec6128
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 40 deletions.
46 changes: 40 additions & 6 deletions src/patching.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import * as Y from "yjs";
import create from "zustand/vanilla";
import { arrayToYArray, objectToYMap, } from "./mapping";
import { getChangeList, patchSharedType, patchStore, } from "./patching";
import {
Change,
getChangeList,
patchSharedType,
patchStore,
} from "./patching";

describe("getChangeList", () =>
{
Expand All @@ -28,7 +33,7 @@ describe("getChangeList", () =>
"Should create an add entry when b contains a new item. (#%#)",
(a, b, change) =>
{
expect(getChangeList(a, b)).toEqual([ change ]);
expect(getChangeList(a, b)).toContainEqual(change);
}
);

Expand All @@ -40,16 +45,19 @@ describe("getChangeList", () =>

it("Should create an add and delete entry when an array changes.", () =>
{
expect(getChangeList( [ 1 ], [ 2 ]))
.toEqual([ [ "delete", 0, undefined ], [ "add", 0, 2 ] ]);
expect(getChangeList([ 1 ], [ 2 ]))
.toContainEqual([ "delete", 0, undefined ]);

expect(getChangeList([ 1 ], [ 2 ]))
.toContainEqual([ "add", 0, 2 ]);
});

it(
"Should create a delete entry when b is missing a value.",
() =>
{
expect(getChangeList({ "foo": 1, }, {}))
.toEqual([ [ "delete", "foo", undefined ] ]);
.toContainEqual([ "delete", "foo", undefined ]);
}
);

Expand All @@ -74,9 +82,17 @@ describe("getChangeList", () =>
(a, b, change) =>
{
expect(getChangeList(a, b))
.toEqual([ change ]);
.toContainEqual(change);
}
);

it("Should create a 'none' change when there are no changes.", () =>
{
const a = { "foo": 1, "bar": 2, };
const b = { "foo": 1, "bar": 3, };

expect(getChangeList(a, b)).toContainEqual<Change>([ "none", "foo", 1 ]);
});
});

describe("patchSharedType", () =>
Expand Down Expand Up @@ -301,4 +317,22 @@ describe("patchStore", () =>

expect(store.getState().foo).toBeUndefined();
});

it("Applies additions to nested objects.", () =>
{
const store = create(() =>
({
"foo": { },
}));

const update = {
"foo": {
"bar": 1,
},
};

patchStore(store, update);

expect((store.getState().foo as { "bar": number, }).bar).toBe(1);
});
});
91 changes: 57 additions & 34 deletions src/patching.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { arrayToYArray, objectToYMap, } from "./mapping";
import { State, StoreApi, } from "zustand/vanilla";

export type Change = [
"add" | "update" | "delete" | "pending",
"add" | "update" | "delete" | "pending" | "none",
string | number,
any
];
Expand Down Expand Up @@ -46,20 +46,24 @@ export const getChangeList = (a: any, b: any): Change[] =>
}
else if (delta instanceof Object)
{
(Object.entries(delta) as [ string, any ]).forEach(([ property, value ]) =>
{
if (property.match(/__added$/))
changes.push([ "add", property.replace(/__added$/, ""), value ]);
(Object.entries({ ...a, ...delta, }) as [ string, any ])
.forEach(([ property, value ]) =>
{
if (property.match(/__added$/))
changes.push([ "add", property.replace(/__added$/, ""), value ]);

else if (property.match(/__deleted$/))
changes.push([ "delete", property.replace(/__deleted$/, ""), undefined ]);
else if (property.match(/__deleted$/))
changes.push([ "delete", property.replace(/__deleted$/, ""), undefined ]);

else if (value.__old !== undefined && value.__new !== undefined)
changes.push([ "update", property, value.__new ]);
else if (value.__old !== undefined && value.__new !== undefined)
changes.push([ "update", property, value.__new ]);

else if (value instanceof Object)
changes.push([ "pending", property, undefined ]);
});
else if (value instanceof Object)
changes.push([ "pending", property, undefined ]);

else
changes.push([ "none", property, value ]);
});
}

return changes;
Expand Down Expand Up @@ -144,30 +148,49 @@ export const patchStore = <S extends State>(
newState: any
): void =>
{
const changes = getChangeList(store.getState(), newState);

changes.forEach(([ type, property, value ]) =>
const patch = (oldState: any, newState: any): any =>
{
switch (type)
{
case "add":
case "update":
{
store.setState((state) =>
({
...state,
[property]: value,
}));
} break;
const changes = getChangeList(oldState, newState);

case "delete":
changes.forEach(([ type, property, value ]) =>
{
switch (type)
{
store.setState((state) =>
case "add":
case "update":
{
delete (state as any)[property as string];
return state;
});
} break;
}
});
store.setState((state) =>
({
...state,
[property]: value,
}));
} break;

case "delete":
{
store.setState((state) =>
{
delete (state as any)[property as string];
return state;
});
} break;

case "pending":
{
store.setState((state) =>
({
...state,
[property]: patch(
(store.getState() as any)[property as string],
newState[property as string]
),
}));
} break;

default:
}
});
};

patch(store.getState(), newState);
};

0 comments on commit 9ec6128

Please sign in to comment.