"""169-1 · 169-3 작도 영역 숫자 칸(`cad_host_area.ts`) · B07 도각 편집(`cad_host_frame_edit.ts`) — Node 로 TS 를 돌려 판정(가짜 DOM). 못박는 것 - 칸 = 왼쪽 · 위 · 폭 · 높이 ↔ 저장 꼴 [x0, y0, x1, y1] · 폭 · 높이 0 이하 거절(그 칸만 짚음). - 도각 편집 = 서버 영역을 칸 · 가이드(끌기 켬)로 싣고 · 끌기 → 칸 · 칸 → CAD 갈기 · [완료] 때 영역 실어 저장. """ import json import shutil import subprocess from pathlib import Path import pytest ROOT = Path(__file__).resolve().parents[2] SCRIPT = r""" const fs = require("fs"); const root = process.argv[1]; const ts = require(root + "/config/node_modules/typescript"); const load = (file, stubs) => { const src = fs.readFileSync(root + "/" + file, "utf8"); const js = ts.transpileModule(src, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, }).outputText; const box = { exports: {} }; new Function("exports", "module", "require", js)(box.exports, box, (n) => stubs[n] ?? require(n)); return box.exports; }; const node = () => ({ kids: [], hidden: false, textContent: "", className: "", disabled: false, on: {}, classList: { add() {} }, append(...x) { this.kids.push(...x); }, addEventListener(k, f) { this.on[k] = f; }, }); globalThis.document = { createElement: node }; const inputs = []; const errors = []; const elements = { showToast: () => {}, createButton: (o) => Object.assign(node(), { click: o.onClick }), createInputField: (o) => { const input = Object.assign(node(), { value: o.value }); const index = inputs.push(input) - 1; return { root: node(), input, setError: (e) => (errors[index] = e) }; }, }; const area = load("ui_template/cad_host/cad_host_area.ts", { "@ui/ui_template_elements": elements }); const edit = load("ui_template/cad_host/cad_host_frame_edit.ts", { "@ui/ui_template_elements": elements, "./cad_host_area": area, }); (async () => { const out = {}; out.toFields = area.areaToFields([42, 47, 812, 567]); out.toArea = area.fieldsToArea([42, 567, 770, 520]); out.zero = area.fieldsToArea([0, 10, 0, 5]); const loads = []; const guides = []; const saved = []; const editor = edit.createFrameTemplateEditor({ api: { fetch: async () => ({ drawing: { entities: [], drawing_area: [10, 20, 410, 320] }, customized: false }), save: async (drawing) => saved.push(drawing), importFile: async () => ({ drawing: { entities: [] }, entity_count: 0 }), reset: async () => {}, }, sendLoad: (drawing, meta, frameEdit, fields, extra) => loads.push(extra), setGuideArea: (a) => guides.push(a), requestCadDrawing: async () => ({ entities: [{ id: "x" }] }), restoreDrawing: () => {}, onSaved: () => {}, currentDrawingInfo: () => null, }); const fields = inputs.slice(-4); await editor.button.click(); out.loaded = loads[0]; out.shown = fields.map((f) => f.value); editor.guideChanged([10, 20, 300.04, 320]); out.dragged = fields.map((f) => f.value); fields[3].value = "100"; fields[3].on.input(); out.typed = guides.slice(); fields[2].value = "0"; fields[2].on.input(); out.zeroError = errors.slice(-4); fields[2].value = "290"; fields[2].on.input(); await editor.banner.kids[3].kids[0].click(); // [완료] out.saved = saved; console.log(JSON.stringify(out)); })(); """ @pytest.fixture(scope="module") def result() -> dict: node = shutil.which("node") if not node: pytest.skip("node 없음") proc = subprocess.run( [node, "-e", SCRIPT, str(ROOT)], capture_output=True, text=True, encoding="utf-8" ) assert proc.returncode == 0, proc.stderr return json.loads(proc.stdout.strip().splitlines()[-1]) def test_fields_round_trip(result): assert result["toFields"] == [42, 567, 770, 520] assert result["toArea"] == [42, 47, 812, 567] assert result["zero"] is None def test_frame_edit_loads_area_and_guide(result): assert result["loaded"] == {"guide": {"area": [10, 20, 410, 320], "editable": True}} assert result["shown"] == ["10", "320", "400", "300"] # 끌기 → 칸(0.1 mm) assert result["dragged"] == ["10", "320", "290", "300"] def test_typing_updates_guide_and_flags_zero(result): # 높이 100 → [10, 220, 300, 320] (끌기 값 300.04 는 칸에서 300 으로) assert result["typed"] == [[10, 220, 300, 320]] assert result["zeroError"][2] == "0 보다 커야 함" and result["zeroError"][3] is None def test_finish_saves_area_with_drawing(result): assert result["saved"] == [{"entities": [{"id": "x"}], "drawing_area": [10, 220, 300, 320]}]