Skip to content

[release/17.0] Dashboard polish: icon colors, sidebar and picker fixes, trace log previews, test flakes - #22232

Merged
Maddy Montaquila (maddymontaquila) merged 12 commits into
release/17.0from
maddymontaquila/dashboard-17-polish
Nov 13, 2026
Merged

Maddy Montaquila (maddymontaquila) merged 12 commits into
release/17.0from
maddymontaquila/dashboard-17-polish

Conversation

@maddymontaquila

@maddymontaquila Maddy Montaquila (maddymontaquila) commented Nov 13, 2026 •

Copy link
Copy Markdown
Collaborator

The remaining dashboard work for release/17.0, as one squashable PR: one commit per change, in this order.

Backported from main (cherry-picked with -x):

  1. Fix flaky terminal WebSocket close handshake abort (ProducerDisconnect test) #21500: Fix flaky terminal WebSocket close handshake abort (test only)
  2. Fix bUnit JS timeout flakes in StructuredLogs/Traces/TraceDetails tests #21503: Fix bUnit JS timeout flakes in StructuredLogs/Traces/TraceDetails tests (test only)
  3. Fix flaky TerminalWebSocketTests final-output drain: deliver queued producer output before ending #21471: Fix flaky TerminalWebSocketTests final-output drain (test only)
  4. Read resource row state at every density in dashboard browser tests #21924: Read resource row state at every density in dashboard browser tests (test only)
  5. Fix inconsistent dashboard action icon colors #22142: Fix inconsistent dashboard action icon colors. It conflicted with Make the resource table the dashboard's home page and run the list to the top #22188 in ResourcesLayout.razor: the resolution keeps the row menu on every row, parameters included, with Fix inconsistent dashboard action icon colors #22142's icon color.

From open PRs against main, applied as they stand:

  1. Fix resource sidebar hover state after selection #22228: Fix resource sidebar hover state after selection
  2. Keep resource picker popup width consistent #22229: Keep resource picker popup width consistent

New:

  1. Render the trace log preview only for the marker that's hovered or focused. Each log marker rendered its own hidden preview for the tooltip, so a span with many logs built over a million hidden elements. The page now renders one preview, filled on hover or focus. This addresses Copilot's review on [release/17.0] Combined dashboard backports (#22145, #22134, #22137, #22149, #22135, #22136, #22138, #22192, #22147) #22230.

  2. Center tag group chevrons over the state dot column. In the compact sidebar at rest, the rows' state dots form a column at the end of the list, and the group header's chevron sat 13px past it. This replaces Center tag group counts over the state dot column #21996.

  3. Fix two resource pane Playwright tests:

    • Rows_KeepDensity expected parameter rows in the expanded list to have no overflow menu, but every row has one now.
    • FilterAndGrouping could hover before the feedback prompt pushed the page down 40px, leaving the pointer on the brand. It now waits for the prompt.

When release/17.0 merges back into main, the Rows_KeepDensity change has to come along with it, because #22228's version of that test expects no menu on parameter rows.

Once this is in release/17.0, release/17.0 merges back into main. #22228 and #22229 can then close on main.

Customer Impact

Fixes and polish to the 17.0 dashboard: icon colors, the resource sidebar and picker, and trace pages with many logs.

Testing

  • Aspire.Dashboard.Components.Tests: 1,447 passed, 0 failed.
  • ResourcePaneResponsiveTests.FilterAndGrouping_* and Rows_KeepDensity*: 11 passed. FilterAndGrouping passed 5 runs in a row after the fix; before it, the 2000px cases failed in about half the runs.
  • The chevron alignment was checked in the browser at 1440px: the chevron and the dots are both centered 25px from the list's end.

Risk

Low. Dashboard-only, and the backports are unchanged apart from the one conflict described above.

Regression?

No.

🤖 Generated with Claude Code

…t test) (#21500)

## Description

Fixes an intermittent Windows failure in
`TerminalWebSocketTests.BrowserView_ProducerDisconnectClosesBrowserWhileWaitingForAcknowledgement(useGrpc:
True)`. The test fails when the browser's `CloseOutputAsync` reply
throws `WebSocketException: The remote party closed the WebSocket
connection without completing the close handshake` (inner error:
`SocketException: An existing connection was forcibly closed`).

No issue tracks this. It is a different test and a different root cause
from #21471, which fixes the producer-side final-output drain in
`BrowserView_DrainsFinalOutputAndGraphicsBeforeDisconnect`.

### Evidence

- Failure: [main push run 41074175594, Dashboard (windows-latest) job
124127031025](https://github.com/microsoft/aspire/actions/runs/41074175594/job/124127031025),
sha 82d064c.
- At 2.689s the server logged `Timed out draining the final terminal
frame` (expected, because this test never acks).
- At 5.305s it logged `Terminal close handshake failed` with
`TaskCanceledException` at the close-reply wait.
- The client's stack shows it called `CloseOutputAsync` as soon as
`ReadCloseAsync` returned, so the browser received the Close after the
server had already aborted.
- Denominators (`ci.yml` attempt 1, Oct 16–30, from a partial survey of
the window): the test failed in 1 of 56 completed Aspire.Dashboard.Tests
Windows jobs and 0 of 56 Ubuntu jobs.
- The test code and `TerminalWebSocketProxy` are unchanged between
82d064c and current main, and the test passes on later main runs. I
also queued a rerun of the failed job on the same head.

### Root cause

`TerminalWebSocketProxy.CloseAsync` sends Close and then waits for the
browser's Close reply. It uses the same 2-second `s_closeTimeout` that
bounds frame drain and sender teardown, and calls `socket.Abort()` when
that expires.

In this test the browser is an in-process `ClientWebSocket` sharing the
test host's thread pool. On a loaded Windows runner its reply can arrive
more than 2 seconds after the server sends Close; the CI log shows about
0.6s of scheduling delay even before `CloseAsync` started. The server
then resets the connection under the browser's in-flight reply.

Waiting for the reply only delays resource cleanup; the browser already
has the authoritative close status. So the short drain budget is the
wrong bound for this step.

**Deterministic repro on main (Linux):** delay the browser's Close reply
by 3 seconds after `ReadCloseAsync`. It fails with the identical
exception:

```
failed ...TerminalWebSocketTests.BrowserView_DelayedCloseReplyCompletesCloseHandshake (5s 869ms)
  WebSocketException : The remote party closed the WebSocket connection without completing the close handshake.
  ---- IOException : Unable to write data to the transport connection: Connection reset by peer.
```

### Fix

- Add a dedicated `s_closeHandshakeTimeout` (10s) for the Close reply
wait in `CloseAsync`.
- The 2-second `s_closeTimeout` still bounds the final-frame drain and
sender teardown, so user-visible close latency is unchanged.
- An unresponsive or disconnected browser is still aborted, and a socket
error ends the wait immediately.

### Validation

- New regression test
`BrowserView_DelayedCloseReplyCompletesCloseHandshake`: fails before the
fix (above) and passes after.
- `Aspire.Dashboard.Tests.Terminal` namespace: 113/113 passed.
- Full `Aspire.Dashboard.Tests`: 2583/2583 passed.

## Checklist

- Is this feature complete?
  - [x] Yes. Ready to ship.
  - [ ] No. Follow-up changes expected.
- Are you including unit tests for the changes and scenario tests if
relevant?
  - [x] Yes
  - [ ] No
- Did you add public API?
  - [ ] Yes
  - [x] No
- Does the change make any security assumptions or guarantees?
  - [ ] Yes
  - [x] No

---
**🤖 Posted by AspireAgent (AI Agent)**

Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 9a91c4f)
…ts (#21503)

## Description

Fixes an intermittent Dashboard.Components flake:
`EmptyState_HelpDisappearsAfterAnySignalAndStaysHiddenAfterClear`
(StructuredLogs/Traces), plus TraceDetails tests, fail with
`Bunit.JSRuntimeInvocationNotSetException` on slow runners (mostly
Windows).

### Root cause

In bUnit 2.x, a planned JS handler without a result leaves the
invocation pending. After `BunitContext.DefaultWaitTimeout` (10s, set in
`DashboardPageTestContext`), bUnit faults the invocation. That fault
surfaces from the component's `OnAfterRenderAsync` and is rethrown by
the next `WaitForAssertion`. So any test still running 10s after the
first render fails.

Two kinds of handlers were left without a result:

- **`focusElement`**: `StructuredLogsTests`, `TracesTests` and
`TraceDetailsTests` each call `JSInterop.SetupVoid("focusElement", _ =>
true)` without a result. They do this after `SetupFluentMenu` registered
a resolved handler. The last registration wins, so the resolved handler
is overridden. Each page awaits `focusElement(<scrollContainer>, true)`
on first render.
- **Tooltip**: `AddCommonDashboardServices` registers the Tooltip
module's `FluentTooltipInitialize` without a result.

This is the same bug class #21412 fixed for `Select.Initialize` and
`observeAttributeChange`, and #21434 fixes in `ManageDataDialogTests`.
This PR doesn't touch those files.

### Fix

Add `.SetVoidResult()` to the four handlers above. Also complete the
other shared void stubs these pages exercise: the three handlers in
`SetupFluentAnchoredRegion` (`goToNextFocusableElement`,
`initializeKeyboardNavigation`, `removeKeyboardNavigation`) and the two
in `SetupFluentOverflow` (`fluentOverflowInitialize`,
`fluentOverflowDispose`). Test-only change.

### CI evidence (attempt-1 failures, `ci.yml`, about Oct 16–31)

Survey: 1000 `ci.yml` runs (466 with Dashboard jobs).
Dashboard.Components had 10 attempt-1 failures in 181 Windows jobs and 1
in 181 Ubuntu jobs. These 3 jobs failed on pending calls this PR fixes:

| Run / job | Head | Failing tests | Pending invocation |
|---|---|---|---|
| main push
[40534267321](https://github.com/microsoft/aspire/actions/runs/40534267321/job/122317031694)
(Windows) | b1b850f | StructuredLogsTests.EmptyState… (and
MetricsTests.EmptyState… on `Select.Initialize`, fixed since by #21412)
| `focusElement("structuredLogsScrollContainer", true)` |
| PR
[40563038552](https://github.com/microsoft/aspire/actions/runs/40563038552/job/122398561156)
(Windows) and [job
122398564683](https://github.com/microsoft/aspire/actions/runs/40563038552/job/122398564683)
(Ubuntu) | f1cc41e | TraceDetailsTests.ExpandAllSpans…,
Render_TraceUpdateWithNewSpans…, ToggleCollapse… |
`focusElement("traceDetailScrollContainer", true)`,
`FluentTooltipInitialize` (plus handlers since fixed on main) |

A fourth job, PR run
[40390542875](https://github.com/microsoft/aspire/actions/runs/40390542875/job/121814090794)
(StructuredLogsTests.EmptyState… on Windows), faulted on
`observeAttributeChange`, which #21412 has since fixed. On the same head
the Ubuntu job passed. Locally, the 9 EmptyState cases passed 15/15
loops on main because they finish well under 10s. The other 6 Components
failures were the MetricsTests histogram test (tracked separately in
#21394).

### Before/after

**Slow-runner simulation (not committed).** Added `await
Task.Delay(11s)` before the final `WaitForAssertion` in the
StructuredLogs/Traces EmptyState tests:

| | EmptyState (9 cases) | Failure |
|---|---|---|
| main | **6 failed** / 9 | `JSRuntimeInvocationNotSetException`:
`focusElement("structuredLogsScrollContainer"/"tracesScrollContainer",
true)` ×5, `FluentTooltipInitialize` ×1 |
| this PR | **9 passed** / 9 | n/a |

No new tests are added. The existing render tests for these pages
already cover the first-render calls.

### Validation

- `Aspire.Dashboard.Components.Tests` full suite: 1161 passed, 0 failed.

## Checklist
- Is this feature complete?
  - [x] Yes. Ready to ship.
- Are you including unit tests for the changes and scenario tests if
relevant?
  - [x] No. Test-only fix; existing render tests cover it.
- Did you add public API?
  - [x] No
- Does the change make any security assumptions or guarantees?
  - [x] No
- Does the change require an update in our Aspire docs?
  - [x] No

**🤖 Posted by AspireAgent (AI Agent)**

---------

Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 3aba713)
…roducer output before ending (#21471)

## Description

Fixes an intermittent failure in
`TerminalWebSocketTests.BrowserView_DrainsFinalOutputAndGraphicsBeforeDisconnect`.
The browser receives `Close` while it is still waiting for the frame
titled `Final screen`.

### Evidence

Dashboard `ci.yml` first attempts, Oct 16–30 (main push and PR):
- **Ubuntu: 2 failures out of 389 completed attempts**, both `useGrpc:
True`:
- [job
122963540707](https://github.com/microsoft/aspire/actions/runs/40728167754/job/122963540707)
- [job
123253486333](https://github.com/microsoft/aspire/actions/runs/40814645838/job/123253486333)
- Windows had 0 failures of this test in 383 completed attempts.

Signature: `ReadUntilAsync` expected `Binary` but got `Close`. The
server logged no drain timeout, so the dashboard drained successfully,
but on a mirror frame that never contained the final output.

No open issue, PR or quarantine covers this test.

### Root cause (test fixture)

1. The test writes the final output, waits for
`WaitForProducerTextAsync("completed")`, then calls
`EndTerminalAsync(includeHmpExit: true)`, which disposes the producer's
`Hmp1PresentationAdapter`.
2. `WaitForProducerTextAsync` only proves that the *producer* terminal
applied the bytes. In Hex1b 0.173.0,
`Hmp1PresentationAdapter.WriteOutputAsync` only enqueues output on each
viewer's channel. A separate write pump delivers it to the transport.
3. `DisposeAsync` → `DisposeSessionAsync` completes the channel and
**discards queued, undelivered output**.
4. If the write pump hadn't flushed yet, the final lines, OSC title and
kitty graphics never reached the dashboard mirror. The mirror then
correctly drained a stale frame and closed.

Hex1b's graceful path is `TerminalCompleted(exitCode)`, which queues HMP
Exit behind every queued output ("Exit must follow every queued
output/replay write on the same pump").

### Fix

- `EndTerminalAsync(includeHmpExit: true)` now ends the producer like an
exited workload:
  1. Call `Presentation.TerminalCompleted(0)`.
2. Wait until the Exit frame has been written to every viewer transport
(or the transport closed).
  3. Then dispose.
- The producer's server-side output stream tracks HMP frame headers to
observe Exit.
- This replaces the fixture's synthetic gRPC Exit frame: the real Exit
now flows through the HMP stream ahead of `Ended`.
- `includeHmpExit: false` still models an abrupt producer end.
- No product code, assertions or timeouts changed, and there are no
sleeps or retries.

### Regression check

- Adds `slowProducerDelivery` cases. They delay each producer transport
write by 500 ms after the initial frame, so output is deterministically
still queued when the terminal ends.
- Results were collected locally on Linux (net11.0 Debug):

| | slowProducerDelivery cases (both transports) | Existing cases |
|---|---|---|
| Before (fence bypassed) | **20/20 failed** (`Expected: Binary, Actual:
Close`) | 20/20 passed |
| After | **40/40 passed** (20 runs) | 40/40 passed |

- Full `TerminalWebSocketTests` class: 16 runs (2 parallel processes ×
8), **0 failures, 67/67 tests each run**.
- `Aspire.Dashboard.Playwright.Tests` (which shares
`TerminalTestProducer.cs`) builds.

### Not addressed here

`BrowserView_ProducerDisconnectClosesBrowserWhileWaitingForAcknowledgement`
failed 3 times in 383 completed Windows first attempts (`useGrpc:
True`).

It has a different signature: the server's 2 s close handshake times out
and aborts, and the client's `CloseOutputAsync` then fails with
WSAECONNRESET. It isn't related to producer output delivery. I couldn't
attribute it deterministically to a code-level cause, so it is left out
of this PR.

## Checklist

- Is this feature complete?
  - [x] Yes. Ready to ship.
  - [ ] No. Follow-up changes expected.
- Are you including unit tests for the changes and scenario tests if
relevant?
  - [x] Yes
  - [ ] No
- Did you add public API?
  - [ ] Yes
  - [x] No
- Does the change make any security assumptions or guarantees?
  - [ ] Yes
  - [x] No

**🤖 Posted by AspireAgent (AI Agent)**

Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit cbb4f01)
…21924)

## Description

The scheduled [Outerloop Tests run on
main](https://github.com/microsoft/aspire/actions/runs/42437120205)
failed in two jobs after #21880:

- [Templates
(ubuntu-latest)](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128616874013):
all 10 `StarterTemplateWithRedisCacheTests` and
`StarterTemplateWithRedisCacheTests_Net11` tests timed out waiting for
`.resource-row-sub > .state-dot + span` in
`DashboardResourceAssertions.CheckResourcesAsync`.
- [Hosting.Testing
(ubuntu-latest)](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128616874062):
`DashboardUrlTests.DashboardDisplaysResourceUpdatesWhileAppHostIsPausedAtBreakpoint`
couldn't find the text "Dashboard test initial" in the resource row. The
row itself was there.

## Failure stats (14d, attempt 1)

`Outerloop Tests` workflow on microsoft/aspire, runs created 2026-10-25
06:28 → 2026-11-08 06:28 UTC, **attempt 1 only**. Denominator: attempt-1
jobs that ran (success or failure). Hits: the failed job's log matches
the signature. Jobs that lost their runner, which have no log, are
excluded.

| | |
|---|---|
| Signature | Templates: `.resource-row-sub > .state-dot + span`
timeout; Hosting.Testing: `Dashboard test initial` not found |
| Job | Outerloop Tests: Templates (ubuntu-latest) and Hosting.Testing
(ubuntu-latest), main |
| main pushes: hits / runs | 2 / 28 (7.14%) |
| main scheduled runs, 30d: hits / jobs | 2 / 60 (3.33%), both on
2026-11-08. Templates also failed on main every scheduled run from
2026-10-10 to 2026-11-04 with other causes |
| PR runs: hits / runs | n/a (Outerloop PR runs don't run these jobs) |
| Combined rate | 2 / 28 = **7.14%** |
| Flake check | main: failed on all 4 attempts of the run (not a flake)
|
| First / last hit | 2026-11-08 / 2026-11-08 |
| Still hitting main? | Hit on the latest scheduled main run
(2026-11-08); no main run since |
| Fix evidence | Local: default-density helper tests fail with the old
helper and pass with the new one. Outerloop dispatched on this PR's
head: Templates and Hosting.Testing (ubuntu) passed 2/2 attempts. |

Failing attempt-1 jobs, most recent first (2 of 2 log-verified hits):

| Date (UTC) | Branch | Job | Signature excerpt |
|---|---|---|---|
| [2026-11-08
02:06](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128604467928)
| **main** | Hosting.Testing (ubuntu-latest) | `GetByText("Dashboard
test initial", Exact) element(s) not found` |
| [2026-11-08
02:06](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128604468061)
| **main** | Templates (ubuntu-latest) | `.resource-row-sub > .state-dot
+ span timeout` |

**Verdict: STRONG (real break, not a flake).** The 2026-11-08 scheduled
main run failed both jobs on **all 4 attempts** with these signatures.
The prior scheduled runs (2026-11-06 and 2026-11-07) passed, and the
signature never appeared earlier in the window. Earlier Templates
failures (2026-10-27 to 2026-11-04) had other causes.

<!-- /failure-stats -->

**Why:** #21880 deliberately brought back single-line rows for
**Compact**, the dashboard's default density. A compact row shows the
state as a dot in a `.resource-row-meta-state` column, and the state's
name is in that column's `data-tooltip`. There's no `.resource-row-sub`
and no visible state text. These browser tests open the dashboard at its
default density, so they no longer found the state.
`Aspire.Dashboard.Playwright.Tests` passed because `PlaywrightTestsBase`
pins those tests to Comfortable unless they opt into the default. This
is a deterministic break, not a flake: the previous scheduled runs on
main
([42327544779](https://github.com/microsoft/aspire/actions/runs/42327544779),
before #21880) passed, and the only dashboard change in between is
#21880.

**Fix:** the tests now match the intended markup, and no assertions are
dropped:

- `DashboardResourceAssertions.GetRowStateAsync` reads a row's state at
any density. In compact rows it reads the state column's tooltip. In
other densities it reads the text after the dot. `RowState(row, state)`
finds the element that shows a given state.
- `CheckResourcesAsync` uses `GetRowStateAsync`, and keeps waiting while
a row hasn't rendered a state yet.
- `DashboardDisplaysResourceUpdatesWhileAppHostIsPausedAtBreakpoint`
waits for the initial and then the updated state with `RowState`.
-
`TemplateResourceAssertionsTests.TemplateHelper_ValidatesSidebarAndDetailsWithoutCards`
now runs at both the default (compact) density and Comfortable, and
checks `RowState` directly, so the helper is covered against the real
dashboard at the density the template tests use.

## Confidence

- Locally, the 6 `TemplateResourceAssertionsTests` pass. With the old
helper, the two new default-density cases fail with the same `Timeout
... exceeded` as the Templates job.
- [Outerloop Tests dispatched on this
branch](https://github.com/microsoft/aspire/actions/runs/42450827141) to
cover the Templates and Hosting.Testing jobs end to end. I didn't run
those two locally; they need the full template build and container
runtime.

**How we stop this recurring:** the shared helper's own test now runs at
the dashboard's default density, so a markup change to default rows
fails that test against the dashboard alone, which is fast to run
locally while changing row markup and points straight at the helper.
It's still an outerloop test, so it doesn't gate PR CI.

## Checklist

- Is this feature complete?
  - [x] Yes. Ready to ship.
  - [ ] No. Follow-up changes expected.
- Are you including unit tests for the changes and scenario tests if
relevant?
  - [x] Yes
  - [ ] No
- Did you add public API?
  - [ ] Yes
  - [x] No
- Does the change make any security assumptions or guarantees?
  - [ ] Yes
  - [x] No

**🤖 Posted by AspireAgent (AI Agent)**

Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 01d9682)
From #22228, which is still open against main (2 commits, applied as one).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From #22229, which is still open against main (2 commits, applied as one).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cused

Each log marker on the trace waterfall rendered its own hidden preview, a
title and a three-row table, for the shared tooltip to clone. A span can
have every retained log, up to 100,000 by default, so a log-heavy trace
built well over a million hidden elements, whether or not any were opened.

The page now renders one shared preview. Pointing at a marker or focusing
it fills that preview with the marker's entry, and the tooltip, which
follows its content's changes, shows it. The handler targets the preview,
so only it renders again on hover, not the whole waterfall.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 22232

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 22232"

@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new desktop assertion expects no overflow menu for parameter rows, causing tests to fail because that menu must remain available to delete parameters.

1 open issue
What changed in this PR

This PR brings dashboard fixes and polish into release/17.0, combining backports with a shared trace-log preview to reduce rendering overhead on log-heavy traces.

Changes:

  • Standardizes action icon colors and fixes sidebar action visibility and resource-picker width.
  • Replaces per-marker hidden trace previews with one preview populated on hover or focus.
  • Extends terminal close-handshake grace and updates test fixtures, JavaScript stubs, and density-aware browser assertions.
File Description
tests/​Shared/​Playwright/​DashboardResourceAssertions.cs Reads resource state across row densities.
tests/​Aspire.Hosting.Testing.Tests/​DashboardUrlTests.cs Uses density-aware state assertions.
tests/​Aspire.Dashboard.Tests/​Terminal/​TerminalWebSocketTests.cs Covers delayed delivery and close replies.
tests/​Aspire.Dashboard.Tests/​Shared/​TerminalTestProducer.cs Waits for queued output and terminal exit delivery.
tests/​Aspire.Dashboard.Tests/​Shared/​TerminalTestHost.cs Uses graceful producer completion.
tests/​Aspire.Dashboard.Playwright.Tests/​Integration/​Playwright/​TemplateResourceAssertionsTests.cs Exercises default and comfortable densities.
tests/​Aspire.Dashboard.Playwright.Tests/​Integration/​Playwright/​ResourcePaneResponsiveTests.cs Updates action visibility and keyboard checks.
tests/​Aspire.Dashboard.Components.Tests/​Shared/​FluentUISetupHelpers.cs Completes shared JavaScript void stubs.
tests/​Aspire.Dashboard.Components.Tests/​Pages/​TracesTests.cs Completes the focus stub.
tests/​Aspire.Dashboard.Components.Tests/​Pages/​TraceDetailsTests.cs Checks shared preview wiring and completes focus setup.
tests/​Aspire.Dashboard.Components.Tests/​Pages/​StructuredLogsTests.cs Completes the focus stub.
tests/​Aspire.Dashboard.Components.Tests/​Layout/​ResourcesLayoutTests.cs Checks that rendering preserves shared icon colors.
tests/​Aspire.Dashboard.Components.Tests/​Controls/​SpanLogPreviewTests.cs Verifies preview content and replacement.
src/​Aspire.Dashboard/​Terminal/​TerminalWebSocketProxy.cs Gives browser close replies a longer grace period.
src/​Aspire.Dashboard/​Components/​Pages/​TraceDetail.razor.cs Routes marker events to the shared preview.
src/​Aspire.Dashboard/​Components/​Pages/​TraceDetail.razor Replaces per-marker previews with one component.
src/​Aspire.Dashboard/​Components/​Layout/​ResourcesLayout.razor.css Fixes picker width and sidebar action visibility.
src/​Aspire.Dashboard/​Components/​Layout/​ResourcesLayout.razor Applies icon colors without mutating cached icons.
src/​Aspire.Dashboard/​Components/​Controls/​SpanLogPreview.razor.cs Stores and updates the active preview entry.
src/​Aspire.Dashboard/​Components/​Controls/​SpanLogPreview.razor Renders shared rich log-preview content.
src/​Aspire.Dashboard/​Components/​Controls/​ResourceActions.razor Uses neutral inherited action-icon colors.

Review effort: Balanced

In the compact sidebar at rest, the rows' state dots form a column at the end of
the list. The group header's chevron sat 13px past it, so center it over that
column. Replaces #21996.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The new full-canvas assertion incorrectly expects parameter overflow menus to be hidden, causing desktop theory cases to fail because those menus remain visible to expose actions such as Delete parameter.

1 open issue
1 previously missed issue

In code that hasn't changed since last review

Low severity Add browser coverage for tooltip updates from real log markers

src/​Aspire.Dashboard/​Components/​Pages/​TraceDetail.razor:280

The lazy preview now depends on these marker events updating Blazor content before or after the JavaScript tooltip opens, but that interaction is untested. TraceDetailsTests only checks that event attributes exist, SpanLogPreviewTests calls Show directly, and the existing browser tooltip test inserts synthetic content rather than using trace markers. Please add a browser regression that hovers and keyboard-focuses different real log markers and verifies that the visible shared tooltip switches to the correct entry while retaining one preview source.

Review effort: Balanced

Rows_KeepDensity still expected parameter rows in the expanded list to have no
overflow menu, but every row has one now, parameters included.

FilterAndGrouping could hover a button before the feedback prompt appeared above
the header. The prompt pushes the page down 40px, leaving the pointer over the
brand, so its tooltip showed instead. Wait for the prompt first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Live trace refreshes and tooltip scroll restoration can display a different log’s preview from the focused marker because marker identity and shared preview content are not reliably synchronized.

No open issues 🎉

2 previously missed issues

In code that hasn't changed since last review

Medium severity Key log markers to preserve identity across live refreshes

src/​Aspire.Dashboard/​Components/​Pages/​TraceDetail.razor:275

When a late-arriving log sorts before a keyboard-focused marker during a live trace refresh, Blazor can reuse the unkeyed button for a different entry without firing focus again. Because every marker now has the same data-tooltip-content, the preview can still describe the previous log while activating the button opens the new one. Key each marker by its log's InternalId to preserve its identity across refreshes.

Medium severity Refresh preview content when a tooltip reopens on scroll

src/​Aspire.Dashboard/​Components/​Pages/​TraceDetail.razor:280

Refreshing the shared preview only on mouseenter/focus leaves a stale-content path: app-tooltips.js recreates the keyboard-focused element's tooltip on scroll without refocusing it. Keyboard-focus marker A, hover marker B, move the pointer away, then scroll: the tooltip reopens on A but displays B's message and timestamp. Please populate the preview for the actual anchor whenever its tooltip opens or reopens, including scroll restoration, and add coverage for this focus → hover → scroll sequence.

1 resolved since last review

Review effort: Balanced

Pages lost their close button when the resource table became the home
page (#22188), and Escape went with it. Escape now does what the brand
does, from every page beside the docked list: it opens the table of
every resource at "/".

The key is left to whatever else uses it: text fields, the terminal,
open menus, popovers, dialogs and shown tooltips. It doesn't go home
from home itself, on narrow layouts (no table there), without the
resource service, or while home is already loading.

When a tab has keyboard focus, Escape takes focus off it, so the strip
re-created at home doesn't give the tab focus back with its keyboard
focus ring.
@github-actions

This comment has been minimized.

The overview's Stop button and its menu button drew their icons in the accent
color while their labels used the text color, so the icon read as separate from
its button. Every action icon now takes its button's text color, labeled or not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Human assessment is needed because the tests check handler registration and direct preview rendering without verifying that marker interactions populate the shared preview with the correct entry.

No open issues 🎉

Review effort: Balanced

@github-actions

Copy link
Copy Markdown
Contributor

Tests selector

14 / 101 PR test projects · 2 PR jobs, from 25 changed files.

Selected PR test projects (14 / 101)

Aspire.Dashboard.Components.Tests, Aspire.Dashboard.Tests, Aspire.Hosting.Azure.Kubernetes.Tests, Aspire.Hosting.Azure.Tests, Aspire.Hosting.Blazor.Tests, Aspire.Hosting.Dotnet.Tests, Aspire.Hosting.DotnetTool.Tests, Aspire.Hosting.JavaScript.Tests, Aspire.Hosting.Radius.Tests, Aspire.Hosting.Testing.Tests, Aspire.Hosting.Tests, Aspire.Playground.Tests, Aspire.Templates.Tests, Infrastructure.Tests

Selected PR jobs (2)

extension-e2e, native-dashboard-validation


How these were chosen — grouped by what changed

⚠️ 8 of the 14 selected test projects come from a single change — src/Aspire.Dashboard/Components/Controls/ResourceActions.razor.

🔧 src/Aspire.Dashboard/Components/Controls/ResourceActions.razor (changed source)
→ 8 via the project graph: Aspire.Hosting.Azure.Kubernetes.Tests (3 hops), Aspire.Hosting.Azure.Tests (2 hops), Aspire.Hosting.Blazor.Tests (2 hops), Aspire.Hosting.Dotnet.Tests (2 hops), Aspire.Hosting.DotnetTool.Tests (2 hops), Aspire.Hosting.JavaScript.Tests, Aspire.Hosting.Radius.Tests (2 hops), Aspire.Hosting.Tests

🧪 tests/Shared/Playwright/DashboardResourceAssertions.cs (changed test)
→ 1 directly: Aspire.Templates.Tests
→ 1 via the project graph: Aspire.Playground.Tests

📦 affected project Aspire.Dashboard
→ 1 test: Aspire.Templates.Tests

🔧 src/Aspire.Dashboard/Components/Layout/ResourcesLayout.razor.js (changed source)
→ 1 directly: Infrastructure.Tests

🧪 tests/Aspire.Dashboard.Components.Tests/Controls/SpanLogPreviewTests.cs (changed test)
→ 1 directly: Aspire.Dashboard.Components.Tests

🧪 tests/Aspire.Dashboard.Components.Tests/Layout/ResourcesLayoutHomeTests.cs (changed test)
→ 1 directly: Aspire.Dashboard.Components.Tests

🧪 tests/Aspire.Dashboard.Components.Tests/Layout/ResourcesLayoutTests.cs (changed test)
→ 1 directly: Aspire.Dashboard.Components.Tests

🧪 tests/Aspire.Dashboard.Components.Tests/Pages/StructuredLogsTests.cs (changed test)
→ 1 directly: Aspire.Dashboard.Components.Tests

🧪 tests/Aspire.Dashboard.Components.Tests/Pages/TraceDetailsTests.cs (changed test)
→ 1 directly: Aspire.Dashboard.Components.Tests

🧪 tests/Aspire.Dashboard.Components.Tests/Pages/TracesTests.cs (changed test)
→ 1 directly: Aspire.Dashboard.Components.Tests

🧪 tests/Aspire.Dashboard.Components.Tests/Shared/FluentUISetupHelpers.cs (changed test)
→ 1 directly: Aspire.Dashboard.Components.Tests

🧪 tests/Aspire.Dashboard.Tests/Shared/TerminalTestHost.cs (changed test)
→ 1 directly: Aspire.Dashboard.Tests

🧪 tests/Aspire.Dashboard.Tests/Shared/TerminalTestProducer.cs (changed test)
→ 1 directly: Aspire.Dashboard.Tests

🧪 tests/Aspire.Dashboard.Tests/Terminal/TerminalWebSocketTests.cs (changed test)
→ 1 directly: Aspire.Dashboard.Tests

🧪 tests/Aspire.Hosting.Testing.Tests/DashboardUrlTests.cs (changed test)
→ 1 directly: Aspire.Hosting.Testing.Tests

Job reasons

Job Triggered by
extension-e2e • src/Aspire.Dashboard/Components/Controls/ResourceActions.razor, src/Aspire.Dashboard/Components/Controls/SpanLogPreview.razor, src/Aspire.Dashboard/Components/Controls/SpanLogPreview.razor.cs, src/Aspire.Dashboard/Components/Layout/ResourcesLayout.razor, src/Aspire.Dashboard/Components/Layout/ResourcesLayout.razor.cs, src/Aspire.Dashboard/Components/Layout/ResourcesLayout.razor.css, src/Aspire.Dashboard/Components/Layout/ResourcesLayout.razor.js, src/Aspire.Dashboard/Components/Pages/TraceDetail.razor, src/Aspire.Dashboard/Components/Pages/TraceDetail.razor.cs, src/Aspire.Dashboard/Terminal/TerminalWebSocketProxy.cs
• affected project Aspire.Dashboard
native-dashboard-validation • tests/Shared/Playwright/DashboardResourceAssertions.cs
• affected project Aspire.Dashboard

Selection computed for commit 9703d99.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Human assessment is needed because scroll-driven tooltip reopening after keyboard focus and hover interactions can display another marker’s message, severity, and timestamp.

No open issues 🎉

Review effort: Balanced

@maddymontaquila
Maddy Montaquila (maddymontaquila) merged commit 38016d3 into release/17.0 Nov 13, 2026
162 checks passed
@microsoft-github-policy-service microsoft-github-policy-service Bot added this to the 17.0 milestone Nov 13, 2026
@aspire-repo-bot

Copy link
Copy Markdown
Contributor

✅ No documentation update needed.

Step 5 branch: excluded → base_branch_is_release, title_release_prefix (backport: title prefixed [release/17.0], base is a release branch). Docs belong to the original forward PR.

Triggered signals: dashboard_user_facing_page_changed (TraceDetail.razor/.razor.cs), new_public_type (SpanLogPreview.razor.cs).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants