109 lines
3.6 KiB
TypeScript
109 lines
3.6 KiB
TypeScript
|
|
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 <Editor onRender> 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;
|
||
|
|
dom: HTMLElement | null;
|
||
|
|
displayName: string;
|
||
|
|
parent: string | null;
|
||
|
|
};
|
||
|
|
const selectNodeSpy = vi.fn();
|
||
|
|
|
||
|
|
vi.mock('@craftjs/core', () => ({
|
||
|
|
useEditor: () => ({ actions: { selectNode: selectNodeSpy } }),
|
||
|
|
useNode: (collect?: (node: any) => any) => {
|
||
|
|
const node = {
|
||
|
|
events: { selected: mockNode.selected },
|
||
|
|
dom: mockNode.dom,
|
||
|
|
data: { custom: {}, displayName: mockNode.displayName, parent: mockNode.parent },
|
||
|
|
};
|
||
|
|
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, dom: nodeDom, displayName: 'Heading', parent: 'ROOT' };
|
||
|
|
selectNodeSpy.mockClear();
|
||
|
|
});
|
||
|
|
|
||
|
|
const rendered = <span data-testid="inner">hello</span>;
|
||
|
|
|
||
|
|
describe('RenderNode (Editor onRender override)', () => {
|
||
|
|
test('passes render through untouched when not selected', () => {
|
||
|
|
render(<RenderNode render={rendered} />);
|
||
|
|
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(<RenderNode render={rendered} />);
|
||
|
|
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(<RenderNode render={rendered} />);
|
||
|
|
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(<RenderNode render={rendered} />);
|
||
|
|
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(<RenderNode render={rendered} />);
|
||
|
|
expect(document.querySelector('.component-indicator-parent-btn')).toBeNull();
|
||
|
|
container.remove();
|
||
|
|
nodeDom.remove();
|
||
|
|
document.querySelector('.component-indicator')?.remove();
|
||
|
|
});
|
||
|
|
});
|