Restore Gaia settings and media workflows, extend native network adapters, and align the APK payload. Generate French/English changelogs and website entries from the simplified JSON source. Validation: shared JS and Tundra unit tests; desktop workflows and persistence; signed APK payload parity; Sargo RAM and installed startup with boot readback. Android runtime qualification is recorded separately.
49 lines
2.7 KiB
JavaScript
49 lines
2.7 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/, '').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');
|
|
});
|