diff --git a/app/src/components/layout/NotesDock.test.tsx b/app/src/components/layout/NotesDock.test.tsx index d825eb3..ceaee6d 100644 --- a/app/src/components/layout/NotesDock.test.tsx +++ b/app/src/components/layout/NotesDock.test.tsx @@ -95,4 +95,19 @@ describe("NotesDock", () => { fireEvent.keyDown(handle, { key: "ArrowLeft" }); expect(state.setNotesDockWidth).toHaveBeenCalled(); }); + + it("widens on ArrowLeft and narrows on ArrowRight, by the exact step", () => { + // The dock sits on the right edge, so dragging or pressing left grows it + // and right shrinks it. Asserting only "was called" would pass even if + // the branches were swapped or the sign inverted. + state.activeTabKey = "home:p1"; + render(); + const handle = screen.getByRole("separator", { name: /resize notes/i }); + + fireEvent.keyDown(handle, { key: "ArrowLeft" }); + expect(state.setNotesDockWidth).toHaveBeenLastCalledWith(368); + + fireEvent.keyDown(handle, { key: "ArrowRight" }); + expect(state.setNotesDockWidth).toHaveBeenLastCalledWith(336); + }); }); diff --git a/app/src/store/dockWidth.test.ts b/app/src/store/dockWidth.test.ts index 9bb0361..1a0e066 100644 --- a/app/src/store/dockWidth.test.ts +++ b/app/src/store/dockWidth.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect } from "vitest"; +import { describe, it, expect, afterEach, vi } from "vitest"; import { clampDockWidth, NOTES_DOCK_MIN_WIDTH, @@ -29,3 +29,34 @@ describe("clampDockWidth", () => { expect(clampDockWidth(400.6)).toBe(401); }); }); + +// The pure function above is only half the contract: the brief calls out that +// the clamp must guard the *read* path too, because localStorage can carry +// anything a previous version, a hand edit, or a different screen left +// behind. These tests exercise the real store initialization — seeding +// localStorage, then re-importing the module fresh so its top-level +// `loadNotesDockWidth()` call runs against the seeded value — rather than a +// function pulled out just to make this testable. A future refactor that +// dropped the clamp from the load path while keeping it on the write path +// would fail these. +describe("notesDockWidth store initialization", () => { + const WIDTH_KEY = "triple-c.notes.dock.width"; + + afterEach(() => { + localStorage.removeItem(WIDTH_KEY); + }); + + it("clamps an out-of-range stored value on load", async () => { + localStorage.setItem(WIDTH_KEY, "99999"); + vi.resetModules(); + const { useAppState } = await import("./appState"); + expect(useAppState.getState().notesDockWidth).toBe(NOTES_DOCK_MAX_WIDTH); + }); + + it("falls back to the default for a non-numeric stored value on load", async () => { + localStorage.setItem(WIDTH_KEY, "banana"); + vi.resetModules(); + const { useAppState } = await import("./appState"); + expect(useAppState.getState().notesDockWidth).toBe(NOTES_DOCK_DEFAULT_WIDTH); + }); +});