Back to Funclet
SOURCE / PINNED RELEASE

Made of little things.

Funclet

Release
0b497cfc2f66…
Author-recorded commit
1e531b24f490…
License
LICENSE
Author’s source reference
nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-6b7a37cac1d

Archive hash verified: 71d97a89ed8c40c2…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

tests/ui-smoke.mjs
// Built-artifact UI regressions with explicit host fixtures. Live host evidence is separate.
import assert from 'node:assert/strict';
import { readFileSync, realpathSync, mkdirSync } from 'node:fs';
import { createRequire } from 'node:module';
import { execFileSync } from 'node:child_process';
const require = createRequire(`${realpathSync('node_modules/@napplet/conformance-cli')}/package.json`);
const { chromium } = require('playwright');
const browsers = JSON.parse(execFileSync('soyli', ['browser', 'path', '--json'], { encoding: 'utf8' }));
const executablePath = browsers.executables.find(x => x.installed && x.name.startsWith('chromium'))?.path;
assert.ok(executablePath, 'soyLI cached Chromium must be installed');
const browser = await chromium.launch({ executablePath });
mkdirSync('.napplet-space/testing', { recursive: true });
const html = readFileSync('dist/index.html', 'utf8');
import { fixture } from './fixtures/host.mjs';
const fixtureScript = `<script>
window.__sounds=[];
const NativeAudioContext=window.AudioContext;
window.AudioContext=class extends NativeAudioContext {
 createBufferSource(){const source=super.createBufferSource();const start=source.start.bind(source);source.start=(...args)=>{window.__sounds.push({time:this.currentTime,length:source.buffer.duration});return start(...args);};return source;}
};
(${fixture.toString()})()<\/script>`;
try {
 const context = await browser.newContext({viewport:{width:1100,height:800},hasTouch:true});
 const page=await context.newPage(); const errors=[]; page.on('pageerror',e=>errors.push(e.message));
 page.on('console',message=>{if(message.type()==='error'&&/Shader Error|VALIDATE_STATUS|shader.*compile/i.test(message.text()))errors.push(message.text());});
 await page.setContent('<iframe sandbox="allow-scripts" style="border:0;width:900px;height:600px"></iframe>');
 await page.locator('iframe').evaluate((el,source)=>el.srcdoc=source,html.replace('<head>','<head>'+fixtureScript));
 const frame=page.frameLocator('iframe');
 await frame.getByRole('button',{name:'Listen to Test Radio',exact:true}).waitFor();
 assert.equal(await frame.locator('#volume').inputValue(),'60','restore saved volume before tuning');
 await frame.getByRole('button',{name:'Listen to Test Radio',exact:true}).tap();
 await frame.locator('#playback-label').filter({hasText:'ON AIR'}).waitFor();
 await frame.locator('#player-title').filter({hasText:'Fixture track'}).waitFor();
 // Exercise visible mesh controls and compare rendered pixels, not private scene state.
 const stage=frame.locator('#receiver-stage'); const canvas=frame.locator('.receiver-canvas');
 assert.equal(await stage.getAttribute('data-renderer'),'webgl','real WebGL renderer must initialize');
 const stageBox=await stage.boundingBox();
 const keyPoint={x:stageBox.width*.405,y:stageBox.height*.75};
 const playingImage=await canvas.screenshot();await page.waitForTimeout(250);
 assert.ok(!playingImage.equals(await canvas.screenshot()),'playing must animate the cube and shader');
 const soundsBefore=await frame.locator('body').evaluate(()=>window.__sounds.length);
 await canvas.tap({position:keyPoint});
 await frame.locator('#playback-label').filter({hasText:'PAUSED'}).waitFor();
 await page.waitForTimeout(100);
 const pausedImage=await canvas.screenshot();await page.waitForTimeout(200);
 assert.ok(!pausedImage.equals(await canvas.screenshot()),'cube and camera must keep moving gently while paused');
 await canvas.tap({position:keyPoint});
 await frame.locator('#playback-label').filter({hasText:'ON AIR'}).waitFor();
 assert.ok(await frame.locator('body').evaluate(()=>window.__sounds.length)>soundsBefore,'play/pause must synthesize key feedback');
 const beforeDial=Number(await frame.locator('#volume').inputValue());
 await page.mouse.move(stageBox.x+stageBox.width*.625,stageBox.y+stageBox.height*.8);
 await page.mouse.down();await page.mouse.move(stageBox.x+stageBox.width*.625-35,stageBox.y+stageBox.height*.8,{steps:8});await page.mouse.up();
 assert.ok(Number(await frame.locator('#volume').inputValue())<beforeDial,'dragging the physical volume dial must reach the host');
 // Wheel gestures reach the shell from either volume surface, without stealing zoom/other scrolls.
 const slider=frame.locator('#volume');
 const wheel=async(target,deltaY,extra={})=>target.evaluate((el,args)=>el.dispatchEvent(new WheelEvent('wheel',{deltaY:args.deltaY,bubbles:true,cancelable:true,...args.extra})),{deltaY,extra});
 await slider.fill('75');await slider.dispatchEvent('input');
 assert.equal(await wheel(slider,-100),false);assert.equal(await slider.inputValue(),'80');
 assert.equal(await wheel(slider,100),false);assert.equal(await slider.inputValue(),'75');
 assert.equal(await frame.locator('body').evaluate(()=>window.__test.calls.filter(c=>c.type==='storage').at(-1).value),'0.75');
 assert.equal(await wheel(slider,-100,{ctrlKey:true}),true);assert.equal(await slider.inputValue(),'75');
 assert.equal(await wheel(slider,0,{deltaX:100}),true);
 await slider.fill('100');await slider.dispatchEvent('input');await wheel(slider,-100);assert.equal(await slider.inputValue(),'100');
 await slider.fill('0');await slider.dispatchEvent('input');await wheel(slider,100);assert.equal(await slider.inputValue(),'0');
 await slider.fill('75');await slider.dispatchEvent('input');
 const knob={clientX:stageBox.x+stageBox.width*.625-8,clientY:stageBox.y+stageBox.height*.8-8};
 // Coordinates inside the iframe exclude the outer document's iframe offset.
 const iframeBox=await page.locator('iframe').boundingBox();knob.clientX-=iframeBox.x;knob.clientY-=iframeBox.y;
 const beforeWheelSound=await frame.locator('body').evaluate(()=>window.__sounds.length);
 await page.waitForTimeout(30);
 assert.equal(await wheel(stage,-100,knob),false);assert.equal(await slider.inputValue(),'80');
 assert.ok(await frame.locator('body').evaluate(()=>window.__sounds.length)>beforeWheelSound,'knob wheel must synthesize a detent');
 assert.equal(await wheel(stage,100,knob),false);assert.equal(await slider.inputValue(),'75');
 assert.equal(await wheel(stage,100,{clientX:0,clientY:0}),true);assert.equal(await slider.inputValue(),'75');
 await page.emulateMedia({reducedMotion:'reduce'});await page.waitForTimeout(100);
 const reducedImage=await canvas.screenshot();
 await page.mouse.move(stageBox.x+stageBox.width*.9,stageBox.y+stageBox.height*.1);await page.waitForTimeout(200);
 assert.ok(reducedImage.equals(await canvas.screenshot()),'reduced motion must freeze the playing sculpture');
 await page.emulateMedia({reducedMotion:'no-preference'});
 await frame.locator('#detail-toggle').tap();assert.equal(await frame.locator('#station-detail').isVisible(),true);
 await frame.locator('#detail-toggle').tap();
 // A GPU reset leaves accessible playback usable and can recover without retuning.
 await canvas.evaluate(el=>{window.__test.graphicsReset=el.getContext('webgl2').getExtension('WEBGL_lose_context');window.__test.graphicsReset.loseContext();});
 await stage.locator('.scene-fallback').waitFor();
 await frame.getByRole('button',{name:'Pause radio',exact:true}).tap();
 await frame.locator('#playback-label').filter({hasText:'PAUSED'}).waitFor();
 await canvas.evaluate(()=>window.__test.graphicsReset.restoreContext());
 await stage.locator('canvas').waitFor({state:'visible'});
 await frame.getByRole('button',{name:'Play radio',exact:true}).tap();
 // HTTP stations are explicit link-outs, and cannot silently create/replace MEDIA sessions.
 await frame.locator('#search').fill('http');await frame.locator('#search-submit').tap();
 const sessionsBefore=await frame.locator('body').evaluate(()=>window.__test.calls.filter(c=>c.type==='create').length);
 await frame.getByRole('button',{name:'External stream options for HTTP Radio',exact:true}).tap();
 const external=frame.locator('#results .station-fallback');
 assert.match(await external.textContent(),/uses HTTP/);
 assert.equal(await frame.locator('body').evaluate(()=>window.__test.calls.filter(c=>c.type==='create').length),sessionsBefore);
 assert.equal(await frame.locator('body').evaluate(()=>window.__test.calls.filter(c=>c.type==='open').length),0,'selection alone must not open a link');
 await frame.locator('body').evaluate(()=>{window.__test.linkStatus='denied';});
 await external.getByRole('button',{name:'Open HTTP Radio in Wavefunc',exact:true}).tap();
 await external.getByRole('status').filter({hasText:'did not open'}).waitFor();
 assert.equal(await external.locator('input').inputValue(),'http://example.org:8000/live');
 assert.equal(await frame.locator('#playback-label').textContent(),'ON AIR','denial keeps the existing station playing');
 await frame.locator('body').evaluate(()=>{window.__test.linkError=true;});
 await external.getByRole('button',{name:'Open HTTP Radio in Wavefunc',exact:true}).tap();
 await external.getByRole('status').filter({hasText:'Could not open'}).waitFor();
 await frame.locator('body').evaluate(()=>{window.__test.linkError=false;window.__test.linkStatus='opened';});
 await external.getByRole('button',{name:'Open HTTP Radio in Wavefunc',exact:true}).tap();
 await external.getByRole('status').filter({hasText:'Opened in Wavefunc'}).waitFor();
 await frame.locator('#playback-label').filter({hasText:'PAUSED'}).waitFor();
 assert.equal(await frame.locator('body').evaluate(()=>window.__test.calls.filter(c=>c.type==='open').at(-1).url),`https://wavefunc.live/station/${'4'.padEnd(64,'0')}`);
 // A delayed external handoff cannot pause a newer station selected meanwhile.
 await frame.getByRole('button',{name:'Play radio',exact:true}).tap();
 await frame.locator('body').evaluate(()=>{window.__test.deferLink=true;});
 await external.getByRole('button',{name:'Open HTTP Radio in Wavefunc',exact:true}).tap();
 await frame.locator('#search').fill('');await frame.locator('#search-submit').tap();
 await frame.getByRole('button',{name:'Listen to Second Radio',exact:true}).tap();
 await frame.locator('#playback-label').filter({hasText:'ON AIR'}).waitFor();
 await frame.locator('body').evaluate(()=>{window.__test.finishLink();window.__test.deferLink=false;});
 await page.waitForTimeout(100);
 assert.equal(await frame.locator('#playback-label').textContent(),'ON AIR','stale external handoff must not pause the new session');
 await frame.locator('#search').fill('mixed');await frame.locator('#search-submit').tap();
 await frame.getByRole('button',{name:'Listen to Mixed Radio',exact:true}).tap();
 await frame.locator('#playback-label').filter({hasText:'ON AIR'}).waitFor();
 assert.equal(await frame.locator('body').evaluate(()=>window.__test.calls.filter(c=>c.type==='create').at(-1).options.source.url),'https://example.org/live.mp3');
 await frame.locator('#search').fill('');await frame.locator('#search-submit').tap();
 await frame.getByRole('button',{name:'Listen to Test Radio',exact:true}).tap();
 await frame.locator('#playback-label').filter({hasText:'ON AIR'}).waitFor();
 await frame.locator('body').evaluate(()=>{window.__test.failMetadata=true;});
 await frame.getByRole('button',{name:'Listen to Second Radio',exact:true}).tap();
 await frame.locator('#player-subtitle').filter({hasText:'Now playing unavailable'}).waitFor();
 const failedCalls=await frame.locator('body').evaluate(()=>window.__test.calls.filter(c=>c.type==='cvm').length);
 await frame.locator('#volume').fill('35');await frame.locator('#volume').dispatchEvent('input');
 await page.waitForTimeout(100);
 assert.equal(await frame.locator('body').evaluate(()=>window.__test.calls.filter(c=>c.type==='cvm').length),failedCalls,'denied metadata must not re-prompt on state updates');
 await frame.locator('body').evaluate(()=>{window.__test.failMetadata=false;});
 await frame.getByRole('button',{name:'Pause radio',exact:true}).tap();
 await frame.locator('#playback-label').filter({hasText:'PAUSED'}).waitFor();
 await frame.locator('#favourites-tab').tap();
 await frame.locator('#favourite-list option').waitFor({state:'attached'});
 assert.match(await frame.locator('#favourite-list').innerText(),/Evening listening/);
 await frame.getByRole('button',{name:'Listen to Test Radio',exact:true}).waitFor();
 assert.equal(await frame.locator('.station-row').count(),1);
 await frame.locator('body').evaluate(()=>{window.__test.missingStations=true;});
 await frame.locator('#refresh-favourites').tap();
 await frame.getByRole('heading',{name:'Saved stations are unavailable.'}).waitFor();
 assert.match(await frame.locator('#results').innerText(),/Your list loaded/);
 assert.match(await frame.locator('#result-count').innerText(),/0 \/ 1 STATIONS/);
 await frame.locator('body').evaluate(()=>window.__test.account(''));
 await frame.getByRole('heading',{name:'Bring your favourites along.'}).waitFor();
 assert.equal(await frame.locator('.station-row').count(),0);
 await frame.locator('#explore-tab').tap();
 await frame.locator('.station-row').first().waitFor();
 await frame.locator('#search').fill('slow');await frame.locator('#search-submit').tap();
 await frame.locator('#search').fill('fast');await frame.locator('#search-submit').tap();
 await frame.getByRole('button',{name:'Listen to fast',exact:true}).waitFor();
 await page.waitForTimeout(250);
 assert.equal(await frame.locator('.station-text strong').textContent(),'fast','stale search must not replace newer result');
 await frame.locator('#search').fill('Enter search');await frame.locator('#search').press('Enter');
 await frame.getByRole('button',{name:'Listen to Enter search',exact:true}).waitFor();
 await frame.locator('body').evaluate(()=>{window.__test.fail=true;});
 await frame.locator('#search-submit').tap();
 await frame.getByRole('heading',{name:'The signal did not come through.'}).waitFor();
 await frame.locator('body').evaluate(()=>{window.__test.fail=false;window.__test.partial=true;});
 await frame.locator('#retry').tap();
 await frame.locator('#connection-status').filter({hasText:'incomplete'}).waitFor();
 assert.equal(await frame.locator('.station-row').count(),1,'partial relay errors must keep usable search results visible');
 await frame.locator('body').evaluate(()=>{window.__test.partial=false;});
 await frame.locator('#search').fill('');await frame.locator('#search-submit').tap();
 await frame.locator('.station-row').first().waitFor();
 const sizes=[[200,160],[320,560],[900,600],[1440,900],[390,844],[360,640],[844,390]];
 const measurements=[];
 for(const theme of ['light','dark']) {
   await page.emulateMedia({colorScheme:theme});
   for(const [width,height] of sizes) {
     await page.setViewportSize({width:Math.max(width+20,300),height:height+20});
     await page.locator('iframe').evaluate((el,size)=>{el.style.width=`${size[0]}px`;el.style.height=`${size[1]}px`;},[width,height]);
     const measured=await frame.locator('body').evaluate(()=>({width:innerWidth,height:innerHeight,overflow:document.documentElement.scrollWidth>innerWidth,color:getComputedStyle(document.body).color,bg:getComputedStyle(document.documentElement).backgroundColor}));
     assert.equal(measured.width,width); assert.equal(measured.height,height);assert.equal(measured.overflow,false);
     assert.equal(measured.bg,'rgb(254, 249, 234)');
     if(width===200){assert.equal(await frame.locator('.minimum').isVisible(),true);assert.equal(await frame.locator('#mini-play').isVisible(),true);}
     else { assert.equal(await frame.locator('#play-toggle').isVisible(),true);const box=await frame.locator('#play-toggle').boundingBox();assert.ok(box.height>=44); }
     await page.locator('iframe').screenshot({path:`.napplet-space/testing/fixture-${width}x${height}-${theme}.png`});
     measurements.push({theme,...measured});
   }
 }
 // Simulate host storage surviving a fresh iframe, then resume the exact station and volume.
 await page.setViewportSize({width:1100,height:800});
 await page.locator('iframe').evaluate(el=>{el.style.width='900px';el.style.height='600px';});
 await frame.locator('#search').fill('');await frame.locator('#search-submit').tap();
 await frame.getByRole('button',{name:'Listen to Second Radio',exact:true}).tap();
 await frame.locator('#playback-label').filter({hasText:'ON AIR'}).waitFor();
 await frame.locator('#volume').fill('35');await frame.locator('#volume').dispatchEvent('input');
 const saved=await frame.locator('body').evaluate(()=>Object.fromEntries(window.__test.calls.filter(c=>c.type==='storage').map(c=>[c.key,c.value])));
 assert.match(saved['receiver-station'],/:2$/);assert.equal(saved['receiver-volume'],'0.35');
 const seed=`<script>window.__savedReceiver=${JSON.stringify(saved)};<\/script>`;
 await page.locator('iframe').evaluate((el,source)=>el.srcdoc=source,html.replace('<head>','<head>'+seed+fixtureScript));
 await frame.locator('#playback-label').filter({hasText:'ON AIR'}).waitFor();
 assert.equal(await frame.locator('#volume').inputValue(),'35');
 assert.equal(await frame.locator('body').evaluate(()=>window.__test.calls.find(c=>c.type==='create').options.source.url),'https://example.org/2.mp3');
 // Fresh load without every optional domain; required station/player work remains usable.
 const optionalRemoval='<script>delete window.napplet.storage;delete window.napplet.identity;delete window.napplet.cvm;delete window.napplet.resource;delete window.napplet.config;delete window.napplet.theme;delete window.napplet.link;<\/script>';
 await page.locator('iframe').evaluate((el,source)=>el.srcdoc=source,html.replace('<head>','<head>'+fixtureScript+optionalRemoval));
 await frame.locator('.station-row').first().waitFor();
 assert.equal(await frame.locator('#settings').isVisible(),false);
 await frame.locator('#search').fill('http');await frame.locator('#search-submit').tap();
 await frame.getByRole('button',{name:'External stream options for HTTP Radio',exact:true}).tap();
 const noLink=frame.locator('#results .station-fallback');
 assert.match(await noLink.textContent(),/cannot open external links/);
 assert.equal(await noLink.locator('input').inputValue(),'http://example.org:8000/live');
 assert.equal(await noLink.locator('button').isVisible(),false);

 await frame.locator('#search-mode').selectOption('artists');
 await frame.getByRole('heading',{name:'Music search is unavailable.'}).waitFor();
 await frame.locator('#favourites-tab').tap();
 await frame.getByRole('heading',{name:'Bring your favourites along.'}).waitFor();
 assert.match(await frame.locator('#results').innerText(),/does not share an identity/);
 // A device without WebGL still gets a complete radio, with no uncaught boot error.
 const noGraphics='<script>const nativeContext=HTMLCanvasElement.prototype.getContext;HTMLCanvasElement.prototype.getContext=function(kind,...args){return kind.startsWith("webgl")?null:nativeContext.call(this,kind,...args);};<\/script>';
 await page.locator('iframe').evaluate((el,source)=>el.srcdoc=source,html.replace('<head>','<head>'+fixtureScript+noGraphics));
 await frame.getByRole('button',{name:'Listen to Test Radio',exact:true}).tap();
 await frame.locator('#playback-label').filter({hasText:'ON AIR'}).waitFor();
 assert.equal(await stage.getAttribute('data-renderer'),'fallback');
 assert.equal(await stage.locator('.scene-fallback').isVisible(),true);
 await frame.getByRole('button',{name:'Pause radio',exact:true}).tap();
 await frame.locator('#playback-label').filter({hasText:'PAUSED'}).waitFor();
 assert.deepEqual(errors,[]);
 console.log(JSON.stringify({passed:true,checks:['HTTP link-outs, denied/failed opens, optional LINK absence and HTTPS preference','WebGL mesh play and dial controls','independent cube and camera motion / reduced motion','context loss and recovery','no-WebGL fallback playback','station information toggle','touch playback','now playing and denial without repeated prompts','read-only favourites','identity change','stale search','click and Enter search','errors and incomplete reads','optional domains absent'],frames:measurements},null,2));
} finally {await browser.close();}