Files
Aislo/resources/tester/test_236_1_hint_one_line.py
T
eomsangdonandClaude Sonnet 5.5 88dd941d73 fix(B08·B09): 표 아래 안내 글 제작 · 셈 설명 지움 · 늘 한 줄 · 페이지 스크롤 0(236-1)
- 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
2026-10-10 19:09:56 +09:00

161 lines
6.6 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.
"""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