Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014utrjfoW831Vc5wJUd13kL
140 lines
5.5 KiB
Python
140 lines
5.5 KiB
Python
"""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
|
|
)
|