"""201-1 공용 표 level(보기 전용) · mergeSame(같은 값 세로 병합) 옵션 + B08 · B09 적용 표. - level { key, of } = 그 열 칸 class ui-data-table__lv · 줄 style --dt-lv(레벨 − 1) · 글 상자 칸도 · 다루기 단추 없음 · 레벨 글꼴 class is-lv1~5 · 묶음 is-node(228-1 · test_228_1) - mergeSame [열] = 위아래 같은 글이 이어지면 rowspan · 앞 병합 열이 갈리면 갈림 · 빈 값 · 빈칸 글 · 고칠 칸 안 합침 · 합계 줄 안 합침 · 그린 구간(win) 안에서만 · 병합 표 칸 = data-c(열 번호) - 옵션 없으면 무변(rowspan · data-c · --dt-lv 없음) """ import json import re import subprocess from pathlib import Path ROOT = Path(__file__).resolve().parents[2] JS = r""" const fs = require("fs"); const path = require("path"); const ts = require(path.join(process.argv[1], "config", "node_modules", "typescript")); const file = path.join(process.argv[1], "ui_template", "ui_template_data_table.ts"); const js = ts.transpileModule(fs.readFileSync(file, "utf8"), { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 }, }).outputText; const box = { exports: {} }; new Function("exports", "module", "require", js)(box.exports, box, (p) => (p.endsWith(".css") ? {} : require(p))); const m = box.exports; const body = (html) => html.split("
")[1].split("")[0].split("").filter(Boolean); const columns = [ { key: "g", label: "구분" }, { key: "s", label: "규격" }, { key: "n", label: "명칭" }, { key: "v", label: "값", kind: "number" }, ]; const rows = [ { g: "흙깍기", s: "가", n: "a", v: 1 }, { g: "흙깍기", s: "가", n: "b", v: 1 }, { g: "흙깍기", s: "나", n: "c", v: 1 }, { g: "운반", s: "나", n: "d", v: 2 }, { g: "운반", s: " ", n: "e", v: 2 }, { g: "", s: " ", n: "f", v: 2 }, { g: "", s: "다", n: "g", v: 3 }, ]; const total = { g: "계", v: 12 }; const out = {}; out.plain = m.renderDataTableHtml({ columns, rows, total }); const merged = { columns, rows, total, mergeSame: ["g", "s"] }; out.merged = body(m.renderDataTableHtml(merged)); out.mergedFoot = m.renderDataTableHtml(merged).split("")[1]; out.spans = m.mergeSpans(merged, 0, rows.length); out.win = m.mergeSpans(merged, 1, 4); out.edit = m.mergeSpans({ ...merged, editable: (i, k) => i === 1 && k === "g" }, 0, 3); out.level = body(m.renderDataTableHtml({ columns, rows: rows.slice(0, 3), level: { key: "n", of: (i) => [1, 2, null][i] }, editable: (i, k) => i === 1 && k === "n", })); out.cell = [m.cellColumn({ dataset: { c: "3" }, cellIndex: 1 }), m.cellColumn({ dataset: {}, cellIndex: 1 })]; console.log(JSON.stringify(out)); """ def _run() -> dict: result = subprocess.run( ["node", "-e", JS, str(ROOT)], capture_output=True, text=True, encoding="utf-8", timeout=60 ) assert result.returncode == 0, result.stderr return json.loads(result.stdout) def test_옵션_없으면_무변(): html = _run()["plain"] assert "rowspan" not in html.split("")[1] assert "data-c=" not in html and "--dt-lv" not in html and "ui-data-table__lv" not in html def test_mergeSame_같은_값만_rowspan(): out = _run() # 구분: 흙깍기 3 · 운반 2 · 빈 값 2(안 합침) / 규격: 가 2(흙깍기 안) · 나 = 구분이 갈려 따로 · 빈칸 글 안 합침 assert [s[0] for s in out["spans"]] == [3, 0, 0, 2, 0, 1, 1] assert [s[1] for s in out["spans"]] == [2, 0, 1, 1, 1, 1, 1] assert all(s[2] == 1 and s[3] == 1 for s in out["spans"]) # 지정 안 한 열 = 그대로 rows = out["merged"] assert '