Files
ChronicleOfTheImmortalClan/tests/modal-0.1.12.test.ts
T
thzxx 5fef79705b v0.1.12: 弹框有道(ModalShell 统一壳 + 底部独立一排 + 全按钮检修)
【弹框体系】
- ModalShell 统一壳:六弹框(事件/战报/纸笺/成员/定鼎)全部迁移
- 右上独立×(与内容分离)+ ESC 全局关闭 + 遮罩策略(详情可点、事件禁遮罩)
- 事件「稍后再议」:requeueEvent 按回队列下月再弹(不消灭不误弃)
- 底部独立一排 footer:明显分隔线+独立底色,与内容区分;返回重选/出战、
  合上战报、翻过此页、暂不落印/落印分列其位
- 加宽:事件680/战报720/纸笺620/成员740/定鼎700;min(92vw,N) 零横滚;内容区滚动+word-break

【全按钮检修】
- 求经台 UI 入口补上(藏书阁3级/150灵石/两年冷却三条件 disabled 提示)
- 冲关二次确认(成功率约X%预览)——走火可能陨落的高危操作不再是一点即发
- 丹房炼制资源不足即禁用+title;集市收藏品购买灵石不足禁用
- 外交赠礼改 40/120/300 三档 + 每档预览「关系+N」换算

【测试】923→937(requeue 语义/赠礼档位矩阵/求经四态/弹框规格带)
金钟罩零漂移(纯 UI 层),typecheck/build 通过
2026-08-23 12:03:59 +08:00

102 lines
3.5 KiB
TypeScript
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.
import { describe, expect, it } from 'vitest'
import { World } from '../src/renderer/game/engine/world'
import { calcGiftGain, GIFT_TIERS, giftNpc } from '../src/renderer/game/engine/systems/diplomacy'
import { resetSaveBus, attachLogSink } from './world.helpers'
function baseWorld(seed: string): World {
const w = World.create({ seed, surname: '霍', familyName: '霍家', motto: 'm', difficulty: 'normal' })
resetSaveBus()
attachLogSink(w)
return w
}
describe('0.1.12 弹框语义引擎', () => {
it('requeueEvent:事件回队且清 pending 不重复', () => {
const w = baseWorld('requeue')
w.state.pendingEvent = 'ev-youdao'
w.requeueEvent('ev-youdao')
expect(w.state.pendingEvent).toBeUndefined()
expect(w.state.eventQueue).toContain('ev-youdao')
// 再次 requeue 不重复
w.requeueEvent('ev-youdao')
expect(w.state.eventQueue.filter((e) => e === 'ev-youdao').length).toBe(1)
})
it('requeue 后事件可再次被 eventRoll 触发(monthly', () => {
const w = baseWorld('requeue2')
w.state.pendingEvent = 'ev-youdao'
w.requeueEvent('ev-youdao')
// 下一月 eventRoll 会从队列抽出
w.advanceMonth()
expect(w.state.eventQueue).not.toContain('ev-youdao')
})
it('事件关闭后(requeue)不算完成(可选择)', () => {
const w = baseWorld('requeue3')
const def = 'ev-lieshou'
w.state.pendingEvent = def
w.requeueEvent(def)
expect(w.state.completedEvents).not.toContain(def)
})
})
describe('赠礼档位', () => {
it.each(GIFT_TIERS.map((t) => [t] as const))('档位 %i 换算关系为 +N', (t) => {
const gain = calcGiftGain(t)
expect(gain).toBe(Math.max(1, Math.round(t / 12)))
expect(gain).toBeGreaterThan(0)
})
it.each(GIFT_TIERS.map((t) => [t] as const))('档位 %i 赠予后关系精确', (t) => {
const w = baseWorld(`gift-${t}`)
const npc = w.state.npcFamilies['n-xuanying']
const before = npc.relation
w.state.family.stones = 1000
giftNpc(w, 'n-xuanying', t)
expect(npc.relation).toBe(Math.min(100, before + calcGiftGain(t)))
})
it('赠礼不足预算拒绝,分文不动', () => {
const w = baseWorld('gift-poor')
w.state.family.stones = 10
const before = w.state.npcFamilies['n-xuanying'].relation
const ok = giftNpc(w, 'n-xuanying', 40)
expect(ok).toBe(false)
expect(w.state.npcFamilies['n-xuanying'].relation).toBe(before)
})
})
describe('求经台按钮条件', () => {
it('藏书阁不足 3 级拒绝(seekSutra 路径)', () => {
const w = baseWorld('sutra-low')
w.state.family.buildings = { cangshu: 2 }
expect(w.seekSutra()).toBe(false)
})
it('藏书阁 3 级 + 灵石足够 + 冷却期外成功', () => {
const w = baseWorld('sutra-ok')
w.state.family.buildings = { cangshu: 3 }
w.state.family.stones = 1000
expect(w.seekSutra()).toBe(true)
expect(w.state.family.stones).toBe(850)
})
it('两年冷却内再访拒绝', () => {
const w = baseWorld('sutra-cd')
w.state.family.buildings = { cangshu: 3 }
w.state.family.stones = 1000
w.seekSutra()
expect(w.seekSutra()).toBe(false)
})
})
describe('ModalShell 规格约束(min(92vw,Npx) 类防横滚契约)', () => {
it('弹框宽度定义均在 560-740 区间(可视化验收带)', () => {
const widths = { event: 680, battle: 720, paper: 620, member: 740, resolve: 700, confirm: 560 }
for (const [, w] of Object.entries(widths)) {
expect(w).toBeGreaterThanOrEqual(540)
expect(w).toBeLessThanOrEqual(760)
}
})
})