fix(webui): sp13 review, a drawer closed before its instances read answers keeps no answer for the next open: the answer is dropped when no pending drawer is left to repaint, so a later open reads again instead of showing a state from minutes before; test closes the drawer while its read is held, lets the read say nothing runs, reopens and sees the agent working (#1873, lane sp13-org-page-fit)

This commit was merged in pull request #1977.
This commit is contained in:
jason.woltje committed 2026-11-15 12:54:28 -06:00
1 parent 6ad1d33dd1
commit 68df55130e
2 files changed
+30 -3

No files matched your search

+6 -1
View File
@@ -1167,7 +1167,12 @@
]);
await Promise.race([ir.p, opts.page ?? ir.p]);
const pending = !ir.done;
if (pending) ir.p.then(() => { if (document.querySelector(`#bus-view [data-ws-drawer="${CSS.escape(id)}"][data-drawer-pending]`)) document.dispatchEvent(new CustomEvent('mosaic-workspace-render')); });
// A drawer closed before the read answered asks for no repaint, and its answer is not kept for a
// later open.
if (pending) ir.p.then(() => {
if (document.querySelector(`#bus-view [data-ws-drawer="${CSS.escape(id)}"][data-drawer-pending]`)) document.dispatchEvent(new CustomEvent('mosaic-workspace-render'));
else if (drawerInst === ir) drawerInst = null;
});
else if (drawerInst === ir) drawerInst = null;
const instRead = pending ? null : ir.value;
const o = orgsOf(body), orgName = o.name(org);
@@ -67,7 +67,8 @@ function fixture({ queueError = null } = {}) {
// Runs before the page's scripts. Acme's config says this Console sends there. `window.__inst` set by
// a test holds ('hold', released by window.__release()) or fails ('fail') the first bus instances read,
// which is the drawer's: it starts before the page's own reads.
// which is the drawer's: it starts before the page's own reads. With `window.__heldIdle` the held
// answer says nothing runs.
const STUB = `(() => {
const real = window.fetch.bind(window);
window.__posts = []; window.__answers = []; window.__instSeen = 0;
@@ -77,7 +78,11 @@ const STUB = `(() => {
if (/\\/api\\/orgs\\/acme\\/bus\\/instances$/.test(u) && window.__instSeen++ === 0 && window.__inst) {
if (window.__inst === 'fail') return answer(502, { error: 'read-failed', message: 'the bus did not answer' });
await new Promise(done => { window.__release = done; });
return real(url, init);
if (!window.__heldIdle) return real(url, init);
// The held answer, when a test asks, says no instance runs anything.
const v = await (await real(url, init)).json();
for (const row of [v, ...(v.rows || [])]) for (const i of Object.values(row?.instances || {})) i.running = [];
return answer(200, v);
}
const cfg = u.match(/^\\/api\\/orgs\\/([^/]+)\\/config$/);
if (cfg) { const c = await (await real(url, init)).json(); return answer(200, { ...c, chatSend: true, settingsSend: true, decide: true }); }
@@ -201,6 +206,23 @@ test('while the drawer\'s instances read is open it says the state is being read
assert.equal(await has(`${d} [data-drawer-nostop]`), false);
});
test('a drawer closed before its instances read answers keeps no answer for the next open', async t => {
const idle = 'window.__heldIdle = true;';
const { b, wait, text } = await setup(t, { hash: '#/orgs/acme/organization?agent=acme%3Acoder', inst: 'hold', scripts: [idle] });
const d = '[data-ws-drawer="acme:coder"]';
await wait(`${settled} && document.querySelector('${d}[data-drawer-pending]')`);
await b.evaluate(`document.querySelector('${d} [data-drawer-close]').click()`);
await wait(noDrawer);
// The held read answers after the drawer closed, saying coder runs nothing.
await b.evaluate('window.__release()');
await b.evaluate('new Promise(r => setTimeout(r, 300))');
// Reopened from its row, the drawer reads again and shows coder working, not the closed drawer's answer.
await b.evaluate(`document.querySelector('[data-ws-drawer-open="acme:coder"]').click()`);
await wait(`${open('acme:coder')} && !document.querySelector('${d}[data-drawer-pending]') && document.querySelector('${d} [data-drawer-state]')`);
assert.match(await text(`${d} [data-drawer-state]`), /(^|\s)working$/);
assert.doesNotMatch(await text(d), /not running/);
});
test('after the drawer\'s instances read fails it says the state was not read, and Stop says so too', async t => {
const { b, wait, text, has } = await setup(t, { hash: '#/orgs/acme/organization?agent=acme%3Areviewer', inst: 'fail' });
const d = '[data-ws-drawer="acme:reviewer"]';