Repository: PebbleTemplates/pebble
Pull request #477 (2019-09-23 12:10:22)

Whitespace Control with New Line Trimming Disabled Tests
I added tests for whitespace control for templates when New Line Trimming is disabled. Also added assertj and commons-io Maven dependencies for use in the tests. Commons IO is used to compare template output to expected output that is loaded from a file. Loading the expected template output from a file is useful for testing multiline template output, which is necessary to test various scenarios. These are a form of Integration Test rather than Unit Tests and so it is practical and reasonable to verify the template output in this way.

Related issue: https://github.com/PebbleTemplates/pebble/issues/470#issue-488221680

ebussieres: @nward1234 build is failing
nward1234: I looked at the build failure details. It does not show anything that I can
tell would be specific to the code or the changes that I made. Is it
possible that TravisCI could not connect to get some sort of build
resources at the time? The build passed for Java 8 and failed for Java 11
and 12. I had been using Java 8, but I installed Java 11 after seeing the
build failure. The clean build ran and tests passed locally with Java 11
for me. I will be glad to look into it further, but I am at a loss to
determine the root cause so far.

Nathan

On Fri, Sep 27, 2019 at 10:58 AM Eric Bussieres <notifications@github.com>
wrote:

> @nward1234 <https://github.com/nward1234> build is failing
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/PebbleTemplates/pebble/pull/477?email_source=notifications&email_token=AA6EQGXBJPWSVNVXXU6R7YDQLYNQXA5CNFSM4IZJ7XLKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7ZFN3Q#issuecomment-535975662>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AA6EQGRQGRNENNJP6U22W2TQLYNQXANCNFSM4IZJ7XLA>
> .
>

nward1234: I'll cancel the pull request and submit a new one and see if the same error
occurs again. If it does, then it must be something that I did to cause it.
If no errors, then it was a temporary TravisCI build issue.

Nathan

On Fri, Sep 27, 2019 at 8:14 PM Nathan Ward <nathanward1234@gmail.com>
wrote:

> I looked at the build failure details. It does not show anything that I
> can tell would be specific to the code or the changes that I made. Is it
> possible that TravisCI could not connect to get some sort of build
> resources at the time? The build passed for Java 8 and failed for Java 11
> and 12. I had been using Java 8, but I installed Java 11 after seeing the
> build failure. The clean build ran and tests passed locally with Java 11
> for me. I will be glad to look into it further, but I am at a loss to
> determine the root cause so far.
>
> Nathan
>
> On Fri, Sep 27, 2019 at 10:58 AM Eric Bussieres <notifications@github.com>
> wrote:
>
>> @nward1234 <https://github.com/nward1234> build is failing
>>
>> —
>> You are receiving this because you were mentioned.
>> Reply to this email directly, view it on GitHub
>> <https://github.com/PebbleTemplates/pebble/pull/477?email_source=notifications&email_token=AA6EQGXBJPWSVNVXXU6R7YDQLYNQXA5CNFSM4IZJ7XLKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD7ZFN3Q#issuecomment-535975662>,
>> or mute the thread
>> <https://github.com/notifications/unsubscribe-auth/AA6EQGRQGRNENNJP6U22W2TQLYNQXANCNFSM4IZJ7XLA>
>> .
>>
>

nward1234: Cool! Closed and reopened so that Travis build would kick off again and the build passed.

Patch:
diff --git a/pebble/pom.xml b/pebble/pom.xml
index f3a0f135..a9d0b896 100644
--- a/pebble/pom.xml
+++ b/pebble/pom.xml
@@ -66,6 +66,20 @@
         </exclusion>
       </exclusions>
     </dependency>
+    <!-- https://mvnrepository.com/artifact/org.assertj/assertj-core -->
+	<dependency>
+	    <groupId>org.assertj</groupId>
+	    <artifactId>assertj-core</artifactId>
+	    <version>3.13.2</version>
+	    <scope>test</scope>
+	</dependency>
+    <!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
+	<dependency>
+	    <groupId>commons-io</groupId>
+	    <artifactId>commons-io</artifactId>
+	    <version>2.6</version>
+	    <scope>test</scope>
+	</dependency>
   </dependencies>
 
   <build>


Test patch:
diff --git a/pebble/src/test/java/com/mitchellbosecke/pebble/NewlineTrimmingTest.java b/pebble/src/test/java/com/mitchellbosecke/pebble/NewlineTrimmingTest.java
index e2c2b05c..145dbb85 100644
--- a/pebble/src/test/java/com/mitchellbosecke/pebble/NewlineTrimmingTest.java
+++ b/pebble/src/test/java/com/mitchellbosecke/pebble/NewlineTrimmingTest.java
@@ -61,8 +61,13 @@ public class NewlineTrimmingTest {
     assertEquals("val1val2", writer.toString());
   }
 
+  /**
+   * Given that Newline Trimming is disabled, 
+   * a template that contains one newline character with text on each line
+   * should output one newline character.
+   */
   @Test
-  public void testPrintSetToFalse() throws PebbleException, IOException {
+  public void testNewLineIncludedWhen_NewLineTrimmingIsFalse() throws PebbleException, IOException {
 
     PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader())
         .newLineTrimming(false)
@@ -100,8 +105,13 @@ public class NewlineTrimmingTest {
     assertEquals("val1\nval2", writer.toString());
   }
 
+  /**
+   * Given that Newline Trimming is disabled, 
+   * a template that contains one or more consecutive newline characters
+   * should output one newline character.
+   */
   @Test
-  public void testPrintSetToFalseTwoNewlines() throws PebbleException, IOException {
+  public void testOneNewLineWhen_NewLineTrimmingFalseAndConsecutiveNewLinesInTemplate() throws PebbleException, IOException {
 
     PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader())
         .newLineTrimming(false)
diff --git a/pebble/src/test/java/com/mitchellbosecke/pebble/WhitespaceControlTest.java b/pebble/src/test/java/com/mitchellbosecke/pebble/WhitespaceControlTest.java
index 64ff0710..c61a0d50 100644
--- a/pebble/src/test/java/com/mitchellbosecke/pebble/WhitespaceControlTest.java
+++ b/pebble/src/test/java/com/mitchellbosecke/pebble/WhitespaceControlTest.java
@@ -10,38 +10,68 @@ package com.mitchellbosecke.pebble;
 
 import static org.junit.Assert.assertEquals;
 
-import com.mitchellbosecke.pebble.error.PebbleException;
-import com.mitchellbosecke.pebble.loader.StringLoader;
-import com.mitchellbosecke.pebble.template.PebbleTemplate;
 import java.io.IOException;
 import java.io.StringWriter;
 import java.io.Writer;
 import java.util.HashMap;
 import java.util.Map;
+
 import org.junit.Test;
 
+import com.mitchellbosecke.pebble.error.PebbleException;
+import com.mitchellbosecke.pebble.loader.StringLoader;
+import com.mitchellbosecke.pebble.template.PebbleTemplate;
+
 public class WhitespaceControlTest {
 
+  /**
+   * A windows newline character (i.e. \n\r) in a template should be recognized
+   * and output as as a Windows newline character. The Windows newline character
+   * should not be converted to a Unix newline character (i.e. \n). 
+   * 
+   * @throws PebbleException
+   * @throws IOException
+   */
   @Test
-  public void testStandardizationOfNewlineCharacters() throws PebbleException, IOException {
+  public void testWindowsNewlineCharacter() throws PebbleException, IOException {
     PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader())
         .strictVariables(true).build();
 
-    // windows
     PebbleTemplate windowsTemplate = pebble.getTemplate("\r\n");
     Writer windowsWriter = new StringWriter();
     windowsTemplate.evaluate(windowsWriter);
     assertEquals("\r\n", windowsWriter.toString());
+  }
+  
+  /**
+   * A Unix newline character (i.e. \n\r) in a template should be recognized
+   * and output as as a Unix newline character. The Unix newline character
+   * should not be converted to a Windows newline character (i.e. \r\n). 
+   * 
+   * @throws PebbleException
+   * @throws IOException
+   */
+  @Test
+  public void testUnixNewlineCharacter() throws PebbleException, IOException {
+    PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader())
+        .strictVariables(true).build();
 
-    // unix
     PebbleTemplate unixTemplate = pebble.getTemplate("\n");
     Writer unixWriter = new StringWriter();
     unixTemplate.evaluate(unixWriter);
     assertEquals("\n", unixWriter.toString());
   }
 
+  /**
+   * A leading Whitespace Control Modifier in an expression delimiter (i.e. Pebble variable reference)
+   * should remove whitespace before the variable reference on the same line up to any
+   * surrounding text, i.e. a print delimiter.
+   * 
+   * @throws PebbleException
+   * @throws IOException
+   */
   @Test
-  public void testLeadingWhitespaceTrimWithPrintDelimiter() throws PebbleException, IOException {
+  public void testLeadingWhitespaceControlModifier() throws PebbleException, IOException {
     PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader())
         .strictVariables(true).build();
 
@@ -54,36 +84,60 @@ public class WhitespaceControlTest {
     assertEquals("<li>bar</li>", writer.toString());
   }
 
+  /**
+   * A trailing Whitespace Control Modifier in an expression delimiter (i.e. Pebble variable reference)
+   * should remove whitespace after the variable reference on the same line up to any
+   * surrounding text.
+   * 
+   * @throws PebbleException
+   * @throws IOException
+   */
   @Test
-  public void testLeadingWhitespaceTrimWithoutOutsideText() throws PebbleException, IOException {
+  public void testTrailingWhitespaceControlModifier() throws PebbleException, IOException {
     PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader())
         .strictVariables(true).build();
 
-    PebbleTemplate template = pebble.getTemplate("{{- foo -}}");
+    PebbleTemplate template = pebble.getTemplate("<li>{{ foo -}}   	</li>");
     Writer writer = new StringWriter();
 
     Map<String, Object> context = new HashMap<>();
     context.put("foo", "bar");
     template.evaluate(writer, context);
-    assertEquals("bar", writer.toString());
+    assertEquals("<li>bar</li>", writer.toString());
   }
-
+  
+  /**
+   * A Whitespace Control Modifier in an expression delimiter (i.e. Pebble variable reference)
+   * should not have any effect if there is no whitespace immediately before or after the 
+   * variable reference. 
+   * 
+   * @throws PebbleException
+   * @throws IOException
+   */
   @Test
-  public void testTrailingWhitespaceTrimWithPrintDelimiter() throws PebbleException, IOException {
+  public void testLeadingWhitespaceTrimWithoutOutsideText() throws PebbleException, IOException {
     PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader())
         .strictVariables(true).build();
 
-    PebbleTemplate template = pebble.getTemplate("<li>{{ foo -}}   	</li>");
+    PebbleTemplate template = pebble.getTemplate("{{- foo -}}");
     Writer writer = new StringWriter();
 
     Map<String, Object> context = new HashMap<>();
     context.put("foo", "bar");
     template.evaluate(writer, context);
-    assertEquals("<li>bar</li>", writer.toString());
+    assertEquals("bar", writer.toString());
   }
 
+  /**
+   * A leading and trailing Whitespace Control Modifiers in an expression delimiter 
+   * (i.e. Pebble variable reference) should remove whitespace before and after 
+   * the variable reference on the same line up to any surrounding text.
+   * 
+   * @throws PebbleException
+   * @throws IOException
+   */
   @Test
-  public void testWhitespaceTrimInPresenceOfMultipleTags() throws PebbleException, IOException {
+  public void testLeadingAndTrailingWhitespaceControlModifier() throws PebbleException, IOException {
     PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader())
         .strictVariables(true).build();
 
@@ -97,8 +151,15 @@ public class WhitespaceControlTest {
     assertEquals("bar <li>bar</li> bar", writer.toString());
   }
 
+  /**
+   * Newline characters immediately before or after a Whitespace Control Modifier 
+   * should be removed.
+   * 
+   * @throws PebbleException
+   * @throws IOException
+   */
   @Test
-  public void testWhitespaceTrimRemovesNewlines() throws PebbleException, IOException {
+  public void testWhitespaceControlModifierRemovesNewlines() throws PebbleException, IOException {
     PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader())
         .strictVariables(true).build();
 
diff --git a/pebble/src/test/java/com/mitchellbosecke/pebble/template/tests/PebbleTestContext.java b/pebble/src/test/java/com/mitchellbosecke/pebble/template/tests/PebbleTestContext.java
new file mode 100644
index 00000000..98a385aa
--- /dev/null
+++ b/pebble/src/test/java/com/mitchellbosecke/pebble/template/tests/PebbleTestContext.java
@@ -0,0 +1,154 @@
+package com.mitchellbosecke.pebble.template.tests;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.io.FileUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.mitchellbosecke.pebble.PebbleEngine;
+import com.mitchellbosecke.pebble.loader.StringLoader;
+import com.mitchellbosecke.pebble.template.PebbleTemplate;
+
+/**
+ * Used by Pebble Template Tests to simply the test code and therefore 
+ * make it easier to understand what is tested by each test.
+ * 
+ *  A separate instance of this class should be instantiated for each
+ *  template test, i.e. for each instance of a template. 
+ * 
+ * @author nathanward
+ */
+public class PebbleTestContext {
+
+	private final Logger logger = LoggerFactory.getLogger(PebbleTestContext.class);
+	
+	/**
+	 * The path relative to the project root directory where template files and 
+	 * expected output files are stored for test purposes.
+	 */
+	private String testFileBasePath = "src/test/resources/template-tests";
+	
+	/**
+	 * The Pebble template context to be used as input for a Pebble Template. 
+	 */
+	private Map<String, Object> templateContext = null;
+
+	/**
+	 * Whether or not the Pebble Engine instantiated will 
+	 * enable New Line Trimming on the Builder when this class instantiates
+	 * a Pebble Engine instance. Defaults to <code>true</code>.
+	 */
+	private boolean newLineTrimming = true;
+	
+	/**
+	 * Initialize the Pebble Template Context.
+	 */ 
+	public PebbleTestContext() {
+		this.templateContext = new HashMap<String, Object>();
+	}
+	
+	public void setNewLineTrimming(boolean b) {
+		this.newLineTrimming = b;
+	}
+	
+	/**
+	 * Put an object into the Pebble template context to be used as input for
+	 * when the template is executed. One or more items can be put into the template
+	 * context.
+	 * 
+	 * @param name Template input/parameter name (i.e. key) for use within the template
+	 * @param value The object that will be referred to by the given name in the template
+	 */
+	public void setTemplateInput(String name, Object value) {
+		this.templateContext.put(name, value);
+	}
+
+	/**
+	 * Load the specified template file and execute the template using a 
+	 * Pebble Engine using the default Builder (classpath and file builder). 
+	 * 
+	 * @param templateFilename The template filename relative to the Test File Base Path
+	 * 
+	 * @return The output of the template as a string.
+	 * 
+	 * @throws IOException Thrown if the template file is not found. 
+	 */
+	public String executeTemplateFromFile(String templateFilename) throws IOException {
+		PebbleEngine pebbleEngine = new PebbleEngine.Builder()
+				.newLineTrimming(this.newLineTrimming).strictVariables(true).build();
+		return this.executeTemplateFromFile(templateFilename, pebbleEngine);
+	}
+	
+	/**
+	 * Execute a template given a template file and a Pebble Engine instance. 
+	 * 
+	 * @param templateFilename The template filename relative to the Test File Base Path
+	 * @param pebbleEngine
+	 * @return
+	 * @throws IOException
+	 */
+	public String executeTemplateFromFile(String templateFilename, PebbleEngine pebbleEngine) throws IOException {
+		Path path = Paths.get(this.testFileBasePath, templateFilename);
+		logger.debug("Executing template file: {}", path.toString());
+		return this.executeTemplate(path.toAbsolutePath().toString(), pebbleEngine);
+	}
+		
+	/**
+	 * Load the specified template file and execute the template using a 
+	 * Pebble Engine using the default Builder (classpath and file builder). 
+	 * 
+	 * @param templateString The template content as a string
+	 * 
+	 * @return The output of the template as a string.
+	 * 
+	 * @throws IOException Thrown if the template file is not found. 
+	 */
+	public String executeTemplateFromString(String templateString) throws IOException {
+		PebbleEngine pebbleEngine = new PebbleEngine.Builder().loader(new StringLoader())
+				.newLineTrimming(this.newLineTrimming).build();
+		return this.executeTemplate(templateString, pebbleEngine);
+	}
+	
+	/**
+	 * Load the specified template file and execute the template using the template input
+	 * that has previously been specified using the setTemplateInput() method. 
+	 * 
+	 * @param templateFilename The template filename relative to the Test File Base Path
+	 * @param pebbleEngine The Pebble Engine to be used to execute the template
+	 * @return The output of the template as a string.
+	 * 
+	 * @throws IOException Thrown if the template file is not found. 
+	 */
+	public String executeTemplate(String templateName, PebbleEngine pebbleEngine) throws IOException {
+		PebbleTemplate template = pebbleEngine.getTemplate(templateName);		
+		Writer writer = new StringWriter();
+		template.evaluate(writer, this.templateContext);
+		String templateOutput = writer.toString();
+		logger.debug("Template Output: {}", templateOutput);
+		return templateOutput;
+	}
+	
+	/**
+	 * Get the Expected Output content for the given filename so that the 
+	 * base path to the expected template output file does not have to be
+	 * specified in the actual test code. 
+	 * 
+	 * @param filename The name of the file that contains the expected template output. 
+	 * @return The content of the expected template output file as a string.
+	 * @throws IOException Thrown if the file by the given name is not found.
+	 */
+	public String getExpectedOutput(String filename) throws IOException {
+		Path path = Paths.get(this.testFileBasePath, filename);
+	    logger.debug("Expected template output file: {}", path.toAbsolutePath());
+	    String expectedOutput = FileUtils.readFileToString(path.toFile(), "UTF-8");
+	    return expectedOutput;
+	}
+	
+}
diff --git a/pebble/src/test/java/com/mitchellbosecke/pebble/template/tests/WhiteSpaceControlWithNewLineTrimmingTests.java b/pebble/src/test/java/com/mitchellbosecke/pebble/template/tests/WhiteSpaceControlWithNewLineTrimmingTests.java
new file mode 100644
index 00000000..863a37f0
--- /dev/null
+++ b/pebble/src/test/java/com/mitchellbosecke/pebble/template/tests/WhiteSpaceControlWithNewLineTrimmingTests.java
@@ -0,0 +1,130 @@
+/*
+ * This file is part of Pebble.
+ *
+ * Copyright (c) 2014 by Mitchell Bösecke
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+package com.mitchellbosecke.pebble.template.tests;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.mitchellbosecke.pebble.error.PebbleException;
+import com.mitchellbosecke.pebble.template.tests.input.PebbleTestItem;
+import com.mitchellbosecke.pebble.template.tests.input.PebbleTestItemType;
+
+/**
+ * Tests of whitespace control when New Line Trimming is enabled. 
+ * This mainly affects vertical spacing, i.e. the control of blank lines. 
+ * With New Line Trimming enabled, Pebble control statements on a line
+ * by themselves products a blank line in the output unless 
+ * the Whitespace Control Modifier is used before and/or after the 
+ * control statement, i.e. the dash character, e.g.  `{% if item.itemType equals "ITEM_TYPE1" -%}`.
+ * 
+ * However, it can be difficult to get the blank lines to come out as expected
+ * in the case that nested control structures are used.
+ * 
+ * @author nathanward
+ */
+public class WhiteSpaceControlWithNewLineTrimmingTests {
+
+	/**
+	 * All tests in this class use this list of objects as the input to the template.
+	 */
+	private List<PebbleTestItem> listOfObjects = null;
+	
+	/**
+	 * Used by each test and initialized before each test to simplify the 
+	 * test execution, which helps make the purpose of each test clear.
+	 */
+	private PebbleTestContext pebbleTestContext = null;
+	
+	@Before
+	public void setup() {
+		pebbleTestContext = new PebbleTestContext();
+		pebbleTestContext.setNewLineTrimming(false);
+		
+		listOfObjects = new ArrayList<PebbleTestItem>();
+		listOfObjects.add(new PebbleTestItem("Item 1", PebbleTestItemType.ITEM_TYPE1));
+		listOfObjects.add(new PebbleTestItem("Item 2", PebbleTestItemType.ITEM_TYPE2));
+		listOfObjects.add(new PebbleTestItem("Item 3", PebbleTestItemType.ITEM_TYPE3));
+		listOfObjects.add(new PebbleTestItem("Item 4", PebbleTestItemType.ITEM_TYPE4, true));
+		
+		pebbleTestContext.setTemplateInput("items", listOfObjects);
+	}
+
+	/**
+	 * Test the whitespace control for a template that has a <code>for</code> loop with a nested
+	 * <code>if</code> statement that uses a macro and the macro also has an if statement. 
+	 * 
+	 * @throws PebbleException
+	 * @throws IOException
+	 */
+	@Test
+	public void testForLoopWithNestedIfStatementAndMacro() throws PebbleException, IOException {;		
+		String templateOutput = pebbleTestContext.executeTemplateFromFile("ForLoopWithNestedIfStatementAndMacro.peb");
+		assertThat(templateOutput).contains(pebbleTestContext.getExpectedOutput("ForLoopWithNestedIfStatementAndMacro.txt"));
+	}
+	
+	/**
+	 * Test the whitespace control for a template that has a <code>for</code> loop with a nested
+	 * <code>if</code> statement that uses a macro and the macro also has an if statement. 
+	 * 
+	 * @throws PebbleException
+	 * @throws IOException
+	 */
+	// TODO not sure why the white space controls work the way that it does for this case.
+	@Test
+	public void testDoubleNestedIfStatement() throws PebbleException, IOException {
+		String templateOutput = pebbleTestContext.executeTemplateFromFile("DoubleNestedIfStatement.peb");
+		assertThat(templateOutput).contains(pebbleTestContext.getExpectedOutput("DoubleNestedIfStatement.txt"));
+	}
+	
+	/**
+	 * Test the whitespace control for a template that has a <code>for</code> loop with a nested
+	 * <code>if</code> statement where some text is output for each item in the list.
+	 * 
+	 * @throws PebbleException
+	 * @throws IOException
+	 */
+	@Test
+	public void testNestedIfStatementWithOneElseIfStatements() throws PebbleException, IOException {
+		String templateOutput = pebbleTestContext.executeTemplateFromFile("NestedIfStatementWithOneElseIfStatements.peb");
+		assertThat(templateOutput).contains(pebbleTestContext.getExpectedOutput("NestedIfStatementWithOneElseIfStatements.txt"));
+	}
+	
+	/**
+	 * Test the whitespace control for a template that has a <code>for</code> loop with a nested
+	 * <code>if</code> statement where some text is output for each item in the list.
+	 * 
+	 * @throws PebbleException
+	 * @throws IOException
+	 */
+	@Test
+	public void testNestedIfStatementWithTwoElseIfStatements() throws PebbleException, IOException {
+		String templateOutput = pebbleTestContext.executeTemplateFromFile("NestedIfStatementWithTwoElseIfStatements.peb");
+		assertThat(templateOutput).contains(pebbleTestContext.getExpectedOutput("NestedIfStatementWithTwoElseIfStatements.txt"));
+	}
+	
+	/**
+	 * Test the whitespace control for a template that has a <code>for</code> loop with a nested
+	 * <code>if</code> statement that skips some of the items in the for loop.
+	 * 
+	 * @throws PebbleException
+	 * @throws IOException
+	 */
+	@Test
+	public void testNestedIfStatementWithThreeElseIfStatements() throws IOException {
+		String templateOutput = pebbleTestContext.executeTemplateFromFile("NestedIfStatementWithThreeElseIfStatements.peb");
+		assertThat(templateOutput).contains(pebbleTestContext.getExpectedOutput("NestedIfStatementWithThreeElseIfStatements.txt"));
+	}
+}
diff --git a/pebble/src/test/java/com/mitchellbosecke/pebble/template/tests/input/PebbleTestItem.java b/pebble/src/test/java/com/mitchellbosecke/pebble/template/tests/input/PebbleTestItem.java
new file mode 100644
index 00000000..3ff602c0
--- /dev/null
+++ b/pebble/src/test/java/com/mitchellbosecke/pebble/template/tests/input/PebbleTestItem.java
@@ -0,0 +1,46 @@
+package com.mitchellbosecke.pebble.template.tests.input;
+
+public class PebbleTestItem {
+
+	private String name;
+	
+	private PebbleTestItemType itemType;
+	
+	private boolean hasPrefix;
+
+	public PebbleTestItem(String name, PebbleTestItemType itemType1) {
+		this.name = name;
+		this.itemType = itemType1;
+	}
+
+	public PebbleTestItem(String name, PebbleTestItemType itemType1, boolean hasPrefix) {
+		this.name = name;
+		this.itemType = itemType1;
+		this.hasPrefix = hasPrefix;
+	}
+	
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public PebbleTestItemType getItemType() {
+		return itemType;
+	}
+
+	public void setItemType(PebbleTestItemType pebbleTestItemType) {
+		this.itemType = pebbleTestItemType;
+	}
+
+	public boolean isHasPrefix() {
+		return hasPrefix;
+	}
+
+	public void setHasPrefix(boolean hasPrefix) {
+		this.hasPrefix = hasPrefix;
+	}
+	
+}
diff --git a/pebble/src/test/java/com/mitchellbosecke/pebble/template/tests/input/PebbleTestItemType.java b/pebble/src/test/java/com/mitchellbosecke/pebble/template/tests/input/PebbleTestItemType.java
new file mode 100644
index 00000000..c2513983
--- /dev/null
+++ b/pebble/src/test/java/com/mitchellbosecke/pebble/template/tests/input/PebbleTestItemType.java
@@ -0,0 +1,5 @@
+package com.mitchellbosecke.pebble.template.tests.input;
+
+public enum PebbleTestItemType {
+	ITEM_TYPE1, ITEM_TYPE2, ITEM_TYPE3, ITEM_TYPE4
+}
diff --git a/pebble/src/test/resources/logback-test.xml b/pebble/src/test/resources/logback-test.xml
index 6237f082..834d8404 100644
--- a/pebble/src/test/resources/logback-test.xml
+++ b/pebble/src/test/resources/logback-test.xml
@@ -9,4 +9,7 @@
   <root level="INFO">
     <appender-ref ref="STDOUT"/>
   </root>
+  
+  <logger name="com.mitchellbosecke.pebble.template.tests" level="DEBUG"/>
+  
 </configuration>
\ No newline at end of file
diff --git a/pebble/src/test/resources/template-tests/DoubleNestedIfStatement.peb b/pebble/src/test/resources/template-tests/DoubleNestedIfStatement.peb
new file mode 100644
index 00000000..f72943f4
--- /dev/null
+++ b/pebble/src/test/resources/template-tests/DoubleNestedIfStatement.peb
@@ -0,0 +1,16 @@
+text before for loop followed by blank line
+{% for item in items %}
+{% if item.itemType equals "ITEM_TYPE1" -%}
+Item 1
+{% elseif item.itemType equals "ITEM_TYPE2" -%}
+Item 2
+{% elseif item.itemType equals "ITEM_TYPE3" -%}
+Item 3
+{% elseif item.itemType equals "ITEM_TYPE4" -%}
+{% if item.hasPrefix -%}
+Item Prefix
+{% endif -%}
+Item 4
+{% endif -%}
+{% endfor %}
+text after for loop preceded by blank line
\ No newline at end of file
diff --git a/pebble/src/test/resources/template-tests/DoubleNestedIfStatement.txt b/pebble/src/test/resources/template-tests/DoubleNestedIfStatement.txt
new file mode 100644
index 00000000..1dd1c80d
--- /dev/null
+++ b/pebble/src/test/resources/template-tests/DoubleNestedIfStatement.txt
@@ -0,0 +1,12 @@
+text before for loop followed by blank line
+
+Item 1
+
+Item 2
+
+Item 3
+
+Item Prefix
+Item 4
+
+text after for loop preceded by blank line
\ No newline at end of file
diff --git a/pebble/src/test/resources/template-tests/ForLoopWithNestedIfStatementAndMacro.peb b/pebble/src/test/resources/template-tests/ForLoopWithNestedIfStatementAndMacro.peb
new file mode 100644
index 00000000..1e1b49a7
--- /dev/null
+++ b/pebble/src/test/resources/template-tests/ForLoopWithNestedIfStatementAndMacro.peb
@@ -0,0 +1,17 @@
+text before for loop followed by blank line
+{% for item in items %}
+{% if item.itemType equals "ITEM_TYPE1" -%}
+{{ item.name }}
+{% else -%}
+{{ itemStatement(item) }}
+{% endif -%}
+{% endfor %}
+text after for loop preceded by blank line
+
+
+{%- macro itemStatement(item) -%}
+{%- if item.hasPrefix == true -%}
+Prefix Text
+{% endif -%}
+{{ item.name }}
+{%- endmacro -%}
\ No newline at end of file
diff --git a/pebble/src/test/resources/template-tests/ForLoopWithNestedIfStatementAndMacro.txt b/pebble/src/test/resources/template-tests/ForLoopWithNestedIfStatementAndMacro.txt
new file mode 100644
index 00000000..d6ba7b3d
--- /dev/null
+++ b/pebble/src/test/resources/template-tests/ForLoopWithNestedIfStatementAndMacro.txt
@@ -0,0 +1,12 @@
+text before for loop followed by blank line
+
+Item 1
+
+Item 2
+
+Item 3
+
+Prefix Text
+Item 4
+
+text after for loop preceded by blank line
\ No newline at end of file
diff --git a/pebble/src/test/resources/template-tests/NestedIfStatementWithOneElseIfStatements.peb b/pebble/src/test/resources/template-tests/NestedIfStatementWithOneElseIfStatements.peb
new file mode 100644
index 00000000..e724e10b
--- /dev/null
+++ b/pebble/src/test/resources/template-tests/NestedIfStatementWithOneElseIfStatements.peb
@@ -0,0 +1,9 @@
+text before for loop followed by blank line
+{% for item in items %}
+{% if item.itemType equals "ITEM_TYPE1" -%}
+Item 1
+{% elseif item.itemType equals "ITEM_TYPE2" -%}
+Item 2
+{%- endif -%}
+{% endfor -%}
+text after for loop preceded by blank line
\ No newline at end of file
diff --git a/pebble/src/test/resources/template-tests/NestedIfStatementWithOneElseIfStatements.txt b/pebble/src/test/resources/template-tests/NestedIfStatementWithOneElseIfStatements.txt
new file mode 100644
index 00000000..3b147118
--- /dev/null
+++ b/pebble/src/test/resources/template-tests/NestedIfStatementWithOneElseIfStatements.txt
@@ -0,0 +1,7 @@
+text before for loop followed by blank line
+
+Item 1
+
+Item 2
+
+text after for loop preceded by blank line
\ No newline at end of file
diff --git a/pebble/src/test/resources/template-tests/NestedIfStatementWithThreeElseIfStatements.peb b/pebble/src/test/resources/template-tests/NestedIfStatementWithThreeElseIfStatements.peb
new file mode 100644
index 00000000..e70657a0
--- /dev/null
+++ b/pebble/src/test/resources/template-tests/NestedIfStatementWithThreeElseIfStatements.peb
@@ -0,0 +1,13 @@
+text before for loop followed by blank line
+{% for item in items %}
+{% if item.itemType equals "ITEM_TYPE1" -%}
+Item 1
+{% elseif item.itemType equals "ITEM_TYPE2" -%}
+Item 2
+{% elseif item.itemType equals "ITEM_TYPE3" -%}
+Item 3
+{% elseif item.itemType equals "ITEM_TYPE4" -%}
+Item 4
+{% endif -%}
+{% endfor %}
+text after for loop preceded by blank line
\ No newline at end of file
diff --git a/pebble/src/test/resources/template-tests/NestedIfStatementWithThreeElseIfStatements.txt b/pebble/src/test/resources/template-tests/NestedIfStatementWithThreeElseIfStatements.txt
new file mode 100644
index 00000000..3d36e6f4
--- /dev/null
+++ b/pebble/src/test/resources/template-tests/NestedIfStatementWithThreeElseIfStatements.txt
@@ -0,0 +1,11 @@
+text before for loop followed by blank line
+
+Item 1
+
+Item 2
+
+Item 3
+
+Item 4
+
+text after for loop preceded by blank line
\ No newline at end of file
diff --git a/pebble/src/test/resources/template-tests/NestedIfStatementWithTwoElseIfStatements.peb b/pebble/src/test/resources/template-tests/NestedIfStatementWithTwoElseIfStatements.peb
new file mode 100644
index 00000000..0b7e8c9a
--- /dev/null
+++ b/pebble/src/test/resources/template-tests/NestedIfStatementWithTwoElseIfStatements.peb
@@ -0,0 +1,11 @@
+text before for loop followed by blank line
+{% for item in items %}
+{% if item.itemType equals "ITEM_TYPE1" -%}
+Item 1
+{% elseif item.itemType equals "ITEM_TYPE2" -%}
+Item 2
+{% elseif item.itemType equals "ITEM_TYPE3" -%}
+Item 3
+{%- endif -%}
+{% endfor %}
+text after for loop preceded by blank line
\ No newline at end of file
diff --git a/pebble/src/test/resources/template-tests/NestedIfStatementWithTwoElseIfStatements.txt b/pebble/src/test/resources/template-tests/NestedIfStatementWithTwoElseIfStatements.txt
new file mode 100644
index 00000000..1be3f0c5
--- /dev/null
+++ b/pebble/src/test/resources/template-tests/NestedIfStatementWithTwoElseIfStatements.txt
@@ -0,0 +1,9 @@
+text before for loop followed by blank line
+
+Item 1
+
+Item 2
+
+Item 3
+
+text after for loop preceded by blank line
\ No newline at end of file