70 lines
3.8 KiB
JavaScript
70 lines
3.8 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import {Input} from '../services/input.mjs';
|
|
function setup() {
|
|
const events=[];
|
|
const input=new Input({identity:actor=>actor.app,broadcast:(...args)=>events.push(args)});
|
|
const keyboard={app:{id:'keyboard'}}, field={app:{id:'sms'}};
|
|
return {input,events,keyboard,field};
|
|
}
|
|
test('keyboard cannot edit a stale field or another app issue keyboard commands',async()=>{
|
|
const {input,keyboard,field}=setup();
|
|
input.changed(field,{text:'',id:0});
|
|
await assert.rejects(input.request(field,{operation:'state'}),/PERMISSION_DENIED/);
|
|
await assert.rejects(input.request(keyboard,{operation:'key',id:0}),/INPUT_CONTEXT_EXPIRED/);
|
|
assert.equal((await input.request(keyboard,{operation:'state'})).id,1);
|
|
});
|
|
for(const action of ['hide','change']) test(`pending edit cannot update state after ${action}`,async()=>{
|
|
const {input,keyboard,field}=setup();let reply;
|
|
field.sendQuery=()=>new Promise(resolve=>{reply=resolve;});
|
|
input.changed(field,{text:'first'});
|
|
const pending=input.request(keyboard,{operation:'key',id:1});
|
|
if(action==='hide') input.hide(); else input.changed(field,{text:'second'});
|
|
reply({text:'first edited'});
|
|
await assert.rejects(pending,/INPUT_CONTEXT_EXPIRED/);
|
|
assert.equal(input.focus?.state.text,action==='hide'?undefined:'second');
|
|
});
|
|
test('current edit updates the focused field; keyboard focus does not replace it',async()=>{
|
|
const {input,keyboard,field}=setup();
|
|
field.sendQuery=async()=>({text:'123',selectionStart:3,selectionEnd:3});
|
|
input.changed(field,{text:''});input.changed(keyboard,{text:'unrelated'});
|
|
assert.equal((await input.request(keyboard,{operation:'key',id:1})).text,'123');
|
|
});
|
|
|
|
test('a composer button retains input and other buttons dismiss only after click',async()=>{
|
|
const {readFile}=await import('node:fs/promises');
|
|
const {runInNewContext}=await import('node:vm');
|
|
const source=await readFile(new URL('../host/actors/VulpesChild.sys.mjs',import.meta.url),'utf8');
|
|
const events=[];
|
|
const Actor=runInNewContext(source.replace(/^import .*;\n/gm, '').replace('export class','class')+';VulpesChild',{
|
|
JSWindowActorChild:class{},Services:{cpmm:{sharedData:{get:()=>true}}}
|
|
});
|
|
const actor=new Actor();actor.sendAsyncMessage=(...args)=>events.push(args);
|
|
const button={localName:'button',closest:()=>null};
|
|
actor.handleEvent({type:'pointerdown',target:button});assert.equal(events.length,0);
|
|
actor.handleEvent({type:'click',target:button});assert.equal(events.length,1);assert.equal(events[0][1],null);
|
|
button.closest=()=>button;
|
|
actor.handleEvent({type:'pointerdown',target:button});actor.handleEvent({type:'click',target:button});
|
|
assert.equal(events.length,1,'send keeps the composer focused');
|
|
});
|
|
|
|
test('physical keypad suppresses the virtual IME without affecting touch hosts',()=>{
|
|
const {input,events,field}=setup();input.runtime.hardwareKeyboard=true;
|
|
input.changed(field,{text:'private text'});
|
|
assert.equal(input.focus,null);assert.equal(events.length,0);
|
|
input.runtime.hardwareKeyboard=false;input.changed(field,{text:''});
|
|
assert.equal(events[0][0],'input-focus');
|
|
});
|
|
|
|
test('an external input uses its visible owner, without gaining keyboard permissions',async()=>{
|
|
const {input,keyboard}=setup();const web={};let visible=true;
|
|
input.runtime.inputIdentity=actor=>actor===web && visible?{id:'marketplace'}:actor.app;
|
|
web.sendQuery=async()=>({text:'test'});
|
|
input.changed(web,{text:''});
|
|
assert.equal((await input.request(keyboard,{operation:'key',id:1})).text,'test');
|
|
await assert.rejects(input.request(web,{operation:'state'}),/PERMISSION_DENIED/);
|
|
visible=false;
|
|
await assert.rejects(input.request(keyboard,{operation:'key',id:1}),/INPUT_CONTEXT_EXPIRED/);
|
|
assert.equal(input.focus,null);
|
|
input.changed(web,{text:'hidden'});assert.equal(input.focus,null);
|
|
});
|