diff --git a/src/stores/cytoscapeStore.ts b/src/stores/cytoscapeStore.ts index 8de21bc..599073d 100644 --- a/src/stores/cytoscapeStore.ts +++ b/src/stores/cytoscapeStore.ts @@ -26,8 +26,8 @@ export interface Node { } export interface NodePositions { - [direction: string]: { - [graphId: string]: Node[]; + [graphId: string]: { + [direction: string]: Node[]; } } @@ -51,16 +51,10 @@ export const useCytoscapeStore = defineStore('cytoscapeStore', { if (!this.nodePositions[this.currentGraphId][direction]) { this.nodePositions[this.currentGraphId][direction] = []; } - // If this graph's data was previously stored in localStorage - if (localStorage.getItem(SAVE_KEY_NAME)) { - const nodeToSave = this.nodePositions[this.currentGraphId][direction] - .find(node => node.id === nodeId); - if (nodeToSave) { - nodeToSave.position = position; - } else { - this.nodePositions[this.currentGraphId][direction] - .push({ id: nodeId, position: position }); - } + const nodeToSave = this.nodePositions[this.currentGraphId][direction] + .find(node => node.id === nodeId); + if (nodeToSave) { + nodeToSave.position = position; } else { this.nodePositions[this.currentGraphId][direction] .push({ id: nodeId, position: position }); diff --git a/tests/stores/cytoscapeStoreSaveNodePosition.test.js b/tests/stores/cytoscapeStoreSaveNodePosition.test.js new file mode 100644 index 0000000..1ad6bbf --- /dev/null +++ b/tests/stores/cytoscapeStoreSaveNodePosition.test.js @@ -0,0 +1,63 @@ +// The Lucia project. +// Copyright 2026-2026 DSP, inc. All rights reserved. +// Authors: +// imacat.yang@dsp.im (imacat), 2026/07/04 + +import { describe, it, expect, beforeEach, vi } from "vitest"; +import { setActivePinia, createPinia } from "pinia"; +import { useCytoscapeStore } from "@/stores/cytoscapeStore"; + +// Mock localStorage since jsdom's localStorage is limited +const localStorageMock = (() => { + let store = {}; + return { + getItem: vi.fn((key) => store[key] || null), + setItem: vi.fn((key, value) => { + store[key] = value; + }), + removeItem: vi.fn((key) => { + delete store[key]; + }), + clear: vi.fn(() => { + store = {}; + }), + }; +})(); +Object.defineProperty(globalThis, "localStorage", { + value: localStorageMock, +}); + +describe("cytoscapeStore saveNodePosition without stored data", () => { + let store; + + beforeEach(() => { + setActivePinia(createPinia()); + store = useCytoscapeStore(); + localStorageMock.clear(); + vi.clearAllMocks(); + // Keep localStorage empty for SAVE_KEY_NAME throughout, as when + // storage is unavailable or cleared externally between saves. + localStorageMock.getItem.mockReturnValue(null); + }); + + it("does not duplicate a node saved twice without stored data", () => { + store.setCurrentGraphId("graph1"); + store.saveNodePosition("node1", { x: 10, y: 20 }, "TB"); + store.saveNodePosition("node1", { x: 30, y: 40 }, "TB"); + expect(store.nodePositions["graph1"]["TB"]).toHaveLength(1); + expect(store.nodePositions["graph1"]["TB"][0]).toEqual({ + id: "node1", + position: { x: 30, y: 40 }, + }); + }); + + it("adds separate entries for different nodes", () => { + store.setCurrentGraphId("graph1"); + store.saveNodePosition("node1", { x: 10, y: 20 }, "TB"); + store.saveNodePosition("node2", { x: 30, y: 40 }, "TB"); + expect(store.nodePositions["graph1"]["TB"]).toEqual([ + { id: "node1", position: { x: 10, y: 20 } }, + { id: "node2", position: { x: 30, y: 40 } }, + ]); + }); +});