test(web): eliminate timeout flakiness in the web test suite (closes #1596)

Root-cause and deterministically fix the recurring 5s-timeout flakes that
surfaced under v8-instrumented, concurrent load. All fixes are test-only; no
product source changed. No test was skipped or disabled, and the per-file
coverage gate (>=90 on all four dims) still holds.

Common root cause across the modal/form/screen suites: userEvent.setup()
schedules a real setTimeout between keystrokes. Under CPU contention those
yields balloon, so multi-field interactions blew past the 5s per-test
timeout. Fix: userEvent.setup({ delay: null }) removes the per-keystroke
real-timer dependence (typing dispatches synchronously), making the suites
load-independent.

Per file:
- ServerConfigModal / ServerImportConfigModal / ServerImportJsonModal /
  ResourcesScreen / InspectorView: userEvent.setup({ delay: null }).
- PromptArgumentsForm (completions): replaced wall-clock sleeps sized just
  past the 300ms completion debounce (await setTimeout(400)) — which race
  the debounce timer under load — with awaited conditions (findBy/waitFor)
  on the real rendered outcome, plus delay:null. Negative assertions now
  lean on the component's synchronous state/timer teardown instead of a
  timed window, so they are deterministic regardless of machine speed.
- inspectorClient integration "tracks stderr logs": the child's stderr is
  piped out-of-band from the tool's JSON-RPC response, so reading the log
  synchronously after callTool raced the stderr chunk. Wrapped the assertion
  in vi.waitFor so it polls until the line lands.

Verified: web validate + 6x test:coverage (4 sequential + 2 concurrent
under load) + standalone test:integration, all green (229 files / 3237
tests per coverage run; 799 integration tests), zero intermittent failures.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S3fTN8H3R8YV4yUGvZjYnX
This commit is contained in:
cliffhall 2026-07-01 21:11:11 -04:00
commit 4bad110942

View file

@ -77,7 +77,7 @@ describe("ResourcesScreen", () => {
});
it("shows the read error alert when error and a resource is selected", async () => {
const user = userEvent.setup();
const user = userEvent.setup({ delay: null });
renderWithMantine(
<ControlledResourcesScreen
readState={{ status: "error", error: "boom" }}
@ -89,7 +89,7 @@ describe("ResourcesScreen", () => {
});
it("falls back to default error when error message is missing", async () => {
const user = userEvent.setup();
const user = userEvent.setup({ delay: null });
renderWithMantine(
<ControlledResourcesScreen readState={{ status: "error" }} />,
);
@ -98,7 +98,7 @@ describe("ResourcesScreen", () => {
});
it("shows the loading state when reading", async () => {
const user = userEvent.setup();
const user = userEvent.setup({ delay: null });
renderWithMantine(
<ControlledResourcesScreen readState={{ status: "pending" }} />,
);
@ -107,7 +107,7 @@ describe("ResourcesScreen", () => {
});
it("renders the preview panel when readState has a result", async () => {
const user = userEvent.setup();
const user = userEvent.setup({ delay: null });
renderWithMantine(
<ControlledResourcesScreen
readState={{
@ -139,7 +139,7 @@ describe("ResourcesScreen", () => {
});
it("toggles a section's open state through the lifted openSections handler", async () => {
const user = userEvent.setup();
const user = userEvent.setup({ delay: null });
renderWithMantine(<ControlledResourcesScreen />);
const urisHeader = screen.getByRole("button", { name: /URIs \(2\)/ });
// Open by default (compact=false); clicking routes through ResourcesScreen's
@ -150,7 +150,7 @@ describe("ResourcesScreen", () => {
});
it("renders the template panel when a template is selected", async () => {
const user = userEvent.setup();
const user = userEvent.setup({ delay: null });
renderWithMantine(<ControlledResourcesScreen />);
// Templates section is open by default; select the template to open its form.
await user.click(screen.getByText("files"));
@ -160,7 +160,7 @@ describe("ResourcesScreen", () => {
});
it("hides the template panel once the user reads the resource", async () => {
const user = userEvent.setup();
const user = userEvent.setup({ delay: null });
const onReadResource = vi.fn();
renderWithMantine(
<ControlledResourcesScreen onReadResource={onReadResource} />,
@ -176,7 +176,7 @@ describe("ResourcesScreen", () => {
});
it("auto-reads when a resource is clicked in the sidebar", async () => {
const user = userEvent.setup();
const user = userEvent.setup({ delay: null });
const onReadResource = vi.fn();
renderWithMantine(
<ControlledResourcesScreen onReadResource={onReadResource} />,
@ -186,7 +186,7 @@ describe("ResourcesScreen", () => {
});
it("forwards refresh and subscribe events from the preview panel", async () => {
const user = userEvent.setup();
const user = userEvent.setup({ delay: null });
const onReadResource = vi.fn();
const onSubscribeResource = vi.fn();
renderWithMantine(
@ -209,7 +209,7 @@ describe("ResourcesScreen", () => {
});
it("closing the preview returns to the originating template form", async () => {
const user = userEvent.setup();
const user = userEvent.setup({ delay: null });
const onReadResource = vi.fn();
const templates: ResourceTemplate[] = [
{ uriTemplate: "file:///{path}", name: "files" },
@ -251,7 +251,7 @@ describe("ResourcesScreen", () => {
});
it("closing the preview from the error state returns to the template form", async () => {
const user = userEvent.setup();
const user = userEvent.setup({ delay: null });
const templates: ResourceTemplate[] = [
{ uriTemplate: "demo://resource/dynamic/text/{id}", name: "Dynamic" },
];
@ -282,7 +282,7 @@ describe("ResourcesScreen", () => {
});
it("closing the preview for a plain resource returns to the empty state", async () => {
const user = userEvent.setup();
const user = userEvent.setup({ delay: null });
renderWithMantine(
<ControlledResourcesScreen
readState={{
@ -300,7 +300,7 @@ describe("ResourcesScreen", () => {
});
it("invokes onUnsubscribeResource when already subscribed", async () => {
const user = userEvent.setup();
const user = userEvent.setup({ delay: null });
const onUnsubscribeResource = vi.fn();
renderWithMantine(
<ControlledResourcesScreen
@ -319,7 +319,7 @@ describe("ResourcesScreen", () => {
});
it("routes sidebar search text through onUiChange", async () => {
const user = userEvent.setup();
const user = userEvent.setup({ delay: null });
const onUiChange = vi.fn();
renderWithMantine(<ControlledResourcesScreen onUiChange={onUiChange} />);
await user.type(screen.getByPlaceholderText("Search..."), "y.txt");
@ -329,7 +329,7 @@ describe("ResourcesScreen", () => {
});
it("renders nothing in the preview when an ok state carries no result", async () => {
const user = userEvent.setup();
const user = userEvent.setup({ delay: null });
renderWithMantine(
<ControlledResourcesScreen
// readResource is truthy (selected resource exists) but the ok
@ -348,7 +348,7 @@ describe("ResourcesScreen", () => {
});
it("threads onCompleteArgument with a ref/resource envelope from the template form", async () => {
const user = userEvent.setup();
const user = userEvent.setup({ delay: null });
const onCompleteArgument = vi
.fn<
(
@ -379,7 +379,7 @@ describe("ResourcesScreen", () => {
});
it("hides the Subscriptions section and Subscribe button when subscriptionsSupported is false", async () => {
const user = userEvent.setup();
const user = userEvent.setup({ delay: null });
renderWithMantine(
<ControlledResourcesScreen
subscriptionsSupported={false}