Files
Aislo/resources/tester/helper_472_open_no_draft.mjs
T

196 lines
7.1 KiB
JavaScript

/* 472 덧(요구 272 C · D) 헬퍼 — 진짜 모듈(helper_472_open_no_draft_entry.ts)로
* ① 표준 횡단 패널 · 설정값 받기 = 세션 초안 칸 안 씀(같은 값이면 칸 없음) · 계산은 저장분
* ② 카드 단추 = 바꾼 칸만 보냄 · 세션 고름은 앞서 바꾼 칸에 얹음
* node helper_472_open_no_draft.mjs <묶음.mjs> <표준 횡단 고정 자료.json>
* 짝 = `test_472_open_no_draft.py` · 표준출력 JSON 한 벌.
*/
import { readFileSync } from "node:fs";
import { pathToFileURL } from "node:url";
const store = () => {
const m = new Map();
return {
getItem: (k) => (m.has(k) ? m.get(k) : null),
setItem: (k, v) => m.set(k, String(v)),
removeItem: (k) => m.delete(k),
key: (i) => [...m.keys()][i] ?? null,
get length() {
return m.size;
},
clear: () => m.clear(),
};
};
/** 작은 가짜 요소 — 자식 · 듣는 함수 · 글만 들고 나머지는 빈 함수. */
function element(tag) {
const base = {
tagName: String(tag).toUpperCase(),
children: [],
listeners: {},
attrs: {},
style: {},
dataset: {},
textContent: "",
classList: { add() {}, remove() {}, toggle() {}, contains: () => false },
append(...kids) {
base.children.push(...kids);
},
prepend(...kids) {
base.children.unshift(...kids);
},
insertBefore(kid) {
base.children.push(kid);
},
replaceChildren(...kids) {
base.children = kids;
},
addEventListener(type, fn) {
(base.listeners[type] ??= []).push(fn);
},
setAttribute(k, v) {
base.attrs[k] = String(v);
},
getAttribute: (k) => base.attrs[k] ?? null,
querySelector: () => null,
querySelectorAll: () => [],
closest: () => null,
clientWidth: 0,
offsetWidth: 0,
scrollWidth: 0,
};
return new Proxy(base, {
get: (t, k) => (k in t ? t[k] : typeof k === "string" ? () => undefined : undefined),
});
}
const noop = new Proxy(function () {}, {
get: (_t, k) => (k === Symbol.toPrimitive ? () => 0 : noop),
apply: () => noop,
construct: () => noop,
});
globalThis.window = globalThis;
globalThis.sessionStorage = store();
globalThis.localStorage = store();
globalThis.document = new Proxy(
{ createElement: element, createElementNS: (_ns, tag) => element(tag) },
{ get: (t, k) => (k in t ? t[k] : noop) },
);
globalThis.ResizeObserver = class {
observe() {}
disconnect() {}
};
globalThis.getComputedStyle = () => ({ gap: "0" });
globalThis.requestAnimationFrame = (run) => setTimeout(run, 0);
globalThis.addEventListener = () => undefined;
globalThis.removeEventListener = () => undefined;
globalThis.fetch = async () => new Response("{}", { status: 404 });
const m = await import(pathToFileURL(process.argv[2]).href);
const draftKeys = () =>
Object.keys(
Object.fromEntries([...Array(sessionStorage.length)].map((_, i) => [sessionStorage.key(i), 1])),
).filter((k) => k.startsWith("aislo:draft:"));
const out = {};
// ① 표준 횡단 — config 와 저장분이 다른 프로젝트
const config = JSON.parse(readFileSync(process.argv[3], "utf8")).standard_cross_section;
const stored = JSON.parse(JSON.stringify(config));
stored.rock.ditch.top_width_m = 0.9; // 저장분 = 사용자가 고쳐 저장한 값(config 와 다름)
const context = {
project_id: "pS",
standard_cross_section: config,
stored_standard_cross_section: stored,
};
m.writeState("section-context", context, "pS");
await m.fetchSectionContext("pS"); // 캐시 길 — 예전엔 여기서 저장분을 초안 칸에 베낌
out.ctxDrafts = draftKeys();
const panel = m.createStandardPanel("pS", config, () => {});
out.panelDrafts = draftKeys();
out.panelRockDitch = panel.getValues().rock.ditch.top_width_m;
out.effectiveRockDitch = m.effectiveStandardCross("pS").rock.ditch.top_width_m;
panel.applyStored(stored);
out.afterStored = draftKeys();
// 옛 판이 남긴 같은 값 칸 — 열면 걷음
m.writeState("std-cross", stored, "pS2");
m.writeState("section-context", { ...context, project_id: "pS2" }, "pS2");
m.createStandardPanel("pS2", config, () => {});
out.oldSameCell = m.readState("std-cross", "pS2");
// 저장분 없는 프로젝트 — config 그대로 = 칸 없음 · 계산은 config
m.writeState("section-context", { project_id: "pC", standard_cross_section: config }, "pC");
m.createStandardPanel("pC", config, () => {});
out.configDrafts = draftKeys();
out.configRockDitch = m.effectiveStandardCross("pC").rock.ditch.top_width_m;
// 사용자가 고친 값은 칸에 남음
const edited = JSON.parse(JSON.stringify(stored));
edited.soil.road_width_m = 3.5;
m.writeState("std-cross", edited, "pE");
m.writeState("section-context", { ...context, project_id: "pE" }, "pE");
m.createStandardPanel("pE", config, () => {});
out.editedRoadWidth = m.readState("std-cross", "pE")?.soil.road_width_m ?? null;
// ② 카드 단추 — 리핑암 · 좌절토 · 측구 자동 판정
sessionStorage.clear();
const section = {
station_id: "No.1",
chainage_m: 20,
uphill_side: "left",
design: {
ground_type: "ripping_rock",
section_mode: "left_cut",
ditch_side: null,
ditch_type: "standard",
paved: false,
two_stage_slope: false,
ditch_choice: null,
ditch_enabled: true,
},
samples: [],
};
const sent = [];
const { bar, groundSegment } = m.buildDesignControls(section, (_c, change) => sent.push(change));
const buttons = [];
const walk = (node) => {
if (!node || typeof node !== "object") return;
if (node.tagName === "BUTTON") buttons.push(node);
(node.children ?? []).forEach(walk);
};
walk(groundSegment);
walk(bar);
const press = (pick) => buttons.find(pick)?.listeners.click?.forEach((fn) => fn({}));
out.soilPressed = buttons[0]?.getAttribute("aria-pressed");
press(
(b) => b.textContent === "측구" || b.textContent === "측구 없음" || /측구/.test(b.textContent),
);
out.ditchChange = sent.at(-1) ?? null;
// 새 카드 · 토사 단추 — 지반만(측구형식은 이미 일반이라 안 실림)
buttons.length = 0;
const again = m.buildDesignControls(section, (_c, change) => sent.push(change));
walk(again.groundSegment);
press(() => true);
out.soilChange = sent.at(-1) ?? null;
// 세션 고름 — 앞서 바꾼 칸에 얹음(지반은 안 박음)
const detail = { cross_sections: [section] };
const sync = m.createDesignSync({
projectId: "pD",
routeId: () => 7,
detail: () => detail,
view: () => noop,
});
await sync.handleDesignChange(20, { ditch_choice: false }).catch(() => undefined);
await sync.handleDesignChange(20, { two_stage_slope: true }).catch(() => undefined);
out.choice = Object.fromEntries(m.crossDesignChoices("pD", 7));
await sync.handleDesignChange(20, {}).catch(() => undefined);
out.choiceAfterEmpty = Object.fromEntries(m.crossDesignChoices("pD", 7));
// 소단 사본(시설 줄이 열 때 구조물에서 씀) — 떠남 확인에 안 잡힘 · 진짜 고침은 잡힘
sessionStorage.clear();
m.writeState(
"berm",
[{ start_m: 0, end_m: 40, width_m: 1, interval_m: 5, slope_deg: 45, depth_m: 1 }],
"pB",
7,
);
out.dirtyBermOnly = m.designDirty("pB");
m.writeCrossDesignChoice("pB", 7, 20, { ditch_choice: false });
out.dirtyWithEdit = m.designDirty("pB");
process.stdout.write(JSON.stringify(out));
process.exit(0);