"""상세 이동 단추 · 도면 상세 → 면적산출(PLAN 105-2 · 105-4) — Node 로 판정 · 화면은 ORCA. 못박는 것 - 머리 제목과 [?] 사이 [도면][일위대가][면적산출](`ui-btn-group` · 보통 크기) · 지금 상세 단추 = 숨김(113-2) · 그 열 문서(번호)가 없으면 그 단추 꺼짐 · 프로젝트 층 · 남의 층은 번호를 안 읽고 다 켬 · 상세 아니면 숨김. - 보임 조건 = 뒤로 단추와 같음 · 누름 = confirmLeave → open({...sel, kind}) · 지금 것 누름 = 그대로. - 도면 상세 머리 [면적산출로 보내기] = CAD 요청 · 받은 표(TOAREA 도 같은 길) → 같은 열 면적 상세로 옮겨 가 넣을 자리 창(`importDrawingTable`) · 읽기 · 프로젝트 층은 단추 없음 · 떠나면 듣기를 뗌. """ import json import shutil import subprocess from pathlib import Path import pytest ROOT = Path(__file__).resolve().parents[2] M02 = ROOT / "M02_MasterTemplete" SCRIPT = r""" const fs = require("fs"); const ts = require(process.argv[1] + "/config/node_modules/typescript"); const src = fs.readFileSync(process.argv[1] + "/M02_MasterTemplete/M02_MasterTemplete_DetailNav.ts", "utf8"); const js = ts.transpileModule(src, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, }).outputText; let fetched = 0; let numbers = {}; const button = (o) => { const b = { label: o.label, size: o.size, dataset: {}, attrs: {}, disabled: false, hidden: false, title: "", onClick: o.onClick }; b.setAttribute = (k, v) => (b.attrs[k] = v); return b; }; const stubs = { "@ui/ui_template_elements": { createButton: button, el: (tag, o) => ({ tag, className: o.className, attrs: o.attrs, children: o.children, hidden: false }), }, "./M02_MasterTemplete_Api_Fetch": { fetchStructureNumbers: async () => (fetched++, numbers), getActiveLayer: () => "system", }, }; const box = { exports: {} }; new Function("exports", "module", "require", js)(box.exports, box, (n) => stubs[n] ?? require(n)); const gone = []; const nav = box.exports.createDetailNav((kind) => gone.push(kind)); const view = () => ({ hidden: nav.root.hidden, shown: nav.root.children.map((b) => !b.hidden), disabled: nav.root.children.map((b) => b.disabled), }); const tick = () => new Promise((r) => setTimeout(r, 0)); (async () => { const out = { className: nav.root.className, labels: nav.root.children.map((b) => b.label), sizes: nav.root.children.map((b) => b.size ?? null), }; numbers = { c1: { 도번: "SD-000001", 일위대가번: "", 면적번: "SA-000001" } }; nav.update({ layer: "system", kind: "area", name: "c1" }); out.before = view(); await tick(); out.area = view(); nav.update({ layer: "project", kind: "structure", name: "c1" }); await tick(); out.project = view(); nav.update({ layer: "company", kind: "labor", name: "c1" }); // 남의 층(지금 층 아님) await tick(); out.other = view(); out.fetched = fetched; nav.update(null); out.none = view(); nav.root.children[1].onClick(); out.gone = gone; console.log(JSON.stringify(out)); })(); """ @pytest.fixture(scope="module") def run(): node = shutil.which("node") if not node: pytest.skip("node 없음") done = subprocess.run( [node, "-e", SCRIPT, str(ROOT)], capture_output=True, text=True, encoding="utf-8", check=True, ) return json.loads(done.stdout) def _text(name: str) -> str: return (M02 / name).read_text(encoding="utf-8") def test_단추_셋_묶음(run): assert run["labels"] == ["도면", "일위대가", "면적산출"] assert "ui-btn-group" in run["className"] assert run["sizes"] == [None, None, None] # 113-2 — [새로고침]와 같은 보통 크기 def test_지금_것_숨김_없는_문서_꺼짐(run): assert run["before"]["shown"] == [True, True, False] # 113-2 — 지금 상세 단추 숨김 assert run["before"]["disabled"] == [False, False, False] # 번호 받기 전엔 켬 assert run["area"]["disabled"] == [False, True, False] # 일위대가번 없음 = 꺼짐 assert run["area"]["hidden"] is False def test_프로젝트_남의_층은_안_읽음(run): assert run["project"]["shown"] == [False, True, True] assert run["project"]["disabled"] == [False, False, False] assert run["other"]["disabled"] == [False, False, False] assert run["fetched"] == 1 def test_상세_아니면_숨김_누름은_옮겨_감(run): assert run["none"]["hidden"] is True assert run["gone"] == ["labor"] def test_머리_줄_자리와_보임_조건(): main = _text("M02_MasterTemplete_UI_Main.ts") assert " title,\n nav.root,\n helpSlot," in main # 제목과 ? 사이 assert "nav.update(back.hidden ? null : sel); // 보임 조건 = 뒤로 단추와 같음" in main assert "if (!at || at.kind === kind || !(await confirmLeave())) return;" in main assert "void open({ ...at, kind });" in main css = (M02 / "M02_MasterTemplete_UI_Style.css").read_text(encoding="utf-8") assert "aria-pressed" not in css # 113-2 — 눌림 대신 숨김 # 머리 [?] 만 보통 크기 · 카드 [?] 는 작게 그대로(`helpParts` sm) assert 'help?.button.classList.remove("ui-btn--sm");' in main shell = _text("M02_MasterTemplete_CardShell.ts") assert 'createButton({ label: CARD_TEXT.help, variant: "ghost", size: "sm" })' in shell def test_도면_상세에서_면적으로_보내기(): main = _text("M02_MasterTemplete_UI_Main.ts") block = main.split("const off = attachDrawingToArea(")[1].split("return;\n }\n empty(")[0] assert 'readOnly || at.layer === "project"' in block # 읽기 · 프로젝트 층 막음 assert "if (!(await confirmLeave())) return;" in block # 미저장 도면이면 물음 assert 'await open({ ...at, kind: "area" });' in block assert "void importDrawingTable(sheetHandle, table);" in block # 넣을 자리 창 · 저장은 사용자 assert "editor = { ...made, destroy: () => (off(), made.destroy()) };" in block # 떠나면 뗌 to_area = _text("M02_MasterTemplete_DrawingToArea.ts") assert "postMessage({ type: EXPORT_TABLE_REQUEST }, window.location.origin)" in to_area assert "if (data?.type !== EXPORT_TABLE || !data.table) return;" in to_area assert "if (onTable) slot.append(send);" in to_area assert "event.source !== win" in to_area # 다른 iframe(면적 도면 패널) 메시지는 안 받음