package com.hubspot.jinjava.el; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; import java.util.Map; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.google.common.base.Throwables; import com.google.common.collect.Lists; import com.google.common.io.Resources; import com.hubspot.jinjava.Jinjava; import com.hubspot.jinjava.interpret.Context; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.TemplateError.ErrorReason; @SuppressWarnings("unchecked") public class ExtendedSyntaxBuilderTest { Context context; JinjavaInterpreter interpreter; @Before public void setup() { interpreter = new Jinjava().newInterpreter(); JinjavaInterpreter.pushCurrent(interpreter); context = interpreter.getContext(); } @After public void cleanup() { JinjavaInterpreter.popCurrent(); } @Test public void itHandlesNonBreakingSpaceProperly() { context.put("foo", "bar"); assertThat(val("\u00A0foo\u00A0")).isEqualTo("bar"); } @Test public void nestedFilters() { context.put("content", "mycontent"); assertThat(val("content|striptags|truncate(100, false)")).isEqualTo("mycontent"); } @Test public void positivePrefixOp() { assertThat(val("+20")).isEqualTo(20L); assertThat(val("2 + 5")).isEqualTo(7L); } @Test public void expTestOp() { context.put("foo", "myfoo"); assertThat(val("foo is defined")).isEqualTo(true); assertThat(val("bar is defined")).isEqualTo(false); assertThat(val("12 is divisibleby 3")).isEqualTo(true); assertThat(val("'foo' is string")).isEqualTo(true); assertThat(val("49 is odd")).isEqualTo(true); } @Test public void stringExpTestOps() { assertThat(val("'football' is string_startingwith 'foot'")).isEqualTo(true); assertThat(val("'football' is string_startingwith 'ball'")).isEqualTo(false); assertThat(val("'football' is string_containing 'tb'")).isEqualTo(true); assertThat(val("'football' is string_containing 'golf'")).isEqualTo(false); } @Test public void namedFnArgs() { context.put("path", "/page"); assertThat(val("path=='/' or truncate(path,length=5,killwords=true,end='')=='/page'")).isEqualTo(true); assertThat(val("truncate('foobar', length = 3)")).isEqualTo("f..."); } @Test public void stringConcat() { context.put("foo", "xx"); assertThat(val("foo + 'bar' + foo")).isEqualTo("xxbarxx"); assertThat(val("123 + 'xx'")).isEqualTo("123xx"); } @Test public void stringConcatOperator() { context.put("foo", 123); assertThat(val("foo ~ 456")).isEqualTo("123456"); assertThat(val("'foo' ~ 'bar'")).isEqualTo("foobar"); } @Test public void stringInStringOperator() { assertThat(val("'foo' in 'foobar'")).isEqualTo(true); assertThat(val("'gg' in 'foobar'")).isEqualTo(false); } @Test public void objInCollectionOperator() { assertThat(val("12 in [1, 2, 3]")).isEqualTo(false); assertThat(val("12 in [1, 12, 3]")).isEqualTo(true); } @Test public void conditionalExprWithNoElse() { context.put("foo", "bar"); assertThat(val("'hello' if foo=='bar'")).isEqualTo("hello"); assertThat(val("'hello' if foo=='barf'")).isNull(); } @Test public void conditionalExprWithElse() { context.put("foo", "bar"); assertThat(val("'hello' if foo=='bar' else 'barf'")).isEqualTo("hello"); assertThat(val("'hello' if foo=='barf' else 'hi'")).isEqualTo("hi"); } @Test public void newlineEscChar() { context.put("comment", "foo\nbar"); assertThat(val("comment|replace('\\n', '<br/>')")).isEqualTo("foo<br/>bar"); } @Test public void literalList() { context.put("foo", "bar"); assertThat((List<Object>) val("[1, foo, 'foo']")).containsExactly(1L, "bar", "foo"); } @Test(expected = UnsupportedOperationException.class) public void literalTuple() { context.put("foo", "bar"); List<Object> list = (List<Object>) val("(1, foo, 'foo')"); assertThat(list).containsExactly(1L, "bar", "foo"); list.add("xx"); } @Test public void mapLiteral() { context.put("foo", "bar"); assertThat((Map<String, Object>) val("{}")).isEmpty(); Map<String, Object> map = (Map<String, Object>) val("{foo: foo, \"foo2\": foo, foo3: 123, foo4: 'string', foo5: {}, foo6: [1, 2]}"); assertThat(map).contains(entry("foo", "bar"), entry("foo2", "bar"), entry("foo3", 123L), entry("foo4", "string"), entry("foo6", Arrays.asList(1L, 2L))); assertThat((Map<String, Object>) val("{\"address\":\"123 Main - Boston, MA 02111\"}")) .contains(entry("address", "123 Main - Boston, MA 02111")); } @Test public void complexMapLiteral() { Map<String, Object> map = (Map<String, Object>) val(fixture("complex")); assertThat(map).hasSize(11); assertThat((Map<String, Object>) map.get("Boston")).contains(entry("city", "Boston")); } @Test public void itParsesDictWithVariableRefs() throws Exception { List<?> theList = Lists.newArrayList(1L, 2L, 3L); context.put("the_list", theList); context.put("i_am_seven", 7L); context.put("False", false); Map<String, Object> map = (Map<String, Object>) val(fixture("dict-with-var-refs")); assertThat(map).contains(entry("seven", 7L), entry("the_list", theList)); List<Object> innerList = (List<Object>) map.get("inner_list_literal"); assertThat(innerList).containsExactly("heya", false); Map<String, Object> innerDict = (Map<String, Object>) map.get("inner_dict"); assertThat(innerDict).contains(entry("car keys", "valuable")); } @Test public void itReturnsLeftResultForOrExpr() throws Exception { context.put("left", "foo"); context.put("right", "bar"); assertThat(val("left or right")).isEqualTo("foo"); } @Test public void itReturnsRightResultForOrExpr() throws Exception { context.put("right", "bar"); assertThat(val("left or right")).isEqualTo("bar"); } private String fixture(String name) { try { return Resources.toString( Resources.getResource(String.format("el/dict/%s.fixture", name)), StandardCharsets.UTF_8); } catch (IOException e) { throw Throwables.propagate(e); } } @Test public void testParseExp() throws Exception { context.put("foo", "fff"); context.put("a", "aaa"); context.put("b", "bbb"); assertThat(val("foo|length > 5")).isEqualTo(Boolean.FALSE); } @Test public void listRangeSyntax() throws Exception { List<?> theList = Lists.newArrayList(1, 2, 3, 4, 5); context.put("mylist", theList); assertThat(val("mylist[0:3]")).isEqualTo(Lists.newArrayList(1, 2, 3)); assertThat(val("mylist[5:15]")).isEqualTo(Lists.newArrayList()); assertThat(val("mylist[2]")).isEqualTo(3); } @Test public void invalidNestedAssignmentExpr() throws Exception { assertThat(val("content.template_path = 'Custom/Email/Responsive/testing.html'")).isEqualTo(""); assertThat(interpreter.getErrors()).isNotEmpty(); assertThat(interpreter.getErrors().get(0).getReason()).isEqualTo(ErrorReason.SYNTAX_ERROR); assertThat(interpreter.getErrors().get(0).getMessage()).containsIgnoringCase("identifier"); } @Test public void invalidIdentifierAssignmentExpr() throws Exception { assertThat(val("content = 'Custom/Email/Responsive/testing.html'")).isEqualTo(""); assertThat(interpreter.getErrors()).isNotEmpty(); assertThat(interpreter.getErrors().get(0).getReason()).isEqualTo(ErrorReason.SYNTAX_ERROR); assertThat(interpreter.getErrors().get(0).getMessage()).containsIgnoringCase("'='"); } @Test public void invalidPipeOperatorExpr() throws Exception { assertThat(val("topics|1")).isEqualTo(""); assertThat(interpreter.getErrors()).isNotEmpty(); assertThat(interpreter.getErrors().get(0).getReason()).isEqualTo(ErrorReason.SYNTAX_ERROR); } private Object val(String expr) { return interpreter.resolveELExpression(expr, -1); } }