- 좌측 패널 · 아래 단추 넷 · 종단도 · 구조물 배치 폼을 페이지에 한 번만 만듦(design_shared) — 두 보기가 같은 노드 - 보기는 가운데 위 칸만: 종단 보기 = 3D(design_view_profile) · 횡단 보기 = 횡단 카드(design_view_cross · 처음 보일 때 그림) - 옛 B05_Profile_UI_Page · B06_Section_UI_Page 삭제 · 한 벌이 받은 조작은 지금 보기로, 관 목록 · 횡단 재계산 알림은 두 보기 모두로 - 진행 단계 종단설계 · 횡단설계 두 칸이 보기를 따라 켜짐 · 누르면 주소만 바꿈 · 제목도 따라감(setTitle · setActiveStep) - [저장] · [확정] 한 벌 — 두 보기 몫을 모아 saveDesign 한 번 · 종단 보기 [확정] 켬(횡단 보기를 숨긴 채 세워 횡단 자료를 얻음) - [초기화] · 계획노선 편집은 같은 페이지라도 다시 세움(remountRoute) - 덧붙임: 방향키는 초점 있는 쪽만(조정창 ↔ 종단도 계획고) · 횡단 보기 계획고 편집에도 [3D 업데이트] 대기 표시 · 종단 보기 폼 관 옵션을 횡단 캐시에도 얹음 · 종단 보기에서 횡단 조정 칸 가림 - 나눔: Cross_View 756→658(_Cross_View_Frame) · Page_Structures_Panel 700→547(_Structures_Spec) · 옛 B05 Page 786 → design_view_profile 638(_Page_Latest · _Page_Stations) - 시험: test_design_view_parity 한 벌 기준(한 패널 · 보기 오가도 같은 노드) · page_single · profile_single · save_channels · save_recompute 등 옮긴 자리로 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KmELpdXFDRZHhfbGuRhSmL
109 lines
5.5 KiB
Python
109 lines
5.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""한 페이지 뼈대(계획서 24-3a) · 겹친 알약(24-6b) 회귀검증.
|
|
|
|
주소 둘(`#/b05-profile` · `#/b06-section`)이 같은 페이지(`A00_Common/design/design_page.ts`)를
|
|
열고, 같은 페이지 안 이동은 다시 만들지 않고 보기만 바꾸는지, 떠날 때 3D(WebGL 문맥) ·
|
|
종단도 · 횡단을 정리하는지, 숨긴 3D 는 그리기 루프를 멈추는지, 창에 건 키 듣기를 떼는지 본다.
|
|
화면 수치(문맥 수 · 루프 · 같은 노드 · 전환 시간)는 ORCA 로 잰다 — 여기서는 길이 끊기지 않게 잠근다.
|
|
"""
|
|
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent.parent
|
|
|
|
|
|
def _read(relative: str) -> str:
|
|
return (ROOT / relative).read_text(encoding="utf-8")
|
|
|
|
|
|
def test_주소_둘이_같은_페이지():
|
|
router = _read("A00_Common/router.ts")
|
|
assert router.count('(await import("./design/design_page")).renderDesignPage') == 2
|
|
assert "export function showWithinPage(" in router
|
|
# 다른 페이지로 떠나면 정리 · 늦게 끝난 옛 렌더는 버림
|
|
assert "mounted?.dispose();" in router
|
|
assert "if (page && seq !== renderSeq) page.dispose();" in router
|
|
|
|
|
|
def test_같은_페이지면_셸도_다시_안_그림():
|
|
main = _read("A00_Common/main.ts")
|
|
within = main.index("showWithinPage(currentRoute())")
|
|
assert within < main.index("renderShell(appRoot")
|
|
assert "if (within) return;" in main
|
|
|
|
|
|
def test_보기_약속_가운데만():
|
|
"""끝 모습(24-3b) — 보기는 가운데 위 칸만 채우고 좌측 패널 · 단추 · 종단도는 한 벌을 쓴다."""
|
|
page = _read("A00_Common/design/design_page.ts")
|
|
assert "export interface DesignPageView" in page
|
|
assert "mount?: (root" not in page # 보기가 틀을 통째로 그리던 중간 길은 걷음
|
|
assert "mountCenter: (" in page and "shared: DesignPageShared" in page
|
|
assert 'import("./design_view_profile")).mountProfileView' in page
|
|
assert 'import("./design_view_cross")).mountCrossView' in page
|
|
for hook in ("onShow:", "onHide:", "onDispose:"):
|
|
assert hook in page
|
|
# 보기는 처음 보일 때 한 번만 만든다 — 있으면 보이기만.
|
|
assert "if (slot) {\n slot.show();" in page
|
|
|
|
|
|
def test_페이지가_숨김_떠남을_건다():
|
|
life = _read("B05_Profile/B05_Profile_UI_Page_Life.ts")
|
|
assert "deps.viewer.setActive(false)" in life and "deps.viewer.dispose()" in life
|
|
assert "bindProfileViewLife(host," in _read("A00_Common/design/design_view_profile.ts")
|
|
cross = _read("A00_Common/design/design_view_cross.ts")
|
|
assert "sectionView.dispose();" in cross[cross.index("host.onDispose(") :]
|
|
# 종단도는 한 벌 — 보기가 숨어도 그대로 두고, 페이지를 떠날 때 한 번 정리한다.
|
|
assert "shared.profile?.dispose();" in _read("A00_Common/design/design_page.ts")
|
|
|
|
|
|
def test_3D_멈춤과_문맥_놓기():
|
|
viewer = _read("B05_Profile/B05_Profile_UI_Viewer.ts")
|
|
assert "setActive(active)" in viewer
|
|
assert "renderer.forceContextLoss();" in viewer
|
|
# 보관 지형은 다음 뷰어가 쓴다 — 떠날 때 버리지 않는다.
|
|
assert "if (terrain !== cachedTerrain.object) disposeObject(terrain);" in viewer
|
|
assert len(viewer.splitlines()) <= 700
|
|
|
|
|
|
def test_창_키_듣기를_뗌():
|
|
tools = _read("B05_Profile/B05_Profile_UI_Profile_Panel_Tools.ts")
|
|
assert 'window.removeEventListener("keydown", onKeyDown);' in tools
|
|
assert 'window.addEventListener("keydown", (event)' not in tools
|
|
profile = _read("A00_Common/design/design_profile.ts")
|
|
assert "setListening(false);" in profile # dispose
|
|
pick = _read("B05_Profile/B05_Profile_UI_Viewer_Structure_Pick.ts")
|
|
assert "setListening(on)" in pick
|
|
|
|
|
|
def test_겹친_알약은_누른_것을_그대로():
|
|
profile = _read("A00_Common/design/design_profile.ts")
|
|
assert "stationIdAtStructure(selectedStructureId) === stationId" in profile
|
|
|
|
|
|
def test_진행_단계_두_칸이_보기를_따라감():
|
|
"""종단설계 · 횡단설계 두 칸은 같은 페이지 — 누르면 주소만 바꿔 보기만 바뀐다(계획서 24-3b)."""
|
|
page = _read("A00_Common/design/design_page.ts")
|
|
assert "const VIEW_STEP: Record<DesignView, number> = { profile: 2, cross: 3 };" in page
|
|
assert "layout?.setActiveStep(VIEW_STEP[view]);" in page
|
|
assert "layout?.setTitle(" in page
|
|
click = page[page.index("onStepClick: (stepIndex) => {") :][:260]
|
|
assert "if (designViewOf(route)) navigateTo(route);" in click
|
|
layout = _read("ui_template/ui_template_workflow_layout.ts")
|
|
assert "setActiveStep: (stepIndex) =>" in layout and "setTitle: (title) =>" in layout
|
|
|
|
|
|
def test_초기화_계획노선_편집은_다시_세움():
|
|
"""같은 페이지 안 이동은 보기만 바꾸므로 [초기화] · 계획노선 편집은 페이지를 다시 세운다."""
|
|
router = _read("A00_Common/router.ts")
|
|
assert "export function remountRoute(" in router
|
|
assert "remountRoute(ROUTES.B05_PROFILE)" in _read("A00_Common/design/design_reset.ts")
|
|
assert "remountRoute(ROUTES.B05_PROFILE)" in _read("A00_Common/design/design_view_profile.ts")
|
|
|
|
|
|
def test_종단_보기_확정은_횡단_보기를_숨긴_채_세움():
|
|
page = _read("A00_Common/design/design_page.ts")
|
|
assert "ensureView: (view) => (slots.get(view) ?? mountView(view, false)).ready," in page
|
|
cross = _read("A00_Common/design/design_view_cross.ts")
|
|
# 숨긴 채 섰으면 한 벌 폼은 종단 보기 몫 — 보일 때 싣는다.
|
|
assert "if (host.visible()) void structuresPanel.load()" in cross
|