# -*- coding: utf-8 -*- """233-3 좌측 목록 · 폼 · 배수유역도 · 남은 바로 꺼짐 자리 · 풀기 = Esc 한 길(조사 AF (나) ⑤ · a3 · a5 · a6). 까닭: 배수유역도 재계산이 관 마커 · 유역 고름을 지움(지도만 꺼짐) · 지도 · 유역 고름은 고름 한 칸에 안 적혀 마름모가 안 옮겨짐 · 목록 관 줄은 폭 0.05 · 관이 조금 옮겨지면 줄 강조 빠짐 · 목록 관 고름은 화면마다 손으로 불러 길마다 빠짐 · 풀기가 화면마다 다름(어디까지 푸는지 제각각). 고침: 종단 보기 구독이 배수유역도(관 마커 · 유역 · 마름모)까지 값으로 칠함(알림 없이 · 재계산 뒤 다시 얹음) · 지도 고름 · 목록 관 고름 · 목록 다시 누름도 고름 한 칸에 적고 칠하기는 구독 몫 · 목록 줄 폭 = `PIPE_MATCH_M` · 옮겨진 관도 고름 따라감 · Esc = 고름 한 칸 비움 → 종단 · 3D · 지도 · 폼 · 횡단 카드 · 범위 · 형상 모두 풂 (입력 칸 · 창 · 먼저 받은 Esc 는 그쪽 몫). 진짜 TS 를 `helper_233_3_release.cjs` 가 Node 로 돌린다. """ from __future__ import annotations import json import os import subprocess import pytest HERE = os.path.dirname(os.path.abspath(__file__)) ROOT = os.path.abspath(os.path.join(HERE, "..", "..")) pytestmark = pytest.mark.skipif( not os.path.isdir(os.path.join(ROOT, "config", "node_modules")), reason="프론트엔드 의존성 미설치", ) def _read(*parts: str) -> str: with open(os.path.join(ROOT, *parts), encoding="utf-8") as handle: return handle.read() @pytest.fixture(scope="module") def out() -> dict: proc = subprocess.run( # noqa: S603 — 고정 실행 파일 ["node", os.path.join(HERE, "helper_233_3_release.cjs")], capture_output=True, text=True, encoding="utf-8", timeout=60, ) assert proc.returncode == 0, proc.stderr return json.loads(proc.stdout) def test_관_고름은_지도_관_마커_유역_마름모까지_알림_없이(out) -> None: assert out["pipePick"]["map"] == {"pipe": 80.17, "mark": 80.17, "notifies": 0} assert out["pipePick"]["profile"]["station"] == "irregular:p80" def test_재계산이_지운_지도_고름을_같은_값으로_다시_얹음(out) -> None: assert out["afterRecompute"]["map"] == {"pipe": 80.17, "mark": 80.17, "notifies": 0} def test_입력칸_창_먼저_받은_Esc는_안_풂(out) -> None: for key in ("escInInput", "escModal", "escPrevented"): assert out[key]["session"] == {"chainageM": 80.17, "structureId": None}, key assert out[key]["form"]["resets"] == 0, key def test_Esc는_값_종단_지도_폼_모두_풂(out) -> None: esc = out["esc"] assert esc["session"] is None assert esc["profile"] == {"station": None, "structure": None} assert (esc["map"]["pipe"], esc["map"]["mark"]) == (None, None) assert esc["form"] == {"open": None, "resets": 1} assert out["crossWhileHidden"] == [] # 숨은 횡단 보기는 안 받음 def test_B05_목록_풀기는_그_길이_폼을_닫고_구독은_폼을_또_안_닫음(out) -> None: release = out["listRelease"] assert release["session"] is None assert release["profile"] == {"station": None, "structure": None} assert release["form"]["resets"] == 1 # Esc 때 한 번뿐 def test_횡단_보기_Esc는_카드_폼_범위_형상_모두(out) -> None: assert out["crossEsc"] == ["releaseForm", "focus:null", "mark:null"] assert out["crossSession"] is None assert out["listeners"] == 2 # 보기마다 하나 — 보이는 보기만 받음 def test_배선() -> None: view = _read("A00_Common", "design", "design_view_profile.ts") assert "onBasinSelected: (chainageM) => pickFromMap(chainageM)" in view assert "onPipeSelected: (chainageM) => pickFromMap(chainageM)" in view assert "if (!selectionSyncing) rememberPick(chainageM, null);" in view assert "onListRelease: () => rememberPick(null, null)" in view assert "form: panel.structures" in view assert len(view.splitlines()) <= 700 panel = _read("B05_Profile", "B05_Profile_UI_Panel.ts") assert "onListRelease: callbacks.onListRelease" in panel drainage = _read("B05_Profile", "B05_Profile_UI_Drainage_Panel.ts") assert "if (notify) callbacks.onPipeSelected?.(pickedChainage);" in drainage assert "selectBasin(basinIndexOfPipe(basins, picked ?? null), notify);" in drainage listing = _read("B05_Profile", "B05_Profile_UI_Structures_List.ts") assert "< PIPE_MATCH_M" in listing and "< 0.05" not in listing structures = _read("B05_Profile", "B05_Profile_UI_Structures_Panel.ts") assert "selectedPipeChainage = kept.chainage_m;" in structures # 저장 · 새로 읽기 뒤 목록 주입이 관 폼을 닫지 않음(닫히며 종단 선까지 꺼졌음) assert "if (!applyingLive && selectedPipeChainage === null)" in structures assert len(structures.splitlines()) <= 700 cross = _read("A00_Common", "design", "design_view_cross.ts") assert "releaseForm: () => structuresPanel.showAtChainage(null)" in cross assert len(cross.splitlines()) <= 685