diff --git a/src/Shared/AppStatePersistenceUtility.test.ts b/src/Shared/AppStatePersistenceUtility.test.ts index f0c951320..e0a02df9e 100644 --- a/src/Shared/AppStatePersistenceUtility.test.ts +++ b/src/Shared/AppStatePersistenceUtility.test.ts @@ -1,4 +1,11 @@ -import { createKeyFromPath, deleteState, loadState, MAX_ENTRY_NB, saveState } from "Shared/AppStatePersistenceUtility"; +import { + createKeyFromPath, + deleteState, + loadState, + MAX_ENTRY_NB, + PATH_SEPARATOR, + saveState, +} from "Shared/AppStatePersistenceUtility"; import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility"; jest.mock("Shared/StorageUtility", () => ({ @@ -166,5 +173,28 @@ describe("AppStatePersistenceUtility", () => { expect(key).toContain(storePath.databaseName); expect(key).toContain(storePath.containerName); }); + + it("should handle components that include special characters", () => { + const storePath = { + componentName: "a/b/c", + subComponentName: 'd"e"f', + globalAccountName: "g:h", + databaseName: "i{j", + 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[1], ["a", "b", "c"])).toBe(true); + expect(expectSubstringsInValue(segments[2], ["d", "e", "f"])).toBe(true); + expect(expectSubstringsInValue(segments[3], ["g", "h"])).toBe(true); + expect(expectSubstringsInValue(segments[4], ["i", "j"])).toBe(true); + expect(expectSubstringsInValue(segments[5], ["https", "blahblah", "document", "com", "443"])).toBe(true); + }); }); }); diff --git a/src/Shared/AppStatePersistenceUtility.ts b/src/Shared/AppStatePersistenceUtility.ts index bcf5ad7f3..dc42e8038 100644 --- a/src/Shared/AppStatePersistenceUtility.ts +++ b/src/Shared/AppStatePersistenceUtility.ts @@ -2,7 +2,7 @@ import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility"; // The component name whose state is being saved. Component name must not include special characters. export type ComponentName = "DocumentsTab"; - +export const PATH_SEPARATOR = "/"; // export for testing purposes const SCHEMA_VERSION = 1; // Export for testing purposes @@ -87,16 +87,10 @@ const orderedPathSegments: (keyof StorePath)[] = [ * @param path */ export const createKeyFromPath = (path: StorePath): string => { - if (path.componentName.includes("/")) { - throw new Error(`Invalid component name: ${path.componentName}`); - } - let key = `/${path.componentName}`; // ComponentName is always there + let key = `${PATH_SEPARATOR}${encodeURIComponent(path.componentName)}`; // ComponentName is always there orderedPathSegments.forEach((segment) => { const segmentValue = path[segment as keyof StorePath]; - if (segmentValue.includes("/")) { - throw new Error(`Invalid setting path segment: ${segment}`); - } - key += `/${segmentValue !== undefined ? segmentValue : ""}`; + key += `${PATH_SEPARATOR}${segmentValue !== undefined ? encodeURIComponent(segmentValue) : ""}`; }); return key; };