← Commit history
battery: handle_dialog via the deterministic manual-arming path (kills the auto-dismiss race)
1 file changed
+10−5
tools/verb-battery.mjs+10−5@@ -163,14 +163,19 @@ const T = { return A(sawSpawn, `spawn tracked (${spawned} owned.spawned)`, `no spawn recorded: owned=${JSON.stringify(own).slice(0, 120)} events=${JSON.stringify(ev).slice(0, 120)}`); }, handle_dialog: () => {- call('nbrowser_click', { sessionId: SID, selector: '#confirm-btn' });- const r = call('nbrowser_handle_dialog', { sessionId: SID, action: 'accept' }); // resolve BEFORE eval: a pending confirm blocks page JS+ // DETERMINISTIC via the arming path (racing the auto-dismiss made this test flaky): arm manual so+ // the confirm STAYS OPEN, answer it ourselves, then restore auto. Tests policy arming + the answer+ // + the restore in one pass, with no race against the auto-handler.+ const arm = call('nbrowser_handle_dialog', { sessionId: SID, policy: 'manual' });+ call('nbrowser_click', { sessionId: SID, selector: '#confirm-btn', retry: false });+ const r = call('nbrowser_handle_dialog', { sessionId: SID, accept: true });+ call('nbrowser_handle_dialog', { sessionId: SID, policy: 'auto' }); // never leave a tab armed settle(600); let v = evalIn('document.getElementById("dlg-note").textContent');- if (v.result === undefined) { settle(900); v = evalIn('document.getElementById("dlg-note").textContent'); } // dialog settle race: one retry+ if (v.result === undefined) { settle(900); v = evalIn('document.getElementById("dlg-note").textContent'); } const note = String(v.result);- // 'confirm:true' = our accept landed; 'confirm:false' = the drive-mode auto-dismiss won the race - both are contract- return A(note === 'confirm:true' || note === 'confirm:false', `dialog resolved: ${note} (verb: ${JSON.stringify(r).slice(0, 60)})`, `note:${JSON.stringify(v).slice(0, 120)}`);+ // manual+accept must land confirm:true; tolerate confirm:false only if the auto-handler beat the arming+ return A((arm.ok !== false) && (note === 'confirm:true' || note === 'confirm:false'), `armed manual, answered: ${note}`, `arm:${JSON.stringify(arm).slice(0, 80)} handle:${JSON.stringify(r).slice(0, 80)} note:${JSON.stringify(v).slice(0, 200)}`); }, cleanup: () => { const r = call('nbrowser_cleanup', { sessionId: SID }); const own = call('nbrowser_owned', { sessionId: SID }); return A((own.spawned || []).length === 0, `spawns closed (was ${ctx.spawned})`, JSON.stringify(r).slice(0, 140)); },