public void TestPropertyInheritance() { var scriptObject = new ScriptObject { { "a", new MyObject { PropertyA = "ClassA" } }, { "b", new MyObject2 { PropertyA = "ClassB", PropertyC = "ClassB-PropC" } } }; var context = new TemplateContext(); context.PushGlobal(scriptObject); var result = Template.Parse("{{a.property_a}}-{{b.property_a}}-{{b.property_c}}").Render(context); EqualityCompareResult compareResult = TextAssert.Equal("ClassA-ClassB-ClassB-PropC", result); if (compareResult.VerboseMessage != null && compareResult.VerboseMessage.Count > 0) { foreach (string vmsg in compareResult.VerboseMessage) { _output.WriteLine(vmsg); } } Assert.True(compareResult.IsEqual); }
public void TestFrontMatter() { var options = new LexerOptions() { Mode = ScriptMode.FrontMatterAndContent }; var input = @"+++ variable = 1 name = 'yes' +++ This is after the frontmatter: {{ name }} {{ variable + 1 }}"; input = input.Replace("\r\n", "\n"); var template = ParseTemplate(_output, input, options); // Make sure that we have a front matter Assert.NotNull(template.Page.FrontMatter); var context = new TemplateContext(); // Evaluate front-matter var frontResult = context.Evaluate(template.Page.FrontMatter); Assert.Null(frontResult); // Evaluate page-content context.Evaluate(template.Page); var pageResult = context.Output.ToString(); EqualityCompareResult compareResult = TextAssert.Equal("This is after the frontmatter: yes\n2", pageResult); if (compareResult.VerboseMessage != null && compareResult.VerboseMessage.Count > 0) { foreach (string vmsg in compareResult.VerboseMessage) { _output.WriteLine(vmsg); } } Assert.True(compareResult.IsEqual); }
public void TestDynamicVariable() { var context = new TemplateContext { TryGetVariable = (TemplateContext templateContext, SourceSpan span, ScriptVariable variable, out object value) => { value = null; if (variable.Name == "myvar") { value = "yes"; return(true); } return(false); } }; { var template = Template.Parse("Test with a dynamic {{ myvar }}"); context.Evaluate(template.Page); var result = context.Output.ToString(); EqualityCompareResult compareResult = TextAssert.Equal("Test with a dynamic yes", result); if (compareResult.VerboseMessage != null && compareResult.VerboseMessage.Count > 0) { foreach (string vmsg in compareResult.VerboseMessage) { _output.WriteLine(vmsg); } } Assert.True(compareResult.IsEqual); } { // Test StrictVariables var template = Template.Parse("Test with a dynamic {{ myvar2 }}"); context.StrictVariables = true; var exception = Assert.Throws <ScriptRuntimeException>(() => context.Evaluate(template.Page)); var result = exception.ToString(); var check = string.Format(SRS.VariableNotFound, "myvar2"); Assert.True(result.Contains(check), $"The exception string `{result}` does not contain the expected value"); } }
public void TestNullableArgument() { var template = Template.Parse("{{ tester 'input1' 1 }}"); var context = new TemplateContext(); var testerObj = new ScriptObjectWithNullable(); context.PushGlobal(testerObj); var result = template.Render(context); EqualityCompareResult compareResult = TextAssert.Equal("input1 Value: 1", result); if (compareResult.VerboseMessage != null && compareResult.VerboseMessage.Count > 0) { foreach (string vmsg in compareResult.VerboseMessage) { _output.WriteLine(vmsg); } } Assert.True(compareResult.IsEqual); }
public void TestJekyllInclude() { var input = "{% include /this/is/a/test.htm %}"; var template = Template.ParseLiquid(input, lexerOptions: new LexerOptions() { EnableIncludeImplicitString = true, Mode = ScriptMode.Liquid }); var context = new TemplateContext { TemplateLoader = new LiquidCustomTemplateLoader() }; var result = template.Render(context); EqualityCompareResult compareResult = TextAssert.Equal("/this/is/a/test.htm", result); if (compareResult.VerboseMessage != null && compareResult.VerboseMessage.Count > 0) { foreach (string vmsg in compareResult.VerboseMessage) { _output.WriteLine(vmsg); } } Assert.True(compareResult.IsEqual); }
public void TestDynamicMember() { var template = Template.Parse("Test with a dynamic {{ a.myvar }}"); var globalObject = new ScriptObject(); globalObject.SetValue("a", new ScriptObject(), true); var context = new TemplateContext { TryGetMember = (TemplateContext localContext, SourceSpan span, object target, string member, out object value) => { value = null; if (member == "myvar") { value = "yes"; return(true); } return(false); } }; context.PushGlobal(globalObject); context.Evaluate(template.Page); var result = context.Output.ToString(); EqualityCompareResult compareResult = TextAssert.Equal("Test with a dynamic yes", result); if (compareResult.VerboseMessage != null && compareResult.VerboseMessage.Count > 0) { foreach (string vmsg in compareResult.VerboseMessage) { _output.WriteLine(vmsg); } } Assert.True(compareResult.IsEqual); }
public static TemplateCompareResult TestTemplate(string expected, string input, bool isLiquid = false, bool isRoundtripTest = false, bool supportExactRoundtrip = true, object model = null, bool specialLiquid = false) { var parserOptions = new ParserOptions() { ConvertLiquidFunctions = isLiquid }; var lexerOptions = new LexerOptions() { Mode = isLiquid ? ScriptMode.Liquid : ScriptMode.Default }; if (isRoundtripTest) { lexerOptions.KeepTrivia = true; } if (specialLiquid) { parserOptions.ExpressionDepthLimit = 500; } List <string> tokensDesc = new List <string>(); var lexer = new Lexer(input, options: lexerOptions); foreach (var token in lexer) { tokensDesc.Add($"{token.Type}: {token.GetText(input)}"); } string roundtripText = null; List <string> verboseMessage = new List <string>(); // We loop first on input text, then on rountrip while (true) { bool isRoundtrip = roundtripText != null; bool hasErrors = false; if (isRoundtrip) { verboseMessage.Add("Rountrip"); verboseMessage.Add("======================================"); verboseMessage.Add(roundtripText); lexerOptions.Mode = ScriptMode.Default; if (lexerOptions.Mode == ScriptMode.Default && !isLiquid && supportExactRoundtrip) { verboseMessage.Add("Checking Exact Roundtrip - Input"); verboseMessage.Add("======================================"); EqualityCompareResult equalResult = TextAssert.Equal(input, roundtripText); if (equalResult.VerboseMessage != null && equalResult.VerboseMessage.Count > 0) { verboseMessage.AddRange(equalResult.VerboseMessage); } if (equalResult.IsEqual == false) { return(new TemplateCompareResult() { Tokens = tokensDesc, IsEqual = false, VerboseMessage = verboseMessage, DebugCode = "INPUT_ROUNDTRIP_TEXT_NOT_EQUAL" }); } } input = roundtripText; } else { verboseMessage.Add("Input"); verboseMessage.Add("======================================"); verboseMessage.Add(input); } var template = Template.Parse(input, "text", parserOptions, lexerOptions); var result = string.Empty; if (template.HasErrors) { hasErrors = true; for (int i = 0; i < template.Messages.Count; i++) { var message = template.Messages[i]; if (i > 0) { result += "\n"; } result += message; } if (specialLiquid) { throw new InvalidOperationException("Parser errors: " + result); } } else { if (isRoundtripTest) { result = template.ToText(); } else { if (template.Page == null) { return(new TemplateCompareResult() { Tokens = tokensDesc, IsEqual = false, VerboseMessage = verboseMessage, DebugCode = "PAGE_IS_NULL", Result = result, Expected = expected }); } if (!isRoundtrip) { // Dumps the rountrip version var lexerOptionsForTrivia = lexerOptions; lexerOptionsForTrivia.KeepTrivia = true; var templateWithTrivia = Template.Parse(input, "input", parserOptions, lexerOptionsForTrivia); roundtripText = templateWithTrivia.ToText(); } try { // Setup a default model context for the tests if (model == null) { var scriptObj = new ScriptObject { ["page"] = new ScriptObject { ["title"] = "This is a title" }, ["user"] = new ScriptObject { ["name"] = "John" }, ["product"] = new ScriptObject { ["title"] = "Orange", ["type"] = "fruit" }, ["products"] = new ScriptArray() { new ScriptObject { ["title"] = "Orange", ["type"] = "fruit" }, new ScriptObject { ["title"] = "Banana", ["type"] = "fruit" }, new ScriptObject { ["title"] = "Apple", ["type"] = "fruit" }, new ScriptObject { ["title"] = "Computer", ["type"] = "electronics" }, new ScriptObject { ["title"] = "Mobile Phone", ["type"] = "electronics" }, new ScriptObject { ["title"] = "Table", ["type"] = "furniture" }, new ScriptObject { ["title"] = "Sofa", ["type"] = "furniture" }, } }; scriptObj.Import(typeof(SpecialFunctionProvider)); model = scriptObj; } var context = isLiquid ? new LiquidTemplateContext() { TemplateLoader = new LiquidCustomTemplateLoader() } : new TemplateContext() { TemplateLoader = new CustomTemplateLoader() }; // We use a custom output to make sure that all output is using the "\n" context.PushOutput(new TextWriterOutput(new StringWriter() { NewLine = "\n" })); var contextObj = new ScriptObject(); contextObj.Import(model); context.PushGlobal(contextObj); result = template.Render(context); } catch (Exception exception) { if (specialLiquid) { throw; } else { result = GetReason(exception); } } } } var testContext = isRoundtrip ? "Roundtrip - " : String.Empty; verboseMessage.Add($"{testContext}Result"); verboseMessage.Add("======================================"); verboseMessage.Add(result); verboseMessage.Add($"{testContext}Expected"); verboseMessage.Add("======================================"); verboseMessage.Add(expected); EqualityCompareResult testEqualResult = TextAssert.Equal(expected, result); if (testEqualResult.VerboseMessage != null && testEqualResult.VerboseMessage.Count > 0) { verboseMessage.AddRange(testEqualResult.VerboseMessage); } if (testEqualResult.IsEqual == false) { return(new TemplateCompareResult { IsEqual = false, VerboseMessage = verboseMessage, DebugCode = "UNKNOWN", Tokens = tokensDesc, Result = result, Expected = expected }); } if (isRoundtripTest || isRoundtrip || hasErrors) { break; } } return(new TemplateCompareResult { IsEqual = true, VerboseMessage = verboseMessage, DebugCode = "", Tokens = tokensDesc }); }
public void TestFrontMatterOnly() { var options = new ParserOptions(); var input = @"+++ variable = 1 name = 'yes' +++ This is after the frontmatter: {{ name }} {{ variable + 1 }}"; input = input.Replace("\r\n", "\n"); var lexer = new Lexer(input, null, new LexerOptions() { Mode = ScriptMode.FrontMatterOnly }); var parser = new Parser(lexer, options); var page = parser.Run(); foreach (var message in parser.Messages) { _output.WriteLine(message.Message); } Assert.False(parser.HasErrors); // Check that the parser finished parsing on the first code exit }} // and hasn't tried to run the lexer on the remaining text Assert.Equal(new TextPosition(30, 3, 0), parser.CurrentSpan.Start); Assert.Equal(new TextPosition(33, 3, 3), parser.CurrentSpan.End); var startPositionAfterFrontMatter = parser.CurrentSpan.End.NextLine(); // Make sure that we have a front matter Assert.NotNull(page.FrontMatter); Assert.Null(page.Body); var context = new TemplateContext(); // Evaluate front-matter var frontResult = context.Evaluate(page.FrontMatter); Assert.Null(frontResult); lexer = new Lexer(input, null, new LexerOptions() { StartPosition = startPositionAfterFrontMatter }); parser = new Parser(lexer); page = parser.Run(); foreach (var message in parser.Messages) { _output.WriteLine(message.Message); } Assert.False(parser.HasErrors); context.Evaluate(page); var pageResult = context.Output.ToString(); EqualityCompareResult compareResult = TextAssert.Equal("This is after the frontmatter: yes\n2", pageResult); if (compareResult.VerboseMessage != null && compareResult.VerboseMessage.Count > 0) { foreach (string vmsg in compareResult.VerboseMessage) { _output.WriteLine(vmsg); } } Assert.True(compareResult.IsEqual); }
public void TestJson() { System.Data.DataTable dataTable = new System.Data.DataTable(); dataTable.Columns.Add("Column1"); dataTable.Columns.Add("Column2"); System.Data.DataRow dataRow = dataTable.NewRow(); dataRow["Column1"] = "Hello"; dataRow["Column2"] = "World"; dataTable.Rows.Add(dataRow); dataRow = dataTable.NewRow(); dataRow["Column1"] = "Bonjour"; dataRow["Column2"] = "le monde"; dataTable.Rows.Add(dataRow); string json = JsonConvert.SerializeObject(dataTable); _output.WriteLine("Json: " + json); var parsed = JsonConvert.DeserializeObject(json); _output.WriteLine("Parsed: " + parsed); string myTemplate = @" [ { {{ for tbr in tb }} ""N"": {{tbr.Column1}}, ""M"": {{tbr.Column2}} {{ end }} }, ] {{tb}} "; // Parse the template var template = Template.Parse(myTemplate); // Render var context = new TemplateContext { MemberRenamer = member => member.Name }; var scriptObject = new ScriptObject(); scriptObject.Import(new { tb = parsed }); context.PushGlobal(scriptObject); var result = template.Render(context); context.PopGlobal(); var expected = @" [ { ""N"": Hello, ""M"": World ""N"": Bonjour, ""M"": le monde }, ] [[[Hello], [World]], [[Bonjour], [le monde]]] "; TextAssert.Equal(expected, result); }