using Bunit;
using Microsoft.Extensions.DependencyInjection;
using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared;
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Shared;
///
/// Deferred-work register row 75 — pins the confirm button's severity → Bootstrap class
/// mapping against the REAL behind a real DialogHost,
/// so these cover what the user actually sees rather than what an argument was.
///
///
/// Row 28 split the label from the styling but left the styling a two-state boolean, so a
/// consequential-but-not-destructive action had to choose between looking like a delete
/// and looking like the safe default. Row 75 added as
/// the middle rung. The three-way mapping is the whole of that change, and these are its
/// pins; SecuredWritesTests separately pins that the Secured Writes page asks for
/// the right one.
///
///
///
/// bUnit cannot run real JS interop, so the host's sbDialog.* focus-trap calls run
/// in as no-ops (same arrangement as
/// FailoverConfirmLabelTests).
///
///
public class DialogSeverityStylingTests : BunitContext
{
private readonly DialogService _dialog = new();
public DialogSeverityStylingTests()
{
Services.AddSingleton(_dialog);
JSInterop.Mode = JSRuntimeMode.Loose;
}
///
/// Opens a confirm dialog on a live host and returns its confirm button.
///
///
/// The ConfirmAsync task is deliberately not awaited — with the real service it
/// does not complete until someone answers the dialog, so awaiting it would deadlock
/// the test. The state is published synchronously before the task is returned.
///
private AngleSharp.Dom.IElement OpenConfirm(Func> open)
{
var cut = Render();
cut.InvokeAsync(() => { _ = open(_dialog); }).GetAwaiter().GetResult();
cut.Render();
var footer = cut.FindAll("div.modal-footer button").ToList();
return footer.Single(b => b.TextContent.Trim() != "Cancel");
}
[Fact]
public void Caution_rendersAmber_notRedAndNotPrimary()
{
// The row-75 case. Amber is the point: not red (which reads "delete"), and not
// the primary blue (which reads "safe default" — wrong for a live device write).
var confirm = OpenConfirm(d => d.ConfirmAsync("Approve thing", "Sure?", DialogSeverity.Caution, "Approve"));
Assert.Contains("btn-warning", confirm.ClassList);
Assert.DoesNotContain("btn-danger", confirm.ClassList);
Assert.DoesNotContain("btn-primary", confirm.ClassList);
}
[Fact]
public void Destructive_stillRendersRed()
{
// Regression guard on the ~20 genuine delete call sites: row 75 is additive and
// must not have shifted what danger looks like.
var confirm = OpenConfirm(d => d.ConfirmAsync("Delete thing", "Sure?", DialogSeverity.Destructive));
Assert.Contains("btn-danger", confirm.ClassList);
}
[Fact]
public void Normal_stillRendersPrimary()
{
var confirm = OpenConfirm(d => d.ConfirmAsync("Do thing", "Sure?", DialogSeverity.Normal));
Assert.Contains("btn-primary", confirm.ClassList);
}
[Theory]
[InlineData(true, "btn-danger")]
[InlineData(false, "btn-primary")]
public void BooleanForm_mapsOntoTheSameTwoSeverities(bool danger, string expectedClass)
{
// The boolean overload is now a thin adapter onto the severity one. Every existing
// call site goes through it, so this pins that the adapter did not shift any of
// them: true is still red, false is still primary.
//
// One case per fact, deliberately: DialogService permits only one open dialog at a
// time and throws on the second, so two OpenConfirm calls in one body would fail
// on the service's own nested-dialog guard rather than on anything about styling.
var confirm = OpenConfirm(d => d.ConfirmAsync("Do thing", "Sure?", danger: danger));
Assert.Contains(expectedClass, confirm.ClassList);
}
[Fact]
public void UnlabelledCaution_readsConfirm_notDelete()
{
// Caution deliberately does NOT inherit the danger "Delete" fallback: a caution
// action is by definition not a delete, so falling back to that verb would
// reintroduce register row 28 through the new door.
var confirm = OpenConfirm(d => d.ConfirmAsync("Approve thing", "Sure?", DialogSeverity.Caution));
Assert.Equal("Confirm", confirm.TextContent.Trim());
}
[Fact]
public void UnlabelledDestructive_stillFallsBackToDelete()
{
// The row-28 fallback is retained deliberately (the Playwright suites click the
// literal "Delete"). Pinned here alongside its new sibling so the pair is read
// together.
var confirm = OpenConfirm(d => d.ConfirmAsync("Delete thing", "Sure?", DialogSeverity.Destructive));
Assert.Equal("Delete", confirm.TextContent.Trim());
}
}