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