Files
Aislo/resources/tester/test_244_10_b08_milestone_tailscale.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

96 lines
4.1 KiB
Python

"""244-10 B08 거리이정표 경로 지도 — 테일스케일 주소(http://100.72.32.27:5173 · 보안 문맥 아님).
① getDisplayMedia 없음 = 지도 그대로 · 캡처 안내 없음([지도 캡처]는 244-11 에서 없앰 · 그림 = 서버 자동)
② SDK 못 받음(script onerror · 도메인 미등록 401) = 지금 주소(location.origin) 등록 안내 한 줄 · 지도 숨김
(가짜 kakao.maps · document 로 node 에서 돌림)
"""
import json
import re
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
IMPORTS = re.compile(r"^import[^;]*;\n", re.MULTILINE)
LAYOUT = "B08_Quantity/B08_Quantity_Milestone_Layout.ts"
KMAP = "B08_Quantity/B08_Quantity_UI_Milestone_KakaoMap.ts"
ORIGIN = "http://100.72.32.27:5173"
def _read(rel):
return (ROOT / rel).read_text(encoding="utf-8")
STUBS = f"""
const el = (tag: string, o: any = {{}}) => ({{ tag, hidden: false, textContent: o.text ?? "",
className: o.className, dataset: {{}}, children: o.children ?? [] }});
const requestAnimationFrame = (f: () => void) => setTimeout(f, 0);
const request = async () => ({{ key: "fake-js-key" }});
const injected: string[] = [];
(globalThis as any).window = globalThis;
(globalThis as any).location = {{ origin: {json.dumps(ORIGIN)} }};
const liveMilestone = () => ({{ site: {{}}, base: {{ enabled: false }},
routes: [{{ id: "r1", name: "거창군청", lon: 127.9, lat: 35.68, via_base: false }}] }});
const siteInfo = () => ({{ start_lonlat: [127.83, 35.64] }});
const routePaths = () => [[[35.68, 127.9], [35.64, 127.83]]];
"""
FAKE_KAKAO = """
class Fake { setMap() {} relayout() {} setBounds() {} extend() {} }
(globalThis as any).kakao = { maps: { load: (f: () => void) => f(), LatLng: Fake,
LatLngBounds: Fake, Map: Fake, Polyline: Fake, Marker: Fake, CustomOverlay: Fake,
event: { addListener: () => {} } } };
"""
def _view(setup):
script = (
STUBS
+ setup
+ IMPORTS.sub("", _read(KMAP))
+ """
// 156 경로 탭 지도 칸 없앰 — 같은 SDK 싣기 · 안내 길 = 연결선 모달 지도 칸(editMap)
const view = editMap("p1", { onMove: () => {}, hint: "길 선 = 카카오 길찾기 · 경로 그림은 서버가 자동으로 만듦" });
view.set([{ key: "r1", name: "거창군청", lon: 127.9, lat: 35.68, movable: true }], routePaths());
await new Promise((r) => setTimeout(r, 30));
const [hint, box] = view.root.children;
console.log(JSON.stringify({ injected, hint: hint.textContent, boxHidden: box.hidden,
children: view.root.children.length }));
"""
)
src = IMPORTS.sub("", _read(LAYOUT)) + script
# 254 — 글이 길어 명령 줄(Windows 32767자)을 넘지 않게 stdin 으로 넘김
result = subprocess.run(
["node", "--experimental-strip-types", "--no-warnings",
"--input-type=module-typescript", "-"], input=src,
capture_output=True, text=True, encoding="utf-8", timeout=60,
) # fmt: skip
assert result.returncode == 0, result.stderr
return json.loads(result.stdout)
NO_MEDIA = """
Object.defineProperty(globalThis, "navigator", { configurable: true, value: {} });
(globalThis as any).document = { createElement: () => ({}), head: { append: () => {} } };
"""
def test_보안_문맥_아님_지도_그대로_캡처_안내_없음():
got = _view(NO_MEDIA + FAKE_KAKAO)
assert not got["boxHidden"] and got["children"] == 2 # 지도는 그대로 · 캡처 단추 없음
assert got["hint"] == "길 선 = 카카오 길찾기 · 경로 그림은 서버가 자동으로 만듦"
assert "캡처는 localhost" not in got["hint"]
def test_SDK_401_도메인_등록_안내():
sdk_401 = """
(globalThis as any).document = { createElement: () => ({}),
head: { append: (s: any) => (injected.push(s.src), setTimeout(() => s.onerror(), 0)) } };
"""
got = _view(NO_MEDIA + sdk_401) # window.kakao 없음 → script 넣음 → 401 = onerror
assert len(got["injected"]) == 1 and "sdk.js?appkey=fake-js-key" in got["injected"][0]
assert got["hint"] == (
f"카카오 JavaScript 키 도메인에 지금 주소({ORIGIN}) 등록 필요 — 지도 대신 [그림 변경]으로 넣으세요"
)
assert got["boxHidden"]