test_serve_app_js_audio.py
Rama main · réplica de lectura del repositorio original.
"""Live view audio controls (toggle, button state, and autoplay fallback)."""
import json
import shutil
import subprocess
import unittest
from pathlib import Path
def audio_source(source):
start = source.index("// ── audio direct (Live View)")
return source[start:source.index("// ── fin audio direct (Live View)", start)]
class LiveViewAudioTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.node = shutil.which("node")
if not cls.node:
raise unittest.SkipTest("Node unavailable")
cls.source = Path(__file__).with_name("serve_app.js").read_text(encoding="utf-8")
cls.code = audio_source(cls.source)
def run_js(self, scenario):
script = r"""
const assert=require("node:assert/strict");
const videos=[];
const tick=()=>new Promise(resolve=>setImmediate(resolve));
class Video {
constructor() {
this.isConnected=true;this._muted=false;this._volume=1;this.events={};
this.defaultMuted=false;this.plays=0;this.blockOnce=false;this.failure=null;
this.classList={classes:new Set(),toggle(c,v){if(v)this.classes.add(c);else this.classes.delete(c);return this.classes.has(c);}};
this.button={disabled:false,classList:this.classList,setAttribute(){},textContent:"",title:""};
videos.push(this);
}
addEventListener(name,callback){(this.events[name]||=[]).push(callback);}
emit(name){for(const fn of this.events[name]||[])fn({target:this});}
get muted(){return this._muted;}
set muted(v){if(this._muted!==v){this._muted=v;queueMicrotask(()=>this.emit("volumechange"));}}
get volume(){return this._volume;}
set volume(v){if(this._volume!==v){this._volume=v;queueMicrotask(()=>this.emit("volumechange"));}}
closest(){return this.live?{querySelector:()=>this.button}:null;}
play(){this.plays++;if(this.failure)return Promise.reject(this.failure);
if(this.blockOnce){this.blockOnce=false;return Promise.reject(Object.assign(new Error("blocked"),{name:"NotAllowedError"}));}
return Promise.resolve();}
}
const document={querySelectorAll:()=>videos.filter(v=>v.isConnected)};
const $=id=>({querySelector:()=>videos.at(-1)});
const h=String,t=String,cssId=String;
let system={camera_audio_enabled:true};
"""
script += self.code + "\n"
script += r"""
(async()=>{
const first=new Video();
first.live=true;
preparerAudioVideo(first);
await tick();
assert.equal(first.muted,true);
assert.equal(first.defaultMuted,true);
if(SCENARIO==="initial"){
assert.equal(first.button.textContent,"live.audio.waiting");
assert.equal(first.button.disabled,true);
}else if(SCENARIO==="speaker"){
first.blinkAudioDisponible=true;
actualiserBoutonAudio(first);
assert.equal(first.button.textContent,"live.audio.listen");
assert.equal(first.button.disabled,false);
assert.equal(first.classList.classes.has("active"),false);
// Toggle unmute (listen)
toggleAudio("Cam");
await tick();
assert.equal(first.muted,false);
assert.equal(first.button.textContent,"live.audio.mute");
assert.equal(first.classList.classes.has("active"),true);
// Toggle mute
toggleAudio("Cam");
await tick();
assert.equal(first.muted,true);
assert.equal(first.button.textContent,"live.audio.listen");
assert.equal(first.classList.classes.has("active"),false);
// Unavailable audio
first.blinkAudioDisponible=false;
actualiserBoutonAudio(first);
assert.equal(first.button.disabled,true);
assert.equal(first.button.textContent,"live.audio.unavailable");
}else if(SCENARIO==="autoplay"){
first.blinkAudioDisponible=true;
actualiserBoutonAudio(first);
first.muted=false;
first.blockOnce=true;
await lireVideo(first);
await tick();
assert.equal(first.muted,true);
assert.equal(first.plays,2);
assert.equal(first.button.textContent,"live.audio.listen");
}else if(SCENARIO==="failure"){
first.failure=new Error("decode failed");
await assert.rejects(lireVideo(first),/decode failed/);
assert.equal(first.muted,true);
}
console.log(JSON.stringify({ok:true}));
})().catch(error=>{console.error(error);process.exitCode=1;});
""".replace("SCENARIO", json.dumps(scenario))
result = subprocess.run([self.node, "-"], input=script, text=True,
encoding="utf-8", capture_output=True, timeout=10)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertTrue(json.loads(result.stdout)["ok"])
def test_live_initial_state_is_muted(self): self.run_js("initial")
def test_live_speaker_toggle_and_button_states(self): self.run_js("speaker")
def test_live_autoplay_rejection_falls_back_to_muted(self): self.run_js("autoplay")
def test_live_media_error_is_reterror(self): self.run_js("failure")
if __name__ == "__main__":
unittest.main()