- 상자 누름 모달 제목 = 「거래처 — 이름」 · 「기준점 — 이름」(표 머리 글 항목 정보 없음) - 장소 그림(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
142 lines
6.9 KiB
Python
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
|