using EnvelopeRenderer.Desktop.Core.Design; namespace EnvelopeRenderer.Desktop.Tests; public class TextElementPropertiesEditorTests { [Fact] public void SetX_UpdatesSelectedElement() { var editor = new TextElementPropertiesEditor(); var element = TextElementLayout.CreateStatic(0, 0, "a"); editor.Select(element); editor.SetX(42.5); Assert.Equal(42.5, element.X, precision: 6); } [Fact] public void SetY_UpdatesSelectedElement() { var editor = new TextElementPropertiesEditor(); var element = TextElementLayout.CreateStatic(0, 0, "a"); editor.Select(element); editor.SetY(99.25); Assert.Equal(99.25, element.Y, precision: 6); } [Fact] public void SetFontFamily_UpdatesSelectedElement() { var editor = new TextElementPropertiesEditor(); var element = TextElementLayout.CreateStatic(0, 0, "a", fontFamily: "Arial"); editor.Select(element); editor.SetFontFamily("Times New Roman [Bold]"); Assert.Equal("Times New Roman [Bold]", element.FontFamily); } [Theory] [InlineData(null)] [InlineData("")] [InlineData(" ")] public void SetFontFamily_BlankInput_IsIgnored(string? blank) { var editor = new TextElementPropertiesEditor(); var element = TextElementLayout.CreateStatic(0, 0, "a", fontFamily: "Arial"); editor.Select(element); editor.SetFontFamily(blank); Assert.Equal("Arial", element.FontFamily); } [Fact] public void SetFontSize_PositiveValue_UpdatesSelectedElement() { var editor = new TextElementPropertiesEditor(); var element = TextElementLayout.CreateStatic(0, 0, "a", fontSize: 12); editor.Select(element); editor.SetFontSize(24); Assert.Equal(24, element.FontSize, precision: 6); } [Theory] [InlineData(0)] [InlineData(-5)] public void SetFontSize_NonPositiveValue_IsIgnored(double invalid) { var editor = new TextElementPropertiesEditor(); var element = TextElementLayout.CreateStatic(0, 0, "a", fontSize: 12); editor.Select(element); editor.SetFontSize(invalid); Assert.Equal(12, element.FontSize, precision: 6); } [Fact] public void SetColor_UpdatesSelectedElement() { var editor = new TextElementPropertiesEditor(); var element = TextElementLayout.CreateStatic(0, 0, "a"); editor.Select(element); editor.SetColor(new RgbColor(10, 20, 30)); Assert.Equal(new RgbColor(10, 20, 30), element.Color); } [Fact] public void SetColumnName_DynamicElement_RebindsColumnWithoutMovingIt() { var editor = new TextElementPropertiesEditor(); var element = TextElementLayout.CreateDynamic(15, 25, "Full Name"); editor.Select(element); editor.SetColumnName("Mailing Address"); Assert.Equal("Mailing Address", element.ColumnName); // Sprint 3, Batch 4 acceptance criterion: rebinding must not require recreating or // repositioning the layout element. Assert.Equal(15, element.X, precision: 6); Assert.Equal(25, element.Y, precision: 6); } [Fact] public void SetColumnName_StaticElement_IsIgnored() { var editor = new TextElementPropertiesEditor(); var element = TextElementLayout.CreateStatic(0, 0, "Static label"); editor.Select(element); editor.SetColumnName("Full Name"); Assert.False(element.IsDynamic); Assert.Null(element.ColumnName); Assert.Equal("Static label", element.StaticText); } [Theory] [InlineData(null)] [InlineData("")] [InlineData(" ")] public void SetColumnName_BlankInput_IsIgnored(string? blank) { var editor = new TextElementPropertiesEditor(); var element = TextElementLayout.CreateDynamic(0, 0, "Full Name"); editor.Select(element); editor.SetColumnName(blank); Assert.Equal("Full Name", element.ColumnName); } [Fact] public void SetColumnName_ArbitraryColumnName_RebindsExactly() { var editor = new TextElementPropertiesEditor(); var element = TextElementLayout.CreateDynamic(0, 0, "Full Name"); editor.Select(element); editor.SetColumnName("Bill To & Ship To's Address"); Assert.Equal("Bill To & Ship To's Address", element.ColumnName); } [Fact] public void SetZOrder_NonNegativeValue_StoresAsGiven() { var editor = new TextElementPropertiesEditor(); var element = TextElementLayout.CreateStatic(0, 0, "a"); editor.Select(element); editor.SetZOrder(7); Assert.Equal(7, element.ZOrder); } [Fact] public void SetZOrder_NegativeValue_ClampsToZeroBottomMost() { var editor = new TextElementPropertiesEditor(); var element = TextElementLayout.CreateStatic(0, 0, "a"); editor.Select(element); editor.SetZOrder(-3); Assert.Equal(0, element.ZOrder); } [Fact] public void AllSetters_NoSelection_AreNoOpsAndDoNotThrow() { var editor = new TextElementPropertiesEditor(); editor.SetX(1); editor.SetY(1); editor.SetFontFamily("Arial"); editor.SetFontSize(10); editor.SetColor(RgbColor.Black); editor.SetZOrder(1); editor.SetColumnName("Full Name"); editor.SetCollapseIfBlank(true); editor.SetRotationAngle(45); Assert.Null(editor.Selected); } [Fact] public void SetCollapseIfBlank_UpdatesSelectedElement() { var editor = new TextElementPropertiesEditor(); var element = TextElementLayout.CreateDynamic(0, 0, "Address2"); editor.Select(element); editor.SetCollapseIfBlank(true); Assert.True(element.CollapseIfBlank); editor.SetCollapseIfBlank(false); Assert.False(element.CollapseIfBlank); } [Fact] public void SetRotationAngle_UpdatesSelectedElement_PositiveAndNegative() { var editor = new TextElementPropertiesEditor(); var element = TextElementLayout.CreateStatic(0, 0, "a"); editor.Select(element); editor.SetRotationAngle(-135.5); Assert.Equal(-135.5, element.RotationAngle, precision: 6); editor.SetRotationAngle(720); Assert.Equal(720, element.RotationAngle, precision: 6); } [Theory] [InlineData(double.NaN)] [InlineData(double.PositiveInfinity)] [InlineData(double.NegativeInfinity)] public void SetRotationAngle_NonFiniteValue_IsIgnored(double invalid) { var editor = new TextElementPropertiesEditor(); var element = TextElementLayout.CreateStatic(0, 0, "a"); element.RotationAngle = 10; editor.Select(element); editor.SetRotationAngle(invalid); Assert.Equal(10, element.RotationAngle, precision: 6); } // Sprint 5, "Mix static text and CSV fields within a single text element". [Fact] public void SetContent_PlainLiteralText_ProducesSingleLiteralRun() { var editor = new TextElementPropertiesEditor(); var element = TextElementLayout.CreateDynamic(0, 0, "Full Name"); // starts dynamic editor.Select(element); editor.SetContent("Plain label"); var run = Assert.Single(element.Runs); Assert.False(run.IsField); Assert.Equal("Plain label", run.Literal); Assert.False(element.IsDynamic); } [Fact] public void SetContent_MixedBracketSyntax_ProducesRunSequence() { var editor = new TextElementPropertiesEditor(); var element = TextElementLayout.CreateStatic(15, 25, "placeholder"); editor.Select(element); editor.SetContent("Attn: {Full Name} - {Last Name}"); Assert.Equal(4, element.Runs.Count); Assert.True(element.IsDynamic); Assert.Equal("Full Name", element.Runs[1].ColumnName); Assert.Equal("Last Name", element.Runs[3].ColumnName); // Sprint 5's own acceptance criterion: editing content must not reposition the element. Assert.Equal(15, element.X, precision: 6); Assert.Equal(25, element.Y, precision: 6); } [Fact] public void SetContent_NoSelection_IsNoOp() { var editor = new TextElementPropertiesEditor(); editor.SetContent("Attn: {Full Name}"); // must not throw Assert.Null(editor.Selected); } [Fact] public void SetColumnName_MixedContentElement_IsIgnored() { // Rebind semantics don't generalize to "which of N field runs?" — SetContent is the // general editor for mixed content; SetColumnName stays scoped to the legacy single-run // dynamic shape only. var editor = new TextElementPropertiesEditor(); var element = TextElementLayout.CreateStatic(0, 0, "placeholder"); editor.Select(element); editor.SetContent("Attn: {Full Name}"); editor.SetColumnName("Different Column"); Assert.Equal(2, element.Runs.Count); Assert.Equal("Full Name", element.Runs[1].ColumnName); } [Fact] public void Select_ThenEdits_ThenSelectDifferentElement_EditsApplyToCorrectElement() { var editor = new TextElementPropertiesEditor(); var first = TextElementLayout.CreateStatic(0, 0, "first"); var second = TextElementLayout.CreateStatic(0, 0, "second"); editor.Select(first); editor.SetX(10); editor.Select(second); editor.SetX(20); Assert.Equal(10, first.X, precision: 6); Assert.Equal(20, second.X, precision: 6); } }