'use strict'; const assert=require('node:assert/strict'); const {harness,ID}=require('./study-helper.test.cjs'); const KEY='icampus.study.v1.fullMuted'; let passed=0; async function test(name,fn){await fn();passed++;console.log(`PASS ${name}`);} function mount(h){const nodes=new Map();const panel={querySelector(s){if(!nodes.has(s))nodes.set(s,{textContent:'',value:'all',checked:true});return nodes.get(s);}}; h.context.document.body={append(){}};h.context.document.createElement=()=>({style:{},attachShadow:()=>panel});h.api.mount();return nodes;} function toggle(ui,enabled){const checkbox=ui.get('[data-full-muted]');checkbox.checked=enabled;checkbox.onchange();} function soundEvent(h,type,video=h.video){h.context.document.dispatchEvent({type,target:video});} (async()=>{ await test('full mute is opt-in and enabling it silences an already playing video immediately',()=>{ const h=harness(),ui=mount(h);assert.equal(ui.get('[data-full-muted]').checked,false); h.video.muted=false;h.video.volume=0.7;toggle(ui,true); assert.equal(h.video.muted,true);assert.equal(h.video.defaultMuted,true);assert.equal(h.video.volume,0.7); assert.equal(h.storage.get(KEY),'on');assert.equal(h.video.pauseCount,0);assert.equal(h.video.playCount,0); }); await test('full mute is applied before assistant playback, even with autoplay fallback disabled',async()=>{ const h=harness(),ui=mount(h);toggle(ui,true); const fallback=ui.get('[data-muted]');fallback.checked=false;fallback.onchange(); h.video.muted=false;let seen=false;h.video.play=function(){seen=this.muted;return Promise.resolve();}; h.api.begin();await h.api.tickCourse(ID);h.advance(1000);await h.api.tickCourse(ID); assert.equal(seen,true);assert.equal(h.video.playbackRate,2); }); await test('page volume resets are silenced again and unrelated media is left alone',()=>{ const h=harness(),ui=mount(h);toggle(ui,true);h.video.muted=false;h.video.defaultMuted=false; soundEvent(h,'volumechange');assert.equal(h.video.muted,true);assert.equal(h.video.defaultMuted,true); const other={muted:false,matches:()=>false};soundEvent(h,'play',other);assert.equal(other.muted,false); }); await test('replacement course player is muted on insertion before any start command',()=>{ const h=harness(),ui=mount(h);toggle(ui,true); const next={muted:false,defaultMuted:false,isConnected:true};h.video.isConnected=false; h.selectors.set('video.player__video',next);h.mutate();assert.equal(next.muted,true);assert.equal(next.defaultMuted,true); }); await test('source reload and native play events keep the current player silent',()=>{ const h=harness(),ui=mount(h);toggle(ui,true); for(const type of ['loadstart','loadedmetadata','play']){h.video.muted=false;soundEvent(h,type);assert.equal(h.video.muted,true);} }); await test('normal refresh restores the preference before playback',async()=>{ const h=harness(),ui=mount(h);toggle(ui,true); const restarted=harness({storageEntries:[...h.storage]});const nextUI=mount(restarted);await restarted.api.tick(); assert.equal(nextUI.get('[data-full-muted]').checked,true);assert.equal(restarted.video.muted,true); assert.equal(restarted.api.state.armed,false); }); await test('turning off restores previous mute state without changing volume or position',()=>{ const h=harness(),ui=mount(h);h.video.muted=false;h.video.defaultMuted=false;h.video.volume=0.5; const time=h.video.currentTime;toggle(ui,true);toggle(ui,false); assert.equal(h.video.muted,false);assert.equal(h.video.defaultMuted,false);assert.equal(h.video.volume,0.5); assert.equal(h.video.currentTime,time);assert.equal(h.storage.get(KEY),'off'); soundEvent(h,'play');assert.equal(h.video.muted,false); }); await test('turning off preserves a player that was already muted beforehand',()=>{ const h=harness(),ui=mount(h);h.video.muted=true;h.video.defaultMuted=true;toggle(ui,true);toggle(ui,false); assert.equal(h.video.muted,true);assert.equal(h.video.defaultMuted,true); }); await test('background, waiting and cancellation retain explicit full-mute preference',async()=>{ const h=harness(),ui=mount(h);toggle(ui,true);h.api.begin();h.api.park('等待确认'); h.context.document.visibilityState='hidden';h.video.muted=false;await h.api.tick();assert.equal(h.video.muted,true); h.api.stop();h.video.muted=false;soundEvent(h,'volumechange');assert.equal(h.video.muted,true); assert.equal(h.storage.get(KEY),'on');assert.equal(h.api.state.armed,false); }); await test('enabling full mute retries a blocked playback without losing running intent',async()=>{ const h=harness(),ui=mount(h);const fallback=ui.get('[data-muted]');fallback.checked=false;fallback.onchange(); let calls=0;h.video.play=function(){calls++;return this.muted?Promise.resolve():Promise.reject({name:'NotAllowedError'});}; h.api.begin();await h.api.tickCourse(ID);h.advance(1000);await h.api.tickCourse(ID);await Promise.resolve(); assert.equal(h.api.state.running,false);assert.equal(calls,1);toggle(ui,true); await h.api.tick();h.advance(1000);await h.api.tick();assert.equal(h.api.state.running,true);assert.equal(calls,2); }); console.log(`${passed} full-mute tests passed.`); })().catch(e=>{console.error(e);process.exitCode=1;});