Files
rhinelab-ui/scripts/check-workbench.mjs
T

58 lines
5.1 KiB
JavaScript

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.');