- 노면공 포장층 강조 = 포장층을 직접 누른 턴만(paveShapeId) · 알약 · 목록 · 측점선 고름은 범위 테두리만 - 물넘이 콘크리트(pipe-…)는 측점 길 강조(highlightShapes)에서 뺌 — 형상 누름 「관」 고름만 켬 · 맹암거 그대로 - 벽 고름 「관」에 콘크리트 표식(형상 누름 ownerM · 관 없는 카드) · 카드 「관」 setter 를 관 그림 / 콘크리트로 가름(splitPipePick) - 연동 관 카드 콘크리트 누름 = 관 형상 안 켬 · 콘크리트 ↔ 관 바꿔 누르면 풀지 않고 옮김 · 좌측 하위 컨테이너 켬 그대로 - 측점 없는 구조물 = 카드 고름 · 스크롤 없음(revealStation 스크롤 걷음 · 넘김 focusPicked) - 시험: test_307_pick_highlight 새로 · 118 · 177_4 · 287 · 294_6 · 294_7 기대값 307 규칙으로 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014utrjfoW831Vc5wJUd13kL
166 lines
8.8 KiB
Python
166 lines
8.8 KiB
Python
"""307 B06 고름 강조 — 측점 고름은 형상 안 켬 · 연동 관 = 콘크리트만 · 측점 없는 구조물 = 카드 · 스크롤 없음.
|
|
|
|
공통(「관」 setter 가름 · 벽 고름 콘크리트 표식)은 진짜 TS 를 Node 로 돌려 보고(helper_307),
|
|
길마다(측점선 · 알약 · 좌측 목록 측점 · 영역 × 노면공 · 물넘이포장 · 연동 관 · 측점 없는 구조물)는
|
|
그 길이 모이는 자리의 배선을 본다.
|
|
"""
|
|
|
|
import json
|
|
import re
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
HELPER = Path(__file__).with_name("helper_307_pick_highlight.cjs")
|
|
|
|
|
|
def _read(*parts: str) -> str:
|
|
return (ROOT.joinpath(*parts)).read_text(encoding="utf-8")
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def out() -> dict:
|
|
run = subprocess.run(
|
|
["node", str(HELPER)], capture_output=True, text=True, encoding="utf-8", cwd=ROOT
|
|
)
|
|
assert run.returncode == 0, run.stderr
|
|
return json.loads(run.stdout)
|
|
|
|
|
|
# ── 공통 ───────────────────────────────────────────────────────────────
|
|
|
|
|
|
def test_공통_관_setter_콘크리트와_관_가름(out):
|
|
split = out["split"]
|
|
assert split["concretePipe"] == {"base": [None], "shape": [True]} # 콘크리트만
|
|
assert split["pipePipe"] == {"base": ["pipe"], "shape": [False]} # 관만
|
|
assert split["concreteWall"] == {"base": ["inlet"], "shape": [False]} # 다른 벽 그대로
|
|
assert split["none"] == {"base": [None], "shape": [False]}
|
|
|
|
|
|
def test_공통_벽고름_콘크리트_표식(out):
|
|
cases = out["revetCases"]
|
|
assert cases["pipeOnPipeCard"] == {"shape": False, "selected": "pipe"}
|
|
assert cases["concreteOnPipeCard"] == {"shape": True, "selected": "pipe"}
|
|
assert cases["concreteOnOwnCard"] == {"shape": True, "selected": "pipe"}
|
|
assert cases["handoffOnOwnCard"] == {"shape": True, "selected": "pipe"}
|
|
assert cases["wall"] == {"shape": False, "selected": "inlet"}
|
|
assert cases["released"] == {"shape": False, "selected": None}
|
|
assert cases["changes"] == 6 # 고를 때마다 연동 카드 다시 칠함(그대로)
|
|
|
|
|
|
# ── 물넘이포장 × 측점 길 넷 ────────────────────────────────────────────
|
|
|
|
|
|
def test_물넘이포장_측점길은_콘크리트_안켬(out):
|
|
# 측점선 · 알약 · 좌측 목록 측점 · 영역 = 끝에 `selectMark("pipe-…")` → highlightShapes
|
|
assert out["byStationPath"] == {"concreteOff": False, "concreteLitKept": True}
|
|
view = _read("B06_Section", "B06_Section_UI_Section_View.ts")
|
|
assert "highlightShapes(card, pickedMarkId);" in view
|
|
shape = _read("B06_Section", "B06_Section_UI_Cross_Shape_Pick.ts")
|
|
assert 'if (id.startsWith("pipe-")) continue;' in shape
|
|
# 네 길이 모이는 자리 — 카드 고름 리스너 · 목록 관 줄 · 폼 관 열기 · 측점 없는 관
|
|
cross = _read("A00_Common", "design", "design_view_cross.ts")
|
|
assert "sectionView.selectMark(mark, false);" in cross
|
|
panel = _read("B06_Section", "B06_Section_UI_Page_Structures_Panel.ts")
|
|
assert "deps.markStructure?.(`pipe-${hit.pipeChainageM.toFixed(2)}`);" in panel # 카드 → 폼
|
|
assert "deps.markStructure?.(chainageM === null ? null : `pipe-${" in panel # 목록 관 줄
|
|
# 측점 길은 「관」 벽 고름을 안 엶 — 카드만 고르면 벽 고름을 비움(그대로)
|
|
assert "stationControls.revetOffset.select(target.chainage_m, null);" in cross
|
|
|
|
|
|
def test_물넘이포장_형상누름은_콘크리트(out):
|
|
view = _read("B06_Section", "B06_Section_UI_Cross_View.ts")
|
|
assert 'shapePickRoute(svg, (at) => toggleRevet("pipe", at))' in view
|
|
assert "splitPipePick(setRevetActive, setShapeActive, shapePicked)" in view
|
|
assert "setRevetActive(highlightRevet);" in view
|
|
assert out["revetCases"]["concreteOnOwnCard"]["shape"] is True
|
|
|
|
|
|
# ── 노면공 × 측점 길 넷 ──────────────────────────────────────────────
|
|
|
|
|
|
def test_노면공_포장층은_포장층_누름만():
|
|
view = _read("B06_Section", "B06_Section_UI_Section_View.ts")
|
|
assert "const pave = span && isPavement(spanMarkId) && paveShapeId === spanMarkId;" in view
|
|
# 알약 · 목록 · 측점 길 = 누름 턴 밖 selectMark → 포장층 표식 풂
|
|
assert "if (!paveShapeTurn) paveShapeId = null;" in view
|
|
# 포장층 누름 = 턴 안에서 목록 고름 길을 타고 · 끝나면 그 노면공 포장층만 켬
|
|
grid = re.search(
|
|
r"const pave = pavementAt\(event\.target, spanMarks\);(.*?)\n \},", view, re.S
|
|
)
|
|
assert grid, "포장층 누름 처리 없음"
|
|
body = grid.group(1)
|
|
assert body.index("paveShapeTurn = true;") < body.index("pavementSelectListener?.(")
|
|
assert body.index("paveShapeTurn = false;") < body.index("paveShapeId = pave.structure_id;")
|
|
assert body.index("paveShapeId = pave.structure_id;") < body.index("syncSpanHighlights();")
|
|
# 범위 테두리(--span)는 그대로 — 다른 구간형 구조물과 같은 길
|
|
assert 'card.classList.toggle("b06-cross-card--span", span);' in view
|
|
assert "spanMarkId = span || isPavement(structureId) ? structureId : null;" in view
|
|
|
|
|
|
# ── 연동 관 ───────────────────────────────────────────────────────────
|
|
|
|
|
|
def test_연동관_콘크리트누름은_관형상_안켬(out):
|
|
assert out["revetCases"]["concreteOnPipeCard"] == {"shape": True, "selected": "pipe"}
|
|
assert out["split"]["concretePipe"]["base"] == [None] # 관 그림 setter 엔 「관」 안 감
|
|
select = _read("B06_Section", "B06_Section_UI_Page_Structures_Selection.ts")
|
|
assert "origRevet(chainageM, key, ownerM);" in select
|
|
view = _read("B06_Section", "B06_Section_UI_Cross_View.ts")
|
|
# 콘크리트 ↔ 관 그림 바꿔 누름 = 풀지 않고 옮김 · 같은 것 다시 누름 = 풂
|
|
assert (
|
|
"activeRevet = activeRevet === key && (ownerM !== undefined) === shapePicked() ? null : key;"
|
|
in view
|
|
)
|
|
# 좌측 하위 컨테이너 켬(경사각도 상자)은 그대로 — 「관」 벽 고름 길
|
|
dock = _read("B06_Section", "B06_Section_UI_Adjust_Dock.ts")
|
|
assert 'side === "pipe" && (!own || own.closest("[hidden]")) ? visibleBox(SLOPE_BOX)' in dock
|
|
|
|
|
|
# ── 측점 없는 구조물 ──────────────────────────────────────────────────
|
|
|
|
|
|
def test_측점없는_구조물_카드_스크롤_없음():
|
|
view = _read("B06_Section", "B06_Section_UI_Section_View.ts")
|
|
body = re.search(r"revealStation\(\) \{(.*?)\n \},", view, re.S)
|
|
assert body, "revealStation 없음"
|
|
assert "revealCard" not in body.group(1)
|
|
assert "markCard(previous);" in body.group(1) # 옛 카드는 풂
|
|
panel = _read("B06_Section", "B06_Section_UI_Page_Structures_Panel.ts")
|
|
# B06 알약 · 좌측 목록 = 폼 onSelect · 측점 없는 관 = selectPipeAt — 둘 다 revealOnly 길
|
|
assert (
|
|
"const revealOnly = !coverable(structure) || !plantsStation(structure, markTypes);" in panel
|
|
)
|
|
assert "deps.focusChainage(pipe.chainage_m, true);" in panel
|
|
# B05 알약 · 3D · 지도 넘김 = focusPicked — 측점 없는 구조물은 카드 고름을 안 부름
|
|
focus = re.search(r"focusPicked: \(structureId, focus\) => \{(.*?)\n \},", panel, re.S)
|
|
assert focus, "focusPicked 없음"
|
|
assert "if (!known || plantsStation(known, markTypes)) {" in focus.group(1)
|
|
assert "if (known) section.selectById(structureId);" in focus.group(1)
|
|
cross = _read("A00_Common", "design", "design_view_cross.ts")
|
|
assert "structuresPanel.focusPicked(handoff?.structureId ?? null, () =>" in cross
|
|
profile = _read("B06_Section", "B06_Section_UI_Section_View_Profile.ts")
|
|
assert "edit.selectStructure(hit.structure_id);" in profile
|
|
|
|
|
|
# ── 지킬 것 ───────────────────────────────────────────────────────────
|
|
|
|
|
|
def test_맹암거_형상강조_그대로(out):
|
|
assert out["drainLit"] is True
|
|
assert out["afterRelease"] == {"drain": False, "concreteLit": True}
|
|
|
|
|
|
def test_줄_한도():
|
|
assert len(_read("A00_Common", "design", "design_view_cross.ts").splitlines()) <= 685
|
|
for name in (
|
|
"B06_Section_UI_Cross_View.ts",
|
|
"B06_Section_UI_Section_View.ts",
|
|
"B06_Section_UI_Page_Station_Controls.ts",
|
|
"B06_Section_UI_Page_Structures_Panel.ts",
|
|
):
|
|
assert len(_read("B06_Section", name).splitlines()) <= 700, name
|