71 lines
5.2 KiB
JavaScript
71 lines
5.2 KiB
JavaScript
'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;});
|