docs(tests): fix CommentChecker findings across tests/ — 13,392 → 0 (repo-wide scan clean)
Comment/XML-doc changes only: - add summaries to ~10k test methods, fixtures, fakes and helpers (+ <param> on theories, <returns> on async Task methods, <inheritdoc/> on interface implementations the checker can resolve) - strip ~2.6k project-tracking references (WP*, P*-*, row-NN, Task N, #NN, Component-0NN) from comments, keeping the explanatory text; reword false positives in comments only - real docs on fakes implementing BCL/library interfaces (IDisposable, ILogger, IOptionsMonitor, IAsyncLifetime) - reword the last 5 src/ false positives (CWE-1236, failures-1, year-0001, M365) Only non-comment edits: four one-line nested classes split across lines so members can carry docs. Build 0 errors / 0 warnings.
This commit is contained in:
@@ -8,7 +8,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Covers the <c>AlarmStateBadges</c> shared component lifted out of
|
||||
/// <c>DebugView</c> (M7-T13 prep). The badge cluster must render identically
|
||||
/// <c>DebugView</c>. The badge cluster must render identically
|
||||
/// wherever it is hosted, so these tests pin the rendered markup: state badge
|
||||
/// class, kind label, native sub-state badges, severity, and HiLo level.
|
||||
/// </summary>
|
||||
@@ -32,6 +32,7 @@ public class AlarmStateBadgesTests : BunitContext
|
||||
Severity: 700),
|
||||
};
|
||||
|
||||
/// <summary>Verifies that the state, kind, sub-state, severity and level badges are rendered.</summary>
|
||||
[Fact]
|
||||
public void Renders_StateKindSubStateSeverityAndLevel_Badges()
|
||||
{
|
||||
|
||||
+23
-1
@@ -9,7 +9,7 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Components.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// bUnit tests for <see cref="AuditDrilldownDrawer"/> (#23 M7 Bundle C / M7-T4..T8).
|
||||
/// bUnit tests for <see cref="AuditDrilldownDrawer"/>.
|
||||
///
|
||||
/// The drawer is a child component opened from the Audit Log page when a grid row
|
||||
/// is clicked. It renders the offcanvas chrome (header, open/close) and delegates
|
||||
@@ -24,6 +24,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Components.Audit;
|
||||
/// </summary>
|
||||
public class AuditDrilldownDrawerTests : BunitContext
|
||||
{
|
||||
/// <summary>Initializes the test context with loose JS interop and the services the drawer needs.</summary>
|
||||
public AuditDrilldownDrawerTests()
|
||||
{
|
||||
// Default to Loose so the cURL clipboard call does not blow up tests
|
||||
@@ -70,6 +71,7 @@ public class AuditDrilldownDrawerTests : BunitContext
|
||||
Extra = extra,
|
||||
};
|
||||
|
||||
/// <summary>Verifies that the drawer renders the occurred-at UTC field.</summary>
|
||||
[Fact]
|
||||
public void Drawer_RendersField_OccurredAtUtc()
|
||||
{
|
||||
@@ -86,6 +88,7 @@ public class AuditDrilldownDrawerTests : BunitContext
|
||||
Assert.Contains("2026-05-20T12:30:45", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a JSON request summary is pretty-printed with indentation.</summary>
|
||||
[Fact]
|
||||
public void Drawer_JsonRequestSummary_PrettyPrinted_Indented()
|
||||
{
|
||||
@@ -104,6 +107,7 @@ public class AuditDrilldownDrawerTests : BunitContext
|
||||
Assert.Matches(@"\n\s+""b"":\s*""two""", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a non-JSON request summary is rendered verbatim.</summary>
|
||||
[Fact]
|
||||
public void Drawer_NonJsonRequestSummary_RenderedVerbatim()
|
||||
{
|
||||
@@ -118,6 +122,7 @@ public class AuditDrilldownDrawerTests : BunitContext
|
||||
Assert.Contains("not really json {{}", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a DbOutbound event renders its SQL block.</summary>
|
||||
[Fact]
|
||||
public void Drawer_DbOutboundChannel_RendersSqlBlock()
|
||||
{
|
||||
@@ -140,6 +145,7 @@ public class AuditDrilldownDrawerTests : BunitContext
|
||||
Assert.Contains("abc", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that an API outbound event shows the copy-as-cURL button.</summary>
|
||||
[Fact]
|
||||
public void Drawer_ApiOutbound_ShowsCopyAsCurlButton()
|
||||
{
|
||||
@@ -152,6 +158,7 @@ public class AuditDrilldownDrawerTests : BunitContext
|
||||
Assert.Contains("data-test=\"copy-as-curl\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a non-API channel event hides the copy-as-cURL button.</summary>
|
||||
[Fact]
|
||||
public void Drawer_NotApiChannel_HidesCopyAsCurlButton()
|
||||
{
|
||||
@@ -165,6 +172,7 @@ public class AuditDrilldownDrawerTests : BunitContext
|
||||
Assert.DoesNotContain("data-test=\"copy-as-curl\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a null correlation id hides the show-all button.</summary>
|
||||
[Fact]
|
||||
public void Drawer_NullCorrelationId_HidesShowAllButton()
|
||||
{
|
||||
@@ -177,6 +185,7 @@ public class AuditDrilldownDrawerTests : BunitContext
|
||||
Assert.DoesNotContain("data-test=\"show-all-events\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a redacted body shows the redaction badge.</summary>
|
||||
[Fact]
|
||||
public void Drawer_RedactedBody_ShowsRedactionBadge()
|
||||
{
|
||||
@@ -191,6 +200,7 @@ public class AuditDrilldownDrawerTests : BunitContext
|
||||
Assert.Contains("data-test=\"redaction-badge-request\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a non-redacted body hides the redaction badge.</summary>
|
||||
[Fact]
|
||||
public void Drawer_NonRedactedBody_HidesBadge()
|
||||
{
|
||||
@@ -203,6 +213,7 @@ public class AuditDrilldownDrawerTests : BunitContext
|
||||
Assert.DoesNotContain("data-test=\"redaction-badge-request\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that show-all-for-operation navigates with the correlation id in the query string.</summary>
|
||||
[Fact]
|
||||
public void ShowAllForOperation_Navigates_WithCorrelationIdQueryString()
|
||||
{
|
||||
@@ -220,6 +231,7 @@ public class AuditDrilldownDrawerTests : BunitContext
|
||||
Assert.Contains(corr.ToString(), nav.Uri);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a null execution id hides the view-this-execution button.</summary>
|
||||
[Fact]
|
||||
public void Drawer_NullExecutionId_HidesViewThisExecutionButton()
|
||||
{
|
||||
@@ -232,6 +244,7 @@ public class AuditDrilldownDrawerTests : BunitContext
|
||||
Assert.DoesNotContain("data-test=\"view-this-execution\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a non-null execution id shows the view-this-execution button.</summary>
|
||||
[Fact]
|
||||
public void Drawer_NonNullExecutionId_ShowsViewThisExecutionButton()
|
||||
{
|
||||
@@ -244,6 +257,7 @@ public class AuditDrilldownDrawerTests : BunitContext
|
||||
Assert.Contains("data-test=\"view-this-execution\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that view-this-execution navigates with the execution id in the query string.</summary>
|
||||
[Fact]
|
||||
public void ViewThisExecution_Navigates_WithExecutionIdQueryString()
|
||||
{
|
||||
@@ -260,6 +274,7 @@ public class AuditDrilldownDrawerTests : BunitContext
|
||||
Assert.Contains($"/audit/log?executionId={exec}", nav.Uri);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a null parent execution id hides the view-parent-execution button.</summary>
|
||||
[Fact]
|
||||
public void Drawer_NullParentExecutionId_HidesViewParentExecutionButton()
|
||||
{
|
||||
@@ -272,6 +287,7 @@ public class AuditDrilldownDrawerTests : BunitContext
|
||||
Assert.DoesNotContain("data-test=\"view-parent-execution\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a non-null parent execution id shows the view-parent-execution button.</summary>
|
||||
[Fact]
|
||||
public void Drawer_NonNullParentExecutionId_ShowsViewParentExecutionButton()
|
||||
{
|
||||
@@ -284,6 +300,7 @@ public class AuditDrilldownDrawerTests : BunitContext
|
||||
Assert.Contains("data-test=\"view-parent-execution\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that view-parent-execution navigates with the parent execution id in the query string.</summary>
|
||||
[Fact]
|
||||
public void ViewParentExecution_Navigates_WithExecutionIdQueryString()
|
||||
{
|
||||
@@ -303,6 +320,7 @@ public class AuditDrilldownDrawerTests : BunitContext
|
||||
Assert.Contains($"/audit/log?executionId={parent}", nav.Uri);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a null execution id hides the view-execution-chain button.</summary>
|
||||
[Fact]
|
||||
public void Drawer_NullExecutionId_HidesViewExecutionChainButton()
|
||||
{
|
||||
@@ -315,6 +333,7 @@ public class AuditDrilldownDrawerTests : BunitContext
|
||||
Assert.DoesNotContain("data-test=\"view-execution-chain\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a non-null execution id shows the view-execution-chain button.</summary>
|
||||
[Fact]
|
||||
public void Drawer_NonNullExecutionId_ShowsViewExecutionChainButton()
|
||||
{
|
||||
@@ -327,6 +346,7 @@ public class AuditDrilldownDrawerTests : BunitContext
|
||||
Assert.Contains("data-test=\"view-execution-chain\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that view-execution-chain navigates to the execution tree page.</summary>
|
||||
[Fact]
|
||||
public void ViewExecutionChain_Navigates_ToExecutionTreePage()
|
||||
{
|
||||
@@ -345,6 +365,8 @@ public class AuditDrilldownDrawerTests : BunitContext
|
||||
Assert.Contains($"/audit/execution-tree?executionId={exec}", nav.Uri);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that copy-as-cURL invokes the clipboard with the cURL command string.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task CopyAsCurl_InvokesClipboard_WithCurlString()
|
||||
{
|
||||
|
||||
+23
-2
@@ -10,8 +10,8 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Components.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// bUnit tests for <see cref="AuditEventDetail"/> — the reusable single-row
|
||||
/// detail body extracted from <see cref="AuditDrilldownDrawer"/> (Task 1 of the
|
||||
/// Execution-Tree Node Detail Modal feature).
|
||||
/// detail body extracted from <see cref="AuditDrilldownDrawer"/> for reuse by the
|
||||
/// Execution-Tree Node Detail Modal.
|
||||
///
|
||||
/// These tests render the detail component directly (not via the drawer) and
|
||||
/// pin the contract the drawer — and any future modal host — relies on:
|
||||
@@ -22,6 +22,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Components.Audit;
|
||||
/// </summary>
|
||||
public class AuditEventDetailTests : BunitContext
|
||||
{
|
||||
/// <summary>Initializes the test context with loose JS interop so unrelated tests do not fail on the clipboard call.</summary>
|
||||
public AuditEventDetailTests()
|
||||
{
|
||||
// Loose so the cURL clipboard call does not blow up tests that do not
|
||||
@@ -67,6 +68,7 @@ public class AuditEventDetailTests : BunitContext
|
||||
Extra = extra,
|
||||
};
|
||||
|
||||
/// <summary>Verifies that the read-only field block renders the audit event fields.</summary>
|
||||
[Fact]
|
||||
public void RendersFieldBlock()
|
||||
{
|
||||
@@ -81,6 +83,7 @@ public class AuditEventDetailTests : BunitContext
|
||||
Assert.Contains("2026-05-20T12:30:45", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the source node field renders between the site and instance fields.</summary>
|
||||
[Fact]
|
||||
public void RendersSourceNodeField_BetweenSiteAndInstance()
|
||||
{
|
||||
@@ -103,6 +106,7 @@ public class AuditEventDetailTests : BunitContext
|
||||
$"Expected SourceSiteId < SourceNode < SourceInstanceId; got {siteIdx}, {nodeIdx}, {instanceIdx}");
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a null source node renders as a dash.</summary>
|
||||
[Fact]
|
||||
public void RendersSourceNodeField_AsDashWhenNull()
|
||||
{
|
||||
@@ -119,6 +123,7 @@ public class AuditEventDetailTests : BunitContext
|
||||
Assert.Contains(">—<", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the error section renders when the event carries an error.</summary>
|
||||
[Fact]
|
||||
public void ErrorSection_RendersWhenErrorPresent()
|
||||
{
|
||||
@@ -134,6 +139,7 @@ public class AuditEventDetailTests : BunitContext
|
||||
Assert.Contains("stack trace here", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the error section is hidden when the event has no error.</summary>
|
||||
[Fact]
|
||||
public void ErrorSection_HiddenWhenNoError()
|
||||
{
|
||||
@@ -144,6 +150,7 @@ public class AuditEventDetailTests : BunitContext
|
||||
Assert.DoesNotContain("data-test=\"section-error\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the request section pretty-prints a JSON payload.</summary>
|
||||
[Fact]
|
||||
public void RequestSection_PrettyPrintsJson()
|
||||
{
|
||||
@@ -156,6 +163,7 @@ public class AuditEventDetailTests : BunitContext
|
||||
Assert.Matches(@"\n\s+""a"":\s*1", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the response section renders when a response payload is present.</summary>
|
||||
[Fact]
|
||||
public void ResponseSection_RendersWhenPresent()
|
||||
{
|
||||
@@ -167,6 +175,7 @@ public class AuditEventDetailTests : BunitContext
|
||||
Assert.Contains("data-test=\"response-body\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the extra section renders when extra data is present.</summary>
|
||||
[Fact]
|
||||
public void ExtraSection_RendersWhenPresent()
|
||||
{
|
||||
@@ -177,6 +186,7 @@ public class AuditEventDetailTests : BunitContext
|
||||
Assert.Contains("data-test=\"section-extra\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a redacted body shows the redaction badge.</summary>
|
||||
[Fact]
|
||||
public void RedactedBody_ShowsRedactionBadge()
|
||||
{
|
||||
@@ -187,6 +197,7 @@ public class AuditEventDetailTests : BunitContext
|
||||
Assert.Contains("data-test=\"redaction-badge-request\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a body that was not redacted hides the redaction badge.</summary>
|
||||
[Fact]
|
||||
public void NonRedactedBody_HidesRedactionBadge()
|
||||
{
|
||||
@@ -197,6 +208,7 @@ public class AuditEventDetailTests : BunitContext
|
||||
Assert.DoesNotContain("data-test=\"redaction-badge-request\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a database outbound event renders its SQL block.</summary>
|
||||
[Fact]
|
||||
public void DbOutboundChannel_RendersSqlBlock()
|
||||
{
|
||||
@@ -210,6 +222,7 @@ public class AuditEventDetailTests : BunitContext
|
||||
Assert.Contains("data-test=\"sql-parameters\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that an API channel event shows the copy-as-cURL button.</summary>
|
||||
[Fact]
|
||||
public void ApiChannel_ShowsCopyAsCurlButton()
|
||||
{
|
||||
@@ -220,6 +233,7 @@ public class AuditEventDetailTests : BunitContext
|
||||
Assert.Contains("data-test=\"copy-as-curl\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a non-API channel event hides the copy-as-cURL button.</summary>
|
||||
[Fact]
|
||||
public void NonApiChannel_HidesCopyAsCurlButton()
|
||||
{
|
||||
@@ -230,6 +244,7 @@ public class AuditEventDetailTests : BunitContext
|
||||
Assert.DoesNotContain("data-test=\"copy-as-curl\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a null correlation id hides the show-all-for-operation button.</summary>
|
||||
[Fact]
|
||||
public void NullCorrelationId_HidesShowAllButton()
|
||||
{
|
||||
@@ -240,6 +255,7 @@ public class AuditEventDetailTests : BunitContext
|
||||
Assert.DoesNotContain("data-test=\"show-all-events\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a non-null correlation id shows the show-all-for-operation button.</summary>
|
||||
[Fact]
|
||||
public void NonNullCorrelationId_ShowsShowAllButton()
|
||||
{
|
||||
@@ -250,6 +266,7 @@ public class AuditEventDetailTests : BunitContext
|
||||
Assert.Contains("data-test=\"show-all-events\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the execution buttons render only when the matching execution ids are present.</summary>
|
||||
[Fact]
|
||||
public void ExecutionButtons_ConditionalOnExecutionIds()
|
||||
{
|
||||
@@ -264,6 +281,7 @@ public class AuditEventDetailTests : BunitContext
|
||||
Assert.Contains("data-test=\"view-execution-chain\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that show-all-for-operation navigates with the correlation id in the query string.</summary>
|
||||
[Fact]
|
||||
public void ShowAllForOperation_Navigates_WithCorrelationIdQueryString()
|
||||
{
|
||||
@@ -278,6 +296,7 @@ public class AuditEventDetailTests : BunitContext
|
||||
Assert.Contains($"/audit/log?correlationId={corr}", nav.Uri);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that view-execution-chain navigates to the execution tree page.</summary>
|
||||
[Fact]
|
||||
public void ViewExecutionChain_Navigates_ToExecutionTreePage()
|
||||
{
|
||||
@@ -292,6 +311,8 @@ public class AuditEventDetailTests : BunitContext
|
||||
Assert.Contains($"/audit/execution-tree?executionId={exec}", nav.Uri);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that copy-as-cURL invokes the clipboard with the generated cURL command.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task CopyAsCurl_InvokesClipboard_WithCurlString()
|
||||
{
|
||||
|
||||
+18
-5
@@ -13,7 +13,7 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Components.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// bUnit tests for <see cref="AuditFilterBar"/> (#23 M7-T2 / Bundle B).
|
||||
/// bUnit tests for <see cref="AuditFilterBar"/>.
|
||||
///
|
||||
/// The bar carries the 10 spec filter elements plus the Errors-only toggle.
|
||||
/// Channel is a single-select <c><select data-test="filter-channel-select"></c>;
|
||||
@@ -33,6 +33,7 @@ public class AuditFilterBarTests : BunitContext
|
||||
private readonly ISiteRepository _siteRepo;
|
||||
private readonly IAuditLogQueryService _auditLogQueryService;
|
||||
|
||||
/// <summary>Initializes the bUnit context with substitute site and audit query services.</summary>
|
||||
public AuditFilterBarTests()
|
||||
{
|
||||
_siteRepo = Substitute.For<ISiteRepository>();
|
||||
@@ -44,7 +45,7 @@ public class AuditFilterBarTests : BunitContext
|
||||
}));
|
||||
Services.AddSingleton(_siteRepo);
|
||||
|
||||
// Task 15: the Node multi-select pulls its options from
|
||||
// The Node multi-select pulls its options from
|
||||
// IAuditLogQueryService.GetDistinctSourceNodesAsync. The default stub
|
||||
// returns the two central nodes the cluster uses; individual tests can
|
||||
// override via _auditLogQueryService.GetDistinctSourceNodesAsync(...).Returns(...).
|
||||
@@ -56,6 +57,7 @@ public class AuditFilterBarTests : BunitContext
|
||||
Services.AddSingleton(_auditLogQueryService);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the filter bar renders all ten filter elements plus the errors-only toggle.</summary>
|
||||
[Fact]
|
||||
public void Render_AllTenElements_Plus_ErrorsOnlyToggle_Present()
|
||||
{
|
||||
@@ -86,6 +88,7 @@ public class AuditFilterBarTests : BunitContext
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that applying the filters raises the filter-changed callback with the selected filters.</summary>
|
||||
[Fact]
|
||||
public void Apply_RaisesOnFilterChanged_WithSelectedFilters()
|
||||
{
|
||||
@@ -103,6 +106,7 @@ public class AuditFilterBarTests : BunitContext
|
||||
Assert.Equal("Plant-A-OPC", captured.Target);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that choosing a different channel replaces the previous selection because the channel is single-select.</summary>
|
||||
[Fact]
|
||||
public void ChangingChannel_ReplacesTheSelection_SingleSelect()
|
||||
{
|
||||
@@ -125,6 +129,7 @@ public class AuditFilterBarTests : BunitContext
|
||||
Assert.Null(captured!.Channels);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that selecting a channel narrows the available kind options.</summary>
|
||||
[Fact]
|
||||
public void Channel_Narrows_Kind_Options_When_Selected()
|
||||
{
|
||||
@@ -149,6 +154,7 @@ public class AuditFilterBarTests : BunitContext
|
||||
Assert.DoesNotContain($"data-test=\"filter-kind-ms-opt-{AuditKind.InboundRequest}\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the errors-only toggle adds the failed, parked and discarded statuses when no status is selected.</summary>
|
||||
[Fact]
|
||||
public void ErrorsOnly_Toggle_Adds_FailedParkedDiscarded_ToStatus_WhenStatusIsEmpty()
|
||||
{
|
||||
@@ -161,7 +167,7 @@ public class AuditFilterBarTests : BunitContext
|
||||
cut.Find("[data-test=\"filter-apply\"]").Click();
|
||||
|
||||
Assert.NotNull(captured);
|
||||
// Task 9: Errors-only targets the full non-success set {Failed, Parked, Discarded}.
|
||||
// Errors-only targets the full non-success set {Failed, Parked, Discarded}.
|
||||
Assert.NotNull(captured!.Statuses);
|
||||
Assert.Equal(3, captured.Statuses!.Count);
|
||||
Assert.Contains(AuditStatus.Failed, captured.Statuses);
|
||||
@@ -175,10 +181,11 @@ public class AuditFilterBarTests : BunitContext
|
||||
Assert.Equal(new[] { AuditStatus.Delivered }, captured!.Statuses);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the node multi-select renders its options from the query service and maps the selection through to the filter.</summary>
|
||||
[Fact]
|
||||
public void NodeMultiSelect_RendersOptions_FromQueryService_AndMapsThroughToFilter()
|
||||
{
|
||||
// Task 15: the Node filter pulls its option set from
|
||||
// The Node filter pulls its option set from
|
||||
// IAuditLogQueryService.GetDistinctSourceNodesAsync and threads the
|
||||
// chip selection into AuditLogQueryFilter.SourceNodes.
|
||||
AuditLogQueryFilter? captured = null;
|
||||
@@ -199,10 +206,11 @@ public class AuditFilterBarTests : BunitContext
|
||||
Assert.Equal(new[] { "central-a" }, captured.SourceNodes);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that applying with several status chips selected passes every selected status to the filter.</summary>
|
||||
[Fact]
|
||||
public void Apply_WithMultipleStatusChips_PassesAllSelectedStatuses()
|
||||
{
|
||||
// Task 9: multiple explicit Status chips all reach the filter — and they
|
||||
// Multiple explicit Status chips all reach the filter — and they
|
||||
// win over the Errors-only default.
|
||||
AuditLogQueryFilter? captured = null;
|
||||
var cut = Render<AuditFilterBar>(p => p
|
||||
@@ -219,6 +227,7 @@ public class AuditFilterBarTests : BunitContext
|
||||
Assert.Contains(AuditStatus.Failed, captured.Statuses);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a pasted execution id is mapped through to the filter on apply.</summary>
|
||||
[Fact]
|
||||
public void Apply_WithPastedExecutionId_MapsThroughToFilter()
|
||||
{
|
||||
@@ -236,6 +245,7 @@ public class AuditFilterBarTests : BunitContext
|
||||
Assert.Equal(executionId, captured!.ExecutionId);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a blank or unparseable execution id leaves the filter execution id null.</summary>
|
||||
[Fact]
|
||||
public void Apply_WithBlankOrUnparseableExecutionId_LeavesFilterExecutionIdNull()
|
||||
{
|
||||
@@ -255,6 +265,7 @@ public class AuditFilterBarTests : BunitContext
|
||||
Assert.Null(captured!.ExecutionId);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a pasted parent execution id is mapped through to the filter on apply.</summary>
|
||||
[Fact]
|
||||
public void Apply_WithPastedParentExecutionId_MapsThroughToFilter()
|
||||
{
|
||||
@@ -272,6 +283,7 @@ public class AuditFilterBarTests : BunitContext
|
||||
Assert.Equal(parentExecutionId, captured!.ParentExecutionId);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a blank or unparseable parent execution id leaves the filter parent execution id null.</summary>
|
||||
[Fact]
|
||||
public void Apply_WithBlankOrUnparseableParentExecutionId_LeavesFilterParentExecutionIdNull()
|
||||
{
|
||||
@@ -291,6 +303,7 @@ public class AuditFilterBarTests : BunitContext
|
||||
Assert.Null(captured!.ParentExecutionId);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that choosing the last-hour time range sets the from-UTC value to approximately one hour ago.</summary>
|
||||
[Fact]
|
||||
public void TimeRange_LastHour_PopulatesFromUtc_ApproxOneHourAgo()
|
||||
{
|
||||
|
||||
+23
-3
@@ -10,7 +10,7 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Components.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// bUnit tests for <see cref="AuditResultsGrid"/> (#23 M7-T3 / Bundle B). The grid
|
||||
/// bUnit tests for <see cref="AuditResultsGrid"/>. The grid
|
||||
/// renders 10 columns, paginates via keyset (passing the last row's
|
||||
/// (OccurredAtUtc, EventId) back to the service), raises a row-click callback
|
||||
/// that Bundle C wires to the drilldown drawer, and styles non-success status
|
||||
@@ -39,6 +39,7 @@ public class AuditResultsGridTests : BunitContext
|
||||
ErrorMessage = status == AuditStatus.Failed ? "boom — unreachable" : null,
|
||||
};
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="AuditResultsGridTests"/> class.</summary>
|
||||
public AuditResultsGridTests()
|
||||
{
|
||||
_service = Substitute.For<IAuditLogQueryService>();
|
||||
@@ -62,6 +63,7 @@ public class AuditResultsGridTests : BunitContext
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>Verifies the case: render, ten Columns, from Stub Service.</summary>
|
||||
[Fact]
|
||||
public void Render_TenColumns_FromStubService()
|
||||
{
|
||||
@@ -84,6 +86,7 @@ public class AuditResultsGridTests : BunitContext
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies the case: click, next Page, calls Service, with Cursor, of Last Row.</summary>
|
||||
[Fact]
|
||||
public void Click_NextPage_CallsService_WithCursor_OfLastRow()
|
||||
{
|
||||
@@ -105,6 +108,7 @@ public class AuditResultsGridTests : BunitContext
|
||||
Assert.Equal(second.EventId, nextCall.Paging.AfterEventId);
|
||||
}
|
||||
|
||||
/// <summary>Verifies the case: click, row, raises On Row Selected.</summary>
|
||||
[Fact]
|
||||
public void Click_Row_RaisesOnRowSelected()
|
||||
{
|
||||
@@ -122,10 +126,11 @@ public class AuditResultsGridTests : BunitContext
|
||||
Assert.Equal(target.EventId, captured!.EventId);
|
||||
}
|
||||
|
||||
/// <summary>Verifies the case: render, includes Node Column, between Site And Channel.</summary>
|
||||
[Fact]
|
||||
public void Render_IncludesNodeColumn_BetweenSiteAndChannel()
|
||||
{
|
||||
// Task 15: the grid surfaces SourceNode in a dedicated "Node" column
|
||||
// The grid surfaces SourceNode in a dedicated "Node" column
|
||||
// positioned between Site and Channel.
|
||||
StubPage(new List<AuditEventView>
|
||||
{
|
||||
@@ -144,6 +149,7 @@ public class AuditResultsGridTests : BunitContext
|
||||
Assert.True(nodeIdx < channelIdx, "Node column must precede Channel.");
|
||||
}
|
||||
|
||||
/// <summary>Verifies the case: render, includes Execution Id Column.</summary>
|
||||
[Fact]
|
||||
public void Render_IncludesExecutionIdColumn()
|
||||
{
|
||||
@@ -158,6 +164,7 @@ public class AuditResultsGridTests : BunitContext
|
||||
Assert.Contains("data-test=\"col-header-ExecutionId\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies the case: execution Id, non Null Row, renders Short Monospace Value.</summary>
|
||||
[Fact]
|
||||
public void ExecutionId_NonNullRow_RendersShortMonospaceValue()
|
||||
{
|
||||
@@ -175,6 +182,7 @@ public class AuditResultsGridTests : BunitContext
|
||||
Assert.Equal(executionId.ToString(), cell.GetAttribute("title"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies the case: execution Id, null Row, renders Blank Placeholder, no Execution Id Cell.</summary>
|
||||
[Fact]
|
||||
public void ExecutionId_NullRow_RendersBlankPlaceholder_NoExecutionIdCell()
|
||||
{
|
||||
@@ -187,6 +195,7 @@ public class AuditResultsGridTests : BunitContext
|
||||
Assert.Empty(cut.FindAll($"[data-test=\"execution-id-{row.EventId}\"]"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies the case: render, includes Parent Execution Id Column.</summary>
|
||||
[Fact]
|
||||
public void Render_IncludesParentExecutionIdColumn()
|
||||
{
|
||||
@@ -201,6 +210,7 @@ public class AuditResultsGridTests : BunitContext
|
||||
Assert.Contains("data-test=\"col-header-ParentExecutionId\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies the case: parent Execution Id, non Null Row, renders Short Monospace Value.</summary>
|
||||
[Fact]
|
||||
public void ParentExecutionId_NonNullRow_RendersShortMonospaceValue()
|
||||
{
|
||||
@@ -218,6 +228,7 @@ public class AuditResultsGridTests : BunitContext
|
||||
Assert.Equal(parentExecutionId.ToString(), cell.GetAttribute("title"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies the case: parent Execution Id, null Row, renders Blank Placeholder, no Parent Execution Id Cell.</summary>
|
||||
[Fact]
|
||||
public void ParentExecutionId_NullRow_RendersBlankPlaceholder_NoParentExecutionIdCell()
|
||||
{
|
||||
@@ -230,6 +241,7 @@ public class AuditResultsGridTests : BunitContext
|
||||
Assert.Empty(cut.FindAll($"[data-test=\"parent-execution-id-{row.EventId}\"]"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies the case: status, failed Row, has Error Badge Class.</summary>
|
||||
[Fact]
|
||||
public void Status_FailedRow_HasErrorBadgeClass()
|
||||
{
|
||||
@@ -247,7 +259,7 @@ public class AuditResultsGridTests : BunitContext
|
||||
Assert.Contains("bg-success", deliveredBadge.GetAttribute("class") ?? string.Empty);
|
||||
}
|
||||
|
||||
// --- column resize + reorder UX (#23 follow-ups Task 10) ---------------
|
||||
// --- column resize + reorder UX ---------------
|
||||
//
|
||||
// The drag interaction itself is browser-side (audit-grid.js) and covered
|
||||
// by the Playwright suite. The bUnit tests below exercise the .NET-side
|
||||
@@ -265,6 +277,7 @@ public class AuditResultsGridTests : BunitContext
|
||||
private static int HeaderIndex(string markup, string key)
|
||||
=> markup.IndexOf($"data-col-key=\"{key}\"", StringComparison.Ordinal);
|
||||
|
||||
/// <summary>Verifies the case: headers, render Resize Handle And Drag Key, for Every Column.</summary>
|
||||
[Fact]
|
||||
public void Headers_RenderResizeHandleAndDragKey_ForEveryColumn()
|
||||
{
|
||||
@@ -280,6 +293,7 @@ public class AuditResultsGridTests : BunitContext
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies the case: column Order Parameter, drives Header Order.</summary>
|
||||
[Fact]
|
||||
public void ColumnOrderParameter_DrivesHeaderOrder()
|
||||
{
|
||||
@@ -300,6 +314,8 @@ public class AuditResultsGridTests : BunitContext
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies the case: on Column Reordered, moves Column Into Target Slot, and Persists.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task OnColumnReordered_MovesColumnIntoTargetSlot_AndPersists()
|
||||
{
|
||||
@@ -318,6 +334,8 @@ public class AuditResultsGridTests : BunitContext
|
||||
Assert.Contains("Status", (string)save.Arguments[1]!);
|
||||
}
|
||||
|
||||
/// <summary>Verifies the case: on Column Resized, below Minimum, clamps To64px, and Persists.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task OnColumnResized_BelowMinimum_ClampsTo64px_AndPersists()
|
||||
{
|
||||
@@ -335,6 +353,7 @@ public class AuditResultsGridTests : BunitContext
|
||||
i => i.Identifier == "auditGrid.save" && (string)i.Arguments[0]! == "columnWidths");
|
||||
}
|
||||
|
||||
/// <summary>Verifies the case: stored Order, with Unknown Key, degrades Gracefully.</summary>
|
||||
[Fact]
|
||||
public void StoredOrder_WithUnknownKey_DegradesGracefully()
|
||||
{
|
||||
@@ -360,6 +379,7 @@ public class AuditResultsGridTests : BunitContext
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies the case: stored Widths, for Unknown Column, are Ignored.</summary>
|
||||
[Fact]
|
||||
public void StoredWidths_ForUnknownColumn_AreIgnored()
|
||||
{
|
||||
|
||||
+15
-1
@@ -11,7 +11,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Components.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// bUnit tests for <see cref="ExecutionDetailModal"/> (Execution-Tree Node Detail
|
||||
/// Modal, Task 3). The modal opens on an execution-tree node double-click: given
|
||||
/// Modal). The modal opens on an execution-tree node double-click: given
|
||||
/// an <c>ExecutionId</c> it loads that execution's audit rows via
|
||||
/// <see cref="IAuditLogQueryService"/> and shows a list → per-row detail.
|
||||
///
|
||||
@@ -24,6 +24,7 @@ public class ExecutionDetailModalTests : BunitContext
|
||||
private readonly IAuditLogQueryService _service;
|
||||
private readonly List<(AuditLogQueryFilter Filter, AuditLogPaging? Paging)> _calls = new();
|
||||
|
||||
/// <summary>Initializes a new instance of the <c>ExecutionDetailModalTests</c> class.</summary>
|
||||
public ExecutionDetailModalTests()
|
||||
{
|
||||
_service = Substitute.For<IAuditLogQueryService>();
|
||||
@@ -66,6 +67,7 @@ public class ExecutionDetailModalTests : BunitContext
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>Verifies that closed modal renders nothing and does not query.</summary>
|
||||
[Fact]
|
||||
public void ClosedModal_RendersNothing_AndDoesNotQuery()
|
||||
{
|
||||
@@ -79,6 +81,7 @@ public class ExecutionDetailModalTests : BunitContext
|
||||
Assert.Empty(_calls);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that open transition queries by execution id with page size 100.</summary>
|
||||
[Fact]
|
||||
public void OpenTransition_QueriesByExecutionId_WithPageSize100()
|
||||
{
|
||||
@@ -101,6 +104,7 @@ public class ExecutionDetailModalTests : BunitContext
|
||||
Assert.Equal(100, _calls[0].Paging!.PageSize);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that still open non open parameter change does not requery.</summary>
|
||||
[Fact]
|
||||
public void StillOpen_NonOpenParameterChange_DoesNotRequery()
|
||||
{
|
||||
@@ -119,6 +123,7 @@ public class ExecutionDetailModalTests : BunitContext
|
||||
Assert.Single(_calls);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that multi row renders list view with one button per row.</summary>
|
||||
[Fact]
|
||||
public void MultiRow_RendersListView_WithOneButtonPerRow()
|
||||
{
|
||||
@@ -138,6 +143,7 @@ public class ExecutionDetailModalTests : BunitContext
|
||||
Assert.Empty(cut.FindAll("[data-test=\"drawer-fields\"]"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that multi row click row shows audit event detail.</summary>
|
||||
[Fact]
|
||||
public void MultiRow_ClickRow_ShowsAuditEventDetail()
|
||||
{
|
||||
@@ -158,6 +164,7 @@ public class ExecutionDetailModalTests : BunitContext
|
||||
Assert.NotNull(cut.Find("[data-test=\"execution-detail-back\"]"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that multi row back control returns to list.</summary>
|
||||
[Fact]
|
||||
public void MultiRow_BackControl_ReturnsToList()
|
||||
{
|
||||
@@ -180,6 +187,7 @@ public class ExecutionDetailModalTests : BunitContext
|
||||
Assert.Empty(cut.FindAll("[data-test=\"drawer-fields\"]"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that single row opens straight to detail no back control.</summary>
|
||||
[Fact]
|
||||
public void SingleRow_OpensStraightToDetail_NoBackControl()
|
||||
{
|
||||
@@ -197,6 +205,7 @@ public class ExecutionDetailModalTests : BunitContext
|
||||
Assert.Empty(cut.FindAll("[data-test=\"execution-detail-back\"]"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that zero row shows friendly empty state.</summary>
|
||||
[Fact]
|
||||
public void ZeroRow_ShowsFriendlyEmptyState()
|
||||
{
|
||||
@@ -211,6 +220,7 @@ public class ExecutionDetailModalTests : BunitContext
|
||||
Assert.Contains("This execution emitted no audit rows.", empty.TextContent);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that query throws shows inline error state does not rethrow.</summary>
|
||||
[Fact]
|
||||
public void QueryThrows_ShowsInlineErrorState_DoesNotRethrow()
|
||||
{
|
||||
@@ -228,6 +238,7 @@ public class ExecutionDetailModalTests : BunitContext
|
||||
Assert.Contains("db is down", error.TextContent);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that close button raises on close.</summary>
|
||||
[Fact]
|
||||
public void CloseButton_RaisesOnClose()
|
||||
{
|
||||
@@ -245,6 +256,7 @@ public class ExecutionDetailModalTests : BunitContext
|
||||
Assert.True(closed);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that backdrop click raises on close.</summary>
|
||||
[Fact]
|
||||
public void BackdropClick_RaisesOnClose()
|
||||
{
|
||||
@@ -262,6 +274,7 @@ public class ExecutionDetailModalTests : BunitContext
|
||||
Assert.True(closed);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that escape key raises on close.</summary>
|
||||
[Fact]
|
||||
public void EscapeKey_RaisesOnClose()
|
||||
{
|
||||
@@ -279,6 +292,7 @@ public class ExecutionDetailModalTests : BunitContext
|
||||
Assert.True(closed);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that header shows short execution id and row count.</summary>
|
||||
[Fact]
|
||||
public void Header_ShowsShortExecutionId_AndRowCount()
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Components.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// bUnit tests for <see cref="ExecutionTree"/> (Audit Log ParentExecutionId
|
||||
/// feature, Task 10). The component takes the FLAT
|
||||
/// feature). The component takes the FLAT
|
||||
/// <see cref="ExecutionTreeNode"/> list the repository returns, assembles it
|
||||
/// into a tree by joining <see cref="ExecutionTreeNode.ParentExecutionId"/> to a
|
||||
/// parent node's <see cref="ExecutionTreeNode.ExecutionId"/>, and renders it
|
||||
@@ -35,6 +35,7 @@ public class ExecutionTreeTests : BunitContext
|
||||
rowCount == 0 ? null : new DateTime(2026, 5, 20, 12, 0, 0, DateTimeKind.Utc),
|
||||
rowCount == 0 ? null : new DateTime(2026, 5, 20, 12, 0, 5, DateTimeKind.Utc));
|
||||
|
||||
/// <summary>Verifies that a single node renders as one tree node.</summary>
|
||||
[Fact]
|
||||
public void SingleNode_RendersOneTreeNode()
|
||||
{
|
||||
@@ -48,6 +49,7 @@ public class ExecutionTreeTests : BunitContext
|
||||
Assert.Contains($"data-test=\"tree-node-{id}\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a multi-level tree is assembled from the flat node list.</summary>
|
||||
[Fact]
|
||||
public void MultiLevel_AssemblesTree_FromFlatList()
|
||||
{
|
||||
@@ -82,6 +84,7 @@ public class ExecutionTreeTests : BunitContext
|
||||
Assert.True(childIdx < grandIdx, "child must render before grandchild");
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a stub node renders with a stub marker.</summary>
|
||||
[Fact]
|
||||
public void StubNode_RendersStubMarker()
|
||||
{
|
||||
@@ -104,6 +107,7 @@ public class ExecutionTreeTests : BunitContext
|
||||
Assert.Contains("no audited actions", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the node the user arrived from is visually highlighted.</summary>
|
||||
[Fact]
|
||||
public void ArrivedFromNode_IsVisuallyHighlighted()
|
||||
{
|
||||
@@ -128,6 +132,7 @@ public class ExecutionTreeTests : BunitContext
|
||||
Assert.DoesNotContain("execution-tree-node--current", other.GetAttribute("class") ?? string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that each node link points to the audit log filtered by that node's execution.</summary>
|
||||
[Fact]
|
||||
public void NodeLink_PointsTo_AuditLogFilteredByThatExecution()
|
||||
{
|
||||
@@ -154,6 +159,7 @@ public class ExecutionTreeTests : BunitContext
|
||||
Assert.Equal($"/audit/log?executionId={root}", rootLink.GetAttribute("href"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that an empty node list renders nothing and does not throw.</summary>
|
||||
[Fact]
|
||||
public void EmptyNodeList_RendersNothingWithoutThrowing()
|
||||
{
|
||||
@@ -164,6 +170,7 @@ public class ExecutionTreeTests : BunitContext
|
||||
Assert.DoesNotContain("data-test=\"tree-node-", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a cyclic flat list is handled without looping forever.</summary>
|
||||
[Fact]
|
||||
public void CyclicFlatList_TerminatesWithoutInfiniteLoop()
|
||||
{
|
||||
@@ -186,6 +193,7 @@ public class ExecutionTreeTests : BunitContext
|
||||
Assert.Equal(1, CountOccurrences(cut.Markup, $"data-test=\"tree-node-{b}\""));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that toggling a node collapses its child subtree and toggling again re-expands it.</summary>
|
||||
[Fact]
|
||||
public void ToggleExpand_CollapsesAndReExpandsChildSubtree()
|
||||
{
|
||||
@@ -229,6 +237,7 @@ public class ExecutionTreeTests : BunitContext
|
||||
cut.Find($"[data-test=\"tree-toggle-{root}\"]").GetAttribute("aria-expanded"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that double-clicking a node raises <c>OnNodeActivated</c> with that node's execution id.</summary>
|
||||
[Fact]
|
||||
public void DoubleClickingNode_RaisesOnNodeActivated_WithExecutionId()
|
||||
{
|
||||
@@ -255,6 +264,7 @@ public class ExecutionTreeTests : BunitContext
|
||||
Assert.Equal(root, activated);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that double-clicking a nested node bubbles the activation up to the root callback.</summary>
|
||||
[Fact]
|
||||
public void DoubleClickingNestedNode_BubblesOnNodeActivated_ToRoot()
|
||||
{
|
||||
|
||||
+6
-1
@@ -16,7 +16,7 @@ using ConnectionCertificates =
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Covers the M7-B10 (T17) connection-certificates management page: an
|
||||
/// Covers the connection-certificates management page: an
|
||||
/// Administrator-gated page that resolves a data connection's owning site, lists
|
||||
/// the site's trusted-peer / rejected certificates via
|
||||
/// <see cref="ICertManagementService.ListAsync"/>, and removes a certificate via
|
||||
@@ -31,6 +31,7 @@ public class ConnectionCertificatesTests : BunitContext
|
||||
private const int ConnectionId = 7;
|
||||
private const string SiteIdentifier = "plant-a";
|
||||
|
||||
/// <summary>Initializes the test context and registers the services the page requires.</summary>
|
||||
public ConnectionCertificatesTests()
|
||||
{
|
||||
Services.AddSingleton(_certs);
|
||||
@@ -84,6 +85,7 @@ public class ConnectionCertificatesTests : BunitContext
|
||||
return host.FindComponent<ConnectionCertificates>();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the page lists both certificates from the owning site.</summary>
|
||||
[Fact]
|
||||
public void Lists_Two_Certs_From_Owning_Site()
|
||||
{
|
||||
@@ -105,6 +107,7 @@ public class ConnectionCertificatesTests : BunitContext
|
||||
_certs.Received(1).ListAsync(SiteIdentifier, Arg.Any<CancellationToken>());
|
||||
}
|
||||
|
||||
/// <summary>Verifies that removing a certificate calls RemoveAsync with the site identifier and thumbprint.</summary>
|
||||
[Fact]
|
||||
public void Remove_Calls_RemoveAsync_With_SiteIdentifier_And_Thumbprint()
|
||||
{
|
||||
@@ -119,6 +122,7 @@ public class ConnectionCertificatesTests : BunitContext
|
||||
_certs.Received(1).RemoveAsync(SiteIdentifier, "AAAA1111", Arg.Any<CancellationToken>());
|
||||
}
|
||||
|
||||
/// <summary>Verifies that an empty certificate store shows the empty-state note.</summary>
|
||||
[Fact]
|
||||
public void Empty_Store_Shows_Empty_Note()
|
||||
{
|
||||
@@ -131,6 +135,7 @@ public class ConnectionCertificatesTests : BunitContext
|
||||
Assert.Empty(cut.FindAll("[data-test=cert-row]"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a list failure shows a load error.</summary>
|
||||
[Fact]
|
||||
public void List_Failure_Shows_Load_Error()
|
||||
{
|
||||
|
||||
+10
-1
@@ -8,7 +8,7 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Components.Health;
|
||||
|
||||
/// <summary>
|
||||
/// bUnit tests for <see cref="AuditKpiTiles"/> (#23 M7 Bundle E / M7-T13). The
|
||||
/// bUnit tests for <see cref="AuditKpiTiles"/>. The
|
||||
/// component renders three Bootstrap-card tiles — Volume, Error Rate, Backlog —
|
||||
/// from a single <see cref="AuditLogKpiSnapshot"/>. The tests pin:
|
||||
///
|
||||
@@ -25,6 +25,7 @@ public class AuditKpiTilesTests : BunitContext
|
||||
private static AuditLogKpiSnapshot MakeSnapshot(long total, long errors, long backlog) =>
|
||||
new(total, errors, backlog, new DateTime(2026, 5, 20, 10, 0, 0, DateTimeKind.Utc));
|
||||
|
||||
/// <summary>Verifies that three tiles are rendered from the snapshot.</summary>
|
||||
[Fact]
|
||||
public void Renders_ThreeTiles_FromSnapshot()
|
||||
{
|
||||
@@ -43,6 +44,7 @@ public class AuditKpiTilesTests : BunitContext
|
||||
Assert.Contains("7", cut.Markup); // backlog
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the error rate is computed from the total and error counts.</summary>
|
||||
[Fact]
|
||||
public void ErrorRate_Computed_From_Total_AndErrors()
|
||||
{
|
||||
@@ -54,6 +56,7 @@ public class AuditKpiTilesTests : BunitContext
|
||||
Assert.Contains("5.0%", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that zero events do not cause a division by zero and render a zero percent error rate.</summary>
|
||||
[Fact]
|
||||
public void ZeroEvents_DoesNotDivideByZero_RendersZeroPercent()
|
||||
{
|
||||
@@ -70,6 +73,7 @@ public class AuditKpiTilesTests : BunitContext
|
||||
Assert.Contains("data-test=\"audit-kpi-volume\"", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that an unavailable snapshot renders em dashes and an error message.</summary>
|
||||
[Fact]
|
||||
public void UnavailableSnapshot_RendersEmDashes_AndErrorMessage()
|
||||
{
|
||||
@@ -85,6 +89,7 @@ public class AuditKpiTilesTests : BunitContext
|
||||
Assert.Contains("DB connection refused", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that clicking the error rate tile navigates to the audit log filtered by failed status.</summary>
|
||||
[Fact]
|
||||
public void ErrorRateTile_Click_NavigatesToAuditLog_WithFailedStatusFilter()
|
||||
{
|
||||
@@ -102,6 +107,7 @@ public class AuditKpiTilesTests : BunitContext
|
||||
Assert.Contains("/audit/log?status=Failed", nav.Uri);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that clicking the volume tile navigates to the unfiltered audit log.</summary>
|
||||
[Fact]
|
||||
public void VolumeTile_Click_NavigatesToUnfilteredAuditLog()
|
||||
{
|
||||
@@ -117,6 +123,7 @@ public class AuditKpiTilesTests : BunitContext
|
||||
Assert.EndsWith("/audit/log", nav.Uri);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that clicking the backlog tile navigates to the audit log.</summary>
|
||||
[Fact]
|
||||
public void BacklogTile_Click_NavigatesToAuditLog()
|
||||
{
|
||||
@@ -131,6 +138,7 @@ public class AuditKpiTilesTests : BunitContext
|
||||
Assert.EndsWith("/audit/log", nav.Uri);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a nonzero error rate below ten percent gets a warning border rather than a danger border.</summary>
|
||||
[Fact]
|
||||
public void NonzeroErrorRate_GetsWarningBorder_NotDangerBelowTenPercent()
|
||||
{
|
||||
@@ -144,6 +152,7 @@ public class AuditKpiTilesTests : BunitContext
|
||||
Assert.DoesNotContain("border-danger", tile.GetAttribute("class") ?? string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a high error rate gets a danger border.</summary>
|
||||
[Fact]
|
||||
public void HighErrorRate_GetsDangerBorder()
|
||||
{
|
||||
|
||||
+11
-1
@@ -8,7 +8,7 @@ using ZB.MOM.WW.ScadaBridge.Commons.Messages.Audit;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Components.Health;
|
||||
|
||||
/// <summary>
|
||||
/// bUnit tests for <see cref="SiteCallKpiTiles"/> (Site Call Audit #22, Task 7).
|
||||
/// bUnit tests for <see cref="SiteCallKpiTiles"/> .
|
||||
/// The component renders three Bootstrap-card tiles — Buffered, Stuck, Parked —
|
||||
/// from a single <see cref="SiteCallKpiResponse"/> snapshot. The tests pin:
|
||||
///
|
||||
@@ -35,6 +35,7 @@ public class SiteCallKpiTilesTests : BunitContext
|
||||
OldestPendingAge: null,
|
||||
StuckCount: stuck);
|
||||
|
||||
/// <summary>Verifies that the component renders three tiles from a snapshot.</summary>
|
||||
[Fact]
|
||||
public void Renders_ThreeTiles_FromSnapshot()
|
||||
{
|
||||
@@ -54,6 +55,7 @@ public class SiteCallKpiTilesTests : BunitContext
|
||||
Assert.Contains(">3<", cut.Markup); // parked
|
||||
}
|
||||
|
||||
/// <summary>Verifies that an unavailable snapshot renders em dashes and the error message.</summary>
|
||||
[Fact]
|
||||
public void UnavailableSnapshot_RendersEmDashes_AndErrorMessage()
|
||||
{
|
||||
@@ -69,6 +71,7 @@ public class SiteCallKpiTilesTests : BunitContext
|
||||
Assert.Contains("site call repository unavailable", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the parked tile gets a danger border when the parked count is above zero.</summary>
|
||||
[Fact]
|
||||
public void ParkedTile_GetsDangerBorder_WhenParkedAboveZero()
|
||||
{
|
||||
@@ -80,6 +83,7 @@ public class SiteCallKpiTilesTests : BunitContext
|
||||
Assert.Contains("border-danger", tile.GetAttribute("class") ?? string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the parked tile has no danger border when the parked count is zero.</summary>
|
||||
[Fact]
|
||||
public void ParkedTile_NoDangerBorder_WhenParkedZero()
|
||||
{
|
||||
@@ -91,6 +95,7 @@ public class SiteCallKpiTilesTests : BunitContext
|
||||
Assert.DoesNotContain("border-danger", tile.GetAttribute("class") ?? string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the stuck tile gets a warning border when the stuck count is above zero.</summary>
|
||||
[Fact]
|
||||
public void StuckTile_GetsWarningBorder_WhenStuckAboveZero()
|
||||
{
|
||||
@@ -104,6 +109,7 @@ public class SiteCallKpiTilesTests : BunitContext
|
||||
Assert.DoesNotContain("border-danger", tile.GetAttribute("class") ?? string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the stuck tile has no warning border when the stuck count is zero.</summary>
|
||||
[Fact]
|
||||
public void StuckTile_NoWarningBorder_WhenStuckZero()
|
||||
{
|
||||
@@ -115,6 +121,7 @@ public class SiteCallKpiTilesTests : BunitContext
|
||||
Assert.DoesNotContain("border-warning", tile.GetAttribute("class") ?? string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the buffered tile never gets a threshold border, even with a high count.</summary>
|
||||
[Fact]
|
||||
public void BufferedTile_HasNoThresholdBorder_EvenWithHighCount()
|
||||
{
|
||||
@@ -130,6 +137,7 @@ public class SiteCallKpiTilesTests : BunitContext
|
||||
Assert.DoesNotContain("border-warning", cls);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that clicking the buffered tile navigates to the unfiltered site calls report.</summary>
|
||||
[Fact]
|
||||
public void BufferedTile_Click_NavigatesToUnfilteredSiteCallsReport()
|
||||
{
|
||||
@@ -145,6 +153,7 @@ public class SiteCallKpiTilesTests : BunitContext
|
||||
Assert.EndsWith("/site-calls/report", nav.Uri);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that clicking the stuck tile navigates to the site calls report with the stuck filter.</summary>
|
||||
[Fact]
|
||||
public void StuckTile_Click_NavigatesToSiteCallsReport_WithStuckFilter()
|
||||
{
|
||||
@@ -160,6 +169,7 @@ public class SiteCallKpiTilesTests : BunitContext
|
||||
Assert.Contains("/site-calls/report?stuck=true", nav.Uri);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that clicking the parked tile navigates to the site calls report with the parked status filter.</summary>
|
||||
[Fact]
|
||||
public void ParkedTile_Click_NavigatesToSiteCallsReport_WithParkedStatusFilter()
|
||||
{
|
||||
|
||||
+11
-2
@@ -6,7 +6,7 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Components;
|
||||
|
||||
/// <summary>
|
||||
/// M7-T16: the Instance Configure page accepts a CSV of attribute overrides via
|
||||
/// The Instance Configure page accepts a CSV of attribute overrides via
|
||||
/// an <c><InputFile></c>, parses it with the shared
|
||||
/// <see cref="OverrideCsvParser"/>, validates each row against the instance's
|
||||
/// overridable attributes (name must exist + not be template-locked, value must be
|
||||
@@ -19,7 +19,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Components;
|
||||
/// <para>
|
||||
/// <c>InstanceConfigure</c> is a heavyweight page (≈7 injected services incl.
|
||||
/// <c>InstanceService</c> + the flattening pipeline), so — consistent with the
|
||||
/// MV-14 List-override and native-alarm coverage — the parse→validate→build-dict
|
||||
/// list-override and native-alarm coverage — the parse→validate→build-dict
|
||||
/// core is extracted to an <c>internal static</c> helper exercised directly here,
|
||||
/// plus structural assertions over the component source that pin the InputFile +
|
||||
/// reuse-the-existing-submit-path wiring.
|
||||
@@ -49,6 +49,7 @@ public class InstanceConfigureCsvImportTests
|
||||
|
||||
// ── Core: valid CSV → override dict, no errors ──────────────────────────
|
||||
|
||||
/// <summary>Verifies that a valid CSV builds the override dictionary with no errors.</summary>
|
||||
[Fact]
|
||||
public void ValidCsv_BuildsOverrideDict_WithNoErrors()
|
||||
{
|
||||
@@ -64,6 +65,7 @@ public class InstanceConfigureCsvImportTests
|
||||
Assert.Equal("Pump-A", outcome.Overrides["Name"]);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a valid list-valued CSV row builds canonical JSON that round-trips through the value codec.</summary>
|
||||
[Fact]
|
||||
public void ValidListCsv_BuildsCanonicalJson_PerCodecRoundTrip()
|
||||
{
|
||||
@@ -81,6 +83,7 @@ public class InstanceConfigureCsvImportTests
|
||||
|
||||
// ── Core: bad rows → errors, NO dict (all-or-nothing) ───────────────────
|
||||
|
||||
/// <summary>Verifies that a row naming an unknown attribute produces an error and applies nothing.</summary>
|
||||
[Fact]
|
||||
public void UnknownAttribute_ProducesError_AndAppliesNothing()
|
||||
{
|
||||
@@ -94,6 +97,7 @@ public class InstanceConfigureCsvImportTests
|
||||
Assert.Contains(outcome.Errors, e => e.Contains("DoesNotExist") && e.Contains("Line 2"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a template-locked attribute is rejected, matching the manual editor.</summary>
|
||||
[Fact]
|
||||
public void TemplateLockedAttribute_IsRejected_LikeTheEditor()
|
||||
{
|
||||
@@ -109,6 +113,7 @@ public class InstanceConfigureCsvImportTests
|
||||
Assert.Contains(outcome.Errors, e => e.Contains("SerialNumber"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a type-incompatible value produces an error and applies nothing.</summary>
|
||||
[Fact]
|
||||
public void TypeIncompatibleValue_ProducesError_AndAppliesNothing()
|
||||
{
|
||||
@@ -123,6 +128,7 @@ public class InstanceConfigureCsvImportTests
|
||||
Assert.Contains(outcome.Errors, e => e.Contains("Setpoint") && e.Contains("Line 2"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that malformed list JSON produces an error and applies nothing.</summary>
|
||||
[Fact]
|
||||
public void MalformedListJson_ProducesError_AndAppliesNothing()
|
||||
{
|
||||
@@ -136,6 +142,7 @@ public class InstanceConfigureCsvImportTests
|
||||
Assert.Contains(outcome.Errors, e => e.Contains("Counts"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that parser errors propagate through the helper and nothing is applied.</summary>
|
||||
[Fact]
|
||||
public void ParserErrors_PropagateThrough_AndApplyNothing()
|
||||
{
|
||||
@@ -153,6 +160,7 @@ public class InstanceConfigureCsvImportTests
|
||||
|
||||
// ── Structural: InputFile + reuse-the-existing-submit-path wiring ───────
|
||||
|
||||
/// <summary>Verifies that the page source wires up the CSV input file control with its test hooks.</summary>
|
||||
[Fact]
|
||||
public void Page_WiresCsvInputFile_WithTestHooks()
|
||||
{
|
||||
@@ -165,6 +173,7 @@ public class InstanceConfigureCsvImportTests
|
||||
Assert.Contains("OverrideCsvParser.Parse", markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the page applies the imported overrides through the existing set-attribute-override path.</summary>
|
||||
[Fact]
|
||||
public void Page_AppliesViaExistingSetAttributeOverridePath()
|
||||
{
|
||||
|
||||
+9
@@ -55,6 +55,7 @@ public class InstanceConfigureNativeAlarmCsvImportTests
|
||||
|
||||
// ── Core: valid CSV → rows, no errors ───────────────────────────────────
|
||||
|
||||
/// <summary>Verifies that a valid CSV builds retarget rows with no errors.</summary>
|
||||
[Fact]
|
||||
public void ValidCsv_BuildsRetargetRows_WithNoErrors()
|
||||
{
|
||||
@@ -83,6 +84,7 @@ public class InstanceConfigureNativeAlarmCsvImportTests
|
||||
Assert.Null(pump.Filter);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a row with all fields blank is marked as a clear.</summary>
|
||||
[Fact]
|
||||
public void AllBlankRow_IsMarkedAsClear()
|
||||
{
|
||||
@@ -98,6 +100,7 @@ public class InstanceConfigureNativeAlarmCsvImportTests
|
||||
Assert.True(row.IsClear);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that sources absent from the file are left untouched because the import merges rather than replaces.</summary>
|
||||
[Fact]
|
||||
public void SourcesAbsentFromTheFile_AreUntouched_MergeNotReplace()
|
||||
{
|
||||
@@ -114,6 +117,7 @@ public class InstanceConfigureNativeAlarmCsvImportTests
|
||||
|
||||
// ── Core: bad rows → errors, NO rows (all-or-nothing) ───────────────────
|
||||
|
||||
/// <summary>Verifies that an unknown source produces an error and nothing is applied.</summary>
|
||||
[Fact]
|
||||
public void UnknownSource_ProducesError_AndAppliesNothing()
|
||||
{
|
||||
@@ -129,6 +133,7 @@ public class InstanceConfigureNativeAlarmCsvImportTests
|
||||
Assert.Contains(outcome.Errors, e => e.Contains("DoesNotExist") && e.Contains("Line 2"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a template-locked source is rejected, matching the server's behavior.</summary>
|
||||
[Fact]
|
||||
public void TemplateLockedSource_IsRejected_LikeTheServer()
|
||||
{
|
||||
@@ -142,6 +147,7 @@ public class InstanceConfigureNativeAlarmCsvImportTests
|
||||
Assert.Contains(outcome.Errors, e => e.Contains("LockedAlarms") && e.Contains("locked"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a duplicate source is rejected and nothing is applied.</summary>
|
||||
[Fact]
|
||||
public void DuplicateSource_IsRejected_AndAppliesNothing()
|
||||
{
|
||||
@@ -157,6 +163,7 @@ public class InstanceConfigureNativeAlarmCsvImportTests
|
||||
Assert.Contains(outcome.Errors, e => e.Contains("BoilerAlarms") && e.Contains("more than once"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that parser errors propagate through and nothing is applied.</summary>
|
||||
[Fact]
|
||||
public void ParserErrors_PropagateThrough_AndApplyNothing()
|
||||
{
|
||||
@@ -174,6 +181,7 @@ public class InstanceConfigureNativeAlarmCsvImportTests
|
||||
|
||||
// ── Structural: InputFile + reuse-the-existing-save-path wiring ─────────
|
||||
|
||||
/// <summary>Verifies that the page wires the native-alarm-source CSV input file with its test hooks.</summary>
|
||||
[Fact]
|
||||
public void Page_WiresNativeAlarmSourceCsvInputFile_WithTestHooks()
|
||||
{
|
||||
@@ -186,6 +194,7 @@ public class InstanceConfigureNativeAlarmCsvImportTests
|
||||
Assert.Contains("BuildNativeAlarmSourceCsvImport", markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the import applies changes through the existing repository upsert path.</summary>
|
||||
[Fact]
|
||||
public void Import_AppliesViaTheExistingRepositoryUpsertPath()
|
||||
{
|
||||
|
||||
+6
-1
@@ -10,7 +10,7 @@ using ZB.MOM.WW.ScadaBridge.Commons.Messages.Management;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Covers the M7-B6 (T15/T16) additions to <c>NodeBrowserDialog</c>: the
|
||||
/// Covers the search additions to <c>NodeBrowserDialog</c>: the
|
||||
/// address-space search box that renders <see cref="IBrowseService.SearchAsync"/>
|
||||
/// matches as a flat selectable list, and that selecting a search result feeds
|
||||
/// the SAME selection mechanism the tree uses (so the dialog's existing
|
||||
@@ -20,6 +20,7 @@ public class NodeBrowserDialogSearchTests : BunitContext
|
||||
{
|
||||
private readonly IBrowseService _browse = Substitute.For<IBrowseService>();
|
||||
|
||||
/// <summary>Initializes the test context with a substituted browse service that returns an empty root.</summary>
|
||||
public NodeBrowserDialogSearchTests()
|
||||
{
|
||||
Services.AddSingleton(_browse);
|
||||
@@ -63,6 +64,7 @@ public class NodeBrowserDialogSearchTests : BunitContext
|
||||
|
||||
private Func<string?> _capturedSelection = () => null;
|
||||
|
||||
/// <summary>Verifies that search matches render as rows carrying the expected data-test hooks.</summary>
|
||||
[Fact]
|
||||
public void Search_RendersMatchRows_WithDataTestHooks()
|
||||
{
|
||||
@@ -84,6 +86,7 @@ public class NodeBrowserDialogSearchTests : BunitContext
|
||||
Assert.Contains("Double", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that clicking a search result raises the selection callback with the node id.</summary>
|
||||
[Fact]
|
||||
public void ClickingSearchResult_RaisesSelectionCallback_WithNodeId()
|
||||
{
|
||||
@@ -106,6 +109,7 @@ public class NodeBrowserDialogSearchTests : BunitContext
|
||||
Assert.Equal("ns=2;s=Pump1.Speed", _capturedSelection());
|
||||
}
|
||||
|
||||
/// <summary>Verifies that submitting a blank query clears the displayed search results.</summary>
|
||||
[Fact]
|
||||
public void BlankQuery_ClearsResults()
|
||||
{
|
||||
@@ -125,6 +129,7 @@ public class NodeBrowserDialogSearchTests : BunitContext
|
||||
Assert.Empty(cut.FindAll("[data-test=node-search-result]"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a blank query after a failed search clears the stale failure alert.</summary>
|
||||
[Fact]
|
||||
public void BlankQueryAfterFailure_ClearsStaleFailureAlert()
|
||||
{
|
||||
|
||||
+6
@@ -13,6 +13,7 @@ public class NodeBrowserDialogSelectionTests : BunitContext
|
||||
{
|
||||
private readonly IBrowseService _browse = Substitute.For<IBrowseService>();
|
||||
|
||||
/// <summary>Initializes the test context with a substituted browse service.</summary>
|
||||
public NodeBrowserDialogSelectionTests()
|
||||
{
|
||||
Services.AddSingleton(_browse);
|
||||
@@ -36,6 +37,7 @@ public class NodeBrowserDialogSelectionTests : BunitContext
|
||||
CapReached: false, Failure: null));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that confirming a selection emits the full node including its data type through the node-selected callback.</summary>
|
||||
[Fact]
|
||||
public void Confirm_emits_full_node_with_datatype_on_OnNodeSelected()
|
||||
{
|
||||
@@ -57,6 +59,7 @@ public class NodeBrowserDialogSelectionTests : BunitContext
|
||||
Assert.Equal("Double", captured.DataType);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that setting ShowSearch to false hides the search box.</summary>
|
||||
[Fact]
|
||||
public void ShowSearch_false_hides_search_box()
|
||||
{
|
||||
@@ -74,6 +77,7 @@ public class NodeBrowserDialogSelectionTests : BunitContext
|
||||
// ONLY user-reachable selection path in production is the tree (Select(TreeNode)
|
||||
// → _selectedNode = node.Source). This guards that path end-to-end: a tree-node
|
||||
// click must carry the node's DataType through Confirm → OnNodeSelected.
|
||||
/// <summary>Verifies that selecting a node in the tree emits the full node including its data type through the node-selected callback.</summary>
|
||||
[Fact]
|
||||
public void TreeSelect_emits_full_node_with_datatype_on_OnNodeSelected()
|
||||
{
|
||||
@@ -107,6 +111,7 @@ public class NodeBrowserDialogSelectionTests : BunitContext
|
||||
// SelectableNodeClasses (design 2026-08-21): the create page needs to pick a
|
||||
// module's CONTAINER object, not just a leaf tag. Default stays Variable-only
|
||||
// so every existing caller is unaffected.
|
||||
/// <summary>Verifies that an Object node is not selectable by default.</summary>
|
||||
[Fact]
|
||||
public void Object_node_is_not_selectable_by_default()
|
||||
{
|
||||
@@ -122,6 +127,7 @@ public class NodeBrowserDialogSelectionTests : BunitContext
|
||||
Assert.Single(cut.FindAll("[data-test=node-search-result] button.btn-link"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that an Object node is selectable when its class is listed as selectable.</summary>
|
||||
[Fact]
|
||||
public void Object_node_is_selectable_when_listed()
|
||||
{
|
||||
|
||||
@@ -13,7 +13,7 @@ using ZB.MOM.WW.ScadaBridge.Security;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Covers the M7-B8 (T17) "Verify endpoint" affordance added to
|
||||
/// Covers the "Verify endpoint" affordance added to
|
||||
/// <see cref="OpcUaEndpointEditor"/>: a button that calls the injected
|
||||
/// <see cref="IEndpointVerificationService"/> with the current endpoint config and
|
||||
/// renders the typed <see cref="VerifyEndpointResult"/> — green on success, a red
|
||||
@@ -21,7 +21,7 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Components;
|
||||
/// <see cref="VerifyFailureKind.UntrustedCertificate"/> with a captured cert.
|
||||
///
|
||||
/// <para>
|
||||
/// Also covers the M7-B10 (T17) "Trust certificate" affordance on the untrusted-cert
|
||||
/// Also covers the "Trust certificate" affordance on the untrusted-cert
|
||||
/// panel: an Administrator-gated button that calls
|
||||
/// <see cref="ICertManagementService.TrustAsync"/> and re-runs Verify on success.
|
||||
/// </para>
|
||||
@@ -34,6 +34,7 @@ public class OpcUaEndpointVerifyTests : BunitContext
|
||||
private readonly ICertManagementService _certs =
|
||||
Substitute.For<ICertManagementService>();
|
||||
|
||||
/// <summary>Registers the substitute verification and certificate-management services with the test container.</summary>
|
||||
public OpcUaEndpointVerifyTests()
|
||||
{
|
||||
Services.AddSingleton(_verify);
|
||||
@@ -86,6 +87,7 @@ public class OpcUaEndpointVerifyTests : BunitContext
|
||||
return host.FindComponent<OpcUaEndpointEditor>();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a successful endpoint verification shows a success message.</summary>
|
||||
[Fact]
|
||||
public void Verify_Success_ShowsSuccessMessage()
|
||||
{
|
||||
@@ -102,6 +104,7 @@ public class OpcUaEndpointVerifyTests : BunitContext
|
||||
Assert.Empty(cut.FindAll("[data-test=verify-cert-panel]"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that an untrusted server certificate shows the certificate panel with its thumbprint.</summary>
|
||||
[Fact]
|
||||
public void Verify_UntrustedCertificate_ShowsCertPanelWithThumbprint()
|
||||
{
|
||||
@@ -128,6 +131,7 @@ public class OpcUaEndpointVerifyTests : BunitContext
|
||||
Assert.NotEmpty(cut.FindAll("[data-test=verify-failure]"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a generic verification failure shows a failure message.</summary>
|
||||
[Fact]
|
||||
public void Verify_GenericFailure_ShowsFailureMessage()
|
||||
{
|
||||
@@ -161,6 +165,7 @@ public class OpcUaEndpointVerifyTests : BunitContext
|
||||
.Returns(new VerifyEndpointResult(
|
||||
false, VerifyFailureKind.UntrustedCertificate, "Untrusted certificate.", cert));
|
||||
|
||||
/// <summary>Verifies that an administrator sees the trust button and that clicking it calls the trust operation.</summary>
|
||||
[Fact]
|
||||
public void TrustButton_Admin_SeesButton_AndClick_CallsTrustAsync()
|
||||
{
|
||||
@@ -183,6 +188,7 @@ public class OpcUaEndpointVerifyTests : BunitContext
|
||||
"plant-a", "PLC-OPC", cert.DerBase64, cert.Thumbprint, Arg.Any<CancellationToken>());
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a user with only the Designer role does not see the trust button.</summary>
|
||||
[Fact]
|
||||
public void TrustButton_NonAdmin_DesignerOnly_DoesNotSeeButton()
|
||||
{
|
||||
|
||||
+5
@@ -5,6 +5,9 @@ namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Components;
|
||||
|
||||
public class SecuredWriteDataTypeMapperTests
|
||||
{
|
||||
/// <summary>Verifies that known Galaxy data type names map to the expected data type.</summary>
|
||||
/// <param name="galaxy">The Galaxy data type name.</param>
|
||||
/// <param name="expected">The expected mapped data type.</param>
|
||||
[Theory]
|
||||
[InlineData("Boolean", DataType.Boolean)]
|
||||
[InlineData("bool", DataType.Boolean)]
|
||||
@@ -22,6 +25,8 @@ public class SecuredWriteDataTypeMapperTests
|
||||
Assert.Equal(expected, dt);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that unknown, null or blank Galaxy type names fail to map.</summary>
|
||||
/// <param name="galaxy">The Galaxy data type name, possibly null or blank.</param>
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
|
||||
@@ -17,7 +17,7 @@ using SecuredWritesPage = ZB.MOM.WW.ScadaBridge.CentralUI.Components.Pages.Opera
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Components;
|
||||
|
||||
/// <summary>
|
||||
/// bUnit rendering + interaction tests for the Secured Writes page (M7 T14b / C5).
|
||||
/// bUnit rendering + interaction tests for the Secured Writes page.
|
||||
/// The page is the UI for the two-person MxGateway write workflow: an Operator submits,
|
||||
/// a different Verifier approves/rejects, and everyone sees the history. The page only
|
||||
/// SUBMITS commands through <see cref="ISecuredWriteService"/> — the server enforces
|
||||
@@ -37,6 +37,7 @@ public class SecuredWritesTests : BunitContext
|
||||
// even though these tests never drive the browser.
|
||||
private readonly IBrowseService _browse = Substitute.For<IBrowseService>();
|
||||
|
||||
/// <summary>Registers the substitute services and default stubs the Secured Writes page needs when it renders.</summary>
|
||||
public SecuredWritesTests()
|
||||
{
|
||||
Services.AddSingleton(_service);
|
||||
@@ -48,7 +49,7 @@ public class SecuredWritesTests : BunitContext
|
||||
// BOTH overloads must be stubbed: Reject takes the bool form, Approve takes the
|
||||
// DialogSeverity form (register row 75). An unstubbed NSubstitute call returns a
|
||||
// null Task, so missing one here surfaces as an NRE inside the click handler
|
||||
// rather than as anything that names the dialog — the row-28 gotcha.
|
||||
// rather than as anything that names the dialog — a known gotcha.
|
||||
_dialog.ConfirmAsync(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<bool>(), Arg.Any<string?>())
|
||||
.Returns(Task.FromResult(true));
|
||||
_dialog.ConfirmAsync(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<DialogSeverity>(), Arg.Any<string?>())
|
||||
@@ -130,6 +131,7 @@ public class SecuredWritesTests : BunitContext
|
||||
|
||||
// ── (a) Operator: submit form present + Submit calls SubmitAsync with the values ──
|
||||
|
||||
/// <summary>Verifies that a user with the Operator role sees the submit form.</summary>
|
||||
[Fact]
|
||||
public void Operator_SeesSubmitForm()
|
||||
{
|
||||
@@ -145,6 +147,7 @@ public class SecuredWritesTests : BunitContext
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>Verifies that submitting as an Operator calls the service with the entered values.</summary>
|
||||
[Fact]
|
||||
public void Operator_Submit_CallsServiceWithEnteredValues()
|
||||
{
|
||||
@@ -175,6 +178,7 @@ public class SecuredWritesTests : BunitContext
|
||||
|
||||
// ── (b) Verifier: pending row renders with Approve/Reject; own-row actions disabled ──
|
||||
|
||||
/// <summary>Verifies that a Verifier sees enabled approve and reject actions on a pending row submitted by another operator.</summary>
|
||||
[Fact]
|
||||
public void Verifier_PendingRow_HasEnabledActions_ForOtherOperatorsRow()
|
||||
{
|
||||
@@ -194,6 +198,7 @@ public class SecuredWritesTests : BunitContext
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a Verifier's own submission has its approve and reject actions disabled.</summary>
|
||||
[Fact]
|
||||
public void Verifier_OwnSubmission_HasApproveAndRejectDisabled()
|
||||
{
|
||||
@@ -216,6 +221,7 @@ public class SecuredWritesTests : BunitContext
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>Verifies that approving a pending row as a Verifier calls <c>ApproveAsync</c> on the service.</summary>
|
||||
[Fact]
|
||||
public void Verifier_Approve_CallsApproveAsync()
|
||||
{
|
||||
@@ -233,6 +239,7 @@ public class SecuredWritesTests : BunitContext
|
||||
|
||||
// ── (c) History: terminal rows render ──
|
||||
|
||||
/// <summary>Verifies that the history section renders rows in terminal states.</summary>
|
||||
[Fact]
|
||||
public void History_RendersTerminalRows()
|
||||
{
|
||||
@@ -263,6 +270,7 @@ public class SecuredWritesTests : BunitContext
|
||||
|
||||
// ── (d) History pager: page-size 50, driven by TotalCount ──
|
||||
|
||||
/// <summary>Verifies that the history pager requests the second page using the total count when Next is clicked.</summary>
|
||||
[Fact]
|
||||
public void History_Pager_NextRequestsSecondPage_UsingTotalCount()
|
||||
{
|
||||
@@ -285,13 +293,14 @@ public class SecuredWritesTests : BunitContext
|
||||
|
||||
cut.Find("[data-test='secured-write-history-next']").Click();
|
||||
|
||||
// The history page-2 fetch offsets by one page (skip: 50).
|
||||
// The history second-page fetch offsets by one page (skip: 50).
|
||||
_service.Received().ListAsync(
|
||||
Arg.Any<string?>(), Arg.Any<string?>(), 50, Arg.Any<int>(), Arg.Any<CancellationToken>());
|
||||
}
|
||||
|
||||
// ── (e) Approve dialog surfaces submission age (stale-setpoint scenario) ──
|
||||
|
||||
/// <summary>Verifies that the approve confirmation dialog shows the age of the submission.</summary>
|
||||
[Fact]
|
||||
public void Approve_ConfirmDialog_ShowsSubmissionAge()
|
||||
{
|
||||
@@ -316,6 +325,7 @@ public class SecuredWritesTests : BunitContext
|
||||
|
||||
// ── (f) Approve/Reject confirm-button severity (register row 75) ──
|
||||
|
||||
/// <summary>Verifies that the approve confirmation dialog uses caution styling rather than destructive styling.</summary>
|
||||
[Fact]
|
||||
public void Approve_ConfirmDialog_IsCautionNotDestructive()
|
||||
{
|
||||
@@ -343,6 +353,7 @@ public class SecuredWritesTests : BunitContext
|
||||
Arg.Any<string>(), Arg.Any<string>(), Arg.Any<bool>(), Arg.Any<string?>());
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the reject confirmation dialog keeps its destructive styling.</summary>
|
||||
[Fact]
|
||||
public void Reject_ConfirmDialog_KeepsDestructiveStyling()
|
||||
{
|
||||
@@ -364,6 +375,7 @@ public class SecuredWritesTests : BunitContext
|
||||
Arg.Any<string>(), Arg.Any<string>(), danger: true, confirmLabel: "Reject");
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a Viewer does not see the Operator or Verifier regions.</summary>
|
||||
[Fact]
|
||||
public void Viewer_DoesNotSeeOperatorOrVerifierRegions()
|
||||
{
|
||||
|
||||
+11
-1
@@ -5,7 +5,7 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types.Kpi;
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Components.Shared;
|
||||
|
||||
/// <summary>
|
||||
/// bUnit tests for <see cref="KpiTrendChart"/> (M6 K12) — the reusable
|
||||
/// bUnit tests for <see cref="KpiTrendChart"/> — the reusable
|
||||
/// dependency-free SVG trend chart. Coverage pins the three render states
|
||||
/// (multi-point chart, unavailable placeholder, single-sample note), the stable
|
||||
/// <c>data-test</c> slug, and the coordinate-math edge guards (all-equal
|
||||
@@ -22,6 +22,7 @@ public class KpiTrendChartTests : BunitContext
|
||||
new KpiSeriesPoint(Base.AddMinutes(10), 2.0),
|
||||
};
|
||||
|
||||
/// <summary>Verifies that an available series with three points renders a polyline, a title and a data-test attribute.</summary>
|
||||
[Fact]
|
||||
public void Available_WithThreePoints_RendersPolylineTitleAndDataTest()
|
||||
{
|
||||
@@ -38,6 +39,7 @@ public class KpiTrendChartTests : BunitContext
|
||||
Assert.Contains("<title>", markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a unit is appended to the rendered value labels.</summary>
|
||||
[Fact]
|
||||
public void Available_WithUnit_AppendsUnitToValueLabels()
|
||||
{
|
||||
@@ -51,6 +53,7 @@ public class KpiTrendChartTests : BunitContext
|
||||
Assert.Contains("max 4 s", cut.Markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that an unavailable series renders an em dash placeholder and the error text without a polyline.</summary>
|
||||
[Fact]
|
||||
public void Unavailable_RendersEmDashPlaceholderAndError_NoPolyline()
|
||||
{
|
||||
@@ -66,6 +69,7 @@ public class KpiTrendChartTests : BunitContext
|
||||
Assert.DoesNotContain("<polyline", markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that null points render the placeholder without a polyline.</summary>
|
||||
[Fact]
|
||||
public void NullPoints_RendersPlaceholder_NoPolyline()
|
||||
{
|
||||
@@ -79,6 +83,7 @@ public class KpiTrendChartTests : BunitContext
|
||||
Assert.DoesNotContain("<polyline", markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a single point renders the single-sample state without a polyline and does not throw.</summary>
|
||||
[Fact]
|
||||
public void SinglePoint_RendersSingleSampleState_NoPolyline_DoesNotThrow()
|
||||
{
|
||||
@@ -92,6 +97,7 @@ public class KpiTrendChartTests : BunitContext
|
||||
Assert.DoesNotContain("<polyline", markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that points with identical timestamps render a polyline without throwing.</summary>
|
||||
[Fact]
|
||||
public void AllEqualTimestamps_DoesNotThrow_AndRendersPolyline()
|
||||
{
|
||||
@@ -114,6 +120,7 @@ public class KpiTrendChartTests : BunitContext
|
||||
Assert.DoesNotContain("Infinity", markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that all-zero values render a flat polyline without throwing.</summary>
|
||||
[Fact]
|
||||
public void AllZeroValues_DoesNotThrow_AndRendersFlatPolyline()
|
||||
{
|
||||
@@ -136,6 +143,9 @@ public class KpiTrendChartTests : BunitContext
|
||||
Assert.DoesNotContain("Infinity", markup);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a chart title is converted to a stable data-test slug.</summary>
|
||||
/// <param name="title">The chart title to slugify.</param>
|
||||
/// <param name="expectedDataTest">The expected data-test slug.</param>
|
||||
[Theory]
|
||||
[InlineData("Queue Depth", "kpi-trend-queue-depth")]
|
||||
[InlineData("Oldest Pending (s)", "kpi-trend-oldest-pending-s")]
|
||||
|
||||
Reference in New Issue
Block a user