mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-28 21:32:05 +00:00
* Migrate copilot persistence to AppState * Migrate persistence of toggle and history to new infra * Save toggle value as boolean * Fix compile bug * Fix unit tests
201 lines
6.7 KiB
TypeScript
201 lines
6.7 KiB
TypeScript
import {
|
|
AppStateComponentNames,
|
|
createKeyFromPath,
|
|
deleteState,
|
|
loadState,
|
|
MAX_ENTRY_NB,
|
|
PATH_SEPARATOR,
|
|
saveState,
|
|
} from "Shared/AppStatePersistenceUtility";
|
|
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
|
|
|
jest.mock("Shared/StorageUtility", () => ({
|
|
LocalStorageUtility: {
|
|
getEntryObject: jest.fn(),
|
|
setEntryObject: jest.fn(),
|
|
},
|
|
StorageKey: {
|
|
AppState: "AppState",
|
|
},
|
|
}));
|
|
|
|
describe("AppStatePersistenceUtility", () => {
|
|
const storePath = {
|
|
componentName: AppStateComponentNames.DocumentsTab,
|
|
subComponentName: "b",
|
|
globalAccountName: "c",
|
|
databaseName: "d",
|
|
containerName: "e",
|
|
};
|
|
const key = createKeyFromPath(storePath);
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
beforeEach(() => {
|
|
(LocalStorageUtility.getEntryObject as jest.Mock).mockReturnValue({
|
|
key0: {
|
|
schemaVersion: 1,
|
|
timestamp: 0,
|
|
data: {},
|
|
},
|
|
});
|
|
});
|
|
|
|
describe("saveState()", () => {
|
|
const testState = { aa: 1, bb: "2", cc: [3, 4] };
|
|
|
|
it("should save state", () => {
|
|
saveState(storePath, testState);
|
|
expect(LocalStorageUtility.setEntryObject).toHaveBeenCalledTimes(1);
|
|
expect(LocalStorageUtility.setEntryObject).toHaveBeenCalledWith(StorageKey.AppState, expect.any(Object));
|
|
|
|
const passedState = (LocalStorageUtility.setEntryObject as jest.Mock).mock.calls[0][1];
|
|
expect(passedState[key].data).toHaveProperty("aa", 1);
|
|
});
|
|
|
|
it("should save state with timestamp", () => {
|
|
saveState(storePath, testState);
|
|
const passedState = (LocalStorageUtility.setEntryObject as jest.Mock).mock.calls[0][1];
|
|
expect(passedState[key]).toHaveProperty("timestamp");
|
|
expect(passedState[key].timestamp).toBeGreaterThan(0);
|
|
});
|
|
|
|
it("should add state to existing state", () => {
|
|
(LocalStorageUtility.getEntryObject as jest.Mock).mockReturnValue({
|
|
key0: {
|
|
schemaVersion: 1,
|
|
timestamp: 0,
|
|
data: { dd: 5 },
|
|
},
|
|
});
|
|
|
|
saveState(storePath, testState);
|
|
const passedState = (LocalStorageUtility.setEntryObject as jest.Mock).mock.calls[0][1];
|
|
expect(passedState["key0"].data).toHaveProperty("dd", 5);
|
|
});
|
|
|
|
it("should remove the oldest entry when the number of entries exceeds the limit", () => {
|
|
// Fill up storage with MAX entries
|
|
const currentAppState = {};
|
|
for (let i = 0; i < MAX_ENTRY_NB; i++) {
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
(currentAppState as any)[`key${i}`] = {
|
|
schemaVersion: 1,
|
|
timestamp: i,
|
|
data: {},
|
|
};
|
|
}
|
|
(LocalStorageUtility.getEntryObject as jest.Mock).mockReturnValue(currentAppState);
|
|
|
|
saveState(storePath, testState);
|
|
|
|
// Verify that the new entry is saved
|
|
const passedState = (LocalStorageUtility.setEntryObject as jest.Mock).mock.calls[0][1];
|
|
expect(passedState[key].data).toHaveProperty("aa", 1);
|
|
|
|
// Verify that the oldest entry is removed (smallest timestamp)
|
|
const passedAppState = (LocalStorageUtility.setEntryObject as jest.Mock).mock.calls[0][1];
|
|
expect(Object.keys(passedAppState).length).toBe(MAX_ENTRY_NB);
|
|
expect(passedAppState).not.toHaveProperty("key0");
|
|
});
|
|
|
|
it("should not remove the oldest entry when the number of entries does not exceed the limit", () => {
|
|
(LocalStorageUtility.getEntryObject as jest.Mock).mockReturnValue({
|
|
key0: {
|
|
schemaVersion: 1,
|
|
timestamp: 0,
|
|
data: {},
|
|
},
|
|
key1: {
|
|
schemaVersion: 1,
|
|
timestamp: 1,
|
|
data: {},
|
|
},
|
|
});
|
|
saveState(storePath, testState);
|
|
const passedAppState = (LocalStorageUtility.setEntryObject as jest.Mock).mock.calls[0][1];
|
|
expect(Object.keys(passedAppState).length).toBe(3);
|
|
});
|
|
});
|
|
|
|
describe("loadState()", () => {
|
|
it("should load state", () => {
|
|
const data = { aa: 1, bb: "2", cc: [3, 4] };
|
|
const testState = {
|
|
[key]: {
|
|
schemaVersion: 1,
|
|
timestamp: 0,
|
|
data,
|
|
},
|
|
};
|
|
(LocalStorageUtility.getEntryObject as jest.Mock).mockReturnValue(testState);
|
|
const state = loadState(storePath);
|
|
expect(state).toEqual(data);
|
|
});
|
|
|
|
it("should return undefined if the state is not found", () => {
|
|
(LocalStorageUtility.getEntryObject as jest.Mock).mockReturnValue(null);
|
|
const state = loadState(storePath);
|
|
expect(state).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe("deleteState()", () => {
|
|
it("should delete state", () => {
|
|
const key = createKeyFromPath(storePath);
|
|
(LocalStorageUtility.getEntryObject as jest.Mock).mockReturnValue({
|
|
[key]: {
|
|
schemaVersion: 1,
|
|
timestamp: 0,
|
|
data: {},
|
|
},
|
|
otherKey: {
|
|
schemaVersion: 2,
|
|
timestamp: 0,
|
|
data: {},
|
|
},
|
|
});
|
|
|
|
deleteState(storePath);
|
|
expect(LocalStorageUtility.setEntryObject).toHaveBeenCalledTimes(1);
|
|
const passedAppState = (LocalStorageUtility.setEntryObject as jest.Mock).mock.calls[0][1];
|
|
expect(passedAppState).not.toHaveProperty(key);
|
|
expect(passedAppState).toHaveProperty("otherKey");
|
|
});
|
|
});
|
|
describe("createKeyFromPath()", () => {
|
|
it("should create path that contains all components", () => {
|
|
const key = createKeyFromPath(storePath);
|
|
expect(key).toContain(storePath.componentName);
|
|
expect(key).toContain(storePath.subComponentName);
|
|
expect(key).toContain(storePath.globalAccountName);
|
|
expect(key).toContain(storePath.databaseName);
|
|
expect(key).toContain(storePath.containerName);
|
|
});
|
|
|
|
it("should handle components that include special characters", () => {
|
|
const storePath = {
|
|
componentName: AppStateComponentNames.DocumentsTab,
|
|
subComponentName: 'd"e"f',
|
|
globalAccountName: "g:hi{j",
|
|
databaseName: "a/b/c",
|
|
containerName: "https://blahblah.document.azure.com:443/",
|
|
};
|
|
const key = createKeyFromPath(storePath);
|
|
const segments = key.split(PATH_SEPARATOR);
|
|
expect(segments.length).toEqual(6); // There should be 5 segments
|
|
expect(segments[0]).toBe("");
|
|
|
|
const expectSubstringsInValue = (value: string, subStrings: string[]): boolean =>
|
|
subStrings.every((subString) => value.includes(subString));
|
|
|
|
expect(expectSubstringsInValue(segments[2], ["d", "e", "f"])).toBe(true);
|
|
expect(expectSubstringsInValue(segments[3], ["g", "hi", "j"])).toBe(true);
|
|
expect(expectSubstringsInValue(segments[4], ["a", "b", "c"])).toBe(true);
|
|
expect(expectSubstringsInValue(segments[5], ["https", "blahblah", "document", "com", "443"])).toBe(true);
|
|
});
|
|
});
|
|
});
|