Files
Aislo/resources/tester/test_244_10_b08_milestone_tailscale.py
T
eomsangdonandClaude Opus 5.5 8f96b16380 feat(b08): 거리이정표 경로 그림 = 서버 자동 캡처 · 브라우저 캡처 없앰 (244-11)
- [등록] · [길찾기] 뒤 길 선 · 표지 → 서버 헤드리스 Edge(없으면 chrome)로 카카오 지도 1200×800 PNG → auto/<경로 id>.png
- 페이지 주소 = KAKAO_CAPTURE_ORIGIN(기본 http://localhost:5173) + /__route_capture page.route 채움
- 사용자 그림 우선 · 자동 그림으로 되돌리기 · 실패 = 까닭 한 줄 · 동시 요청 잠금 · 좌표 저장 없음
- [지도 캡처] · [화면 캡처] · getDisplayMedia · 244-10 캡처 안내 없앰(지도 SDK 도메인 안내는 그대로)
- 새 시험 test_244_11 · 244_4 · 244_9 · 244_10 기대값 고침 · requirements playwright

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01STqEqztUthVFBmEP18wBSz
2026-10-10 22:59:25 +09:00

92 lines
3.7 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 } };
"""
def _view(setup):
script = (
STUBS
+ setup
+ IMPORTS.sub("", _read(KMAP))
+ """
const view = routeMapView("p1", "r1");
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
result = subprocess.run(
["node", "--experimental-strip-types", "--no-warnings",
"--input-type=module-typescript", "-e", 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"]