test(M02): 50-3③ 서고 화면 시나리오 — 좌측 컨테이너 차례 · 층 단추 · 새로 창 · 상세 서고 모드 수치

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PSXsvyDxjNmufteN5bqmt
This commit is contained in:
2026-10-05 19:07:38 +09:00
co-authored by Claude Opus 5.5
parent 29ccf4a94e
commit 6430b65036
@@ -0,0 +1,97 @@
/* 50-3 ③ — M02 좌측 「일위대가 조합」 서고 컨테이너 · 상세 서고 모드
* 시작 화면: M02 마스터 템플릿 · 마스터 층(#/m02-master-template?open=일위대가조합&root=master) · 시스템 관리자
* 부르는 법: orca eval --expression "$(cat 이 파일)" (async 식 · JSON 글을 돌려줌)
* 쓰기 없음 — 새로 창은 열고 닫기만 · 상세 모드는 떼어 둔 칸에 가짜 문서로 붙여 봄(서버 calc 만 부름)
* 기대 수치:
* - side.order = ["구조물 조합", "일위대가 조합", …] · side.open = true(진입 open=일위대가조합 이 펼침)
* - side.layerButtons ≥ 1 · side.subs = ["promote", "precombo"] · side.newButtons = 2(관리자)
* - picker.opened = true(단가승격 [새로] = 로직 고르기 창) · precomboDialog = { opened: true, closed: true }
* - detail.promote = { name: true, mats: false, diagram: false, add: false, rows: 1, source: true }
* - detail.precombo = { name: true, mats: false, diagram: false, add: false, rows: 0 }
*/
(async () => {
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
const sections = [...document.querySelectorAll(".m02-side__lists > section")];
const title = (s) => s.querySelector(".ui-collapsible__title")?.textContent?.trim() ?? "";
const lib = sections.find((s) => title(s) === "일위대가 조합");
const side = {
order: sections.map(title),
open: !!lib && !lib.classList.contains("is-collapsed"),
layerButtons: lib?.querySelectorAll(".m02-layers__btn").length ?? 0,
subs: [...(lib?.querySelectorAll("[data-precombo]") ?? [])].map((x) => x.dataset.precombo),
newButtons: [...(lib?.querySelectorAll(".m02-side__row") ?? [])].length,
rows: lib?.querySelectorAll("[data-precombo-key]").length ?? 0,
};
const btn = (kind, i) =>
lib?.querySelector(`[data-precombo="${kind}"] .m02-side__row`)?.querySelectorAll("button")[i];
// 단가승격 [새로] = 로직 고르기 창 → 닫기
btn("promote", 0)?.click();
await wait(300);
const pick = document.querySelector(".m01-logic__pick");
const picker = { opened: !!pick };
pick?.closest(".m01-logic__backdrop")?.remove();
// 사전조합 [새로] = 이름 창 → 닫기(Escape)
btn("precombo", 0)?.click();
await wait(300);
const modal = document.querySelector(".m02-modal");
const precomboDialog = { opened: !!modal };
modal?.querySelector(".ui-modal__top button")?.click(); // [닫기]
await wait(200);
// 상세 서고 모드 — 떼어 둔 칸(화면 밖)에 가짜 문서
// 모듈 주소 = 페이지가 받은 UI_Side 주소에서 파일 이름만 바꿈(vite 뿌리 밖 `/@fs/…`)
const sideUrl = performance
.getEntriesByType("resource")
.map((e) => e.name)
.find((n) => n.includes("M02_MasterTemplete_UI_Side.ts"));
const { mountLaborDetail } = await import(
sideUrl.replace(/M02_MasterTemplete_UI_Side\.ts.*$/, "M02_MasterTemplete_Labor_Detail.ts")
);
const probe = async (mode, doc) => {
const host = document.createElement("div");
host.style.cssText = "position:fixed;left:-9999px;top:0;width:900px";
document.body.append(host);
const h = mountLaborDetail(host, doc, {
layer: "system",
column: doc.키,
readOnly: false,
onChanged: () => {},
goArea: null,
mode,
});
await wait(400);
const out = {
name: !!host.querySelector('[data-labor-field="이름"] input'),
nameValue: host.querySelector('[data-labor-field="이름"] input')?.value ?? null,
mats: [...host.querySelectorAll("h3")].some((x) => x.textContent === "자재목록"),
diagram: !!host.querySelector(".m02-labor__dg"),
add: [...host.querySelectorAll("button")].some((x) => x.textContent === "로직 추가"),
rows: host.querySelectorAll("[data-labor-logic]").length,
source:
!!host.querySelector("[data-labor-source]") || !!host.querySelector(".ui-side-panels"),
kind: h.getDoc().종류,
keepsColumn: "열" in h.getDoc() || "자재" in h.getDoc(),
};
h.destroy();
host.remove();
return out;
};
const detail = {
promote: await probe("promote", {
종류: "단가승격",
키: "SP-999999",
이름: "시험 승격",
단위: null,
담은로직: [{ 로직: "L-0001", 입력값: {} }],
}),
precombo: await probe("precombo", {
종류: "사전조합",
키: "SC-999999",
이름: "시험 사전조합",
단위: null,
담은로직: [],
}),
};
precomboDialog.closed = !document.querySelector(".m02-modal");
return JSON.stringify({ side, picker, precomboDialog, detail });
})();