test(apps): cover the modern MRTR leg of app-rendered elicitations

Both review findings from round two.

The routing change sits in the funnel BOTH entry points share, but only the
legacy inbound handler was driven by a test — the modern leg, where an
elicitation arrives as an `input_required` result the MRTR driver unpacks and
retries, was covered by construction alone. Adds
`mrtr_app_choose_option` (a modern MRTR tool whose *embedded* elicitation
carries `_meta.ui.resourceUri`) and drives it live over a real transport: the
app answers, the retry carries that answer through `inputResponses`, and the
server echoes it back as the tool's result. A second case cancels the tool
call while the app is up and asserts the request-scoped signal aborts; a third
runs the same tool against a modern server that never advertised the
capability and asserts the native queue takes it.

The cancellation case turned up a real gap: `tryAppElicitation` mounted an app
for an already-aborted signal (the caller cancelled during an earlier MRTR
round), leaving a modal nobody was waiting on and a promise that could never
settle. It now throws the abort straight through.

Also fixes the smoke count in the root README, which still said "two"
headless-Chromium smokes and then introduced a third.

Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-08-22 20:17:37 -04:00
commit 44010c313b

View file

@ -2953,12 +2953,12 @@ export class InspectorClient extends InspectorClientEventTarget {
// Request-scoped abort: forwards the caller's signal (MRTR cancellation)
// and is aborted by `settleAndDropPendingPeerRequests` on disconnect, so a
// rendered app cannot outlive the connection that asked for it.
// Already cancelled before we got here (the caller aborted while an earlier
// MRTR round was in flight): don't mount an app nobody is waiting on.
if (signal?.aborted) throw createPendingAbortError();
const controller = new AbortController();
const forwardAbort = () => controller.abort(signal?.reason);
if (signal) {
if (signal.aborted) forwardAbort();
else signal.addEventListener("abort", forwardAbort, { once: true });
}
signal?.addEventListener("abort", forwardAbort, { once: true });
this.activeAppElicitations.add(controller);
try {
const result = await renderer({