feat(ui): drill-in from Notifications to Audit Log (#23 M7)
This commit is contained in:
@@ -163,6 +163,14 @@
|
||||
<td><TimestampDisplay Value="@n.CreatedAt" Format="yyyy-MM-dd HH:mm" /></td>
|
||||
<td><TimestampDisplay Value="@n.DeliveredAt" Format="yyyy-MM-dd HH:mm" NullText="—" /></td>
|
||||
<td class="text-end">
|
||||
@* Bundle D (#23 M7-T10) drill-in: NotificationId is the audit
|
||||
CorrelationId, so the link deep-links into the central Audit
|
||||
Log pre-filtered to this notification's lifecycle events. *@
|
||||
<a class="btn btn-outline-secondary btn-sm me-1"
|
||||
href="/audit/log?correlationId=@n.NotificationId"
|
||||
data-test="audit-link-@n.NotificationId">
|
||||
View audit history
|
||||
</a>
|
||||
@if (n.Status == "Parked")
|
||||
{
|
||||
<button class="btn btn-outline-success btn-sm me-1"
|
||||
@@ -174,10 +182,6 @@
|
||||
Discard
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="text-muted small">—</span>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
|
||||
@@ -177,6 +177,62 @@ public class NotificationReportPageTests : BunitContext
|
||||
Assert.Contains("outbox query backend unavailable", cut.Markup));
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Bundle D drill-in (#23 M7-T10) — every notification row carries a
|
||||
// "View audit history" link to /audit/log?correlationId={NotificationId}.
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void NotificationRow_ViewAuditHistory_Link_HasCorrectHref()
|
||||
{
|
||||
var cut = Render<NotificationReportPage>();
|
||||
|
||||
cut.WaitForAssertion(() =>
|
||||
{
|
||||
// Both rows (Parked + Delivered) must surface the link — the drill-in
|
||||
// is row-scope, not status-scope. We pin the parked row's href to the
|
||||
// canonical correlationId-deep-link shape.
|
||||
var parkedRow = cut.FindAll("tbody tr")
|
||||
.First(r => r.TextContent.Contains("Pump fault at Plant-A"));
|
||||
var link = parkedRow.QuerySelector("a[data-test^=\"audit-link-\"]");
|
||||
Assert.NotNull(link);
|
||||
Assert.Equal(
|
||||
"/audit/log?correlationId=notif-aaaaaaaa-1111",
|
||||
link!.GetAttribute("href"));
|
||||
Assert.Contains("View audit history", link.TextContent);
|
||||
|
||||
var deliveredRow = cut.FindAll("tbody tr")
|
||||
.First(r => r.TextContent.Contains("Daily summary"));
|
||||
var deliveredLink = deliveredRow.QuerySelector("a[data-test^=\"audit-link-\"]");
|
||||
Assert.NotNull(deliveredLink);
|
||||
Assert.Equal(
|
||||
"/audit/log?correlationId=notif-bbbbbbbb-2222",
|
||||
deliveredLink!.GetAttribute("href"));
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Click_NavigatesTo_AuditLog_WithCorrelationId()
|
||||
{
|
||||
// The drill-in is a plain <a href> — browser-native navigation, not a
|
||||
// Blazor onclick handler — so this test verifies the rendered anchor's
|
||||
// attributes are exactly what a browser would follow: href, role, and
|
||||
// human-visible text. (Triggering bUnit's .Click() on a bare anchor
|
||||
// raises MissingEventHandlerException because there is no onclick
|
||||
// handler to invoke; the navigation contract lives in the <a> markup.)
|
||||
var cut = Render<NotificationReportPage>();
|
||||
|
||||
cut.WaitForState(() => cut.Markup.Contains("Pump fault at Plant-A"));
|
||||
|
||||
var parkedRow = cut.FindAll("tbody tr")
|
||||
.First(r => r.TextContent.Contains("Pump fault at Plant-A"));
|
||||
var link = parkedRow.QuerySelector("a[data-test^=\"audit-link-\"]")!;
|
||||
|
||||
Assert.Equal("a", link.TagName, ignoreCase: true);
|
||||
Assert.Equal("/audit/log?correlationId=notif-aaaaaaaa-1111", link.GetAttribute("href"));
|
||||
Assert.Contains("View audit history", link.TextContent);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
|
||||
Reference in New Issue
Block a user