Files
90CNServer/pvf/frontend/tests/archive-save.test.ts
T
hanfuit 645b64fe21 DNF90 服务端与启动器完整源码(含 GM 工具功能增强)
服务端(Go):
- GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面
- GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新
- 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新)
- 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍
- 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0)
- 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算

启动器(Python/JS):
- 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭
- GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐)
- 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
2026-11-04 14:31:55 +08:00

95 lines
4.5 KiB
TypeScript

import { beforeEach, expect, test, vi } from "vitest";
import { createPinia, setActivePinia } from "pinia";
const api = vi.hoisted(() => ({ Save: vi.fn(), SaveAsDialog: vi.fn(), SetText: vi.fn(), GetAnnotations: vi.fn(), refreshInfo: vi.fn(), refreshTreeTags: vi.fn(), handlers: new Map<string, (event: any) => void>() }));
vi.mock("@wailsio/runtime", () => ({ Events: { On: (name: string, handler: (event: any) => void) => { api.handlers.set(name, handler); } } }));
vi.mock("../bindings/pvfine/services", () => ({ EditorService: api, ArchiveService: {}, FileGUIService: {} }));
vi.mock("../src/stores/archive", () => ({ useArchiveStore: () => ({ refreshInfo: api.refreshInfo }) }));
vi.mock("../src/stores/script", () => ({ useScriptStore: () => ({}) }));
vi.mock("../src/stores/explorer", () => ({ useExplorerStore: () => api }));
import { useEditorStore } from "../src/stores/editor";
beforeEach(() => {
setActivePinia(createPinia());
vi.resetAllMocks();
api.handlers.clear();
});
test("保存从准备到写盘持续反馈,拦截重复保存,完成后释放状态", async () => {
let finish!: (value: any) => void;
api.Save.mockReturnValue(new Promise(resolve => { finish = resolve; }));
const store = useEditorStore();
const pending = store.save();
expect(store.archiveSaveStage).toBe("preparing");
expect(store.saving).toBe(true);
await vi.waitFor(() => expect(store.archiveSaveStage).toBe("saving"));
await expect(store.save()).rejects.toThrow("正在保存");
await expect(store.saveAs()).rejects.toThrow("正在保存");
expect(api.Save).toHaveBeenCalledTimes(1);
expect(api.SaveAsDialog).not.toHaveBeenCalled();
api.handlers.get("archive:save-progress")!({ data: { phase: "packing", done: 5, total: 10, unit: "chunks" } });
expect(store.archiveSaveProgress).toEqual({ phase: "packing", done: 5, total: 10, unit: "chunks" });
api.handlers.get("archive:save-progress")!({ data: { phase: "syncing", done: 0, total: 0, unit: "" } });
expect(store.archiveSaveProgress?.phase).toBe("syncing");
finish({ fileCount: 1 });
await pending;
expect(store.archiveSaveStage).toBeNull();
expect(store.saving).toBe(false);
expect(store.archiveSaveProgress).toBeNull();
api.handlers.get("archive:save-progress")!({ data: { phase: "done", done: 1, total: 1, unit: "" } });
expect(store.archiveSaveProgress).toBeNull();
});
test("写盘失败后收起反馈并允许重试", async () => {
api.Save.mockRejectedValueOnce(new Error("磁盘写入失败")).mockResolvedValueOnce({});
const store = useEditorStore();
await expect(store.save()).rejects.toThrow("磁盘写入失败");
expect(store.archiveSaveStage).toBeNull();
expect(store.saving).toBe(false);
await store.save();
expect(api.Save).toHaveBeenCalledTimes(2);
});
test("另存为取消或失败后清理反馈;成功时等待工作区刷新", async () => {
const store = useEditorStore();
api.SaveAsDialog.mockResolvedValueOnce("").mockRejectedValueOnce(new Error("保存失败"));
expect(await store.saveAs()).toBeNull();
expect(store.archiveSaveStage).toBeNull();
expect(api.refreshInfo).not.toHaveBeenCalled();
await expect(store.saveAs()).rejects.toThrow("保存失败");
expect(store.archiveSaveStage).toBeNull();
expect(store.saving).toBe(false);
let finish!: () => void;
api.SaveAsDialog.mockImplementation(async () => {
expect(store.archiveSaveStage).toBe("saveAs");
return "saved.pvf";
});
api.refreshInfo.mockReturnValue(new Promise<void>(resolve => { finish = resolve; }));
const pending = store.saveAs();
await vi.waitFor(() => expect(store.archiveSaveStage).toBe("finishing"));
expect(store.saving).toBe(true);
finish();
expect(await pending).toBe("saved.pvf");
expect(store.archiveSaveStage).toBeNull();
expect(store.saving).toBe(false);
});
test("单文件保存不触发归档遮罩", async () => {
const store = useEditorStore();
store.tabs.push({ index: 1, path: "test.stk", title: "test.stk", dataType: 1,
size: 1, tags: [], editable: true, original: "before", text: "after", modified: false,
annotations: [], annotationsHidden: false, lineWrapping: true, icon: null,
fieldImage: null, loading: false, loadError: null });
let finish!: () => void;
api.SetText.mockReturnValue(new Promise<void>(resolve => { finish = resolve; }));
const pending = store.saveFileTab(1);
expect(store.saving).toBe(true);
expect(store.archiveSaveStage).toBeNull();
finish();
expect(await pending).toBe(true);
expect(api.SetText).toHaveBeenCalledWith(1, "after");
expect(store.saving).toBe(false);
expect(store.archiveSaveStage).toBeNull();
});