fix: wait for the reopened SkillsScreen layout to settle before comparing geometry

The tail of the `Long Skill Document` story collapsed and reopened the Skill
Resource accordion and then compared all four section heights against the
pre-collapse sample with an exact `toEqual`. `userEvent.click` resolves once the
event is dispatched, ahead of React committing the `openSections` update,
remounting the panel's content, and the browser running the layout pass that
redistributes height across the flex sections — so the read sampled the panes
mid-redistribution, two heights off by 23px in opposite directions and summing
to the same total.

There is no panel animation involved: the accordion sets `transitionDuration={0}`
because a height animation fights its flex sizing, and App.css restores a
transition only for the chevron transform.

Read the geometry under `waitFor` so it retries until the panes settle. The
reopened layout converges on the pre-collapse geometry rather than passing
through it, so retrying cannot mask a real regression; a tolerance would have
hidden the race instead of removing it, and would still fail on an early enough
sample.

Closes #2278

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XtdD6rzc1ddT28ZNE9wUjV
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-09-07 17:24:49 -04:00
commit ff0f5d28cd

View file

@ -1,7 +1,7 @@
import { useState } from "react";
import type { ComponentProps } from "react";
import type { Meta, StoryObj } from "@storybook/react-vite";
import { expect, fn, userEvent, within } from "storybook/test";
import { expect, fn, userEvent, waitFor, within } from "storybook/test";
import type { SkillEntry } from "@inspector/core/mcp/skillsSchemas";
import { SkillsScreen } from "./SkillsScreen";
import type { SkillsUiState } from "./SkillsScreen";
@ -428,9 +428,22 @@ export const LongSkillDocument: Story = {
detailCard.clientHeight + 1,
);
// Collapse then reopen restores the same geometry.
// Collapse then reopen restores the same geometry. There is no panel
// animation to wait out — the accordion sets `transitionDuration={0}`
// because a height animation fights its flex sizing — but the reopen is
// still not settled when the click resolves: `userEvent.click` returns once
// the event is dispatched, ahead of React committing the `openSections`
// update, remounting the panel's content, and the browser running the
// layout pass that redistributes height across the flex sections. Sampling
// once lands mid-redistribution under load and fails intermittently
// (#2278), so read the geometry under `waitFor` and let it retry until the
// panes stop moving. The reopened layout only ever converges ON `before`
// rather than passing through it, so retrying cannot mask a real
// regression.
await userEvent.click(viewerControl);
await userEvent.click(viewerControl);
await expect(geometry()).toEqual(before);
await waitFor(async () => {
await expect(geometry()).toEqual(before);
});
},
};