2026-06-26 16:20:09 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* Component Tests – RibbonBar, Topbar, StatusBar, LayerPanel, PropertiesPanel
|
|
|
|
|
|
* Testet Rendering, Button-Klicks und Callback-Aufrufe.
|
|
|
|
|
|
*/
|
|
|
|
|
|
import { describe, it, expect, vi } from 'vitest';
|
|
|
|
|
|
import { render, screen, fireEvent } from '@testing-library/react';
|
|
|
|
|
|
import RibbonBar from '../src/components/RibbonBar';
|
|
|
|
|
|
import Topbar from '../src/components/Topbar';
|
|
|
|
|
|
import StatusBar from '../src/components/StatusBar';
|
|
|
|
|
|
import LayerPanel from '../src/components/LayerPanel';
|
|
|
|
|
|
import PropertiesPanel from '../src/components/PropertiesPanel';
|
|
|
|
|
|
import type { CADElement, CADLayer } from '../src/types/cad.types';
|
|
|
|
|
|
|
|
|
|
|
|
// ─── Fixtures ────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
function makeLayer(overrides: Partial<CADLayer> = {}): CADLayer {
|
|
|
|
|
|
return {
|
|
|
|
|
|
id: 'layer-1',
|
|
|
|
|
|
name: 'Layer 1',
|
|
|
|
|
|
visible: true,
|
|
|
|
|
|
locked: false,
|
|
|
|
|
|
color: '#ffffff',
|
|
|
|
|
|
lineType: 'solid',
|
|
|
|
|
|
transparency: 0,
|
|
|
|
|
|
sortOrder: 0,
|
|
|
|
|
|
parentId: null,
|
|
|
|
|
|
...overrides,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function makeElement(overrides: Partial<CADElement> = {}): CADElement {
|
|
|
|
|
|
return {
|
|
|
|
|
|
id: 'elem-1',
|
|
|
|
|
|
type: 'rect',
|
|
|
|
|
|
layerId: 'layer-1',
|
|
|
|
|
|
x: 10,
|
|
|
|
|
|
y: 20,
|
|
|
|
|
|
width: 100,
|
|
|
|
|
|
height: 50,
|
|
|
|
|
|
properties: {},
|
|
|
|
|
|
...overrides,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ─── RibbonBar ────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
describe('RibbonBar', () => {
|
|
|
|
|
|
it('should render all tab buttons', () => {
|
|
|
|
|
|
render(<RibbonBar activeTab="start" onTabChange={() => {}} onAction={() => {}} />);
|
|
|
|
|
|
// Tabs are in a tablist - use role=tab
|
|
|
|
|
|
const tabs = screen.getAllByRole('tab');
|
|
|
|
|
|
expect(tabs.length).toBeGreaterThanOrEqual(3);
|
|
|
|
|
|
expect(screen.getByText('Start')).toBeInTheDocument();
|
|
|
|
|
|
// 'Einfügen' appears as tab AND action - use getAllByText
|
|
|
|
|
|
const einfuegen = screen.getAllByText('Einfügen');
|
|
|
|
|
|
expect(einfuegen.length).toBeGreaterThanOrEqual(1);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('should call onTabChange when clicking a tab', () => {
|
|
|
|
|
|
const onTabChange = vi.fn();
|
|
|
|
|
|
render(<RibbonBar activeTab="start" onTabChange={onTabChange} onAction={() => {}} />);
|
|
|
|
|
|
// Click the 'Format' tab (unique text)
|
|
|
|
|
|
fireEvent.click(screen.getByText('Format'));
|
|
|
|
|
|
expect(onTabChange).toHaveBeenCalledWith('format');
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('should call onAction when clicking an action button', () => {
|
|
|
|
|
|
const onAction = vi.fn();
|
|
|
|
|
|
render(<RibbonBar activeTab="start" onTabChange={() => {}} onAction={onAction} />);
|
|
|
|
|
|
// Find action buttons by class name
|
|
|
|
|
|
const actionBtns = document.querySelectorAll('.ribbon-btn');
|
|
|
|
|
|
if (actionBtns.length > 0) {
|
|
|
|
|
|
fireEvent.click(actionBtns[0]);
|
|
|
|
|
|
expect(onAction).toHaveBeenCalled();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// ─── Topbar ───────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
describe('Topbar', () => {
|
|
|
|
|
|
it('should render project name and saved status', () => {
|
|
|
|
|
|
render(
|
|
|
|
|
|
<Topbar projectName="Test Project" savedStatus="Gespeichert" onUndo={() => {}} onRedo={() => {}} onThemeToggle={() => {}} theme="dark" />,
|
|
|
|
|
|
);
|
|
|
|
|
|
expect(screen.getByText('Test Project')).toBeInTheDocument();
|
|
|
|
|
|
expect(screen.getByText('Gespeichert')).toBeInTheDocument();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('should call onUndo when clicking undo button', () => {
|
|
|
|
|
|
const onUndo = vi.fn();
|
|
|
|
|
|
render(
|
|
|
|
|
|
<Topbar projectName="P" savedStatus="" onUndo={onUndo} onRedo={() => {}} onThemeToggle={() => {}} theme="dark" />,
|
|
|
|
|
|
);
|
|
|
|
|
|
fireEvent.click(screen.getByLabelText(/Rückgängig/i));
|
|
|
|
|
|
expect(onUndo).toHaveBeenCalled();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('should call onRedo when clicking redo button', () => {
|
|
|
|
|
|
const onRedo = vi.fn();
|
|
|
|
|
|
render(
|
|
|
|
|
|
<Topbar projectName="P" savedStatus="" onUndo={() => {}} onRedo={onRedo} onThemeToggle={() => {}} theme="dark" />,
|
|
|
|
|
|
);
|
|
|
|
|
|
fireEvent.click(screen.getByLabelText(/Wiederherstellen/i));
|
|
|
|
|
|
expect(onRedo).toHaveBeenCalled();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('should call onThemeToggle when clicking theme button', () => {
|
|
|
|
|
|
const onThemeToggle = vi.fn();
|
|
|
|
|
|
render(
|
|
|
|
|
|
<Topbar projectName="P" savedStatus="" onUndo={() => {}} onRedo={() => {}} onThemeToggle={onThemeToggle} theme="dark" />,
|
|
|
|
|
|
);
|
|
|
|
|
|
fireEvent.click(screen.getByLabelText(/Hell.*Dunkel|Theme/i));
|
|
|
|
|
|
expect(onThemeToggle).toHaveBeenCalled();
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// ─── StatusBar ────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
describe('StatusBar', () => {
|
|
|
|
|
|
const defaultProps = {
|
|
|
|
|
|
snapEnabled: true,
|
|
|
|
|
|
orthoEnabled: false,
|
|
|
|
|
|
polarEnabled: true,
|
|
|
|
|
|
gridEnabled: false,
|
|
|
|
|
|
cursorX: 123.456,
|
|
|
|
|
|
cursorY: 78.9,
|
|
|
|
|
|
activeLayer: 'Layer 1',
|
|
|
|
|
|
activeTool: 'line',
|
|
|
|
|
|
onlineCount: 3,
|
|
|
|
|
|
onToggleSnap: vi.fn(),
|
|
|
|
|
|
onToggleOrtho: vi.fn(),
|
|
|
|
|
|
onTogglePolar: vi.fn(),
|
|
|
|
|
|
onToggleGrid: vi.fn(),
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
it('should render cursor coordinates', () => {
|
|
|
|
|
|
render(<StatusBar {...defaultProps} />);
|
2026-07-04 16:43:55 +02:00
|
|
|
|
// Default unit is mm, scaleFactor=1: 123.456 → '123', 78.9 → '79'
|
2026-06-26 16:20:09 +02:00
|
|
|
|
expect(screen.getByText(/123/)).toBeInTheDocument();
|
2026-07-04 16:43:55 +02:00
|
|
|
|
expect(screen.getByText(/79/)).toBeInTheDocument();
|
2026-06-26 16:20:09 +02:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('should render active layer and tool', () => {
|
|
|
|
|
|
render(<StatusBar {...defaultProps} />);
|
|
|
|
|
|
expect(screen.getByText('Layer 1')).toBeInTheDocument();
|
|
|
|
|
|
expect(screen.getByText('line')).toBeInTheDocument();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('should render online count', () => {
|
|
|
|
|
|
render(<StatusBar {...defaultProps} />);
|
|
|
|
|
|
// Online count is in a div with title containing 'online'
|
|
|
|
|
|
const onlineEl = screen.getByTitle(/online/i);
|
|
|
|
|
|
expect(onlineEl).toBeInTheDocument();
|
|
|
|
|
|
expect(onlineEl.textContent).toContain('3');
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('should call onToggleSnap when clicking snap toggle', () => {
|
|
|
|
|
|
const onToggleSnap = vi.fn();
|
|
|
|
|
|
render(<StatusBar {...defaultProps} onToggleSnap={onToggleSnap} />);
|
|
|
|
|
|
// StatusBar uses div with title attribute, not button with aria-label
|
|
|
|
|
|
fireEvent.click(screen.getByTitle(/Snap-Modus/i));
|
|
|
|
|
|
expect(onToggleSnap).toHaveBeenCalled();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('should call onToggleOrtho when clicking ortho toggle', () => {
|
|
|
|
|
|
const onToggleOrtho = vi.fn();
|
|
|
|
|
|
render(<StatusBar {...defaultProps} onToggleOrtho={onToggleOrtho} />);
|
|
|
|
|
|
fireEvent.click(screen.getByTitle(/Ortho-Modus/i));
|
|
|
|
|
|
expect(onToggleOrtho).toHaveBeenCalled();
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// ─── LayerPanel ──────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
describe('LayerPanel', () => {
|
|
|
|
|
|
const layers = [
|
|
|
|
|
|
makeLayer({ id: 'layer-1', name: 'Wände' }),
|
|
|
|
|
|
makeLayer({ id: 'layer-2', name: 'Türen', visible: false }),
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
it('should render layer names', () => {
|
|
|
|
|
|
render(
|
|
|
|
|
|
<LayerPanel
|
|
|
|
|
|
layers={layers}
|
|
|
|
|
|
onSelectLayer={() => {}}
|
|
|
|
|
|
onAddLayer={() => {}}
|
|
|
|
|
|
onToggleLayer={() => {}}
|
|
|
|
|
|
/>,
|
|
|
|
|
|
);
|
|
|
|
|
|
expect(screen.getByText('Wände')).toBeInTheDocument();
|
|
|
|
|
|
expect(screen.getByText('Türen')).toBeInTheDocument();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('should call onAddLayer when clicking add button', () => {
|
|
|
|
|
|
const onAddLayer = vi.fn();
|
|
|
|
|
|
render(
|
|
|
|
|
|
<LayerPanel
|
|
|
|
|
|
layers={layers}
|
|
|
|
|
|
onSelectLayer={() => {}}
|
|
|
|
|
|
onAddLayer={onAddLayer}
|
|
|
|
|
|
onToggleLayer={() => {}}
|
|
|
|
|
|
/>,
|
|
|
|
|
|
);
|
|
|
|
|
|
// Add button has class 'add-layer-btn'
|
|
|
|
|
|
const addBtn = document.querySelector('.add-layer-btn');
|
|
|
|
|
|
expect(addBtn).toBeTruthy();
|
|
|
|
|
|
fireEvent.click(addBtn!);
|
|
|
|
|
|
expect(onAddLayer).toHaveBeenCalled();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('should call onSelectLayer when clicking a layer', () => {
|
|
|
|
|
|
const onSelectLayer = vi.fn();
|
|
|
|
|
|
render(
|
|
|
|
|
|
<LayerPanel
|
|
|
|
|
|
layers={layers}
|
|
|
|
|
|
activeLayerId="layer-1"
|
|
|
|
|
|
onSelectLayer={onSelectLayer}
|
|
|
|
|
|
onAddLayer={() => {}}
|
|
|
|
|
|
onToggleLayer={() => {}}
|
|
|
|
|
|
/>,
|
|
|
|
|
|
);
|
|
|
|
|
|
fireEvent.click(screen.getByText('Wände'));
|
|
|
|
|
|
expect(onSelectLayer).toHaveBeenCalledWith('layer-1');
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('should call onToggleLayer when toggling visibility', () => {
|
|
|
|
|
|
const onToggleLayer = vi.fn();
|
|
|
|
|
|
render(
|
|
|
|
|
|
<LayerPanel
|
|
|
|
|
|
layers={layers}
|
|
|
|
|
|
onSelectLayer={() => {}}
|
|
|
|
|
|
onAddLayer={() => {}}
|
|
|
|
|
|
onToggleLayer={onToggleLayer}
|
|
|
|
|
|
/>,
|
|
|
|
|
|
);
|
|
|
|
|
|
// Visibility toggle has aria-label 'Verstecken' (visible) or 'Anzeigen' (hidden)
|
|
|
|
|
|
const toggleBtn = screen.getByLabelText('Verstecken');
|
|
|
|
|
|
fireEvent.click(toggleBtn);
|
|
|
|
|
|
expect(onToggleLayer).toHaveBeenCalled();
|
|
|
|
|
|
});
|
2026-07-04 16:43:55 +02:00
|
|
|
|
// Helper: expand a tree node by clicking its toggle button
|
|
|
|
|
|
function expandLayer(label: string) {
|
|
|
|
|
|
const layerNode = screen.getByText(label).closest('.tree-node');
|
|
|
|
|
|
expect(layerNode).toBeTruthy();
|
|
|
|
|
|
const toggle = layerNode?.querySelector('.tree-toggle');
|
|
|
|
|
|
expect(toggle).toBeTruthy();
|
|
|
|
|
|
fireEvent.click(toggle!);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
it('should highlight selected element in tree when selectedElementIds is provided', () => {
|
|
|
|
|
|
const layers = [makeLayer({ id: 'layer-1', name: 'Layer 1' })];
|
|
|
|
|
|
const elements = [
|
|
|
|
|
|
makeElement({ id: 'elem-1', type: 'rect', layerId: 'layer-1' }),
|
|
|
|
|
|
makeElement({ id: 'elem-2', type: 'circle', layerId: 'layer-1' }),
|
|
|
|
|
|
];
|
|
|
|
|
|
const { container } = render(
|
|
|
|
|
|
<LayerPanel
|
|
|
|
|
|
layers={layers}
|
|
|
|
|
|
elements={elements}
|
|
|
|
|
|
selectedElementIds={['elem-2']}
|
|
|
|
|
|
activeLayerId="layer-1"
|
|
|
|
|
|
onSelectLayer={() => {}}
|
|
|
|
|
|
onAddLayer={() => {}}
|
|
|
|
|
|
onToggleLayer={() => {}}
|
|
|
|
|
|
/>,
|
|
|
|
|
|
);
|
|
|
|
|
|
expandLayer('Layer 1');
|
|
|
|
|
|
// The element node for elem-2 should have the 'active' class
|
|
|
|
|
|
const treeNodes = container.querySelectorAll('.tree-node');
|
|
|
|
|
|
const elemNode = Array.from(treeNodes).find((n) => n.textContent?.includes('Kreis'));
|
|
|
|
|
|
expect(elemNode).toBeTruthy();
|
|
|
|
|
|
expect(elemNode?.classList.contains('active')).toBe(true);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('should call onSelectElement when clicking an element in the tree', () => {
|
|
|
|
|
|
const layers = [makeLayer({ id: 'layer-1', name: 'Layer 1' })];
|
|
|
|
|
|
const elements = [
|
|
|
|
|
|
makeElement({ id: 'elem-1', type: 'rect', layerId: 'layer-1' }),
|
|
|
|
|
|
];
|
|
|
|
|
|
const onSelectElement = vi.fn();
|
|
|
|
|
|
render(
|
|
|
|
|
|
<LayerPanel
|
|
|
|
|
|
layers={layers}
|
|
|
|
|
|
elements={elements}
|
|
|
|
|
|
activeLayerId="layer-1"
|
|
|
|
|
|
onSelectLayer={() => {}}
|
|
|
|
|
|
onSelectElement={onSelectElement}
|
|
|
|
|
|
onAddLayer={() => {}}
|
|
|
|
|
|
onToggleLayer={() => {}}
|
|
|
|
|
|
/>,
|
|
|
|
|
|
);
|
|
|
|
|
|
expandLayer('Layer 1');
|
|
|
|
|
|
fireEvent.click(screen.getByText('Rechteck'));
|
|
|
|
|
|
expect(onSelectElement).toHaveBeenCalledWith('elem-1');
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('should render groups as tree nodes with their elements as children', () => {
|
|
|
|
|
|
const layers = [makeLayer({ id: 'layer-1', name: 'Layer 1' })];
|
|
|
|
|
|
const elements = [
|
|
|
|
|
|
makeElement({ id: 'elem-1', type: 'rect', layerId: 'layer-1' }),
|
|
|
|
|
|
makeElement({ id: 'elem-2', type: 'circle', layerId: 'layer-1' }),
|
|
|
|
|
|
];
|
|
|
|
|
|
const groups = [
|
|
|
|
|
|
{ id: 'group-1', name: 'Gruppe A', elementIds: ['elem-1', 'elem-2'], parentGroupId: null },
|
|
|
|
|
|
];
|
|
|
|
|
|
render(
|
|
|
|
|
|
<LayerPanel
|
|
|
|
|
|
layers={layers}
|
|
|
|
|
|
elements={elements}
|
|
|
|
|
|
groups={groups}
|
|
|
|
|
|
activeLayerId="layer-1"
|
|
|
|
|
|
onSelectLayer={() => {}}
|
|
|
|
|
|
onAddLayer={() => {}}
|
|
|
|
|
|
onToggleLayer={() => {}}
|
|
|
|
|
|
/>,
|
|
|
|
|
|
);
|
|
|
|
|
|
expandLayer('Layer 1');
|
|
|
|
|
|
expect(screen.getByText('Gruppe A')).toBeInTheDocument();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('should select all group elements when clicking a group node', () => {
|
|
|
|
|
|
const layers = [makeLayer({ id: 'layer-1', name: 'Layer 1' })];
|
|
|
|
|
|
const elements = [
|
|
|
|
|
|
makeElement({ id: 'elem-1', type: 'rect', layerId: 'layer-1' }),
|
|
|
|
|
|
makeElement({ id: 'elem-2', type: 'circle', layerId: 'layer-1' }),
|
|
|
|
|
|
];
|
|
|
|
|
|
const groups = [
|
|
|
|
|
|
{ id: 'group-1', name: 'Gruppe A', elementIds: ['elem-1', 'elem-2'], parentGroupId: null },
|
|
|
|
|
|
];
|
|
|
|
|
|
const onSelectElement = vi.fn();
|
|
|
|
|
|
render(
|
|
|
|
|
|
<LayerPanel
|
|
|
|
|
|
layers={layers}
|
|
|
|
|
|
elements={elements}
|
|
|
|
|
|
groups={groups}
|
|
|
|
|
|
activeLayerId="layer-1"
|
|
|
|
|
|
onSelectLayer={() => {}}
|
|
|
|
|
|
onSelectElement={onSelectElement}
|
|
|
|
|
|
onAddLayer={() => {}}
|
|
|
|
|
|
onToggleLayer={() => {}}
|
|
|
|
|
|
/>,
|
|
|
|
|
|
);
|
|
|
|
|
|
expandLayer('Layer 1');
|
|
|
|
|
|
fireEvent.click(screen.getByText('Gruppe A'));
|
|
|
|
|
|
expect(onSelectElement).toHaveBeenCalledWith('elem-1');
|
|
|
|
|
|
});
|
2026-06-26 16:20:09 +02:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// ─── PropertiesPanel ─────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
describe('PropertiesPanel', () => {
|
|
|
|
|
|
const layers = [makeLayer({ id: 'layer-1', name: 'Layer 1' })];
|
|
|
|
|
|
const element = makeElement({ id: 'elem-1', type: 'rect', x: 10, y: 20, width: 100, height: 50 });
|
|
|
|
|
|
|
2026-07-04 16:43:55 +02:00
|
|
|
|
it('should show empty inputs when no element selected', () => {
|
2026-06-26 16:20:09 +02:00
|
|
|
|
render(<PropertiesPanel selectedElement={null} layers={layers} onUpdateProperty={() => {}} />);
|
2026-07-04 16:43:55 +02:00
|
|
|
|
// When no element is selected, input fields are empty
|
|
|
|
|
|
expect(screen.getByLabelText('Position X')).toHaveDisplayValue('');
|
|
|
|
|
|
expect(screen.getByLabelText('Position Y')).toHaveDisplayValue('');
|
2026-06-26 16:20:09 +02:00
|
|
|
|
});
|
|
|
|
|
|
|
2026-07-04 16:43:55 +02:00
|
|
|
|
it('should display element coordinates in mm by default', () => {
|
2026-06-26 16:20:09 +02:00
|
|
|
|
render(<PropertiesPanel selectedElement={element} layers={layers} onUpdateProperty={() => {}} />);
|
2026-07-04 16:43:55 +02:00
|
|
|
|
// Default unit is mm with scaleFactor=1, so world units are displayed as mm
|
|
|
|
|
|
expect(screen.getByLabelText('Position X')).toHaveDisplayValue('10');
|
|
|
|
|
|
expect(screen.getByLabelText('Position Y')).toHaveDisplayValue('20');
|
2026-06-26 16:20:09 +02:00
|
|
|
|
});
|
|
|
|
|
|
|
2026-07-04 16:43:55 +02:00
|
|
|
|
it('should display element dimensions in mm by default', () => {
|
2026-06-26 16:20:09 +02:00
|
|
|
|
render(<PropertiesPanel selectedElement={element} layers={layers} onUpdateProperty={() => {}} />);
|
2026-07-04 16:43:55 +02:00
|
|
|
|
expect(screen.getByLabelText('Breite')).toHaveDisplayValue('100');
|
|
|
|
|
|
expect(screen.getByLabelText('Tiefe')).toHaveDisplayValue('50');
|
2026-06-26 16:20:09 +02:00
|
|
|
|
});
|
|
|
|
|
|
|
2026-07-04 16:43:55 +02:00
|
|
|
|
it('should call onUpdateProperty with world-unit number when changing a property input', () => {
|
2026-06-26 16:20:09 +02:00
|
|
|
|
const onUpdateProperty = vi.fn();
|
|
|
|
|
|
render(<PropertiesPanel selectedElement={element} layers={layers} onUpdateProperty={onUpdateProperty} />);
|
|
|
|
|
|
fireEvent.change(screen.getByLabelText('Position X'), { target: { value: '999' } });
|
2026-07-04 16:43:55 +02:00
|
|
|
|
// In mm mode with scaleFactor=1, input '999' → 999 world units
|
|
|
|
|
|
expect(onUpdateProperty).toHaveBeenCalledWith('x', 999);
|
2026-06-26 16:20:09 +02:00
|
|
|
|
});
|
|
|
|
|
|
});
|