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.
53 lines
4 KiB
JavaScript
53 lines
4 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import vm from 'node:vm';
|
|
import {readFile} from 'node:fs/promises';
|
|
const load=name=>readFile(new URL('../gaia/apps/settings/js/modules/'+name+'.js',import.meta.url),'utf8');
|
|
const serviceSource=await load('dialog_service');
|
|
const managerSource=await load('dialog_manager');
|
|
const defer=()=>{let resolve,reject;const promise=new Promise((a,b)=>{resolve=a;reject=b;});return {promise,resolve,reject};};
|
|
const settle=()=>new Promise(resolve=>setImmediate(resolve));
|
|
function serviceFixture() {
|
|
let service;const opens=[],closes=[];let rejectClose=false;
|
|
const panel=Object.assign(new EventTarget(),{classList:{remove(){}},hidden:false});
|
|
const factory=()=>({panel,cleanup(){}});
|
|
const manager={open(dialog,options){const d=defer();opens.push({options,...d});return d.promise;},close(dialog,type){closes.push(type);return rejectClose?Promise.reject(Error('invalid')):Promise.resolve('value');}};
|
|
vm.runInNewContext(serviceSource,{Promise,CustomEvent,document:{getElementById:()=>panel,querySelector:()=>null},define:fn=>{service=fn(name=>name==='settings'?{currentPanel:'#root'}:name==='modules/defer'?defer:name==='modules/dialog_manager'?manager:factory);}});
|
|
return {service,opens,closes,panel,reject:value=>{rejectClose=value;}};
|
|
}
|
|
test('early cancel waits for opening and queued reopen runs after closure',async()=>{
|
|
const f=serviceFixture();const first=f.service.show('one');const second=f.service.show('two');
|
|
f.opens[0].options.onWrapCancel();f.opens[0].options.onWrapCancel();
|
|
await settle();assert.equal(f.closes.length,0);assert.equal(f.opens.length,1);
|
|
f.opens[0].resolve();assert.equal((await first).type,'cancel');await settle();
|
|
assert.equal(f.closes.length,1);assert.equal(f.opens.length,2);
|
|
f.opens[1].resolve();f.opens[1].options.onWrapCancel();await second;assert.equal(f.service._navigating,false);
|
|
});
|
|
test('failed validation preserves dialog and permits a later submit',async()=>{
|
|
const f=serviceFixture();let failures=0;f.panel.addEventListener('dialogvalidationfailed',()=>failures++);
|
|
const shown=f.service.show('one');f.opens[0].resolve();await settle();
|
|
f.reject(true);f.opens[0].options.onWrapSubmit();await settle();
|
|
assert.equal(failures,1);assert.equal(f.service._navigating,true);
|
|
f.reject(false);f.opens[0].options.onWrapSubmit();assert.equal((await shown).type,'submit');assert.equal(f.service._navigating,false);
|
|
});
|
|
function transitionFixture(duration='0.4s') {
|
|
let manager,next=0;const timers=new Map();const classes=new Set();
|
|
const panel=Object.assign(new EventTarget(),{hidden:true,classList:{toggle(name,on){on?classes.add(name):classes.delete(name);}}});
|
|
vm.runInNewContext(managerSource,{Promise,document:{querySelector:()=>({})},window:{getComputedStyle:()=>({transitionDuration:duration,transitionDelay:'0s'})},
|
|
setTimeout:(fn,ms)=>{timers.set(++next,{fn,ms});return next;},clearTimeout:id=>timers.delete(id),define:fn=>{manager=fn(()=>({}));}});
|
|
return {manager,panel,classes,timers,step(){const [id,timer]=timers.entries().next().value;timers.delete(id);timer.fn();return timer.ms;}};
|
|
}
|
|
test('missing visibility transition and disabled animations both complete',async()=>{
|
|
for(const duration of ['0.4s','0s']) {
|
|
const f=transitionFixture(duration);const opened=f.manager._transit('open',{panel:f.panel},{});
|
|
assert.equal(f.panel.hidden,false);assert.equal(f.step(),150);assert(f.classes.has('current'));
|
|
assert.equal(f.step(),duration==='0s'?100:500);await opened;
|
|
const closed=f.manager._transit('close',{panel:f.panel},{});f.step();f.step();await closed;
|
|
assert.equal(f.panel.hidden,true);assert.equal(f.timers.size,0);
|
|
}
|
|
});
|
|
test('cancelled visibility transition resolves once and removes fallback',async()=>{
|
|
const f=transitionFixture();const p=f.manager._transit('close',{panel:f.panel},{});f.step();
|
|
const e=new Event('transitioncancel');Object.defineProperty(e,'propertyName',{value:'visibility'});f.panel.dispatchEvent(e);await p;
|
|
assert.equal(f.panel.hidden,true);assert.equal(f.timers.size,0);
|
|
});
|