'use strict'; const assert=require('node:assert/strict'); const {panelPosition}=require('./icampus-study-helper.user.js'); const {harness}=require('./study-helper.test.cjs'); const KEY='icampus.study.v1.panel'; 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);}}; const host={style:{},attachShadow:()=>panel,getBoundingClientRect(){return { left:parseFloat(this.style.left)||16,top:parseFloat(this.style.top)||100, width:nodes.get('[data-main-panel]')?.hidden?240:322,height:nodes.get('[data-main-panel]')?.hidden?44:620};}}; h.context.window.innerWidth=1400;h.context.window.innerHeight=900; h.context.document.body={append(){}};h.context.document.createElement=()=>host;h.api.mount(); return {nodes,host}; } function pointer(id,x,y,target={closest:()=>null}){return {pointerId:id,clientX:x,clientY:y,button:0,isPrimary:true,target,preventDefault(){}};} (async()=>{ await test('collapse and expand preserve active playback, intent and options',()=>{ const h=harness(),{nodes}=mount(h);h.api.begin();h.api.track(h.video); nodes.get('[data-collapse]').onclick();assert.equal(nodes.get('[data-main-panel]').hidden,true);assert.equal(nodes.get('[data-expand]').hidden,false); assert.equal(h.api.state.running,true);assert.equal(h.api.state.armed,true);assert.equal(h.video.pauseCount,0); nodes.get('[data-expand]').onclick({detail:0});assert.equal(nodes.get('[data-main-panel]').hidden,false);assert.equal(h.api.state.running,true); }); await test('dragging the title changes and stores position',()=>{ const h=harness(),{nodes,host}=mount(h),handle=nodes.get('[data-drag-handle]'); handle.onpointerdown(pointer(1,20,104));handle.onpointermove(pointer(1,220,204));handle.onpointerup(pointer(1,220,204)); assert.equal(host.style.left,'216px');assert.equal(host.style.top,'200px'); assert.deepEqual(JSON.parse(h.storage.get(KEY)),{x:216,y:200,collapsed:false}); }); await test('dragged compact button remains collapsed and a later click expands',()=>{ const h=harness(),{nodes}=mount(h);nodes.get('[data-collapse]').onclick();const compact=nodes.get('[data-expand]'); compact.onpointerdown(pointer(1,20,104));compact.onpointermove(pointer(1,120,204));compact.onpointerup(pointer(1,120,204)); compact.onclick({detail:1});assert.equal(nodes.get('[data-main-panel]').hidden,true); compact.onpointerdown(pointer(2,120,204));compact.onpointerup(pointer(2,120,204));compact.onclick({detail:1}); assert.equal(nodes.get('[data-main-panel]').hidden,false); }); await test('refresh restores compact state and saved position',()=>{ const h=harness(),{nodes}=mount(h);const header=nodes.get('[data-drag-handle]'); header.onpointerdown(pointer(1,20,104));header.onpointermove(pointer(1,220,204));header.onpointerup(pointer(1,220,204));nodes.get('[data-collapse]').onclick(); const restarted=harness({storageEntries:[...h.storage]}),ui=mount(restarted); assert.equal(ui.nodes.get('[data-main-panel]').hidden,true);assert.equal(ui.host.style.left,'216px');assert.equal(ui.host.style.top,'200px'); assert.equal(restarted.api.state.armed,false); }); await test('resize keeps the header and expanded panel inside the smaller viewport',()=>{ const h=harness(),{nodes,host}=mount(h);const header=nodes.get('[data-drag-handle]'); header.onpointerdown(pointer(1,20,104));header.onpointermove(pointer(1,1000,250));header.onpointerup(pointer(1,1000,250)); h.window.innerWidth=400;h.window.innerHeight=700;h.window.dispatchEvent({type:'resize'}); assert.equal(host.style.left,'70px');assert.equal(host.style.top,'72px'); }); await test('tiny movements and button presses do not start a drag or change settings',()=>{ const h=harness(),{nodes,host}=mount(h),header=nodes.get('[data-drag-handle]'); const before={...host.style};header.onpointerdown(pointer(1,20,104));header.onpointermove(pointer(1,21,105));header.onpointerup(pointer(1,21,105)); assert.equal(host.style.left,before.left);assert.equal(h.storage.has(KEY),false); header.onpointerdown(pointer(2,20,104,{closest:()=>({})}));header.onpointermove(pointer(2,500,500));header.onpointerup(pointer(2,500,500)); assert.equal(host.style.left,before.left);assert.equal(h.api.state.armed,false); }); await test('cancelled pointer capture allows the next drag to work',()=>{ const h=harness(),{nodes,host}=mount(h),header=nodes.get('[data-drag-handle]'); header.onpointerdown(pointer(1,20,104));header.onpointercancel(pointer(1,20,104)); header.onpointerdown(pointer(2,20,104));header.onpointermove(pointer(2,120,104));header.onpointerup(pointer(2,120,104)); assert.equal(host.style.left,'116px'); }); await test('invalid and off-screen preferences safely fall back or clamp',()=>{ const invalid=harness({storageEntries:[[KEY,'broken json']]});assert.equal(mount(invalid).nodes.get('[data-main-panel]').hidden,false); const outside=harness({storageEntries:[[KEY,JSON.stringify({x:100000,y:-100,collapsed:false})]]});const ui=mount(outside); assert.equal(ui.host.style.left,'1070px');assert.equal(ui.host.style.top,'8px'); assert.deepEqual(panelPosition(NaN,Infinity,322,620,1400,900),{x:16,y:16}); }); await test('compact status reflects waiting and cancellation',()=>{ const h=harness(),{nodes}=mount(h);nodes.get('[data-start]').onclick();nodes.get('[data-collapse]').onclick(); h.api.park('等待网络');assert.equal(nodes.get('[data-mini-state]').textContent,'等待恢复'); assert.match(nodes.get('[data-expand]').title,/等待网络/);h.api.stop();assert.equal(nodes.get('[data-mini-state]').textContent,'未启动'); }); await test('blocked local storage does not prevent collapsing or dragging',()=>{ const h=harness(),{nodes,host}=mount(h);h.context.localStorage.setItem=()=>{throw new Error('blocked');}; nodes.get('[data-collapse]').onclick();const compact=nodes.get('[data-expand]'); compact.onpointerdown(pointer(1,20,104));compact.onpointermove(pointer(1,120,204));compact.onpointerup(pointer(1,120,204)); assert.equal(nodes.get('[data-main-panel]').hidden,true);assert.equal(host.style.left,'116px'); }); console.log(`${passed} panel control tests passed.`); })().catch(e=>{console.error(e);process.exitCode=1;});