|
- using EnvelopeRenderer.Desktop.Core.Design;
-
- namespace EnvelopeRenderer.Desktop.Tests;
-
- public class TemplateLayoutXmlSerializerTests : IDisposable
- {
- private readonly string _tempPath = Path.Combine(Path.GetTempPath(), $"envelope-designer-test-{Guid.NewGuid():N}.xml");
-
- public void Dispose()
- {
- if (File.Exists(_tempPath))
- {
- File.Delete(_tempPath);
- }
- }
-
- private static TemplateLayoutDocument BuildSampleDocument()
- {
- var document = new TemplateLayoutDocument(CanvasSettings.FromUnitValues(4.125, 9.5, CanvasUnit.Inches));
- var staticElement = TextElementLayout.CreateStatic(120, 240, "Static label:", "Arial", 12, zOrder: 0);
- staticElement.Color = new RgbColor(200, 10, 30);
- var dynamicElement = TextElementLayout.CreateDynamic(120, 225, "Full Name", "Arial [Bold]", 14, zOrder: 1);
- document.Elements.Add(staticElement);
- document.Elements.Add(dynamicElement);
- return document;
- }
-
- [Fact]
- public void SaveThenLoad_RoundTripsCanvasSettingsExactly()
- {
- var original = BuildSampleDocument();
-
- TemplateLayoutXmlSerializer.Save(original, _tempPath);
- var loaded = TemplateLayoutXmlSerializer.TryLoad(_tempPath, out var document, out var errors);
-
- Assert.True(loaded, string.Join("; ", errors));
- Assert.Equal(original.Canvas.WidthPoints, document!.Canvas.WidthPoints, precision: 6);
- Assert.Equal(original.Canvas.HeightPoints, document.Canvas.HeightPoints, precision: 6);
- Assert.Equal(original.Canvas.DisplayUnit, document.Canvas.DisplayUnit);
- }
-
- [Fact]
- public void SaveThenLoad_RoundTripsElementPositionsAndProperties()
- {
- var original = BuildSampleDocument();
-
- TemplateLayoutXmlSerializer.Save(original, _tempPath);
- TemplateLayoutXmlSerializer.TryLoad(_tempPath, out var document, out var errors);
-
- Assert.NotNull(document);
- Assert.Equal(2, document!.Elements.Count);
-
- var loadedStatic = document.Elements.Single(e => !e.IsDynamic);
- Assert.Equal(120, loadedStatic.X, precision: 6);
- Assert.Equal(240, loadedStatic.Y, precision: 6);
- Assert.Equal("Static label:", loadedStatic.StaticText);
- Assert.Equal("Arial", loadedStatic.FontFamily);
- Assert.Equal(12, loadedStatic.FontSize, precision: 6);
- Assert.Equal(new RgbColor(200, 10, 30), loadedStatic.Color);
- Assert.Equal(0, loadedStatic.ZOrder);
-
- var loadedDynamic = document.Elements.Single(e => e.IsDynamic);
- Assert.Equal("Full Name", loadedDynamic.ColumnName);
- Assert.Equal("Arial [Bold]", loadedDynamic.FontFamily);
- Assert.Equal(14, loadedDynamic.FontSize, precision: 6);
- Assert.Equal(1, loadedDynamic.ZOrder);
- }
-
- [Theory]
- // Sprint 3, Batch 3: the persisted `column` attribute must round-trip arbitrary, non-hardcoded
- // CSV header text exactly, including characters XML must escape (`&`, `'`, `"`) — proving the
- // designer doesn't just work for the couple of column names already used elsewhere as
- // examples (e.g. "Full Name").
- [InlineData("Zip+4 Code")]
- [InlineData("Bill To & Ship To's Address")]
- [InlineData("Customer_Reference-No.")]
- [InlineData("Quoted \"Nickname\" Field")]
- public void SaveThenLoad_RoundTripsArbitraryColumnNameExactly(string columnName)
- {
- var document = new TemplateLayoutDocument(CanvasSettings.CreateDefault());
- document.Elements.Add(TextElementLayout.CreateDynamic(10, 20, columnName));
-
- TemplateLayoutXmlSerializer.Save(document, _tempPath);
- var loaded = TemplateLayoutXmlSerializer.TryLoad(_tempPath, out var reopened, out var errors);
-
- Assert.True(loaded, string.Join("; ", errors));
- Assert.Equal(columnName, reopened!.Elements.Single().ColumnName);
- }
-
- [Fact]
- public void Save_DynamicElement_PersistsColumnAttributeInRenderTimeFormat()
- {
- // Confirms the designer writes the exact attribute shape TEMPLATE_FORMAT.md/
- // TemplateXmlParser already consume at render time (`column="..."` on `<text>`), not a
- // different token syntax invented for the designer.
- var document = new TemplateLayoutDocument(CanvasSettings.CreateDefault());
- document.Elements.Add(TextElementLayout.CreateDynamic(10, 20, "Full Name"));
-
- TemplateLayoutXmlSerializer.Save(document, _tempPath);
- var xml = File.ReadAllText(_tempPath);
-
- Assert.Contains("column=\"Full Name\"", xml);
- }
-
- [Fact]
- public void Load_MissingFile_ReturnsFalseWithErrorNotException()
- {
- var result = TemplateLayoutXmlSerializer.TryLoad(
- Path.Combine(Path.GetTempPath(), $"does-not-exist-{Guid.NewGuid():N}.xml"), out var document, out var errors);
-
- Assert.False(result);
- Assert.Null(document);
- Assert.NotEmpty(errors);
- }
-
- [Fact]
- public void Load_MalformedXml_ReturnsFalseWithClearError()
- {
- File.WriteAllText(_tempPath, "<envelopeTemplate pageWidth=\"297\" pageHeight=\"684\"><text></envelopeTemplate>");
-
- var result = TemplateLayoutXmlSerializer.TryLoad(_tempPath, out var document, out var errors);
-
- Assert.False(result);
- Assert.Null(document);
- Assert.Contains(errors, e => e.Contains("not valid XML", StringComparison.OrdinalIgnoreCase));
- }
-
- [Fact]
- public void Load_WrongRootElement_ReturnsFalseWithClearError()
- {
- File.WriteAllText(_tempPath, "<notATemplate />");
-
- var result = TemplateLayoutXmlSerializer.TryLoad(_tempPath, out var document, out var errors);
-
- Assert.False(result);
- Assert.Null(document);
- Assert.Contains(errors, e => e.Contains("envelopeTemplate", StringComparison.Ordinal));
- }
-
- [Fact]
- public void Load_MissingRequiredPageDimension_ReturnsFalseWithClearError()
- {
- File.WriteAllText(_tempPath, "<envelopeTemplate pageHeight=\"684\"><text x=\"1\" y=\"1\" font=\"Arial\" size=\"12\">Hi</text></envelopeTemplate>");
-
- var result = TemplateLayoutXmlSerializer.TryLoad(_tempPath, out var document, out var errors);
-
- Assert.False(result);
- Assert.Contains(errors, e => e.Contains("pageWidth", StringComparison.Ordinal));
- }
-
- [Fact]
- public void Load_TextElementWithBothColumnAndInlineText_ReturnsFalseWithClearError()
- {
- File.WriteAllText(
- _tempPath,
- "<envelopeTemplate pageWidth=\"297\" pageHeight=\"684\">" +
- "<text x=\"1\" y=\"1\" font=\"Arial\" size=\"12\" column=\"Name\">Hi</text>" +
- "</envelopeTemplate>");
-
- var result = TemplateLayoutXmlSerializer.TryLoad(_tempPath, out var document, out var errors);
-
- Assert.False(result);
- Assert.Contains(errors, e => e.Contains("exactly one", StringComparison.OrdinalIgnoreCase));
- }
-
- [Fact]
- public void Load_InvalidColorValue_ReturnsFalseWithClearError()
- {
- File.WriteAllText(
- _tempPath,
- "<envelopeTemplate pageWidth=\"297\" pageHeight=\"684\">" +
- "<text x=\"1\" y=\"1\" font=\"Arial\" size=\"12\" color=\"notacolor\">Hi</text>" +
- "</envelopeTemplate>");
-
- var result = TemplateLayoutXmlSerializer.TryLoad(_tempPath, out var document, out var errors);
-
- Assert.False(result);
- Assert.Contains(errors, e => e.Contains("color", StringComparison.OrdinalIgnoreCase));
- }
-
- [Fact]
- public void Load_TemplateWithoutDesignerAttributes_LoadsWithDefaults()
- {
- // Matches Sprint 1's plain render-time template shape (no canvasUnit/color/zOrder) —
- // the designer must still be able to open it, per this story's back-compat intent.
- File.WriteAllText(
- _tempPath,
- "<envelopeTemplate pageWidth=\"297\" pageHeight=\"684\">" +
- "<text x=\"120\" y=\"240\" font=\"Arial\" size=\"12\">Static label:</text>" +
- "<text x=\"120\" y=\"225\" font=\"Arial\" size=\"12\" column=\"Full Name\" />" +
- "</envelopeTemplate>");
-
- var result = TemplateLayoutXmlSerializer.TryLoad(_tempPath, out var document, out var errors);
-
- Assert.True(result, string.Join("; ", errors));
- Assert.Equal(CanvasUnit.Points, document!.Canvas.DisplayUnit);
- Assert.Equal(2, document.Elements.Count);
- Assert.All(document.Elements, e => Assert.Equal(RgbColor.Black, e.Color));
- Assert.Equal(0, document.Elements[0].ZOrder);
- Assert.Equal(1, document.Elements[1].ZOrder);
- }
-
- [Fact]
- public void Load_NegativeZOrder_ClampsToZeroRatherThanErroring()
- {
- File.WriteAllText(
- _tempPath,
- "<envelopeTemplate pageWidth=\"297\" pageHeight=\"684\">" +
- "<text x=\"1\" y=\"1\" font=\"Arial\" size=\"12\" zOrder=\"-5\">Hi</text>" +
- "</envelopeTemplate>");
-
- var result = TemplateLayoutXmlSerializer.TryLoad(_tempPath, out var document, out var errors);
-
- Assert.True(result, string.Join("; ", errors));
- Assert.Equal(0, document!.Elements[0].ZOrder);
- }
-
- [Fact]
- public void Save_OverwritesExistingFile()
- {
- var first = new TemplateLayoutDocument(CanvasSettings.CreateDefault());
- first.Elements.Add(TextElementLayout.CreateStatic(0, 0, "first"));
- TemplateLayoutXmlSerializer.Save(first, _tempPath);
-
- var second = new TemplateLayoutDocument(CanvasSettings.CreateDefault());
- second.Elements.Add(TextElementLayout.CreateStatic(0, 0, "second"));
- TemplateLayoutXmlSerializer.Save(second, _tempPath);
-
- TemplateLayoutXmlSerializer.TryLoad(_tempPath, out var document, out _);
-
- Assert.Single(document!.Elements);
- Assert.Equal("second", document.Elements[0].StaticText);
- }
- }
|