Files
ChronicleOfTheImmortalClan/tests/modal-0.1.12.test.ts
T
thzxx ff6df8054c refactor(0.1.14-P1): 内核归位——game/ 按引擎架构重排(零行为漂移)
结构(旧 game/core+engine → 新 engine/ 域):
- engine/kernel/  时钟/随机/插件协议/fxqueue/timesense/format/urgency/guide/names(原 core)
- engine/narrative/  legacy/报告/列传/谱系/年轴(原 core 叙事族)
- engine/runtime/   World/creation/pcgen/ApiFacade/capabilities/pluginManager/boot/clocks + Systems/*(12 系统)
- engine/sim/     Market(未来 WorldSim 同行)
- 旧 game/core、engine/systems、engine/world.ts 等路径全部废弃(无 re-export 兼容层)

验证:35 套件/967 测试全绿(金钟罩三档零漂移=纯搬迁无行为变化)
typecheck 0 error
2026-08-23 13:23:25 +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/runtime/World'
import { calcGiftGain, GIFT_TIERS, giftNpc } from '../src/renderer/game/engine/runtime/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)
}
})
})