Files
Aislo/resources/tester/test_244_6_b08_milestone_pan_client.py
T
eomsangdonandClaude Opus 5.5 41f68b8178 feat(B08): 거리이정표 상자 모달 제목 · 상자 안 장소 그림(254-3)
- 상자 누름 모달 제목 = 「거래처 — 이름」 · 「기준점 — 이름」(표 머리 글 항목 정보 없음)
- 장소 그림(256-1 place_images) = 상자 글 아래 132 × 88 · 그림 없으면 자리 없음 · 키 바뀔 때만 다시 받음
- 시험 = test_254_3 새로 · 244_6 · 244_10 node 입력 = stdin(명령 줄 길이)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01STqEqztUthVFBmEP18wBSz
2026-10-11 14:12:24 +09:00

142 lines
6.9 KiB
Python

"""244-6 B08 거리이정표 추가 고침(사용자 132 · 133번) — 표 폭 = 다이어그램 폭 ·
거래처 상자 항목 글 · 좌측 패널 카드 없음(공사지 · 기준점 = 다이어그램 모달 · [기준점 +]) ·
팬(가운데 · 빈 곳 왼쪽 끌기 · 원배율에서도) · 화면 글 「업체」 → 「거래처」.
팬은 공용 `A00_Common/b_svg_zoom_pan.ts` + `…_Milestone_Layout.ts`(panStart · panContent)를
node 로 돌려 pointerdown · pointermove · pointerup 사건을 실제로 보내
그림 묶음 transform 이 바뀌는지 잼.
"""
import json
import re
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
IMPORTS = re.compile(r"^import[^;]*;\n", re.MULTILINE)
UI = ("UI_Milestone.ts", "UI_Milestone_Diagram.ts", "UI_Milestone_Modal.ts",
"UI_Milestone_Route.ts", "UI_Milestone_State.ts", "Milestone_Layout.ts") # fmt: skip
def _read(rel):
return (ROOT / rel).read_text(encoding="utf-8")
def _node(script):
src = "".join(
IMPORTS.sub("", _read(rel))
for rel in ("A00_Common/b_svg_zoom_pan.ts", "B08_Quantity/B08_Quantity_Milestone_Layout.ts")
)
# 254 — 글이 길어 명령 줄(Windows 32767자)을 넘지 않게 stdin 으로 넘김
result = subprocess.run(
["node", "--experimental-strip-types", "--no-warnings",
"--input-type=module-typescript", "-"], input=src + "\n" + script,
capture_output=True, text=True, encoding="utf-8", timeout=60,
) # fmt: skip
assert result.returncode == 0, result.stderr
return json.loads(result.stdout)
PAN = """
const W = 600, H = 400;
class Svg extends EventTarget {
classList = { add() {}, remove() {} };
setPointerCapture() {}
releasePointerCapture() {}
getBoundingClientRect() { return { left: 0, top: 0, width: W, height: H }; }
}
const shape = { closest: (s: string) => (s.includes(".b08-milestone__box") ? {} : null) };
const empty = { closest: () => null };
function fire(svg: EventTarget, type: string, button: number, x: number, y: number,
target: unknown) {
const ev = new Event(type, { cancelable: true });
for (const [k, v] of Object.entries({ button, clientX: x, clientY: y, pointerId: 1, target }))
Object.defineProperty(ev, k, { value: v });
svg.dispatchEvent(ev);
}
function drag(button: number, target: unknown) {
const svg = new Svg();
const layer = { t: "", setAttribute(_k: string, v: string) { this.t = v; } };
attachSvgZoomPan({ svg: svg as any, layer: layer as any, widthPx: W, heightPx: H,
pad: { left: 0, right: 0, top: 0, bottom: 0 }, wheelZoom: true,
content: panContent(W, H), panStart });
fire(svg, "pointerdown", button, 100, 100, target);
fire(svg, "pointermove", button, 160, 130, target);
fire(svg, "pointerup", button, 160, 130, target);
return layer.t;
}
console.log(JSON.stringify({
leftEmpty: drag(0, empty), leftShape: drag(0, shape),
middleShape: drag(1, shape), right: drag(2, empty),
}));
"""
def test_실제_포인터_사건으로_팬():
got = _node(PAN)
# 원배율(1)에서도 빈 곳 왼쪽 끌기 · 가운데 버튼 끌기 = 그림 묶음이 끈 만큼 옮김
assert got["leftEmpty"] == "translate(60 30) scale(1)"
assert got["middleShape"] == "translate(60 30) scale(1)"
assert got["leftShape"] == "" and got["right"] == "" # 상자 위 왼쪽 = 그 화면 고르기 몫
def test_거래처_상자_항목_글():
data = {
"site": {"mid_station_m": None, "mid_road": "비포장"},
"bases": [],
"routes": [
{"id": "g", "name": "거창군청", "address": "", "to": "site", "dir": None,
"legs": [], "rows": [{"item": "장비", "dest": "start", "note": ""},
{"item": " 장비 ", "dest": "mid", "note": ""},
{"item": "골재", "dest": "mid", "note": ""}]},
{"id": "k", "name": "", "address": "", "to": "site", "dir": None,
"legs": [], "rows": [{"item": "", "dest": "start", "note": ""}]},
],
} # fmt: skip
lay = _node(f"console.log(JSON.stringify(layoutMilestone({json.dumps(data)}, null, 0)));")
boxes = {b["id"]: b for b in lay["boxes"]}
assert boxes["g"]["items"] == "장비 · 골재" and boxes["g"]["label"] == "거창군청"
assert boxes["k"]["items"] == "" and boxes["k"]["label"] == "거래처"
ui = _read("B08_Quantity/B08_Quantity_UI_Milestone_Diagram.ts")
# 244-12 — 글 줄 = 항목(윗줄) · 이름 · 작은 글씨
assert '[box.items, "b08-milestone__items"]' in ui and '[box.label, ""]' in ui
def test_표_폭_다이어그램_폭_좌측_카드_없음():
css = _read("B08_Quantity/B08_Quantity_UI_Milestone.css")
block = css[css.index(".b08-milestone {") :]
assert "width: 100%;" in block[: block.index("}")] # 149 틀 = 페이지 폭 전체
assert ".b08-milestone > .ui-data-table {\n width: 100%;" in css # 149 표도 폭 전체
ui = _read("B08_Quantity/B08_Quantity_UI_Milestone_Diagram.ts")
assert "const fw = Math.max(host.clientWidth, 320);" in ui # 149 다이어그램 = 틀 폭 · 틀 비율
page = _read("B08_Quantity/B08_Quantity_UI_Milestone.ts")
assert "children: [diagram.root, none, table.root]" in page
# 좌측 패널 카드 없음 · 공사지 · 기준점은 모달
options = _read("B08_Quantity/B08_Quantity_UI_Options.ts")
assert "milestoneCardBody" not in options and "await saveMilestone(projectId);" in options
assert "milestoneCardBody" not in _read("B08_Quantity/B08_Quantity_UI_Milestone_State.ts")
modal = _read("B08_Quantity/B08_Quantity_UI_Milestone_Modal.ts")
site = modal[modal.index("export function openSiteInfo") :]
assert 'label: "중점 측점 (km)"' in site and 'label: "시점 → 중점 도로"' in site
assert 'setLeg(fresh, "start", 0, {' in site and "putMilestone(" in site
# 244-13 — 기준점 = 등록 모달 분류 「기준위치」 · [기준점 +] 없앰 · 254 여럿 · 삭제 = 그 기준점만
base = modal[
modal.index("export function openPlaceModal") : modal.index("interface SiteAddress")
]
assert "fresh.bases.push({" in base and 'label: "삭제"' in base
assert "fresh.bases = fresh.bases.filter((b) => b.id !== base.id);" in base
assert 'label: "기준점 +"' not in ui and "openBaseInfo" not in ui and "addBase" not in ui
assert "content: panContent(vw, vh)" in ui and "panStart," in ui
def test_화면_글_거래처():
for name in UI:
text = _read(f"B08_Quantity/B08_Quantity_{name}")
assert "업체" not in text, name
modal = _read("B08_Quantity/B08_Quantity_UI_Milestone_Modal.ts")
assert 'label: "거래처 이름"' in modal
page = _read("B08_Quantity/B08_Quantity_UI_Milestone.ts")
assert '{ key: "company", label: "거래처 이름"' in page and "`거래처 ${" in page
for name in UI:
assert len(_read(f"B08_Quantity/B08_Quantity_{name}").splitlines()) <= 700