Files
Aislo/resources/tester/test_722_ho_detail.py
T
eomsangdonandClaude Sonnet 5.5 0c0d0ce629 feat(748-2): 텍스트 수식 — 행 고르기 걷음 · 변수 이름 · 값 하나하나 누르면 줄 모달(748-1) · 줄마다 옵션 값 칩 걷음(748-3 한 조각)
- 텍스트 수식 줄 hover · 누름 고정(행 단위 선택) 걷음
- 변수 이름 식 = 변수 이름마다 낮은 단추 · 값 든 식 = 수 하나하나 낮은 단추 → 같은 줄의 상세 모달(적용 · 삭제 · 닫기)
- 변수가 시험 입력(옵션)이면 옵션 고르기 칩으로
- 공용 상세 `lineOpener` 새로 — M02 SK 상세 · M01 단가산출 기초 같은 길(M01 = 시험 고침 고리)
- 상세 보기 줄마다 붙던 옵션 값 칩(`쓰는입력`) 걷음 — 위 옵션 칸이 대신(748-3)
- 새 파일 `LogicLab_FormulaTokens`(조각 나누기 순수 셈) · 시험 test_748_2 · 낡은 시험 고침 test_722(inputChip 줄 걷음)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-02 22:15:33 +09:00

114 lines
5.1 KiB
Python

"""722 M01 단가산출 상세 개편 지킴 시험 — 글 · 순수 함수(화면 · 서버 없음).
- 722-1 공용 단가산출 상세(`M01_MasterData_UI_LogicLab_Ho.ts`) — 하위 박스(서버 묶음) · 장비 한 줄 + 펼침 · 계산식 열 · 박스 옵션
- 722-2 그 자리 고침(`M01_MasterData_UI_LogicLab_Cover.ts`) — M01 = 시험 값(줄 사본) · 정본 · 캐시 안 바뀜
"""
import json
import shutil
import subprocess
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
M01 = ROOT / "M01_MasterData"
HO = M01 / "M01_MasterData_UI_LogicLab_Ho.ts"
COVER = M01 / "M01_MasterData_UI_LogicLab_Cover.ts"
def _text(p: Path) -> str:
return p.read_text(encoding="utf-8")
def _pure(p: Path) -> str:
s = _text(p)
return s[s.index("/* ── 순수 셈 ── */") : s.index("/* ── 순수 셈 끝 ── */")]
def _node(code: str):
out = subprocess.run(
["node", "--experimental-strip-types", "--no-warnings",
"--input-type=module-typescript", "-e", code],
capture_output=True, text=True, encoding="utf-8", timeout=60,
) # fmt: skip
assert out.returncode == 0, out.stderr
return json.loads(out.stdout)
def test_상세가_공용_부품을_씀():
detail = _text(M01 / "M01_MasterData_UI_LogicLab_Detail.ts")
assert "buildHo({ ...ctx, edit: trialEdit(ctx) })" in detail
ho = _text(HO)
# 부품 상자만 — 표 · 카드 · 계산 줄 · 변수 칸(calc_sheet) · 입력 칩(텍스트 수식)
for part in (
"tableShell(",
"tableRow(",
"createCard(",
"varRow(",
"sheetFormula(",
):
assert part in ho, part
assert "inputChip(" not in ho # 748-3 줄마다 붙던 옵션 값 칩 걷음(위 옵션 칸이 대신)
for s in (ho, _text(COVER)):
assert 'el("table"' not in s and 'el("button"' not in s and 'el("input"' not in s
assert "document.createElement" not in s and "sessionStorage" not in s
assert "export function varRow(" in _text(ROOT / "A00_Common" / "calc_sheet.ts")
for p in (HO, COVER, M01 / "M01_MasterData_UI_LogicLab.ts"):
assert len(_text(p).splitlines()) <= (
627 if p.name == "M01_MasterData_UI_LogicLab.ts" else 700
)
def test_시험_고침은_캐시_밖():
lab = _text(M01 / "M01_MasterData_UI_LogicLab.ts")
# 시험 계산에만 덮은 사본 · 고침이 있으면 줄을 보냄 · drafts(캐시)에는 원본 줄
assert ": trial.apply(row);" in lab
assert "dirty: () => current.id in drafts || trial.has(current.row)," in lab
assert "onTrial: drawCalc," in lab
cover = _text(COVER)
assert "drafts" not in cover and "touch" not in cover and "/save" not in cover
modal = _text(M01 / "M01_MasterData_UI_LogicLab_Modal.ts")
assert "...(target.edit ? [target.edit(close, head)] : [])" in modal
@pytest.mark.skipif(shutil.which("node") is None, reason="node 가 없음")
def test_박스_묶기():
body = """
const e = (idx, bundle, merge, amount) => ({ idx, bundle, merge, amount });
const got = boxesOf([
e(0, "인력", false, 10), e(1, "경비", true, 100), e(1, "경비", true, 20), e(1, "경비", true, 30),
e(2, "하위", true, 5), e(2, "하위", true, null), e(3, "재료", false, null),
], ["인력", "재료", "경비"]).map((b) => ({ 묶음: b.묶음, 소계: b.소계,
줄: b.줄.map((r) => "몫" in r ? [r.idx, r.금액, r.몫.length] : r.idx) }));
console.log(JSON.stringify(got));
"""
got = _node(_pure(HO) + "\n" + body)
assert [b["묶음"] for b in got] == ["인력", "재료", "경비", "하위"]
assert got[0] == {"묶음": "인력", "소계": 10, "줄": [0]}
assert got[1] == {"묶음": "재료", "소계": 0, "줄": [3]}
assert got[2] == {"묶음": "경비", "소계": 150, "줄": [[1, 150, 3]]} # 장비 한 줄 = 몫 셋 합
assert got[3]["줄"] == [[2, 5, 2]]
empty = _node(_pure(HO) + '\nconsole.log(JSON.stringify(boxesOf([], ["인력"])))')
assert empty == [{"묶음": "인력", "줄": [], "소계": 0}]
@pytest.mark.skipif(shutil.which("node") is None, reason="node 가 없음")
def test_고침_덮기():
body = """
const row = { 키: "GF1", 호표: [
{ 종류: "인력", 요소: "LB1", 수량: "찾기(Q).수량" },
{ 종류: "로직", 요소: "로직(GC1)", 수량: "1 / 작업량", 값묶음: { 경비: 3 } },
{ 종류: "재료", 요소: "MT1", 수량: "2" } ], 덧줄: [{ 이름: "공구", 식: "노무비 * 3 / 100" }] };
const out = applyCovers(row, { 0: { qty: 0.0000001, labor: 100000 }, 1: { labor: 50000 }, 9: { qty: 1 } });
console.log(JSON.stringify({ out, same: row.호표[0].수량, keep: out.호표[2] === row.호표[2] }));
"""
got = _node(_pure(COVER) + "\n" + body)
out = got["out"]
assert (
out["호표"][0]["수량"] == "0.0000001" and out["호표"][0]["단가"] == 100000
) # 지수 꼴 없음 · 손 단가
assert out["호표"][1]["값묶음"] == {"경비": 3, "노무비": 50000} and "단가" not in out["호표"][1]
assert got["keep"] and got["same"] == "찾기(Q).수량" # 원본 안 바뀜 · 안 고친 줄 그대로
assert out["덧줄"] == [{"이름": "공구", "식": "노무비 * 3 / 100"}]