import { describe, test, expect, vi, beforeEach } from 'vitest'; import React from 'react'; import { createRoot, Root } from 'react-dom/client'; import { act } from 'react-dom/test-utils'; /* Same DOM-harness pattern as Footer.editguard.test.tsx: mock @craftjs/core so RenderNode (the override) can be driven without a real Editor tree. document.body doubles as the portal target, same as the component itself uses. */ let mockNode: { id: string; selected: boolean; hovered: boolean; dom: HTMLElement | null; displayName: string; parent: string | null; isCanvas: boolean; }; const selectNodeSpy = vi.fn(); vi.mock('@craftjs/core', () => ({ useEditor: () => ({ actions: { selectNode: selectNodeSpy } }), useNode: (collect?: (node: any) => any) => { const node = { events: { selected: mockNode.selected, hovered: mockNode.hovered }, dom: mockNode.dom, data: { custom: {}, displayName: mockNode.displayName, parent: mockNode.parent, isCanvas: mockNode.isCanvas, }, }; return { id: mockNode.id, ...(collect ? collect(node) : {}) }; }, })); import { RenderNode } from './RenderNode'; let container: HTMLDivElement; let root: Root; let nodeDom: HTMLElement; function render(ui: React.ReactElement) { container = document.createElement('div'); document.body.appendChild(container); act(() => { root = createRoot(container); root.render(ui); }); } beforeEach(() => { nodeDom = document.createElement('div'); document.body.appendChild(nodeDom); mockNode = { id: 'node-1', selected: false, hovered: false, dom: nodeDom, displayName: 'Heading', parent: 'ROOT', isCanvas: false, }; selectNodeSpy.mockClear(); }); const rendered = hello; describe('RenderNode (Editor onRender override)', () => { test('passes render through untouched when not selected', () => { render(); expect(container.querySelector('[data-testid="inner"]')).not.toBeNull(); expect(document.querySelector('.component-indicator')).toBeNull(); container.remove(); nodeDom.remove(); }); test('shows the badge with the displayName when selected', () => { mockNode.selected = true; render(); const badge = document.querySelector('.component-indicator'); expect(badge).not.toBeNull(); expect(badge?.textContent).toContain('Heading'); container.remove(); nodeDom.remove(); document.querySelector('.component-indicator')?.remove(); }); test('never shows a badge for ROOT even if "selected"', () => { mockNode.selected = true; mockNode.id = 'ROOT'; render(); expect(document.querySelector('.component-indicator')).toBeNull(); container.remove(); nodeDom.remove(); }); test('chevron click selects the parent node', () => { mockNode.selected = true; mockNode.parent = 'parent-42'; render(); const chevron = document.querySelector('.component-indicator-parent-btn') as HTMLElement; expect(chevron).not.toBeNull(); act(() => { chevron.dispatchEvent(new MouseEvent('mousedown', { bubbles: true })); }); expect(selectNodeSpy).toHaveBeenCalledWith('parent-42'); container.remove(); nodeDom.remove(); document.querySelector('.component-indicator')?.remove(); }); test('no chevron when there is no parent', () => { mockNode.selected = true; mockNode.parent = null; render(); expect(document.querySelector('.component-indicator-parent-btn')).toBeNull(); container.remove(); nodeDom.remove(); document.querySelector('.component-indicator')?.remove(); }); test('tags a droppable (isCanvas) node dom with data-craft-node', () => { mockNode.isCanvas = true; render(); expect(nodeDom.hasAttribute('data-craft-node')).toBe(true); container.remove(); nodeDom.remove(); }); test('does not tag a non-canvas (leaf) node dom with data-craft-node', () => { mockNode.isCanvas = false; render(); expect(nodeDom.hasAttribute('data-craft-node')).toBe(false); container.remove(); nodeDom.remove(); }); test('never tags ROOT with data-craft-node even though ROOT is a canvas', () => { mockNode.id = 'ROOT'; mockNode.isCanvas = true; render(); expect(nodeDom.hasAttribute('data-craft-node')).toBe(false); container.remove(); nodeDom.remove(); }); test('tags the dom with data-craft-hovered when the Craft hovered event is set (Layers panel hover sync)', () => { mockNode.hovered = true; render(); expect(nodeDom.hasAttribute('data-craft-hovered')).toBe(true); container.remove(); nodeDom.remove(); }); test('removes data-craft-hovered once the hovered event clears', () => { mockNode.hovered = true; render(); expect(nodeDom.hasAttribute('data-craft-hovered')).toBe(true); mockNode.hovered = false; act(() => { root.render(); }); expect(nodeDom.hasAttribute('data-craft-hovered')).toBe(false); container.remove(); nodeDom.remove(); }); });