diff --git a/resources/tester/helper_346_2_draft_cross_pills.cjs b/resources/tester/helper_346_2_draft_cross_pills.cjs deleted file mode 100644 index 8c25fc387..000000000 --- a/resources/tester/helper_346_2_draft_cross_pills.cjs +++ /dev/null @@ -1,231 +0,0 @@ -/* PLAN 346-2 덧 3 헬퍼 — 노선 초안 동안 횡단 카드 = 묶음 새 노선 상세 · 종단 알약 = follow 뒤 자리. - * 진짜 TS(`B06_Section_UI_Page_Station_Swap` · `design_pipeline` · `design_store` · `B05_Profile_UI_Structures_Marks` · - * `logic/B05_Profile_Structure_Follow`)를 그 자리에서 트랜스파일해 Node 로 돌림. 페이지 묶음 창구 - * (`design_pipeline_page` — pagePipeline · pipelineDetail)와 알약 이름 · 벌룬 · 서버 창구만 가짜. - * 결과 = 표준출력 JSON. 짝 = `test_346_2_draft_cross_pills.py`. */ -const fs = require("fs"); -const path = require("path"); -const { makeLoader } = require("./helper_ts_stub_loader.cjs"); - -const ROOT = path.join(__dirname, "..", ".."); -const P = "p3463"; -const R = 7; - -function memoryStorage() { - const map = new Map(); - return { - getItem: (key) => (map.has(key) ? map.get(key) : null), - setItem: (key, value) => map.set(key, String(value)), - removeItem: (key) => map.delete(key), - key: (index) => [...map.keys()][index] ?? null, - get length() { - return map.size; - }, - }; -} -globalThis.window = globalThis; -globalThis.sessionStorage = memoryStorage(); -globalThis.localStorage = memoryStorage(); -localStorage.setItem("frd_current_project_id", P); -const element = () => { - const node = { - style: {}, - dataset: {}, - children: [], - classList: { toggle: () => undefined, add: () => undefined }, - append: (...items) => node.children.push(...items), - prepend: (item) => node.children.unshift(item), - addEventListener: () => undefined, - }; - return node; -}; -globalThis.document = { createElement: element }; - -/* 페이지 묶음 창구 가짜 — 묶음은 진짜(`createDesignPipeline`) · 지도는 시험이 쥠 */ -const fake = { pipe: null, detail: null }; -const STUBS = { - design_pipeline_page: { - pagePipeline: (id) => (id === P ? fake.pipe : null), - pipelineDetail: (id) => (id === P ? fake.detail : null), - }, - B06_Section_Section_Store: { - onAnySwap: () => () => undefined, - swapSectionStations: async () => null, - }, - touch_bridge: { pickPx: () => 14 }, - B05_Profile_Api_Structures: { - structureAnchorM: (s) => s.chainage_m ?? s.start_m ?? 0, - }, - B05_Profile_UI_Structures_List: { etcName: () => null, numberDuplicateNames: (names) => names }, - B05_Profile_UI_Structures_Balloon: { balloonLines: () => [] }, - common_util_structure_catalog: { - catalogRow: (id) => (id === "wall" ? { placement: "interval", owner: "structures" } : null), - }, -}; - -const { load, loadTs } = makeLoader({ - root: ROOT, - requireFor: (full) => (request) => { - const key = Object.keys(STUBS).find((name) => request.endsWith(name)); - if (key) return STUBS[key]; - if (request.endsWith("config_frontend") || request.startsWith("@config/")) - return { CURRENT_PROJECT_ID_KEY: "frd_current_project_id", API_BASE_URL: "http://fake" }; - if (request.startsWith("@util/")) - return loadTs(path.join(ROOT, "common_util", `${request.slice(6)}.ts`)); - if (request.startsWith(".")) return loadTs(path.join(path.dirname(full), `${request}.ts`)); - return require(request); - }, -}); - -const pipeMod = load("A00_Common", "design", "logic", "design_pipeline.ts"); -const storeMod = load("A00_Common", "design", "design_store.ts"); -const swap = load("B06_Section", "B06_Section_UI_Page_Station_Swap.ts"); -const marks = load("B05_Profile", "B05_Profile_UI_Structures_Marks.ts"); -const follow = load("B05_Profile", "logic", "B05_Profile_Structure_Follow.ts"); - -const tick = (ms = 30) => new Promise((resolve) => setTimeout(resolve, ms)); -const out = {}; - -async function main() { - /* 1. 횡단 카드 — 저장 노선 → 노선 초안(새 노선 상세) → 숨은 동안 안 그림 → 초안 버림(저장 노선) */ - { - const saved = { tag: "saved", cross_sections: [] }; - const drawn = []; - let shown = true; - const before = swap.shownDetail(P, saved).tag; - // 횡단 보기가 먼저 섬 — 묶음은 아직 없음(종단 보기가 세움) - const stop = swap.followSwaps( - P, - () => R, - { visible: () => shown }, - async () => void drawn.push(swap.shownDetail(P, saved).tag), - ); - const pipe = pipeMod.createDesignPipeline({ - stages: { - profile: (ctx) => ({ grade: ctx.inputs.alignment }), - cross: () => ({ byKey: {} }), - }, - }); - // 페이지 묶음과 같은 차례 — 가게 잇기 · 새 노선 상세(초안 동안만) · 그다음 듣는 쪽 - pipe.subscribe(pipeMod.publishToStore(storeMod.useDesignStore(P, null))); - pipe.subscribe((event) => { - if (event.kind !== "stage" || (event.stage !== "profile" && event.stage !== "cross")) return; - fake.detail = pipe.input("route") - ? { tag: `draft-${event.stage}-${event.generation}`, cross_sections: [] } - : null; - }); - fake.pipe = pipe; - await pipe.start({ - route: { rev: 1 }, - alignment: 1, - cross: null, - pipes: null, - structures: null, - }); - await tick(); - const afterDraft = [...drawn]; - const draftNow = swap.shownDetail(P, saved).tag; - shown = false; - await pipe.update("grade", 2); - await tick(); - const whileHidden = [...drawn]; - const hiddenNow = swap.shownDetail(P, saved).tag; - shown = true; - await pipe.update("route", null); - await tick(); - await pipe.update("grade", 3); - await tick(); - const discarded = [...drawn]; - const savedNow = swap.shownDetail(P, saved).tag; - stop(); - await pipe.update("route", { rev: 2 }); - await pipe.update("grade", 4); - await tick(); - out.cards = { - before, - afterDraft, - draftNow, - whileHidden, - hiddenNow, - discarded, - savedNow, - afterStop: drawn.length, - }; - pipe.dispose(); - fake.pipe = null; - fake.detail = null; - } - - /* 2. 종단 알약 — follow 뒤 자리(구조물 id 짝 · 관은 서버 id 와 세션 id 가 달라 차례 짝 · 구간형 띠도) */ - { - const pill = (structure_id, type_id, chainage_m, start_m = null, end_m = null) => ({ - structure_id, - type_id, - placement: start_m === null ? "point" : "interval", - chainage_m, - start_m, - end_m, - options: {}, - }); - const pills = [ - pill("pipe-s1", "pipe", 30), - pill("st-1", "gabion", 50), - pill("st-2", "wall", 70, 60, 80), - pill("pipe-s2", "pipe", 90), - pill("st-9", "gabion", 120), - ]; - const inputs = { - route: { rev: 3 }, - structures: [ - { structure_id: "st-1", chainage_m: 55 }, - { structure_id: "st-2", chainage_m: 72, start_m: 62, end_m: 82 }, - ], - pipes: [ - { id: "PB", chainage_m: 96 }, - { id: "PA", chainage_m: 33 }, - ], - }; - fake.pipe = { input: (key) => inputs[key] ?? null }; - const lane = () => { - const root = marks.buildStructureLane({ - structures: pills, - types: [], - x: (chainage) => chainage * 10, - chainageAt: (px) => px / 10, - maxChainageM: 200, - widthPx: 2000, - axisWidthPx: 40, - stationIntervalM: 20, - selectedId: null, - onSelect: () => undefined, - onMove: () => undefined, - }); - const left = {}; - const band = []; - for (const node of root.children) { - if (node.dataset.structureId) left[node.dataset.structureId] = parseFloat(node.style.left); - if (node.className?.startsWith("b05-structure__band")) - band.push(parseFloat(node.style.left), parseFloat(node.style.width)); - } - return { left, band }; - }; - const draft = lane(); - inputs.route = null; // 초안 버림 — 저장 자리 - const saved = lane(); - // 관 수가 안 맞으면 관 알약은 안 옮김(잘못 짝짓지 않음) - const short = follow.followedMarks(pills, inputs.structures, [{ id: "PA", chainage_m: 33 }]); - out.pills = { - draft, - saved, - short: short.map((s) => s.chainage_m), - untouched: pills[1].chainage_m, - }; - fake.pipe = null; - } - - process.stdout.write(JSON.stringify(out)); -} -main().catch((error) => { - console.error(error); - process.exit(1); -}); diff --git a/resources/tester/helper_418_reset_drafts.cjs b/resources/tester/helper_418_reset_drafts.cjs index ef1ac63a0..d9cf957e1 100644 --- a/resources/tester/helper_418_reset_drafts.cjs +++ b/resources/tester/helper_418_reset_drafts.cjs @@ -24,6 +24,7 @@ const STUBS = { }, B05_Profile_UI_Profile_Edit: { clearAlignmentDrafts: () => {} }, B06_Section_Section_Store: { invalidateSectionDetail: () => {} }, + design_save: { designSaveBusy: () => false, SAVE_BUSY_TEXT: "" }, }; const cache = new Map(); diff --git a/resources/tester/helper_440_draft_card_ids.cjs b/resources/tester/helper_440_draft_card_ids.cjs index 443ddf9f8..0b7cc3559 100644 --- a/resources/tester/helper_440_draft_card_ids.cjs +++ b/resources/tester/helper_440_draft_card_ids.cjs @@ -12,6 +12,7 @@ const STUBS = { B05_Profile_Pipeline_Grade: { stationIdOf: idOf }, common_util_station: { stationLabel: (m) => String(m) }, B05_Profile_UI_Profile_Data: { toDesignProfile: () => ({}) }, + design_pipeline_types: { SOURCE_INPUT: {} }, b_page_state: { readState: () => null }, B06_Section_Section_Canon: { savedDesigns: () => null }, B06_Section_UI_Standard_Panel: { diff --git a/resources/tester/test_311_panel_folded.py b/resources/tester/test_311_panel_folded.py index 35528886e..6a6ff2864 100644 --- a/resources/tester/test_311_panel_folded.py +++ b/resources/tester/test_311_panel_folded.py @@ -120,8 +120,8 @@ def test_389_3_공용_카드_기본값_닫힘_그_밖_자리(): ).replace("\r\n", "\n") form = _read("B05_Profile/B05_Profile_UI_Structures_Form.ts") assert "ui-collapsible ui-sidebar-section is-collapsed" in form - # 우클릭 추가 = 구조물 배치 카드를 폄(116-1) 그대로 - assert 'root.classList.remove("is-collapsed");' in _read( + # 우클릭 추가도 카드의 사용자가 정한 열림 상태를 바꾸지 않는다(513-2). + assert 'root.classList.remove("is-collapsed");' not in _read( "B05_Profile/B05_Profile_UI_Structures_Panel.ts" ) # M02 구조물 집계 양식도 처음엔 닫힘 — 열림 해제 줄 없음 diff --git a/resources/tester/test_343_4_derived.py b/resources/tester/test_343_4_derived.py index a5a799835..2e7aaa6dc 100644 --- a/resources/tester/test_343_4_derived.py +++ b/resources/tester/test_343_4_derived.py @@ -64,15 +64,15 @@ def _wire(monkeypatch: pytest.MonkeyPatch, root: Path, outputs: list) -> list: async def _stored(_fn, _project_id): # type: ignore[no-untyped-def] return "p" - def _run(_bundle, _script, payload): # type: ignore[no-untyped-def] - calls.append(payload) + def _run(result, context): # type: ignore[no-untyped-def] + calls.append((result, context)) return outputs.pop(0) monkeypatch.setattr(haul, "haul_inputs_for", _none) monkeypatch.setattr(haul, "conversion_factors_for", _none) monkeypatch.setattr(haul, "haul_limits_for", _none) monkeypatch.setattr(haul, "_mass_haul_context", lambda *_: {"limits": []}) - monkeypatch.setattr(haul, "run_bundle_json", _run) + monkeypatch.setattr(haul, "_run_view", _run) monkeypatch.setattr(haul, "run_with_connection", _stored) monkeypatch.setattr(haul, "resolve_stored_project_path", lambda _p: str(root)) haul._KEPT.clear() diff --git a/resources/tester/test_346_2_draft_cross_pills.py b/resources/tester/test_346_2_draft_cross_pills.py deleted file mode 100644 index 3e424dea6..000000000 --- a/resources/tester/test_346_2_draft_cross_pills.py +++ /dev/null @@ -1,81 +0,0 @@ -# -*- coding: utf-8 -*- -"""346-2 덧 3 — 노선 초안 동안 횡단 카드 · 종단 구조물 알약. - -못박는 것 - - 횡단 카드 = 노선 초안 동안 묶음 새 노선 상세(`pipelineDetail`) · profile · cross 단계 뒤 다시 그림 · - 숨은 동안은 안 그림(상세는 새것) · 초안을 버리면 저장 노선 카드 · 묶음이 횡단 보기보다 늦게 서도 붙음 · - 끊으면 안 그림. - - 종단 알약 = follow 뒤 자리 — 구조물은 id 짝 · 관은 세션 id ≠ 서버 id 라 누가거리 차례 짝 · - 구간형 띠도 옮김 · 짝 없는 것은 그대로 · 관 수가 안 맞으면 관은 안 옮김 · 초안을 버리면 저장 자리. - -Node 헬퍼 `helper_346_2_draft_cross_pills.cjs` 가 진짜 TS 를 트랜스파일해 부름. -""" - -import json -import subprocess -from pathlib import Path - -import pytest - -HERE = Path(__file__).resolve().parent -ROOT = HERE.parents[1] - - -@pytest.fixture(scope="module") -def out(): - proc = subprocess.run( - ["node", str(HERE / "helper_346_2_draft_cross_pills.cjs")], - cwd=ROOT, - capture_output=True, - text=True, - encoding="utf-8", - timeout=120, - ) - assert proc.returncode == 0, proc.stderr - return json.loads(proc.stdout) - - -def test_노선_초안_동안_카드는_새_노선_상세(out): - cards = out["cards"] - assert cards["before"] == "saved" - assert cards["afterDraft"] == ["draft-cross-1"] # profile 차례 중간 상세는 안 그림(365-4) - assert cards["draftNow"] == "draft-cross-1" - - -def test_숨은_동안은_안_그림(out): - cards = out["cards"] - assert cards["whileHidden"] == ["draft-cross-1"] - assert cards["hiddenNow"] == "draft-cross-2" - - -def test_초안_버리면_저장_노선_카드(out): - cards = out["cards"] - assert cards["discarded"][-1] == "saved" and cards["savedNow"] == "saved" - assert cards["afterStop"] == 2 # 끊은 뒤엔 안 그림 - - -def test_알약은_따라간_누가거리(out): - draft = out["pills"]["draft"] - # x = 누가거리 × 10 px — 관 30 → 33 · 90 → 96(차례 짝) · 구조물 id 짝 · 짝 없는 st-9 그대로 - assert draft["left"] == { - "pipe-s1": 330, - "st-1": 550, - "st-2": 720, - "pipe-s2": 960, - "st-9": 1200, - } - assert draft["band"] == [620, 200] # 구간형 띠 62 ~ 82 m - - -def test_초안_버리면_저장_자리_관_수_다르면_관은_그대로(out): - pills = out["pills"] - assert pills["saved"]["left"] == { - "pipe-s1": 300, - "st-1": 500, - "st-2": 700, - "pipe-s2": 900, - "st-9": 1200, - } - assert pills["saved"]["band"] == [600, 200] - assert pills["short"] == [30, 55, 72, 90, 120] - assert pills["untouched"] == 50 # 원본 목록은 안 고침 diff --git a/resources/tester/test_route_direct_link.py b/resources/tester/test_route_direct_link.py index 8e88f6cb3..c83105ee2 100644 --- a/resources/tester/test_route_direct_link.py +++ b/resources/tester/test_route_direct_link.py @@ -22,7 +22,7 @@ def fake_project(tmp_path: Path) -> Path: xx, yy = np.meshgrid(x, y) z = 100.0 + 0.02 * xx + 0.01 * yy # 경사 2% — 제약에 안 걸린다 np.savez_compressed( - models / "dtm_test_smooth.npz", + models / "dtm_classification_smooth.npz", x=x, y=y, z=z, @@ -37,7 +37,7 @@ def _solve(project_root: Path, pts: list[dict]) -> list[list[float]]: "ep": pts[-1], "cp": [dict(p, order=i + 1) for i, p in enumerate(pts[1:-1])], } - result = S.solve_optimal_route(project_root, "test", True, points_data, {}, "dtm") + result = S.solve_optimal_route(project_root, "classification", True, points_data, {}, "dtm") return result["polyline"]