26 lines
2.8 KiB
JavaScript
26 lines
2.8 KiB
JavaScript
/**
|
||
* 键盘输入路由的纯逻辑回归。通过上下文标记和简化closest/composedPath对象,测试输入优先级而不创建真实浏览器DOM。
|
||
* 回调计数用于确认被文本/IME/浮层认领的事件不会继续触发文档快捷键。
|
||
*/
|
||
import {test} from 'node:test';import assert from 'node:assert/strict';
|
||
import {routeCanvasKey,canvasKeyContext} from '../../src/lib/canvas-bindings/input-router.ts';
|
||
// 逐个设置textEditing/composing/overlay/defaultPrevented,Backspace必须在动作层之前退出,gesture与shortcut调用数都为0。
|
||
test('text, IME, overlays and already consumed input never execute document shortcuts',()=>{for(const flags of [{textEditing:true},{composing:true},{overlay:true},{defaultPrevented:true}]){let calls=0;const result=routeCanvasKey({key:'Backspace',...flags},{gesture:()=>{calls++;return true;},shortcut:()=>{calls++;return true;}});assert.equal(calls,0);assert.ok(['text','overlay','unclaimed'].includes(result));}});
|
||
// 依次让gesture/tool/shortcut成为首个赢家,只运行到认领层;无处理器返回unclaimed。
|
||
test('gesture then tool then shortcut claim once; declining handlers fall through',()=>{for(const winner of ['gesture','tool','shortcut']){const calls=[];const handlers=Object.fromEntries(['gesture','tool','shortcut'].map(layer=>[layer,()=>{calls.push(layer);return layer===winner;}]));assert.equal(routeCanvasKey({key:'Escape'},handlers),winner);assert.deepEqual(calls,['gesture','tool','shortcut'].slice(0,['gesture','tool','shortcut'].indexOf(winner)+1));}assert.equal(routeCanvasKey({key:'x'},{}),'unclaimed');});
|
||
|
||
// 用伪closest模拟select和注册浮层,并传空/无效路径项,确认上下文识别不依赖全局Element。
|
||
test('DOM input context recognizes registered overlays and native selects without browser globals',()=>{
|
||
const select={closest:selector=>selector.split(',').includes('select')?select:null};
|
||
const overlay={closest:selector=>selector.includes('[data-canvas-overlay-view]')?overlay:null};
|
||
assert.deepEqual(canvasKeyContext({target:select}),{textEditing:true,overlay:false});
|
||
assert.deepEqual(canvasKeyContext({target:overlay}),{textEditing:false,overlay:true});
|
||
assert.deepEqual(canvasKeyContext({target:null,composedPath:()=>[null,{},42]}),{textEditing:false,overlay:false});
|
||
});
|
||
// 事件target已被Shadow DOM重定向到host,composedPath仍含textarea;Escape必须留给内部文本而非取消画布手势。
|
||
test('retargeted shadow events retain their inner text owner and never reach a gesture',()=>{
|
||
const input={closest:selector=>selector.split(',').includes('textarea')?input:null},host={closest:()=>null};
|
||
const context=canvasKeyContext({target:host,composedPath:()=>[input,host]});let canceled=false;
|
||
assert.equal(routeCanvasKey({key:'Escape',...context},{gesture:()=>{canceled=true;return true;}}),'text');assert.equal(canceled,false);
|
||
});
|