Skip to content

Commit

Permalink
serializer error handler impl
Browse files Browse the repository at this point in the history
  • Loading branch information
AkifumiSato committed Sep 10, 2023
1 parent ea40332 commit 14baeb9
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 8 deletions.
36 changes: 36 additions & 0 deletions packages/location-state-core/src/stores/storage-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,24 @@ test("On `load` called with serializer, if the value of the corresponding key is
expect(store.get("foo")).toBe("dummy-result");
});

test("On `load` called with invalid serializer, the value of slice remains at its initial value.", () => {
// Arrange
const navigationKey = "current_location";
storageMock.getItem.mockReturnValueOnce(
JSON.stringify({ foo: "storage value" }),
);
const store = new StorageStore(storage, {
serialize: JSON.stringify,
deserialize: () => {
throw new Error("deserialize error");
},
});
// Act
store.load(navigationKey);
// Assert
expect(store.get("foo")).toBeUndefined();
});

test("On `load` called, all listener notified.", async () => {
// Arrange
const navigationKey = "current_location";
Expand Down Expand Up @@ -181,6 +199,24 @@ test("On `save` called with serializer, the state is saved in Storage with the p
);
});

test("On `save` called with invalid serializer, the state is not saved in Storage.", () => {
// Arrange
const currentLocationKey = "current_location";
const store = new StorageStore(storage, {
serialize: () => {
throw new Error("serialize error");
},
deserialize: JSON.parse,
});
store.load(currentLocationKey);
store.set("foo", "updated");
// Act
store.save();
// Assert
expect(store.get("foo")).toBe("updated");
expect(storageMock.setItem).not.toBeCalled();
});

test("Calling `save` with empty will remove the Storage with Location key.", () => {
// Arrange
const currentLocationKey = "current_location";
Expand Down
17 changes: 12 additions & 5 deletions packages/location-state-core/src/stores/storage-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ export class StorageStore implements Store {
this.currentKey = locationKey;
const value = this.storage?.getItem(this.createStorageKey()) ?? null;
if (value !== null) {
this.state = this.serializer.deserialize(value);
try {
this.state = this.serializer.deserialize(value);
} catch (e) {
this.state = {};
}
} else {
this.state = {};
}
Expand All @@ -74,10 +78,13 @@ export class StorageStore implements Store {
this.storage?.removeItem(this.createStorageKey());
return;
}
this.storage?.setItem(
this.createStorageKey(),
this.serializer.serialize(this.state),
);
let value: string;
try {
value = this.serializer.serialize(this.state);
} catch (e) {
return;
}
this.storage?.setItem(this.createStorageKey(), value);
}

private createStorageKey() {
Expand Down
51 changes: 49 additions & 2 deletions packages/location-state-core/src/stores/url-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ test("If params is empty, the initial value is undefined.", () => {
expect(slice).toBeUndefined();
});

test("On `set` called, store's values are updated and reflected in the URL", () => {
test("On `set` called, store's values are updated and reflected in the URL.", () => {
// Arrange
prepareLocation({
pathname: "/",
Expand All @@ -56,7 +56,7 @@ test("On `set` called, store's values are updated and reflected in the URL", ()
);
});

test("On `set` called with serializer, store's values are updated and reflected in the URL", () => {
test("On `set` called with serializer, store's values are updated and reflected in the URL.", () => {
// Arrange
prepareLocation({
pathname: "/",
Expand All @@ -78,6 +78,30 @@ test("On `set` called with serializer, store's values are updated and reflected
);
});

test("On `set` called with invalid serializer, store's values are initial value and not reflected in the URL.", () => {
// Arrange
const consoleSpy = jest.spyOn(console, "error").mockImplementation();
prepareLocation({
pathname: "/",
search: "?hoge=fuga",
});
const store = new URLStore("store-key", syncerMock, {
serialize: () => {
throw new Error("serialize error");
},
deserialize: () => ({
foo: "not-used-value",
}),
});
// Act
store.set("foo", "updated");
// Assert
expect(store.get("foo")).toBe("updated");
expect(syncerMock.updateURL).not.toHaveBeenCalled();
// post processing
consoleSpy.mockRestore();
});

test("listener is called when updating slice.", () => {
// Arrange
const store = new URLStore("store-key", syncerMock);
Expand Down Expand Up @@ -168,6 +192,29 @@ test("On `load` called with serializer, the value is obtained through serialize.
expect(store.get("foo")).toBe("dummy-result");
});

test("On `load` called with invalid serializer, the value is initial value.", () => {
// Arrange
const consoleSpy = jest.spyOn(console, "error").mockImplementation();
prepareLocation({
pathname: "/",
search: "?store-key=%7B%22foo%22%3A%22updated%22%7D",
});
const store = new URLStore("store-key", syncerMock, {
serialize: JSON.stringify,
deserialize: () => {
throw new Error("deserialize error");
},
});
// Act
store.load();
// Assert
expect(store.get("foo")).toBeUndefined();
expect(syncerMock.updateURL).toHaveBeenCalledTimes(1);
expect(syncerMock.updateURL).toHaveBeenCalledWith("http://localhost/");
// post processing
consoleSpy.mockRestore();
});

test("On `load` called, all listener notified.", async () => {
// Arrange
const store = new URLStore("store-key", syncerMock);
Expand Down
9 changes: 8 additions & 1 deletion packages/location-state-core/src/stores/url-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,14 @@ export class URLStore implements Store {
} else {
this.state[name] = value;
}
this.stateJSON = this.serializer.serialize(this.state);
try {
this.stateJSON = this.serializer.serialize(this.state);
} catch (e) {
console.error(e);
this.notify(name);
// Not reflected in URL.
return;
}
// save to url
const url = new URL(location.href);
url.searchParams.set(this.key, this.stateJSON);
Expand Down

0 comments on commit 14baeb9

Please sign in to comment.