fix(centralui): Topology inline area rename commits the typed name on Enter (Gitea #38)

The rename input bound _renameBuffer with the default onchange trigger. A
browser fires change only after the Enter keydown (or on blur, which cancels
by design), so CommitRename always saw the original name and silently
no-oped: a UI rename never persisted. Present since the page was written
(pre-dates 513fa1aa); the Playwright test masked it by dispatching a
synthetic change event before Enter. Bind on oninput instead.

Tests: bUnit InlineRename_TypeThenEnter_PersistsNewName (fails on the old
binding); the Playwright rename test now types + Enter like an operator.
Also fix the Playwright OpenStableTopologyAsync race: it counted 'Load
instances' loaders immediately after clicking Expand all, before the
circuit re-render landed, and returned an unexpanded tree; it now waits
for no collapsed branch first.
This commit is contained in:
Joseph Doherty
2026-10-22 13:45:56 -04:00
parent 52a1e1b614
commit 560adefb0c
3 changed files with 41 additions and 6 deletions
@@ -139,13 +139,10 @@ public class TopologyAreaTests : IClassFixture<DeploymentFixture>
var input = page.Locator($"input[aria-label='Rename {original}']");
await Assertions.Expect(input).ToBeVisibleAsync();
// Type then Enter, exactly as an operator does. The input binds per keystroke
// (`oninput`, Gitea #38) — this test used to dispatch a synthetic `change` event
// first, which masked the bug that a real Enter never committed the typed name.
await input.FillAsync(renamed);
// The input is @bind="_renameBuffer" with the default onchange trigger, which a
// raw Enter keystroke does NOT fire (onchange only fires on blur in the browser) —
// and blur CANCELS the rename. So dispatch a `change` event to flush the bound
// value to the server WITHOUT losing focus, then press Enter to run CommitRename
// against the now-updated _renameBuffer.
await input.DispatchEventAsync("change");
await input.PressAsync("Enter"); // commit WITHOUT blurring (blur would cancel)
await Assertions.Expect(page.Locator(".toast")).ToHaveCountAsync(1, new() { Timeout = 15_000 });
@@ -321,9 +318,15 @@ public class TopologyAreaTests : IClassFixture<DeploymentFixture>
if (await live.IsCheckedAsync()) await live.UncheckAsync(); // stop the 15s tree reload
// Lazy topology loads a bounded first page per branch. These small authoring
// fixtures need their sites/areas loaded before addressing labels or instances.
// "Expand all" re-renders over the SignalR circuit, so the click returns before the
// newly-expanded branches (and their "Load instances" loaders) exist. Wait until no
// collapsed branch remains before counting loaders — counting immediately raced the
// render and returned an unexpanded tree whenever the round-trip lost.
var collapsed = page.Locator("li[role='treeitem'][aria-expanded='false']");
for (var branch = 0; branch < 300; branch++)
{
await page.Locator("button[aria-label='Expand all areas']").ClickAsync();
await Assertions.Expect(collapsed).ToHaveCountAsync(0);
var load = page.GetByRole(AriaRole.Button, new() { Name = "Load instances", Exact = true });
if (await load.CountAsync() == 0) return page;
var pages = page.GetByText("Page 1", new() { Exact = true });