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"));
}
}