Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ji4nuyLVHcxrXWHTdb5nRD
131 lines
5.6 KiB
Python
131 lines
5.6 KiB
Python
"""PLAN 173-3 · 173-4 — B06 횡단 카드 면적표 열 연동 · [상세] 그림 테마 색 · 칸 가득.
|
|
|
|
짝: `B06_Section/B06_Section_UI_Style_Cross_Areas.css`(글자 = 그림 칸 폭 · 4열부터 「계」만) ·
|
|
`_UI_Section_View.ts`(그리드 data-columns · `detailChart`) · `_UI_Cross_Detail.ts`(칸 크기로 새로 그림) ·
|
|
`_UI_Style_Cross_Detail.css`(테마 토큰) · `_UI_Cross_View_Metrics.ts` `fitPlotBase`(Node 헬퍼
|
|
`helper_173_detail_fit.cjs` 가 실제 TS 를 돌림).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import re
|
|
import subprocess
|
|
|
|
import pytest
|
|
|
|
HERE = os.path.dirname(__file__)
|
|
ROOT = os.path.join(HERE, "..", "..")
|
|
B06 = os.path.join(ROOT, "B06_Section")
|
|
|
|
|
|
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 fit() -> dict:
|
|
if not os.path.isdir(os.path.join(ROOT, "config", "node_modules")):
|
|
pytest.skip("프론트엔드 의존성 미설치")
|
|
proc = subprocess.run( # noqa: S603 — 고정 실행 파일
|
|
["node", os.path.join(HERE, "helper_173_detail_fit.cjs")],
|
|
capture_output=True,
|
|
text=True,
|
|
encoding="utf-8",
|
|
timeout=60,
|
|
)
|
|
assert proc.returncode == 0, proc.stderr
|
|
return json.loads(proc.stdout)
|
|
|
|
|
|
# ── 173-3 면적표 ─────────────────────────────────────────────
|
|
|
|
|
|
def test_면적표_글자_그림_칸_폭_비례_높이_변수_같은_식():
|
|
css = _read("B06_Section_UI_Style_Cross_Areas.css")
|
|
wrap = css.split(".b06-cross-card__chart-wrap {", 1)[1].split("}", 1)[0]
|
|
assert "--b06-areas-font: clamp(0.6rem, 0.55rem + 0.7cqw, 0.9rem);" in wrap
|
|
assert "--b06-areas-height: calc(var(--b06-areas-font) * 5);" in wrap
|
|
table = css.split(".b06-cross-card__areas {", 1)[1].split("}", 1)[0]
|
|
assert "font-size: var(--b06-areas-font);" in table
|
|
assert "font-size: 0.72rem" not in css
|
|
|
|
# 3열 그림 칸 ≈ 400 px → 지금 글자(0.72rem)와 거의 같음 · 1열 · 6열은 끝값
|
|
def rem(width_px: float) -> float: # 1cqw = 칸 폭 1% · 1rem = 16 px
|
|
return min(max(0.6, 0.55 + 0.7 * width_px / 100 / 16), 0.9)
|
|
|
|
assert rem(400) == pytest.approx(0.725)
|
|
assert rem(1232) == 0.9 and rem(192) == pytest.approx(0.634)
|
|
|
|
|
|
def test_면적표_4열부터_계만_접기():
|
|
css = _read("B06_Section_UI_Style_Cross_Areas.css")
|
|
rule = re.search(
|
|
r'\.b06-section__grid:is\(\[data-columns="4"\], \[data-columns="5"\], \[data-columns="6"\]\)\s+'
|
|
r"\.b06-cross-card__areas\s+:is\(th, td\):nth-child\(n \+ 2\):nth-child\(-n \+ 4\) \{\n display: none;",
|
|
css,
|
|
)
|
|
assert rule, "4~6열 EA · RR · BR 접기 규칙 없음"
|
|
src = _read("B06_Section_UI_Section_View.ts")
|
|
assert "grid.dataset.columns = String(cols);" in src # 실제 그린 열 수(휴대폰 폭 = 1 · 기기대응)
|
|
|
|
|
|
# ── 173-4 [상세] 그림 ────────────────────────────────────────
|
|
|
|
|
|
def test_상세_테마_토큰_어두운_고정색_없음():
|
|
css = _read("B06_Section_UI_Style_Cross_Detail.css")
|
|
cad = css.split(".b06-detail__cad {", 1)[1].split("}", 1)[0]
|
|
assert "background: var(--color-surface-raised);" in cad
|
|
assert "color: var(--color-text);" in cad
|
|
assert "--color-" not in cad.replace("var(--color-", "") # 토큰 덮어쓰기 없음
|
|
assert "#111111" not in css and "#f2f2f2" not in css and "#1a1a1a" not in css
|
|
|
|
|
|
def test_상세_칸_크기로_새로_그림_배선():
|
|
src = _read("B06_Section_UI_Cross_Detail.ts")
|
|
assert "const { clientWidth: width, clientHeight: height } = cad;" in src
|
|
assert "const drawn = drawChart(state.section, width, height);" in src
|
|
assert "host?.chart(section.station_id, widthPx, heightPx)" in src
|
|
assert "new ResizeObserver(() => state.paint())" in src and "resize.disconnect()" in src
|
|
# 카드 clip id 와 가름 · 그리는 동안 refresh 되먹임 막음
|
|
assert "clip.id = `b06-detail-${id}`;" in src
|
|
assert "if (drawing || !open || open.stationId !== section.station_id) return;" in src
|
|
assert "getElementById(`cross-" not in src # 카드 DOM 복제 지움
|
|
view = _read("B06_Section_UI_Section_View.ts")
|
|
assert "detailChart(stationId, widthPx, heightPx) {" in view
|
|
assert "base && fitPlotBase(base, heightPx)" in view
|
|
assert "cardZoomStates.delete(stationId);" in view
|
|
types = _read("B06_Section_UI_Section_View_Types.ts")
|
|
# 179-3 — svg + 축 옵션
|
|
assert (
|
|
" ) => { svg: SVGSVGElement; axes: CrossAxesOptions | null; homeScale: number } | null;"
|
|
in types
|
|
)
|
|
assert (
|
|
" detailChart: (\n stationId: string,\n widthPx: number,\n heightPx: number,"
|
|
in types
|
|
)
|
|
sync = _read("B06_Section_UI_Page_Design_Sync.ts")
|
|
assert "ctx.view().detailChart(stationId, widthPx, heightPx)" in sync
|
|
|
|
|
|
def test_칸_가득_넓은_칸은_가로_범위_넓힘(fit):
|
|
assert fit["natural"] > 400
|
|
assert fit["fitHeight"] == pytest.approx(400)
|
|
assert fit["plotHeight"] == pytest.approx(400)
|
|
assert fit["innerAfter"] == pytest.approx(fit["innerBefore"]) # 도면 폭 그대로
|
|
assert fit["wider"] is True
|
|
assert fit["midAfter"] == pytest.approx(fit["midBefore"]) # 좌우 같이
|
|
|
|
|
|
def test_칸_가득_높은_칸은_그대로(fit):
|
|
assert fit["tallSame"] is True # crossPlotFromBase 가 세로 범위를 늘려 채움
|
|
|
|
|
|
def test_뷰_파일_700줄_안():
|
|
for name in ("B06_Section_UI_Section_View.ts", "B06_Section_UI_Cross_Detail.ts"):
|
|
assert len(_read(name).splitlines()) <= 700, name
|