using Inspectron.Epson.Templates.Configuration; using Inspectron.Epson.Templates.Interpreter; using Inspectron.Epson.Templates.Language; using Inspectron.Epson.Templates.Language.Nodes; using Inspectron.Epson.Templates.Tests.Helpers; namespace Inspectron.Epson.Templates.Tests.Integration; public class KitchenReceiptTests { private readonly PrinterProfile _profile = new("tm-t30iii", "TM-T30III", 48, 24, false); private const string KitchenReceiptJson = """ { "Title": "Restaurant Test", "TransactionDateTime": "2024-03-15T14:30:00", "ReceiptNumber": "12345", "WaiterName": "John Doe", "WaiterId": "W001", "TableNumber": "5", "SpecialInstruction": "Rush Order", "Gangs": [ { "Id": 1, "Name": "Starters", "Dishes": [ { "Number": 2, "Name": "Caesar Salad", "Modifications": { "Removed": ["Croutons"], "Added": ["Extra Dressing"] }, "Comment": "No anchovies" } ] } ], "Dishes": [ { "Number": 1, "Name": "Grilled Salmon", "Modifications": { "Removed": [], "Added": [] }, "Comment": null } ] } """; private const string KitchenTemplate = """ #red,big,tall,center# {Title} # --- #center# {TransactionDateTime:dd-MMM-yy HH:mm} Nr.:{ReceiptNumber} # #center# {WaiterName} # #center# {WaiterId} # #big,bold,center# Tisch: {TableNumber} # @if SpecialInstruction #big,bold,center# {SpecialInstruction} # @end --- @foreach gang in Gangs #red,big,tall,center# {gang.Id}. {gang.Name} # @foreach dish in gang.Dishes #tall# {dish.Number}x {dish.Name} # @if dish.Modifications.Removed.count > 0 @foreach removed in dish.Modifications.Removed #bold,tall# - {removed} # @end @end @if dish.Modifications.Added.count > 0 @foreach added in dish.Modifications.Added #bold,tall# + {added} # @end @end @if dish.Comment #bold,tall# Comment: {dish.Comment} # @end @end @end @if Gangs.count > 0 --- @end @foreach dish in Dishes #tall# {dish.Number}x {dish.Name} # @end """; private TemplateNode Parse(string source) { var lexer = new Lexer(source); var parser = new Parser(lexer.Tokenize().Tokens); return parser.Parse().Template; } [Fact] public void KitchenReceipt_RendersFully() { var template = Parse(KitchenTemplate); var interpreter = new TemplateInterpreter(_profile); var commands = interpreter.Interpret(template, KitchenReceiptJson); // Verify key elements are present Assert.Contains(commands, c => c.Text.Contains("Restaurant Test")); Assert.Contains(commands, c => c.Text.Contains("John Doe")); Assert.Contains(commands, c => c.Text.Contains("Tisch: 5")); Assert.Contains(commands, c => c.Text.Contains("Rush Order")); Assert.Contains(commands, c => c.Text.Contains("1. Starters")); Assert.Contains(commands, c => c.Text.Contains("2x Caesar Salad")); Assert.Contains(commands, c => c.Text.Contains("- Croutons")); Assert.Contains(commands, c => c.Text.Contains("+ Extra Dressing")); Assert.Contains(commands, c => c.Text.Contains("Comment: No anchovies")); Assert.Contains(commands, c => c.Text.Contains("1x Grilled Salmon")); } [Fact] public void KitchenReceipt_AppliesStyles() { var template = Parse(KitchenTemplate); var interpreter = new TemplateInterpreter(_profile); var commands = interpreter.Interpret(template, KitchenReceiptJson); // Title should be big and tall var titleCmd = commands.FirstOrDefault(c => c.Text.Contains("Restaurant Test")); Assert.NotNull(titleCmd); Assert.True(titleCmd.IsBig); Assert.True(titleCmd.IsTall); // Table should be bold and big var tableCmd = commands.FirstOrDefault(c => c.Text.Contains("Tisch: 5")); Assert.NotNull(tableCmd); Assert.True(tableCmd.IsBold); Assert.True(tableCmd.IsBig); // Modifications should be bold and tall var modCmd = commands.FirstOrDefault(c => c.Text.Contains("- Croutons")); Assert.NotNull(modCmd); Assert.True(modCmd.IsBold); Assert.True(modCmd.IsTall); } [Fact] public void KitchenReceipt_GeneratesSeparators() { var template = Parse(KitchenTemplate); var interpreter = new TemplateInterpreter(_profile); var commands = interpreter.Interpret(template, KitchenReceiptJson); var separatorCount = commands.Count(c => c.Text.All(ch => ch == '-') && c.Text.Length == 48); Assert.True(separatorCount >= 2, $"Expected at least 2 separators, found {separatorCount}"); } [Fact] public void KitchenReceipt_FormatsDateTime() { var template = Parse(KitchenTemplate); var interpreter = new TemplateInterpreter(_profile); var commands = interpreter.Interpret(template, KitchenReceiptJson); Assert.Contains(commands, c => c.Text.Contains("15-Mar-24") || c.Text.Contains("15-Mär-24")); } [Fact] public void KitchenReceipt_WithoutSpecialInstruction_OmitsSection() { var jsonWithoutInstruction = """ { "Title": "Test", "TransactionDateTime": "2024-03-15T14:30:00", "ReceiptNumber": "123", "WaiterName": "John", "WaiterId": "W001", "TableNumber": "1", "SpecialInstruction": null, "Gangs": [], "Dishes": [] } """; var template = Parse(KitchenTemplate); var interpreter = new TemplateInterpreter(_profile); var commands = interpreter.Interpret(template, jsonWithoutInstruction); // Should not have any content that looks like special instruction Assert.DoesNotContain(commands, c => c.Text.Contains("Rush")); } }