# -*- coding: utf-8 -*- """283 · 493-2 · 493-5 B05 [저장] = 보이는 화면 그대로 · 초기화 기준 = 노선 추천 [적용](요구 289 · 290). - 손 고침 [저장] — 띠 · 확인 창 · 저장 뒤 초기화 알림 · 세션 초안 통째 비움 없음(492-2 걷음 · 소스 확인). - 추천 [적용] 확인 창 — 지워질 수 = 서버 저장분(구조물 · 관 · 추가 측점) = 적용 뒤 알림 수 (서버 `route_reset.dropped` = 저장 파일 길이) · 세션 초안은 안 셈 · 관 저장 파일 없음 = 0. - [적용] 뒤 추가 측점 저장분 다시 받음 — 지운 측점이 목록 · [저장]에 남지 않음(493-5). """ from __future__ import annotations import json import subprocess from pathlib import Path import pytest HERE = Path(__file__).resolve().parent ROOT = HERE.parents[1] pytestmark = pytest.mark.skipif( not (ROOT / "config" / "node_modules" / "typescript").is_dir(), reason="프론트엔드 의존성 미설치", ) SERVERS = { "saved": { "structures": {"structures": [{"id": "s1"}]}, "pipes": {"saved": True, "pipe_points": [{}, {}, {}]}, "plus": [120], }, "unsaved_pipes": { # 관 저장 파일 없음 — 응답 관은 자동 제안 · 서버가 지울 것 0 "structures": {"structures": []}, "pipes": {"saved": False, "pipe_points": [{}, {}]}, "plus": [], }, "fail": {"structures": "fail", "pipes": "fail", "plus": "fail"}, } RESET = {"reset": True, "dropped": {"structures": 1, "pipes": 3, "plus_stations": 1}} @pytest.fixture(scope="module") def ts(tmp_path_factory): source = tmp_path_factory.mktemp("s283") / "in.json" source.write_text(json.dumps({"servers": SERVERS, "reset": RESET}), encoding="utf-8") proc = subprocess.run( # noqa: S603 — 고정 실행 파일 ["node", str(HERE / "helper_283_route_reset_confirm.cjs"), str(source)], capture_output=True, text=True, encoding="utf-8", timeout=120, check=False, ) assert proc.returncode == 0, proc.stderr return json.loads(proc.stdout) def _read(*parts: str) -> str: return ROOT.joinpath(*parts).read_text(encoding="utf-8") # ── 손 고침 [저장] = 보이는 화면 그대로 ─────────────────────────────────────── def test_저장_확인창_초기화알림_초안비움_없음(): src = _read("A00_Common", "design", "design_save.ts") for gone in ("confirmRouteReset", "routeResetNotice", "route_reset", "clearDrafts("): assert gone not in src body = src[src.index("export async function saveDesign(") :] # 잠금 막 앞에 묻는 것 없음 — 노선 초안 읽기는 잠금 막 뒤(492 이전 차례) assert body.index("showLoadingOverlay()") < body.index("pendingRouteSave(") def test_띠_걷음(): assert "mountRouteResetBand" not in _read("A00_Common", "design", "design_view_profile.ts") assert "reset-band" not in _read("B05_Profile", "B05_Profile_UI_Style_RouteEdit.css") assert "pageRoutePlan" not in _read("A00_Common", "design", "design_page_data.ts") assert "routeShapeChanged" not in _read("B05_Profile", "logic", "B05_Profile_Grade_Carry.ts") logic = _read("B05_Profile", "logic", "B05_Profile_Route_Reset.ts") for gone in ("ROUTE_RESET_BAND", "draftShapeChanged", "askRouteReset"): assert gone not in logic # ── 추천 [적용] 확인 창 = 서버가 지울 수 ───────────────────────────────────── def test_적용_확인창_서버_저장분_수(ts): text = ts["texts"]["saved"] assert text.startswith("추천 노선을 [적용]하면 초기화") for part in ("구조물 1", "관 3", "추가 측점 1", "종단 · 횡단 손 입력"): assert part in text assert text.endswith("적용할까요?") def test_적용_확인창_수_적용뒤_알림_수_같음(ts): dropped = RESET["dropped"] text, notice = ts["texts"]["saved"], ts["notice"] for name, key in (("구조물", "structures"), ("관", "pipes"), ("추가 측점", "plus_stations")): assert f"{name} {dropped[key]} " in text and f"{name} {dropped[key]}" in notice def test_관_저장파일_없음_0_받기실패_0(ts): assert "구조물 0 · 관 0 · 추가 측점 0" in ts["texts"]["unsaved_pipes"] assert "구조물 0 · 관 0 · 추가 측점 0" in ts["texts"]["fail"] def test_적용_확인창_배선(): initial = _read("B05_Profile", "B05_Profile_UI_RouteEdit_Initial.ts") assert "confirmText: () => applyResetText(projectId)" in initial ui = _read("B05_Profile", "B05_Profile_UI_RouteReset.ts") assert "useDesignStore" not in ui # 세션 초안 안 셈 # ── 적용 뒤 알림 ───────────────────────────────────────────────────────────── def test_적용뒤_알림_dropped(ts): assert ts["notice"] == ( "노선이 바뀌어 초기화했습니다 — 지움: 구조물 1 · 관 3 · 추가 측점 1 · 새 교차 관 2" ) assert ts["noticeNone"] == [None, None] # ── 493-5 [적용] 뒤 화면 초안 = 새 초기값 ──────────────────────────────────── def test_적용뒤_추가측점_저장분_다시받음(ts): fixed, stale = ts["plusApply"]["fixed"], ts["plusApply"]["stale"] assert fixed["before"] == [127, 163] assert fixed["after"] == [] and fixed["body"] is None # [저장]이 지운 측점을 다시 안 보냄 assert stale["after"] == [127, 163] # 대조 — 다시 안 받으면 옛 저장분이 남음(고치기 전) def test_적용_배선_추가측점_다시받기(): src = _read("B05_Profile", "B05_Profile_UI_RouteEdit_Apply.ts") body = src[src.index("export async function saveRecommendedRoute(") :] assert ( body.index("clearDrafts(projectId)") < body.index("await reloadSavedPlusStations(projectId)") < body.index("await rebuildAfterRouteSave(projectId)") )