Files
Aislo/resources/tester/test_203_b09_hint_lines.py
T

186 lines
7.9 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.
"""203 B09 탭 상세 안내 글 영역 = 줄 수 변수(`--b09-hint-lines` 기본 3)만큼 · 넘치면 영역 안 스크롤 · 표 칸 = 창 끝까지.
- CSS: 줄 수 변수 하나 · 안내 글 최대 높이 = 변수 × 줄 높이(1lh) · 세로 스크롤 · 한 줄 줄임표 걷음
- Shell: 탭 표 = 공용 fillViewport(팝업 · 인쇄 표는 그대로) · 설계내역서 레벨 안내 + 탭 안내 = 한 영역
- 공용 spaceBelow: 아래 형제 바깥 여백(B09 안내 글 margin-top)도 셈 — 빼면 페이지 스크롤이 생김
- 실제 브라우저(정적 쪽 · 서버 불필요): 변수 3 · 2 × 줄 많음 · 적음 — 안내 글 높이 · 합계 줄 · 안내 글 자리 · 페이지 스크롤 0
"""
import re
import subprocess
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
B09 = ROOT / "B09_Estimation"
UI = ROOT / "ui_template"
def _read(path: Path) -> str:
return path.read_text("utf-8")
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_CSS_줄_수_변수_하나():
css = _read(B09 / "B09_Estimation_UI_Page.css")
assert re.search(r"--b09-hint-lines:\s*3;", _block(css, ".b09-estimation"))
assert css.count("--b09-hint-lines:") == 1 # 고칠 자리 하나
hint = _block(css, ".b09-estimation__line-hint")
assert "max-height: calc(var(--b09-hint-lines) * 1lh)" in hint
assert "overflow-y: auto" in hint
for gone in ("white-space: nowrap", "text-overflow: ellipsis"):
assert gone not in hint, gone
def test_Shell_표_창_끝_안내_한_영역():
shell = _read(B09 / "B09_Estimation_UI_Shell.ts")
assert "fillViewport: true" in shell
assert "panel.replaceChildren(table.root, ...(foot ? [foot] : []))" in shell
assert "hint.textContent = `${level.guide.textContent} · ${hint.textContent}`" in shell
# 팝업 · 인쇄 표는 창 끝 셈 안 씀(팝업 = 창 안 max-height · 인쇄 = 펼침)
assert "fillViewport" not in _read(B09 / "B09_Estimation_UI_Builders.ts")
assert "fillViewport" not in _read(B09 / "B09_Estimation_UI_Popup.ts")
# ── 실제 브라우저 ─────────────────────────────────────────────────────────────
TOKENS = (
":root { --spacing-4: 4px; --spacing-8: 8px; --spacing-16: 16px; --spacing-24: 24px;"
" --text-caption: 12px; --radius-lg: 8px; }"
" html, body { margin: 0; line-height: 1.5; font-family: sans-serif; }"
)
LONG = " · ".join(["긴 안내 글 시험 문장"] * 40)
SHORT = "줄을 누르면 단가를 낸 탭 · 줄로 이동"
def _module_js() -> str:
"""공용 표 모듈(TS) → 브라우저 ES 모듈(CSS import 뺌)."""
src = UI / "ui_template_data_table.ts"
code = (
"const ts = require(process.argv[1]);"
"const fs = require('fs');"
"const out = ts.transpileModule(fs.readFileSync(process.argv[2], 'utf8'),"
" { compilerOptions: { module: ts.ModuleKind.ESNext, target: ts.ScriptTarget.ES2020 } });"
"process.stdout.write(out.outputText);"
)
ts_path = ROOT / "config" / "node_modules" / "typescript"
proc = subprocess.run(
["node", "-e", code, str(ts_path), str(src)],
capture_output=True,
text=True,
encoding="utf-8",
timeout=60,
)
assert proc.returncode == 0, proc.stderr
return re.sub(r'^import\s+"[^"]+\.css";\s*$', "", proc.stdout, flags=re.M)
MEASURE = """async ([rows, lines, text]) => {
const { createDataTable } = window.dt;
const body = document.querySelector('.b09-estimation');
body.style.setProperty('--b09-hint-lines', String(lines));
const panel = body.querySelector('.b09-estimation__panel');
const table = createDataTable({
columns: [{ key: 'name', label: '이름' }, { key: 'sum', label: '금액', kind: 'number' }],
rows: Array.from({ length: rows }, (_, i) => ({ name: '줄 ' + i, sum: i })),
total: { name: '계', sum: 1 },
fillViewport: true,
});
const hint = document.createElement('p');
hint.className = 'ui-general-block__empty b09-estimation__line-hint';
hint.textContent = text;
panel.replaceChildren(table.root, hint);
await new Promise((r) => setTimeout(r, 300));
const t = table.root.getBoundingClientRect();
const h = hint.getBoundingClientRect();
const foot = table.root.querySelector('tfoot').getBoundingClientRect();
return {
vh: innerHeight,
page: document.scrollingElement.scrollHeight,
lh: parseFloat(getComputedStyle(hint).lineHeight),
hintH: h.height,
hintScrolls: hint.scrollHeight > hint.clientHeight,
tableTop: t.top,
tableBottom: t.bottom,
tableScrolls: table.root.scrollHeight > table.root.clientHeight,
maxH: parseFloat(table.root.style.maxHeight),
hintTop: h.top,
hintBottom: h.bottom,
padB: parseFloat(getComputedStyle(body).paddingBottom),
footBottom: foot.bottom,
};
}"""
@pytest.fixture(scope="module")
def page():
from playwright.sync_api import sync_playwright
css = _read(UI / "ui_template_data_table.css") + _read(B09 / "B09_Estimation_UI_Page.css")
html = (
f"<!doctype html><html><head><style>{TOKENS}{css}</style></head><body>"
"<div class='b09-estimation'><div class='b09-estimation__tabs'><button>탭</button></div>"
"<div class='b09-estimation__panel'></div></div></body></html>"
)
js = _module_js()
with sync_playwright() as p:
browser = p.chromium.launch(channel="msedge", headless=True)
pg = browser.new_page(viewport={"width": 1280, "height": 900})
pg.set_content(html)
pg.add_script_tag(
type="module",
content=f"{js}\nwindow.dt = {{ createDataTable }};",
)
pg.wait_for_function("!!window.dt")
yield pg
browser.close()
@pytest.mark.parametrize("lines", [3, 2])
def test_줄_많음_표_창_끝_합계_줄_고정_안내_글_바닥(page, lines):
out = page.evaluate(MEASURE, [300, lines, LONG])
assert out["hintH"] == pytest.approx(lines * out["lh"], abs=0.5), out # 높이 = 줄 수 × 줄 높이
assert out["hintScrolls"], out # 넘친 글 = 영역 안 스크롤
assert out["tableScrolls"], out
assert out["page"] == out["vh"], out # 페이지 스크롤 0
assert out["hintBottom"] + out["padB"] == pytest.approx(out["vh"], abs=1.5), out # 창 끝까지 씀
assert out["hintTop"] == pytest.approx(out["tableBottom"] + 8, abs=0.5), out # 표 바로 아래
assert out["tableBottom"] - 30 < out["footBottom"] <= out["tableBottom"], (
out
) # 합계 줄 = 표 칸 바닥
def test_줄_수_2_이면_표가_한_줄만큼_더_김(page):
three = page.evaluate(MEASURE, [300, 3, LONG])
two = page.evaluate(MEASURE, [300, 2, LONG])
assert two["maxH"] - three["maxH"] == pytest.approx(three["lh"], abs=1), (two, three)
@pytest.mark.parametrize("lines", [3, 2])
def test_줄_적음_합계_줄_안내_글_끌려_올라옴(page, lines):
out = page.evaluate(MEASURE, [3, lines, LONG])
assert not out["tableScrolls"], out
assert out["tableBottom"] - out["tableTop"] < out["maxH"] - 100, out # 내용만큼(창 끝 아님)
assert out["hintTop"] == pytest.approx(out["tableBottom"] + 8, abs=0.5), out # 표 끝에 붙음
assert out["hintBottom"] < out["vh"] - 300, out # 화면 바닥에 떨어져 있지 않음
assert out["hintH"] == pytest.approx(lines * out["lh"], abs=0.5), out
assert out["footBottom"] <= out["tableBottom"], out
def test_짧은_글은_한_줄만(page):
out = page.evaluate(MEASURE, [300, 3, SHORT])
assert out["hintH"] == pytest.approx(out["lh"], abs=0.5), out
assert not out["hintScrolls"], out
assert out["page"] == out["vh"], out
def test_spaceBelow_형제_바깥_여백_셈():
src = _read(UI / "ui_template_data_table.ts")
assert "px(m.marginTop) + px(m.marginBottom)" in src # 브라우저 배치는 위 시험이 잼