/** * CAD → 부모 표 보내기(`integration/aislo-table-export.ts` · M02 83-2) 검증. * 실행: cd B07_DesignDetail/openwebcad && npx vitest run --dir ../../resources/tester/cad table-export * (시험 파일은 바깥 라이브러리를 직접 부르지 않음 — 엔티티는 fromJson 으로 만듦) */ import { beforeEach, describe, expect, it, vi } from "vitest"; import { getCommandById, isViewOnlyCommand, } from "../../../B07_DesignDetail/openwebcad/src/commands/registry.ts"; import type { JsonEntity } from "../../../B07_DesignDetail/openwebcad/src/entities/Entity.ts"; import { LineEntity } from "../../../B07_DesignDetail/openwebcad/src/entities/LineEntity.ts"; import { TextEntity } from "../../../B07_DesignDetail/openwebcad/src/entities/TextEntity.ts"; import { AISLO_EXPORT_TABLE_MESSAGE, AISLO_EXPORT_TABLE_REQUEST_MESSAGE, handleTableExportMessage, selectionTable, } from "../../../B07_DesignDetail/openwebcad/src/integration/aislo-table-export.ts"; import { getEntities, setEntities, setLayers, setSelectedEntityIds, } from "../../../B07_DesignDetail/openwebcad/src/state.ts"; const layer = { id: "layer-1", isLocked: false, isVisible: true, name: "작업" }; const line = (x1: number, y1: number, x2: number, y2: number) => LineEntity.fromJson({ id: crypto.randomUUID(), type: "Line", lineColor: "#fff", lineWidth: 1, layerId: layer.id, shapeData: { startPoint: { x: x1, y: y1 }, endPoint: { x: x2, y: y2 } }, } as unknown as JsonEntity); const text = (label: string, x: number, y: number) => TextEntity.fromJson({ id: crypto.randomUUID(), type: "Text", lineColor: "#fff", lineWidth: 1, layerId: layer.id, shapeData: { label, basePoint: { x, y }, options: { textDirection: { x: 1, y: 0 }, textAlign: "left", textColor: "#fff", fontSize: 2.5, fontFamily: "Noto Sans KR", }, }, } as unknown as JsonEntity); /** 2×2 선 표 + 글 넷 + 표 밖 선 하나(고르지 않음) */ async function drawing() { const parts = await Promise.all([ line(0, 20, 40, 20), line(0, 10, 40, 10), line(0, 0, 40, 0), line(0, 0, 0, 20), line(20, 0, 20, 20), line(40, 0, 40, 20), text("항목", 2, 14), text("수량", 22, 14), text("흙깎기", 2, 4), text("1,500", 22, 4), ]); const outside = await line(100, 100, 200, 100); setEntities([...parts, outside]); return { parts, outside }; } beforeEach(() => { setLayers([layer]); setEntities([]); setSelectedEntityIds([]); }); describe("고른 범위 → 표 자료", () => { it("고른 선 + 글만 표로 · 숫자 글은 숫자", async () => { const { parts } = await drawing(); setSelectedEntityIds(parts.map((entity) => entity.id)); const table = await selectionTable(); expect(table).toMatchObject({ kind: "grid", rows: [ ["항목", "수량"], ["흙깎기", 1500], ], colWidths: [20, 20], }); }); it("고른 것 없음 = empty · 글 없는 선 = no-text", async () => { const { outside } = await drawing(); expect(await selectionTable()).toBe("empty"); setSelectedEntityIds([outside.id]); expect(await selectionTable()).toBe("no-text"); }); }); describe("부모 요청 메시지", () => { it("export-table-request = 고른 범위를 export-table{table} 로 회신 · 도면은 그대로", async () => { const { parts } = await drawing(); setSelectedEntityIds(parts.map((entity) => entity.id)); const before = getEntities(); const notify = vi.fn(); expect( await handleTableExportMessage({ type: AISLO_EXPORT_TABLE_REQUEST_MESSAGE }, notify), ).toBe(true); expect(notify).toHaveBeenCalledTimes(1); const [type, payload] = notify.mock.calls[0]; expect(type).toBe(AISLO_EXPORT_TABLE_MESSAGE); expect(payload.table.rows[1]).toEqual(["흙깎기", 1500]); expect(getEntities()).toBe(before); }); it("고른 것이 없으면 회신 안 함 · 다른 메시지는 false", async () => { await drawing(); const notify = vi.fn(); expect( await handleTableExportMessage({ type: AISLO_EXPORT_TABLE_REQUEST_MESSAGE }, notify), ).toBe(true); expect(notify).not.toHaveBeenCalled(); expect(await handleTableExportMessage({ type: "aislo:b08:save-request" }, notify)).toBe(false); expect(await handleTableExportMessage(null, notify)).toBe(false); }); }); describe("CAD 명령 TOAREA", () => { it("고른 것이 있어야 하고 · 확정(보기 전용) 도면에서도 됨", () => { const command = getCommandById("TOAREA"); expect(command?.label).toBe("면적산출로 보내기"); expect(command?.needsSelection).toBe(true); expect(command && isViewOnlyCommand(command)).toBe(true); }); });