Files
Aislo/resources/tester/test_201_1_table_level_merge.py
T
eomsangdon d827a15473 Merge remote-tracking branch 'origin/sub_laptop_3' into main_laptop_1
# Conflicts:
#	B09_Estimation/B09_Estimation_UI_Tabs.ts
2026-10-10 14:32:01 +09:00

136 lines
6.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""201-1 공용 표 level(보기 전용) · mergeSame(같은 값 세로 병합) 옵션 + B08 · B09 적용 표.
- level { key, of } = 그 열 칸 class ui-data-table__lv · 줄 style --dt-lv(레벨 − 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("<tbody>")[1].split("</tbody>")[0].split("</tr>").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("<tfoot>")[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("<tbody>")[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 '<td rowspan="3" data-c="0">흙깍기</td>' in rows[0].replace(' class="is-merged"', "")
assert 'class="is-merged"' in rows[0]
assert (
rows[1].count("<td") == 2 and '<td data-c="2">b</td>' in rows[1]
) # 합쳐진 칸 빠짐 · 열 번호
assert '<td data-num="" data-c="3">2</td>' in rows[4] # 숫자 값 그대로
assert "rowspan" not in out["mergedFoot"] # 합계 줄 안 합침
# 그린 구간(1~3줄) 안에서만 · 고칠 칸 안 합침
assert out["win"] == [[2, 1, 1, 1], [0, 1, 1, 1], [1, 1, 1, 1]]
assert [s[0] for s in out["edit"]] == [1, 1, 1]
assert out["cell"] == [3, 1]
def test_level_보기_전용_들여쓰기():
rows = _run()["level"]
assert [re.search(r'style="--dt-lv:(\d)"', r).group(1) for r in rows] == ["0", "1", "0"]
assert '<td class="ui-data-table__lv">a</td>' in rows[0]
assert rows[0].count("ui-data-table__lv") == 1 # 지정 열만
assert '<td class="is-edit ui-data-table__lv" data-num="">' in rows[1] # 글 상자 칸도
assert "button" not in "".join(rows) # 다루기 단추 없음(설계내역서만)
css = (ROOT / "ui_template" / "ui_template_data_table.css").read_text("utf-8")
pad = r"padding-left: calc\(var\(--spacing-8\) \+ var\(--dt-lv, 0\) \* 1em\);"
assert re.search(r"td\.ui-data-table__lv \{\s*" + pad + r"\s*text-align: left;", css)
assert re.search(r"td\.ui-data-table__lv \.ui-data-table__input \{\s*" + pad, css)
assert re.search(r"td\.is-merged \{\s*vertical-align: top;", css)
#: 적용 표 — [파일, 들어 있어야 할 글]
APPLIED = [
("B08_Quantity/B08_Quantity_UI_Earthwork_Sheets.ts", 'mergeSame: ["group", "spec"]'),
("B08_Quantity/B08_Quantity_UI_Material.ts", 'mergeSame: ["name"]'),
("B08_Quantity/B08_Quantity_UI_Material.ts", 'mergeSame: ["group"]'),
("B08_Quantity/B08_Quantity_UI_Material.ts", "mergeSame: built.mergeSame"),
("B09_Estimation/B09_Estimation_UI_Tabs_Base.ts", 'mergeSame: ["part"]'),
("B09_Estimation/B09_Estimation_UI_Tabs_Base.ts", 'level: blockLevel("name"'),
("B09_Estimation/B09_Estimation_UI_Tabs_Labor.ts", 'level: blockLevel("name"'),
("B09_Estimation/B09_Estimation_UI_Tabs_Sangeun.ts", 'level: blockLevel("text"'),
("B09_Estimation/B09_Estimation_UI_Tabs_Sangeun.ts", 'mergeSame: ["kind"]'),
("B09_Estimation/B09_Estimation_UI_Tabs_Total.ts", 'mergeSame: ["group", "part"]'),
("B09_Estimation/B09_Estimation_UI_Tabs_Total.ts", 'mergeSame: ["part"]'),
("B09_Estimation/B09_Estimation_UI_Tabs_Bill.ts", 'level: { key: "name"'),
("B09_Estimation/B09_Estimation_UI_Shell.ts", "mergeSame: spec.mergeSame"),
]
def test_B08_B09_적용():
for path, text in APPLIED:
assert text in (ROOT / path).read_text("utf-8"), (path, text)
# 옛 글 빈칸 지우기 · 앞 빈칸 들여쓰기는 없앰(병합 · level 로 갈음)
mat = (ROOT / "B08_Quantity/B08_Quantity_UI_Material.ts").read_text("utf-8")
assert '=== row.name ? ""' not in mat and '=== row.group ? ""' not in mat
labor = (ROOT / "B09_Estimation/B09_Estimation_UI_Tabs_Labor.ts").read_text("utf-8")
assert 'const CHILD = "ㄴ "' in labor and '" 합 계"' not in labor
# 셸 칸 → 열 = cellColumn(병합 표에서 cellIndex 가 밀림)
shell = (ROOT / "B09_Estimation/B09_Estimation_UI_Shell.ts").read_text("utf-8")
assert ".cellIndex" not in shell and shell.count("cellColumn(") == 2