"""549-1 대시보드 세부 페이지(받은 예정노선 · 임시 보관함 · 사람 · 회사 정보 · 회사 설계 기본값) 시험. · 화면 계산(`logic/B01_Dashboard_Logic_Pages.ts`) — 역할 · 상태 한글 · 남은 날 · 막대 · 가입 요청 수 — Node 로 바로 돌림 · 등록부 — 네 몫 다섯 열쇠가 옛 구역(legacy)이 아닌 새 페이지를 가리킴 · 역할 값 그대로 · 화면 조작 · 눌러 보기는 ORCA """ from __future__ import annotations import json import re import shutil import subprocess from pathlib import Path import pytest ROOT = Path(__file__).resolve().parents[2] B01 = ROOT / "B01_Dashboard" LOGIC = B01 / "logic" / "B01_Dashboard_Logic_Pages.ts" MINE = { "assigned": "mountAssigned", "temp": "mountTemp", "people": "mountPeople", "company": "mountCompany", "company-defaults": "mountCompanyDefaults", } needs_node = pytest.mark.skipif(shutil.which("node") is None, reason="node 가 없음") def _run_logic(body: str) -> object: script = LOGIC.read_text(encoding="utf-8") + "\n" + body result = subprocess.run( ["node", "--experimental-strip-types", "--no-warnings", "--input-type=module-typescript", "-e", script], capture_output=True, text=True, encoding="utf-8", timeout=60, ) # fmt: skip assert result.returncode == 0, result.stderr return json.loads(result.stdout) @needs_node def test_역할_상태_한글_배지(): out = _run_logic( "console.log(JSON.stringify([" "roleText('SYSTEM_ADMIN'), roleText('ADMIN'), roleText('USER'), roleText(null)," "statusBadgeOf('ACTIVE'), statusBadgeOf('inactive'), statusBadgeOf('PENDING')," "statusBadgeOf('REJECTED'), statusBadgeOf('뭔가'), statusBadgeOf(null)]));" ) assert out[:4] == ["시스템 관리자", "회사 관리자", "일반 사용자", "-"] assert out[4] == {"text": "활성", "tone": "success"} assert out[5] == {"text": "비활성", "tone": "neutral"} assert out[6] == {"text": "대기", "tone": "warning"} assert out[7] == {"text": "거절", "tone": "danger"} assert out[8] == {"text": "뭔가", "tone": "neutral"} assert out[9] == {"text": "-", "tone": "neutral"} @needs_node def test_보관함_남은_날_막대_강조(): out = _run_logic( "const now = Date.parse('2026-10-26T00:00:00Z');" "const d = (s: string | null) => daysLeft(s, now);" "console.log(JSON.stringify([" "d('2026-11-25T00:00:00Z'), d('2026-11-02T00:00:00Z'), d('2026-11-01T12:00:00Z')," "d('2026-10-20T00:00:00Z'), d(null), d('아무말')," "leftPercent(30, 30), leftPercent(15, 30), leftPercent(0, 30), leftPercent(null, 30)," "leftPercent(40, 30), leftPercent(5, 0)," "isExpiringSoon(7), isExpiringSoon(8), isExpiringSoon(0), isExpiringSoon(null)]));" ) # 30 일 · 7 일 · 올림(6.5 → 7) · 지남 0 · 없음 · 잘못된 값 assert out[:6] == [30, 7, 7, 0, None, None] assert out[6:12] == [100, 50, 0, 0, 100, 0] assert out[12:] == [True, False, True, False] @needs_node def test_가입_요청_수는_대기만(): out = _run_logic( "console.log(JSON.stringify([" "pendingCount([]), pendingCount([{status:'PENDING'},{status:'pending'},{}])," "pendingCount([{status:'APPROVED'},{status:'REJECTED'}])]));" ) assert out == [0, 3, 0] @needs_node def test_바이트_글(): out = _run_logic( "console.log(JSON.stringify([bytesText(100), bytesText(2048), bytesText(5*1024*1024)," "bytesText(3*1024*1024*1024)]));" ) assert out == ["1 KB", "2 KB", "5.0 MB", "3.00 GB"] def test_등록부_네_몫_다섯_열쇠는_새_페이지(): text = (B01 / "B01_Dashboard_UI_Views.ts").read_text(encoding="utf-8") for key, mount in MINE.items(): line = next(ln for ln in text.splitlines() if f'key: "{key}"' in ln) assert f"mount: {mount}" in line, key assert "legacy(" not in line, key assert f"import {{ {mount} }}" in text, key # 역할 값은 546 그대로 assert re.search(r'key: "assigned".*roles: \["USER", "ADMIN", "SYSTEM_ADMIN"\]', text) assert re.search(r'key: "people".*roles: \["ADMIN", "SYSTEM_ADMIN"\]', text) assert re.search(r'key: "company-defaults".*roles: \["USER", "ADMIN", "FOREST"\]', text) def test_새_페이지_파일은_700줄_이하(): files = [*B01.glob("B01_Dashboard_UI_View_*.ts"), B01 / "B01_Dashboard_UI_PageKit.ts", B01 / "B01_Dashboard_UI_TempUpload.ts", LOGIC] # fmt: skip for path in files: assert len(path.read_text(encoding="utf-8").splitlines()) <= 700, path.name