Files
90CNServer/pvf/frontend/tests/shortcuts.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

136 lines
5.7 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import {
assignBinding,
bindingFromEvent,
commandForBinding,
dispatchShortcut,
effectiveBinding,
formatBinding,
isValidShortcutBinding,
reservedBindingReason,
} from "../src/shortcuts";
function keyEvent(code: string, options: Partial<KeyboardEvent> = {}): KeyboardEvent {
return {
code,
ctrlKey: false,
metaKey: false,
altKey: false,
shiftKey: false,
defaultPrevented: false,
isComposing: false,
repeat: false,
preventDefault: vi.fn(),
...options,
} as KeyboardEvent;
}
describe("应用快捷键", () => {
it("将不同平台的主修饰键规范为 Mod 并使用物理键码", () => {
expect(bindingFromEvent(keyEvent("KeyS", { metaKey: true, shiftKey: true }), true)).toBe("Mod+Shift+KeyS");
expect(bindingFromEvent(keyEvent("KeyS", { ctrlKey: true, shiftKey: true }), false)).toBe("Mod+Shift+KeyS");
expect(bindingFromEvent(keyEvent("Backslash", { ctrlKey: true }), false)).toBe("Mod+Backslash");
expect(formatBinding("Mod+Backslash", true)).toMatch(/^⌘ /);
expect(formatBinding("Mod+KeyO", false)).toBe("Ctrl+O");
});
it("提供新增应用命令的默认键位", () => {
const defaults = {
"search.advanced": "Mod+Shift+KeyF",
"settings.open": "Mod+Comma",
"editor.closeOthers": "Mod+Shift+KeyW",
"editor.closeAll": "Mod+Alt+KeyW",
} as const;
for (const [command, binding] of Object.entries(defaults)) {
expect(effectiveBinding(command as keyof typeof defaults, {})).toBe(binding);
expect(commandForBinding(binding, {})).toBe(command);
}
expect(effectiveBinding("version.open", {})).toBe("");
expect(bindingFromEvent(keyEvent("Comma", { metaKey: true }), true)).toBe("Mod+Comma");
});
it("拒绝裸字母、仅修饰键和系统或编辑器保留键", () => {
expect(bindingFromEvent(keyEvent("KeyA"), false)).toBeNull();
expect(bindingFromEvent(keyEvent("KeyA", { shiftKey: true }), false)).toBeNull();
expect(bindingFromEvent(keyEvent("ControlLeft", { ctrlKey: true }), false)).toBeNull();
expect(isValidShortcutBinding("Alt+Tab")).toBe(false);
expect(isValidShortcutBinding("F5")).toBe(true);
expect(isValidShortcutBinding("Shift+F5")).toBe(true);
expect(reservedBindingReason("Mod+KeyQ")).toMatch(/系统/);
expect(reservedBindingReason("Alt+ArrowLeft")).toMatch(/编辑器/);
expect(reservedBindingReason("Mod+Shift+KeyG")).toMatch(/编辑器/);
});
it("确认替换时解绑旧命令,清除和恢复默认有确定语义", () => {
const assigned = assignBinding("settings.open", "Mod+KeyO", {});
expect(assigned.displaced).toBe("archive.open");
expect(assigned.overrides["archive.open"]).toBe("");
expect(commandForBinding("Mod+KeyO", assigned.overrides)).toBe("settings.open");
const cleared = assignBinding("settings.open", "", assigned.overrides);
expect(effectiveBinding("settings.open", cleared.overrides)).toBe("");
const restored = assignBinding("archive.open", "Mod+KeyO", cleared.overrides);
expect(restored.overrides["archive.open"]).toBeUndefined();
expect(effectiveBinding("archive.open", restored.overrides)).toBe("Mod+KeyO");
});
it("弹窗、已处理的编辑器事件和不可用命令优先于应用分发", () => {
const execute = vi.fn();
const event = keyEvent("KeyO", { ctrlKey: true });
expect(dispatchShortcut(event, {}, true, execute, undefined, undefined, false)).toBeNull();
expect(dispatchShortcut(keyEvent("KeyO", { ctrlKey: true, defaultPrevented: true }), {}, false, execute, undefined, undefined, false)).toBeNull();
expect(dispatchShortcut(event, {}, false, execute, undefined, () => false, false)).toBeNull();
expect(execute).not.toHaveBeenCalled();
expect(dispatchShortcut(event, {}, false, execute, undefined, () => true, false)).toBe("archive.open");
expect(event.preventDefault).toHaveBeenCalledOnce();
expect(execute).toHaveBeenCalledWith("archive.open");
});
it("关闭其他和关闭全部标签的组合键分发到不同命令", () => {
const execute = vi.fn();
expect(dispatchShortcut(keyEvent("KeyW", { ctrlKey: true, shiftKey: true }), {}, false, execute, undefined, undefined, false)).toBe("editor.closeOthers");
expect(dispatchShortcut(keyEvent("KeyW", { ctrlKey: true, altKey: true }), {}, false, execute, undefined, undefined, false)).toBe("editor.closeAll");
expect(execute.mock.calls.map(([command]) => command)).toEqual(["editor.closeOthers", "editor.closeAll"]);
});
it("workspace.save 快捷键异常拦截:saveActiveTab 抛出错误时捕获并路由至消息提示", async () => {
const errorMessages: string[] = [];
const mockMessageApi = {
error: (msg: string) => errorMessages.push(msg),
};
// 模拟 saveActiveTab 抛出 GUI 未应用修改错误
const saveActiveTab = vi.fn().mockRejectedValue(
new Error("当前文件存在未应用的界面修改,请先在界面中点击【应用修改】后再保存"),
);
function executeSaveShortcut() {
saveActiveTab().catch((err: any) => {
mockMessageApi.error(err?.message ?? String(err));
});
}
const event = keyEvent("KeyS", { ctrlKey: true });
const dispatched = dispatchShortcut(
event,
{},
false,
(cmd) => {
if (cmd === "workspace.save") executeSaveShortcut();
},
undefined,
() => true,
false,
);
expect(dispatched).toBe("workspace.save");
expect(saveActiveTab).toHaveBeenCalledOnce();
// 等待微任务异步捕获完成
await Promise.resolve();
expect(errorMessages).toEqual([
"当前文件存在未应用的界面修改,请先在界面中点击【应用修改】后再保存",
]);
});
});