# -*- coding: utf-8 -*- """PLAN 280 — B05 · B06 구조물조합 칸 · 「구조물 조합 선택」 모달. 280-1 이름 칸 = 고르기 단추 노릇(누름 · Enter · Space · 손 커서 · 풍선 · 초점 테두리) · [선택] 없음 · [해제] 그대로 · 본체 집계 항목 · 그룹 상자 · 명세 컨테이너 잇기가 같은 줄 부품. 280-2 모달 제목 · 위 표 고름 → 나무(펼쳐 굴림) · 오른쪽 카드 · 층 단추 → 위 표 = 그 층(Node · 대역). """ import json import re import subprocess from pathlib import Path import pytest HERE = Path(__file__).resolve().parent ROOT = HERE.parents[1] UI = ROOT / "ui_template" B05 = ROOT / "B05_Profile" def _text(path: Path) -> str: return path.read_text(encoding="utf-8") def _node(helper: str) -> dict: proc = subprocess.run( ["node", str(HERE / helper)], capture_output=True, text=True, encoding="utf-8", timeout=60, ) assert proc.returncode == 0, proc.stderr return json.loads(proc.stdout) @pytest.fixture(scope="module") def row(): return _node("helper_b05_structures_form_summary.cjs") @pytest.fixture(scope="module") def picker(): return _node("helper_280_1_combo_pick.cjs") @pytest.fixture(scope="module") def table(): return _node("helper_224_1_picker_top_table.cjs") # ── 280-1 ──────────────────────────────────────────────────────────────── def test_이름_칸_누름_Enter_Space_고르기_선택_단추_없음_해제_있음(row): assert row["rowEmpty"]["buttons"] == ["해제"] assert row["clicks"][:2] == ["pick", "clear"] assert row["keyClicks"] == { "clicks": ["pick", "pick"], "prevented": ["prevent:Enter", "prevent: "], } # 고른 뒤 · 해제 뒤 이름 칸 글 바뀜 그대로 assert row["rowSet"]["name"] == "돌기슭막이 메쌓기 1.2m" assert row["rowCleared"] == {"name": "미지정", "clearDisabled": True} def test_이름_칸_풍선_초점_손_커서(): src = _text(B05 / "B05_Profile_UI_Structures_Summary_Row.ts") assert 'const PICK_TIP = "눌러서 고르기";' in src assert "name.title = `${summaryTooltip(item)} · ${PICK_TIP}`;" in src assert "name.tabIndex = -1;" not in src # Tab 으로 초점이 감 assert '"선택"' not in src css = _text(B05 / "B05_Profile_UI_Style_Structures.css") base = re.search(r"\.b05-structure__summary-name \{([^}]*)\}", css).group(1) assert "cursor: pointer;" in base focus = re.search(r"\.b05-structure__summary-name:focus-visible \{([^}]*)\}", css).group(1) assert "outline:" in focus def test_같은_꼴_줄_모두_한_부품(): # 본체 집계 항목 · 그룹 상자 · 명세 컨테이너 잇기 = 같은 `buildSummaryRow` for path in ( B05 / "B05_Profile_UI_Structures_Form.ts", B05 / "B05_Profile_UI_Structures_Groups.ts", ROOT / "A00_Common" / "design" / "design_structure_form.ts", ): assert "buildSummaryRow()" in _text(path), path.name # ── 280-2 ──────────────────────────────────────────────────────────────── def test_모달_제목(): locale = _text(UI / "ui_template_locale_m2.ts") assert 'M02_PickerTitle: ["구조물 조합 선택", "Select structure combo"],' in locale assert 'title: t("M02_PickerTitle"),' in _text(UI / "ui_template_item_picker.ts") def test_처음_위_표_보이는_층_표(picker): assert picker["start"] == { "table": "personal|u1|", "layer": ["M02_PickerPersonal"], "detail": None, } def test_위_표_고름_나무_펼쳐_고름_오른쪽_카드(picker): assert picker["tablePick"] == { "active": "personal|u1|SP2", "visible": True, "scrolled": True, "detail": "SP2", "table": "personal|u1|rvP2", } # 같은 열 · 표에 없는 열 = 안 바뀜(표 → 나무 → 표 되돌이 없음) assert picker["sameAgain"] == {"detail": 0, "table": 0} # 접힌 폴더 속 항목도 펼쳐서 고름 assert picker["systemLeavesBefore"] == [] assert picker["systemPick"] == { "active": "system||SS2", "visible": True, "detail": "SS2", "table": "system||rvS2", } def test_층_단추_위_표_그_층(picker): assert picker["layerSystem"] == { "table": "system||", "layer": ["M02_PickerSystem"], "detail": "SP2", # 고른 항목은 그대로 } assert picker["layerCompany"] == {"table": "company|c1|", "layer": ["M02_PickerCompany"]} assert picker["layerBack"] == "personal|u1|rvP2" # 고른 항목 층으로 오면 그 열 def test_나무_고름_위_표_열_지금_길(picker): assert picker["treePick"] == { "active": "system||SS2", "detail": "SS2", "table": "system||rvS2", } def test_찾기에_가려진_항목_표에서_고르면_찾기_풂(picker): assert picker["searchCleared"] == {"value": "", "active": "system||SS1"} def test_위_표_열_고름은_부른_쪽에_층만_바꾸면_열_없이(table): assert table["onColumn"] == ["rv2"] assert table["layerOnly"]["reads"] == [ ["readTemplate", "system", "table", "구조물집계표", None, None] ]