working camera movement

This commit is contained in:
marcinator_2 committed 2026-10-16 01:38:10 +02:00
1 parent fce8f62534
commit 11e0875a20
21 files changed
+2117 -334

No files matched your search

+115
View File
@@ -0,0 +1,115 @@
// Verify endpoint presentation and that starting never saves stray form limits.
const assert = require('node:assert/strict');
const fs = require('node:fs');
const vm = require('node:vm');
const html = fs.readFileSync(require('node:path').join(__dirname,'../halloween/camera.html'),'utf8');
const state = html.slice(html.indexOf(' function endpointState('),html.indexOf(' async function pollFollow()'));
const begin = html.slice(html.indexOf(' async function beginFollow('),html.indexOf(" $('follow-preview').onclick"));
const save = html.slice(html.indexOf(' async function saveFollow()'),html.indexOf(" for (const [id,hue]"));
const config={search_enabled:false,follow_speed:80,hue:85,tolerance:18,pan_min:1400,pan_max:1600,tilt_min:1400,tilt_max:1600,pan_reverse:false,tilt_reverse:false,pan_setup_min:1100,pan_setup_max:1900,tilt_setup_min:1100,tilt_setup_max:1900};
const nodes={'follow-speed':{value:80}}, calls=[];
const ctx=vm.createContext({
$:id => nodes[id] || (nodes[id]={checked:false,value:18}),
setupReady:true, manualLimitsDirty:false, configEpoch:0, followConfig:{...config}, followLatest:null,
releaseJog:()=>{}, jogSequence:0, followSession:null, followSample:null, followEpoch:0,
api:async (url,body) => {calls.push({url,body}); return url.endsWith('/start') ? {session:'test'} : body;},
fillFollow:c=>{ctx.followConfig=c;}
});
vm.runInContext(state+begin+save,ctx);
(async()=>{
ctx.sample={config:{...config},pan_us:1100,tilt_us:1500};
vm.runInContext('renderEndpoints(sample)',ctx);
assert.equal(nodes['setup-value-left'].textContent,'Gespeichert: 1400 µs');
assert.equal(nodes['setup-save-left'].disabled,false);
assert.match(nodes['setup-hint-left'].textContent,/1110/);
assert.equal(nodes['setup-save-right'].disabled,true);
assert.equal(nodes['setup-save-up'].disabled,true);
ctx.sample={config:{...config,pan_reverse:true},pan_us:1100,tilt_us:1900};
vm.runInContext('renderEndpoints(sample)',ctx);
assert.equal(nodes['setup-save-left'].disabled,true);
assert.equal(nodes['setup-save-right'].disabled,false);
assert.equal(nodes['setup-save-down'].disabled,false);
ctx.sample={config:{...config,pan_min:1728,pan_max:1800},pan_us:1718,tilt_us:1500,setup_origin:{pan:1750,tilt:1500}};
vm.runInContext('renderEndpoints(sample)',ctx);
assert.equal(nodes['setup-save-left'].disabled,false); // above 1500 is valid
assert.equal(nodes['setup-save-right'].disabled,true);
ctx.sample.pan_us=1751;
vm.runInContext('renderEndpoints(sample)',ctx);
assert.equal(nodes['setup-save-left'].disabled,true); // original session centre
ctx.sample.centering=true;
ctx.sample.pan_us=1718;
vm.runInContext('renderEndpoints(sample)',ctx);
assert.equal(nodes['setup-save-left'].disabled,true);
assert.match(nodes['setup-hint-left'].textContent,/Mittelstellungsfahrt/);
ctx.sample.centering=false;
ctx.setupReady=false;
vm.runInContext('renderEndpoints(sample)',ctx);
assert.equal(nodes['setup-save-right'].disabled,true);
assert.match(nodes['setup-hint-right'].textContent,/Einrichtmodus/);
await vm.runInContext("beginFollow(false,'setup')",ctx);
assert.deepEqual(calls.map(c=>c.url),['/api/follow/start']);
assert.equal(ctx.followSession,'test');
nodes['follow-pan-reverse'].checked=true;
await assert.rejects(vm.runInContext("beginFollow(false)",ctx),/zuerst dort speichern/);
assert.equal(calls.length,1);
nodes['follow-pan-reverse'].checked=false;
nodes['follow-speed'].value=400;
await assert.rejects(vm.runInContext("beginFollow(false)",ctx),/zuerst dort speichern/);
nodes['follow-speed'].value=80;
nodes['follow-search'].checked=true;
await assert.rejects(vm.runInContext("beginFollow(false)",ctx),/zuerst dort speichern/);
nodes['follow-speed'].value=400;
nodes['follow-pan-reverse'].checked=true;
// A settings save only changes colour/direction/speed, never endpoint values.
nodes['follow-pan-min']={value:500}; nodes['follow-pan-max']={value:500};
await vm.runInContext('saveFollow()',ctx);
assert.equal(calls[1].body.pan_min,1400);
assert.equal(calls[1].body.pan_max,1600);
assert.equal(calls[1].body.pan_reverse,true);
assert.equal(calls[1].body.follow_speed,400);
assert.equal(calls[1].body.search_enabled,true);
assert.ok(html.includes('id="follow-pan-min"'));
assert.ok(html.includes('Diese Position als Links speichern'));
ctx.manualLimitsDirty=true;
await assert.rejects(vm.runInContext("beginFollow(false)",ctx),/noch nicht gespeichert/);
ctx.manualLimitsDirty=false;
const manual=html.slice(html.indexOf(' function fillManualLimits('),html.indexOf(' // Manual limit form bindings.'));
vm.runInContext(manual,ctx);
vm.runInContext('fillManualLimits(followConfig)',ctx);
nodes['follow-pan-min'].value=1200; nodes['follow-pan-max'].value=1800;
nodes['follow-tilt-min']={value:1300}; nodes['follow-tilt-max']={value:1700};
await vm.runInContext('saveManualLimits()',ctx);
assert.equal(calls.at(-1).body.pan_min,1200);
assert.equal(calls.at(-1).body.tilt_max,1700);
assert.equal(calls.at(-1).body.pan_reverse,true);
assert.match(nodes['manual-limits-status'].textContent,/gespeichert/);
assert.equal(calls.at(-1).body.follow_speed,400);
assert.equal(calls.at(-1).body.search_enabled,true);
nodes['follow-tilt-min'].value=1700; nodes['follow-tilt-max'].value=1800;
await vm.runInContext('saveManualLimits()',ctx);
assert.equal(calls.at(-1).body.tilt_min,1700); // range need not contain 1500
const count=calls.length;
nodes['follow-pan-max'].value=2500;
await assert.rejects(vm.runInContext('saveManualLimits()',ctx),/500–2500/);
assert.equal(calls.length,count);
nodes['follow-pan-setup-max'].value=2500;
await vm.runInContext('saveManualLimits()',ctx);
assert.equal(calls.at(-1).body.pan_max,2500);
assert.equal(calls.at(-1).body.pan_setup_max,2500);
nodes['follow-pan-setup-max'].value=2501;
await assert.rejects(vm.runInContext('saveManualLimits()',ctx),/500–2500/);
const centerCode=html.slice(html.indexOf(' async function centerSetup()'),html.indexOf(" $('setup-center').onclick"));
vm.runInContext(centerCode,ctx);
ctx.setupReady=false;
await assert.rejects(vm.runInContext('centerSetup()',ctx),/Einrichtmodus/);
ctx.setupReady=true; ctx.followSession='test'; ctx.jogSequence=5;
ctx.api=async (url,body)=>{calls.push({url,body});return {accepted:true};};
ctx.sendJog=async direction=>{calls.push({url:'/api/follow/jog',body:{direction,sequence:++ctx.jogSequence}});};
await vm.runInContext('centerSetup()',ctx);
assert.equal(calls.at(-1).url,'/api/follow/center');
assert.equal(calls.at(-1).body.sequence,6);
await vm.runInContext('stopSetupMotion()',ctx);
assert.equal(calls.at(-1).body.direction,null);
assert.equal(calls.at(-1).body.sequence,7);
console.log('Endpoint UI tests passed');
})().catch(e=>{console.error(e);process.exit(1);});