- 치수 글 두 줄(사면길이 · 수평 / 높이 · 구배 · 이동) · 좁은 카드는 면적표 아래 높이로 - [◀ ▶] 지움 → 공용 조정창 꼴 이동 패널(◀ ↺ ▶ · 방향키 ←→ · 0.1 m · ✕ = 고름 풂) - 시험 — test_137_design_pick · test_139_6_line_shift(패널 · 방향키 · ↺ · 숨김) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018jonfdxn1Xit9bdkNJpE4G
123 lines
5.6 KiB
Python
123 lines
5.6 KiB
Python
"""PLAN 137-2 · 137-3 — B06 보라 선 고름: 직선 조각 · 가림 · 치수 · 구역 · 판정 선 · 페이지 한 칸.
|
||
|
||
짝: `B06_Section/B06_Section_UI_Cross_Design_Pick.ts` · `A00_Common/b_polygon_clip.ts`(B07 에서 옮김) ·
|
||
`B06_Section_UI_Cross_View.ts`(배선 · 하나만). 실제 TS 를 `helper_137_design_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_137_design_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:
|
||
"""0.5 m 토막 25개 → 성토 사면 · 노면 둘(가운데 꺾임) · 측구 · 절토 사면 = 다섯 · 거꾸로 그린 토막도 합침."""
|
||
assert out["merge"]["segments"] == 25
|
||
assert out["merge"]["pieces"] == [[-6.5, -2.5], [-2.5, 0], [0, 2.5], [2.5, 3], [3, 6]]
|
||
assert out["mergeStructure"] == 2 # 구조물 선은 설계선과 안 합침
|
||
|
||
|
||
def test_벽에_가린_부분은_빼고_보이는_조각만(out) -> None:
|
||
assert out["occlude"]["pieces"] == [[3, 4], [4.5, 6]]
|
||
assert out["occlude"]["covered"] == 0
|
||
|
||
|
||
def test_치수_사면길이_수평_높이_구배(out) -> None:
|
||
fill = out["measure"]["fill"]
|
||
assert (fill["horizontalM"], fill["heightM"], fill["grade"]) == (3, 2, "1:1.50")
|
||
assert abs(fill["lengthM"] - 13**0.5) < 1e-9
|
||
assert out["measure"]["road"]["grade"] == "2.0%" # 노면 = %
|
||
assert out["measure"]["flatSlope"] == "0.0%"
|
||
# 두 줄(151-1) — 1줄 = 길이 · 수평 · 2줄 = 높이 · 구배
|
||
assert out["label"]["fill"] == "사면길이 3.61m · 수평 3.00m\n높이 2.00m · 1:1.50"
|
||
assert out["label"]["road"] == "길이 2.50m · 수평 2.50m\n높이 0.05m · 2.0%"
|
||
|
||
|
||
def test_구역_도로_절토_성토(out) -> None:
|
||
zone = out["zone"]
|
||
assert zone["road"] == {"zone": "road", "side": "left"}
|
||
assert zone["roadEdge"] == "road"
|
||
assert zone["ditch"] == {"zone": "cut", "side": "left"} # 측구 = 절토
|
||
assert zone["cutSlope"] == {"zone": "cut", "side": "left"}
|
||
assert zone["fillSlope"] == {"zone": "fill", "side": "right"}
|
||
assert zone["structure"] == "fill" # 구조물 트림 선 = 성토
|
||
assert zone["rightCutDitch"] == "cut"
|
||
assert zone["bothFill"] == "fill"
|
||
assert zone["pieceZones"] == ["fill", "road", "road", "cut", "cut"]
|
||
|
||
|
||
def test_실제_배관_측점_조각과_판정_선(out) -> None:
|
||
real = out["real"]
|
||
assert real["pieces"] < real["designLines"] # 토막이 조각으로 합쳐짐
|
||
assert real["structurePieces"] > 0 # 구조물 트림 선도 고름 대상
|
||
assert real["hits"] == real["pieces"]
|
||
assert real["wallCount"] == 2 and real["insideWall"] == 0 # 벽 안 조각 없음
|
||
assert real["hitsBelowStructure"] is True # 구조물 판정면이 위
|
||
assert real["picked"] == [0] and real["stopped"] is True # 카드 고름으로 안 번짐
|
||
assert real["activeCount"] == 1 and real["activeIndex"] == 1
|
||
assert real["labelOn"]["hidden"] is False and real["labelOn"]["text"].startswith("사면길이 ")
|
||
assert real["labelOff"] == {"hidden": True, "text": ""}
|
||
assert set(real["zones"]) <= {"road", "cut", "fill"}
|
||
|
||
|
||
def test_페이지_한_칸_고름(out) -> None:
|
||
assert out["slot"] == {"a1": 2, "a2": None, "b": 1, "cleared": None}
|
||
|
||
|
||
def test_카드_배선_하나만() -> None:
|
||
"""137-3 — 선을 고르면 벽 · 본체 · 면적을 풀고 카드도 고름 · 다른 고름은 선을 풂."""
|
||
view = _src("B06_Section/B06_Section_UI_Cross_View.ts")
|
||
assert "const pickLine = (index: number): void => {" in view
|
||
assert "if (!isSelected) onSelect(section.station_id);" in view
|
||
assert view.count("dropLine();") >= 4 # 본체 · 벽 · 카드 풂 · 면적
|
||
assert "setLineActive(pickedLineAt(section.station_id));" in view # 다시 그려도 되살림
|
||
assert "zoomBar.prepend(linePick.label);" in view # 치수 = [+ − ⤢] 왼쪽
|
||
assert "chartWrap.append(linePick.panel);" in view # 사면 이동 패널(151-3)
|
||
css = _src("B06_Section/B06_Section_UI_Style_Cross_Areas.css")
|
||
assert ".b06-chart__design-hit.is-active {" in css
|
||
|
||
|
||
def test_치수_두_줄_좁은_카드는_표_아래로() -> None:
|
||
"""151-1 — 줄바꿈 글자를 살리고 오른쪽 맞춤 · 좁은 카드(3열 등)는 표 높이 아래로 내려 표를 안 덮음."""
|
||
css = _src("B06_Section/B06_Section_UI_Style_Cross_Controls.css")
|
||
dims = css.split(".b06-cross-card__line-dims {", 1)[1].split("}", 1)[0]
|
||
assert "white-space: pre-line;" in dims and "text-align: right;" in dims
|
||
assert "container-type: inline-size;" in css
|
||
narrow = css.split("@container (max-width: 620px) {", 1)[1]
|
||
assert "top: calc(var(--spacing-8) + var(--b06-areas-height, 3.6rem));" in narrow
|
||
|
||
|
||
def test_폴리곤_자르기는_공용_한_벌() -> None:
|
||
b07 = _src("B07_DesignDetail/B07_DesignDetail_Cross_Overlay.ts")
|
||
assert "function clipSegment(" not in b07 and "function pointInPolygon(" not in b07
|
||
assert 'from "../A00_Common/b_polygon_clip"' in b07
|