Files
Aislo/resources/tester/test_ui_loading.py
T
eomsangdonandClaude Opus 5.5 25cf716a0d feat(M02): 컨테이너 안 「불러오는 중」 — 0.3초 넘을 때만 · 실패 = 까닭 + 다시 불러오기 (PLAN 30-6)
- 공용 부품 ui_template_loading(showLoading · loadingError · delayedShow) — 칸 안 작은 회전 표시 + 「불러오는 중...」 · 화면 전체 잠금 아님 · 다른 페이지도 씀
- 붙인 자리 — 좌측 나무(구조물 · 일위대가 · 사람 나무) · 상세 칸(구조물 표 · 도면 · 면적 · 재료) · 조합 목록 · 조합 상세 · 항목 카드
- 공용 글 Common_Load_Failed · Common_Reload
- 시험 test_ui_loading(0.3초 규칙 가짜 시계 · 실패 글 · 붙은 자리)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 20:02:36 +09:00

85 lines
3.8 KiB
Python

"""컨테이너 안 「불러오는 중」(PLAN 30-6 · `ui_template_loading.ts`) — 0.3초 · 실패 글 · M02 자리.
① `delayedShow` 를 Node 로 가짜 시계로 돌림 — 0.3초 전에 끝나면 안 보임(깜빡임 없음) · 넘으면 보이고
끝나면 걷힘 · 끝낸 뒤 시계가 와도 안 보임.
② 실패 자리 = 까닭 글(「불러오지 못함 — 까닭」) + [다시 불러오기] · 화면 전체 잠금 안 씀.
③ M02 나무 · 상세(표 · 도면) · 조합 목록 · 조합 상세 · 항목 카드가 이 부품을 씀.
"""
from __future__ import annotations
import json
import re
import shutil
import subprocess
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
LOADING = ROOT / "ui_template" / "ui_template_loading.ts"
M02 = ROOT / "M02_MasterTemplete"
def _text(path: Path) -> str:
return path.read_text(encoding="utf-8")
@pytest.mark.skipif(shutil.which("node") is None, reason="node 가 없음")
def test_0점3초_넘을_때만_보임():
found = re.search(r"^export function delayedShow[\s\S]*?^}", _text(LOADING), re.M)
assert found, "delayedShow 를 못 찾음"
script = (
"const LOADING_DELAY_MS = 300;\n"
+ found.group(0).replace("export ", "")
+ """
let now = 0; const jobs = [];
const timer = {
set: (fn, ms) => { jobs.push({ at: now + ms, fn, live: true }); return jobs.length - 1; },
clear: (id) => { if (jobs[id]) jobs[id].live = false; },
};
const tick = (ms) => {
now += ms;
for (const j of jobs) if (j.live && j.at <= now) { j.live = false; j.fn(); }
};
const log = [];
const on = (n) => () => log.push(n + ":show"); const off = (n) => () => log.push(n + ":hide");
const fast = delayedShow(on("fast"), off("fast"), undefined, timer);
tick(200); fast(); tick(500);
const slow = delayedShow(on("slow"), off("slow"), undefined, timer);
tick(299); log.push("299"); tick(1); slow(); tick(500);
console.log(JSON.stringify(log));
"""
)
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
assert json.loads(result.stdout) == ["299", "slow:show", "slow:hide"]
def test_실패_글과_다시_불러오기_M02_자리():
src = _text(LOADING)
assert 'reason ? `${t("Common_Load_Failed")} — ${reason}` : t("Common_Load_Failed")' in src
assert 'createButton({ label: t("Common_Reload")' in src and 'role: "alert"' in src
assert "showLoadingOverlay()" not in src # 칸 안만(화면 전체 잠금 안 부름)
# 브라우저 setTimeout 을 객체 메서드로 부르면 Illegal invocation — 감싼 기본값(ORCA 흠)
assert "set: (fn, ms) => setTimeout(fn, ms)," in src
assert "clear: (id) => clearTimeout(id)," in src
locale = _text(ROOT / "ui_template" / "ui_template_locale_common.ts")
assert 'Common_Load_Failed: ["불러오지 못함"' in locale
assert 'Common_Reload: ["다시 불러오기"' in locale
tree = _text(M02 / "M02_MasterTemplete_TreePanel.ts")
assert "const stop = showLoading(body);" in tree
assert "loadingError(why(error), () => void refresh())" in tree
main = _text(M02 / "M02_MasterTemplete_UI_Main.ts")
assert "const stop = showLoading(host);" in main
assert "loadingError(why, () => void open(next))" in main
edit = _text(M02 / "M02_MasterTemplete_Labor_Edit.ts")
assert "loadingError(reason(error), () => void open(key))" in edit
assert "(current ? () => {} : showLoading(view))" in edit
assert "const stop = editor.loading();" in _text(M02 / "M02_MasterTemplete_Labor.ts")
assert "showLoading(host)" in _text(M02 / "M02_MasterTemplete_ItemCards.ts")