- swapPricePrecisionHelper: onKeydown 回车时无条件派发 input+enter, 不再依赖仅值变化才触发的原生 change;onPaste 粘贴即派发 input 标 REV; onChange 不再派发 enter(改由 onKeydown 唯一派发,避免重复计算) - 新增 fe-tests/swapPriceInput.component.test.js 回归守卫(6 用例, 回退旧版即变红,锁定该 QA 高优 Bug 修复) - bundle/bundleV2: otcDebug 调试开关改为 localStorage 持久化 + URL ?otcdebug=1 触发,便于排查价格计算链路
101 lines
5.2 KiB
JavaScript
101 lines
5.2 KiB
JavaScript
/**
|
||
* swapPriceInput.component.test.js — vue-swap-price-input 事件派发回归守卫
|
||
* ============================================================================
|
||
* 目的:锁定债券三字段输入组件「粘贴 / 重输原值 不派发 input/enter」的 bug。
|
||
* 症状:粘贴或重输原值后,点空白(标REV/清其他标识)与回车(触发计算)均无反应。
|
||
* 根因:组件把 input/enter 全部锁在原生 change 事件里,而 change 仅在「值相对聚焦时
|
||
* 变动」才触发;onPaste 甚至完全不派发任何事件。
|
||
*
|
||
* 做法:直接实例化组件 options 对象(createVueInputComponent),用 mock this + $emit 间谍
|
||
* 驱动 onPaste/onKeydown/onChange,断言事件派发。不依赖 Vue 运行时挂载、不改动源文件。
|
||
*
|
||
* 运行:cd YLErpWeb/fe-tests && npx jest swapPriceInput.component
|
||
* 设 SWAP_PRICE_HELPER 指向其它版本文件可做对照(如改之前的旧代码应使用例变红)。
|
||
*/
|
||
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
function loadComponent() {
|
||
const rel = process.env.SWAP_PRICE_HELPER
|
||
|| '../wwwroot/Scripts/app/swaptrade/swapPricePrecisionHelper.js';
|
||
const file = path.resolve(__dirname, rel);
|
||
const code = fs.readFileSync(file, 'utf8');
|
||
// swapPricePrecisionHelper.js: var swapPricePrecision = (function(global){...})(window);
|
||
// 在受控作用域求值并取出 createVueInputComponent(IIFE 不挂 window,故用 new Function 取返回值)。
|
||
const factory = new Function('window', 'global', 'console', code + '\n;return swapPricePrecision;');
|
||
const sandbox = {};
|
||
const swapPricePrecision = factory(sandbox, sandbox, console);
|
||
return swapPricePrecision.createVueInputComponent();
|
||
}
|
||
|
||
function makeCtx(component, opts) {
|
||
opts = opts || {};
|
||
const emitted = [];
|
||
const ctx = {
|
||
text: opts.text != null ? String(opts.text) : '',
|
||
enterPressed: false,
|
||
formatSnapshot: '{}',
|
||
format: opts.format || { integerDigits: 2, precision: 4, percent: true },
|
||
$emit: function (name, payload) { emitted.push({ name: name, payload: payload }); },
|
||
};
|
||
Object.keys(component.methods).forEach(function (m) {
|
||
ctx[m] = component.methods[m].bind(ctx);
|
||
});
|
||
return { ctx: ctx, emitted: emitted };
|
||
}
|
||
|
||
describe('vue-swap-price-input 事件派发(粘贴 / 重输原值场景)', () => {
|
||
const component = loadComponent();
|
||
|
||
test('粘贴原值应派发 input(点空白标REV / 清其他标识)', () => {
|
||
const { ctx, emitted } = makeCtx(component, { text: '3.5', format: { integerDigits: 2, precision: 4, percent: true } });
|
||
const evt = { preventDefault() {}, clipboardData: { getData: () => '3.5' }, target: { value: '' } };
|
||
ctx.onPaste(evt);
|
||
const inputs = emitted.filter(e => e.name === 'input');
|
||
expect(inputs.length).toBe(1);
|
||
expect(inputs[0].payload).toBe('0.035'); // percent:true → ÷100
|
||
});
|
||
|
||
test('回车(原值未变)应派发 enter(触发自动计算)', () => {
|
||
const { ctx, emitted } = makeCtx(component, { text: '3.5', format: { integerDigits: 2, precision: 4, percent: true } });
|
||
const evt = { which: 13, keyCode: 13, key: 'Enter', target: { blur() {}, value: '3.5' } };
|
||
ctx.onKeydown(evt);
|
||
const enters = emitted.filter(e => e.name === 'enter');
|
||
expect(enters.length).toBe(1);
|
||
expect(enters[0].payload).toBe('0.035');
|
||
});
|
||
|
||
test('回车(值已改)应派发 input+enter 各一次,且不重复派发 enter', () => {
|
||
const { ctx, emitted } = makeCtx(component, { text: '3.6', format: { percent: true } });
|
||
const evt = { which: 13, keyCode: 13, key: 'Enter', target: { blur() {}, value: '3.6' } };
|
||
ctx.onKeydown(evt);
|
||
expect(emitted.filter(e => e.name === 'input').length).toBe(1);
|
||
expect(emitted.filter(e => e.name === 'enter').length).toBe(1);
|
||
});
|
||
|
||
test('非 Enter 按键不应派发 enter / input', () => {
|
||
const { ctx, emitted } = makeCtx(component, { text: '3.5', format: { percent: true } });
|
||
const evt = { which: 65, keyCode: 65, key: 'a', target: { blur() {} } };
|
||
ctx.onKeydown(evt);
|
||
expect(emitted.filter(e => e.name === 'enter').length).toBe(0);
|
||
expect(emitted.filter(e => e.name === 'input').length).toBe(0);
|
||
});
|
||
|
||
test('失焦(onChange)即使值未变也应派发 input(点空白标REV),且不再派发 enter', () => {
|
||
const { ctx, emitted } = makeCtx(component, { text: '3.5', format: { percent: true } });
|
||
const evt = { target: { value: '3.5' } };
|
||
ctx.onChange(evt);
|
||
expect(emitted.filter(e => e.name === 'input').length).toBe(1);
|
||
expect(emitted.filter(e => e.name === 'enter').length).toBe(0);
|
||
});
|
||
|
||
test('粘贴但格式为 percent:false 也应派发 input(原值透传)', () => {
|
||
const { ctx, emitted } = makeCtx(component, { text: '3.5', format: { integerDigits: 2, precision: 4, percent: false } });
|
||
const evt = { preventDefault() {}, clipboardData: { getData: () => '3.5' }, target: { value: '' } };
|
||
ctx.onPaste(evt);
|
||
expect(emitted.filter(e => e.name === 'input').length).toBe(1);
|
||
expect(emitted.filter(e => e.name === 'input')[0].payload).toBe('3.5');
|
||
});
|
||
});
|