Files
Aislo/resources/tester/test_m02_card_shell.py
T
eomsangdon 78f436b136 Merge remote-tracking branch 'origin/sub_laptop_2' into main_laptop_1
시험 충돌(test_m02_card_shell.py) = 47-1 · 47-2 시험과 47-5 시험 둘 다 남김
2026-10-05 13:48:45 +09:00

139 lines
7.7 KiB
Python

"""M02 항목 카드 공용 틀(PLAN 31-7 · `M02_MasterTemplete_CardShell.ts`) — 네 카드가 틀 하나를 씀.
정함: 제목 줄(제목 · 점 ● · 오른쪽 단추) + 한 줄 부제 · 「〇〇 수정」 = 빈 단추 한 모양 ·
[저장] = 늘 보임 · 고쳐야 켜짐 · 카드 안 Ctrl+S · 알림 글 한 벌 · ? = 도면 · 엑셀 ·
불러오는 중 = 네 카드 · 실패 = 카드 안 글 + [다시 불러오기] · 물음 한 번. 화면 조작 판정은 ORCA.
"""
from __future__ import annotations
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
M02 = ROOT / "M02_MasterTemplete"
def _text(name: str) -> str:
return (M02 / name).read_text(encoding="utf-8")
def test_틀_제목_줄_부제_상태():
shell = _text("M02_MasterTemplete_CardShell.ts")
assert "children: [title, dot, tools]," in shell
assert 'const title = el("span", { text: opt.title });' in shell
assert 'tools.addEventListener("click", (event) => event.stopPropagation());' in shell
assert 'el("div", { className: "m02-struct__sub", children: opt.sub })' in shell
assert "loading: () => showLoading(host)," in shell
assert "fail: (reason, retry) => host.replaceChildren(loadingError(reason, retry))," in shell
assert "setDirty: (dirty) => (dot.hidden = !dirty)," in shell
assert 'event.key.toLowerCase() !== "s"' in shell # 카드 안 Ctrl+S
def test_저장_단추_알림_한_벌():
shell = _text("M02_MasterTemplete_CardShell.ts")
assert 'saved: "저장했음"' in shell and 'failed: "저장 못 함 — {value}"' in shell
assert 'stale: "그 사이 바뀜 — 다시 읽음"' in shell
assert "button.disabled = !dirty || saving;" in shell
assert "error instanceof StaleError ? CARD_TEXT.stale" in shell
assert "shell.onSave(() => void (dirty && run()));" in shell
def test_네_카드가_틀을_씀_수정_단추_한_모양():
cards = _text("M02_MasterTemplete_ItemCards.ts")
assert "const shell = cardShell({ title, body, sub: [no, name] });" in cards
assert (
"shell.tools.append(saver.button, take, edit, del);" in cards
) # 40-2 · ? 는 제목 옆(47-1)
assert 'variant: "filled"' not in cards # [수정] 은 모두 빈 단추(ghost)
assert "c.saver.button.hidden = !can;" in cards # [저장] 늘 보임 · ? 는 늘
assert "helpButton(shell, title, CARD_HELP[kind])" in cards
inputs = _text("M02_Labor_Values.ts")
assert inputs.count("cardShell({") == 1 # 넣을 값 카드(선택 입력 카드는 없앰 · 39-7b)
assert "shell.fail(" in inputs and "shell.loading()" in inputs
assert "m02-struct__card ui-collapsible" not in inputs # 손으로 만든 겉 틀 없음
drawing = _text("M02_MasterTemplete_DrawingCard.ts")
assert "createButton" not in drawing and "showToast" not in drawing # 단추 · 알림은 틀
def test_실패는_카드_안_글과_다시_불러오기():
cards = _text("M02_MasterTemplete_ItemCards.ts")
assert "showToast" not in cards # 토스트 안 씀(저장 알림은 틀)
assert "c.shell.fail(" in cards and "reload" in cards
picker = (ROOT / "ui_template" / "ui_template_item_picker_detail.ts").read_text("utf-8")
# 일위대가 카드 · 값 카드가 다시 불러오기 하나(retry)를 같이 씀(39-1c)
assert "const retry = (): void => void paintLabor(row, seq);" in picker
assert "inputs.set(null, why(error), retry);" in picker
assert "const stops = [labor.loading(), inputs.loading()];" in picker
def test_일위대가_카드는_상세_카드_갈래():
"""43-4 — 일위대가 카드 몸 = Labor_Detail 카드 갈래(단가산출 로직 2행 표 + 자재목록 · 순서 ·
로직 추가 · 다이어그램 없음 · 빼기 그대로) · 카드 [저장] = 면적 먼저 → 일위대가 ·
한 줄 표 지움."""
cards = _text("M02_MasterTemplete_ItemCards.ts")
assert "mod.mountLaborDetail(laborHost, got.문서, {" in cards and "card: true," in cards
assert 'makeCard("labor", PART_WORD.labor, laborHost, () => saveLabor())' in cards
save = cards[cards.index("async function saveLabor()") :]
assert save.index("laborEd.saveArea()") < save.index('saveTemplate(layer, "labor"')
assert "onChanged: (dirty) => laborCard.saver.mark(dirty && opts.editable())," in cards
assert "Labor_View" not in cards # 한 줄 표(40-2) 안 씀
detail = _text("M02_MasterTemplete_Labor_Detail.ts")
assert "...(opt.card ? {} : { move })" in detail # 순서 단추 없음 · 빼기는 그대로
assert "opt.card ? null : addLogic" in detail and "...(opt.card ? [] : [dgBox])," in detail
rows = _text("M02_MasterTemplete_Labor_Rows.ts")
assert "const tools = !!(move || remove || option);" in rows and "compactTable" not in rows
assert "compact" not in _text("M02_MasterTemplete_Labor_View.ts").split("*/", 1)[1]
def test_일위대가_상세_단위는_표준_단위_고르기():
"""44-7 — 상세 머리 「단위」 = 표준 단위 목록(UnitPick) · 지금 값이 목록에 없어도 선택지 ·
고르면 캐시만(저장은 상세 [저장]) · 「-」 = 없음."""
detail = _text("M02_MasterTemplete_Labor_Detail.ts")
assert 'import { createUnitPick, NO_UNIT } from "./DataFlow/M02_DF_UnitPick";' in detail
assert "extra: [unit0, cur.단위].filter((u): u is string => !!u)," in detail
assert "cur.단위 = u === NO_UNIT ? null : u;" in detail
assert "readOnly: !edit," in detail and "maxLength = 20" not in detail
def test_도움말_제목_옆_네_절():
"""47-1 — ? = 카드 제목 바로 옆 · 도움말 = 설명 · 단축키 · 기능 · 사용법 · 카드 셋 모두."""
shell = _text("M02_MasterTemplete_CardShell.ts")
assert "shell.title.after(button);" in shell
for part in ("about", "keys", "features", "steps"):
assert f'"{part}"' in shell
assert "event.stopPropagation(); // 누름 ≠ 접기" in shell
help_ = _text("M02_MasterTemplete_CardHelp.ts")
for kind in ("structure:", "labor:", "area:"):
assert kind in help_
assert "keys: DRAWING_KEYS," in help_ and "keys: SHEET_KEYS," in help_
for label in ("가져오기", "수정", "삭제", "저장", "알약"):
assert f'"{label}"' in help_
def test_미리보기_파일_불러오기_없음():
"""47-2 — 도면 카드 미리보기엔 📂 없음 · 도면 상세(전체 편집)는 그대로."""
drawing = _text("M02_MasterTemplete_Drawing.ts")
assert "const canImport = !readOnly && options.noImport !== true;" in drawing
assert "noImport: true," in _text("M02_MasterTemplete_DrawingCard.ts")
def test_일위대가_줄_고름과_옵션_단추():
"""47-5 — 줄 누름 = 고름(하이라이트 · 키로 둬서 다시 그려도 유지) · 넣을 값 모달 = [옵션] ·
단추 위로 · 아래로 · 옵션 · 삭제(빨강) · 묶음 간격 = 공용 `ui-btn-group`."""
detail = _text("M02_MasterTemplete_Labor_Detail.ts")
assert "open: pick," in detail and "option: openRow," in detail
assert "{ open: pick, option: openRow }" in detail # 읽기만도 고름 · [옵션] = 보기 모달
assert "selected: picked" in detail
rows = _text("M02_MasterTemplete_Labor_Rows.ts")
up, down = rows.index('small(tc("Up")'), rows.index('small(tc("Down")')
opt, rm = (
rows.index('small(tc("Labor_Option")'),
rows.index('small(tc("Remove"), () => remove(i), "danger")'),
)
assert up < down < opt < rm and 'className: "ui-btn-group"' in rows
text = _text("M02_MasterTemplete_Labor_Text.ts")
for t in ('Up: ["위로"', 'Down: ["아래로"', 'Remove: ["삭제"', 'Labor_Option: ["옵션"'):
assert t in text
css = (ROOT / "ui_template" / "ui_template_general_layout.css").read_text(encoding="utf-8")
assert ".ui-btn-group {" in css and "gap: var(--btn-group-gap);" in css