"""560-4 D 단계 이동 = 저장 뒤 이동 · 572-2 B 떠남 물음 지킴 시험 — 서버 · 화면 없이 소스만 본다. 단계 아이콘 · [<] [>] · 머리 메뉴 · 뒤로가 한 함수(beforeLeave)를 거치고 (D02 기준 지정은 591-2 에서 걷음 · 지점 = D03). 572-3 — D · E 페이지는 공용 가드에 {dirty, save} 를 둠(말없이 저장 없음) · 캐시 = 등록표 · 프로젝트 = currentProjectId.""" import re from pathlib import Path ROOT = Path(__file__).resolve().parents[2] def _src(path: str) -> str: return (ROOT / path).read_text(encoding="utf-8") def test_group_step_waits_before_leave(): ts = _src("A00_Common/group_workflow.ts") assert "beforeLeave?: () => Promise" in ts assert "setLeaveGuard(" in ts # 머리 메뉴 · 뒤로 assert "leave().then((ok) => ok && navigateTo(" in ts # 단계 아이콘 · [<] [>] # 572-2 B 페이지 떠남 물음 — 공용 가드에 {dirty, save} · 지금 프로젝트 = 탭마다(currentProjectId) B_GUARDS = { "B04_PreProcess/B04_PreProcess_UI_Page.ts": ( "mapViewer.drainageDirty", "mapViewer.commitDrainage", ), "B07_DesignDetail/B07_DesignDetail_UI_Page.ts": ( "cadDirty || tabs.anyDirty()", "saveEditedDrawings(store)", ), "B08_Quantity/B08_Quantity_UI_Page.ts": ("readState(name, projectId)", "save: saveShown"), "B09_Estimation/B09_Estimation_UI_Shell.ts": ( 'readState("b09-estimation", projectId)', "save: saveChanged", ), } B_PROJECT_READS = [ "B02_ProjRegister/B02_ProjRegister_UI_Page.ts", "B03_FileInput/B03_FileInput_UI_Page.ts", "B03_FileInput/B03_FileInput_UI_Page_Flow.ts", "B03_FileInput/B03_FileInput_UI_PlanImport.ts", *B_GUARDS, "B09_Estimation/B09_Estimation_UI_Tabs_Bill_Link.ts", "B09_Estimation/B09_Estimation_UI_Tabs_Base_Quote.ts", "B09_Estimation/B09_Estimation_UI_Haul_Modal.ts", "B09_Estimation/draft/B09_Draft_Print_Page.ts", "B11_Status/B11_Status_UI_Loading.ts", ] def test_b_pages_guard_leave(): for path, (dirty, save) in B_GUARDS.items(): ts = _src(path) at = ts.index("guardProjectLeave(layout.root, {") block = ts[at : ts.index("});", at)] assert dirty in block and save in block, path b07 = _src("B07_DesignDetail/B07_DesignDetail_UI_Page.ts") assert "discard: () => discardEditedRecovery(store)" in b07 # 폐기 = CAD 자동백업도 def test_b_pages_read_tab_project(): for path in B_PROJECT_READS: ts = _src(path) # 탭 공용 localStorage 직접 읽기 · 쓰기 없음 assert "CURRENT_PROJECT_ID_KEY" not in ts, path assert "currentProjectId()" in ts or "setCurrentProject(" in ts, path def test_b_pages_leave_other_project_cache_to_enter(): # 다른 프로젝트 보관함 지우기 = enterProject 한 곳(다른 탭 보관분을 지우지 않게) for path in ( "B04_PreProcess/B04_PreProcess_UI_Page.ts", "A00_Common/design/design_view_profile.ts", "B11_Status/B11_Status_UI_Loading.ts", ): assert "purgeOtherProjects" not in _src(path), path # 572-3 D · E — 고침 판별 · 저장 · [폐기] 때 지울 등록표 칸(E03 = 별 저장 진행 중만 · 지울 칸 없음) DE_GUARDED = { "D03_PlanAnalysis/D03_PlanAnalysis_UI_Page.ts": "d03-plan", "D04_PlanConfirm/D04_PlanConfirm_UI_Page.ts": "d04-assignees", "E02_StabGround/E02_StabGround_UI_Page.ts": "e02-ground", "E03_StabSection/E03_StabSection_UI_Page.ts": None, "E05_StabReport/E05_StabReport_UI_Page.ts": "e05-report", "E06_StabRock/E06_StabRock_UI_Page.ts": "e06-rock", } DE_PAGES = [ *DE_GUARDED, "E03_StabSection/E03_StabSection_UI_Prob.ts", "E04_Stab3D/E04_Stab3D_UI_Page.ts", "E07_StabArea/E07_StabArea_UI_Page.ts", ] DE_STATES = [ "e02-ground", "e03-prob", "e04-settings", "e05-report", "e06-rock", "e07-settings", ] def test_de_pages_guard_leave(): for path, name in DE_GUARDED.items(): ts = _src(path) call = ts[ts.index("mountGroupStep(root") :] call = call[: call.index("});")] assert "dirty: () =>" in call and "save" in call, path assert "beforeLeave" not in ts, path # 말없이 저장 없음 — 공용 셋 물음 if name: assert f'discard: () => clearState("{name}", projectId)' in call, path # D03 — 못 읽은 채(프로젝트 없음 · 불러오기 실패) 떠나면 고침 없음 assert "let savedJson = JSON.stringify({ network, points, pointNames, site });" in _src( "D03_PlanAnalysis/D03_PlanAnalysis_UI_Page.ts" ) e03 = _src("E03_StabSection/E03_StabSection_UI_Page.ts") assert "dirty: () => repPending > 0" in e03 and "repPending -= 1" in e03 def test_de_pages_shared_project_and_registry(): for path in DE_PAGES: ts = _src(path) assert "CURRENT_PROJECT_ID_KEY" not in ts and "localStorage" not in ts, path assert "sessionStorage" not in ts, path if path.endswith("_UI_Page.ts"): assert "const projectId = currentProjectId();" in ts, path registry = _src("A00_Common/b_page_state.ts") for name in DE_STATES: assert re.search(rf'"{name}": \{{[^}}]*bucket: "draft",[^}}]*scope: "project"', registry), ( name )