- B09 표 탭 안내 글 = 쓸모 있는 한 줄(lineHint) · 묶음 탭과 같은 줄임표 - B08 표 아래 안내 · 경고 줄 = footNote 한 줄 · 서버 알림은 한 줄로 이어 붙임 - 새 시험 test_236_1 · 창 높이별 창 끝까지 셈 헬퍼 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018jonfdxn1Xit9bdkNJpE4G
161 lines
6.6 KiB
Python
161 lines
6.6 KiB
Python
"""236-1 B08 · B09 표 아래 안내 글 — 제작 · 셈 설명 지움 · 늘 한 줄 · 페이지 스크롤 0.
|
||
|
||
- 안내 글 낱말에 제작 · 셈 설명(ROUND · 「 = 」 · 장 번호 · M01 · 곱셈 나눗셈 기호)이 없음
|
||
- 안내 글 한 조각 70 자 이하(긴 글은 줄임표 + 공용 호버)
|
||
- B09 안내 글(lineHint) · 레벨 안내 · B08 안내 줄(footNote) = 한 줄(nowrap · 줄임표)
|
||
- 창 크기(1080 · 768 · 600)마다 한 줄 안내 + 표 칸 = 창 끝(199-1 fillViewport 셈)
|
||
"""
|
||
|
||
import json
|
||
import re
|
||
import subprocess
|
||
from pathlib import Path
|
||
|
||
ROOT = Path(__file__).resolve().parents[2]
|
||
B08 = ROOT / "B08_Quantity"
|
||
B09 = ROOT / "B09_Estimation"
|
||
|
||
STRING = re.compile(r'"([^"\\\n]*)"|`([^`\n]*)`')
|
||
FORBID = ["ROUND", " = ", "÷", "×", "Σ", "M01", "역전개", "원문"]
|
||
CHAPTER = re.compile(r"\(\d{2,3}-\d\)|\b\d{1,3}-\d-\d\b|\b\d{2,3}-\d\b")
|
||
MAX_PIECE = 70
|
||
|
||
|
||
def _read(path: Path) -> str:
|
||
return path.read_text("utf-8")
|
||
|
||
|
||
def _pieces(expr: str) -> list[str]:
|
||
out = []
|
||
for m in STRING.finditer(expr):
|
||
text = m.group(1) if m.group(1) is not None else m.group(2)
|
||
out.append(re.sub(r"\$\{[^}]*\}", "", text).strip())
|
||
return [p for p in out if p]
|
||
|
||
|
||
def _b09_hints() -> list[tuple[str, str]]:
|
||
found: list[tuple[str, str]] = []
|
||
for path in sorted(B09.glob("B09_Estimation_UI_Tabs_*.ts")):
|
||
src = _read(path)
|
||
for m in re.finditer(r"\bhint:\s*(.*?),\n\s+\w+[:(]", src, re.S):
|
||
found += [(path.name, p) for p in _pieces(m.group(1))]
|
||
for m in re.finditer(
|
||
r"^const (?:LINK_HINT|BLANK|HINT|EXPENSE_NONE|GUIDE)\s*=\s*(.*?);\n", src, re.S | re.M
|
||
):
|
||
found += [(path.name, p) for p in _pieces(m.group(1))]
|
||
return found
|
||
|
||
|
||
def _b08_hints() -> list[tuple[str, str]]:
|
||
found: list[tuple[str, str]] = []
|
||
for name in (
|
||
"B08_Quantity_UI_Earthwork.ts",
|
||
"B08_Quantity_UI_Earthwork_Sheets.ts",
|
||
"B08_Quantity_UI_Material.ts",
|
||
):
|
||
src = _read(B08 / name)
|
||
for m in re.finditer(r"footNote\(((?:\s|\+|\"[^\"\n]*\"|`[^`\n]*`)+)\)", src):
|
||
found += [(name, p) for p in _pieces(m.group(1))]
|
||
for m in re.finditer(r"^const HINTS[^=]*=\s*\{(.*?)\n\};", src, re.S | re.M):
|
||
found += [(name, p) for p in _pieces(m.group(1))]
|
||
for m in re.finditer(r"^const HIDDEN\s*=\s*(.*?);\n", src, re.S | re.M):
|
||
found += [(name, p) for p in _pieces(m.group(1))]
|
||
return found
|
||
|
||
|
||
def _server_notes() -> list[tuple[str, str]]:
|
||
found: list[tuple[str, str]] = []
|
||
for name in ("Router_Summary", "Router_Material"):
|
||
src = _read(B08 / f"B08_Quantity_{name}.py")
|
||
for m in re.finditer(r"notes\.append\(\s*(.*?)\)\n", src, re.S):
|
||
found += [(name, p) for p in _pieces(m.group(1).replace('f"', '"'))]
|
||
sangeun = _read(B09 / "B09_Estimation_Sangeun.py")
|
||
for m in re.finditer(r'^(?:NO_SOURCE|EMPTY) = "([^"]*)"', sangeun, re.M):
|
||
found.append(("Sangeun", m.group(1)))
|
||
return found
|
||
|
||
|
||
def _all() -> list[tuple[str, str]]:
|
||
return _b09_hints() + _b08_hints() + _server_notes()
|
||
|
||
|
||
def test_안내_글_모음이_비지_않음():
|
||
assert len(_b09_hints()) >= 20
|
||
assert len(_b08_hints()) >= 4
|
||
assert len(_server_notes()) >= 7
|
||
|
||
|
||
def test_제작_셈_설명_낱말_없음():
|
||
bad = []
|
||
for where, text in _all():
|
||
bad += [(where, token, text) for token in FORBID if token in text]
|
||
if CHAPTER.search(text):
|
||
bad.append((where, "장 번호", text))
|
||
assert not bad, bad
|
||
|
||
|
||
def test_안내_글_조각_짧음():
|
||
long = [(w, t) for w, t in _all() if len(t) > MAX_PIECE]
|
||
assert not long, long
|
||
|
||
|
||
# ── 늘 한 줄 ───────────────────────────────────────────────────────────────
|
||
|
||
|
||
def _block(css: str, selector: str) -> str:
|
||
m = re.search(re.escape(selector) + r"\s*\{([^}]*)\}", css)
|
||
assert m, selector
|
||
return m.group(1)
|
||
|
||
|
||
def test_B09_안내_글_한_줄():
|
||
css = _read(B09 / "B09_Estimation_UI_Page.css")
|
||
block = _block(css, ".b09-estimation__line-hint")
|
||
assert "white-space: nowrap" in block and "text-overflow: ellipsis" in block
|
||
shell = _read(B09 / "B09_Estimation_UI_Shell.ts")
|
||
assert "lineHint(note(spec.hint))" in shell # 표 탭도 묶음 탭과 같은 한 줄
|
||
assert "export function lineHint" in _read(B09 / "B09_Estimation_UI_Tabs_Groups.ts")
|
||
level = _read(B09 / "B09_Estimation_UI_Tabs_Bill_Level.ts")
|
||
assert 'guide.className = "ui-general-block__empty b09-estimation__line-hint"' in level
|
||
|
||
|
||
def test_B08_안내_줄_한_줄():
|
||
css = _read(B08 / "B08_Quantity_UI_Page.css")
|
||
block = _block(css, ".b08-quantity__foot")
|
||
assert "white-space: nowrap" in block and "text-overflow: ellipsis" in block
|
||
assert "display" not in block # hidden 속성(콘크리트 빈 까닭)을 덮지 않음
|
||
foot = _read(B08 / "B08_Quantity_UI_Foot.ts")
|
||
assert "b08-quantity__foot" in foot and "attachHoverInfo" in foot
|
||
for name in ("Earthwork", "Earthwork_Sheets", "Material", "Summary"):
|
||
assert "footNote" in _read(B08 / f"B08_Quantity_UI_{name}.ts"), name
|
||
|
||
|
||
def test_B08_표_아래_안내는_footNote만():
|
||
material = _read(B08 / "B08_Quantity_UI_Material.ts")
|
||
assert "note(BASIS)" not in material # 「구조물 수량 기준 …」 제작 설명 줄 없음
|
||
assert "const excel = footNote(" in material
|
||
assert "const concreteNote = footNote(" in material
|
||
summary = _read(B08 / "B08_Quantity_UI_Summary.ts")
|
||
assert 'footNote(summary.notes.join(" · "))' in summary # 서버 알림 = 한 줄로 이어 붙임
|
||
sheets = _read(B08 / "B08_Quantity_UI_Earthwork_Sheets.ts")
|
||
assert "tableCard(haulNote(table), grid.root, averages)" in sheets # 제작 설명 줄 없음
|
||
|
||
|
||
# ── 창 끝까지 셈 ────────────────────────────────────────────────────────────
|
||
|
||
|
||
def test_창_크기마다_한_줄_안내_뒤_창_끝():
|
||
proc = subprocess.run(
|
||
["node", str(ROOT / "resources" / "tester" / "helper_236_1_fill_sizes.cjs")],
|
||
capture_output=True,
|
||
text=True,
|
||
encoding="utf-8",
|
||
timeout=60,
|
||
)
|
||
assert proc.returncode == 0, proc.stderr
|
||
rows = json.loads(proc.stdout)
|
||
assert [r["viewport"] for r in rows] == [1080, 768, 600]
|
||
for r in rows:
|
||
assert r["below"] == 18 + 8 + 12 + 8 # 안내 한 줄 + 줄 사이 + 카드 + 본문 여백
|
||
assert r["bottom"] == r["viewport"], r # 문서 맨 아래 = 창 끝 → 페이지 스크롤 0
|