Files
gplan-2/game/tests/unit/presentation/forge_workbench_test.gd
T
DKChan 2915f19c4b feat(迭代23-T6): 营地锻造工坊工作间——两区六路线行板实装(占位面板替换)+三意图信号接线
- MetaProgression.forge_shop_status():只读投影(等级/cap/解锁态/选中态/下级几何价/解锁价/前置通道进度——纯结构面,余额禁用归视图)
- CampOverlay:_forge_workbench 占位面板→武器锻造区/护甲锻造区两区六路线行板(件名+Lv+解锁态文案+锻造/解锁/选中钮);_rebuild_forge 随 open/refresh 重建;默认不可见+门控重建=锁定态零行板零购买路径(占位纪律延续);三信号 forge_buy/unlock/select_requested(契约 9 UI 只发意图)
- camp_shop_presenter:camp_data +forge 投影;on_camp_forge_buy/unlock/select 三处理器(写口唯一归 MetaProgression 锻造写口族;拒绝可见 pattern 1 toast)
- main.gd:三信号接 presenter
- 新单测 forge_workbench_test 12 断言(锁定态纪律/两区结构/钮态/余额禁用/满级/派生解锁出钮/三意图外发);run_all 23→24 套
- 轻闸:冒烟 938/0+run_all 24 套+probe --click 全绿(presentation 触碰加跑)
2026-10-13 00:21:41 +08:00

110 lines
6.3 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
extends SceneTree
# 锻造工坊工作间 UI 直验套件(迭代 23 T6——V13 人闸的自动化前置面:两区结构/钮态/意图信号/锁定态纪律)
# 覆盖:锁定态零行板零购买路径(占位纪律延续)· 两区各三路线 · 初始持有行购买钮(几何价 20)·
# 待解锁行解锁钮(200)+无购买钮 · 选中钮当前态禁用 · 余额不足禁用态 · 满级无购买钮 ·
# 三意图信号外发(UI 只发意图——契约 9,写口归 MetaProgression 由冒烟侧覆盖)。
# 运行:godot --headless --path game --script res://tests/unit/presentation/forge_workbench_test.gd(expect 0)
const Kit := preload("res://tests/unit/test_kit.gd")
const _TEST_SAVE := "user://test_forge_workbench.json"
func _initialize() -> void:
call_deferred("_run")
func _row_by_id(ov) -> Dictionary:
var out := {}
for r in ov._forge_rows:
out[String(r.route_id)] = r
return out
func _run() -> void:
var db = load("res://core/config_db.gd").new()
var load_ok: bool = db.load_tables("res://data/tables", true)
if not load_ok:
Kit.check("ConfigDb 加载(工作间测试夹具前提)", false)
quit(Kit.finish("forge_workbench_test"))
return
var sv = load("res://core/save_manager.gd").new(_TEST_SAVE)
sv.load_from_disk()
var mp = load("res://core/meta_progression.gd").new(db, sv)
var ov = load("res://presentation/views/camp_overlay_view.gd").new()
root.add_child(ov)
var buys := []
var unlocks := []
var selects := []
ov.forge_buy_requested.connect(func(rid): buys.append(rid))
ov.forge_unlock_requested.connect(func(rid): unlocks.append(rid))
ov.forge_select_requested.connect(func(rid): selects.append(rid))
# ① 锁定态:树钥匙未点亮 → 工作间不可见、两区零行板(无任何可交互购买路径)
ov.open_camp({"balance": 999, "forge": mp.forge_shop_status(), "tree": []})
Kit.check("锁定态:工作间不可见(forge_workshop 钥匙未点亮)〔锻造工坊批 V13〕",
not ov._forge_workbench.visible and ov._forge_lock_l.visible)
Kit.check("锁定态:两区零行板(占位纪律——零购买路径)〔锻造工坊批 V13〕",
ov._forge_zone_weapon.get_child_count() == 0 and ov._forge_zone_armor.get_child_count() == 0)
# ② 解锁态:钥匙点亮 → 两区各三路线
ov.open_camp({"balance": 500, "forge": mp.forge_shop_status(),
"tree": [{"node_id": "forge_workshop", "level": 1}]})
Kit.check("解锁态:工作间可见(钥匙点亮展开)〔锻造工坊批 V13〕", ov._forge_workbench.visible)
Kit.check("两区各三路线(武器锻造区 3 + 护甲锻造区 3)〔锻造工坊批 V13〕",
ov._forge_zone_weapon.get_child_count() == 3 and ov._forge_zone_armor.get_child_count() == 3)
var rows: Dictionary = _row_by_id(ov)
Kit.check("六路线行册恰全(sword/spear/staff/light/heavy/ward)〔锻造工坊批 V13〕",
rows.has("sword") and rows.has("spear") and rows.has("staff")
and rows.has("light") and rows.has("heavy") and rows.has("ward"))
# ③ 初始持有行(长剑/轻甲):购买钮现形带几何价、选中钮=当前禁用
var rsw = rows.get("sword", {})
var rli = rows.get("light", {})
Kit.check("长剑行:购买钮「锻造 20」(0 级位几何价)+选中钮「当前」禁用〔锻造工坊批 V13〕",
rsw.get("buy_btn") != null and String(rsw.buy_btn.text) == "锻造 20" and not rsw.buy_btn.disabled
and rsw.get("select_btn") != null and String(rsw.select_btn.text) == "当前" and rsw.select_btn.disabled)
Kit.check("轻甲行:购买钮「锻造 20」+选中钮「当前」禁用〔锻造工坊批 V13〕",
rli.get("buy_btn") != null and String(rli.buy_btn.text) == "锻造 20"
and String(rli.select_btn.text) == "当前" and rli.select_btn.disabled)
# ④ 待解锁行(长枪/法杖/重甲/魔法护甲):解锁钮 200、无购买钮、无选中钮、状态含双通道文案
var rsp = rows.get("spear", {})
var rwa = rows.get("ward", {})
Kit.check("长枪行:解锁钮「解锁 200」+无购买钮+无选中钮〔锻造工坊批 V13〕",
rsp.get("unlock_btn") != null and String(rsp.unlock_btn.text) == "解锁 200" and not rsp.unlock_btn.disabled
and rsp.get("buy_btn") == null and rsp.get("select_btn") == null)
Kit.check("魔法护甲行:解锁钮「解锁 200」+状态含并联双通道文案(练满轻甲锻造 或 200 钥石直购)〔锻造工坊批 V13〕",
rwa.get("unlock_btn") != null and String(rwa.status_l.text).contains("练满轻甲锻造")
and String(rwa.status_l.text).contains("200"))
# ⑤ 余额不足禁用态(常态防线)
ov.refresh_camp({"balance": 0, "forge": mp.forge_shop_status(),
"tree": [{"node_id": "forge_workshop", "level": 1}]})
rows = _row_by_id(ov)
Kit.check("余额 0:长剑购买钮禁用+长枪解锁钮禁用(余额联动常态防线)〔锻造工坊批 V13〕",
rows.sword.buy_btn.disabled and rows.spear.unlock_btn.disabled)
# ⑥ 满级行:无购买钮、状态=已满级(直置存档域构造——两分法调试口径)
sv.set_forge_route_level("sword", 5)
ov.refresh_camp({"balance": 500, "forge": mp.forge_shop_status(),
"tree": [{"node_id": "forge_workshop", "level": 1}]})
rows = _row_by_id(ov)
Kit.check("长剑满级:无购买钮+状态「已满级」+长枪派生解锁出解锁钮消失(练满通道)〔锻造工坊批 V13〕",
rows.sword.get("buy_btn") == null and String(rows.sword.status_l.text) == "已满级"
and rows.spear.get("unlock_btn") == null and rows.spear.get("buy_btn") != null)
# ⑦ 三意图信号外发(UI 只发意图——按真实按钮路径;选中钮直发信号面)
buys.clear(); unlocks.clear(); selects.clear()
rows.spear.buy_btn.pressed.emit() # 长枪已派生解锁——真实按钮路径
rows.heavy.unlock_btn.pressed.emit() # 重甲待解锁——真实按钮路径
ov.forge_select_requested.emit("spear") # 选中意图(当前行选中钮禁用态——信号面直发)
Kit.check("三意图信号外发(buy=spear/unlock=heavy/select=spear——UI 只发意图)〔锻造工坊批 V13〕",
buys == ["spear"] and unlocks == ["heavy"] and selects == ["spear"])
# 尾清(测试文件绝不残留)
ov.queue_free()
for suffix in ["", ".tmp", ".corrupt.bak"]:
if FileAccess.file_exists(_TEST_SAVE + suffix):
DirAccess.remove_absolute(_TEST_SAVE + suffix)
quit(Kit.finish("forge_workbench_test"))