import { beforeAll, describe, expect, it } from 'vitest'; import type { DesignMeta } from './App.types'; import type { Entity } from './entities/Entity'; import { clearDrawingDirty, getActiveLayerId, getEntities, getHighlightedEntityIds, isDrawingDirty, isDrawingReadOnly, resetUndoBaseline, setActiveLayerId, setDesignMeta, setEntities, setHighlightedEntityIds, setLayers, undo, } from './state'; const layer = (id: string, isLocked: boolean) => ({ id, name: id, isVisible: true, isLocked, }); /** 노드 환경에는 window가 없다 — 상태 알림을 받는 시늉만 시킨다. */ const stubWindow = () => { (globalThis as { window?: unknown }).window = { dispatchEvent: () => true }; }; const entityOnLayer = (id: string, layerId: string) => ({ id, layerId }) as unknown as Entity; const meta = (confirmed: boolean): DesignMeta => ({ kind: 'cross', drawingId: 'cross_s00020m', title: '1장', info: '', confirmed, quantityTable: null, hasPrev: false, hasNext: false, }); describe('setActiveLayerId', () => { it('잠금 도면층(배경)은 현재 도면층이 되지 않고 잠금 아닌 첫 층으로 간다', () => { setLayers([layer('도각', true), layer('계획선', false)]); setActiveLayerId('도각'); expect(getActiveLayerId()).toBe('계획선'); }); it('잠금 아닌 층이 하나도 없으면 요청값을 그대로 둔다', () => { setLayers([layer('도각', true)]); setActiveLayerId('도각'); expect(getActiveLayerId()).toBe('도각'); }); }); describe('resetUndoBaseline', () => { beforeAll(stubWindow); it('기준선을 세운 뒤의 되돌리기는 실은 도면까지만 물러난다', () => { const loaded = [{ id: 'a' } as unknown as Entity]; setEntities(loaded, false); resetUndoBaseline(); setEntities([...loaded, { id: 'b' } as unknown as Entity], true); undo(); expect(getEntities()).toHaveLength(1); }); }); describe('setHighlightedEntityIds', () => { it('잠금 도면층(도각·등고선) 객체는 마우스가 가도 밝아지지 않는다', () => { setLayers([layer('도각', true), layer('계획선', false)]); setEntities([entityOnLayer('배경', '도각'), entityOnLayer('내선', '계획선')], false); setHighlightedEntityIds(['배경', '내선']); expect(getHighlightedEntityIds()).toEqual(['내선']); }); it('잠금층만 가리키면 아무것도 밝아지지 않는다', () => { setLayers([layer('도각', true)]); setEntities([entityOnLayer('배경', '도각')], false); setHighlightedEntityIds(['배경']); expect(getHighlightedEntityIds()).toEqual([]); }); }); describe('미저장 표시(drawingDirty)', () => { beforeAll(stubWindow); it('편집하면 서고, 도면을 새로 실으면(기준선 재설정) 내려간다', () => { setEntities([entityOnLayer('a', 'L')], false); resetUndoBaseline(); expect(isDrawingDirty()).toBe(false); setEntities([entityOnLayer('a', 'L'), entityOnLayer('b', 'L')], true); expect(isDrawingDirty()).toBe(true); resetUndoBaseline(); expect(isDrawingDirty()).toBe(false); }); it('되돌리기도 미저장이다 — 저장본과 달라질 수 있다', () => { setEntities([entityOnLayer('a', 'L')], false); resetUndoBaseline(); setEntities([entityOnLayer('a', 'L'), entityOnLayer('b', 'L')], true); clearDrawingDirty(); undo(); expect(isDrawingDirty()).toBe(true); }); }); describe('isDrawingReadOnly', () => { beforeAll(stubWindow); it('확정한 도면은 읽기 전용, 확정을 풀면 편집이 열린다', () => { setDesignMeta(meta(true)); expect(isDrawingReadOnly()).toBe(true); setDesignMeta(meta(false)); expect(isDrawingReadOnly()).toBe(false); setDesignMeta(null); expect(isDrawingReadOnly()).toBe(false); }); });