Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018jonfdxn1Xit9bdkNJpE4G
173 lines
8.1 KiB
Python
173 lines
8.1 KiB
Python
"""202-1 B08 · B09 표 칸 — 안내 · 경고 줄 = 맨 아래 · 박스보기 · 가로 열 수 = 보조 탭 줄 오른쪽.
|
|
|
|
- B09 안내 줄 = 표 · 카드 판 아래(마지막 자식) · 묶음 탭 도구(보기 단추 · 열 수) = 아래 탭 줄 오른쪽 · 단독 탭 빈 줄에서도
|
|
- B08 안내 · 경고 줄 = 표 · 아래 줄 뒤 맨 아래(토공 · 운반 시트 · 자재 · 구조물 집계)
|
|
- 창 끝까지 셈(199-1) = 아래로 간 안내 줄 몫 포함
|
|
"""
|
|
|
|
import json
|
|
import re
|
|
import subprocess
|
|
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_"
|
|
BAR = f"{B09}TabBar.ts"
|
|
DG = {"id": "DG1", "no": 1, "name": "찰쌓기", "unit": "m2", "lines": [], "sum": 0}
|
|
|
|
|
|
def _block(css: str, selector: str) -> str:
|
|
m = re.search(re.escape(selector) + r"\s*\{([^}]*)\}", css)
|
|
assert m, selector
|
|
return m.group(1)
|
|
|
|
|
|
# ── B09 보조 탭 줄 오른쪽 도구 ──────────────────────────────────────────────
|
|
|
|
|
|
def test_도구_자리_보조_줄_오른쪽_빈_줄에서도_보임():
|
|
out = run(
|
|
[TABS, BAR],
|
|
"""
|
|
const bars = createTabBars(() => {});
|
|
const mk = (n) => { const b = document.createElement('button'); b.textContent = n; return b; };
|
|
const snap = () => ({
|
|
kids: bars.sub.children.map((c) => (c.classList.contains('b09-estimation__tools') ? 'TOOLS' : c.textContent)),
|
|
last: bars.sub.children[bars.sub.children.length - 1].children.map((x) => x.textContent),
|
|
});
|
|
bars.draw('dg');
|
|
bars.setTools([mk('가'), mk('나')]);
|
|
const a = snap();
|
|
bars.draw('compare'); // 다른 묶음으로 옮겨도 도구 자리는 그대로 마지막
|
|
const b = snap();
|
|
bars.draw('cost'); // 단독 탭 = 빈 보조 줄
|
|
const c = snap();
|
|
const ghost = bars.sub.children[0];
|
|
bars.setTools([]);
|
|
const d = snap();
|
|
console.log(JSON.stringify({
|
|
a: a.kids, aLast: a.last,
|
|
b: b.kids.slice(-1), bLast: b.last,
|
|
c: c.kids, cLast: c.last,
|
|
blank: bars.sub.classList.contains('b09-estimation__tabs--blank'),
|
|
subHidden: bars.sub.attrs['aria-hidden'] ?? null, ghostHidden: ghost.attrs['aria-hidden'],
|
|
dLast: d.last.length,
|
|
}));
|
|
""",
|
|
)
|
|
tools = "TOOLS"
|
|
assert out["a"] == ["일위대가목록표", "일위대가표", tools] # 아래 탭 뒤 오른쪽 자리
|
|
assert out["aLast"] == ["가", "나"]
|
|
assert out["b"] == [tools] and out["bLast"] == ["가", "나"] # 탭을 옮겨도 도구는 그대로
|
|
assert out["blank"] and len(out["c"]) == 2 and out["c"][1] == tools # 숨은 단추 + 도구
|
|
assert out["cLast"] == ["가", "나"]
|
|
assert out["subHidden"] is None and out["ghostHidden"] == "true" # 줄 전체가 아니라 숨은 단추만
|
|
assert out["dLast"] == 0 # 비움
|
|
|
|
|
|
def test_묶음_탭_단추가_보조_줄에_안내는_아래에():
|
|
"""쉘이 하는 대로 — 줄 그림 → 묶음 판 그림(`setTools` = 보조 줄 도구) → 단추는 줄에, 판에는 없음."""
|
|
out = run(
|
|
[COMMON, TABS, f"{B09}Tabs_Labor.ts", GROUPS, BAR],
|
|
f"\nconst spec = buildDaegaBlocks({{ sheets: {{ daega_blocks: [{json.dumps(DG, ensure_ascii=False)}] }} }});"
|
|
"\nconst bars = createTabBars(() => {});"
|
|
"\nconst hint = document.createElement('p');"
|
|
"\nconst res = {};"
|
|
"\nfor (const view of ['cards', 'table']) {"
|
|
"\n chosen.set('dg', view);"
|
|
"\n bars.draw('dg'); bars.setTools([]);"
|
|
"\n const panel = renderGroupPanel({ tab: 'dg', spec, hint, goto: () => {}, redraw: () => {},"
|
|
" makeTable: () => ({ root: document.createElement('table'), focusRow: () => {} }),"
|
|
" setTools: bars.setTools });"
|
|
"\n const tools = bars.sub.children[bars.sub.children.length - 1];"
|
|
"\n res[view] = { inRow: tools.all('[data-b09-group-view]').map((b) => b.textContent),"
|
|
" cols: tools.all('[data-group-cols]').length,"
|
|
" inPanel: panel.flatMap((e) => [e, ...e.all('[data-b09-group-view]'), ...e.all('[data-group-cols]')])"
|
|
".filter((e) => e.dataset.b09GroupView || e.dataset.groupCols).length,"
|
|
" hintLast: panel[panel.length - 1] === hint, n: panel.length };"
|
|
"\n}"
|
|
"\nconsole.log(JSON.stringify(res));",
|
|
RAW,
|
|
)
|
|
assert out["cards"]["inRow"] == ["표로 보기"] and out["cards"]["cols"] == 4
|
|
assert out["table"]["inRow"] == ["박스보기"] and out["table"]["cols"] == 0
|
|
for view in ("cards", "table"):
|
|
assert out[view]["inPanel"] == 0 # 판 안에는 단추 없음
|
|
assert out[view]["hintLast"] and out[view]["n"] == 2 # 판 + 안내 줄(맨 아래)
|
|
|
|
|
|
def test_잇기_쉘_안내_줄_아래_도구_비움():
|
|
shell = (ROOT / "B09_Estimation" / "B09_Estimation_UI_Shell.ts").read_text("utf-8")
|
|
assert "panel.replaceChildren(table.root, ...(hint ? [hint] : []))" in shell
|
|
assert "...(hint ? [hint] : []), table.root" not in shell # 위 자리 없음
|
|
assert re.search(r"bars\.draw\(tab\);\s*bars\.setTools\(\[\]\);", shell)
|
|
assert "setTools: bars.setTools" in shell
|
|
|
|
|
|
def test_CSS_도구_오른쪽_맞춤_빈_줄_숨김_규칙에_안_걸림():
|
|
css = (ROOT / "B09_Estimation" / "B09_Estimation_UI_Page.css").read_text("utf-8")
|
|
tools = _block(css, ".b09-estimation__tools")
|
|
assert "margin-left: auto" in tools and "flex-wrap: nowrap" in tools
|
|
hidden = _block(css, ".b09-estimation__tabs--blank > .ui-btn")
|
|
assert "visibility: hidden" in hidden # 직계 단추만 — 도구 묶음은 안 숨김
|
|
assert ".b09-estimation__bar" not in css # 옛 머리 줄(안내 + 단추) 풂
|
|
|
|
|
|
# ── B08 안내 · 경고 줄 맨 아래 ──────────────────────────────────────────────
|
|
|
|
|
|
def test_B08_표_카드_차례_표_아래줄_안내():
|
|
src = (ROOT / "B08_Quantity" / "B08_Quantity_UI_Earthwork.ts").read_text("utf-8")
|
|
fn = re.search(r"export function tableCard\(.*?\n}\n", src, re.S)
|
|
assert fn
|
|
code = fn.group(0).replace("export ", "")
|
|
js = (
|
|
"const createCard = ({ body }) => body;\n"
|
|
+ code
|
|
+ "\nconsole.log(JSON.stringify(tableCard(['i1', 'i2'], 'T', ['a1', 'a2'])));"
|
|
)
|
|
result = subprocess.run(
|
|
["node", "--experimental-strip-types", "--no-warnings", "--input-type=module-typescript"],
|
|
input=js,
|
|
capture_output=True,
|
|
text=True,
|
|
encoding="utf-8",
|
|
timeout=60,
|
|
)
|
|
assert result.returncode == 0, result.stderr
|
|
assert json.loads(result.stdout) == ["T", "a1", "a2", "i1", "i2"] # 표 · 아래 줄 · 안내 줄
|
|
|
|
|
|
def test_B08_자재_구조물_안내_줄_표_아래():
|
|
material = (ROOT / "B08_Quantity" / "B08_Quantity_UI_Material.ts").read_text("utf-8")
|
|
assert "createCard({ body: [toolbar, table.root, excel, notes] })" in material
|
|
assert "toolbar.append(save);" in material # 저장 단추만 위 줄(안내는 아래)
|
|
summary = (ROOT / "B08_Quantity" / "B08_Quantity_UI_Summary.ts").read_text("utf-8")
|
|
assert "createCard({ body: [table, ...parts] })" in summary
|
|
css = (ROOT / "B08_Quantity" / "B08_Quantity_UI_Page.css").read_text("utf-8")
|
|
assert "justify-content: flex-end" in _block(css, ".b08-material__toolbar")
|
|
|
|
|
|
# ── 창 끝까지 셈(199-1)에 아래 줄 몫 ────────────────────────────────────────
|
|
|
|
|
|
def test_창_끝까지_셈에_아래_안내_줄_포함():
|
|
proc = subprocess.run(
|
|
["node", str(ROOT / "resources" / "tester" / "helper_202_1_fill_below.cjs")],
|
|
capture_output=True,
|
|
text=True,
|
|
encoding="utf-8",
|
|
timeout=60,
|
|
)
|
|
assert proc.returncode == 0, proc.stderr
|
|
r = json.loads(proc.stdout)
|
|
assert r["below"] == r["belowExpected"] == 134 # 아래 줄 · 안내 줄 둘 + 여백
|
|
assert r["bottom"] == r["viewport"] # 문서 맨 아래 = 창 끝 → 페이지 스크롤 0
|
|
assert r["afterHintGrow"] == r["firstMinusGrow"] # 안내 줄이 늘면 표 칸이 그만큼 줄어듦
|