- 315-1 fordChuteLine — 길이 맞춘 선의 첫 조각만 쓰던 흠(원지반 이음 조각 버림) · 이은 조각까지 한 선 · 길이 없음 · 짧은 길이 · 절토 쪽 그대로 - 315-2 overflowLine — 성토 쪽(노면 끝 밖 설계선이 내려감) = 성토 사면선을 노면 끝 너머 위로 연장해 월류 높이선과 만나는 자리에서 끝 · 절토 · 늘인 노면 끝(294-3) 그대로 · 같은 점선 양식(B06 카드 · B07 [상세] 같은 그림) - 시험 test_315_ford_chute_overflow 새로 · test_287 월류선 기대값 315 규칙 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TteK8mfYZetk5P3bEZkyVZ
166 lines
7.8 KiB
Python
166 lines
7.8 KiB
Python
"""287 물넘이포장 후속(요구 183-2 · 4 · 6 · 7 · 8 · 9).
|
|
|
|
못박는 것
|
|
- 301-1 도수로/돌붙임 면적 = 늘 손 입력(요구 192-2 · 287-1 자동 셈 걷음) — 길이 · 월류 폭을 바꾸거나
|
|
길이를 비워도 면적 그대로 · 길이 = 그림 전용(294-4).
|
|
- 287-3 월류 폭 → 높이 자동 = 세월교 한 벌(`fordHeightM` · 폼 `overflow(…, "slope")`) · 유량 없으면 손 입력.
|
|
- 287-5 물넘이포장 형상 누름 = 다른 시설과 같은 고름 창구(`toggleRevet("pipe")` → 폼 · 알약 · 측점선) ·
|
|
판정면은 보라 손잡이 아래(요구 185 ①) · 끌기(3 px 넘게 움직임) = 고름 아님(②) · 노면공 포장층
|
|
(범위 표식)은 형상 고름 밖(③) · 목록 · 알약 고름이 형상 강조를 따라옴.
|
|
- 287-2 · 287-4 콘크리트 윗면 = 설계 노면(서버 · TS 짝이 경사각도로 지대 낮은 쪽 한 경사 · 중심점이 그 위)
|
|
· 포장층 = 노면공 포장 형상 노견까지(노면공 항목과 안 섞임) · 월류 높이 점선 = 노면 위 같은 각도 ·
|
|
3D 노면을 파지 않음 · 설계 측구 없음.
|
|
|
|
진짜 TS 는 `helper_287_ford_pavement.cjs` 가 Node 로 돌림. 화면은 사용자 확인.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
HERE = Path(__file__).resolve().parent
|
|
ROOT = HERE.parents[1]
|
|
if str(ROOT) not in sys.path:
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
pytestmark = pytest.mark.skipif(
|
|
not (ROOT / "config" / "node_modules" / "typescript").is_dir(),
|
|
reason="프론트엔드 의존성 미설치",
|
|
)
|
|
|
|
|
|
def _read(*parts: str) -> str:
|
|
return (ROOT.joinpath(*parts)).read_text(encoding="utf-8")
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def ts() -> dict:
|
|
proc = subprocess.run(
|
|
["node", str(HERE / "helper_287_ford_pavement.cjs")],
|
|
capture_output=True,
|
|
text=True,
|
|
encoding="utf-8",
|
|
timeout=60,
|
|
)
|
|
assert proc.returncode == 0, proc.stderr
|
|
return json.loads(proc.stdout)
|
|
|
|
|
|
def test_도수로_면적은_손_입력_그대로(ts):
|
|
# 길이 4 넣음 · 월류 폭 8 · 길이 비움 — 면적은 손 입력 17 그대로
|
|
assert ts["areaKeptPave"] == ["17", "17", "17"]
|
|
assert ts["areaKeptFord"] == ["17", "17", "17"]
|
|
|
|
|
|
def test_폼이_길이_면적을_잇지_않는다():
|
|
form = _read("B05_Profile", "B05_Profile_UI_Drainage_Facility.ts")
|
|
assert "syncChuteArea" not in form
|
|
assert "onChange: fordFlowBox.onChange," in form
|
|
assert "onChange: paveFlowBox.onChange," in form
|
|
calc = _read("A00_Common", "design", "design_structure_ford_calc.ts")
|
|
assert "syncChuteArea" not in calc and "chuteAreaM2" not in calc
|
|
|
|
|
|
def _pts(text: str) -> list[float]:
|
|
return [float(v) for pair in text.split() for v in pair.split(",")]
|
|
|
|
|
|
def test_포장층_노견까지_점선은_노면_위(ts):
|
|
assert ts["drew"] is True
|
|
# 포장층 하나 = 노견 끝(±3)까지 · 윗면 = 설계 노면 · 두께 0.2
|
|
assert len(ts["pavePoints"]) == 1
|
|
assert _pts(ts["pavePoints"][0]) == pytest.approx([3, 100.15, -3, 99.85, -3, 99.65, 3, 99.95])
|
|
# 월류 높이선 = 노면 + 0.4 · 같은 각도 · 우(성토) = 성토선 연장과 만난 자리 → 노면 끝(315-2)
|
|
d = 0.4 / (2.65 / 4 - 0.3 / 6)
|
|
meet = [-3 + d, 99.85 + 2.65 / 4 * d]
|
|
assert _pts(ts["planPoints"][0]) == pytest.approx([3, 100.55, *meet, -3, 99.85])
|
|
assert ts["inflowLeft"] is True # 노면 높은 쪽 = 유입 · 도수로 띠 = 낮은 쪽
|
|
|
|
|
|
def test_3D_노면을_파지_않고_옛_파임_그림을_걷음():
|
|
station = _read("B05_Profile", "B05_Profile_UI_Corridor_Station.ts")
|
|
assert "fordDeckElevationAt" not in station
|
|
pave = _read("B06_Section", "B06_Section_UI_Cross_Ford_Pavement.ts")
|
|
assert "hideFordPavedRoad" not in pave and "fordDeckElevationAt" not in pave
|
|
view = _read("B06_Section", "B06_Section_UI_Cross_View.ts")
|
|
assert "hideFordPavedRoad" not in view
|
|
|
|
|
|
def test_월류_폭_넣으면_높이_자동_세월교_한_벌(ts):
|
|
assert ts["paveSlopeItems"] == ["angle", "width", "height"]
|
|
assert ts["heightByWidth"] and ts["heightByWidth"] > 0
|
|
assert ts["heightNoFlow"] is None
|
|
form = _read("B05_Profile", "B05_Profile_UI_Drainage_Facility.ts")
|
|
assert 'if (facility === "ford_pavement") paveFlowBox.fill();' in form
|
|
|
|
|
|
def test_형상_판정면은_보라_손잡이_아래(ts):
|
|
order = ts["shapeOrder"]
|
|
# 그림 → 형상 판정면 → 보라 손잡이 판정 → 구조물 판정면(위가 이김)
|
|
assert (
|
|
order.index("b06-chart__shape-hit")
|
|
< order.index("b06-chart__design-hit")
|
|
< order.index("b06-chart__culvert-revet-hit")
|
|
)
|
|
|
|
|
|
def test_형상_누름은_고름_끌기는_고름_아님(ts):
|
|
assert ts["picked"] == ["pipe-230.00"] # 누름 한 번만 고름 · 끌기는 안 셈
|
|
assert ts["clickStops"] is True and ts["dragStops"] is False # 끌기는 카드로 그대로
|
|
assert ts["pavedShapeIsPicked"] is False # 노면공 포장층은 형상 고름 밖(포장 고름 몫)
|
|
|
|
|
|
def test_형상_강조와_표식(ts):
|
|
assert ts["activeAfterSet"] is True
|
|
# 물넘이 콘크리트는 「관」 고름(형상 누름) 몫 — 측점 길 강조(highlightShapes)가 끄지도 켜지도 않음(307)
|
|
assert ts["activeAfterOther"] is True and ts["activeAfterSame"] is True
|
|
assert ts["fordTag"] == ["pipe-230.00"] # 포장층 표식 = 관 지점 알약 id
|
|
|
|
|
|
def test_보라_손잡이는_계획고만_형상_누름은_고름만():
|
|
view = _read("B06_Section", "B06_Section_UI_Cross_View.ts")
|
|
# 형상 누름 = 시설 고름 창구만(설계 · 계획고를 안 건드림) · 보라 선 고름보다 먼저 꽂음(손잡이가 위)
|
|
assert 'shapePickRoute(svg, (at) => toggleRevet("pipe", at))' in view
|
|
assert view.index("attachShapePick(") < view.index("attachDesignLinePick({")
|
|
pick = _read("B06_Section", "B06_Section_UI_Cross_Shape_Pick.ts")
|
|
assert "onDesignChange" not in pick and "design_elevation" not in pick
|
|
assert '"b06-chart__culvert-revet-hit"' in pick # 구조물 판정면 앞(아래)에 꽂을 기준만
|
|
assert 'hit.setAttribute("class", "b06-chart__shape-hit")' in pick
|
|
# 보라 손잡이 판정 = 계획고 · 사면 이동 창구(설계선 고름 · 구조물 판정면 아래)
|
|
design_pick = _read("B06_Section", "B06_Section_UI_Cross_Design_Pick.ts")
|
|
assert 'el.classList.contains("b06-chart__culvert-revet-hit")' in design_pick
|
|
# 목록 · 알약 · 폼 고름 → 형상 강조
|
|
section_view = _read("B06_Section", "B06_Section_UI_Section_View.ts")
|
|
assert "highlightShapes(card, pickedMarkId);" in section_view
|
|
selection = _read("B06_Section", "B06_Section_UI_Page_Structures_Selection.ts")
|
|
assert "sectionAt.ford_pavement?.chainage_m" in selection
|
|
|
|
|
|
def test_월류_폭_높이_화면_길_유량_있음(ts):
|
|
got = ts["overflowFlow"]
|
|
assert got["afterWidth8"] == "0.26" # 폭 8 → 필요 수심(유량 14.58 ㎥/s)
|
|
assert got["afterClear"] == "0.26" # 높이 비움 → 다시 채움
|
|
assert got["afterLow"] == "0.26" # 필요 수심 아래 → 끌어올림
|
|
assert got["afterHigh"] == "0.9" # 올려 적은 값은 그대로
|
|
assert got["hint"] == ["필요 수심 0.26 이상"] and got["otherBoxHint"] == 0
|
|
assert got["emitted"] == 4
|
|
|
|
|
|
def test_월류_폭_높이_화면_길_유량_없음(ts):
|
|
got = ts["overflowNoFlow"]
|
|
assert got["afterWidth8"] == "0.05" and got["afterClear"] == "" # 손 입력 그대로
|
|
assert got["hint"] == ["설계유량 없음 — 수기 입력"]
|
|
|
|
|
|
def test_세월교_물넘이_한_벌_안내_훅():
|
|
form = _read("B05_Profile", "B05_Profile_UI_Drainage_Facility.ts")
|
|
flat = " ".join(form.split())
|
|
assert 'overflowSync( () => fordForm, "ford", () => fordFlow, emit, )' in flat
|
|
assert 'overflowSync( () => paveForm, "slope", () => fordFlow, emit, )' in flat
|
|
assert "...fordFlowBox.fields(c)" in form and "fields: paveFlowBox.fields," in form
|