package com.hubspot.jinjava.lib.tag; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import com.google.common.collect.ImmutableMap; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.select.Elements; import org.junit.Before; import org.junit.Test; import com.google.common.base.Splitter; import com.google.common.base.Throwables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; 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.tree.Node; import com.hubspot.jinjava.tree.TagNode; import com.hubspot.jinjava.tree.TreeParser; public class ForTagTest { ForTag tag; Context context; JinjavaInterpreter interpreter; Jinjava jinjava; @Before public void setup() { jinjava = new Jinjava(); interpreter = jinjava.newInterpreter(); context = interpreter.getContext(); tag = new ForTag(); } @Test public void forLoopUsingLoopLastVar() throws Exception { context.put("the_list", Lists.newArrayList(1L, 2L, 3L, 7L)); TagNode tagNode = (TagNode) fixture("loop-last-var"); Document dom = Jsoup.parseBodyFragment(tag.interpret(tagNode, interpreter)); assertThat(dom.select("h3")).hasSize(3); } @Test public void forLoopNestedFor() throws Exception { TagNode tagNode = (TagNode) fixture("nested-fors"); assertThat(Splitter.on("\n").trimResults().omitEmptyStrings().split( tag.interpret(tagNode, interpreter))) .contains("02", "03", "12", "13"); } @Test public void forLoopMultipleLoopVars() throws Exception { Map<String, Object> dict = Maps.newHashMap(); dict.put("foo", "one"); dict.put("bar", 2L); context.put("the_dictionary", dict); TagNode tagNode = (TagNode) fixture("multiple-loop-vars"); Document dom = Jsoup.parseBodyFragment(tag.interpret(tagNode, interpreter)); assertThat(dom.select("p")).hasSize(2); } @Test public void forLoopMultipleLoopVarsArbitraryNames() throws Exception { Map<String, Object> dict = ImmutableMap.of( "grand", "ol'", "adserving", "team"); context.put("the_dictionary", dict); String template = "" + "{% for foo, bar in the_dictionary.items() %}" + "{{ foo }}: {{ bar }}\n" + "{% endfor %}"; String rendered = jinjava.render(template, context); assertEquals("grand: ol'\nadserving: team\n", rendered); } @Test public void forLoopLiteralLoopExpr() throws Exception { TagNode tagNode = (TagNode) fixture("literal-loop-expr"); assertThat(tag.interpret(tagNode, interpreter)).isEqualTo("012345"); } @Test public void forLoopWithNestedCycle() throws Exception { context.put("cycle1", "odd"); context.put("cycle2", "even"); TagNode tagNode = (TagNode) fixture("nested-cycle"); List<String> result = Lists.newArrayList(Splitter.on("\n").omitEmptyStrings().trimResults().split(tag.interpret(tagNode, interpreter))); assertThat(result).containsExactly("odd", "even", "odd", "even", "odd"); } @Test public void forLoopIndexVar() throws Exception { TagNode tagNode = (TagNode) fixture("loop-index-var"); assertThat(tag.interpret(tagNode, interpreter)).isEqualTo("012345"); } @Test public void forLoopSupportsAllLoopVarsInHublDocs() throws Exception { TagNode tagNode = (TagNode) fixture("hubl-docs-loop-vars"); Document dom = Jsoup.parseBodyFragment(tag.interpret(tagNode, interpreter)); Elements els = dom.select(".item"); assertThat(els).hasSize(4); assertThat(dom.select(".item-0 .index").text()).isEqualTo("1"); assertThat(dom.select(".item-0 .index0").text()).isEqualTo("0"); assertThat(dom.select(".item-0 .first").text()).isEqualTo("true"); assertThat(dom.select(".item-0 .last").text()).isEqualTo("false"); assertThat(dom.select(".item-0 .revindex").text()).isEqualTo("4"); assertThat(dom.select(".item-0 .revindex0").text()).isEqualTo("3"); assertThat(dom.select(".item-0 .length").text()).isEqualTo("4"); assertThat(dom.select(".item-0 .depth").text()).isEqualTo("1"); assertThat(dom.select(".item-0 .depth0").text()).isEqualTo("0"); assertThat(dom.select(".item-0 .subnum").text()).isEqualTo("6 0"); } @Test public void testForLoopConstants() { Map<String, Object> context = Maps.newHashMap(); String template = "" + "{% for i in range(1 * 1, 2 * 2) %}{{i}}{% endfor %}"; String rendered = jinjava.render(template, context); assertEquals("123", rendered); } @Test public void testForLoopVariablesWithoutSpaces() { Map<String, Object> context = Maps.newHashMap(); context.put("a", 2); context.put("b", 3); String template = "" + "{% for index in range(a*b,a*b+b) %}" + "{{index}} " + "{% endfor %}"; String rendered = jinjava.render(template, context); assertEquals("6 7 8 ", rendered); } @Test public void testFoorLoopVariablesWithSpaces() { Map<String, Object> context = Maps.newHashMap(); context.put("a", 2); context.put("b", 3); String template = "" + "{% for index in range(a * b, a * b + b) %}" + "{{index}} " + "{% endfor %}"; String rendered = jinjava.render(template, context); assertEquals("6 7 8 ", rendered); } @Test public void testForLoopRangeWithStringsWithSpaces() { Map<String, Object> context = Maps.newHashMap(); String template = "" + "{% for i in ['a ','b'] %}{{i}}{% endfor %}"; String rendered = jinjava.render(template, context); System.out.println(rendered); assertEquals("a b", rendered); } private Node fixture(String name) { try { return new TreeParser(interpreter, Resources.toString( Resources.getResource(String.format("tags/fortag/%s.jinja", name)), StandardCharsets.UTF_8)) .buildTree().getChildren().getFirst(); } catch (IOException e) { throw Throwables.propagate(e); } } }