"""278 B09 단가산출근거 [박스보기] 머리 줄 = 운반 단가 창 ③ 칸 꼴(입력값 · 중간값 · 이름 · 값 · 식) · 줄 나눔 · 그리기 한 벌(`Haul_Steps.headSteps`) · 호표 줄 · 총계 · 다른 탭 카드 그대로 · 운반 단가 창 왼쪽 폭(278-1).""" import json import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[2] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) from resources.tester.helper_b09_group_cards import COMMON, GROUPS, RAW, TABS, run # noqa: E402 B09 = "B09_Estimation/B09_Estimation_UI_" STEPS = f"{B09}Haul_Steps.ts" # 공용 el 짝(가짜 DOM) — data-* = dataset EL = r""" const el = (tag, o = {}) => { const n = document.createElement(tag); if (o.className) n.className = o.className; if (o.text !== undefined) n.textContent = o.text; for (const [k, v] of Object.entries(o.attrs ?? {})) { n.setAttribute(k, v); if (k.startsWith("data-")) n.dataset[k.slice(5)] = v; } for (const c of o.children ?? []) n.append(c); return n; }; """ SG = { "key": "SG:A", "no": 1, "name": "골재운반", "unit": "㎥", "spec": "Cm=100", "spec_raw": "Cm=100", "source": "산림품셈 10-3-1", "head": ["골재운반 / ㎥", "산림품셈 10-3-1 적용", "qt : 15 ㎥", "Cm : 운반 사이클 = 100 분", "작업량 = 골재운반 덤프트럭 시간당 작업량 = 8.1", "└ 결과 = 반올림(60 * 15 / 100, 3) = 8.1", "덤프트럭 15ton 수량 = 1 / 작업량 = 1 / 8.1 = 0.1235 시간"], "lines": [ {"kind": "labor", "name": "보통인부", "ref": {"tab": "labor", "key": "LB2"}, "why": None, "labor": 2979.8, "material": 0.0, "expense": 0.0, "text": ["보통인부 : 165,545 * 0.018 = 2,979.8"]}, ], "labor": 2979, "material": 0, "expense": 0, "sum": 2979, "why": None, "foot": "계 : 2,979", } # fmt: skip PANEL = """ const gone = []; const o = { tab: TAB, spec, hint: null, goto: (l) => gone.push(l), redraw: () => {}, setTools: () => {}, makeTable: () => ({ root: document.createElement('table'), focusRow: () => {} }) }; const [view] = renderGroupPanel(o); const top = view.querySelector('.b09-sgsteps'); const rows = top ? top.querySelectorAll('.b09-haulstep__row').map((r) => [r.dataset.at ?? null, ...r.children.map((c) => [c.className, c.textContent])]) : null; const groups = top ? top.querySelectorAll('.b09-haulstep__group').map((g) => g.textContent) : null; const body = view.querySelectorAll('tr').filter((tr) => tr.dataset.at !== undefined).map((tr) => [tr.dataset.at, tr.children[0].text]); const foot = view.querySelector('.ui-group-card__foot')?.dataset.at ?? null; if (top) top.querySelectorAll('.b09-haulstep__row')[2].click(); const lit = view.querySelectorAll('.is-focus').map((e) => e.dataset.at); console.log(JSON.stringify({ rows, groups, body, foot, gone, lit })); """ def _panel(tab, files, build, sheets, prelude=""): js = ( f"\nconst TAB = '{tab}';" f"\nconst spec = {build}({{ sheets: {json.dumps(sheets, ensure_ascii=False)} }});" + PANEL ) return run([COMMON, TABS, *files, STEPS, GROUPS], js, EL + prelude) def test_박스보기_머리_줄_나눔(): out = _panel("sg", [f"{B09}Tabs_Sangeun.ts"], "buildSg", {"sg": [SG]}) assert out["groups"] == ["산림품셈 10-3-1", "입력값", "중간값"] # 출처 = 칸 맨 위 v, f = "b09-haulstep__value", "b09-haulstep__formula" assert out["rows"] == [ ["2", ["", "qt"], [v, "15 ㎥"]], # 표 줄 번호 그대로(머리 첫 줄 = 카드 머리 · 출처 줄 = 위) ["3", ["", "Cm"], [v, "100 분"], [f, "운반 사이클"]], ["4", ["", "작업량"], [v, "8.1"], [f, "골재운반 덤프트럭 시간당 작업량\n└ 결과 = 반올림(60 * 15 / 100, 3) = 8.1"]], ["6", ["", "덤프트럭 15ton 수량"], [v, "0.1235 시간"], [f, "1 / 작업량 = 1 / 8.1"]], ] # fmt: skip # 호표 줄 · 총계 그대로(머리 줄은 표 몸에서 빠짐) assert out["body"] == [ ["7", "보통인부 : 165,545 * 0.018 = 2,979.8노무"] ] # 가짜 DOM 글 = 이름 + 꼬리표 assert out["foot"] == "8" # 머리 줄 누름 = 고름(is-focus) · 이동 없음(표와 같음) assert out["lit"] == ["4"] and out["gone"] == [None] def test_다른_탭_카드_그대로(): dg = { "id": "DG1", "no": 1, "name": "찰쌓기", "spec": "", "spec_raw": "", "unit": "m2", "kind": "", "cite": "", "lines": [{"kind": "labor", "key": "L1", "name": "석공", "qty": 0.072, "unit": "인", "price": 1, "amount": 1, "ref_no": "", "prices": {"labor": 1, "material": None, "expense": None}, "amounts": {"labor": 1, "material": None, "expense": None}}], "sum": 1, "labor": 1, "material": 0, "expense": 0, "blank": 0, "blank_names": [], } # fmt: skip out = _panel("dg", [f"{B09}Tabs_Labor.ts"], "buildDaegaBlocks", {"daega_blocks": [dg]}, RAW) assert out["rows"] is None and out["body"] == [["1", "석공"]] def test_한_벌_잇기(): groups = (ROOT / GROUPS).read_text("utf-8") assert 'import { headSteps } from "./B09_Estimation_UI_Haul_Steps";' in groups assert 'indexOf(" : ")' not in groups # 줄 나눔 복사 없음 assert "sgHeadCards(tabGroups(o.tab, o.spec), o.spec.rows)" in groups steps = (ROOT / STEPS).read_text("utf-8") assert "export function headSteps" in steps and "[...headSteps(b), ...group(" in steps common = (ROOT / COMMON).read_text("utf-8") assert "top?: HTMLElement | null;" in common and "if (g.top) body.append(g.top);" in common sangeun = (ROOT / f"{B09}Tabs_Sangeun.ts").read_text("utf-8") assert 'source: b.source ?? ""' in sangeun def test_운반_창_왼쪽_폭(): modal = (ROOT / f"{B09}Haul_Modal.ts").read_text("utf-8") assert "grid-template-columns:minmax(260px,0.7fr) minmax(340px,1.4fr)" in modal