template engine
This commit is contained in:
@@ -0,0 +1,290 @@
|
||||
using System.Text.Json;
|
||||
using Inspectron.Epson.TemplateEngine.DataBinding;
|
||||
|
||||
namespace Inspectron.Epson.TemplateEngine.Tests;
|
||||
|
||||
public class ExpressionEvaluatorTests
|
||||
{
|
||||
private static DataContext CreateContext(string json)
|
||||
{
|
||||
var root = JsonDocument.Parse(json).RootElement;
|
||||
return new DataContext(root);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Evaluate_SimpleProperty()
|
||||
{
|
||||
var ctx = CreateContext("""{"Name":"John"}""");
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.Equal("Hello John", eval.Evaluate("Hello {{Name}}"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Evaluate_NestedProperty()
|
||||
{
|
||||
var ctx = CreateContext("""{"Person":{"Name":"Alice"}}""");
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.Equal("Hi Alice", eval.Evaluate("Hi {{Person.Name}}"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Evaluate_MissingProperty_ReturnsEmpty()
|
||||
{
|
||||
var ctx = CreateContext("""{"Name":"John"}""");
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.Equal("Hi ", eval.Evaluate("Hi {{Missing}}"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Evaluate_NumberProperty()
|
||||
{
|
||||
var ctx = CreateContext("""{"Count":42}""");
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.Equal("Count: 42", eval.Evaluate("Count: {{Count}}"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Evaluate_FormatString_Decimal()
|
||||
{
|
||||
var ctx = CreateContext("""{"Price":9.5}""");
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.Equal("9.50", eval.Evaluate("{{Price:F2}}"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Evaluate_FormatString_DateTime()
|
||||
{
|
||||
var ctx = CreateContext("""{"Date":"2024-03-15T14:30:00Z"}""");
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.Equal("15-Mar-24 14:30", eval.Evaluate("{{Date:dd-MMM-yy HH:mm}}"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Evaluate_MultipleExpressions()
|
||||
{
|
||||
var ctx = CreateContext("""{"First":"John","Last":"Doe"}""");
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.Equal("John Doe", eval.Evaluate("{{First}} {{Last}}"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Evaluate_CaseInsensitivePropertyLookup()
|
||||
{
|
||||
var ctx = CreateContext("""{"name":"Alice"}""");
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.Equal("Alice", eval.Evaluate("{{Name}}"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Evaluate_EmptyTemplate()
|
||||
{
|
||||
var ctx = CreateContext("""{}""");
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.Equal("", eval.Evaluate(""));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Evaluate_NoExpressions()
|
||||
{
|
||||
var ctx = CreateContext("""{}""");
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.Equal("plain text", eval.Evaluate("plain text"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Evaluate_LoopVariable_Index()
|
||||
{
|
||||
var ctx = CreateContext("""{}""");
|
||||
ctx.SetLoopVariable("$index", 3);
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.Equal("Index: 3", eval.Evaluate("Index: {{$index}}"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCondition_TruthyString()
|
||||
{
|
||||
var ctx = CreateContext("""{"Name":"John"}""");
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.True(eval.EvaluateCondition("Name"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCondition_EmptyString_IsFalsy()
|
||||
{
|
||||
var ctx = CreateContext("""{"Name":""}""");
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.False(eval.EvaluateCondition("Name"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCondition_MissingProperty_IsFalsy()
|
||||
{
|
||||
var ctx = CreateContext("""{}""");
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.False(eval.EvaluateCondition("Name"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCondition_Negation()
|
||||
{
|
||||
var ctx = CreateContext("""{"Name":"John"}""");
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.False(eval.EvaluateCondition("!Name"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCondition_Negation_Missing()
|
||||
{
|
||||
var ctx = CreateContext("""{}""");
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.True(eval.EvaluateCondition("!Name"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCondition_NullValue_IsFalsy()
|
||||
{
|
||||
var ctx = CreateContext("""{"Name":null}""");
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.False(eval.EvaluateCondition("Name"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCondition_Comparison_Equal()
|
||||
{
|
||||
var ctx = CreateContext("""{"Count":5}""");
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.True(eval.EvaluateCondition("Count==5"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCondition_Comparison_NotEqual()
|
||||
{
|
||||
var ctx = CreateContext("""{"Count":5}""");
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.True(eval.EvaluateCondition("Count!=3"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCondition_Comparison_GreaterThan()
|
||||
{
|
||||
var ctx = CreateContext("""{"Count":5}""");
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.True(eval.EvaluateCondition("Count>3"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCondition_Comparison_StringEqual()
|
||||
{
|
||||
var ctx = CreateContext("""{"Status":"active"}""");
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.True(eval.EvaluateCondition("Status=='active'"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCondition_LoopVariable_First()
|
||||
{
|
||||
var ctx = CreateContext("""{}""");
|
||||
ctx.SetLoopVariable("$first", true);
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.True(eval.EvaluateCondition("$first"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCondition_LoopVariable_NotLast()
|
||||
{
|
||||
var ctx = CreateContext("""{}""");
|
||||
ctx.SetLoopVariable("$last", false);
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.True(eval.EvaluateCondition("!$last"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCondition_NonEmptyArray_IsTruthy()
|
||||
{
|
||||
var ctx = CreateContext("""{"Items":[1,2,3]}""");
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.True(eval.EvaluateCondition("Items"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCondition_EmptyArray_IsFalsy()
|
||||
{
|
||||
var ctx = CreateContext("""{"Items":[]}""");
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.False(eval.EvaluateCondition("Items"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCondition_BooleanTrue_IsTruthy()
|
||||
{
|
||||
var ctx = CreateContext("""{"Active":true}""");
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.True(eval.EvaluateCondition("Active"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EvaluateCondition_BooleanFalse_IsFalsy()
|
||||
{
|
||||
var ctx = CreateContext("""{"Active":false}""");
|
||||
var eval = new ExpressionEvaluator(ctx);
|
||||
|
||||
Assert.False(eval.EvaluateCondition("Active"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DataContext_ChildScope_InheritsParentVariables()
|
||||
{
|
||||
var ctx = CreateContext("""{}""");
|
||||
var item = JsonDocument.Parse("""{"Name":"Pizza"}""").RootElement;
|
||||
ctx.SetVariable("item", item);
|
||||
|
||||
var child = ctx.CreateChildScope();
|
||||
var childEval = new ExpressionEvaluator(child);
|
||||
|
||||
Assert.Equal("Pizza", childEval.Evaluate("{{item.Name}}"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DataContext_ChildScope_OverridesParentVariable()
|
||||
{
|
||||
var ctx = CreateContext("""{}""");
|
||||
var item1 = JsonDocument.Parse("""{"Name":"Pizza"}""").RootElement;
|
||||
ctx.SetVariable("item", item1);
|
||||
|
||||
var child = ctx.CreateChildScope();
|
||||
var item2 = JsonDocument.Parse("""{"Name":"Pasta"}""").RootElement;
|
||||
child.SetVariable("item", item2);
|
||||
|
||||
var childEval = new ExpressionEvaluator(child);
|
||||
Assert.Equal("Pasta", childEval.Evaluate("{{item.Name}}"));
|
||||
|
||||
var parentEval = new ExpressionEvaluator(ctx);
|
||||
Assert.Equal("Pizza", parentEval.Evaluate("{{item.Name}}"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user