Initial commit: RHINE LAB · ANALYSIS OS:三维界面与动效实验(本仓库不含个人归档索引数据)
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import ts from 'typescript';
|
||||
import vm from 'node:vm';
|
||||
const source = ts.transpileModule(readFileSync('src/workbench-state.ts', 'utf8'), {compilerOptions:{module:ts.ModuleKind.ESNext,target:ts.ScriptTarget.ES2022}}).outputText;
|
||||
const {parseTarget, restoreTimer, timerLeft, durationText, dayKey} = await import(`data:text/javascript;base64,${Buffer.from(source).toString('base64')}`);
|
||||
assert.equal(parseTarget('2026-02-30'), null);
|
||||
assert.equal(parseTarget('2026-09-10 24:00'), null);
|
||||
assert.equal(parseTarget('invalid'), null);
|
||||
assert.equal(parseTarget('2026-09-10'), new Date(2026,8,10).getTime());
|
||||
assert.equal(parseTarget('2028-02-29 12:30'), new Date(2028,1,29,12,30).getTime());
|
||||
assert.equal(dayKey(new Date(2026,0,2,0,1)), '2026-01-02');
|
||||
const running={phase:'focus',status:'running',remaining:60000,deadline:100000};
|
||||
assert.equal(timerLeft(restoreTimer(JSON.parse(JSON.stringify(running))), 85000),15000,'Reload resumes by absolute deadline');
|
||||
assert.equal(timerLeft(running,1000000),0,'Wallpaper suspension cannot extend the timer');
|
||||
assert.equal(timerLeft({...running,status:'paused',remaining:12000},1000000),12000,'Paused timer does not elapse');
|
||||
assert.equal(restoreTimer({...running,remaining:NaN}).status,'idle');
|
||||
assert.equal(restoreTimer({phase:'wrong'}).status,'idle');
|
||||
assert.equal(durationText(1),'00:01');
|
||||
const listeners={};const window={dispatchEvent(){},wallpaperMediaIntegration:{PLAYBACK_PLAYING:7}};
|
||||
for (const kind of ['Status','Properties','Thumbnail','Playback','Timeline']) window[`wallpaperRegisterMedia${kind}Listener`] = callback => listeners[kind]=callback;
|
||||
vm.runInNewContext(readFileSync('wallpaper/host.js','utf8'),{window,Event,CustomEvent});
|
||||
listeners.Properties({title:'A'}); listeners.Timeline({position:12,duration:100});listeners.Thumbnail({thumbnail:'old'});listeners.Playback({state:7});
|
||||
assert.equal(window.rhineWallpaperMedia.playing,true);
|
||||
listeners.Properties({title:'B'});
|
||||
assert.equal(window.rhineWallpaperMedia.properties.title,'B');
|
||||
assert.equal(window.rhineWallpaperMedia.timeline.position,12,'Text updates cannot discard the independent timeline channel');
|
||||
assert.equal(window.rhineWallpaperMedia.thumbnail.thumbnail,'old','Same artwork may be reused across tracks without another callback');
|
||||
// Local player: artwork arrives first, then text, then a late metadata refresh.
|
||||
listeners.Thumbnail({thumbnail:'magic-theorem-cover'});
|
||||
listeners.Timeline({position:0,duration:242});
|
||||
listeners.Properties({title:'Magic Theorem',artist:'塞壬唱片-MSR/Adam Gubman/Sarah Kang'});
|
||||
listeners.Playback({state:7});
|
||||
listeners.Properties({title:'Magic Theorem',artist:'塞壬唱片-MSR/Adam Gubman/Sarah Kang',albumTitle:'Magic Theorem'});
|
||||
assert.equal(window.rhineWallpaperMedia.thumbnail.thumbnail,'magic-theorem-cover','Late text cannot erase a local-file cover');
|
||||
assert.equal(window.rhineWallpaperMedia.timeline.duration,242);
|
||||
// Reverse callback order, pause/resume and switching between players.
|
||||
listeners.Playback({state:2});listeners.Properties({title:'Cloud track'});listeners.Thumbnail({thumbnail:'cloud-cover'});
|
||||
assert.equal(window.rhineWallpaperMedia.thumbnail.thumbnail,'cloud-cover');
|
||||
listeners.Thumbnail({thumbnail:'magic-theorem-cover'});listeners.Properties({title:'Magic Theorem'});listeners.Playback({state:7});
|
||||
assert.equal(window.rhineWallpaperMedia.thumbnail.thumbnail,'magic-theorem-cover');
|
||||
// Explicit empty events still remove unavailable artwork/progress.
|
||||
listeners.Thumbnail({thumbnail:''});listeners.Timeline({position:0,duration:0});listeners.Properties({title:'No artwork'});
|
||||
assert.equal(window.rhineWallpaperMedia.thumbnail.thumbnail,'');
|
||||
assert.equal(window.rhineWallpaperMedia.timeline.duration,0);
|
||||
console.log('Workbench dates, timer suspension/reload, corrupt storage and early media callbacks passed.');
|
||||
const visibilitySource = ts.transpileModule(readFileSync('src/workbench-visibility.ts', 'utf8'), {compilerOptions:{module:ts.ModuleKind.ESNext,target:ts.ScriptTarget.ES2022}}).outputText;
|
||||
const {defaultWorkbenchVisibility, applyVisibilityProperties, workbenchElements} = await import(`data:text/javascript;base64,${Buffer.from(visibilitySource).toString('base64')}`);
|
||||
const hiddenProps = Object.fromEntries(workbenchElements.map(([key]) => [`show${key}`, {value:false}]));
|
||||
const quiet = applyVisibilityProperties(defaultWorkbenchVisibility(), hiddenProps);
|
||||
assert.ok(Object.values(quiet).every(value => value === false), 'WE can hide every element including settings');
|
||||
const clockOnly = applyVisibilityProperties(quiet, {showclock:{value:true}});
|
||||
assert.equal(clockOnly.clock, true);
|
||||
assert.equal(clockOnly.settings, false, 'Partial host changes preserve hidden settings');
|
||||
assert.deepEqual(applyVisibilityProperties(clockOnly,{sound:{value:true},showtasks:{value:'false'}}),clockOnly,'Unrelated/invalid properties cannot reset visibility');
|
||||
assert.deepEqual(applyVisibilityProperties(defaultWorkbenchVisibility(),hiddenProps),quiet,'Startup host properties reproduce the saved composition');
|
||||
console.log('Workbench host visibility: all hidden, clock-only, partial updates and startup restoration passed.');
|
||||
Reference in New Issue
Block a user