1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Update immer to version 9.x (#3882)

This commit is contained in:
Roman 2021-10-15 18:17:08 +03:00 committed by GitHub
parent 246305cd63
commit dabfce3609
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 74 additions and 108 deletions

View File

@ -205,7 +205,7 @@
"grapheme-splitter": "^1.0.4", "grapheme-splitter": "^1.0.4",
"handlebars": "^4.7.7", "handlebars": "^4.7.7",
"http-proxy": "^1.18.1", "http-proxy": "^1.18.1",
"immer": "^8.0.4", "immer": "^9.0.6",
"joi": "^17.4.2", "joi": "^17.4.2",
"js-yaml": "^3.14.0", "js-yaml": "^3.14.0",
"jsdom": "^16.7.0", "jsdom": "^16.7.0",

View File

@ -19,149 +19,109 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
import { reaction } from "mobx"; import { observable, reaction } from "mobx";
import { StorageAdapter, StorageHelper } from "../storageHelper"; import { StorageHelper } from "../storageHelper";
import { delay } from "../../../common/utils/delay"; import { delay } from "../../../common/utils/delay";
describe("renderer/utils/StorageHelper", () => { type StorageModel = {
describe("window.localStorage might be used as StorageAdapter", () => { [prop: string]: any /*json-serializable*/;
type StorageModel = string; message?: string;
description?: any;
};
describe("renderer/utils/StorageHelper", () => {
describe("Using custom StorageAdapter", () => {
const storageKey = "ui-settings"; const storageKey = "ui-settings";
const remoteStorageMock = observable.map<string, StorageModel>();
let storageHelper: StorageHelper<StorageModel>; let storageHelper: StorageHelper<StorageModel>;
let storageHelperAsync: StorageHelper<StorageModel>;
beforeEach(() => { beforeEach(() => {
localStorage.clear(); remoteStorageMock.set(storageKey, {
message: "saved-before", // pretending as previously saved data
});
storageHelper = new StorageHelper<StorageModel>(storageKey, { storageHelper = new StorageHelper<StorageModel>(storageKey, {
autoInit: false, autoInit: false,
storage: localStorage, defaultValue: {
defaultValue: "test", message: "blabla",
}); description: "default"
}); },
storage: {
it("initialized with default value", async () => { getItem(key: string): StorageModel {
localStorage.setItem(storageKey, "saved"); // pretending it was saved previously return Object.assign(
storageHelper.defaultValue,
expect(storageHelper.key).toBe(storageKey); remoteStorageMock.get(key),
expect(storageHelper.defaultValue).toBe("test"); );
expect(storageHelper.get()).toBe("test"); },
setItem(key: string, value: StorageModel) {
storageHelper.init(); remoteStorageMock.set(key, value);
},
expect(storageHelper.key).toBe(storageKey); removeItem(key: string) {
expect(storageHelper.defaultValue).toBe("test"); remoteStorageMock.delete(key);
expect(storageHelper.get()).toBe("saved"); }
}); },
it("updates storage", async () => {
storageHelper.init();
storageHelper.set("test2");
expect(localStorage.getItem(storageKey)).toBe("test2");
localStorage.setItem(storageKey, "test3");
storageHelper.init({ force: true }); // reload from underlying storage and merge
expect(storageHelper.get()).toBe("test3");
});
});
describe("Using custom StorageAdapter", () => {
type SettingsStorageModel = {
[key: string]: any;
message: string;
};
const storageKey = "mySettings";
const storageMock: Record<string, any> = {};
let storageHelper: StorageHelper<SettingsStorageModel>;
let storageHelperAsync: StorageHelper<SettingsStorageModel>;
let storageAdapter: StorageAdapter<SettingsStorageModel>;
const storageHelperDefaultValue: SettingsStorageModel = {
message: "hello-world",
anyOtherStorableData: 123,
};
beforeEach(() => {
storageMock[storageKey] = {
message: "saved-before",
} as SettingsStorageModel;
storageAdapter = {
onChange: jest.fn(),
getItem: jest.fn((key: string) => {
return storageMock[key];
}),
setItem: jest.fn((key: string, value: any) => {
storageMock[key] = value;
}),
removeItem: jest.fn((key: string) => {
delete storageMock[key];
}),
};
storageHelper = new StorageHelper(storageKey, {
autoInit: false,
defaultValue: storageHelperDefaultValue,
storage: storageAdapter,
}); });
storageHelperAsync = new StorageHelper(storageKey, { storageHelperAsync = new StorageHelper(storageKey, {
autoInit: false, autoInit: false,
defaultValue: storageHelperDefaultValue, defaultValue: storageHelper.defaultValue,
storage: { storage: {
...storageAdapter, ...storageHelper.storage,
async getItem(key: string): Promise<SettingsStorageModel> { async getItem(key: string): Promise<StorageModel> {
await delay(500); // fake loading timeout await delay(500); // fake loading timeout
return storageAdapter.getItem(key); return storageHelper.storage.getItem(key);
} }
}, },
}); });
}); });
it("loads data from storage with fallback to default-value", () => { it("initialized with default value", async () => {
expect(storageHelper.get()).toEqual(storageHelperDefaultValue);
storageHelper.init(); storageHelper.init();
expect(storageHelper.key).toBe(storageKey);
expect(storageHelper.get().message).toBe("saved-before"); expect(storageHelper.get()).toEqual(storageHelper.defaultValue);
expect(storageAdapter.getItem).toHaveBeenCalledWith(storageHelper.key);
}); });
it("async loading from storage supported too", async () => { it("async loading from storage supported too", async () => {
expect(storageHelperAsync.initialized).toBeFalsy(); expect(storageHelperAsync.initialized).toBeFalsy();
storageHelperAsync.init(); storageHelperAsync.init();
await delay(300); await delay(300);
expect(storageHelperAsync.get()).toEqual(storageHelperDefaultValue); expect(storageHelperAsync.get()).toEqual(storageHelper.defaultValue);
await delay(200); await delay(200);
expect(storageHelperAsync.get().message).toBe("saved-before"); expect(storageHelperAsync.get().message).toBe("saved-before");
}); });
it("set() fully replaces data in storage", () => { it("set() fully replaces data in storage", () => {
storageHelper.init(); storageHelper.init();
storageHelper.set({ message: "test2" }); storageHelper.set({ message: "msg" });
expect(storageHelper.get().message).toBe("test2"); storageHelper.get().description = "desc";
expect(storageMock[storageKey]).toEqual({ message: "test2" }); expect(storageHelper.get().message).toBe("msg");
expect(storageAdapter.setItem).toHaveBeenCalledWith(storageHelper.key, { message: "test2" }); expect(storageHelper.get().description).toBe("desc");
expect(remoteStorageMock.get(storageKey)).toEqual({
message: "msg",
description: "desc",
} as StorageModel);
}); });
it("merge() does partial data tree updates", () => { it("merge() does partial data tree updates", () => {
storageHelper.init(); storageHelper.init();
storageHelper.merge({ message: "updated" }); storageHelper.merge({ message: "updated" });
expect(storageHelper.get()).toEqual({ ...storageHelperDefaultValue, message: "updated" }); expect(storageHelper.get()).toEqual({ ...storageHelper.defaultValue, message: "updated" });
expect(storageAdapter.setItem).toHaveBeenCalledWith(storageHelper.key, { ...storageHelperDefaultValue, message: "updated" }); expect(remoteStorageMock.get(storageKey)).toEqual({ ...storageHelper.defaultValue, message: "updated" });
// `draft` modified inside, returning `void` is expected
storageHelper.merge(draft => { storageHelper.merge(draft => {
draft.message = "updated2"; draft.message = "updated2";
}); });
expect(storageHelper.get()).toEqual({ ...storageHelperDefaultValue, message: "updated2" }); expect(storageHelper.get()).toEqual({ ...storageHelper.defaultValue, message: "updated2" });
// returning object modifies `draft` as well
storageHelper.merge(draft => ({ storageHelper.merge(draft => ({
message: draft.message.replace("2", "3") message: draft.message.replace("2", "3")
})); }));
expect(storageHelper.get()).toEqual({ ...storageHelperDefaultValue, message: "updated3" }); expect(storageHelper.get()).toEqual({ ...storageHelper.defaultValue, message: "updated3" });
}); });
}); });

View File

@ -20,10 +20,9 @@
*/ */
// Helper for working with storages (e.g. window.localStorage, NodeJS/file-system, etc.) // Helper for working with storages (e.g. window.localStorage, NodeJS/file-system, etc.)
import { action, comparer, makeObservable, observable, toJS, when, } from "mobx"; import { action, comparer, makeObservable, observable, toJS, when, } from "mobx";
import produce, { Draft } from "immer"; import produce, { Draft, isDraft } from "immer";
import { isEqual, isFunction, isPlainObject } from "lodash"; import { isEqual } from "lodash";
import logger from "../../main/logger"; import logger from "../../main/logger";
export interface StorageAdapter<T> { export interface StorageAdapter<T> {
@ -150,15 +149,22 @@ export class StorageHelper<T> {
@action @action
merge(value: Partial<T> | ((draft: Draft<T>) => Partial<T> | void)) { merge(value: Partial<T> | ((draft: Draft<T>) => Partial<T> | void)) {
const nextValue = produce(this.toJSON(), (state: Draft<T>) => { const nextValue = produce<T>(this.toJSON(), (draft: Draft<T>) => {
const newValue = isFunction(value) ? value(state) : value;
return isPlainObject(newValue) if (typeof value == "function") {
? Object.assign(state, newValue) // partial updates for returned plain objects const newValue = value(draft);
: newValue;
// merge returned plain objects from `value-as-callback` usage
// otherwise `draft` can be just modified inside a callback without returning any value (void)
if (newValue && !isDraft(newValue)) {
Object.assign(draft, newValue);
}
} else {
Object.assign(draft, value);
}
}); });
this.set(nextValue as T); this.set(nextValue);
} }
toJSON(): T { toJSON(): T {

View File

@ -7278,10 +7278,10 @@ immediate@~3.0.5:
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps= integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=
immer@^8.0.4: immer@^9.0.6:
version "8.0.4" version "9.0.6"
resolved "https://registry.yarnpkg.com/immer/-/immer-8.0.4.tgz#3a21605a4e2dded852fb2afd208ad50969737b7a" resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.6.tgz#7a96bf2674d06c8143e327cbf73539388ddf1a73"
integrity sha512-jMfL18P+/6P6epANRvRk6q8t+3gGhqsJ9EuJ25AXE+9bNTYtssvzeYbEd0mXRYWCmmXSIbnlpz6vd6iJlmGGGQ== integrity sha512-G95ivKpy+EvVAnAab4fVa4YGYn24J1SpEktnJX7JJ45Bd7xqME/SCplFzYFmTbrkwZbQ4xJK1xMTUYBkN6pWsQ==
import-cwd@^3.0.0: import-cwd@^3.0.0:
version "3.0.0" version "3.0.0"