Files
icampus-learning-assistant/panel-controls.test.cjs
T

84 lines
6.3 KiB
JavaScript

'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;});