根因:onBondPriceKeydown 的按键排除列表只排除回车/Ctrl-Alt-Meta/Tab/Home/End/←→/F5, 漏掉 ↑(38)/↓(40)/Insert(45)/PageUp(33)/PageDown(34)/F1-F4,F6-F12/纯Shift。 这些键不改变数值却会触发 applyBondManualEdit 标 REV——正是用户观察到的 "鼠标放输入框没改值、只按了方向键/功能键却有时出现REV"。 修复: - swapCalc.js 新增纯函数 isBondPriceValueKey(event):正向白名单(数字/小数点/减号/Backspace/Delete) 才返回 true,其余(回车/修饰键/导航键/功能键/纯Shift)返回 false,jest 可测。 - swapTradeEdit.js onBondPriceKeydown 改用该函数判定,并移除两行每次按键的 console.log 噪音。 - 纯鼠标聚焦(无按键)本就不会标REV(无 focus/click 处理;vue-number-input 的 input 仅在值变化(失焦/回车)触发), 本次仅修正按键守卫缺口。 - 同步补 9 个单测覆盖各类按键(响应"最好单测阶段解决")。 约定1/约定2/约定3 累加REV 行为不受任何影响(仅改判定是否标REV的触发条件)。 测试:bondCalc 全套 81 例通过。
331 lines
19 KiB
JavaScript
331 lines
19 KiB
JavaScript
/**
|
||
* bondCalc.test.js — 债券净价/全价/收益率三字段互算(driver 源 + 交互约定2/3 状态机)
|
||
* ============================================================================
|
||
* 模型(交互约定1/2/3):
|
||
* - driver(bondDriverType):回车的"源"字段(CP净价 / DP全价 / YD收益率),恒为最后回车者。
|
||
* - bondAuto:计算器算出的另两个字段("AUTO")。
|
||
* - bondRev:编辑未回车的人工输入字段("REV")。
|
||
* - applyBondCalcResult(state, calc, driver):回写时跳过 driver 源字段(保留用户手输值),
|
||
* 另两字段用计算器结果覆盖——即"回车某字段即以它为源反算另两个"。
|
||
*
|
||
* 运行:cd YLErpWeb/fe-tests && npm i && npm test
|
||
* 纯逻辑在 swapCalc.js(UMD,node 可直接 require)。
|
||
*/
|
||
const SwapCalc = require('../wwwroot/Scripts/app/swaptrade/swapCalc.js');
|
||
|
||
const CALC = { cleanPrice: 99, dirtyPrice: 100, ytm: 2.5 };
|
||
|
||
describe('以 driver 为源:回写时源字段保持、另两字段被反算覆盖', () => {
|
||
test('回车净价(CP)为源:净价保持,全价/收益率被反算覆盖', () => {
|
||
const state = { cleanPrice: 99.5, dirtyPrice: null, ytm: null };
|
||
SwapCalc.applyBondCalcResult(state, CALC, 'CP');
|
||
expect(state.cleanPrice).toBe(99.5); // 源:保持
|
||
expect(state.dirtyPrice).toBe(100); // 非源:被反算覆盖
|
||
expect(state.ytm).toBe(2.5);
|
||
});
|
||
|
||
test('回车全价(DP)为源:全价保持,净价/收益率被反算覆盖', () => {
|
||
const state = { cleanPrice: null, dirtyPrice: 100.5, ytm: null };
|
||
SwapCalc.applyBondCalcResult(state, CALC, 'DP');
|
||
expect(state.dirtyPrice).toBe(100.5); // 源:保持
|
||
expect(state.cleanPrice).toBe(99);
|
||
expect(state.ytm).toBe(2.5);
|
||
});
|
||
|
||
test('回车收益率(YD)为源:收益率保持,净价/全价被反算覆盖', () => {
|
||
const state = { cleanPrice: null, dirtyPrice: null, ytm: 2.6 };
|
||
SwapCalc.applyBondCalcResult(state, CALC, 'YD');
|
||
expect(state.ytm).toBe(2.6); // 源:保持
|
||
expect(state.cleanPrice).toBe(99);
|
||
expect(state.dirtyPrice).toBe(100);
|
||
});
|
||
});
|
||
|
||
describe('边界:计算器未返回的值不覆盖、无变化不写', () => {
|
||
test('回车净价(源) 且 计算器未返收益率:源与缺失值均不写', () => {
|
||
const state = { cleanPrice: null, dirtyPrice: null, ytm: 2.6 };
|
||
const partial = { cleanPrice: 99, dirtyPrice: 100, ytm: undefined };
|
||
SwapCalc.applyBondCalcResult(state, partial, 'CP');
|
||
expect(state.cleanPrice).toBe(null); // CP 源:即便 calc 返 99 也不写
|
||
expect(state.dirtyPrice).toBe(100); // 非源:被覆盖
|
||
expect(state.ytm).toBe(2.6); // 计算器没返收益率 → 保持用户值
|
||
});
|
||
|
||
test('派生值与当前值差异<EPS:不写(避免光标跳动)', () => {
|
||
const state = { cleanPrice: 99.5, dirtyPrice: 100.0, ytm: 2.5 };
|
||
const near = { cleanPrice: 99.5, dirtyPrice: 100.00001, ytm: 2.5 };
|
||
SwapCalc.applyBondCalcResult(state, near, 'CP');
|
||
expect(state.cleanPrice).toBe(99.5); // 源:不写
|
||
expect(state.dirtyPrice).toBe(100.0); // 差异 1e-5 < EPS → 不写
|
||
expect(state.ytm).toBe(2.5);
|
||
});
|
||
|
||
// 模拟 calcBondForItem:proxy 统一用【展示态】,模型字段是【存储态】,回写时 bondCalcPriceToStorage。
|
||
test('浮动腿 item 写回:仅非源字段被覆盖,且存储态转换正确', () => {
|
||
// 用户输入净价 99.5 → vue-number-input(percent:true) 存为存储态 0.995
|
||
const item = { PosiNetNoFeePrice: 0.995, PosiGrossPrice: 1.0, InitYtm: 0.026, bondDriverType: 'CP' };
|
||
const calc = { cleanPrice: 99, dirtyPrice: 100, ytm: 2.5 }; // 计算器返回展示态
|
||
const proxy = {
|
||
cleanPrice: SwapCalc.bondPriceToCalc(item.PosiNetNoFeePrice), // 0.995 → 99.5(源)
|
||
dirtyPrice: SwapCalc.bondPriceToCalc(item.PosiGrossPrice), // 1.0 → 100
|
||
ytm: SwapCalc.bondPriceToCalc(item.InitYtm) // 0.026 → 2.6
|
||
};
|
||
SwapCalc.applyBondCalcResult(proxy, calc, 'CP');
|
||
item.PosiNetNoFeePrice = SwapCalc.bondCalcPriceToStorage(proxy.cleanPrice);
|
||
item.PosiGrossPrice = SwapCalc.bondCalcPriceToStorage(proxy.dirtyPrice);
|
||
item.InitYtm = SwapCalc.bondCalcPriceToStorage(proxy.ytm);
|
||
expect(item.PosiNetNoFeePrice).toBe(0.995); // 源(净价):存储态保持 0.995(界面仍显示 99.5)
|
||
expect(item.PosiGrossPrice).toBe(1.0); // 全价:被反算覆盖,存储态 100/100=1.0
|
||
expect(item.InitYtm).toBe(0.025); // 收益率:被反算覆盖,存储态 2.5/100=0.025
|
||
});
|
||
|
||
// 核心回归:用户输入净价 100(存储态 1.0)时,绝不能因 proxy 残留存储态而再 ÷100 变成 0.01
|
||
test('核心回归:输入净价 100 不会被二次缩小为 1(存储态 0.01)', () => {
|
||
const item = { PosiNetNoFeePrice: 1.0, PosiGrossPrice: null, InitYtm: null, bondDriverType: 'CP' };
|
||
const calc = { cleanPrice: 100, dirtyPrice: 102.17928767123287, ytm: 4.860445236 };
|
||
const proxy = {
|
||
cleanPrice: SwapCalc.bondPriceToCalc(item.PosiNetNoFeePrice), // 1.0 → 100
|
||
dirtyPrice: SwapCalc.bondPriceToCalc(item.PosiGrossPrice),
|
||
ytm: SwapCalc.bondPriceToCalc(item.InitYtm)
|
||
};
|
||
SwapCalc.applyBondCalcResult(proxy, calc, 'CP');
|
||
item.PosiNetNoFeePrice = SwapCalc.bondCalcPriceToStorage(proxy.cleanPrice);
|
||
item.PosiGrossPrice = SwapCalc.bondCalcPriceToStorage(proxy.dirtyPrice);
|
||
item.InitYtm = SwapCalc.bondCalcPriceToStorage(proxy.ytm);
|
||
expect(item.PosiNetNoFeePrice).toBe(1.0); // 存储态仍是 1.0(界面显示 100)
|
||
expect(item.PosiGrossPrice).toBeCloseTo(1.0217928767123287, 9); // 102.179.../100
|
||
expect(item.InitYtm).toBeCloseTo(0.04860445236, 9); // 4.860.../100
|
||
});
|
||
|
||
// 多轮不同净价:95 / 105,确保存储态始终与界面输入一致,不会被二次缩小
|
||
test.each([
|
||
[95, 97.5, 5.123456],
|
||
[105, 107.3, 4.567890],
|
||
[100, 102.17928767123287, 4.860445236]
|
||
])('多轮净价校验:输入净价 %s 时,存储态保持 %s/100 且不会被二次缩小', (inputClean, calcDirty, calcYtm) => {
|
||
const item = { PosiNetNoFeePrice: inputClean / 100, PosiGrossPrice: null, InitYtm: null, bondDriverType: 'CP' };
|
||
const calc = { cleanPrice: inputClean, dirtyPrice: calcDirty, ytm: calcYtm };
|
||
const proxy = {
|
||
cleanPrice: SwapCalc.bondPriceToCalc(item.PosiNetNoFeePrice),
|
||
dirtyPrice: SwapCalc.bondPriceToCalc(item.PosiGrossPrice),
|
||
ytm: SwapCalc.bondPriceToCalc(item.InitYtm)
|
||
};
|
||
SwapCalc.applyBondCalcResult(proxy, calc, 'CP');
|
||
item.PosiNetNoFeePrice = SwapCalc.bondCalcPriceToStorage(proxy.cleanPrice);
|
||
item.PosiGrossPrice = SwapCalc.bondCalcPriceToStorage(proxy.dirtyPrice);
|
||
item.InitYtm = SwapCalc.bondCalcPriceToStorage(proxy.ytm);
|
||
expect(item.PosiNetNoFeePrice).toBe(inputClean / 100);
|
||
expect(item.PosiGrossPrice).toBeCloseTo(calcDirty / 100, 9);
|
||
expect(item.InitYtm).toBeCloseTo(calcYtm / 100, 9);
|
||
});
|
||
});
|
||
|
||
describe('错误反馈:getBondCalcErrorMessage(对齐 C# BondCalcHepler 的 errCode 守卫)', () => {
|
||
test('空响应 → 提示"无响应",且绝不回写', () => {
|
||
expect(SwapCalc.getBondCalcErrorMessage(null)).toBe("债券计算器无响应,已保留手工输入");
|
||
});
|
||
|
||
test('业务错误码 errCode!=0(债券不存在/信息不全/参数非法)→ 返回真实 errMsg', () => {
|
||
const resp = { errCode: 1, errMsg: "债券不存在或信息不全", dirtyPrice: 0, cleanPrice: 0, ytm: 0 };
|
||
expect(SwapCalc.getBondCalcErrorMessage(resp)).toBe("债券不存在或信息不全");
|
||
});
|
||
|
||
test('业务错误码 errCode!=0 但缺 errMsg → 返回兜底文案', () => {
|
||
const resp = { errCode: 2, dirtyPrice: 0, cleanPrice: 0, ytm: 0 };
|
||
expect(SwapCalc.getBondCalcErrorMessage(resp)).toBe("债券计算失败,请检查标的或参数");
|
||
});
|
||
|
||
test('正常成功响应(errCode=0) → 返回 null(应继续回写)', () => {
|
||
const resp = { errCode: 0, errMsg: null, dirtyPrice: 100, cleanPrice: 99, ytm: 2.5 };
|
||
expect(SwapCalc.getBondCalcErrorMessage(resp)).toBeNull();
|
||
});
|
||
|
||
test('无 errCode 字段的正常响应 → 返回 null(应继续回写)', () => {
|
||
const resp = { dirtyPrice: 100, cleanPrice: 99, ytm: 2.5 };
|
||
expect(SwapCalc.getBondCalcErrorMessage(resp)).toBeNull();
|
||
});
|
||
|
||
test('防御值域:成功(errCode=0)但净价为负 → 拦截提示、不回写(用户实测 180205.IB 场景)', () => {
|
||
const resp = { errCode: 0, dirtyPrice: 100, cleanPrice: -117.93, ytm: 6.37 };
|
||
const err = SwapCalc.getBondCalcErrorMessage(resp);
|
||
expect(err).not.toBeNull();
|
||
expect(err).toContain("净价");
|
||
});
|
||
|
||
test('防御值域:成功但收益率量级爆炸(378543) → 拦截提示、不回写', () => {
|
||
const resp = { errCode: 0, dirtyPrice: 100, cleanPrice: 97.82, ytm: 378543.526601942 };
|
||
const err = SwapCalc.getBondCalcErrorMessage(resp);
|
||
expect(err).not.toBeNull();
|
||
expect(err).toContain("收益率");
|
||
});
|
||
|
||
test('防御值域:净价超过 1000(量纲错误)→ 拦截', () => {
|
||
const resp = { errCode: 0, dirtyPrice: 100, cleanPrice: 9782, ytm: 6.37 };
|
||
expect(SwapCalc.getBondCalcErrorMessage(resp)).not.toBeNull();
|
||
});
|
||
|
||
test('防御值域:命中哨兵值 -999999 → 拦截', () => {
|
||
const resp = { errCode: 0, dirtyPrice: 100, cleanPrice: 99, ytm: -999999 };
|
||
expect(SwapCalc.getBondCalcErrorMessage(resp)).not.toBeNull();
|
||
});
|
||
|
||
test('防御值域:合理范围内的正常值(净价97.82/收益率6.37) → 放行返回 null', () => {
|
||
const resp = { errCode: 0, dirtyPrice: 100, cleanPrice: 97.82, ytm: 6.37 };
|
||
expect(SwapCalc.getBondCalcErrorMessage(resp)).toBeNull();
|
||
});
|
||
|
||
test('防御值域:负收益率但量级合理(-2.5%) → 放行(允许负利率债券)', () => {
|
||
const resp = { errCode: 0, dirtyPrice: 100, cleanPrice: 99, ytm: -2.5 };
|
||
expect(SwapCalc.getBondCalcErrorMessage(resp)).toBeNull();
|
||
});
|
||
|
||
test('端到端:业务错误时保留源字段、清空另两字段与标识(交互约定2 失败)', () => {
|
||
const item = { PosiNetNoFeePrice: 99.5, PosiGrossPrice: 100.0, InitYtm: 2.6, bondDriverType: 'CP' };
|
||
const respObj = { errCode: 1, errMsg: "债券不存在", dirtyPrice: 0, cleanPrice: 0, ytm: 0 };
|
||
const err = SwapCalc.getBondCalcErrorMessage(respObj);
|
||
expect(err).toBe("债券不存在"); // 有错 → 调用方会 main.message(err) 并调 applyBondCalcFailure
|
||
if (err) SwapCalc.applyBondCalcFailure(item, 'CP');
|
||
expect(item.PosiNetNoFeePrice).toBe(99.5); // 源:保留用户输入
|
||
expect(item.PosiGrossPrice).toBeNull(); // 非源:清空
|
||
expect(item.InitYtm).toBeNull();
|
||
expect(item.bondDriverType).toBeNull(); // 三字段标识全清
|
||
expect(item.bondAuto).toEqual({ CP: false, DP: false, YD: false });
|
||
expect(item.bondRev).toEqual({ CP: false, DP: false, YD: false });
|
||
});
|
||
});
|
||
|
||
// ============================================================================
|
||
// 交互约定1/2/3 状态机:applyBondCalcSuccess / applyBondCalcFailure / applyBondManualEdit
|
||
// ============================================================================
|
||
describe('交互约定2/3 状态机:applyBondCalcSuccess / Failure / ManualEdit', () => {
|
||
test('applyBondCalcSuccess(CP):源=CP,AUTO={DP,YD},REV 清空', () => {
|
||
const s = { bondDriverType: null, bondAuto: { CP: false, DP: false, YD: false }, bondRev: { CP: true, DP: false, YD: false } };
|
||
SwapCalc.applyBondCalcSuccess(s, 'CP');
|
||
expect(s.bondDriverType).toBe('CP');
|
||
expect(s.bondAuto).toEqual({ CP: false, DP: true, YD: true });
|
||
expect(s.bondRev).toEqual({ CP: false, DP: false, YD: false });
|
||
});
|
||
|
||
test('applyBondCalcSuccess(DP):源=DP,AUTO={CP,YD}', () => {
|
||
const s = {};
|
||
SwapCalc.applyBondCalcSuccess(s, 'DP');
|
||
expect(s.bondDriverType).toBe('DP');
|
||
expect(s.bondAuto).toEqual({ CP: true, DP: false, YD: true });
|
||
});
|
||
|
||
test('applyBondCalcFailure(CP):源值保留,另两字段清空,标识全清', () => {
|
||
const s = { PosiNetNoFeePrice: 99.5, PosiGrossPrice: 100, InitYtm: 2.6,
|
||
bondDriverType: 'CP', bondAuto: { CP: false, DP: true, YD: true }, bondRev: { CP: false, DP: false, YD: false } };
|
||
SwapCalc.applyBondCalcFailure(s, 'CP');
|
||
expect(s.PosiNetNoFeePrice).toBe(99.5); // 源:保留
|
||
expect(s.PosiGrossPrice).toBeNull(); // 非源:清空
|
||
expect(s.InitYtm).toBeNull();
|
||
expect(s.bondDriverType).toBeNull();
|
||
expect(s.bondAuto).toEqual({ CP: false, DP: false, YD: false });
|
||
expect(s.bondRev).toEqual({ CP: false, DP: false, YD: false });
|
||
});
|
||
|
||
test('applyBondManualEdit(DP):清 源/AUTO,仅 DP 标 REV(交互约定3)', () => {
|
||
const s = { bondDriverType: 'CP', bondAuto: { CP: false, DP: true, YD: true }, bondRev: { CP: false, DP: false, YD: false } };
|
||
SwapCalc.applyBondManualEdit(s, 'DP');
|
||
expect(s.bondDriverType).toBeNull();
|
||
expect(s.bondAuto).toEqual({ CP: false, DP: false, YD: false });
|
||
expect(s.bondRev).toEqual({ CP: false, DP: true, YD: false }); // 仅本字段 REV
|
||
});
|
||
|
||
test('全价回车后净价再回车:净价成新源、全价降级为 AUTO(最后回车者恒为源)', () => {
|
||
const s = { PosiNetNoFeePrice: null, PosiGrossPrice: null, InitYtm: null,
|
||
bondDriverType: null, bondAuto: { CP: false, DP: false, YD: false }, bondRev: { CP: false, DP: false, YD: false } };
|
||
SwapCalc.applyBondCalcSuccess(s, 'DP'); // 全价回车
|
||
expect(s.bondDriverType).toBe('DP');
|
||
expect(s.bondAuto).toEqual({ CP: true, DP: false, YD: true });
|
||
SwapCalc.applyBondCalcSuccess(s, 'CP'); // 净价再回车
|
||
expect(s.bondDriverType).toBe('CP');
|
||
expect(s.bondAuto).toEqual({ CP: false, DP: true, YD: true }); // 全价从源降级为 AUTO
|
||
});
|
||
});
|
||
|
||
describe('单位换算边界(前端↔债券计算器 存储态小数 ↔ 展示态百分比)', () => {
|
||
test('bondPriceToCalc:存储态 0.995 → 发送计算器的展示态 99.5', () => {
|
||
expect(SwapCalc.bondPriceToCalc(0.995)).toBeCloseTo(99.5, 6);
|
||
expect(SwapCalc.bondPriceToCalc(1.0)).toBe(100); // 用户敲全价100 → percent:true 收成 1.0 → 发 100
|
||
});
|
||
|
||
test('bondCalcPriceToStorage:展示态 97.82 → 落库存储态 0.9782', () => {
|
||
expect(SwapCalc.bondCalcPriceToStorage(97.82)).toBeCloseTo(0.9782, 6);
|
||
expect(SwapCalc.bondCalcPriceToStorage(6.37)).toBeCloseTo(0.0637, 6);
|
||
});
|
||
|
||
// 复刻用户实测的 -117.93 / 378543 离谱值根因,验证修复后不再出现:
|
||
// 旧:模型 1.0(用户敲100) 漏×100 直接发 1.0 → 计算器当 1% of par → 返回 cleanPrice=-1.1793, ytm=37.8543
|
||
// → 漏÷100 原样写回 -1.1793 → percent:true 显示 ×100 → -117.93 / 378543
|
||
// 新:发前×100、回写÷100 → 模型 0.9782 / 1.0 / 0.0637 → 显示 97.82 / 100 / 6.37
|
||
test('端到端:用户敲全价100 → 修复后模型与显示均为合理值(不再 -117.93/378543)', () => {
|
||
const modelGross = 1.0; // percent:true 把界面 100 收成 1.0(存储态)
|
||
const sentToCalc = SwapCalc.bondPriceToCalc(modelGross);
|
||
expect(sentToCalc).toBe(100); // 必须 ×100,不是 1.0
|
||
const calcResp = { errCode: 0, cleanPrice: 97.82, dirtyPrice: 100, ytm: 6.37 };
|
||
const modelNet = SwapCalc.bondCalcPriceToStorage(calcResp.cleanPrice);
|
||
const modelGrossBack = SwapCalc.bondCalcPriceToStorage(calcResp.dirtyPrice);
|
||
const modelYtm = SwapCalc.bondCalcPriceToStorage(calcResp.ytm);
|
||
expect(modelNet).toBeCloseTo(0.9782, 6);
|
||
expect(modelGrossBack).toBe(1.0);
|
||
expect(modelYtm).toBeCloseTo(0.0637, 6);
|
||
// percent:true 显示时再 ×100:97.82 / 100 / 6.37,与计算器一致,无离谱值
|
||
expect(modelNet * 100).toBeCloseTo(97.82, 4);
|
||
expect(modelYtm * 100).toBeCloseTo(6.37, 4);
|
||
});
|
||
});
|
||
|
||
describe('isBondPriceValueKey:只有真正改值的按键才标REV(防止"没改值却REV")', () => {
|
||
const K = (keyCode, extra) => Object.assign({ which: keyCode, keyCode: keyCode }, extra || {});
|
||
test('数字键(主键盘/小键盘) → 改值', () => {
|
||
expect(SwapCalc.isBondPriceValueKey(K(49))).toBe(true); // 主键盘 1
|
||
expect(SwapCalc.isBondPriceValueKey(K(97))).toBe(true); // 小键盘 1
|
||
});
|
||
test('小数点/减号(主键盘/小键盘) → 改值', () => {
|
||
expect(SwapCalc.isBondPriceValueKey(K(190))).toBe(true); // 主键盘 .
|
||
expect(SwapCalc.isBondPriceValueKey(K(110))).toBe(true); // 小键盘 .
|
||
expect(SwapCalc.isBondPriceValueKey(K(189))).toBe(true); // 主键盘 -
|
||
expect(SwapCalc.isBondPriceValueKey(K(109))).toBe(true); // 小键盘 -
|
||
});
|
||
test('Backspace / Delete → 改值', () => {
|
||
expect(SwapCalc.isBondPriceValueKey(K(8))).toBe(true);
|
||
expect(SwapCalc.isBondPriceValueKey(K(46))).toBe(true);
|
||
});
|
||
test('回车 → 不改标识(由 enter 事件处理)', () => {
|
||
expect(SwapCalc.isBondPriceValueKey(K(13))).toBe(false);
|
||
expect(SwapCalc.isBondPriceValueKey(K(108))).toBe(false);
|
||
});
|
||
test('方向键 ←↑→↓ → 不改标识(修复:此前漏掉 ↑↓ 会误标REV)', () => {
|
||
expect(SwapCalc.isBondPriceValueKey(K(37))).toBe(false);
|
||
expect(SwapCalc.isBondPriceValueKey(K(38))).toBe(false);
|
||
expect(SwapCalc.isBondPriceValueKey(K(39))).toBe(false);
|
||
expect(SwapCalc.isBondPriceValueKey(K(40))).toBe(false);
|
||
});
|
||
test('Home/End/Tab/Insert/PageUp/PageDown → 不改标识(修复:此前漏掉 Insert/PageUp/PageDown)', () => {
|
||
expect(SwapCalc.isBondPriceValueKey(K(35))).toBe(false);
|
||
expect(SwapCalc.isBondPriceValueKey(K(36))).toBe(false);
|
||
expect(SwapCalc.isBondPriceValueKey(K(9))).toBe(false);
|
||
expect(SwapCalc.isBondPriceValueKey(K(45))).toBe(false);
|
||
expect(SwapCalc.isBondPriceValueKey(K(33))).toBe(false);
|
||
expect(SwapCalc.isBondPriceValueKey(K(34))).toBe(false);
|
||
});
|
||
test('F1-F12 → 不改标识(修复:此前漏掉除 F5 外的功能键)', () => {
|
||
[112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123].forEach(function (kc) {
|
||
expect(SwapCalc.isBondPriceValueKey(K(kc))).toBe(false);
|
||
});
|
||
});
|
||
test('纯 Shift / Ctrl / Alt / Meta 组合键 → 不改标识', () => {
|
||
expect(SwapCalc.isBondPriceValueKey(K(16))).toBe(false); // 纯 Shift
|
||
expect(SwapCalc.isBondPriceValueKey(K(65, { ctrlKey: true }))).toBe(false); // Ctrl+A
|
||
expect(SwapCalc.isBondPriceValueKey(K(86, { ctrlKey: true }))).toBe(false); // Ctrl+V
|
||
expect(SwapCalc.isBondPriceValueKey(K(67, { altKey: true }))).toBe(false);
|
||
expect(SwapCalc.isBondPriceValueKey(K(67, { metaKey: true }))).toBe(false);
|
||
});
|
||
test('无事件对象 → 安全返回 false', () => {
|
||
expect(SwapCalc.isBondPriceValueKey(null)).toBe(false);
|
||
expect(SwapCalc.isBondPriceValueKey(undefined)).toBe(false);
|
||
});
|
||
});
|