Files
Nitro-V3/src/components/floorplan-editor/views/FloorplanCanvasSVG.test.tsx
T
medievalshell 48ed3ad7ba fix: show furniture-occupied tiles in the floor plan editor
The editor never requested occupied tiles, so tiles holding furniture
were indistinguishable from empty floor and could be edited/voided.

- request GetOccupiedTilesMessageComposer when the editor opens
- handle RoomOccupiedTilesMessageEvent -> SET_OCCUPIED_TILES
- new Tile.occupied flag (kept separate from `blocked`/void): occupied
  tiles render with a distinct marker and are protected from PAINT/
  ERASE/ADJUST and brush-to-selection edits
- occupied is purely informational and never changes the saved tilemap
  (no accidental voiding of floor under furni)

Tests: reducer cases for SET_OCCUPIED_TILES + edit protection; container
test asserts the occupied event is non-destructive on save; route the
canvas pointer test through elementFromPoint (jsdom has no getScreenCTM).
2026-05-28 09:20:20 +02:00

66 lines
3.0 KiB
TypeScript

import { describe, it, expect, vi } from 'vitest';
import { fireEvent, render } from '@testing-library/react';
import { FloorplanCanvasSVG } from './FloorplanCanvasSVG';
import { initialState } from '../state/reducer';
describe('FloorplanCanvasSVG', () =>
{
it('renders one polygon per non-blocked tile', () =>
{
const state = {
...initialState,
tiles: [
[{ h: 0, blocked: false }, { h: 1, blocked: true }],
[{ h: 2, blocked: false }, { h: 3, blocked: false }]
]
};
const { container } = render(<FloorplanCanvasSVG state={ state } dispatch={ () => {} } />);
const polys = container.querySelectorAll('polygon');
expect(polys.length).toBeGreaterThanOrEqual(3);
});
it('renders door marker on the door tile', () =>
{
const state = {
...initialState,
tiles: [[{ h: 0, blocked: false }, { h: 0, blocked: false }]],
door: { x: 1, y: 0, dir: 2 as const }
};
const { container } = render(<FloorplanCanvasSVG state={ state } dispatch={ () => {} } />);
expect(container.querySelector('[data-testid="door-marker"]')).toBeTruthy();
});
it('forwards pointer events to a tool dispatch (PAINT_TILE with brush)', () =>
{
const state = {
...initialState,
tiles: [[{ h: 0, blocked: false }]],
brush: { h: 0, action: 'SET' as const }
};
const dispatch = vi.fn();
const { container } = render(<FloorplanCanvasSVG state={ state } dispatch={ dispatch } />);
const svg = container.querySelector('svg') as SVGSVGElement;
// usePointerToTile resolves the tile via document.elementFromPoint first
// (the tile polygons carry data-row/data-col). jsdom returns null and has
// no SVGSVGElement.getScreenCTM, so point the hit-test at the tile polygon.
const tilePoly = container.querySelector('polygon[data-row="0"][data-col="0"]') as Element;
// jsdom's document has no elementFromPoint at all — define it for this test.
const prevEfp = (document as { elementFromPoint?: unknown }).elementFromPoint;
(document as unknown as { elementFromPoint: (x: number, y: number) => Element | null }).elementFromPoint = () => tilePoly;
fireEvent.pointerDown(svg, { clientX: 1024, clientY: 0, pointerId: 1 });
expect(dispatch).toHaveBeenCalled();
const call = dispatch.mock.calls[0][0];
expect(call.type).toBe('PAINT_TILE');
(document as { elementFromPoint?: unknown }).elementFromPoint = prevEfp;
});
it('zoom in/out buttons adjust the viewBox', () =>
{
const { container } = render(<FloorplanCanvasSVG state={ initialState } dispatch={ () => {} } />);
const svg = container.querySelector('svg') as SVGSVGElement;
const initialVB = svg.getAttribute('viewBox');
fireEvent.click(container.querySelector('[data-testid="zoom-in"]') as Element);
expect(svg.getAttribute('viewBox')).not.toBe(initialVB);
});
});