# -*- coding: utf-8 -*- """116-1 종단 우클릭 → 종류 고름 = 「[추가]까지 끝난 상태」. 못박는 것 - 필수 빈칸 없는 종류 = 그 측점에 바로 추가(캐시) + 새 항목을 고른 채(구조물 = 임시 id · A군 = 측점). - 필수 빈칸 남는 종류 · 노면공(확인창) = 폼만 · 세월교 · 물넘이포장은 바로 추가(126-1 · test_126). - [추가]가 거절하면 아무것도 안 듦 · 고르지 않음. - 새로 놓기에서 시작 — 옛 집계 항목 · 그룹 잇기 · 메모가 새 항목에 안 실림. - 세 길(B05 종단 · B06 종단 · 3D)이 모두 패널 `addAt` 한 곳 · 패널을 열고 「구조물 배치」를 폄. Node 헬퍼 `helper_116_right_click_add.cjs` 가 판정 · 끝맺음 · `loadForm(null)` 을 돌림. 화면은 사용자 확인. """ import json import os import re import subprocess import pytest HERE = os.path.dirname(os.path.abspath(__file__)) ROOT = os.path.abspath(os.path.join(HERE, "..", "..")) def _read(*parts: str) -> str: with open(os.path.join(ROOT, *parts), encoding="utf-8") as f: return f.read() @pytest.fixture(scope="module") def out(): proc = subprocess.run( ["node", os.path.join(HERE, "helper_116_right_click_add.cjs")], capture_output=True, text=True, encoding="utf-8", timeout=60, ) assert proc.returncode == 0, proc.stderr return json.loads(proc.stdout) def test_필수_빈칸_없는_종류만_바로_추가(out): once = out["once"] assert once["noRequired"] is True assert once["pipe"] is True and once["box"] is True assert once["requiredEmpty"] is False def test_노면공만_폼만_세월교_물넘이포장은_바로(out): once = out["once"] assert once["pavement"] is False assert once["fordBridge"] is True and once["fordPavement"] is True # 126-1 def test_구조물은_목록에_들고_새_임시_id로_고른_채(out): s = out["structure"] assert s["done"] is True and s["commits"] == 1 and s["count"] == 2 assert s["picked"] == [["id", "local1"]] # 옛 항목(old1)이 아니라 새 항목 def test_추가가_거절하면_고르지_않고_폼만(out): r = out["refused"] assert r["done"] is False and r["count"] == 1 and r["picked"] == [] def test_A군은_추가_뒤_그_측점_관을_고름(out): p = out["pipe"] assert p["done"] is True and p["commits"] == 1 assert p["picked"] == [["pipe", 120]] def test_새로_놓기가_옛_집계_항목_그룹_잇기를_비움(out): c = out["cleared"] assert c["summary"] is None and c["groups"] == {} and c["memo"] == "" def _add_at_body() -> str: src = _read("B05_Profile", "B05_Profile_UI_Structures_Panel.ts") start = src.index(" addAt(chainageM, typeId) {") return src[start : src.index(" moveById(", start)] def test_addAt_은_새로_놓기_패널_폄_후_판정(): body = _add_at_body() order = [ body.index("startNew(type.group, typeId)"), body.index("callbacks.onReveal?.()"), body.index('root.classList.remove("is-collapsed")'), body.index("addsAtOnce(type, optionInputs)"), body.index("finishAdd("), body.index("commitTempPipe()"), # 폼만 갈래 — A군 임시 배치 그대로 ] assert order == sorted(order) assert "DEFAULT_INTERVAL_LENGTH_M" in body # 구간형 기본 15 m 그대로 events = _read("B05_Profile", "B05_Profile_UI_Structures_Panel_Events.ts") new = events[ events.index("function startNew(") : events.index("ctx.groupSelect.addEventListener") ] assert "ctx.resetForm()" in new and "onListRelease" in new assert "return { startNew }" in events def test_세_길이_모두_패널_addAt_한_곳(): profile = _read("A00_Common", "design", "design_view_profile.ts") assert re.search( r"onStructureTypeAdd: \(chainage, typeId\) => panel\.structures\.addAt\(", profile ) cross = _read("A00_Common", "design", "design_view_cross.ts") assert "structuresPanel.addStructureAt(chainageM, typeId)" in cross b06 = _read("B06_Section", "B06_Section_UI_Page_Structures_Panel.ts") assert "addStructureAt: (chainageM, typeId) => section.addAt(chainageM, typeId)" in b06 viewer = _read("B05_Profile", "B05_Profile_UI_Page_Structures.ts") assert "onPick: (chainage, typeId) => deps.panel().structures.addAt(chainage, typeId)" in viewer