UNPKG

@gravity-ui/graph

Version:

Modern graph editor component

213 lines (212 loc) 11.2 kB
import { MultipleSelectionBucket } from "./MultipleSelectionBucket"; import { SingleSelectionBucket } from "./SingleSelectionBucket"; import { ESelectionStrategy } from "./types"; describe("Selection Resolver Integration", () => { describe("MultipleSelectionBucket with resolver", () => { const testEntities = [ { id: "1", name: "Entity One", value: 100 }, { id: "2", name: "Entity Two", value: 200 }, { id: "3", name: "Entity Three", value: 300 }, ]; const resolver = (ids) => { return ids.map((id) => testEntities.find((e) => e.id === id)).filter((e) => e !== undefined); }; it("should resolve selected IDs to entities", () => { const bucket = new MultipleSelectionBucket("test", undefined, undefined, resolver); bucket.select(["1", "2"], ESelectionStrategy.REPLACE); expect(bucket.$selectedEntities.value).toEqual([ { id: "1", name: "Entity One", value: 100 }, { id: "2", name: "Entity Two", value: 200 }, ]); }); it("should update resolved entities when selection changes", () => { const bucket = new MultipleSelectionBucket("test", undefined, undefined, resolver); // Initial selection bucket.select(["1"], ESelectionStrategy.REPLACE); expect(bucket.$selectedEntities.value).toEqual([{ id: "1", name: "Entity One", value: 100 }]); // Add to selection bucket.select(["2"], ESelectionStrategy.APPEND); expect(bucket.$selectedEntities.value).toEqual([ { id: "1", name: "Entity One", value: 100 }, { id: "2", name: "Entity Two", value: 200 }, ]); // Remove from selection bucket.select(["1"], ESelectionStrategy.SUBTRACT); expect(bucket.$selectedEntities.value).toEqual([{ id: "2", name: "Entity Two", value: 200 }]); }); it("should return empty array when no resolver is provided", () => { const bucket = new MultipleSelectionBucket("test"); bucket.select(["1", "2"], ESelectionStrategy.REPLACE); expect(bucket.$selectedEntities.value).toEqual([]); expect(Array.from(bucket.$selected.value)).toEqual(["1", "2"]); }); it("should filter out non-existent IDs", () => { const bucket = new MultipleSelectionBucket("test", undefined, undefined, resolver); bucket.select(["1", "non-existent", "2"], ESelectionStrategy.REPLACE); expect(bucket.$selectedEntities.value).toEqual([ { id: "1", name: "Entity One", value: 100 }, { id: "2", name: "Entity Two", value: 200 }, ]); }); it("should work with reactive subscriptions", () => { const bucket = new MultipleSelectionBucket("test", undefined, undefined, resolver); const updates = []; // Subscribe to changes const unsubscribe = bucket.$selectedEntities.subscribe((entities) => { updates.push([...entities]); }); bucket.select(["1"], ESelectionStrategy.REPLACE); bucket.select(["2"], ESelectionStrategy.APPEND); bucket.select([], ESelectionStrategy.REPLACE); unsubscribe(); // First update is the initial empty state, then 3 changes expect(updates).toHaveLength(4); expect(updates[0]).toEqual([]); // Initial state expect(updates[1]).toEqual([{ id: "1", name: "Entity One", value: 100 }]); expect(updates[2]).toEqual([ { id: "1", name: "Entity One", value: 100 }, { id: "2", name: "Entity Two", value: 200 }, ]); expect(updates[3]).toEqual([]); }); }); describe("SingleSelectionBucket with resolver", () => { const testEntities = [ { id: "a", name: "Alpha" }, { id: "b", name: "Beta" }, { id: "c", name: "Gamma" }, ]; const resolver = (ids) => { return ids.map((id) => testEntities.find((e) => e.id === id)).filter((e) => e !== undefined); }; it("should resolve single selected ID to entity", () => { const bucket = new SingleSelectionBucket("test", undefined, undefined, resolver); bucket.select(["a"], ESelectionStrategy.REPLACE); expect(bucket.$selectedEntities.value).toEqual([{ id: "a", name: "Alpha" }]); }); it("should replace selection when selecting new item", () => { const bucket = new SingleSelectionBucket("test", undefined, undefined, resolver); bucket.select(["a"], ESelectionStrategy.REPLACE); expect(bucket.$selectedEntities.value).toEqual([{ id: "a", name: "Alpha" }]); bucket.select(["b"], ESelectionStrategy.REPLACE); expect(bucket.$selectedEntities.value).toEqual([{ id: "b", name: "Beta" }]); }); it("should return empty array when selection is cleared", () => { const bucket = new SingleSelectionBucket("test", undefined, undefined, resolver); bucket.select(["a"], ESelectionStrategy.REPLACE); expect(bucket.$selectedEntities.value).toEqual([{ id: "a", name: "Alpha" }]); bucket.reset(); expect(bucket.$selectedEntities.value).toEqual([]); }); it("should ignore multiple IDs in single selection", () => { const bucket = new SingleSelectionBucket("test", undefined, undefined, resolver); // Single selection only takes first ID bucket.select(["a", "b", "c"], ESelectionStrategy.REPLACE); expect(bucket.$selectedEntities.value).toEqual([{ id: "a", name: "Alpha" }]); }); }); describe("Type safety", () => { it("should maintain type safety with TypeScript", () => { const blocks = [ { id: "block1", x: 0, y: 0, width: 100, height: 50 }, { id: "block2", x: 150, y: 0, width: 100, height: 50 }, ]; const resolver = (ids) => { return ids.map((id) => blocks.find((b) => b.id === id)).filter((b) => b !== undefined); }; const bucket = new MultipleSelectionBucket("block", undefined, undefined, resolver); bucket.select(["block1"], ESelectionStrategy.REPLACE); const selectedBlocks = bucket.$selectedEntities.value; // TypeScript should know the type expect(selectedBlocks[0].x).toBe(0); expect(selectedBlocks[0].width).toBe(100); }); }); describe("Component resolution", () => { // Mock GraphComponent-like entity class MockComponent { constructor(id, name) { this.id = id; this.name = name; } getEntityId() { return this.id; } } // Mock State with getViewComponent class MockState { constructor(id, component) { this.id = id; this.component = component; } getViewComponent() { return this.component; } } it("should resolve entities to components via getViewComponent()", () => { const component1 = new MockComponent("comp1", "Component 1"); const component2 = new MockComponent("comp2", "Component 2"); const states = [new MockState("state1", component1), new MockState("state2", component2)]; const resolver = (ids) => { return ids.map((id) => states.find((s) => s.id === id)).filter((s) => s !== undefined); }; const bucket = new MultipleSelectionBucket("test", undefined, undefined, resolver); bucket.select(["state1", "state2"], ESelectionStrategy.REPLACE); const components = bucket.$selectedComponents.value; expect(components).toHaveLength(2); expect(components[0].name).toBe("Component 1"); expect(components[1].name).toBe("Component 2"); }); it("should resolve entities that are already components", () => { const component1 = new MockComponent("comp1", "Component 1"); const component2 = new MockComponent("comp2", "Component 2"); const components = [component1, component2]; const resolver = (ids) => { return ids.map((id) => components.find((c) => c.id === id)).filter((c) => c !== undefined); }; const bucket = new MultipleSelectionBucket("test", undefined, undefined, resolver); bucket.select(["comp1"], ESelectionStrategy.REPLACE); const resolvedComponents = bucket.$selectedComponents.value; expect(resolvedComponents).toHaveLength(1); expect(resolvedComponents[0].name).toBe("Component 1"); }); it("should return empty array for entities without components", () => { const entities = [ { id: "e1", value: 100 }, { id: "e2", value: 200 }, ]; const resolver = (ids) => { return ids.map((id) => entities.find((e) => e.id === id)).filter((e) => e !== undefined); }; const bucket = new MultipleSelectionBucket("test", undefined, undefined, resolver); bucket.select(["e1", "e2"], ESelectionStrategy.REPLACE); // Entities exist expect(bucket.$selectedEntities.value).toHaveLength(2); // But no components (entities don't have getViewComponent) expect(bucket.$selectedComponents.value).toHaveLength(0); }); it("should reactively update components when selection changes", () => { const component1 = new MockComponent("comp1", "Component 1"); const component2 = new MockComponent("comp2", "Component 2"); const states = [new MockState("state1", component1), new MockState("state2", component2)]; const resolver = (ids) => { return ids.map((id) => states.find((s) => s.id === id)).filter((s) => s !== undefined); }; const bucket = new MultipleSelectionBucket("test", undefined, undefined, resolver); const updates = []; const unsubscribe = bucket.$selectedComponents.subscribe((components) => { updates.push([...components]); }); bucket.select(["state1"], ESelectionStrategy.REPLACE); bucket.select(["state2"], ESelectionStrategy.APPEND); bucket.select([], ESelectionStrategy.REPLACE); unsubscribe(); expect(updates).toHaveLength(4); // Initial + 3 changes expect(updates[0]).toHaveLength(0); // Initial empty expect(updates[1]).toHaveLength(1); expect(updates[1][0].name).toBe("Component 1"); expect(updates[2]).toHaveLength(2); expect(updates[3]).toHaveLength(0); // Cleared }); }); });