- A00_Common/design/design_leave.ts — 고침 칸(초안 통 − 고름 · 관 id 장부 · 종류 캐시 · 구조물 넘김 · 표준 횡단면 · b08 · b09) 지문 · 모든 노선 - 횡단 초안은 [저장] 뒤에도 남음 — 저장 · 확정 끝 깨끗한 표(markDesignClean)와 다를 때만 물음 · [초기화]는 초안을 다 지움 - B05 ↔ B06 이동은 안 묻고 다시 걺 · [버리고 넘어감] = 고침 칸만 지움 · 저장 실패 = 머묾 - design_page 한 줄(걺) · design_shared 한 줄(저장 끝 표) · 문구 B05_Design_Leave · b07-extent 묶음 다시 지음 - 시험 test_346_4_design_leave.py Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QyNXY2TUKXCFMwAca2APG
168 lines
5.8 KiB
JavaScript
168 lines
5.8 KiB
JavaScript
/* 346-4 떠남 확인 단위검증 헬퍼 — `A00_Common/design/design_leave.ts` 를 그 자리에서 트랜스파일 ·
|
|
* `b_page_state` 는 진짜 · 라우터 · 물음 창 · 문구는 흉내. 결과 JSON 을 stdout 으로.
|
|
*/
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const ts = require(path.join(__dirname, "..", "..", "config", "node_modules", "typescript"));
|
|
|
|
const ROOT = path.join(__dirname, "..", "..");
|
|
|
|
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),
|
|
clear: () => map.clear(),
|
|
key: (index) => [...map.keys()][index] ?? null,
|
|
get length() {
|
|
return map.size;
|
|
},
|
|
};
|
|
}
|
|
const session = memoryStorage();
|
|
const win = new EventTarget();
|
|
win.sessionStorage = session;
|
|
win.localStorage = memoryStorage();
|
|
win.setTimeout = setTimeout;
|
|
globalThis.window = win;
|
|
globalThis.sessionStorage = session;
|
|
|
|
let route = "/b05-profile";
|
|
let guard = null;
|
|
let guardSets = 0;
|
|
let choice = "cancel";
|
|
const asked = [];
|
|
const STUBS = {
|
|
"@config/config_frontend": {
|
|
ROUTES: { B05_PROFILE: "/b05-profile", B06_SECTION: "/b06-section" },
|
|
},
|
|
"@ui/ui_template_locale": { t: (key) => key },
|
|
"@ui/ui_template_leave_dialog": {
|
|
leaveOrStay: async (message, hooks) => {
|
|
asked.push(message);
|
|
if (choice === "cancel") return "stay";
|
|
if (choice === "discard") {
|
|
await hooks.discard();
|
|
return "discarded";
|
|
}
|
|
await hooks.save();
|
|
return hooks.stillDirty() ? "stay" : "saved";
|
|
},
|
|
},
|
|
"../router": {
|
|
currentRoute: () => route,
|
|
setLeaveGuard: (fn) => {
|
|
guard = fn;
|
|
guardSets += 1;
|
|
},
|
|
},
|
|
};
|
|
|
|
function loadTs(file) {
|
|
const js = ts.transpileModule(fs.readFileSync(file, "utf8"), {
|
|
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
|
|
}).outputText;
|
|
const box = { exports: {} };
|
|
const localRequire = (request) => {
|
|
if (request in STUBS) return STUBS[request];
|
|
if (request.startsWith(".")) return loadTs(path.join(path.dirname(file), `${request}.ts`));
|
|
return require(request);
|
|
};
|
|
new Function("exports", "module", "require", js)(box.exports, box, localRequire);
|
|
return box.exports;
|
|
}
|
|
|
|
const leave = loadTs(path.join(ROOT, "A00_Common", "design", "design_leave.ts"));
|
|
const P = "p1";
|
|
const tick = () => new Promise((resolve) => setTimeout(resolve, 5));
|
|
/** 브라우저 BeforeUnloadEvent 처럼 returnValue 를 쓸 수 있게. */
|
|
class BeforeUnload extends Event {
|
|
returnValue = true;
|
|
}
|
|
const unloadBlocked = () => {
|
|
const event = new BeforeUnload("beforeunload", { cancelable: true });
|
|
win.dispatchEvent(event);
|
|
return event.defaultPrevented;
|
|
};
|
|
|
|
(async () => {
|
|
const out = {};
|
|
const root = { isConnected: true };
|
|
let saveMarks = true;
|
|
let saves = 0;
|
|
leave.guardDesignLeave(P, root, async () => {
|
|
saves += 1;
|
|
if (saveMarks) leave.markDesignClean(P);
|
|
});
|
|
|
|
out.emptyDirty = leave.designDirty(P);
|
|
// 고침이 아닌 칸 · 다른 페이지 칸 · 표준 횡단면만 — 안 물음
|
|
session.setItem(`aislo:draft:selection:${P}`, '{"chainageM":20}');
|
|
session.setItem(`aislo:draft:std-cross:${P}`, '{"soil":{}}');
|
|
session.setItem(`aislo:draft:pipe-ids:${P}`, '{"next":3}');
|
|
session.setItem(`aislo:draft:b08-materials:${P}`, '{"units":1}');
|
|
session.setItem(`aislo:draft:crossdesign:${P}:7`, "{}"); // 빈 칸
|
|
out.nonEditDirty = leave.designDirty(P);
|
|
out.nonEditUnload = unloadBlocked();
|
|
route = "/b08-quantity";
|
|
out.nonEditLeave = await guard();
|
|
out.askedNonEdit = asked.length;
|
|
|
|
// 횡단 초안(노선 7) — 물음
|
|
session.setItem(`aislo:draft:crossdesign:${P}:7`, '{"20.000":{"ground_type":"soil"}}');
|
|
out.editDirty = leave.designDirty(P);
|
|
out.editUnload = unloadBlocked();
|
|
|
|
// 같은 페이지(B05 → B06) — 안 묻고 한 틱 뒤 다시 걺
|
|
route = "/b06-section";
|
|
const setsBefore = guardSets;
|
|
out.samePage = await guard();
|
|
await tick();
|
|
out.reArmed = guardSets === setsBefore + 1;
|
|
out.askedSamePage = asked.length;
|
|
|
|
// 다른 페이지 — 취소 = 머묾
|
|
route = "/b08-quantity";
|
|
choice = "cancel";
|
|
out.cancelLeave = await guard();
|
|
|
|
// 저장 실패(깨끗한 표 없음) = 머묾 · 저장 성공 = 넘어감 · 뒤로 안 물음
|
|
choice = "save";
|
|
saveMarks = false;
|
|
out.saveFailLeave = await guard();
|
|
saveMarks = true;
|
|
out.saveLeave = await guard();
|
|
out.saves = saves;
|
|
out.afterSaveDirty = leave.designDirty(P);
|
|
out.afterSaveUnload = unloadBlocked();
|
|
const askedAfterSave = asked.length;
|
|
out.afterSaveLeave = await guard();
|
|
out.askedAfterSave = asked.length - askedAfterSave;
|
|
|
|
// 저장 뒤 다시 고침 — 다시 물음 · 버리고 넘어감 = 고침 칸만 지움
|
|
session.setItem(`aislo:draft:alignment-draft:${P}:7`, '{"station_offsets":{"20.000":0.3}}');
|
|
session.setItem(`aislo:draft:route-draft:${P}`, '{"rev":2}');
|
|
out.reEditDirty = leave.designDirty(P);
|
|
choice = "discard";
|
|
out.discardLeave = await guard();
|
|
out.left = {
|
|
crossdesign: session.getItem(`aislo:draft:crossdesign:${P}:7`),
|
|
alignment: session.getItem(`aislo:draft:alignment-draft:${P}:7`),
|
|
routeDraft: session.getItem(`aislo:draft:route-draft:${P}`),
|
|
selection: session.getItem(`aislo:draft:selection:${P}`),
|
|
stdCross: session.getItem(`aislo:draft:std-cross:${P}`),
|
|
b08: session.getItem(`aislo:draft:b08-materials:${P}`),
|
|
};
|
|
out.afterDiscardDirty = leave.designDirty(P);
|
|
|
|
// 페이지가 화면에서 빠짐 — 안 물음
|
|
session.setItem(`aislo:draft:crossdesign:${P}:7`, '{"40.000":{}}');
|
|
root.isConnected = false;
|
|
const askedGone = asked.length;
|
|
out.goneLeave = await guard();
|
|
out.goneUnload = unloadBlocked();
|
|
out.askedGone = asked.length - askedGone;
|
|
process.stdout.write(JSON.stringify(out));
|
|
})();
|