"""780-2 지킴 — 엑셀 보기 단가산출표 · 상세 탭 = 오른쪽 단가산출 목록(요구 627). 서버 없이 잡음: 탭(역할) 따라 판 바꿈(일위대가표 = 호표 목록 · 단가산출 탭 = 단가산출 목록 · 칸은 처음 한 번만 지음) · 시트 행 → 목록 강조 · 목록 줄 → 지금 탭의 블록 자리 · 만들 층 ≠ 파일 층 = 보기만 · 거르기(전체 · 안 · 밖 · 고를 줄 있음) · [끝내기] = 막지 않고 물음창 글만 · 화면 = 부품 상자만 · 700 줄. """ import json import os import re import subprocess from pathlib import Path HERE = os.path.dirname(__file__) M02 = Path(__file__).resolve().parents[2] / "M02_MasterTemplete" def _node(payload: dict) -> dict: proc = subprocess.run( ["node", os.path.join(HERE, "helper_778_5_sk_pick.cjs")], input=json.dumps(payload, ensure_ascii=False), capture_output=True, text=True, encoding="utf-8", check=False, ) assert proc.returncode == 0, proc.stderr return json.loads(proc.stdout) PICKS = [["s1", "B5"], ["s2", "B12"], ["s1", "C3"], ["s3", "A85"], ["s3", "A2"]] def test_판_바꿈_목록_지음_강조_뛰기(): got = _node({"sheetSk": {"same": True, "canWrite": True, "picks": PICKS}}) # 785-6 — 단가산출 목록 = 새 블록 판정(hopyo-judge 목록) · 처음 단가산출 탭에 올 때 한 번 읽음 hide = [(s["호표판숨김"], s["단가판숨김"], s["읽음"]) for s in got["seen"]] assert hide == [ (False, True, 0), (True, False, 1), (False, True, 1), (True, False, 1), (True, False, 1), ] assert [on for _, on in got["toggled"]] == [False, True, False, True, True] assert got["reads"] == [{"key": "HP-000001", "to": {"layer": "personal", "owner": None}}] # 목록 행 12 → 단산8 강조 · 상세 2 행 → 블록 0(없음) — 목록 줄 누름 뒤 단산8 assert [s["강조"] for s in got["seen"]] == [ None, "단산8 거푸집", "단산8 거푸집", "단산8 거푸집", None, ] # 목록 줄 누름 = 지금 탭의 자리(단가산출표 = 목록 행 · 상세 = 블록 범위) + 시트 아래 블록 판 assert got["jumps"] == [["s2", [8, 9]], ["s3", [8, 9]], ["s3", [8, 9]]] assert got["blocks"] == [{"no": 8, "readOnly": False, "onSheet": True}] * 3 rows = {r["label"]: r for r in got["rows"]} assert rows["단산8 거푸집"]["sub"] == "㎡ · 고를 것 2 · M02_JudgeCalls 1" assert rows["단산8 거푸집"]["tail"] == "accent:M02_JudgeConfirmed" # 캐시 확인 assert rows["단산9 잡석"]["sub"] == "㎥ · 줄 조합 · 준용 · M02_JudgeOutside" assert rows["단산10 운반"]["tail"] == "success:M02_JudgeMade SK-000001" assert got["count"] == {"블록": 2, "밖": 1, "줄": 2} # 만든 SK 뺌 def test_다른_층_권한_없음_보기만(): for same, can in ((False, True), (True, False)): got = _node({"sheetSk": {"same": same, "canWrite": can, "picks": [["s2", "B5"]]}}) assert got["blocks"][0]["readOnly"] is True def _src(name: str) -> str: return (M02 / f"M02_MasterTemplete_Hopyo_{name}.ts").read_text(encoding="utf-8") def test_캐시_공유_알림_칠_다시(): sheet = _src("Sheet") assert "createSheetSk(" in sheet and "sk?.at(s);" in sheet side = _src("SheetSk") assert "readJudge(opt.scope, opt.key, opt.target)" in side # 785-6 새 블록 판정 목록 nav = _src("SheetNav") assert "blockAt(sheet: string, row: number)" in nav and "spotOf(no: number, role: Role)" in nav wizard = _src("Import") assert 'L("M02_SkUnmoved")' in wizard and "leftCount" not in wizard # 밖 = 안 막음 · 옛 남은 줄 막음 걷음 tone = (M02 / "logic" / "M02_MasterTemplete_Hopyo_SheetMap.ts").read_text(encoding="utf-8") assert '글: "일위대가 밖"' in tone def test_부품_상자만_700줄(): for name in ("SheetSk", "Sheet", "SheetNav", "Import", "Side"): src = _src(name) assert len(src.splitlines()) <= 700, name assert not re.search(r"#[0-9a-fA-F]{3,6}\b|rgb\(", src), name for name in ("SheetSk", "Import"): assert not re.search(r'el\("(button|input|select)"|