Files
Aislo/resources/tester/test_244_14_b08_milestone_item_input.py
T
eomsangdonandClaude Opus 5.5 d70b66f57e feat(b08): 거리이정표 — 수정 글 · 상자 하나 = 항목 하나 · 폭 전체 · 처음 배율 맞춤 · 지도 높이 · 도로 구분 = 선 자리 (250)
- 등록 모달 [+ 줄] · 같은 이름 합침 없앰 · 기준위치 = 항목 · 목적지 없음 · 「고침」 → 「수정」
- 다이어그램 · 표 폭 전체 · 틀 높이 고정 · 틀 비율 그림 범위 · 처음 배율 = 상자 범위 맞춤
- 카카오 지도 높이 어디서나 480px
- 도로 구분 = 선 자리(거래처 → 기준점 2차포장 · 기준점 → 시점 1차포장 · 그 밖 포장 · 비포장만 고름) · 파이썬 · TS 짝

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LLCk2y5xE8YGVgjbTntWsY
2026-10-11 00:08:57 +09:00

84 lines
3.7 KiB
Python

"""244-14 B08 거리이정표 [+] 모달 「항목」 = 손 입력 + 제안 목록(사용자 120-답 ③ 「항목: 수기 입력」).
거래처 제안 = 견본 다섯 · 기준위치 제안 = 기준점 · 목록 밖 글도 받음 · 분류 바꾸면 제안만 바뀜(쓴 글 그대로)
— node 로 실제 rowField 를 돌림(가짜 DOM).
"""
import json
import re
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
MODAL = ROOT / "B08_Quantity/B08_Quantity_UI_Milestone_Modal.ts"
IMPORTS = re.compile(r"^import[^;]*;\n", re.MULTILINE)
STUBS = """
type N = any;
const el = (tag: string, o: any = {}): N => {
const n: N = { tag, children: [] as N[], attrs: { ...(o.attrs ?? {}) }, dataset: {}, style: {},
value: "", placeholder: "",
get id() { return n.attrs.id; },
setAttribute(k: string, v: string) { n.attrs[k] = v; },
append(...c: N[]) { n.children.push(...c); },
replaceChildren(...c: N[]) { n.children = c; } };
return n;
};
const createInputField = (o: any) => {
const input = el("input");
return { root: el("div", { children: [] }), input, label: o.label, setError: () => undefined };
};
const createSelectField = (o: any) => {
const select = el("select");
return { root: el("div"), select, setOptions: () => undefined };
};
"""
SCRIPT = """
const sug = (f: any) => f.item.root.children.find((c: N) => c.tag === "datalist")
.children.map((c: N) => c.attrs.value);
const a = rowField({ item: "", dest: "start", note: "" }, "route");
const empty = { value: a.item.input.value, list: sug(a), ph: a.item.input.placeholder,
linked: a.item.input.attrs.list === a.item.root.children[0].id };
a.item.input.value = " 레미콘(25-21-150) "; // 목록 밖 손 글
a.setKind("base");
const base = { value: a.item.input.value, list: sug(a), ph: a.item.input.placeholder };
a.setKind("route");
const old = rowField({ item: "옛 항목", dest: "mid", note: "" }, "route");
const b = rowField({ item: "", dest: "start", note: "" }, "route");
console.log(JSON.stringify({ empty, base, back: a.item.input.value, list: sug(a),
old: [old.item.input.value, old.dest.select.value, sug(old)],
ids: new Set([a, old, b].map((f) => f.item.input.attrs.list)).size,
tag: a.item.input.dataset.b08MilestoneAdd }));
"""
def test_항목_손_입력_제안_분류_바꿔도_글_유지():
src = STUBS + IMPORTS.sub("", MODAL.read_text(encoding="utf-8"))
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
got = json.loads(result.stdout)
five = ["장비(20톤)", "장비(8톤)", "혼합골재", "전석운반", "철근"]
# 빈 줄 = 빈 손 입력 칸 + 거래처 제안 다섯(datalist 이어짐)
assert got["empty"] == {"value": "", "list": five, "ph": "예 장비(20톤)", "linked": True}
# 분류 → 기준위치 = 제안만 기준점 · 쓴 글 그대로
assert got["base"] == {
"value": " 레미콘(25-21-150) ",
"list": ["기준점"],
"ph": "예 기준점",
}
assert got["back"] == " 레미콘(25-21-150) " and got["list"] == five
# 옛 항목 글 = 그대로 칸에 · 목적지 그대로
assert got["old"] == ["옛 항목", "mid", five]
assert got["ids"] == 3 and got["tag"] == "item" # 줄마다 제안 목록 따로
def test_저장_값_손_글_그대로():
modal = MODAL.read_text(encoding="utf-8")
assert "item: first.item.input.value.trim()," in modal # 목록 밖 글도 받음(앞뒤 빈칸만 뺌)
assert 'createSelectField({ label: "항목"' not in modal
assert len(modal.splitlines()) <= 700