Merge branch 'refs/heads/glms/feature/1.4.2' into glms/feature/dotnumber

This commit is contained in:
张名锐
2026-07-31 09:19:09 +08:00
40 changed files with 1899 additions and 287 deletions
@@ -0,0 +1,100 @@
/**
* 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);
// 在受控作用域求值并取出 createVueInputComponentIIFE 不挂 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');
});
});