Share the K1 live scene template and preserve idle media channels
This commit is contained in:
@@ -35,9 +35,15 @@ test('active acquisition exposes STOP and Rerun without another START',()=>{
|
||||
const active={...device,control:{...device.control,can_start:false,can_stop:true},snapshot:{...device.snapshot,acquisition:'streaming'}};
|
||||
const markup=render(active);
|
||||
assert.match(markup,/Остановить устройство/);
|
||||
assert.match(markup,/k1-preview-spatial/);
|
||||
assert.match(markup,/rerun-viewport__canvas/);
|
||||
assert.match(markup,/>Статус</);
|
||||
assert.match(markup,/>Rerun</);
|
||||
assert.match(markup,/>Пространственная модель</);
|
||||
for(const label of ['Движок','Слои','Отображение','Камера K1','Окно накопления облака точек'])assert.ok(markup.includes(label));
|
||||
assert.ok(markup.includes('data-presented="false"'));
|
||||
assert.ok(!markup.includes('Loading Rerun'));
|
||||
assert.ok(!markup.includes('The data layer'));
|
||||
assert.ok(!markup.includes('data-align="center"'));
|
||||
|
||||
assert.doesNotMatch(markup,/Восстановить просмотр|nodedc-activity-indicator/);
|
||||
assert.doesNotMatch(markup,/Инициировать запуск|Обновить просмотр/);
|
||||
assert.equal(presentation.k1ManualState(active,false).canStart,false);
|
||||
|
||||
@@ -2,9 +2,11 @@ import assert from 'node:assert/strict';
|
||||
import {before,after,test} from 'node:test';
|
||||
import {execFileSync} from 'node:child_process';
|
||||
import {createServer} from 'vite';
|
||||
let server,previewFrames,assertRrd;
|
||||
let server,previewFrames,assertRrd,previewFreshness,previewCamera;
|
||||
before(async()=>{
|
||||
server=await createServer({appType:'custom',logLevel:'silent',server:{middlewareMode:true}});
|
||||
({previewCamera}=await server.ssrLoadModule('@xgrids-k1/frontend/sensors/previewCamera.ts'));
|
||||
({previewFreshness}=await server.ssrLoadModule('@xgrids-k1/frontend/sensors/previewFreshness.ts'));
|
||||
({previewFrames,assertRrd}=await server.ssrLoadModule('@xgrids-k1/frontend/sensors/previewFrames.ts'));
|
||||
});
|
||||
after(async()=>{await server?.close();});
|
||||
@@ -41,3 +43,38 @@ test('partial, oversized, unframed and invalid records never enter the decoder',
|
||||
receiver.push(header(12));assert.throws(()=>receiver.push(array(new Uint8Array(12))));
|
||||
assert.equal(calls,0);
|
||||
});
|
||||
|
||||
test('source age expires even when metadata repeats, old frames never look live',()=>{
|
||||
let now=10000;const gate=previewFreshness(()=>now);
|
||||
assert.equal(gate.fresh,false);
|
||||
gate.receive({type:'lidar-state',sequence:1,age_ms:250,points:3});
|
||||
assert.equal(gate.fresh,true);assert.equal(gate.points,3);
|
||||
now+=1800;gate.receive({type:'lidar-state',sequence:1,age_ms:0,points:3});
|
||||
assert.equal(gate.fresh,false);
|
||||
gate.receive({type:'lidar-state',sequence:2,age_ms:9000,points:3});
|
||||
assert.equal(gate.fresh,false);
|
||||
gate.receive({type:'lidar-state',sequence:3,age_ms:0,points:4});
|
||||
assert.equal(gate.fresh,true);assert.equal(gate.points,4);
|
||||
assert.throws(()=>gate.receive({type:'lidar-state',sequence:4,age_ms:-1,points:4}));
|
||||
});
|
||||
|
||||
test('onboard camera admits immediate sourceopen and ignores callbacks after close',()=>{
|
||||
const saved={media:globalThis.MediaSource,create:URL.createObjectURL,revoke:URL.revokeObjectURL};
|
||||
let source,buffer,appended=0,ready=0,failed=0;
|
||||
class Buffer extends EventTarget {updating=false;buffered={length:0};appendBuffer(){appended++;}}
|
||||
class Media extends EventTarget {
|
||||
readyState='open';static isTypeSupported(){return true;}
|
||||
constructor(){super();source=this;}
|
||||
addSourceBuffer(){buffer=new Buffer();return buffer;}
|
||||
}
|
||||
class Video extends EventTarget {currentTime=0;set src(_){source.dispatchEvent(new Event('sourceopen'));}pause(){}load(){}removeAttribute(){}play(){return Promise.resolve();}}
|
||||
globalThis.MediaSource=Media;URL.createObjectURL=()=> 'blob:synthetic';URL.revokeObjectURL=()=>{};
|
||||
try{
|
||||
const video=new Video(),decoder=previewCamera(video,()=>ready++,()=>failed++);
|
||||
decoder.open('video/mp4');decoder.push(new Uint8Array([1,2,3]));
|
||||
assert.equal(appended,1);
|
||||
video.dispatchEvent(new Event('loadeddata'));assert.equal(ready,1);
|
||||
decoder.close();video.dispatchEvent(new Event('loadeddata'));buffer.dispatchEvent(new Event('error'));
|
||||
assert.equal(ready,1);assert.equal(failed,0);
|
||||
}finally{globalThis.MediaSource=saved.media;URL.createObjectURL=saved.create;URL.revokeObjectURL=saved.revoke;}
|
||||
});
|
||||
|
||||
@@ -414,7 +414,7 @@ test("data recordings keep the compact session dropdown and laboratory results s
|
||||
|
||||
test("recording preparation statuses share the viewer's left alignment", async () => {
|
||||
const spatialStyles = await readFile(
|
||||
new URL("../src/styles/spatial.css", import.meta.url),
|
||||
new URL("../../../packages/spatial-ui/src/spatial.css", import.meta.url),
|
||||
"utf8",
|
||||
);
|
||||
const statusStack = spatialStyles.slice(
|
||||
|
||||
@@ -280,7 +280,7 @@ test("recorded player skips only a proven buffered corrupt timestamp interval",
|
||||
|
||||
test("loading and error overlays fully conceal recorded camera pixels", async () => {
|
||||
const css = await readFile(
|
||||
new URL("../src/styles/observation.css", import.meta.url),
|
||||
new URL("../../../packages/spatial-ui/src/observation.css", import.meta.url),
|
||||
"utf8",
|
||||
);
|
||||
assert.match(
|
||||
|
||||
@@ -382,7 +382,7 @@ test("the spatial header reports raw replay as an active source", async () => {
|
||||
});
|
||||
|
||||
test("the complete vendor canvas host is hidden during partial and failed admission", async () => {
|
||||
const css = await readFile(new URL("../src/styles/spatial.css", import.meta.url), "utf8");
|
||||
const css = await readFile(new URL("../../../packages/spatial-ui/src/spatial.css", import.meta.url), "utf8");
|
||||
assert.match(
|
||||
css,
|
||||
/\.rerun-viewport:is\(\[data-status="loading"\], \[data-status="error"\]\)\s+\.rerun-viewport__canvas\s*\{[^}]*visibility:\s*hidden;[^}]*pointer-events:\s*none;/s,
|
||||
|
||||
Reference in New Issue
Block a user