Files
Aislo/resources/tester/test_269_panel_form.py
T
eomsangdonandClaude Opus 5.5 9e3c87edd5 feat(B05,B08,B09): 269-3 B05 이상 좌측 컨테이너 공용 양식 점검 · 맞춤
- B05 칸 틀(페이지 설정 · 계획노선 · 선택 포인트) = .ui-panel-form
- B08 옵션 카드 · B09 데이터 점검 카드 = createPanelCard · 틈 덧칠 정리
- 아래 단추 묶음은 그대로
- 시험 test_269 점검(빠진 곳 0) · 122 · 203-1 · 240-1 기대값

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QND7Co43B24zK2P937rdxt
2026-10-11 16:50:56 +09:00

175 lines
8.0 KiB
Python

# -*- coding: utf-8 -*-
"""269 좌측 패널 컨테이너 공용 양식 — 기준 = B05/B06 「구조물 배치」.
못박는 것
- 공용 양식 = ui_template 한 곳(`.ui-panel-form` · `.ui-panel-btn` · 카드 `createPanelCard`) ·
칸 높이 · 라벨 틈 토큰 = theme 한 곳 · 「구조물 배치」도 같은 토큰.
- B07 컨테이너 = 공용 카드 · 도면 단추 = `.ui-panel-btn` · 페이지 덧칠(크기 · 글자 · 바탕) 없음.
- B06 컨테이너(횡단 보기 설정 · 표준 횡단면 설정) = 칸 틀 `createSectionFrame` 이 공용 양식을 붙임 ·
단추 · 입력칸 = 공용 함수(createButton · createInputField · createSelectField) · B06 덧칠 없음.
"""
import re
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
def _read(rel: str) -> str:
return (ROOT / rel).read_text(encoding="utf-8")
THEME = _read("ui_template/ui_template_theme.css")
OVERLAY = _read("ui_template/ui_template_overlay.css")
COLLAPSIBLE = _read("ui_template/ui_template_collapsible.ts")
B05_STRUCT_CSS = _read("B05_Profile/B05_Profile_UI_Style_Structures.css")
B07_CSS = _read("B07_DesignDetail/B07_DesignDetail_UI_Style.css")
B07_PANELS = _read("B07_DesignDetail/B07_DesignDetail_UI_Panels.ts")
B07_PLAN = _read("B07_DesignDetail/B07_DesignDetail_UI_PlanKinds.ts")
DESIGN_PANEL = _read("A00_Common/design/design_panel.ts")
B06_CSS = _read("B06_Section/B06_Section_UI_Style.css")
B06_PANEL = _read("B06_Section/B06_Section_UI_Page_Panel.ts")
B06_STD = _read("B06_Section/B06_Section_UI_Standard_Panel.ts")
def _block(css: str, selector: str) -> str:
m = re.search(r"(?:^|\n)" + re.escape(selector) + r"\s*\{([^}]*)\}", css)
assert m, selector
return m.group(1)
def test_공용_양식_토큰은_theme_한_곳_구조물_배치도_같은_토큰():
assert "--panel-control-h: 26px;" in THEME
assert "--panel-label-gap: 2px;" in THEME
assert "--b05-control-h: var(--panel-control-h);" in B05_STRUCT_CSS
assert "--b05-label-gap: var(--panel-label-gap);" in B05_STRUCT_CSS
def test_공용_양식_칸_라벨_단추_규칙():
field = _block(
OVERLAY,
'.ui-panel-form input:not([type="checkbox"], [type="radio"], [type="range"]),\n'
".ui-panel-form select",
)
assert "height: var(--panel-control-h);" in field
assert "padding: 0 6px;" in field
assert "font-size: var(--text-caption);" in field
label = _block(OVERLAY, ".ui-panel-form .ui-field,\n.ui-panel-form .b05-route__field")
assert "gap: var(--panel-label-gap);" in label
assert "display: none;" in _block(OVERLAY, ".ui-panel-form .ui-field__error:not(.is-visible)")
button = _block(OVERLAY, ".ui-panel-form .ui-btn:not(.ui-help-btn)")
# 「구조물 배치」 단추(b05-route__irregular-btn)와 같은 크기
assert "padding: var(--spacing-8);" in button
assert "font-size: var(--text-caption);" in button
assert "border-radius: var(--radius-inputs);" in button
raw = _block(OVERLAY, ":where(.ui-panel-btn)")
for decl in (
"padding: var(--spacing-8);",
"border-radius: var(--radius-inputs);",
"background: var(--color-surface);",
"font-size: var(--text-caption);",
):
assert decl in raw, decl
# 아래 단추 묶음은 컨테이너 밖 — 공용 양식이 `.ui-sidebar-actions` 를 겨누지 않음
form_rules = OVERLAY[OVERLAY.index("좌측 패널 컨테이너 공용 양식(269)") :]
form_rules = form_rules[: form_rules.index("/* 카드 안에 다시 카드")]
assert "ui-sidebar-actions {" not in form_rules and "ui-sidebar-actions ." not in form_rules
def test_공용_카드_함수():
assert "export function createPanelCard(" in COLLAPSIBLE
assert "ui-collapsible ui-sidebar-section ui-sidebar-card ui-panel-form" in COLLAPSIBLE
def test_B07_컨테이너_공용_카드_도면_단추_공용_꼴():
assert "createPanelCard(`${group.label} ${items.length}`)" in B07_PANELS
assert "createPanelCard(`평면도 ${items.length}`)" in B07_PLAN
for src in (B07_PANELS, B07_PLAN):
assert "ui-collapsible ui-sidebar-section" not in src # 손으로 짠 카드 없음
assert B07_PANELS.count('"b07-drawing-button ui-panel-btn"') == 2
assert '"ui-panel-btn b07-plan-manage-open"' in B07_PLAN
def test_B07_페이지_덧칠_없음():
assert not re.search(r"(?:^|\n)\.b07-drawing-group\s*\{", B07_CSS)
assert ".b07-drawing-group h3 {" not in B07_CSS
assert ".b07-plan-manage-open {" not in B07_CSS
button = _block(B07_CSS, ".b07-drawing-button")
for prop in ("min-height", "padding", "border-radius", "background", "font-size"):
assert not re.search(r"(?:^|\s)" + prop + r"\s*:", button), prop
assert "font-size" not in _block(B07_CSS, ".b07-drawing-button__name")
assert ".b07-cross-layout .ui-btn" not in B07_CSS
assert ".b07-cross-layout .ui-field__error" not in B07_CSS
def test_B06_컨테이너_공용_양식():
frame = DESIGN_PANEL[DESIGN_PANEL.index("export function createSectionFrame(") :]
frame = frame[: frame.index("\n}\n")]
cross = frame[frame.index("b06-profile__group ui-collapsible") :]
assert "ui-panel-form" in cross.splitlines()[0]
for src in (B06_PANEL, B06_STD):
assert "createSectionFrame(" in src or "createButton(" in src
assert "<button" not in src and 'createElement("button")' not in src
assert 'createElement("input")' not in src and 'createElement("select")' not in src
assert "createInputField(" in B06_PANEL and "createButton(" in B06_PANEL
# 덧칠 없음 — 칸 글자 · 오류 칸 접기는 공용 양식 몫
group = _block(B06_CSS, ".b06-profile__group")
assert "font-size" not in group and not re.search(r"(?:^|\s)color\s*:", group)
assert ".b06-profile__field-row .ui-field__error" not in B06_CSS
for sel in (".ui-btn {", ".ui-input {", ".ui-select {"):
for line in B06_CSS.splitlines():
if line.endswith(sel) and line.startswith(".b06-profile"):
rule = _block(B06_CSS, line[:-2])
for prop in ("height", "padding", "font-size"):
assert not re.search(r"(?:^|\s)" + prop + r"\s*:", rule), (line, prop)
B08_OPTIONS = _read("B08_Quantity/B08_Quantity_UI_Options.ts")
B09_SHELL = _read("B09_Estimation/B09_Estimation_UI_Shell.ts")
# 기준(「구조물 배치」 폼 자신) · 부르는 곳 없는 옛 함수는 점검 밖
PANEL_DIRS = (
"A00_Common/design",
"B05_Profile",
"B06_Section",
"B07_DesignDetail",
"B08_Quantity",
"B09_Estimation",
)
REFERENCE = {
"B05_Profile/B05_Profile_UI_Structures_Form.ts",
"B05_Profile/B05_Profile_UI_IrregularStations.ts",
}
def test_B05_칸_틀_두_보기_모두_공용_양식():
frame = DESIGN_PANEL[DESIGN_PANEL.index("export function createSectionFrame(") :]
frame = frame[: frame.index("\n}\n")]
assert frame.count("ui-sidebar-section ui-panel-form") == 2
def test_B08_B09_카드_공용_카드():
assert "createPanelCard(L(title))" in B08_OPTIONS
assert 'createPanelCard("데이터 점검")' in B09_SHELL
for src in (B08_OPTIONS, B09_SHELL):
assert "ui-sidebar-card" not in src
def test_점검_B05_이상_좌측_컨테이너는_모두_공용_양식():
"""손으로 짠 컨테이너(ui-sidebar-section)에 공용 양식이 빠진 곳 = 0."""
misses, users = [], []
for page in PANEL_DIRS:
for path in (ROOT / page).glob("*.ts"):
rel = path.relative_to(ROOT).as_posix()
text = path.read_text(encoding="utf-8")
if "createIrregularStationsSection(" in text:
users.append(path.name)
if rel in REFERENCE:
continue
for n, line in enumerate(text.splitlines(), 1):
code = line.split("//")[0]
if "ui-sidebar-section" in code and "ui-panel-form" not in code:
misses.append(f"{rel}:{n}")
assert misses == []
# 점검 밖 옛 함수 = 정말 부르는 곳 없음(살아나면 점검에 넣음)
assert users == ["B05_Profile_UI_IrregularStations.ts"]