"""PLAN 118-1 — B06 배관 횡단도 관 고름 → 관 두 겹 강조 · 좌측 「관」 상자 강조 · 조정창 없음. 짝: `B06_Section/B06_Section_UI_Cross_Culvert.ts`(관 판정면 · 강조) · `_Cross_Structure_Panel.ts`(show "pipe") · `_Adjust_Dock.ts`(side "pipe") · `_Page_Structures_Panel.ts`(`keyOf`) · `_Page_Structure_Pick.ts`(3D pipe 키). 실제 TS 를 `helper_118_pipe_pick.cjs` 가 Node 로 돌린다. """ from __future__ import annotations import json import os import subprocess import pytest HERE = os.path.dirname(__file__) ROOT = os.path.join(HERE, "..", "..") pytestmark = pytest.mark.skipif( not os.path.isdir(os.path.join(ROOT, "config", "node_modules")), reason="프론트엔드 의존성 미설치", ) @pytest.fixture(scope="module") def out() -> dict: proc = subprocess.run( # noqa: S603 — 고정 실행 파일 ["node", os.path.join(HERE, "helper_118_pipe_pick.cjs")], capture_output=True, text=True, encoding="utf-8", timeout=60, ) assert proc.returncode == 0, proc.stderr return json.loads(proc.stdout) def _src(rel: str) -> str: with open(os.path.join(ROOT, rel), encoding="utf-8") as f: return f.read() def test_관을_누르면_관을_고름(out) -> None: overlay = out["overlay"] assert overlay["firstPicked"] == ["pipe"] assert overlay["stopped"] is True # 카드 고름으로 안 번짐 assert overlay["hitMatchesOuter"] is True # 판정면 = 외경 def test_관_판정면은_벽_아래(out) -> None: """겹친 자리는 벽이 이김 — 관 판정면이 벽 · 집수정 판정면보다 먼저 얹힘.""" order = out["overlay"]["hitOrder"] assert order[0] == "pipe" assert order.count("pipe") == 1 and len(order) > 1 def test_관_고름은_관_두_겹만_강조(out) -> None: overlay = out["overlay"] assert overlay["pipeCount"] == 2 assert overlay["onPipe"]["pipes"] == [True, True] assert not any(overlay["onPipe"]["walls"]) # 벽을 고르면 관 강조는 꺼짐 — 동시 강조 없음 assert not any(overlay["onOutlet"]["pipes"]) and any(overlay["onOutlet"]["walls"]) assert not any(overlay["onNone"]["pipes"] + overlay["onNone"]["walls"]) def test_관을_숨긴_카드엔_관_판정면_없음(out) -> None: """링크 · 독립 흙막이 · 세월교 · 박스 측점(hidden_pipe) — 관은 세월교 오버레이 몫.""" assert out["hiddenPipe"]["pipes"] == 0 assert "pipe" not in out["hiddenPipe"]["picked"] def test_관_강조는_주인_측점_카드만(out) -> None: stands = out["stands"] assert stands["self"] is True assert stands["neighbour"] is False assert stands["outletNeighbour"] is True # 벽 연동은 그대로 def test_3D_관_고름이_B06_으로_이어짐(out) -> None: assert out["pick"]["pipe"] == [["revet", "pipe"]] assert out["pick"]["basin"] == [] assert out["pick"]["fordPipe"] == [] # 세월교 측점 = 세월교 창 몫 def test_관은_조정창_없음(out) -> None: panel = out["panel"] assert panel["outlet"] == {"hidden": False, "side": "outlet"} assert panel["pipe"] == {"hidden": True, "side": "pipe", "touched": []} assert panel["none"] == {"hidden": True, "side": ""} def test_좌측_관_상자_강조_스크롤() -> None: dock = _src("B06_Section/B06_Section_UI_Adjust_Dock.ts") assert 'if (side === "body" || side === "pipe") {' in dock assert "'fieldset[data-spec-container=\"pipe\"]'" in dock assert 'body.classList.add("is-active");' in dock assert 'if (scroll) body.scrollIntoView({ block: "nearest" });' in dock # 관 상자 칸을 고치면 관을 고름 — 횡단에서 누른 것과 같은 길 page = _src("B06_Section/B06_Section_UI_Page_Structures_Selection.ts") assert 'if (id === "inlet" || id === "outlet" || id === "pipe") return id;' in page css = _src("B06_Section/B06_Section_UI_Style_Cross_Areas.css") assert ".b06-chart__culvert-pipe.is-active {" in css def test_벽_본체_동시_고름_금지_그대로() -> None: """107-1 — 관도 같은 고름 저장소라 세월교 · 박스 고름과 함께 서지 않음.""" station = _src("B06_Section/B06_Section_UI_Page_Station_Controls.ts") assert "let revetSelection: { at: string; key: RevetPick } | null = null;" in station assert ( "if (key && ford.control.selectedFor(chainageM)) ford.control.select(chainageM, null);" in station ) view = _src("B06_Section/B06_Section_UI_Cross_View.ts") assert "const toggleRevet = (key: RevetPick): void => {" in view assert "if (activeRevet !== null) toggleRevet(activeRevet);" in view