- 면적표 글자 = 그림 칸 폭 비례(clamp · cqw) · 표 높이 변수 같은 식 · 4열부터 「계」만(그리드 data-columns) - [상세] 그림 = 테마 토큰(어두운 고정색 지움) · 카드 그리기 함수로 모달 칸 크기에 맞춰 새로 그림(fitPlotBase · 빈 띠 없음) - 칸 크기 바뀌면 다시 그림(ResizeObserver) · clip id 카드와 가름 · 그리는 동안 refresh 되먹임 막음 - 시험 test_173_areas_detail 새로 · test_164 · test_150 단정 고침 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JywHxQuWPk6UYXTebyrBoU
105 lines
4.2 KiB
Python
105 lines
4.2 KiB
Python
"""PLAN 164 — B06 [상세] 그림 줌 · 팬(164-1) · 표 강조 붉은 한 벌(164-2).
|
|
|
|
짝: `B06_Section/B06_Section_UI_Cross_Detail.ts`(`attachDetailZoom` · 칸 누름) ·
|
|
`_UI_Style_Cross_Detail.css`(칸 비율 · 붉은 강조). 실제 TS 를 `helper_164_detail_zoom.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_164_detail_zoom.cjs")],
|
|
capture_output=True,
|
|
text=True,
|
|
encoding="utf-8",
|
|
timeout=60,
|
|
)
|
|
assert proc.returncode == 0, proc.stderr
|
|
return json.loads(proc.stdout)
|
|
|
|
|
|
def test_복제_자식을_g_하나로_감쌈_비율(out):
|
|
assert out["wrapped"] is True # 축 · 눈금 · 도형이 한 장으로 움직임
|
|
assert out.get("ratio") is None # 173-4 — 칸 = viewBox(칸 크기로 새로 그림) · 비율 변수 없음
|
|
assert out["before"] is None
|
|
|
|
|
|
def test_휠_당기면_확대_커서_고정(out):
|
|
assert out["wheelIn"]["scale"] == pytest.approx(1.15)
|
|
assert out["cursorFixed"] == pytest.approx(300) # 커서 아래 svg x 300 이 제자리
|
|
assert out["wheelOut"] < out["wheelIn"]["scale"]
|
|
assert out["max"] == 16
|
|
|
|
|
|
def test_가운데_단추_팬_좌클릭은_팬_아님(out):
|
|
assert out["leftDrag"] == {"scale": 1, "tx": 0, "ty": 0}
|
|
# 1배에서도 팬 · 화면 200·40 px = svg 100·20(칸 2 배)
|
|
assert out["middlePan"] == {"scale": 1, "tx": 100, "ty": 20}
|
|
assert out["panClickStopped"] is True
|
|
|
|
|
|
def test_다시_떠도_배율_유지_더블클릭_원복(out):
|
|
assert out["restored"] == "translate(100 20) scale(1)"
|
|
assert out["reset"] == "translate(0 0) scale(1)"
|
|
|
|
|
|
def test_모달_배선_단추_칸_비율():
|
|
src = _read("B06_Section_UI_Cross_Detail.ts")
|
|
css = _read("B06_Section_UI_Style_Cross_Detail.css")
|
|
assert "wheelZoom: true" in src and "attachSvgZoomPan({" in src
|
|
assert "cad.replaceChildren(clone, buildZoomControls(handle))" in src
|
|
assert "attachDetailZoom(clone, zoom, (next) => (zoom = next))" in src
|
|
assert "position: relative;" in css
|
|
# 173-4 — 그림 = 칸 크기로 새로 그림 · svg 칸 가득
|
|
assert ".b06-detail__cad > svg {\n display: block;\n width: 100%;\n height: 100%;" in css
|
|
assert "--ratio" not in css and "--ratio" not in src
|
|
|
|
|
|
def test_강조_붉은_한_벌_고름_hover_다름_빈칸():
|
|
css = _read("B06_Section_UI_Style_Cross_Detail.css")
|
|
for selector in (
|
|
".b06-detail__cad .b06-chart__area.is-active > polygon {\n fill: color-mix(in srgb, var(--color-danger)",
|
|
".b06-detail__cad .b06-chart__design-hit.is-active {\n stroke: color-mix(in srgb, var(--color-danger)",
|
|
".b06-detail__cad .b06-chart__center.is-active line {\n stroke: var(--color-danger);",
|
|
".b06-detail__cad .b06-chart__spoil-fill.is-active {\n stroke: var(--color-danger);",
|
|
".b06-detail__qtable .is-picked {\n background: color-mix(in srgb, var(--color-danger) 18%",
|
|
".b06-detail__qtable tr:has(.is-picked) {",
|
|
".b06-detail__qtable [data-key][data-empty] {\n cursor: default;",
|
|
):
|
|
assert selector in css, selector
|
|
assert "royal-amethyst" not in css and "--color-warning);" not in css.split("/* 그림 강조")[1]
|
|
assert (
|
|
".b06-detail__qtable [data-key]:hover {\n background: color-mix(in srgb, var(--color-danger) 8%"
|
|
in css
|
|
)
|
|
|
|
|
|
def test_빈칸_누름_없음_이름_칸_첫_키():
|
|
src = _read("B06_Section_UI_Cross_Detail.ts")
|
|
assert 'el.toggleAttribute("data-empty", !present.has(el.dataset.key ?? ""))' in src
|
|
assert 'if (!key || el.hasAttribute("data-empty")) return;' in src
|
|
assert 'cell("th", groupL, { row: 2 }, keyL)' in src
|
|
assert 'cell("th", groupM, { row: 2 }, keyM)' in src
|