Merge remote-tracking branch 'origin/sub_laptop_3' into main_laptop_1
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
/* 520-7 배수유역 저장분 받기 헬퍼 — `B05_Profile_UI_Drainage_Saved.ts` · `_Run.ts` 를 그 자리에서
|
||||
* 트랜스파일해 가짜 fetch 로 돌리고 단계별 GET 수 · 「올렸나」를 JSON 으로 냄.
|
||||
*/
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript"));
|
||||
|
||||
const B05 = path.join(__dirname, "..", "..", "B05_Profile");
|
||||
|
||||
/* ---- 가짜 저장분 GET(답은 시험이 정할 때) ---- */
|
||||
const gets = [];
|
||||
let pending = null; // 세션 관 초안
|
||||
const detailCalls = [];
|
||||
const stubs = {
|
||||
"../B04_PreProcess/B04_PreProcess_Api_Fetch": {
|
||||
fetchDetailPipePoints: () => new Promise((resolve) => gets.push(resolve)),
|
||||
computeDetailBasins: async (_projectId, points) => (
|
||||
detailCalls.push(points),
|
||||
{ basins: [], pipe_points: points, stale: null }
|
||||
),
|
||||
},
|
||||
"./B05_Profile_Api_Pipes_Draft": { readPendingPipes: () => pending },
|
||||
"./B05_Profile_UI_Drainage_Parts": { analyzeFailed: (_error, fallback) => fallback },
|
||||
};
|
||||
const cache = new Map();
|
||||
function load(file) {
|
||||
if (cache.has(file)) return cache.get(file);
|
||||
const js = ts.transpileModule(fs.readFileSync(file, "utf8"), {
|
||||
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
|
||||
}).outputText;
|
||||
const box = { exports: {} };
|
||||
cache.set(file, box.exports);
|
||||
const local = (name) => {
|
||||
if (stubs[name]) return stubs[name];
|
||||
if (name.startsWith(".")) return load(path.join(path.dirname(file), `${name}.ts`));
|
||||
return require(name);
|
||||
};
|
||||
new Function("exports", "module", "require", js)(box.exports, box, local);
|
||||
return box.exports;
|
||||
}
|
||||
|
||||
const { createSavedLoader } = load(path.join(B05, "B05_Profile_UI_Drainage_Saved.ts"));
|
||||
const { createDetailRunner } = load(path.join(B05, "B05_Profile_UI_Drainage_Run.ts"));
|
||||
const flush = async () => {
|
||||
for (let i = 0; i < 10; i += 1) await new Promise((r) => setImmediate(r));
|
||||
};
|
||||
const el = () => ({ hidden: false, textContent: "", disabled: false });
|
||||
|
||||
async function main() {
|
||||
const out = {};
|
||||
let pipes = [{ chainage_m: 386, source: "stream" }];
|
||||
const shown = [];
|
||||
let loader;
|
||||
const runner = createDetailRunner({
|
||||
projectId: () => "p1",
|
||||
updateButton: el(),
|
||||
status: el(),
|
||||
showProgress: () => undefined,
|
||||
apply: (response) => (shown.push(response), loader.applied(response)), // 패널 takePipes 와 같은 줄
|
||||
basinCount: () => 1,
|
||||
scheduleDraw: () => undefined,
|
||||
label: (key) => key,
|
||||
});
|
||||
loader = createSavedLoader(runner, () => pipes);
|
||||
const answer = (tag) => ({ tag, basins: [], pipe_points: pipes, stale: null });
|
||||
const results = [];
|
||||
const keep = (promise) => promise.then((ok) => results.push(ok));
|
||||
|
||||
// 열 때 — GET 한 번
|
||||
keep(loader.run());
|
||||
await flush();
|
||||
gets[0](answer("saved1"));
|
||||
await flush();
|
||||
out.open = { gets: gets.length, results: [...results], matches: loader.matches() };
|
||||
|
||||
// 되돌리기 ① — 관 자리가 노선 따라 옮겨진 뒤(몸통 다름): 관 자리 되돌림 · reload · 초안 버림 한 번에
|
||||
pipes = [{ chainage_m: 386.4, source: "stream" }];
|
||||
results.length = 0;
|
||||
const before = gets.length;
|
||||
keep(loader.matches() ? Promise.resolve(true) : loader.run(true)); // setPipeChainages(null)
|
||||
keep(loader.run()); // followFacilities → reload()
|
||||
keep(loader.back()); // 묶음 초안 버림 → backToSaved
|
||||
await flush();
|
||||
pipes = [{ chainage_m: 386, source: "stream" }];
|
||||
gets[gets.length - 1](answer("saved2"));
|
||||
await flush();
|
||||
out.undoMoved = { gets: gets.length - before, results: [...results], shown: shown.at(-1).tag };
|
||||
|
||||
// 되돌리기 ② — 화면이 저장분 그대로 · 초안 버림이 먼저 오고 reload 가 뒤따름
|
||||
results.length = 0;
|
||||
const before2 = gets.length;
|
||||
const shownBefore = shown.length;
|
||||
keep(loader.back());
|
||||
keep(loader.run());
|
||||
await flush();
|
||||
gets[gets.length - 1](answer("saved3"));
|
||||
await flush();
|
||||
out.backThenReload = {
|
||||
gets: gets.length - before2,
|
||||
results: [...results],
|
||||
shown: shown.slice(shownBefore).map((r) => r.tag),
|
||||
};
|
||||
|
||||
// 초안 버림만 — 화면이 저장분 그대로면 GET 없이 그 답을 다시 올림
|
||||
results.length = 0;
|
||||
const before3 = gets.length;
|
||||
keep(loader.back());
|
||||
await flush();
|
||||
out.backOnly = { gets: gets.length - before3, results: [...results], shown: shown.at(-1).tag };
|
||||
|
||||
// 다른 답(지금 노선 [업데이트] 등)이 오른 뒤 초안 버림 — 다시 받음
|
||||
await runner.run(async () => answer("live"), true);
|
||||
results.length = 0;
|
||||
const before4 = gets.length;
|
||||
keep(loader.back());
|
||||
await flush();
|
||||
gets[gets.length - 1](answer("saved4"));
|
||||
await flush();
|
||||
out.afterLive = { gets: gets.length - before4, results: [...results], shown: shown.at(-1).tag };
|
||||
|
||||
// 세션 관 초안이 있으면 그 관 목록으로(GET 아님)
|
||||
pending = [{ chainage_m: 500, source: "user" }];
|
||||
const before5 = gets.length;
|
||||
results.length = 0;
|
||||
keep(loader.run(true));
|
||||
await flush();
|
||||
out.pending = { gets: gets.length - before5, detail: detailCalls.length, results: [...results] };
|
||||
|
||||
process.stdout.write(JSON.stringify(out));
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
process.stderr.write(String(error && error.stack ? error.stack : error));
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -104,9 +104,12 @@ def test_B06_초안_쓰면_판이_한_번_다시_나눔():
|
||||
|
||||
|
||||
def test_다시_나눔은_세션_초안으로_계산만_저장_안_함():
|
||||
load = _body(PANEL, "const loadSaved = ", "const analyze = ")
|
||||
assert "readPendingPipes(projectId as string)" in load
|
||||
assert "computeDetailBasins(projectId as string, pending)" in load
|
||||
# 저장분 받기 = 패널 밖 `_Drainage_Saved`(520-7)
|
||||
saved = _read("B05_Profile", "B05_Profile_UI_Drainage_Saved.ts")
|
||||
load = _body(saved, "const load = ", "const matches = ")
|
||||
assert "readPendingPipes(projectId)" in load
|
||||
assert "computeDetailBasins(projectId, pending)" in load
|
||||
assert "const savedLoad = createSavedLoader(runner, pipeRequest);" in PANEL
|
||||
analyze = _body(
|
||||
PANEL, "const analyze = ", "const live = createDrainageLive("
|
||||
) # 326-6 [유역 재분할] 걷음
|
||||
|
||||
@@ -65,16 +65,14 @@ def test_삭제_직후_되돌리기_단추_켜짐(out) -> None:
|
||||
|
||||
def test_배수_판_저장분_견줌() -> None:
|
||||
panel = _read("B05_Profile", "B05_Profile_UI_Drainage_Panel.ts")
|
||||
saved = _read("B05_Profile", "B05_Profile_UI_Drainage_Saved.ts") # 저장분 받기(520-7)
|
||||
# 저장분 답을 받은 때만 견줄 몸통을 적음 · 초안 없는 걸음은 다르면 조용히 다시 읽음
|
||||
assert "(response) => ((saved = { response, request: null }), response)" in panel
|
||||
assert (
|
||||
"if (saved?.response === response) saved.request = JSON.stringify(pipeRequest());" in panel
|
||||
)
|
||||
assert (
|
||||
"return void (JSON.stringify(pipeRequest()) !== saved?.request && run(loadSaved, true));"
|
||||
in panel
|
||||
)
|
||||
assert "if (projectId !== nextProjectId) saved = null;" in panel
|
||||
assert "(response) => ((saved = { response, request: null }), response)" in saved
|
||||
assert "showing = saved?.response === response;" in saved
|
||||
assert "if (showing) saved!.request = JSON.stringify(pipeRequest());" in saved
|
||||
assert "savedLoad.applied(response);" in panel
|
||||
assert "return void (!savedLoad.matches() && savedLoad.run(true));" in panel
|
||||
assert "if (projectId !== nextProjectId) savedLoad.reset();" in panel
|
||||
tools = _read("B05_Profile", "B05_Profile_UI_Profile_Panel_Tools.ts")
|
||||
assert "initialPipes" not in tools
|
||||
profile = _read("A00_Common", "design", "design_profile.ts")
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
"""520-7 배수유역 저장분 받기 — 되돌리기(Ctrl+Z) 한 번에 셋이 부르는 저장분 GET 을 한 번으로.
|
||||
|
||||
화면 파일(`B05_Profile_UI_Drainage_Saved` · `_Run`)을 가짜 fetch 로 돌림(`helper_520_7_drainage_saved_once.cjs`).
|
||||
|
||||
- 관 자리 되돌림 · 시설 따라감 reload · 묶음 초안 버림이 한 번에 = GET 한 번 · 셋 다 「올렸음」.
|
||||
- 초안 버림이 먼저 · reload 가 뒤따름 = GET 한 번 · 초안 버림도 「올렸음」(낡음 남지 않음).
|
||||
- 화면이 저장분 그대로면 초안 버림 = GET 없이 그 답 다시 올림 · 다른 답이 오른 뒤면 다시 받음.
|
||||
- 세션 관 초안이 있으면 그 관 목록으로 세부 유역(GET 아님).
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import pytest
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def r():
|
||||
proc = subprocess.run(
|
||||
["node", os.path.join(HERE, "helper_520_7_drainage_saved_once.cjs")],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=60,
|
||||
encoding="utf-8",
|
||||
)
|
||||
assert proc.returncode == 0, proc.stderr
|
||||
return json.loads(proc.stdout)
|
||||
|
||||
|
||||
def test_open_fetches_once(r):
|
||||
assert r["open"] == {"gets": 1, "results": [True], "matches": True}
|
||||
|
||||
|
||||
def test_undo_three_callers_one_get(r):
|
||||
assert r["undoMoved"] == {"gets": 1, "results": [True, True, True], "shown": "saved2"}
|
||||
|
||||
|
||||
def test_back_before_reload_still_applied(r):
|
||||
assert r["backThenReload"] == {"gets": 1, "results": [True, True], "shown": ["saved3"]}
|
||||
|
||||
|
||||
def test_back_reuses_shown_saved_answer(r):
|
||||
assert r["backOnly"] == {"gets": 0, "results": [True], "shown": "saved3"}
|
||||
assert r["afterLive"] == {"gets": 1, "results": [True], "shown": "saved4"}
|
||||
|
||||
|
||||
def test_pending_pipes_use_detail_not_get(r):
|
||||
assert r["pending"] == {"gets": 0, "detail": 1, "results": [True]}
|
||||
@@ -140,10 +140,10 @@ def test_집계_항목만_바뀌면_폼_검증과_무관하게_기록에_반영(
|
||||
def test_배수유역_화면은_들어올_때_세션_초안_관을_먼저_씀():
|
||||
# 흠 3 — B05→B06→B05 를 오가도 저장 전 지정 · 옵션이 옛값으로 안 돌아감
|
||||
source = open(
|
||||
os.path.join(HERE, "..", "..", "B05_Profile", "B05_Profile_UI_Drainage_Panel.ts"),
|
||||
os.path.join(HERE, "..", "..", "B05_Profile", "B05_Profile_UI_Drainage_Saved.ts"),
|
||||
encoding="utf-8",
|
||||
).read()
|
||||
body = source.split("const loadSaved", 1)[1].split("/**", 1)[0]
|
||||
).read() # 저장분 받기 = 패널 밖(520-7)
|
||||
body = source.split("const load =", 1)[1].split("const matches", 1)[0]
|
||||
assert "readPendingPipes(" in body and "computeDetailBasins(" in body
|
||||
assert body.index("readPendingPipes(") < body.index("fetchDetailPipePoints(")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user