Files
Aislo/resources/tester/test_m02_leave_dialog.py
T

126 lines
4.3 KiB
Python

"""저장 안 한 고침 3단추 물음(PLAN 86-1 · `ui_template_leave_dialog.ts`) — Node 로 TS 를 돌려 판정.
못박는 것
- 단추 순서 [취소] [버리고 넘어감] [저장 후 넘어감] · 기본 초점 = 취소 · Esc = 취소.
- 저장 후 넘어감: 저장 성공 + 고침 없음 = saved / 저장 던짐 · 고침 남음 = stay(머묾).
- 버리고 넘어감: discard 갈고리 호출 · 취소: 아무것도 안 부름.
- M02 자리(UI_Main · Structure · ItemCards)가 이 물음을 씀.
"""
import json
import subprocess
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
M02 = ROOT / "M02_MasterTemplete"
SCRIPT = r"""
const fs = require("fs");
const ts = require(process.argv[1] + "/config/node_modules/typescript");
const src = fs.readFileSync(process.argv[1] + "/ui_template/ui_template_leave_dialog.ts", "utf8");
const js = ts.transpileModule(src, {
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
}).outputText;
let focused = null;
let overlay = null;
let buttons = [];
const node = (tag, o = {}) => ({
tag, ...o, kids: [], on: {},
append(...x) { this.kids.push(...x); },
addEventListener(k, f) { this.on[k] = f; },
remove() { overlay = null; },
});
const stubs = {
"./ui_template_elements": {
el: node,
createButton: (o) => {
const b = node("button", { label: o.label, variant: o.variant });
b.focus = () => { focused = b.label; };
b.click = o.onClick;
buttons.push(b);
return b;
},
},
"./ui_template_locale": { t: (k) => k },
};
globalThis.document = { activeElement: null, body: { append(x) { overlay = x; } } };
const box = { exports: {} };
new Function("exports", "module", "require", js)(box.exports, box, (n) => stubs[n] ?? require(n));
const { leaveOrStay } = box.exports;
const run = async (pick, hooks) => {
buttons = [];
focused = null;
const calls = [];
const wrap = {
save: async () => { calls.push("save"); if (hooks.throws) throw new Error("x"); },
stillDirty: () => !!hooks.left,
discard: () => calls.push("discard"),
};
const p = leaveOrStay("msg", wrap);
const labels = buttons.map((b) => b.label);
const first = focused;
if (pick === "esc") overlay.on.keydown({ key: "Escape", stopPropagation() {} });
else buttons[pick].click();
return { got: await p, calls, labels, first };
};
(async () => {
const out = {
cancel: await run(0, {}),
esc: await run("esc", {}),
discard: await run(1, {}),
save: await run(2, {}),
saveThrows: await run(2, { throws: true }),
saveLeft: await run(2, { left: true }),
};
process.stdout.write(JSON.stringify(out));
})();
"""
@pytest.fixture(scope="module")
def got() -> dict:
done = subprocess.run(
["node", "-e", SCRIPT, str(ROOT)],
capture_output=True,
text=True,
encoding="utf-8",
check=True,
)
return json.loads(done.stdout)
def test_단추_순서_기본_초점_취소(got):
row = got["cancel"]
assert row["labels"] == ["M02_TreeCancel", "M02_TreeDiscardGo", "M02_TreeSaveGo"]
assert row["first"] == "M02_TreeCancel"
def test_취소_Esc는_머묾(got):
assert got["cancel"]["got"] == "stay" and got["cancel"]["calls"] == []
assert got["esc"]["got"] == "stay" and got["esc"]["calls"] == []
def test_버리고_넘어감(got):
assert got["discard"]["got"] == "discarded"
assert got["discard"]["calls"] == ["discard"]
def test_저장_후_넘어감과_실패_머묾(got):
assert got["save"] == {**got["save"], "got": "saved", "calls": ["save"]}
assert got["saveThrows"]["got"] == "stay" # 저장 던짐
assert got["saveLeft"]["got"] == "stay" # 저장 뒤에도 고침이 남음(카드 저장 실패 등)
def test_M02_자리가_3단추_물음을_씀():
main = (M02 / "M02_MasterTemplete_UI_Main.ts").read_text(encoding="utf-8")
assert "leaveOrStay(message, { save: doSave, stillDirty: busy" in main
assert "askSettle" not in main
assert "!(await showConfirmDialog(L(" not in main
cards = (M02 / "M02_MasterTemplete_ItemCards.ts").read_text(encoding="utf-8")
assert "leaveOrStay(TEXT.discard" in cards
struct = (M02 / "M02_MasterTemplete_Structure.ts").read_text(encoding="utf-8")
assert struct.count("leaveOrStay(TEXT.discardCards") == 2