feat: render health pages from server summaries

This commit is contained in:
weiyuqing committed 2026-10-17 14:10:27 +08:00
1 parent c9ca625de1
commit bcd4cd3759
9 files changed
+241 -103

No files matched your search

+6 -5
View File
@@ -3,17 +3,18 @@ const assert=require('node:assert/strict');
const vm=require('node:vm');
const fs=require('node:fs');
const HealthViews=require('../src/mi_fitness_mcp/web_assets/static/js/health-views.js');
function summary(kind='sleep',rows=[]){return {status:'ok',kind,statistics:{count:rows.length,mean_daily_asleep_minutes:400,mean_daily_deep_minutes:null,nap_count:0},coverage:rows.length,stride:1,points:[{date:'2026-10-16',values:[null,null,null,400/60],missing:false}],page:{number:1,size:10,total:rows.length,rows}};}
function fixture(){
const nodes=new Map(),saved={};let callback;
const get=id=>{if(!nodes.has(id))nodes.set(id,{value:({startDate:'2026-10-15',endDate:'2026-10-17',napsSelect:'true'})[id]||'',textContent:'',innerHTML:'',checked:false,dataset:{},setAttribute(k,v){this[k]=v;},classList:{toggle(){},add(){},remove(){}}});return nodes.get(id);};
const c=vm.createContext({HealthViews,AbortController,console,performance,document:{getElementById:get,querySelectorAll(){return [];},addEventListener(name,fn){callback=fn;},documentElement:{setAttribute(){}}},localStorage:{getItem(k){return saved[k]||'';},setItem(k,v){saved[k]=v;}},setTimeout(){},setInterval(){},clearTimeout(){},clearInterval(){}});
const c=vm.createContext({HealthViews,summary,AbortController,console,performance,document:{getElementById:get,querySelectorAll(){return [];},addEventListener(name,fn){callback=fn;},documentElement:{setAttribute(){}}},localStorage:{getItem(k){return saved[k]||'';},setItem(k,v){saved[k]=v;}},setTimeout(){},setInterval(){},clearTimeout(){},clearInterval(){}});
vm.runInContext(fs.readFileSync('src/mi_fitness_mcp/web_assets/static/js/health-ui.js','utf8'),c);
vm.runInContext(fs.readFileSync('src/mi_fitness_mcp/web_assets/static/js/app.js','utf8'),c);
vm.runInContext('showToast=()=>{};',c);
return{c,get,saved};
}
test('切换到睡眠页自动查询并清空上个接口,修改日期会重新加载',async()=>{
const f=fixture();vm.runInContext('calls=[];api=async(method,path)=>{calls.push(path);return {status:"ok",data:[]};};state.lastResponse={old:true};',f.c);
const f=fixture();vm.runInContext('calls=[];api=async(method,path)=>{calls.push(path);return summary();};state.lastResponse={old:true};',f.c);
await vm.runInContext("switchTab('sleep')",f.c);
assert.equal(vm.runInContext('state.lastResponse',f.c),null);
assert.equal(vm.runInContext('calls.length',f.c),1);
@@ -28,15 +29,15 @@ test('后返回的旧日期请求不会覆盖新日期结果或控制台',async(
f.get('startDate').value='2026-10-16';
const second=vm.runInContext('querySleep()',f.c);
assert.equal(waits[0].view.isCurrent(),false);
waits[1].resolve({status:'ok',data:[{end_at:'2026-10-16T08:00:00+08:00',time_asleep_minutes:400}]});await second;
waits[0].resolve({status:'ok',data:[{end_at:'2026-10-15T08:00:00+08:00',time_asleep_minutes:100}]});await first;
waits[1].resolve(summary('sleep',[{end_at:'2026-10-16T08:00:00+08:00',time_asleep_minutes:400}]));await second;
waits[0].resolve(summary('sleep',[{end_at:'2026-10-15T08:00:00+08:00',time_asleep_minutes:100}]));await first;
assert.equal(vm.runInContext('state.healthViews.sleep.model.records[0].time_asleep_minutes',f.c),400);
});
test('失败与无数据分别显示,控制台开关会记忆选择',async()=>{
const f=fixture();vm.runInContext('state.activeTab="spo2";api=async()=>null;',f.c);
await vm.runInContext('querySpo2()',f.c);
assert.match(f.get('healthView-spo2').innerHTML,/查询失败/);
vm.runInContext('api=async()=>({status:"ok",data:[]});',f.c);
vm.runInContext('api=async()=>summary("spo2");',f.c);
await vm.runInContext('querySpo2()',f.c);
assert.match(f.get('healthView-spo2').innerHTML,/未保存/);
vm.runInContext('setInspectorCollapsed(true)',f.c);