UNPKG

@gravity-ui/graph

Version:

Modern graph editor component

221 lines (220 loc) 9.52 kB
import { Graph } from "../../graph"; import { EAnchorType } from "../anchor/Anchor"; const generateBlock = () => { return { id: Math.random() * 100, is: "FakeBlock", x: Math.random() * 100, y: Math.random() * 100, width: Math.random() * 100, height: Math.random() * 100, selected: false, name: "fakeBlock", anchors: [], }; }; describe("BlocksList", () => { let graph; let store; beforeEach(() => { graph = new Graph({}); store = graph.rootStore.blocksList; }); it("should set blocks", () => { const blocks = [generateBlock(), generateBlock()]; store.setBlocks(blocks); expect(store.toJSON()).toEqual(blocks); }); it("should append blocks", () => { const blocks = [generateBlock(), generateBlock()]; store.setBlocks(blocks); const newBlocks = [generateBlock(), generateBlock()]; store.updateBlocks(newBlocks); expect(store.toJSON()).toEqual([...blocks, ...newBlocks]); }); it("should update blocks", () => { const block1 = generateBlock(); const block2 = generateBlock(); store.setBlocks([block1, block2]); const prevBlockState = store.getBlockState(block1.id); const updatedBlock = { ...block1, x: 100, }; const newBlock = generateBlock(); store.updateBlocks([updatedBlock, newBlock]); const nextBlockState = store.getBlockState(block1.id); expect(store.toJSON()).toEqual([updatedBlock, block2, newBlock]); expect(prevBlockState).toBe(nextBlockState); }); it("should remove block by id", () => { const blocks = [generateBlock(), generateBlock()]; store.setBlocks(blocks); expect(store.toJSON()).toEqual(blocks); store.deleteBlocks([blocks[0]]); expect(store.toJSON()).toEqual([blocks[1]]); }); it("Should reuse block states on setBlocks", () => { const block1 = generateBlock(); const blocks = [block1, generateBlock()]; store.setBlocks(blocks); const blockState = store.$blocks.value.find((block) => block.id === block1.id); const nextBlock = { ...block1, name: "ChangedName", }; const nextBlocks = [nextBlock, generateBlock()]; store.setBlocks(nextBlocks); expect(store.toJSON()).toEqual(nextBlocks); const nextBlockState = store.$blocks.value.find((block) => block.id === block1.id); expect(blockState).toBe(nextBlockState); }); it.todo("should change block position"); it.todo("should emit event on block change"); it.todo("should recompute usableRect on change block"); }); describe("Blocks selection", () => { let graph; let store; let block1; let block2; let block3; beforeEach(() => { graph = new Graph({}); store = graph.rootStore.blocksList; block1 = generateBlock(); block2 = generateBlock(); block3 = generateBlock(); store.setBlocks([block1, block2, block3]); }); it("Should select block", () => { store.updateBlocksSelection([block1.id], true); expect(store.$selectedBlocks.value.map((b) => b.id)).toEqual([block1.id]); expect(store.getBlockState(block1.id)?.selected).toBe(true); }); it("Should unselect block", () => { store.updateBlocksSelection([block1.id], true); store.updateBlocksSelection([block1.id], false); expect(store.$selectedBlocks.value.length).toBe(0); expect(store.getBlockState(block1.id)?.selected).toBe(false); }); it("Should select multiple blocks", () => { store.updateBlocksSelection([block1.id], true); store.updateBlocksSelection([block2.id], true, require("../../services/selection/types").ESelectionStrategy.APPEND); expect(store.$selectedBlocks.value.map((b) => b.id).sort()).toEqual([block1.id, block2.id].sort()); expect(store.getBlockState(block1.id)?.selected).toBe(true); expect(store.getBlockState(block2.id)?.selected).toBe(true); }); it("Should unselect multiple blocks", () => { store.updateBlocksSelection([block1.id, block2.id], true); store.updateBlocksSelection([block1.id, block2.id], false); expect(store.$selectedBlocks.value.length).toBe(0); expect(store.getBlockState(block1.id)?.selected).toBe(false); expect(store.getBlockState(block2.id)?.selected).toBe(false); }); it("Should reset selection", () => { store.updateBlocksSelection([block1.id, block2.id], true); store.resetSelection(); expect(store.$selectedBlocks.value.length).toBe(0); expect(store.getBlockState(block1.id)?.selected).toBe(false); expect(store.getBlockState(block2.id)?.selected).toBe(false); }); it.todo("Should select anchor"); it.todo("Should unselect anchor"); it.todo("Should emit event on select anchor"); it("Should emit blocks-selection-change event with correct params", () => { const handler = jest.fn(); graph.on("blocks-selection-change", handler); store.updateBlocksSelection([block1.id], true); expect(handler).toHaveBeenCalledTimes(1); const eventArg = handler.mock.calls[0][0]; // Проверяем, что в eventArg есть нужные id expect(eventArg.detail.changes.add).toEqual([block1.id]); expect(eventArg.detail.changes.removed).toEqual([]); }); it("Should prevent selection change if preventDefault called", () => { graph.on("blocks-selection-change", (event) => { event.preventDefault(); }); store.updateBlocksSelection([block1.id], true); // Selection не должен измениться expect(store.$selectedBlocks.value.length).toBe(0); expect(store.getBlockState(block1.id)?.selected).toBe(false); }); }); describe("Anchors selection", () => { let graph; let store; let block; let anchorId; beforeEach(() => { graph = new Graph({}); store = graph.rootStore.blocksList; // Создаем блок с одним якорем block = { ...generateBlock(), anchors: [{ id: "anchor-1", blockId: "block-1", type: EAnchorType.IN, index: 0 }], }; // Проставляем blockId в блоке для якоря block.id = "block-1"; store.setBlocks([block]); anchorId = block.anchors[0].id; }); it("Should select anchor", () => { store.setAnchorSelection(block.id, anchorId, true); // Проверяем, что выбранный якорь совпадает по id expect(store.$selectedAnchor.value?.id).toBe(anchorId); }); it("Should unselect anchor", () => { store.setAnchorSelection(block.id, anchorId, true); store.setAnchorSelection(block.id, anchorId, false); expect(store.$selectedAnchor.value).toBeUndefined(); }); it("Should emit block-anchor-selection-change event with correct params", () => { const handler = jest.fn(); graph.on("block-anchor-selection-change", handler); store.setAnchorSelection(block.id, anchorId, true); expect(handler).toHaveBeenCalledTimes(1); const eventArg = handler.mock.calls[0][0]; expect(eventArg.detail.anchor.id).toBe(anchorId); expect(eventArg.detail.selected).toBe(true); }); it("Should prevent anchor selection if preventDefault called", () => { graph.on("block-anchor-selection-change", (event) => { event.preventDefault(); }); store.setAnchorSelection(block.id, anchorId, true); // Selection не должен измениться expect(store.$selectedAnchor.value).toBeUndefined(); }); it("Should allow only one anchor to be selected at a time, even across different blocks", () => { // Первый блок с anchor-1 const blockA = { ...generateBlock(), id: "block-A", anchors: [{ id: "anchor-1", blockId: "block-A", type: EAnchorType.IN, index: 0 }], }; // Второй блок с anchor-2 const blockB = { ...generateBlock(), id: "block-B", anchors: [{ id: "anchor-2", blockId: "block-B", type: EAnchorType.IN, index: 0 }], }; store.setBlocks([blockA, blockB]); // Выделяем anchor-1 store.setAnchorSelection(blockA.id, "anchor-1", true); expect(store.$selectedAnchor.value?.id).toBe("anchor-1"); // Теперь выделяем anchor-2 (другой блок) store.setAnchorSelection(blockB.id, "anchor-2", true); // Должен быть выбран только anchor-2 expect(store.$selectedAnchor.value?.id).toBe("anchor-2"); // anchor-1 больше не должен быть выбран const blockAState = store.getBlockState(blockA.id); const anchor1State = blockAState?.getAnchorState("anchor-1"); expect(anchor1State?.$selected.value).toBe(false); // anchor-2 должен быть выбран const blockBState = store.getBlockState(blockB.id); const anchor2State = blockBState?.getAnchorState("anchor-2"); expect(anchor2State?.$selected.value).toBe(true); }); });