Files
stack/packages/webui/tests/processing-browser.test.mjs
T
rocko 9a7251403a processing: inspectable per-run stop seam for lifecycle fire
Unify halt and fire cancellation by verified run identity with durable first-owner receipts and shared deadlines. Integrate the instance control with the current host, library and tester-socket paths. Focused nine-file evidence is 62 passing tests; broad suites, mutants and review remain pending.
2026-10-21 21:04:37 -05:00

60 lines
4.2 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { startServer } from '../src/serve.mjs';
import { fixture, close } from './fixture.mjs';
import { browser } from './browser.mjs';
const GEN = '12345678-1234-4234-8234-123456789abc';
async function setup(t, readonly = false) {
const f = await fixture(); t.after(() => f.close());
const server = await startServer({ port: 0, board: f.boardURL }); t.after(() => close(server));
const b = await browser(); t.after(() => b.close());
await b.call('Page.addScriptToEvaluateOnNewDocument', { source: `(() => {
const real=fetch.bind(window);window.writes=[];window.failWrite=false;window.confirmResume=true;window.confirmed=0;
window.confirm=()=>{window.confirmed++;return window.confirmResume};
window.processing={version:1,generation:'${GEN}',halted:${readonly},quiescent:true,remaining:0,stopped:0};
window.fetch=async(url,init={})=>{
if(url==='/api/config')return new Response(JSON.stringify({send:${!readonly}}));
if(!String(url).startsWith('/api/processing'))return real(url,init);
if(init.method==='POST'){
window.writes.push({path:url,body:JSON.parse(init.body),redirect:init.redirect});
if(window.failWrite)return new Response(JSON.stringify({error:'outcome-unknown',message:'do not echo'}),{status:503});
window.processing.halted=String(url).endsWith('/halt');
}
return new Response(JSON.stringify(window.processing));
};
})();` });
await b.navigate(`http://127.0.0.1:${server.address().port}/`);
const wait = expression => b.evaluate(`(async()=>{for(let i=0;i<100;i++){if(${expression})return;await new Promise(r=>setTimeout(r,30));}throw Error(${JSON.stringify(expression)})})()`);
await wait(`document.querySelector('#processing-state')?.textContent.includes(${JSON.stringify(readonly ? 'halted' : 'enabled')})`);
return { b, wait };
}
test('processing browser: keyboard Stop, confirmed Resume, unknown outcome and persistent banner', async t => {
const { b, wait } = await setup(t);
await b.evaluate(`document.querySelector('#processing-halt').focus(),true`); await b.key('Enter');
await wait('window.writes.length===1 && !document.querySelector("#processing-banner").hidden');
assert.deepEqual(await b.evaluate('window.writes[0]'), { path: '/api/processing/halt', body: {}, redirect: 'manual' });
for (const route of ['#/tasks', '#/settings', '#/board']) {
await b.evaluate(`location.hash=${JSON.stringify(route)},true`);
assert.equal(await b.evaluate('document.querySelector("#processing-banner").hidden'), false);
}
await b.evaluate('window.confirmResume=false;document.querySelector("#processing-resume").click();true');
assert.equal(await b.evaluate('window.writes.length'), 1);
await b.evaluate('window.confirmResume=true;document.querySelector("#processing-resume").click();true');
await wait('window.writes.length===2 && document.querySelector("#processing-banner").hidden');
assert.deepEqual(await b.evaluate('window.writes[1].body'), { generation: GEN });
assert.equal(await b.evaluate('window.confirmed'), 2);
await b.evaluate('window.failWrite=true;document.querySelector("#processing-halt").click();true');
await wait('document.querySelector("#processing-receipt").textContent.includes("Outcome unknown")');
assert.equal(await b.evaluate('window.writes.length'), 3);
assert.doesNotMatch(await b.evaluate('document.querySelector("#processing-receipt").textContent'), /do not echo/);
});
test('processing browser: Q175 read-only interim keeps both writes disabled and names CLI recovery at 320px', async t => {
const { b, wait } = await setup(t, true); await b.viewport(320);
assert.equal(await b.evaluate('document.querySelector("#processing-halt").disabled'), true);
assert.equal(await b.evaluate('document.querySelector("#processing-resume").disabled'), true);
assert.match(await b.evaluate('document.querySelector("#processing-unavailable").textContent'), /mosaic halt.*mosaic resume/);
await b.evaluate('document.querySelector("#processing-halt").click();document.querySelector("#processing-resume").click();true');
assert.equal(await b.evaluate('window.writes.length'), 0);
assert.equal(await b.evaluate('document.querySelector("#processing-banner").hidden'), false);
});