test(B05): 48-3 · 48-4 화면 시나리오 — 판 1 전후 대조 · 정의 사본 따라감
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BVPp2o6Mh2FZJr72yW3ofZ
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
/* 48-3 · 48-4 — B05 · B06 좌측 「구조물 배치」 칸 = 구조물 정의대로 그림
|
||||
* 시작 화면: B05 종단설계(#/b05-profile) · 구조물 목록이 있는 프로젝트 · 폼 비어 있음(아무것도 안 고름)
|
||||
* 부르는 법: orca eval --expression "$(cat 이 파일)" (async 식 · JSON 글을 돌려줌 · 60초 안)
|
||||
* 쓰는 것: 없음 — 목록 항목은 고르고 다시 눌러 풀기만 · 정의 사본은 **메모리 안 종류 객체만** 고침
|
||||
* (파일 · 서버 쓰기 없음) · 끝나면 화면을 다시 읽어(reload) 사본을 버림
|
||||
* 기대 수치:
|
||||
* - 판 1 전후 대조(목록 항목마다): 칸 키 차례 = 정의 옵션 차례(B05 몫 · 보임만) · 격자 = 2열 기본 ·
|
||||
* 규칙 가림 0 · 숨긴 보기 0 · 「지난 정의 값」 상자 숨김 · 기준 측점 보임 · 선택지 밖 옛 값 0 → sameAll = true
|
||||
* - 정의 사본(레지스트리 종류): 이름 바꿈 · 순서(배치 줄) · 안 씀 · 선택지 줄임 · 규칙(보이기 · 선택지 · 기본값)이
|
||||
* 칸에 그대로 → copy.* = true
|
||||
* - 정의 사본(배수관 · 손코딩 시설 폼): 규칙 선택지 · 기본값 · 가림 → pipe.* = true
|
||||
*/
|
||||
(async () => {
|
||||
const w = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
const sec = document.querySelector(".b05-structure-section");
|
||||
if (!sec) return JSON.stringify({ error: "구조물 배치 칸 없음" });
|
||||
const url = performance
|
||||
.getEntriesByType("resource")
|
||||
.map((e) => e.name)
|
||||
.filter((n) => n.includes("/B05_Profile/B05_Profile_Api_Structures.ts"))
|
||||
.pop();
|
||||
const api = await import(url);
|
||||
const types = await api.fetchStructureTypes(true);
|
||||
const byId = new Map(types.map((t) => [t.type_id, t]));
|
||||
const [groupSel, typeSel] = sec.querySelectorAll(".b05-structure__grid select");
|
||||
const optionRow = [...sec.querySelectorAll(".b05-structure__grid")][1];
|
||||
const vis = (n) => n && n.offsetParent !== null;
|
||||
const facility = sec.querySelector(".b05-drainage__facility");
|
||||
const stale = sec.querySelector(".b05-structure__stale");
|
||||
const capture = () => {
|
||||
const typeId = typeSel.value;
|
||||
const t = byId.get(typeId);
|
||||
const keys = [...optionRow.querySelectorAll("[data-option-keys]")]
|
||||
.filter((n) => !n.classList.contains("is-rule-hidden"))
|
||||
.map((n) => n.dataset.optionKeys);
|
||||
const groupKeys = [...sec.querySelectorAll(".b05-structure__optgroup [data-option-keys]")].map(
|
||||
(n) => n.dataset.optionKeys,
|
||||
);
|
||||
const want = t.managed_by
|
||||
? []
|
||||
: t.options
|
||||
.filter((o) => o.phase !== "detail" && o.visible !== false && o.enabled !== false)
|
||||
.map((o) => o.key)
|
||||
.filter((k) => !groupKeys.includes(k));
|
||||
return {
|
||||
typeId,
|
||||
keysSame: JSON.stringify(keys) === JSON.stringify(want),
|
||||
grid: optionRow.style.gridTemplateColumns === "",
|
||||
ruleHidden: sec.querySelectorAll(".is-rule-hidden").length,
|
||||
hiddenOptions: sec.querySelectorAll("option[hidden]").length,
|
||||
stale: !stale.hidden,
|
||||
anchor: !sec.querySelectorAll(".b05-structure__position-row > label")[2].hidden,
|
||||
facility: !!t.managed_by && vis(facility),
|
||||
// 같은 값 — 정의 선택지 밖 옛 값을 남겨 보인 칸 수(판 1 이면 0 = 옛 화면과 같은 값)
|
||||
keptOld: [...optionRow.querySelectorAll("[data-option-keys] select")].filter((s) => {
|
||||
const o = t.options.find(
|
||||
(x) => x.key === s.closest("[data-option-keys]").dataset.optionKeys,
|
||||
);
|
||||
return s.value && o && !o.choices.includes(s.value);
|
||||
}).length,
|
||||
};
|
||||
};
|
||||
const out = { items: [], copy: {}, pipe: {} };
|
||||
// ── 판 1 전후 대조 — 목록 항목마다 고르고 · 재고 · 다시 눌러 풂 ──
|
||||
const items = [...document.querySelectorAll(".b05-route__irregular-list li")];
|
||||
for (const li of items) {
|
||||
(li.querySelector("button") ?? li).click();
|
||||
await w(250);
|
||||
if (typeSel.value) out.items.push(capture());
|
||||
(li.querySelector("button") ?? li).click(); // 재선택 = 해제
|
||||
await w(150);
|
||||
}
|
||||
out.count = out.items.length;
|
||||
out.sameAll = out.items.every(
|
||||
(r) =>
|
||||
r.keysSame &&
|
||||
r.grid &&
|
||||
!r.ruleHidden &&
|
||||
!r.hiddenOptions &&
|
||||
!r.stale &&
|
||||
r.anchor &&
|
||||
!r.keptOld,
|
||||
);
|
||||
out.bad = out.items.filter(
|
||||
(r) => !(r.keysSame && r.grid && !r.ruleHidden && !r.hiddenOptions && !r.stale && r.anchor),
|
||||
);
|
||||
// ── 정의 사본 — 레지스트리 종류(고르기 칸 둘 이상) · 새 폼에서만(저장 없음) ──
|
||||
const pickType = (t) => {
|
||||
groupSel.value = t.group;
|
||||
groupSel.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
typeSel.value = t.type_id;
|
||||
typeSel.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
};
|
||||
// 흙막이 — 고르기 칸 하나(설치 측) + 숫자 칸 여럿
|
||||
const reg = byId.get("soil_guard");
|
||||
const opt = (k) => reg.options.find((o) => o.key === k);
|
||||
const side = opt("side");
|
||||
opt("tiers").label = "바꾼 이름";
|
||||
opt("shift_m").enabled = false;
|
||||
side.choices = side.choices.slice(0, 2);
|
||||
reg.spots = { tiers: { 줄: 1, 칸: 1, 너비: 2 } };
|
||||
reg.rules = [
|
||||
{
|
||||
종류: "soil_guard",
|
||||
만약: { 옵션: "side", 값들: [side.choices[0]] },
|
||||
그러면: { 옵션: "tiers", 기본값: 3 },
|
||||
},
|
||||
{
|
||||
종류: "soil_guard",
|
||||
만약: { 옵션: "side", 값들: [side.choices[1]] },
|
||||
그러면: { 옵션: "lift_m", 보이기: false },
|
||||
},
|
||||
];
|
||||
pickType(reg);
|
||||
await w(250);
|
||||
const wrapOf = (k) => sec.querySelector(`[data-option-keys="${k}"]`);
|
||||
const first = optionRow.querySelector("[data-option-keys]");
|
||||
out.copy.type = reg.type_id;
|
||||
out.copy.rename = wrapOf("tiers")?.querySelector("span")?.textContent.startsWith("바꾼 이름");
|
||||
out.copy.order = first?.dataset.optionKeys === "tiers" && first.style.gridColumn === "1 / span 2";
|
||||
out.copy.unused = !wrapOf("shift_m");
|
||||
const c1 = wrapOf("side").querySelector("select");
|
||||
out.copy.choices =
|
||||
[...c1.children]
|
||||
.slice(1)
|
||||
.map((o) => o.value)
|
||||
.join(",") === side.choices.join(",");
|
||||
c1.value = side.choices[0];
|
||||
c1.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
await w(100);
|
||||
out.copy.ruleDefault = wrapOf("tiers").querySelector("input").value === "3";
|
||||
c1.value = side.choices[1];
|
||||
c1.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
await w(100);
|
||||
out.copy.ruleHide = wrapOf("lift_m").classList.contains("is-rule-hidden");
|
||||
// ── 정의 사본 — 배수관(손코딩 시설 폼 · 동작 계곡통과시설) ──
|
||||
const pipe = byId.get("pipe");
|
||||
pipe.rules = [
|
||||
{
|
||||
종류: "pipe",
|
||||
만약: { 옵션: "pipe_kind", 값들: ["흄관"] },
|
||||
그러면: { 옵션: "pipe_diameter_mm", 선택지: ["800", "1000"], 기본값: "800" },
|
||||
},
|
||||
{
|
||||
종류: "pipe",
|
||||
만약: { 옵션: "pipe_kind", 값들: ["VR관"] },
|
||||
그러면: { 옵션: "wing_wall_type", 보이기: false },
|
||||
},
|
||||
];
|
||||
pickType(pipe);
|
||||
await w(300);
|
||||
const pk = sec.querySelector('[data-option-keys="pipe_kind"] select');
|
||||
const pd = () => sec.querySelector('[data-option-keys="pipe_diameter_mm"] select');
|
||||
pk.value = "흄관";
|
||||
pk.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
await w(150);
|
||||
out.pipe.choices = [...pd().options].filter((o) => o.value && !o.hidden).map((o) => o.value);
|
||||
out.pipe.defaultOk = pd().value === "800";
|
||||
pk.value = "VR관";
|
||||
pk.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
await w(150);
|
||||
out.pipe.wingHidden = sec
|
||||
.querySelector('[data-option-keys="wing_wall_type"]')
|
||||
.classList.contains("is-rule-hidden");
|
||||
out.pipe.temp =
|
||||
document.querySelectorAll(".b05-route__irregular-list li").length === items.length;
|
||||
return JSON.stringify(out);
|
||||
})();
|
||||
Reference in New Issue
Block a user