From ec6f6c030a79da8d6fcf72e98556fd44aff3c9c9 Mon Sep 17 00:00:00 2001 From: draedful Date: Fri, 27 Feb 2026 13:01:28 +0300 Subject: [PATCH 1/2] fix(Block): resolve HTML blocks disappearing after double setEntities call --- e2e/build-bundle.js | 25 +++- e2e/page-objects/GraphPageObject.ts | 12 ++ e2e/page-objects/ReactGraphPageObject.ts | 112 ++++++++++++++++++ e2e/pages/react.html | 26 +++++ e2e/react-entry.ts | 13 +++ e2e/tests/reload-test.spec.ts | 72 ++++++++++++ e2e/tests/set-entities.spec.ts | 140 +++++++++++++++++++++++ src/components/canvas/blocks/Block.ts | 23 +++- src/react-components/Block.tsx | 4 +- src/store/block/Block.ts | 8 +- 10 files changed, 424 insertions(+), 11 deletions(-) create mode 100644 e2e/page-objects/ReactGraphPageObject.ts create mode 100644 e2e/pages/react.html create mode 100644 e2e/react-entry.ts create mode 100644 e2e/tests/reload-test.spec.ts create mode 100644 e2e/tests/set-entities.spec.ts diff --git a/e2e/build-bundle.js b/e2e/build-bundle.js index bc21672a..9015af21 100644 --- a/e2e/build-bundle.js +++ b/e2e/build-bundle.js @@ -18,7 +18,7 @@ const cssPlugin = { }, }; -esbuild +const baseBundle = esbuild .build({ entryPoints: [path.join(__dirname, "entry.ts")], bundle: true, @@ -37,3 +37,26 @@ esbuild console.error("E2E bundle failed:", err); process.exit(1); }); + +const reactBundle = esbuild + .build({ + entryPoints: [path.join(__dirname, "react-entry.ts")], + bundle: true, + outfile: path.join(__dirname, "dist/graph-react.bundle.js"), + format: "iife", + globalName: "GraphModule", + platform: "browser", + target: ["es2020"], + sourcemap: true, + plugins: [cssPlugin], + jsx: "automatic", + }) + .then(() => { + console.log("E2E React bundle created successfully with CSS"); + }) + .catch((err) => { + console.error("E2E React bundle failed:", err); + process.exit(1); + }); + +Promise.all([baseBundle, reactBundle]).catch(() => process.exit(1)); diff --git a/e2e/page-objects/GraphPageObject.ts b/e2e/page-objects/GraphPageObject.ts index fb54f5f8..14c811aa 100644 --- a/e2e/page-objects/GraphPageObject.ts +++ b/e2e/page-objects/GraphPageObject.ts @@ -483,4 +483,16 @@ export class GraphPageObject { return new GraphEventListener(this.page, key); } + + /** + * Call setEntities on the graph with new blocks and connections + */ + async setEntities(config: GraphConfig): Promise { + await this.page.evaluate((cfg) => { + window.graph.setEntities({ + blocks: cfg.blocks || [], + connections: cfg.connections || [], + }); + }, config); + } } diff --git a/e2e/page-objects/ReactGraphPageObject.ts b/e2e/page-objects/ReactGraphPageObject.ts new file mode 100644 index 00000000..184d54a6 --- /dev/null +++ b/e2e/page-objects/ReactGraphPageObject.ts @@ -0,0 +1,112 @@ +import { Page } from "@playwright/test"; +import { TBlock } from "../../src/components/canvas/blocks/Block"; +import { TConnection } from "../../src/store/connection/ConnectionState"; +import { GraphPageObject, GraphConfig } from "./GraphPageObject"; + +/** + * PageObject for React-based graph rendering (using GraphCanvas + BlocksList). + * Extends GraphPageObject with React-specific initialization. + */ +export class ReactGraphPageObject extends GraphPageObject { + constructor(page: Page) { + super(page); + } + + /** + * Initialize React-based graph with GraphCanvas + BlocksList + * Uses the react.html page which loads the React bundle + */ + async initialize(config: GraphConfig): Promise { + await this.page.goto("/react.html"); + + // Wait for React bundle to load + await this.page.waitForFunction(() => { + return (window as any).graphLibraryLoaded === true; + }); + + // Initialize React graph using GraphCanvas component + await this.page.evaluate((cfg) => { + const { Graph, GraphCanvas, GraphBlock, React, ReactDOM } = (window as any).GraphModule; + + const rootEl = document.getElementById("root"); + if (!rootEl) { + throw new Error("Root element not found"); + } + + const graph = new Graph(cfg, rootEl); + + // Render with React and GraphCanvas (enables HTML block rendering via BlocksList) + const reactRoot = ReactDOM.createRoot(rootEl); + + const renderBlock = (g: unknown, block: { id: string; name?: string }) => { + return React.createElement( + GraphBlock, + { graph: g, block }, + React.createElement("div", { "data-testid": `block-${block.id}`, style: { padding: "8px" } }, block.name || block.id) + ); + }; + + reactRoot.render( + React.createElement(GraphCanvas, { + graph, + renderBlock, + style: { width: "100%", height: "100vh" }, + }) + ); + + // Set initial entities if provided + if (cfg.blocks || cfg.connections) { + graph.setEntities({ + blocks: cfg.blocks || [], + connections: cfg.connections || [], + }); + } + + graph.start(); + graph.zoomTo("center"); + + // Expose to window for tests + window.graph = graph; + window.graphInitialized = true; + }, config); + + // Wait for graph to be ready + await this.page.waitForFunction( + () => window.graphInitialized === true, + { timeout: 5000 } + ); + + // Wait for initial render + await this.waitForFrames(5); + } + + /** + * Get count of rendered HTML blocks in the DOM + */ + async getRenderedHtmlBlockCount(): Promise { + return await this.page.evaluate(() => { + return document.querySelectorAll("[data-testid^='block-']").length; + }); + } + + /** + * Get IDs of rendered HTML blocks in the DOM + */ + async getRenderedHtmlBlockIds(): Promise { + return await this.page.evaluate(() => { + const elements = document.querySelectorAll("[data-testid^='block-']"); + return Array.from(elements).map((el) => + el.getAttribute("data-testid")?.replace("block-", "") || "" + ); + }); + } + + /** + * Check if a specific HTML block is rendered in the DOM + */ + async isHtmlBlockRendered(blockId: string): Promise { + return await this.page.evaluate((id) => { + return !!document.querySelector(`[data-testid='block-${id}']`); + }, blockId); + } +} diff --git a/e2e/pages/react.html b/e2e/pages/react.html new file mode 100644 index 00000000..a81cc8df --- /dev/null +++ b/e2e/pages/react.html @@ -0,0 +1,26 @@ + + + + + Graph React E2E Test + + + +
+ + + + diff --git a/e2e/react-entry.ts b/e2e/react-entry.ts new file mode 100644 index 00000000..d458a2d7 --- /dev/null +++ b/e2e/react-entry.ts @@ -0,0 +1,13 @@ +// React E2E bundle entry point +import React from "react"; +import ReactDOM from "react-dom/client"; + +import "../src/services/Layer.css"; +import "../src/react-components/graph-canvas.css"; +import "../src/react-components/Block.css"; +import "../src/react-components/Anchor.css"; + +// Re-export everything from main and react indexes +export * from "../src/index"; +export * from "../src/react-components/index"; +export { React, ReactDOM }; diff --git a/e2e/tests/reload-test.spec.ts b/e2e/tests/reload-test.spec.ts new file mode 100644 index 00000000..77a3e9f3 --- /dev/null +++ b/e2e/tests/reload-test.spec.ts @@ -0,0 +1,72 @@ +import { test, expect } from "@playwright/test"; + +test("html blocks disappear after reload with double setEntities", async ({ page }) => { + await page.goto("/react.html"); + + await page.waitForFunction(() => (window as any).graphLibraryLoaded === true); + + const blocks = [ + { id: "b1", is: "Block", x: 100, y: 100, width: 200, height: 100, name: "B1", anchors: [], selected: false }, + { id: "b2", is: "Block", x: 400, y: 100, width: 200, height: 100, name: "B2", anchors: [], selected: false }, + ]; + + await page.evaluate((blocks) => { + const { Graph, GraphCanvas, GraphBlock, React, ReactDOM } = (window as any).GraphModule; + const rootEl = document.getElementById("root"); + const graph = new Graph({}, rootEl); + const reactRoot = ReactDOM.createRoot(rootEl); + + const renderBlock = (g: any, block: any) => { + return React.createElement( + GraphBlock, + { graph: g, block }, + React.createElement("div", { "data-testid": `block-${block.id}` }, block.name) + ); + }; + + reactRoot.render(React.createElement(GraphCanvas, { graph, renderBlock })); + graph.setEntities({ blocks, connections: [] }); + graph.start(); + graph.zoom({ scale: 1.0 }); + window.graph = graph; + window.graphInitialized = true; + }, blocks); + + await page.waitForFunction(() => window.graphInitialized === true); + + // Wait enough for initial render + await page.evaluate(() => new Promise(resolve => { + let n = 10; + const tick = () => { if (--n <= 0) resolve(); else requestAnimationFrame(tick); }; + requestAnimationFrame(tick); + })); + + const initialState = await page.evaluate(() => { + const els = document.querySelectorAll("[data-testid^='block-']"); + return { count: els.length, scale: window.graph.cameraService.getCameraScale() }; + }); + console.log("Initial state:", initialState); + + // Now simulate "reload" - double setEntities + await page.evaluate((blocks) => { + window.graph.setEntities({ blocks: [], connections: [] }); + window.graph.setEntities({ blocks, connections: [] }); + }, blocks); + + await page.evaluate(() => new Promise(resolve => { + let n = 20; + const tick = () => { if (--n <= 0) resolve(); else requestAnimationFrame(tick); }; + requestAnimationFrame(tick); + })); + + const afterReloadState = await page.evaluate(() => { + const els = document.querySelectorAll("[data-testid^='block-']"); + const storeBlocks = window.graph.rootStore.blocksList.$blocks.value.map((b: any) => b.id); + const scale = window.graph.cameraService.getCameraScale(); + const cameraLevel = window.graph.cameraService.getCameraBlockScaleLevel(); + return { count: els.length, storeBlocks, scale, cameraLevel }; + }); + console.log("After reload state:", afterReloadState); + + expect(afterReloadState.count).toBe(2); +}); diff --git a/e2e/tests/set-entities.spec.ts b/e2e/tests/set-entities.spec.ts new file mode 100644 index 00000000..3afb5f7c --- /dev/null +++ b/e2e/tests/set-entities.spec.ts @@ -0,0 +1,140 @@ +import { test, expect } from "@playwright/test"; +import { ReactGraphPageObject } from "../page-objects/ReactGraphPageObject"; + +const BLOCKS_DATA = [ + { + id: "block-1", + is: "Block" as const, + x: 100, + y: 100, + width: 200, + height: 100, + name: "Block 1", + anchors: [], + selected: false, + }, + { + id: "block-2", + is: "Block" as const, + x: 400, + y: 100, + width: 200, + height: 100, + name: "Block 2", + anchors: [], + selected: false, + }, + { + id: "block-3", + is: "Block" as const, + x: 250, + y: 300, + width: 200, + height: 100, + name: "Block 3", + anchors: [], + selected: false, + }, +]; + +test.describe("setEntities - HTML blocks rendering (issue #249)", () => { + let graphPO: ReactGraphPageObject; + + test.beforeEach(async ({ page }) => { + graphPO = new ReactGraphPageObject(page); + + // Initialize with empty graph at "Detailed" zoom level (scale >= 0.7) + // so HTML blocks (ReactLayer / BlocksList) are rendered + await graphPO.initialize({ + blocks: [], + connections: [], + }); + + // Zoom in to "Detailed" scale level (>= 0.7) to activate HTML block rendering + const camera = graphPO.getCamera(); + await camera.zoomToScale(1.0); + }); + + test("should render HTML blocks after initial setEntities", async () => { + await graphPO.setEntities({ blocks: BLOCKS_DATA, connections: [] }); + await graphPO.waitForFrames(5); + + const count = await graphPO.getRenderedHtmlBlockCount(); + expect(count).toBe(3); + }); + + test("should render HTML blocks after setEntities with empty then full data (issue #249)", async () => { + // This is the exact scenario from the issue: + // calling setEntities twice rapidly - first clear, then populate + await graphPO.page.evaluate((blocks) => { + // Call both setEntities back-to-back with no await between them + window.graph.setEntities({ blocks: [], connections: [] }); + window.graph.setEntities({ blocks, connections: [] }); + }, BLOCKS_DATA); + + // Wait enough frames for the rendering to complete + await graphPO.waitForFrames(10); + + // All 3 HTML blocks should be visible + const count = await graphPO.getRenderedHtmlBlockCount(); + expect(count).toBe(3); + + const ids = await graphPO.getRenderedHtmlBlockIds(); + expect(ids).toContain("block-1"); + expect(ids).toContain("block-2"); + expect(ids).toContain("block-3"); + }); + + test("should render HTML blocks after setEntities with small delay between calls", async () => { + // As described in the issue, even a small timeout may not prevent the problem + await graphPO.page.evaluate(async (blocks) => { + window.graph.setEntities({ blocks: [], connections: [] }); + + // Small synchronous delay (setTimeout 0 - one tick) + await new Promise((resolve) => setTimeout(resolve, 0)); + + window.graph.setEntities({ blocks, connections: [] }); + }, BLOCKS_DATA); + + await graphPO.waitForFrames(10); + + const count = await graphPO.getRenderedHtmlBlockCount(); + expect(count).toBe(3); + }); + + test("should update HTML blocks when setEntities replaces blocks with different data", async () => { + // Set initial blocks + await graphPO.setEntities({ blocks: BLOCKS_DATA, connections: [] }); + await graphPO.waitForFrames(5); + + expect(await graphPO.getRenderedHtmlBlockCount()).toBe(3); + + // Replace with different blocks + const newBlocks = [ + { + id: "new-block-1", + is: "Block" as const, + x: 200, + y: 200, + width: 200, + height: 100, + name: "New Block 1", + anchors: [], + selected: false, + }, + ]; + + await graphPO.page.evaluate((blocks) => { + window.graph.setEntities({ blocks: [], connections: [] }); + window.graph.setEntities({ blocks, connections: [] }); + }, newBlocks); + + await graphPO.waitForFrames(10); + + const count = await graphPO.getRenderedHtmlBlockCount(); + expect(count).toBe(1); + + const rendered = await graphPO.isHtmlBlockRendered("new-block-1"); + expect(rendered).toBe(true); + }); +}); diff --git a/src/components/canvas/blocks/Block.ts b/src/components/canvas/blocks/Block.ts index f3a7875f..9f5026ca 100644 --- a/src/components/canvas/blocks/Block.ts +++ b/src/components/canvas/blocks/Block.ts @@ -106,6 +106,8 @@ export class Block; + private connectedStateUnsubscribers: (() => void)[] = []; + protected lastDragEvent?: MouseEvent; protected startDragCoords: number[] = []; @@ -171,6 +173,9 @@ export class Block unsub()); + this.connectedStateUnsubscribers = []; + this.connectedState = selectBlockById(this.context.graph, id); this.state = cloneDeep(this.connectedState.$state.value); this.connectedState.setViewComponent(this); @@ -183,15 +188,14 @@ export class Block { + this.connectedStateUnsubscribers = [ + this.connectedState.$anchors.subscribe(() => { this.setState({ anchors: this.connectedState.$anchors.value, }); this.shouldUpdateChildren = true; }), - this.subscribeSignal(this.connectedState.$state, () => { + this.connectedState.$state.subscribe(() => { this.setState({ ...this.connectedState.$state.value, anchors: this.connectedState.$anchors.value, @@ -202,6 +206,14 @@ export class Block unsub()); + this.connectedStateUnsubscribers = []; + // Release ownership of all ports owned by this block const connectionsList = this.context.graph.rootStore.connectionsList; diff --git a/src/react-components/Block.tsx b/src/react-components/Block.tsx index 9fad4ea8..b7994dd8 100644 --- a/src/react-components/Block.tsx +++ b/src/react-components/Block.tsx @@ -5,7 +5,7 @@ import { noop } from "lodash"; import { TBlock } from "../components/canvas/blocks/Block"; import { Graph } from "../graph"; -import { useSignalEffect } from "./hooks"; +import { useComputedSignal, useSignalEffect } from "./hooks"; import { useBlockState } from "./hooks/useBlockState"; import { cn } from "./utils/cn"; @@ -97,7 +97,7 @@ export const GraphBlock = ({ const lastStateRef = useRef({ x: 0, y: 0, width: 0, height: 0, zIndex: 0 }); const state = useBlockState(graph, block); - const viewState = state?.getViewComponent(); + const viewState = useComputedSignal(() => state?.$viewComponent.value, [state]); const [interactive, setInteractive] = useState(viewState?.isInteractive() ?? false); /** diff --git a/src/store/block/Block.ts b/src/store/block/Block.ts index f4e46f84..eaf15ddb 100644 --- a/src/store/block/Block.ts +++ b/src/store/block/Block.ts @@ -138,7 +138,7 @@ export class BlockState { ); }); - private blockView: Block; + public readonly $viewComponent = signal(undefined); constructor( public readonly store: BlockListStore, @@ -168,16 +168,16 @@ export class BlockState { public updateXY(x: number, y: number, forceUpdate = false) { this.store.updatePosition(this.id, { x, y }); if (forceUpdate) { - this.blockView.updatePosition(x, y, true); + this.$viewComponent.value?.updatePosition(x, y, true); } } public setViewComponent(blockComponent: Block) { - this.blockView = blockComponent; + this.$viewComponent.value = blockComponent; } public getViewComponent() { - return this.blockView; + return this.$viewComponent.value; } public getConnections() { From ec17b683e5b1e6cb8fe2e190bd2f0bd61f1b0fe0 Mon Sep 17 00:00:00 2001 From: draedful Date: Tue, 3 Mar 2026 00:28:31 +0300 Subject: [PATCH 2/2] ... --- e2e/page-objects/GraphPageObject.ts | 42 +++++++++--- e2e/page-objects/ReactGraphPageObject.ts | 26 ++------ e2e/tests/reload-test.spec.ts | 84 +++++------------------- e2e/tests/selection-test.spec.ts | 8 +-- 4 files changed, 57 insertions(+), 103 deletions(-) diff --git a/e2e/page-objects/GraphPageObject.ts b/e2e/page-objects/GraphPageObject.ts index 14c811aa..4b129496 100644 --- a/e2e/page-objects/GraphPageObject.ts +++ b/e2e/page-objects/GraphPageObject.ts @@ -101,17 +101,18 @@ export class GraphPageObject { } /** - * Initialize graph with config + * Returns the URL of the HTML page to navigate to for initialization. + * Override in subclasses to use a different page (e.g. /react.html). */ - async initialize(config: GraphConfig): Promise { - await this.page.goto("/base.html"); - - // Wait for Graph library to load from the HTML page - await this.page.waitForFunction(() => { - return (window as any).graphLibraryLoaded === true; - }); + protected getUrl(): string { + return "/base.html"; + } - // Initialize graph using the loaded module + /** + * Creates and configures the graph instance in the browser context. + * Override in subclasses to use a different rendering setup (e.g. React). + */ + protected async setupGraph(config: GraphConfig): Promise { await this.page.evaluate((cfg) => { const rootEl = document.getElementById("root"); if (!rootEl) { @@ -137,6 +138,20 @@ export class GraphPageObject { window.graph = graph; window.graphInitialized = true; }, config); + } + + /** + * Initialize graph with config + */ + async initialize(config: GraphConfig): Promise { + await this.page.goto(this.getUrl()); + + // Wait for Graph library to load from the HTML page + await this.page.waitForFunction(() => { + return (window as any).graphLibraryLoaded === true; + }); + + await this.setupGraph(config); // Wait for graph to be ready await this.page.waitForFunction( @@ -148,6 +163,15 @@ export class GraphPageObject { await this.waitForFrames(3); } + /** + * Set camera zoom to a specific scale level + */ + async setZoom(scale: number): Promise { + await this.page.evaluate((s) => { + window.graph.zoom({ scale: s }); + }, scale); + } + /** * Wait for N animation frames to complete using graph's scheduler * This is necessary because the library uses Scheduler with requestAnimationFrame diff --git a/e2e/page-objects/ReactGraphPageObject.ts b/e2e/page-objects/ReactGraphPageObject.ts index 184d54a6..3383de44 100644 --- a/e2e/page-objects/ReactGraphPageObject.ts +++ b/e2e/page-objects/ReactGraphPageObject.ts @@ -12,19 +12,14 @@ export class ReactGraphPageObject extends GraphPageObject { super(page); } + protected getUrl(): string { + return "/react.html"; + } + /** - * Initialize React-based graph with GraphCanvas + BlocksList - * Uses the react.html page which loads the React bundle + * Creates graph wrapped in React GraphCanvas (enables HTML block rendering via BlocksList). */ - async initialize(config: GraphConfig): Promise { - await this.page.goto("/react.html"); - - // Wait for React bundle to load - await this.page.waitForFunction(() => { - return (window as any).graphLibraryLoaded === true; - }); - - // Initialize React graph using GraphCanvas component + protected async setupGraph(config: GraphConfig): Promise { await this.page.evaluate((cfg) => { const { Graph, GraphCanvas, GraphBlock, React, ReactDOM } = (window as any).GraphModule; @@ -69,15 +64,6 @@ export class ReactGraphPageObject extends GraphPageObject { window.graph = graph; window.graphInitialized = true; }, config); - - // Wait for graph to be ready - await this.page.waitForFunction( - () => window.graphInitialized === true, - { timeout: 5000 } - ); - - // Wait for initial render - await this.waitForFrames(5); } /** diff --git a/e2e/tests/reload-test.spec.ts b/e2e/tests/reload-test.spec.ts index 77a3e9f3..09f6e972 100644 --- a/e2e/tests/reload-test.spec.ts +++ b/e2e/tests/reload-test.spec.ts @@ -1,72 +1,22 @@ import { test, expect } from "@playwright/test"; +import { ReactGraphPageObject } from "../page-objects/ReactGraphPageObject"; +import { TBlock } from "../../src/components/canvas/blocks/Block"; -test("html blocks disappear after reload with double setEntities", async ({ page }) => { - await page.goto("/react.html"); - - await page.waitForFunction(() => (window as any).graphLibraryLoaded === true); +const blocks: TBlock[] = [ + { id: "b1", is: "Block", x: 100, y: 100, width: 200, height: 100, name: "B1", anchors: [], selected: false }, + { id: "b2", is: "Block", x: 400, y: 100, width: 200, height: 100, name: "B2", anchors: [], selected: false }, +]; - const blocks = [ - { id: "b1", is: "Block", x: 100, y: 100, width: 200, height: 100, name: "B1", anchors: [], selected: false }, - { id: "b2", is: "Block", x: 400, y: 100, width: 200, height: 100, name: "B2", anchors: [], selected: false }, - ]; +test("Issue #249: html blocks disappear after reload with double setEntities", async ({ page }) => { + const graphPage = new ReactGraphPageObject(page); + await graphPage.initialize({ blocks, connections: [] }); + await graphPage.setZoom(1.0); + await graphPage.waitForFrames(10); - await page.evaluate((blocks) => { - const { Graph, GraphCanvas, GraphBlock, React, ReactDOM } = (window as any).GraphModule; - const rootEl = document.getElementById("root"); - const graph = new Graph({}, rootEl); - const reactRoot = ReactDOM.createRoot(rootEl); - - const renderBlock = (g: any, block: any) => { - return React.createElement( - GraphBlock, - { graph: g, block }, - React.createElement("div", { "data-testid": `block-${block.id}` }, block.name) - ); - }; - - reactRoot.render(React.createElement(GraphCanvas, { graph, renderBlock })); - graph.setEntities({ blocks, connections: [] }); - graph.start(); - graph.zoom({ scale: 1.0 }); - window.graph = graph; - window.graphInitialized = true; - }, blocks); - - await page.waitForFunction(() => window.graphInitialized === true); - - // Wait enough for initial render - await page.evaluate(() => new Promise(resolve => { - let n = 10; - const tick = () => { if (--n <= 0) resolve(); else requestAnimationFrame(tick); }; - requestAnimationFrame(tick); - })); - - const initialState = await page.evaluate(() => { - const els = document.querySelectorAll("[data-testid^='block-']"); - return { count: els.length, scale: window.graph.cameraService.getCameraScale() }; - }); - console.log("Initial state:", initialState); - - // Now simulate "reload" - double setEntities - await page.evaluate((blocks) => { - window.graph.setEntities({ blocks: [], connections: [] }); - window.graph.setEntities({ blocks, connections: [] }); - }, blocks); - - await page.evaluate(() => new Promise(resolve => { - let n = 20; - const tick = () => { if (--n <= 0) resolve(); else requestAnimationFrame(tick); }; - requestAnimationFrame(tick); - })); - - const afterReloadState = await page.evaluate(() => { - const els = document.querySelectorAll("[data-testid^='block-']"); - const storeBlocks = window.graph.rootStore.blocksList.$blocks.value.map((b: any) => b.id); - const scale = window.graph.cameraService.getCameraScale(); - const cameraLevel = window.graph.cameraService.getCameraBlockScaleLevel(); - return { count: els.length, storeBlocks, scale, cameraLevel }; - }); - console.log("After reload state:", afterReloadState); - - expect(afterReloadState.count).toBe(2); + // Simulate "reload" — double setEntities + await graphPage.setEntities({ blocks: [], connections: [] }); + await graphPage.setEntities({ blocks, connections: [] }); + await graphPage.waitForFrames(20); + + expect(await graphPage.getRenderedHtmlBlockCount()).toBe(2); }); diff --git a/e2e/tests/selection-test.spec.ts b/e2e/tests/selection-test.spec.ts index edece3fb..79241802 100644 --- a/e2e/tests/selection-test.spec.ts +++ b/e2e/tests/selection-test.spec.ts @@ -25,16 +25,10 @@ test.describe("Selection Test", () => { // Try selecting programmatically first await page.evaluate(() => { const blockState = window.graph.blocks.getBlockState("block-1"); - console.log("BlockState exists:", !!blockState); - console.log("SelectionService:", window.graph.selectionService); // Try to select the block using correct API const { ESelectionStrategy } = window.GraphModule; - window.graph.selectionService.select( - "block", - ["block-1"], - ESelectionStrategy.REPLACE - ); + window.graph.selectionService.select("block", ["block-1"], ESelectionStrategy.REPLACE); }); await page.waitForTimeout(200);