Files
Aislo/resources/tester/test_169_area_fields.py
T
eomsangdonandClaude Opus 5.5 3e5b7cf21f feat(b07): 도면 작도 영역 가이드 선 · 숫자 칸 공용 부품 · 끌기 · 도각 편집 영역 저장 (169-1·169-3·169-4)
- 그림칸 = 양식 drawing_area 그대로(여백 2 % 없앰) · 도각 편집이 영역 싣고 [완료] 때 저장
- 도면 응답 guide(작도 영역 도면 좌표) · M02 구조물 도각 응답 area
- CAD 그리기 겹판 점선 사각형(저장 · DXF · 인쇄 제외) · 모서리 · 변 끌기(도각 편집 · M02 양식만)
- 숫자 칸 공용 부품(왼쪽 · 위 · 폭 · 높이 mm) — M02 양식 · B07 도각 편집 · 끌기와 한 벌
- B08 구조물도 · M02 도면 카드 = 얹은 도각 영역 가이드(보기만)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TNMXVCZXMpHpVvqmFkeChL
2026-10-10 08:35:31 +09:00

130 lines
4.6 KiB
Python

"""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]}]