test: hardened new spinner and title-latch suites against full-suite pollution

- Exported stopSharedSpinnerTicker() and wired it into InteractiveMode.stop(): a live block missed by per-component stopAnimation kept the shared 80ms interval alive as a lingering event-loop handle; the spinner suite uses it to observe a freshly armed ticker instead of one leaked by earlier files
- Title-disposal tests now save/clear/restore PI_NO_TITLE (main() in ACP/RPC mode sets it process-wide), matching the prewarm and orphan-submit precedent
This commit is contained in:
can1357
2026-08-19 03:20:50 +02:00
parent aa98ea9ed5
commit d94bdfa1bb
4 changed files with 38 additions and 3 deletions
@@ -317,6 +317,19 @@ function unregisterSpinnerBlock(block: ToolExecutionComponent): void {
}
}
/** Stop the shared spinner ticker and drop every registered live block.
* Called on interactive-mode teardown so a stray live block cannot keep the
* process-wide 80ms interval alive past shutdown (lingering event-loop
* handles pin the process; cf. `postmortem.quit`). Test files that assert on
* ticker arming also use this to start from a clean slate. */
export function stopSharedSpinnerTicker(): void {
liveSpinnerBlocks.clear();
if (sharedSpinnerTimer) {
clearInterval(sharedSpinnerTimer);
sharedSpinnerTimer = undefined;
}
}
// Stable per-instance counter so each tool execution's inline images get a
// graphics id that survives child re-creation (the image budget keys off it).
let toolExecutionInstanceSeq = 0;
@@ -166,7 +166,7 @@ import type { HookInputComponent } from "./components/hook-input";
import type { HookSelectorComponent, HookSelectorSlider } from "./components/hook-selector";
import { type PlanReviewAnnotationState, PlanReviewOverlay } from "./components/plan-review-overlay";
import { StatusLineComponent } from "./components/status-line";
import type { ToolExecutionHandle } from "./components/tool-execution";
import { stopSharedSpinnerTicker, type ToolExecutionHandle } from "./components/tool-execution";
import { TranscriptContainer } from "./components/transcript-container";
import { WelcomeComponent, type LspServerInfo as WelcomeLspServerInfo } from "./components/welcome";
import { BtwController } from "./controllers/btw-controller";
@@ -4160,6 +4160,9 @@ export class InteractiveMode implements InteractiveModeContext {
this.#stopLoadingAnimation(false);
}
this.#cleanupMicAnimation();
// Stop the shared tool-spinner ticker: a live block missed by per-component
// stopAnimation would otherwise keep an 80ms interval pinning the process.
stopSharedSpinnerTicker();
this.#liveCommandController.dispose();
this.#cancelTodoAutoClearTimer();
this.#cancelObserverUiSyncTimer();