using EnvelopeRenderer.Desktop.Core.Design; namespace EnvelopeRenderer.Desktop.Tests; /// Address Control text case / vertical anchor and the template page background: preview /// parity with the CLI scenarios (BackgroundAndAddressOptionsTests), geometry, XML round trip. public class AddressOptionsAndBackgroundTests { private static IReadOnlyDictionary Record(params (string Key, string Value)[] pairs) => pairs.ToDictionary(p => p.Key, p => p.Value, StringComparer.OrdinalIgnoreCase); private static AddressControlLayout Control( AddressTextCase textCase = AddressTextCase.None, AddressVerticalAnchor anchor = AddressVerticalAnchor.Top) { var control = AddressControlLayout.CreateDefault(120, 500); control.Lines.Clear(); control.Lines.Add(AddressControlLineLayout.CreateField("Name", fontSize: 12)); control.Lines.Add(AddressControlLineLayout.CreateField("Line2", fontSize: 12)); var literal = new AddressControlLineLayout("Arial", 12); literal.Runs.Clear(); literal.Runs.Add(TextRun.ForLiteral("Mixed Case")); control.Lines.Add(literal); control.TextCase = textCase; control.VerticalAnchor = anchor; return control; } private static IReadOnlyList Preview(AddressControlLayout control, string line2) { var document = new TemplateLayoutDocument(new CanvasSettings(297, 684, CanvasUnit.Points)); document.AddressControls.Add(control); var result = TemplatePreviewBuilder.Build( document, new[] { "Name", "Line2" }, Record(("Name", "Alice Smith"), ("Line2", line2))); Assert.True(result.Success, result.Message); return result.Draws; } [Theory] [InlineData(AddressTextCase.None, "Alice Smith", "Mixed Case")] [InlineData(AddressTextCase.Upper, "ALICE SMITH", "MIXED CASE")] [InlineData(AddressTextCase.Lower, "alice smith", "mixed case")] public void Preview_TextCase_AppliesToFieldAndLiteralText(AddressTextCase textCase, string name, string literal) { var draws = Preview(Control(textCase), "x"); Assert.Equal(name, draws[0].Text); Assert.Equal(literal, draws[2].Text); } [Fact] public void Preview_TopAnchor_BlockGrowsDownFromAnchor() => Assert.Equal(new[] { 500.0, 485.0, 470.0 }, Preview(Control(), "s").Select(d => d.Y).ToArray()); [Fact] public void Preview_BottomAnchor_LastLineOnAnchorAndBlockGrowsUp() => Assert.Equal( new[] { 530.0, 515.0, 500.0 }, Preview(Control(anchor: AddressVerticalAnchor.Bottom), "s").Select(d => d.Y).ToArray()); [Fact] public void Preview_BottomAnchor_CollapsedMiddleLine_KeepsLastVisibleLineOnAnchor() => Assert.Equal( new[] { 515.0, 500.0 }, Preview(Control(anchor: AddressVerticalAnchor.Bottom), "").Select(d => d.Y).ToArray()); [Fact] public void BottomAnchor_BoxGeometry_ExtendsUpwardFromAnchor() { var top = Control(); var bottom = Control(anchor: AddressVerticalAnchor.Bottom); Assert.Equal(500, top.TopBaselineY, precision: 6); Assert.Equal(530, bottom.TopBaselineY, precision: 6); Assert.Equal(top.BoxCenter.Y + 30, bottom.BoxCenter.Y, precision: 6); Assert.Equal(500, bottom.BaselineYForLine(2), precision: 6); } private static string TempFile() => Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xml"); [Fact] public void Xml_RoundTrip_PreservesCaseAnchorAndBackground() { var document = new TemplateLayoutDocument(new CanvasSettings(297, 684, CanvasUnit.Points)); document.AddressControls.Add(Control(AddressTextCase.Upper, AddressVerticalAnchor.Bottom)); document.Background = new PageBackgroundLayout { FileColumn = "Form", PageColumn = "Pg" }; var path = TempFile(); TemplateLayoutXmlSerializer.Save(document, path); var loaded = TemplateLayoutXmlSerializer.TryLoad(path, out var doc, out var errors); Assert.True(loaded, string.Join("; ", errors)); var control = Assert.Single(doc!.AddressControls); Assert.Equal(AddressTextCase.Upper, control.TextCase); Assert.Equal(AddressVerticalAnchor.Bottom, control.VerticalAnchor); Assert.Equal("Form", doc.Background!.FileColumn); Assert.Equal("Pg", doc.Background.PageColumn); Assert.Null(doc.Background.File); } [Fact] public void Xml_Defaults_WriteNoNewAttributesOrBackground() { var document = new TemplateLayoutDocument(new CanvasSettings(297, 684, CanvasUnit.Points)); document.AddressControls.Add(Control()); var path = TempFile(); TemplateLayoutXmlSerializer.Save(document, path); var xml = File.ReadAllText(path); Assert.DoesNotContain("case=", xml); Assert.DoesNotContain("verticalAnchor", xml); Assert.DoesNotContain("")] [InlineData("")] [InlineData("")] [InlineData("")] public void Xml_InvalidBackground_FailsToLoad(string backgroundXml) { var path = TempFile(); File.WriteAllText(path, $"{backgroundXml}Hi"); Assert.False(TemplateLayoutXmlSerializer.TryLoad(path, out _, out var errors)); Assert.Contains(errors, e => e.Contains("")); } [Fact] public void Xml_InvalidCase_FailsToLoad() { var path = TempFile(); File.WriteAllText(path, "" + ""); Assert.False(TemplateLayoutXmlSerializer.TryLoad(path, out _, out var errors)); Assert.Contains(errors, e => e.Contains("'case'")); } [Fact] public void Background_ResolvesFileAndPagePerRecord() { var background = new PageBackgroundLayout { FileColumn = "Form", PageColumn = "Pg" }; var record = Record(("Form", " a.pdf "), ("Pg", " 2 ")); Assert.Equal(Path.Combine(@"C:\t", "a.pdf"), background.ResolveFile(record, @"C:\t")); Assert.Equal(2, background.ResolvePage(record)); Assert.Null(background.ResolveFile(Record(("Form", "")), @"C:\t")); Assert.Null(background.ResolvePage(Record(("Pg", "zero")))); Assert.Equal(@"D:\x\b.png", new PageBackgroundLayout { File = @"D:\x\b.png" }.ResolveFile(null, @"C:\t")); } }