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:
+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()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user