diff --git a/resources/tester/ui_checks/52-2_박스암거_컨테이너.js b/resources/tester/ui_checks/52-2_박스암거_컨테이너.js new file mode 100644 index 000000000..67c606819 --- /dev/null +++ b/resources/tester/ui_checks/52-2_박스암거_컨테이너.js @@ -0,0 +1,180 @@ +/* 52-2 — B05 · B06 좌측 「구조물 배치」 박스암거 = 사용자 구조물 표 02 고친 판(컨테이너 셋) + * 시작 화면: B05 종단설계(#/b05-profile) · 검증 프로젝트(sub3_검증) · 목록에 「BOX암거」 + * 부르는 법: 단계마다 한 번씩 — 앞에 `window.__m522 = "단계";` 를 붙여 + * orca eval --expression "window.__m522='edit'; $(cat 이 파일)" → 고치고 [저장] (원래 관 지점은 sessionStorage 에 둠) + * (orca reload 로 다시 열기) + * orca eval --expression "window.__m522='verify'; $(cat 이 파일)" → 다시 연 값 · B06 같은 그리기 + * orca eval --expression "window.__m522='restore'; $(cat 이 파일)" → 원래 관 지점으로 되돌림(PUT) · 초안 비움 + * (연결이 끊기면 결과는 `sessionStorage.getItem("__522_out_{단계}")` 로 읽음) + * 쓰는 것: 검증 프로젝트 pipe_points.json(되돌림 단계가 원래 값으로 다시 씀) + * 기대 수치(edit): 컨테이너 3(본체 · 유입 구조 · 유출 구조 · 모두 필수 · [추가] 0) · 옛 값 3 × 2.5 = 사용자 입력 · + * 옛 「있음」 = 날개벽 · 규격 2.0 × 2.0 고르면 숫자 칸 0 · 사용자 입력 고르면 가로 · 세로 2 · + * 구조 선택지 없음/날개벽/흙막이 · 잇기 본체 · 유입 지정 · 유출 지정 뒤 풂 · + * 저장 = body 2.4 × 1.8 · wing_in 흙막이 · wing_out 없음 · group_items gboxbody · gboxin + * 기대 수치(verify): 저장 값 그대로 · 같은 그리기(B05 = B06) + */ +(async () => { + const MODE = window.__m522 || "edit"; + const w = (ms) => new Promise((r) => setTimeout(r, ms)); + const until = async (fn, ms = 20000) => { + for (let t = 0; t < ms; t += 200) { + if (fn()) return true; + await w(200); + } + return false; + }; + const projectId = localStorage.getItem("frd_current_project_id"); + const modUrl = (part) => + performance + .getEntriesByType("resource") + .map((e) => e.name) + .filter((n) => n.includes(part)) + .pop(); + const fetchApi = await import(modUrl("/B04_PreProcess/B04_PreProcess_Api_Fetch.ts")); + const isBox = (p) => p.facility === "box_culvert"; + // 보이는 명세 폼(배관 · BOX 가 각자 폼을 가짐 — 숨은 것은 뺌) + const spec = () => + [...document.querySelectorAll(".b05-structure__spec")].find((n) => n.offsetParent !== null); + const box = (c) => spec()?.querySelector(`[data-spec-container="${c}"]`); + const field = (c, item) => box(c)?.querySelector(`[data-spec-item="${c}.${item}"]`); + const ctl = (c, item) => field(c, item)?.querySelector("select, input"); + const set = async (c, item, v) => { + const el = ctl(c, item); + el.value = v; + el.dispatchEvent(new Event("change", { bubbles: true })); + await w(200); + }; + const labels = (c, item) => [...ctl(c, item).options].filter((o) => o.value).map((o) => o.text); + const pickBox = async () => { + await until(() => document.querySelectorAll(".b05-route__irregular-list li").length > 0); + const li = [...document.querySelectorAll(".b05-route__irregular-list li")].find((l) => + /BOX암거$/.test(l.textContent.trim()), + ); + // 다시 열면 고른 것이 복원돼 있음 — 누르면 풀리므로 풀렸으면 한 번 더(재선택 = 해제) + (li.querySelector("button") ?? li).click(); + await w(800); + if (!box("body")) (li.querySelector("button") ?? li).click(); + await until(() => !!box("body")); + await w(500); + }; + const shape = () => + [...spec().querySelectorAll("[data-spec-container]")].map((b) => ({ + c: b.dataset.specContainer, + legend: b.querySelector("legend").firstChild.textContent, + items: [...b.querySelectorAll("[data-spec-item]")].map((n) => n.dataset.specItem), + values: [...b.querySelectorAll("[data-spec-item]")].map( + (n) => n.querySelector("select, input").value, + ), + link: b.querySelector(".b05-structure__summary-name")?.textContent, + })); + const linked = (c) => + box(c).querySelector(".b05-structure__summary-name").classList.contains("is-assigned"); + const pickLink = async (c) => { + box(c).querySelector('[data-b05-summary="pick"]').click(); + await until(() => document.querySelector(".ui-item-picker__tree .ui-tree__row")); + for (let k = 0; k < 8; k++) { + const rows = [...document.querySelectorAll(".ui-item-picker__tree .ui-tree__row")]; + const leaf = rows.find((r) => !r.classList.contains("is-branch")); + if (leaf) { + leaf.click(); + break; + } + rows.find((r) => r.querySelector(".ui-tree__caret")?.textContent === "▸")?.click(); + await w(300); + } + await w(300); + [...document.querySelectorAll(".ui-item-picker__actions button")] + .find((b) => b.textContent.includes("적용")) + .click(); + await w(500); + }; + const out = { mode: MODE }; + + if (MODE === "edit") { + const before = await fetchApi.fetchDetailPipePoints(projectId); + // 원래 값은 처음 한 번만 둠(끊겨 다시 돌려도 고친 값으로 덮지 않음 · 되돌림이 비움) + if (!sessionStorage.getItem("__522_original")) + sessionStorage.setItem("__522_original", JSON.stringify(before.pipe_points)); + out.original = before.pipe_points.find(isBox)?.options; + await pickBox(); + const s0 = shape(); + out.containers = s0.map((b) => [b.c, b.legend]); + out.allVisible = ["body", "inlet", "outlet"].every((c) => box(c)?.offsetParent !== null); + out.addButtons = spec().querySelectorAll("[data-spec-add]").length; + out.oldRead = { + body: s0[0].values, + inlet: ctl("inlet", "structure").value, + outlet: ctl("outlet", "structure").value, + }; + out.sizes = labels("body", "size"); + out.structures = labels("inlet", "structure"); + await set("body", "size", "2.0×2.0"); + out.presetNumberFields = ["width", "height"].filter((i) => field("body", i)).length; + await set("body", "size", "사용자 입력"); + out.customNumberFields = ["width", "height"] + .filter((i) => field("body", i)) + .map((i) => field("body", i).querySelector("label, span")?.textContent ?? i); + await set("body", "width", "2.4"); + await set("body", "height", "1.8"); + await set("inlet", "structure", "흙막이"); + await set("outlet", "structure", "없음"); + await pickLink("body"); + await pickLink("inlet"); + await pickLink("outlet"); + out.linkPicked = ["body", "inlet", "outlet"].map(linked); + box("outlet").querySelector('[data-b05-summary="clear"]').click(); + await w(300); + out.linkOutletCleared = !linked("outlet"); + document.querySelector('[data-dock-action="save"]').click(); + await w(500); + out.saved = await until(() => !document.querySelector(".ui-overlay.is-active"), 60000); + const after = await fetchApi.fetchDetailPipePoints(projectId); + const p = after.pipe_points.find(isBox); + out.file = p && { options: p.options, group_items: Object.keys(p.group_items ?? {}) }; + } + + if (MODE === "verify") { + await pickBox(); + const b05 = shape(); + out.reopened = b05.map((b) => [b.c, b.values, b.link]); + out.linkReopened = ["body", "inlet", "outlet"].map(linked); + location.hash = "#/b06-section"; + await until(() => location.hash.includes("b06"), 5000); + await w(6000); + await pickBox(); + const b06 = shape(); + out.sameDrawing = JSON.stringify(b05) === JSON.stringify(b06); + if (!out.sameDrawing) out.diff = { b05, b06 }; + } + + if (MODE === "restore") { + const original = JSON.parse(sessionStorage.getItem("__522_original") || "null"); + if (!original) return JSON.stringify({ error: "원래 값 없음" }); + const keys = [ + "chainage_m", + "source", + "facility", + "start_m", + "end_m", + "options", + "summary_item", + "group_items", + ]; + const points = original.map((p) => + Object.fromEntries( + keys.filter((k) => p[k] !== undefined && p[k] !== null).map((k) => [k, p[k]]), + ), + ); + await fetchApi.saveDetailPipePoints(projectId, points); + for (const k of Object.keys(sessionStorage)) + if (k.startsWith("aislo:draft:pipes:") || k === "__522_original") + sessionStorage.removeItem(k); + const now = await fetchApi.fetchDetailPipePoints(projectId); + const pick = (list) => + JSON.stringify(list.map((p) => [p.chainage_m, p.options ?? {}, p.group_items ?? {}])); + out.restored = pick(now.pipe_points) === pick(original); + } + // 긴 단계는 ORCA 연결이 끊길 수 있어 결과를 남김 — `sessionStorage.__522_out_{단계}` 로 읽음 + sessionStorage.setItem(`__522_out_${MODE}`, JSON.stringify(out)); + return JSON.stringify(out); +})();