"""383-2 교각점 방향키 — `logic/B05_Profile_RouteEdit_KeyNudge.ts` 를 Node(`helper_383_2_key_nudge.cjs`)로 돌려 판정(화면 축 0.1 m · Shift ×10 · 돌린 지도 · 스냅 뒤 실제 이동 · 함께 밀린 IP · 제자리 안내) + 이음(`_RouteEdit_Keys` · 끌기 걸기 한 줄 · 평면이 삼킴 → 종단 ▲▼ 비킴). ./venv/Scripts/python.exe -m pytest resources/tester/test_383_2_route_keys.py -q """ import json import math import subprocess from pathlib import Path import pytest ROOT = Path(__file__).resolve().parents[2] B05 = ROOT / "B05_Profile" HELPER = Path(__file__).with_name("helper_383_2_key_nudge.cjs") @pytest.fixture(scope="module") def got() -> dict: run = subprocess.run( # noqa: S603 — 고정 실행 파일 ["node", str(HELPER)], capture_output=True, text=True, encoding="utf-8", timeout=60 ) assert run.returncode == 0, run.stderr return json.loads(run.stdout) def _read(name: str) -> str: return (B05 / name).read_text(encoding="utf-8") def test_화면_축_0_1m_Shift_10배(got) -> None: plain = got["plain"] assert plain["ArrowUp"] == pytest.approx([0, 0.1]) # 화면 위 = 도면 북쪽(뒤집힌 y) assert plain["ArrowDown"] == pytest.approx([0, -0.1]) assert plain["ArrowLeft"] == pytest.approx([-0.1, 0]) assert plain["ArrowRight"] == pytest.approx([0.1, 0]) assert got["shift"]["ArrowRight"] == pytest.approx([1.0, 0]) assert got["arrows"] == [True, True, True, True, False] def test_돌린_지도도_화면_위(got) -> None: up = got["rotated"]["ArrowUp"] turn = math.radians(30) assert math.hypot(*up) == pytest.approx(0.1) assert up == pytest.approx([-0.1 * math.sin(turn), 0.1 * math.cos(turn)]) def test_스냅_뒤_실제_이동_함께_밀린_IP(got) -> None: assert got["snapped"]["movedM"] == pytest.approx(0.8) assert got["snapped"]["pushed"] == 1 assert got["stayed"]["movedM"] == pytest.approx(0.02) and got["stayed"]["pushed"] == 0 assert got["merged"]["movedM"] == pytest.approx(0.4) and got["merged"]["pushed"] == 0 messages = got["messages"] assert "실제 이동 0.8 m" in messages["snapped"] and "함께 밀린 IP 1곳" in messages["snapped"] assert "같은 측점 — 스냅 끄고 미세 조정" in messages["stayed"] assert "0.3 m 옮겼습니다" in messages["free"] def test_이음_끌기_걸기_한_줄_창_가로채기() -> None: pointer = _read("B05_Profile_UI_RouteEdit_Pointer.ts") assert pointer.count("bindRouteEditKeys(") == 1 keys = _read("B05_Profile_UI_RouteEdit_Keys.ts") assert 'window.addEventListener("keydown", onKeyDown, true)' in keys # 앞서 받음(capture) assert 'lastPointerWithin(".b05-routeedit")' in keys assert "event.preventDefault();\n event.stopPropagation();" in keys assert "state.apexLock[index] === true" in keys # 고정 IP 는 안 옮김 assert "shortfallCrossed(" in keys # 하한 검사 = 끌기와 같음 assert "params.commit();" in keys # 뗄 때 한 걸음(스냅이면 그때 맞춤) # 종단 ▲▼ 는 앞선 preventDefault 를 보고 비킴 tools = _read("B05_Profile_UI_Profile_Panel_Tools.ts") assert "if (event.defaultPrevented) return;" in tools base = (ROOT / "B06_Section/B06_Section_UI_Cross_Panel_Base.ts").read_text(encoding="utf-8") assert "export function lastPointerWithin(selector: string)" in base def test_RouteEdit_본문_줄_안_늘림() -> None: assert len(_read("B05_Profile_UI_RouteEdit.ts").splitlines()) <= 700