[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
119
application/state/useStoredString.test.ts
Normal file
119
application/state/useStoredString.test.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import {
|
||||
createStoredStringSyncHandlers,
|
||||
readOptionalStoredStringValue,
|
||||
readStoredStringValue,
|
||||
resolveStoredStringUpdate,
|
||||
} from "./useStoredString.ts";
|
||||
|
||||
type TestMode = "edit" | "preview";
|
||||
|
||||
const isTestMode = (value: string | null): value is TestMode =>
|
||||
value === "edit" || value === "preview";
|
||||
|
||||
function installLocalStorage() {
|
||||
const previousLocalStorage = Object.getOwnPropertyDescriptor(globalThis, "localStorage");
|
||||
const backing = new Map<string, string>();
|
||||
const storage: Storage = {
|
||||
get length() {
|
||||
return backing.size;
|
||||
},
|
||||
clear() {
|
||||
backing.clear();
|
||||
},
|
||||
getItem(key: string) {
|
||||
return backing.get(key) ?? null;
|
||||
},
|
||||
key(index: number) {
|
||||
return Array.from(backing.keys())[index] ?? null;
|
||||
},
|
||||
removeItem(key: string) {
|
||||
backing.delete(key);
|
||||
},
|
||||
setItem(key: string, value: string) {
|
||||
backing.set(key, value);
|
||||
},
|
||||
};
|
||||
|
||||
Object.defineProperty(globalThis, "localStorage", {
|
||||
value: storage,
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
return {
|
||||
storage,
|
||||
restore() {
|
||||
if (previousLocalStorage) {
|
||||
Object.defineProperty(globalThis, "localStorage", previousLocalStorage);
|
||||
return;
|
||||
}
|
||||
Reflect.deleteProperty(globalThis, "localStorage");
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
test("stored string sync handlers refresh from same-window and browser storage events", (t) => {
|
||||
const env = installLocalStorage();
|
||||
t.after(() => env.restore());
|
||||
|
||||
const storageKey = "netcatty:test-mode";
|
||||
const syncedValues: TestMode[] = [];
|
||||
const handlers = createStoredStringSyncHandlers<TestMode>({
|
||||
storageKey,
|
||||
fallback: "edit",
|
||||
isAllowedValue: isTestMode,
|
||||
onValue: (value) => syncedValues.push(value),
|
||||
});
|
||||
|
||||
env.storage.setItem(storageKey, "preview");
|
||||
handlers.handleAdapterChange({ detail: { key: storageKey } } as CustomEvent<{ key: string }>);
|
||||
assert.deepEqual(syncedValues, ["preview"]);
|
||||
|
||||
env.storage.setItem(storageKey, "invalid");
|
||||
handlers.handleBrowserStorage({ type: "storage", key: storageKey } as StorageEvent);
|
||||
assert.deepEqual(syncedValues, ["preview", "edit"]);
|
||||
|
||||
env.storage.setItem(storageKey, "preview");
|
||||
handlers.handleAdapterChange({ detail: { key: "other-key" } } as CustomEvent<{ key: string }>);
|
||||
assert.deepEqual(syncedValues, ["preview", "edit"]);
|
||||
});
|
||||
|
||||
test("stored string helpers read fallback and resolve updater-style toggles", (t) => {
|
||||
const env = installLocalStorage();
|
||||
t.after(() => env.restore());
|
||||
|
||||
const storageKey = "netcatty:test-mode";
|
||||
assert.equal(readStoredStringValue(storageKey, "edit", isTestMode), "edit");
|
||||
assert.equal(readOptionalStoredStringValue(storageKey, isTestMode), null);
|
||||
|
||||
env.storage.setItem(storageKey, "preview");
|
||||
assert.equal(readStoredStringValue(storageKey, "edit", isTestMode), "preview");
|
||||
assert.equal(readOptionalStoredStringValue(storageKey, isTestMode), "preview");
|
||||
|
||||
env.storage.setItem(storageKey, "invalid");
|
||||
assert.equal(readStoredStringValue(storageKey, "edit", isTestMode), "edit");
|
||||
assert.equal(readOptionalStoredStringValue(storageKey, isTestMode), null);
|
||||
|
||||
assert.equal(resolveStoredStringUpdate<TestMode>("edit", "preview"), "preview");
|
||||
assert.equal(
|
||||
resolveStoredStringUpdate<TestMode>("preview", (currentValue) => (
|
||||
currentValue === "edit" ? "preview" : "edit"
|
||||
)),
|
||||
"edit",
|
||||
);
|
||||
});
|
||||
|
||||
test("stored string helpers work with default validator when isAllowedValue is omitted", (t) => {
|
||||
const env = installLocalStorage();
|
||||
t.after(() => env.restore());
|
||||
|
||||
const storageKey = "netcatty:test-font";
|
||||
assert.equal(readStoredStringValue(storageKey, "default-font"), "default-font");
|
||||
assert.equal(readOptionalStoredStringValue(storageKey), null);
|
||||
|
||||
env.storage.setItem(storageKey, "Inter");
|
||||
assert.equal(readStoredStringValue(storageKey, "default-font"), "Inter");
|
||||
assert.equal(readOptionalStoredStringValue(storageKey), "Inter");
|
||||
});
|
||||
Reference in New Issue
Block a user