# -*- coding: utf-8 -*- """233-2 3D(띠 · 핀 · 부재) · 횡단 카드(고름 · --span · 형상 · 포장층) · [상세] 선 조각 = 고름 한 칸에서 파생. 까닭: 3D 는 화면끼리 손으로 부르는 사슬로만 칠해져 길마다 빠짐(구조물 고름 뒤 옛 띠 남음 · 횡단 카드 고름은 부재를 안 켬) · 횡단 카드를 풀어도 `--span` · 포장층이 남음(a4) · 풀기 길마다 끄는 범위가 다름 · [상세] 선 조각은 다시 그릴 때(창 크기 · 카드 다시 세움) 고름을 지워 바로 꺼짐. 고침: 종단 보기 구독이 3D 띠 · 핀 · 부재까지 값으로 칠함(마커 알림 없이) · 횡단 보기 구독(`bindCrossPick`)은 다른 곳이 바꾼 값을 넘김 적용 길로 다시 세우고 · 풀기는 한 길(`releaseCrossHighlights` — 카드 · 폼 관 · 범위 테두리 · 포장층 · 형상) · [상세] 다시 그림은 같은 자리 조각 고름 유지. 진짜 TS 를 `helper_233_2_view_pick.cjs` 가 Node(three 포함)로 돌린다. """ from __future__ import annotations import json import os import subprocess import pytest HERE = os.path.dirname(os.path.abspath(__file__)) ROOT = os.path.abspath(os.path.join(HERE, "..", "..")) pytestmark = pytest.mark.skipif( not os.path.isdir(os.path.join(ROOT, "config", "node_modules")), reason="프론트엔드 의존성 미설치", ) def _read(*parts: str) -> str: with open(os.path.join(ROOT, *parts), encoding="utf-8") as handle: return handle.read() @pytest.fixture(scope="module") def out() -> dict: proc = subprocess.run( # noqa: S603 — 고정 실행 파일 ["node", os.path.join(HERE, "helper_233_2_view_pick.cjs")], capture_output=True, text=True, encoding="utf-8", timeout=60, ) assert proc.returncode == 0, proc.stderr return json.loads(proc.stdout) def test_3D_횡단_카드_고름은_띠_핀_그_측점_부재(out) -> None: card = out["card120"] assert (card["red"], card["pin"], card["wall"], card["member"]) == (["s120"], True, True, False) assert card["profile"]["station"] == "s120" def test_3D_부재키_고름은_그_부재만_띠는_없음(out) -> None: part = out["part160"] assert (part["red"], part["pin"], part["member"], part["wall"]) == ([], False, True, False) def test_3D_구조물_고름은_그_자리_구조물_측점_띠(out) -> None: assert out["sign"]["red"] == ["irregular:sign"] assert out["sign"]["profile"]["structure"] == "sign" def test_3D_풂과_알림_없음(out) -> None: released = out["released3d"] assert (released["red"], released["pin"], released["member"], released["wall"]) == ( [], False, False, False, ) assert out["notified"] == 0 # 칠하기만 — 마커 알림이 사슬을 다시 돌리지 않음 def test_횡단_제_누름은_되받지_않고_다른_곳_값은_다시_세움(out) -> None: assert out["ownPick"] == [] assert out["otherPick"] == ["reapply"] assert out["hidden"] == [] # 숨은 보기는 안 받음 def test_횡단_풀기_한_길은_범위_테두리_형상까지(out) -> None: for key in ("release", "otherRelease"): assert out[key] == ["releaseForm", "focus:null", "mark:null", out[key][-1]] assert out["release"][-1] == "refresh:120" assert out["revetAfter"] == [] def test_상세_다시_그려도_같은_자리_조각_고름_유지(out) -> None: assert out["detailPicked"] == 1 assert out["detailAfterRedraw"] == 1 assert out["detailAfterChange"] == 0 def test_배선() -> None: cross = _read("A00_Common", "design", "design_view_cross.ts") assert "bindCrossPick(projectId, host," in cross assert "crossPick.release()" in cross # 카드 다시 누름 = 풀기 한 길 assert "releaseCross(); // 카드 다시 누름" in cross assert len(cross.splitlines()) <= 685 profile = _read("A00_Common", "design", "design_view_profile.ts") assert "{ profile: profilePanel, viewer," in profile assert len(profile.splitlines()) <= 700