feat: introduce Vue Bootstrap interface and scoped router network status

This commit is contained in:
Codex
2026-10-05 20:23:05 +08:00
parent cf895110ee
commit 04d56d5aa2
48 changed files with 4096 additions and 145 deletions
+29
View File
@@ -0,0 +1,29 @@
const test=require('node:test'),assert=require('node:assert/strict'),fs=require('node:fs');
const read=path=>fs.readFileSync(path,'utf8');
test('Vue production entry uses local hashed assets and precompiled templates',()=>{
const html=read('web/ui/index.html');
assert.match(html,/type="module"[^>]+src="\/ui\/assets\/index-/);
assert.match(html,/rel="stylesheet"[^>]+href="\/ui\/assets\/index-/);
assert.doesNotMatch(html,/https?:\/\/|unsafe-eval/);
const config=read('frontend/package.json');assert.match(config,/"vue"/);assert.match(config,/"bootstrap"/);
});
test('Vue compatibility boundary preserves unique static DOM identities',()=>{
const source=require('./test-support/ui-source')();
const ids=[...source.matchAll(/\sid="([\w-]+)"/g)].map(m=>m[1]);
assert.equal(ids.length,new Set(ids).size);
for(const id of ['auth-form','device-form','household-list','asset-list','device-topology','device-detail','live-grid','journal-form'])assert.ok(ids.includes(id),id);
const app=read('web/app.js');assert.match(app,/window\.ZhaoVue\?\.clear\(\)/);assert.match(app,/window\.ZhaoVue\?\.publish\?\.\(\)/);
const load=read('frontend/src/compat/load.js');assert.match(load,/await new Promise/);assert.match(load,/await window\.ZhaoVue.boot/);
});
test('network widget rejects stale responses and stops timers on unmount',()=>{
const component=read('frontend/src/components/NetworkStatus.vue');
assert.match(component,/current===generation/);assert.match(component,/onUnmounted/);assert.match(component,/clearTimeout\(timer\)/);
assert.match(component,/rel="noopener noreferrer"/);assert.match(component,/snapshot.admin/);
const routes=read('app.py');assert.match(routes,/self.require_manager\(\); self.answer\(NETWORK.read/);
});
test('explicit device detail does not force camera live view',()=>{
const vm=require('node:vm'),source=read('web/steward.js');let shown='';
const context=vm.createContext({ZhaoDevices:{catalog:()=>[{id:'a',source:'camera'}]},state:{cameras:[{}]},toast(){},stopPlayers(){},resetPlayback(){},deviceCameras:()=>[{}],$:()=>({value:''}),showPage:value=>shown=value});
vm.runInContext(source.slice(source.indexOf('function enterDevice('),source.indexOf('function renderSteward(')),context);
context.enterDevice('camera','a','device');assert.equal(shown,'device');context.enterDevice('camera','a');assert.equal(shown,'live');
});