Release Preview 20 shared desktop, Android and Sargo updates

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.
This commit is contained in:
OverSu 2026-10-13 00:19:45 +02:00
commit 1311bed5e9
170 changed files with 19698 additions and 2495 deletions

71
tools/test-hotspot-ui.py Normal file
View file

@ -0,0 +1,71 @@
#!/usr/bin/env python3
"""Exercise real Gaia hotspot dialogs in the isolated PC profile with a fake radio.
This tests UI behavior only. Native hotspot qualification is a separate test.
"""
import importlib.util,json,time
from pathlib import Path
from marionette_client import Client
spec=importlib.util.spec_from_file_location('probe',Path(__file__).with_name('probe-desktop.py'))
probe=importlib.util.module_from_spec(spec);spec.loader.exec_module(probe)
with Client(2857) as client:
probe.register(client)
def ev(code):
result=probe.evaluate(client,'settings',code)
if 'error' in result:raise AssertionError(result['error'])
return result.get('value')
def wait(code):
for _ in range(100):
if ev(code):return
time.sleep(.1)
state=ev("const d=await new Promise(r=>require(['modules/dialog_service'],r));const p=document.getElementById('hotspot-wifiSettings');return {navigating:d._navigating,pending:d._pendingRequests.length,hidden:p.hidden,current:Array.from(document.querySelectorAll('section.current'),p=>p.id),transition:getComputedStyle(p).transition,visibility:getComputedStyle(p).visibility,notes:Array.from(p.querySelectorAll('[role=status]'),p=>p.textContent)};")
raise AssertionError((code,state))
for _ in range(100):
ready=probe.evaluate(client,'system','return document.body?.getAttribute("ready-state")==="fullyLoaded";')
if ready.get('value'):break
time.sleep(.2)
probe.evaluate(client,'system','ScreenManager.turnScreenOn(true);if(Service.query("locked"))await Service.request("unlock",{forcibly:true});await VulpesCompat.call("apps.launch",{manifestURL:"http://settings.localhost:8765/manifest.webapp"});return true;')
wait('return !!document.querySelector("#root.current") && typeof require==="function";')
ev('location.reload();return true;');time.sleep(.7)
wait('return !!document.querySelector("#root.current") && typeof require==="function";')
ev('''window.__hotspotOriginalCall=VulpesCompat.call;
window.__hotspotFake={enabled:false,configured:false,ssid:'Vulpes OS',security:'wpa2-psk',password:''};
window.__hotspotWrites=[];window.__hotspotFail=false;
VulpesCompat.call=async(method,p)=>{
if(method!=='platform.wifi')return __hotspotOriginalCall(method,p);
if(p.operation==='hotspotSave'){
if(__hotspotFail)throw Error('HOTSPOT_SAVE_FAILED');
__hotspotWrites.push('save');Object.assign(__hotspotFake,{configured:true,ssid:p.ssid,security:p.security,password:p.password});
} else if(p.operation==='hotspotPower'){__hotspotWrites.push(p.enabled?'on':'off');__hotspotFake.enabled=p.enabled;}
return {...__hotspotFake};
};
window.VulpesNativeWifi=true;
const script=document.createElement('script');script.src='/_vulpes/gaia-wifi.js';document.head.append(script);return true;''')
try:
wait('return !!window.VulpesWifiSettings;')
ev("require(['modules/settings_service'],s=>s.navigate('hotspot'));return true;")
wait("return !!document.querySelector('#hotspot.current #tethering-wifi-enabled:not([disabled])');")
def open_dialog():
ev("document.querySelector('#hotspot-settings-section a').click();return true;")
wait("return document.querySelector('#hotspot-wifiSettings').classList.contains('current');")
def closed():
wait("const d=await new Promise(r=>require(['modules/dialog_service'],r));return !d._navigating && !document.querySelector('#hotspot-wifiSettings').classList.contains('current') && !document.querySelector('#tethering-wifi-enabled').disabled;")
open_dialog()
ev("document.querySelector('#hotspot-wifiSettings button[type=reset]').click();return true;");closed()
open_dialog()
ev("const p=document.querySelector('#hotspot-wifiSettings');p.querySelector('input[type=text]').value='UI test';p.querySelector('input[name=password]').value='short';p.querySelector('button[type=submit]').click();return true;")
time.sleep(.3)
assert ev("return document.querySelector('#hotspot-wifiSettings').classList.contains('current') && __hotspotWrites.length===0;")
ev("window.__hotspotFail=true;const p=document.querySelector('#hotspot-wifiSettings');p.querySelector('input[name=password]').value='testpassword';p.querySelector('button[type=submit]').click();return true;")
wait("return !!document.querySelector('#hotspot-wifiSettings [role=status]').textContent;")
state=ev("const p=document.getElementById('hotspot-wifiSettings');return {preserved:p.querySelector('input[name=password]').value==='testpassword',writes:__hotspotWrites,current:p.classList.contains('current'),message:p.querySelector('[role=status]').textContent,disabled:p.querySelector('button[type=submit]').disabled};")
assert state['preserved'] and not state['writes'],state
ev("window.__hotspotFail=false;document.querySelector('#hotspot-wifiSettings button[type=submit]').click();return true;");closed()
assert ev("return __hotspotFake.configured && __hotspotWrites.join(',')==='save';")
for enabled in (True,False):
ev("const c=document.querySelector('#tethering-wifi-enabled');c.checked="+json.dumps(enabled)+";c.dispatchEvent(new Event('change',{bubbles:true}));return true;")
wait("return !document.querySelector('#tethering-wifi-enabled').disabled && __hotspotFake.enabled==="+json.dumps(enabled)+';')
assert ev("return __hotspotWrites.join(',')==='save,on,off';")
print('PASS: Gaia hotspot cancel/reopen, invalid input, failed save retention, retry, start/stop. Fake radio only.')
finally:
ev("VulpesCompat.call=__hotspotOriginalCall;location.reload();return true;")