"""PLAN 295 — 횡단 카드 · [상세] 모달 고르기 차례 · 모달 첫 보기 · 구조물 선 안 나눔(294-5 포함). - 295-1 카드 = 판정면 넓이 큰 것부터 아래(작은 것 위) · 보라 선(사면 손잡이) 맨 위 · 띠 있는 성토 사면 누름 = 사면 · 끌기 = 고름 아님 / 모달 `pickAt` = 선 > 작은 면 > 큰 면 · 띠(띠는 영역보다 작을 때만) - 295-2 모달 첫 보기 = 중심점이 칸 가운데 · 그림 전체가 들어가게 축마다 큰 쪽 반폭 - 295-3 모달 구조물 선 = 다른 선을 나누는 기준 그대로 · 고를 땐 한 선 통째 · 닫힌 영역 그대로 · 옛 조각 다시 고름 실제 TS 를 `helper_295_pick_order_detail.cjs` 가 Node 로 돌린다. """ from __future__ import annotations import json import os import subprocess import pytest HERE = os.path.dirname(__file__) ROOT = os.path.join(HERE, "..", "..") B06 = os.path.join(ROOT, "B06_Section") pytestmark = pytest.mark.skipif( not os.path.isdir(os.path.join(ROOT, "config", "node_modules")), reason="프론트엔드 의존성 미설치", ) def _read(name: str) -> str: with open(os.path.join(B06, name), encoding="utf-8") as f: return f.read().replace(chr(13), "") @pytest.fixture(scope="module") def out() -> dict: proc = subprocess.run( # noqa: S603 — 고정 실행 파일 ["node", os.path.join(HERE, "helper_295_pick_order_detail.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): # 띠(14400) > 벽(4000) > 형상(100) > 관(25) · 보라 선 둘 맨 위 · 면적 띠는 맨 아래 그대로 assert out["cardOrder"] == ["band", "chute", "wall", "shape", "pipe", "slope", "top"] assert out["area"] == 200 def test_카드_띠_있는_성토_사면_누름은_사면_294_5(out): assert out["slopeClickOld"] == "chute" # 옛 차례 = 띠가 사면을 가림 assert out["slopeClick"] == "slope" assert out["chuteClick"] == "chute" # 선 밖 띠 자리는 띠 assert out["pipeOverWall"] == "pipe" def test_카드_끌기는_고름_아님(out): assert out["dragStopped"] is True assert out["clickStopped"] is False def test_모달_선_작은_면_큰_면_띠(out): m = out["modal"] assert m["line"] == {"piece": 4} assert m["small"] == {"face": 1} assert m["big"] == {"face": 0} assert m["bandBigger"] == {"face": 1} # 띠보다 좁은 영역이 앞(옛 「띠가 앞」 폐기) assert m["bandSmaller"] == {"band": True} assert m["bandBigFace"] == {"band": True} assert m["bandOnly"] == {"band": True} assert m["lineOverBand"] == {"piece": 4} def test_모달_첫_보기_중심점_가운데(out): h = out["home"] assert h["center"] == pytest.approx(h["plotCenter"]) assert h["scale"] == pytest.approx(140 / 210) # 아래로 먼 쪽 210 px 이 반높이에 assert h["fits"] is True assert h["flat"] == pytest.approx(2) # 세로가 낮으면 가로(카드 반폭) 배율 def test_모달_구조물_선_한_조각(out): s = out["structure"] assert s["groundPieces"] == 3 # 구조물 선이 원지반선을 나눔(기준 그대로) assert s["members"] == 2 and s["chosen"] == 2 # 나뉜 조각 둘 = 한 선 통째 assert s["again"] == 0 assert s["additiveOff"] == [99] assert s["label"].startswith("구조물 선 길이 10.00m") assert s["wholeValue"] == pytest.approx(10) assert out["collected"] == [ {"line": "design", "group": None}, {"line": "design", "group": None}, {"line": "structure", "group": 1}, {"line": "structure", "group": 1}, ] def test_모달_닫힌_영역_옛_조각(out): s = out["structure"] assert s["faces"] == [50, 50] assert s["oldPick"] == 1 def test_배선(): view = _read("B06_Section_UI_Cross_View.ts") assert view.index("setLineActive(pickedLineAt(") < view.index("stackPickTargets(plotLayer)") select = " ".join(_read("B06_Section_UI_Cross_Detail_Select.ts").split()) assert "groupMembers(pieces, pick.piece)" in select assert 'pick && "face" in pick' in select table = _read("B06_Section_UI_Cross_Detail_Table.ts") assert 'pointsArea(hit.getAttribute("points"))' in table detail = " ".join(_read("B06_Section_UI_Cross_Detail.ts").split()) assert "detailHomeView(layerPoints(layer), [cx, cy], plot, homeScale, 16)" in detail for name in ("B06_Section_UI_Cross_View.ts", "B06_Section_UI_Cross_Detail_Select.ts"): assert len(_read(name).splitlines()) <= 700 def test_보라_선_첫_누름은_그_선_295_4(): """안 골라진 카드의 성토 사면선 첫 누름 — 남은 부재키(도수로 띠)를 먼저 풀어 카드 고름이 되살리지 않음.""" proc = subprocess.run( # noqa: S603 — 고정 실행 파일 ["node", os.path.join(HERE, "helper_294_7_ford_slab_pick.cjs")], capture_output=True, text=True, encoding="utf-8", timeout=60, ) assert proc.returncode == 0, proc.stderr first = json.loads(proc.stdout)["firstLine"] assert first["oldKey"] == "chute" # 옛 길 = 카드 고름이 도수로 띠 부재키를 지킴 → 선 풀림 assert first["newKey"] is None view = " ".join(_read("B06_Section_UI_Cross_View.ts").split()) assert "if (!isSelected) { // 첫 누름 = 이 선" in view assert ( "if (activeRevet === null) revetOffset?.select(section.chainage_m, null); onSelect(section.station_id);" in view )