Files
2026-02-03 14:27:13 +01:00

446 lines
12 KiB
C#

namespace Inspectron.Epson.TemplateEngine.Tests;
public class TemplateRendererTests
{
private readonly ReceiptTemplateEngine _engine = new();
[Fact]
public void Render_EmptyReceipt()
{
var commands = _engine.Render("<receipt></receipt>", "{}", 42, 22);
Assert.Empty(commands);
}
[Fact]
public void Render_SimpleLine()
{
var commands = _engine.Render(
"<receipt><line>Hello World</line></receipt>",
"{}", 42, 22);
Assert.Single(commands);
Assert.Equal("Hello World", commands[0].Text);
}
[Fact]
public void Render_LineWithDataBinding()
{
var commands = _engine.Render(
"<receipt><line>Hello {{Name}}</line></receipt>",
"""{"Name":"John"}""", 42, 22);
Assert.Single(commands);
Assert.Equal("Hello John", commands[0].Text);
}
[Fact]
public void Render_LineWithFormatting()
{
var commands = _engine.Render(
"""<receipt><line bold="true" big="true" tall="true" red="true">Text</line></receipt>""",
"{}", 42, 22);
var cmd = commands[0];
Assert.True(cmd.IsBold);
Assert.True(cmd.IsBig);
Assert.True(cmd.IsTall);
Assert.True(cmd.IsRed);
}
[Fact]
public void Render_LineWithCenterAlignment()
{
var commands = _engine.Render(
"""<receipt><line align="center">Test</line></receipt>""",
"{}", 42, 22);
Assert.Equal(" Test", commands[0].Text);
}
[Fact]
public void Render_LineWithCenterAlignment_BigFont()
{
var commands = _engine.Render(
"""<receipt><line align="center" big="true">Test</line></receipt>""",
"{}", 42, 22);
Assert.Equal(" Test", commands[0].Text);
}
[Fact]
public void Render_EmptyLine()
{
var commands = _engine.Render(
"<receipt><line /></receipt>",
"{}", 42, 22);
Assert.Single(commands);
Assert.Equal("", commands[0].Text);
}
[Fact]
public void Render_Separator()
{
var commands = _engine.Render(
"<receipt><separator /></receipt>",
"{}", 42, 22);
Assert.Single(commands);
Assert.Equal(new string('-', 42), commands[0].Text);
}
[Fact]
public void Render_Cut()
{
var commands = _engine.Render(
"<receipt><cut /></receipt>",
"{}", 42, 22);
Assert.Single(commands);
Assert.True(commands[0].IsCut);
}
[Fact]
public void Render_Feed()
{
var commands = _engine.Render(
"""<receipt><feed lines="3" /></receipt>""",
"{}", 42, 22);
Assert.Equal(3, commands.Count);
Assert.All(commands, c => Assert.Equal("", c.Text));
}
[Fact]
public void Render_Columns()
{
var commands = _engine.Render(
"""<receipt><columns left="Name" right="Price" /></receipt>""",
"{}", 42, 22);
Assert.Single(commands);
Assert.Equal(42, commands[0].Text.Length);
Assert.StartsWith("Name", commands[0].Text);
Assert.EndsWith("Price", commands[0].Text);
}
[Fact]
public void Render_ColumnsWithDataBinding()
{
var commands = _engine.Render(
"""<receipt><columns left="{{Label}}" right="{{Value}}" /></receipt>""",
"""{"Label":"Total","Value":"100.00"}""", 42, 22);
Assert.Single(commands);
Assert.Contains("Total", commands[0].Text);
Assert.Contains("100.00", commands[0].Text);
}
[Fact]
public void Render_Foreach()
{
var commands = _engine.Render("""
<receipt>
<foreach items="Items" var="item">
<line>{{item.Name}}</line>
</foreach>
</receipt>
""",
"""{"Items":[{"Name":"Pizza"},{"Name":"Pasta"},{"Name":"Salad"}]}""",
42, 22);
Assert.Equal(3, commands.Count);
Assert.Equal("Pizza", commands[0].Text);
Assert.Equal("Pasta", commands[1].Text);
Assert.Equal("Salad", commands[2].Text);
}
[Fact]
public void Render_Foreach_EmptyArray()
{
var commands = _engine.Render("""
<receipt>
<foreach items="Items" var="item">
<line>{{item.Name}}</line>
</foreach>
</receipt>
""",
"""{"Items":[]}""", 42, 22);
Assert.Empty(commands);
}
[Fact]
public void Render_Foreach_WithLoopVariables()
{
var commands = _engine.Render("""
<receipt>
<foreach items="Items" var="item">
<line>{{$index}}: {{item.Name}}</line>
</foreach>
</receipt>
""",
"""{"Items":[{"Name":"A"},{"Name":"B"},{"Name":"C"}]}""",
42, 22);
Assert.Equal(3, commands.Count);
Assert.Equal("0: A", commands[0].Text);
Assert.Equal("1: B", commands[1].Text);
Assert.Equal("2: C", commands[2].Text);
}
[Fact]
public void Render_NestedForeach()
{
var commands = _engine.Render("""
<receipt>
<foreach items="Groups" var="group">
<line>{{group.Name}}</line>
<foreach items="group.Items" var="item">
<line> {{item.Name}}</line>
</foreach>
</foreach>
</receipt>
""",
"""{"Groups":[{"Name":"G1","Items":[{"Name":"A"},{"Name":"B"}]},{"Name":"G2","Items":[{"Name":"C"}]}]}""",
42, 22);
Assert.Equal(5, commands.Count);
Assert.Equal("G1", commands[0].Text);
Assert.Equal(" A", commands[1].Text);
Assert.Equal(" B", commands[2].Text);
Assert.Equal("G2", commands[3].Text);
Assert.Equal(" C", commands[4].Text);
}
[Fact]
public void Render_If_True()
{
var commands = _engine.Render("""
<receipt>
<if test="Name">
<line>Has name: {{Name}}</line>
</if>
</receipt>
""",
"""{"Name":"John"}""", 42, 22);
Assert.Single(commands);
Assert.Equal("Has name: John", commands[0].Text);
}
[Fact]
public void Render_If_False()
{
var commands = _engine.Render("""
<receipt>
<if test="Name">
<line>Has name</line>
</if>
</receipt>
""",
"""{}""", 42, 22);
Assert.Empty(commands);
}
[Fact]
public void Render_IfElse_True()
{
var commands = _engine.Render("""
<receipt>
<if test="Name">
<line>Yes</line>
</if>
<else>
<line>No</line>
</else>
</receipt>
""",
"""{"Name":"John"}""", 42, 22);
Assert.Single(commands);
Assert.Equal("Yes", commands[0].Text);
}
[Fact]
public void Render_IfElse_False()
{
var commands = _engine.Render("""
<receipt>
<if test="Name">
<line>Yes</line>
</if>
<else>
<line>No</line>
</else>
</receipt>
""",
"""{}""", 42, 22);
Assert.Single(commands);
Assert.Equal("No", commands[0].Text);
}
[Fact]
public void Render_If_NegatedCondition()
{
var commands = _engine.Render("""
<receipt>
<if test="!$last">
<cut />
</if>
</receipt>
""",
"""{}""", 42, 22);
// $last is not set so not a boolean false, but the variable doesn't exist
// When there's no foreach context, $last is null, so !null = true
Assert.Single(commands);
Assert.True(commands[0].IsCut);
}
[Fact]
public void Render_Foreach_WithIfNotLast()
{
var commands = _engine.Render("""
<receipt>
<foreach items="Items" var="item">
<line>{{item.Name}}</line>
<if test="!$last">
<separator />
</if>
</foreach>
</receipt>
""",
"""{"Items":[{"Name":"A"},{"Name":"B"},{"Name":"C"}]}""",
42, 22);
// A, sep, B, sep, C = 5
Assert.Equal(5, commands.Count);
Assert.Equal("A", commands[0].Text);
Assert.Equal(new string('-', 42), commands[1].Text);
Assert.Equal("B", commands[2].Text);
Assert.Equal(new string('-', 42), commands[3].Text);
Assert.Equal("C", commands[4].Text);
}
[Fact]
public void Render_LineWithWrap()
{
var commands = _engine.Render(
"""<receipt><line wrap="true">The quick brown fox jumps over the lazy dog and more text here</line></receipt>""",
"{}", 30, 15);
Assert.True(commands.Count > 1);
}
[Fact]
public void Render_LineSpacing()
{
var commands = _engine.Render(
"""<receipt><line lineSpacing="50">Text</line></receipt>""",
"{}", 42, 22);
Assert.Equal(50, commands[0].SetLineSpacing);
}
[Fact]
public void Render_FormatString()
{
var commands = _engine.Render(
"<receipt><line>{{Price:F2}}</line></receipt>",
"""{"Price":9.5}""", 42, 22);
Assert.Equal("9.50", commands[0].Text);
}
[Fact]
public void Render_DateTimeFormat()
{
var commands = _engine.Render(
"<receipt><line>{{Date:dd.MM.yyyy}}</line></receipt>",
"""{"Date":"2024-03-15T14:30:00Z"}""", 42, 22);
Assert.Equal("15.03.2024", commands[0].Text);
}
[Fact]
public void Render_Row()
{
var commands = _engine.Render("""
<receipt>
<row>
<col width="14">Left</col>
<col width="14" align="center">Center</col>
<col width="14" align="right">Right</col>
</row>
</receipt>
""",
"{}", 42, 22);
Assert.Single(commands);
Assert.Equal(42, commands[0].Text.Length);
}
[Fact]
public void Render_InvalidXml_ThrowsParsingException()
{
Assert.Throws<TemplateParsingException>(() =>
_engine.Render("not xml", "{}", 42, 22));
}
[Fact]
public void Render_InvalidJson_ThrowsRenderingException()
{
Assert.Throws<TemplateRenderingException>(() =>
_engine.Render("<receipt></receipt>", "not json", 42, 22));
}
[Fact]
public void Render_ComplexTemplate()
{
var template = """
<receipt>
<line align="center" big="true" red="true">Restaurant</line>
<separator />
<line />
<line align="center">{{DateTime:dd-MMM-yy HH:mm}} Nr.:{{Number}}</line>
<line align="center" big="true" bold="true">Tisch: {{Table}}</line>
<separator />
<foreach items="Items" var="item">
<line bold="true">{{item.Qty}}x {{item.Name}}</line>
</foreach>
<separator />
<columns left="Total:" right="{{Total:F2}} CHF" bold="true" />
<cut />
</receipt>
""";
var json = """
{
"DateTime": "2024-03-15T14:30:00Z",
"Number": "42",
"Table": "5",
"Items": [
{"Qty": 2, "Name": "Margherita"},
{"Qty": 1, "Name": "Tiramisu"}
],
"Total": 45.50
}
""";
var commands = _engine.Render(template, json, 42, 22);
Assert.True(commands.Count >= 10);
// Header
Assert.True(commands[0].IsRed);
Assert.True(commands[0].IsBig);
Assert.Contains("Restaurant", commands[0].Text);
// Last command is cut
Assert.True(commands[^1].IsCut);
}
}