Merge pull request #153 from jstachio/custom_spec_tests

Fix nested partial indentation and custom spec tests
This commit is contained in:
Michael Bayne
2023-11-21 10:45:16 -08:00
committed by GitHub
9 changed files with 372 additions and 118 deletions
@@ -430,25 +430,31 @@ public class Mustache {
if (prevBlank && block.firstLeadsBlank()) {
if (pseg != null) segs[ii-1] = prev.trimTrailBlank();
block.trimFirstBlank();
block._standaloneStart = true;
}
if (nextBlank && block.lastTrailsBlank()) {
block.trimLastBlank();
if (nseg != null) segs[ii+1] = next.trimLeadBlank();
block._standaloneEnd = true;
}
}
// we have to indent partials if there is space before
// they are also standalone...
else if (seg instanceof IncludedTemplateSegment) {
IncludedTemplateSegment include = (IncludedTemplateSegment) seg;
if (prev != null && prevBlank && nextBlank) {
String indent = prev.indent();
include._standalone = true;
if (! indent.equals("")) {
segs[ii] = seg.indent(indent, nseg == null);
include = include.indent(indent, pseg == null,nseg == null);
segs[ii] = include;
}
//segs[ii-1] = prev.trimTrailBlank();
/*
* We trim the end because partials
* follow standalone just like blocks
*/
if (nseg != null) {
if (next != null) {
segs[ii+1] = next.trimLeadBlank();
}
}
@@ -464,6 +470,59 @@ public class Mustache {
return segs;
}
static Template.Segment[] indentSegs(Template.Segment[] _segs, String indent) {
// unlike trim this method clones the segments if they have changed
// so the return value must be handled
// a simple identity check on the return can be used to determine
// if there is change
if (indent.equals("")) {
return _segs;
}
int length = _segs.length;
Template.Segment[] copySegs = new Template.Segment[length];
boolean changed = false;
for (int i = 0; i < _segs.length; i++) {
Template.Segment seg = _segs[i];
Template.Segment pseg = (i > 0) ? _segs[i-1] : null;
Template.Segment nseg = (i < length - 1) ? _segs[i+1] : null;
/*
* If we are the first segment then we rely on the indentation
* already present before the partial tag.
* [ WS ]{{> partial }}
*
* That is partial tags do not have the trailing blank
* removed.
*
* This avoids needlessley creating StringSegment tags.
*/
boolean first = pseg == null ? false : seg.isStandalone() || pseg.isStandalone();
boolean last;
if (nseg instanceof BlockSegment){
BlockSegment bs = (BlockSegment) nseg;
last = bs.isStandaloneStart();
}
else if (nseg == null || nseg.isStandalone()) {
last = false;
}
else {
last = true;
}
/*
* Recursively indent
*/
Template.Segment copy = seg.indent(indent, first, last);
if (copy != seg) {
changed = true;
}
copySegs[i] = copy;
}
if (changed) {
return copySegs;
}
return _segs;
}
protected static void restoreStartTag (StringBuilder text, Delims starts) {
text.insert(0, starts.start1);
if (starts.start2 != NO_CHAR) {
@@ -801,15 +860,16 @@ public class Mustache {
/** A simple segment that reproduces a string. */
protected static class StringSegment extends Template.Segment {
public StringSegment (String text, boolean first) {
this(text, blankPos(text, true, first), blankPos(text, false, first));
this(text, blankPos(text, true, first), blankPos(text, false, first), first);
}
public StringSegment (String text, int leadBlank, int trailBlank) {
public StringSegment (String text, int leadBlank, int trailBlank, boolean first) {
assert leadBlank >= -1;
assert trailBlank >= -1;
_text = text;
_leadBlank = leadBlank;
_trailBlank = trailBlank;
_first = first;
}
public boolean leadsBlank () { return _leadBlank != -1; }
@@ -818,11 +878,11 @@ public class Mustache {
public StringSegment trimLeadBlank () {
if (_leadBlank == -1) return this;
int lpos = _leadBlank+1, newTrail = _trailBlank == -1 ? -1 : _trailBlank-lpos;
return new StringSegment(_text.substring(lpos), -1, newTrail);
return new StringSegment(_text.substring(lpos), -1, newTrail, _first);
}
public StringSegment trimTrailBlank () {
return _trailBlank == -1 ? this : new StringSegment(
_text.substring(0, _trailBlank), _leadBlank, -1);
_text.substring(0, _trailBlank), _leadBlank, -1, _first);
}
/**
@@ -836,12 +896,16 @@ public class Mustache {
return _text.substring(_trailBlank);
}
StringSegment indent(String indent, boolean last) {
StringSegment indent(String indent, boolean first, boolean last) {
if (indent.equals("")) {
return this;
}
String reindent = reindent(_text, indent, last);
return new StringSegment(reindent , _leadBlank, _trailBlank);
String reindent = reindent(_text, indent, first, last);
return new StringSegment(reindent, _first);
}
@Override boolean isStandalone() {
return false;
}
@Override public void execute (Template tmpl, Template.Context ctx, Writer out) {
@@ -859,13 +923,16 @@ public class Mustache {
}
//we indent after every new line for partial indententation
private static String reindent(String input, String indent, boolean last) {
private static String reindent(String input, String indent, boolean first, boolean last) {
int length = input.length();
StringBuilder sb = new StringBuilder(length);
StringBuilder sb = new StringBuilder(indent.length() + length);
if (first) {
sb.append(indent);
}
for (int i = 0; i < length; i++) {
char c = input.charAt(i);
sb.append(c);
if (c == '\n' && ! ( last && i == length - 1)) {
if (c == '\n' && ( last || i != length - 1) ) {
sb.append(indent);
}
}
@@ -887,6 +954,7 @@ public class Mustache {
protected final String _text;
protected final int _leadBlank, _trailBlank;
protected final boolean _first;
}
/** A segment that loads and executes a sub-template. */
@@ -920,20 +988,30 @@ public class Mustache {
}
return _template;
}
protected IncludedTemplateSegment indent(String indent, boolean last) {
protected IncludedTemplateSegment indent(String indent, boolean first, boolean last) {
// Indent this partial based on the spacing provided.
// per the spec however much the partial reference is indendented (leading whitespace)
// is how much the partial content should be indented.
if (indent.equals("")) {
if (indent.equals("") || ! _standalone) {
return this;
}
return new IncludedTemplateSegment(_comp, _name, indent + this._indent );
IncludedTemplateSegment is = new IncludedTemplateSegment(_comp, _name, indent + this._indent );
is._standalone = _standalone;
return is;
}
@Override boolean isStandalone() { return _standalone; }
@Override
public String toString() {
return "Include(name=" + _name + ", indent=" + _indent + ", standalone=" + _standalone
+ ")";
}
protected final Compiler _comp;
protected final String _name;
private final String _indent;
private Template _template;
protected boolean _standalone = false;
}
/** A helper class for named segments. */
@@ -970,9 +1048,13 @@ public class Mustache {
visitor.visitVariable(_name);
}
@Override
VariableSegment indent(String indent, boolean last) {
VariableSegment indent(String indent, boolean first, boolean last) {
return this;
}
@Override
boolean isStandalone() {
return false;
}
@Override public String toString () {
return "Var(" + _name + ":" + _line + ")";
}
@@ -1004,15 +1086,31 @@ public class Mustache {
super(name, line);
_segs = trim(segs, false);
}
protected BlockSegment (BlockSegment original, Template.Segment[] segs) {
super(original._name, original._line);
// this call assumes the segments are already trimmed
_segs = segs;
}
protected void executeSegs (Template tmpl, Template.Context ctx, Writer out) {
for (Template.Segment seg : _segs) {
seg.execute(tmpl, ctx, out);
}
}
protected abstract BlockSegment indent(String indent, boolean last);
protected abstract BlockSegment indent (String indent, boolean first, boolean last);
@Override
boolean isStandalone() {
return _standaloneEnd;
}
boolean isStandaloneStart() {
return _standaloneStart;
}
protected final Template.Segment[] _segs;
protected boolean _standaloneStart = false;
protected boolean _standaloneEnd = false;
}
/** A segment that represents a section. */
@@ -1021,6 +1119,10 @@ public class Mustache {
super(name, segs, line);
_comp = compiler;
}
protected SectionSegment(SectionSegment original, Template.Segment[] segs) {
super(original, segs);
_comp = original._comp;
}
@Override public void execute (Template tmpl, Template.Context ctx, Writer out) {
Object value = tmpl.getSectionValue(ctx, _name, _line); // won't return null
Iterator<?> iter = _comp.collector.toIterator(value);
@@ -1059,16 +1161,15 @@ public class Mustache {
}
}
}
@Override
protected SectionSegment indent(String indent, boolean last) {
@Override protected SectionSegment indent (String indent, boolean first, boolean last) {
if (indent.equals("")) {
return this;
}
Template.Segment[] segs = Template.indentSegs(_segs, indent);
Template.Segment[] segs = indentSegs(_segs, indent);
if (segs == _segs) {
return this;
}
return new SectionSegment(_comp, _name, segs, _line);
return new SectionSegment(this, segs);
}
@Override public String toString () {
return "Section(" + _name + ":" + _line + "): " + Arrays.toString(_segs);
@@ -1082,6 +1183,10 @@ public class Mustache {
super(name, segs, line);
_comp = compiler;
}
protected InvertedSegment (InvertedSegment original, Template.Segment[] segs) {
super(original, segs);
_comp = original._comp;
}
@Override public void execute (Template tmpl, Template.Context ctx, Writer out) {
Object value = tmpl.getSectionValue(ctx, _name, _line); // won't return null
Iterator<?> iter = _comp.collector.toIterator(value);
@@ -1116,15 +1221,15 @@ public class Mustache {
}
}
@Override
protected InvertedSegment indent(String indent, boolean last) {
protected InvertedSegment indent (String indent, boolean first, boolean last) {
if (indent.equals("")) {
return this;
}
Template.Segment[] segs = Template.indentSegs(_segs, indent);
Template.Segment[] segs = indentSegs(_segs, indent);
if (segs == _segs) {
return this;
}
return new InvertedSegment(_comp, _name, segs, _line);
return new InvertedSegment(this, segs);
}
@Override public String toString () {
return "Inverted(" + _name + ":" + _line + "): " + Arrays.toString(_segs);
@@ -1136,7 +1241,8 @@ public class Mustache {
@Override public void execute (Template tmpl, Template.Context ctx, Writer out) {} // nada
@Override public void decompile (Delims delims, StringBuilder into) {} // nada
@Override public void visit (Visitor visit) {}
@Override FauxSegment indent(String indent, boolean last) { return this; }
@Override FauxSegment indent (String indent, boolean first, boolean last) { return this; }
@Override boolean isStandalone() { return true; }
@Override public String toString () { return "Faux"; }
}
@@ -176,34 +176,13 @@ public class Template {
if (indent.equals("")) {
return this;
}
Segment[] copySegs = indentSegs(_segs, indent);
Segment[] copySegs = Mustache.indentSegs(_segs, indent);
if (copySegs == _segs) {
return this;
}
return new Template(copySegs, _compiler);
}
static Segment[] indentSegs(Segment[] _segs, String indent) {
if (indent.equals("")) {
return _segs;
}
int length = _segs.length;
Segment[] copySegs = new Segment[length];
boolean changed = false;
for (int i = 0; i < _segs.length; i++) {
Segment seg = _segs[i];
Segment copy = seg.indent(indent, i == (length - 1));
if (copy != seg) {
changed = true;
}
copySegs[i] = copy;
}
if (changed) {
return copySegs;
}
return _segs;
}
protected void executeSegs (Context ctx, Writer out) throws MustacheException {
for (Segment seg : _segs) {
seg.execute(this, ctx, out);
@@ -422,8 +401,28 @@ public class Template {
abstract void decompile (Mustache.Delims delims, StringBuilder into);
abstract void visit (Mustache.Visitor visitor);
abstract Segment indent(String indent, boolean last);
/**
* Recursively indent by the parameter indent.
* @param indent should be space characters that are not \n
* @param first append indent to the first line (regardless if it has a \n or not)
* @param last append indent on the last \n that has no text after it
* @return newly crated segment or the same segment if nothing changed.
*/
abstract Segment indent(String indent, boolean first, boolean last);
/**
* Whether or not the segment is standalone.
* For blocks this is based on the closing tag.
* Once Trim is called standalone tags are determined so
* that proper (re)indentation will work without reparsing
* the template.
*
* String and variable tags are never standalone.
*
* The definition of standalone is defined by the mustache spec.
*
* @return true if the tag is standalone
*/
abstract boolean isStandalone();
protected static void write (Writer out, CharSequence data) {
try {
@@ -4,16 +4,34 @@
package com.samskivert.mustache;
import static java.util.Map.entry;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.util.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.rules.TestRule;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import com.samskivert.mustache.specs.SpecTest;
/**
* Vestige from when JMustache supported both GWT and JVM.
@@ -211,16 +229,24 @@ public abstract class SharedTests
} catch (UnsupportedOperationException uoe) {} // expected
}
Map<String,String> partials = new LinkedHashMap<>();
@SafeVarargs
protected final Mustache.TemplateLoader partials(Map.Entry<String, String> ... entries) {
Map<String,String> templates = new LinkedHashMap<>();
for (Entry<String, String> e : entries) {
templates.put(e.getKey(), e.getValue());
}
partials = templates;
return name -> new StringReader(templates.get(name));
}
@Test public void testPartial () {
test(Mustache.compiler().withLoader(new Mustache.TemplateLoader() {
public Reader getTemplate (String name) {
if (name.equals("foo")) {
return new StringReader("inside:{{bar}}");
} else {
return new StringReader("nonfoo");
}
}
}), "foo inside:foo nonfoo foo", "{{bar}} {{>foo}} {{>baz}} {{bar}}", context("bar", "foo"));
test(Mustache.compiler().withLoader(
partials(entry("foo", "inside:{{bar}}"),
entry("baz", "nonfoo")))
, "foo inside:foo nonfoo foo", "{{bar}} {{>foo}} {{>baz}} {{bar}}", context("bar", "foo"));
}
@Test public void testPartialIndent () {
@@ -231,15 +257,15 @@ public abstract class SharedTests
}), "\\\n |\n <\n->\n |\n/\n", "\\\n {{>partial}}\n/\n", context("content", "<\n->"));
}
/* @Test */ public void testPartialBlankLines () {
@Test public void testPartialBlankLines () {
test(Mustache.compiler().withLoader(new Mustache.TemplateLoader() {
public Reader getTemplate (String name) {
return new StringReader("|\na\n\nb\n|\n");
}
}), "\\\n\t|\n\ta\n\n\tb\n\t|\n/\n", "\\\n\t{{>partial}}\n/\n", context());
}), "\\\n\t|\n\ta\n\t\n\tb\n\t|\n/\n", "\\\n\t{{>partial}}\n/\n", context());
}
/* @Test */ public void testNestedPartialBlankLines () {
@Test public void testNestedPartialBlankLines () {
test(Mustache.compiler().withLoader(new Mustache.TemplateLoader() {
public Reader getTemplate (String name) {
if (name.equals("partial")) {
@@ -248,10 +274,10 @@ public abstract class SharedTests
return new StringReader("2\na\n\nb\n2\n");
}
}
}), "\\\n\t1\n\t\t2\n\t\ta\n\n\t\tb\n\t\t2\n\t1\n/\n", "\\\n\t{{>partial}}\n/\n", context());
}), "\\\n\t1\n\t\t2\n\t\ta\n\t\t\n\t\tb\n\t\t2\n\t1\n/\n", "\\\n\t{{>partial}}\n/\n", context());
}
/* @Test */ public void testNestedPartialBlankLinesCRLF () {
@Test public void testNestedPartialBlankLinesCRLF () {
test(Mustache.compiler().withLoader(new Mustache.TemplateLoader() {
public Reader getTemplate (String name) {
if (name.equals("partial")) {
@@ -260,19 +286,14 @@ public abstract class SharedTests
return new StringReader("2\r\na\r\n\r\nb\r\n2\r\n");
}
}
}), "\\\r\n\t1\r\n\t\t2\r\n\t\ta\r\n\r\n\t\tb\r\n\t\t2\r\n\t1\r\n/\r\n", "\\\r\n\t{{>partial}}\r\n/\r\n", context());
}), "\\\r\n\t1\r\n\t\t2\r\n\t\ta\r\n\t\t\r\n\t\tb\r\n\t\t2\r\n\t1\r\n/\r\n", "\\\r\n\t{{>partial}}\r\n/\r\n", context());
}
/* @Test */ public void testNestedPartialIndent () {
test(Mustache.compiler().withLoader(new Mustache.TemplateLoader() {
public Reader getTemplate (String name) {
if (name.equals("partial")) {
return new StringReader("1\n {{>nest}}\n1\n");
} else {
return new StringReader("2\n{{{content}}}\n2\n");
}
}
}), "|\n 1\n 2\n <\n->\n 2\n 1\n|\n", "|\n {{>partial}}\n|\n", context("content", "<\n->"));
@Test public void testNestedPartialIndent () {
Mustache.TemplateLoader loader = partials(entry("partial", "1\n {{>nest}}\n1\n"), entry("nest", "2\n{{{content}}}\n2\n"));
test(Mustache.compiler().withLoader(loader),
"|\n 1\n 2\n <\n->\n 2\n 1\n|\n",
"|\n {{>partial}}\n|\n", context("content", "<\n->"));
}
@Test public void testPartialIndentWithVariableAtTheStart () {
@@ -826,12 +847,40 @@ public abstract class SharedTests
test(Mustache.compiler().withDelims("<% %>"), "bar", "<%foo%>", context("foo", "bar"));
}
protected String name;
@Rule public TestRule watcher = new TestWatcher() {
protected void starting(Description description) {
name = description.getDisplayName();
}
};
protected void test(Mustache.Compiler compiler, String expected, String template, Object ctx) {
check(expected, compiler.compile(template).execute(ctx));
String actual = compiler.compile(template).execute(ctx);
if (! Objects.equals(expected, actual)) {
System.out.println("");
System.out.println("----------------------------------------");
System.out.println("");
System.out.println("Failed: " + name);
System.out.println("Template : \"" + SpecTest.showWhitespace(template) + "\"");
if (! partials.isEmpty()) {
System.out.println("Partials : ");
for (Entry<String, String> e : partials.entrySet()) {
System.out.println("\t" + e.getKey() + ": \"" + SpecTest.showWhitespace(e.getValue()) + "\"");
}
}
System.out.println("Expected : \"" + SpecTest.showWhitespace(expected) + "\"");
System.out.println("Result : \"" + SpecTest.showWhitespace(actual) + "\"");
System.out.println("----------------------------------------");
System.out.println("");
}
check(expected, actual);
}
protected void check (String expected, String output) {
assertEquals(uncrlf(expected), uncrlf(output));
//assertEquals(uncrlf(expected), uncrlf(output));
assertEquals(SpecTest.showWhitespace(expected), SpecTest.showWhitespace(output));
}
protected void test (String expected, String template, Object ctx) {
@@ -0,0 +1,24 @@
package com.samskivert.mustache.specs;
import java.util.Collection;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class CustomSpecTest extends SpecTest {
public CustomSpecTest(Spec spec, String name) {
super(spec, name);
}
@Parameters(name = "{1}")
public static Collection<Object[]> data () {
String[] groups = new String[] {
"partials"
};
return SpecTest.data("/custom/specs/", groups);
}
}
@@ -0,0 +1,29 @@
package com.samskivert.mustache.specs;
import java.util.Collection;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class OfficialSpecTest extends SpecTest {
public OfficialSpecTest(Spec spec, String name) {
super(spec, name);
}
@Parameters(name = "{1}")
public static Collection<Object[]> data () {
String[] groups = new String[] {
"comments",
"delimiters",
"interpolation",
"inverted",
"sections",
"partials"
};
return SpecTest.data("/specs/specs/", groups);
}
}
@@ -5,6 +5,7 @@
package com.samskivert.mustache.specs;
import java.util.Map;
import java.util.function.Consumer;
/**
* @author Yoryos Valotasios
@@ -26,7 +27,7 @@ public class Spec
}
public String getDescription () {
return (String) map.get("descr");
return (String) map.get("desc");
}
public String getTemplate () {
@@ -44,4 +45,20 @@ public class Spec
public String getPartial (String name) {
return partials == null ? null : partials.get(name);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
Consumer<String> value = s -> sb.append("\"").append(s).append("\"").append("\n");
Consumer<String> label = s -> sb.append("").append(s).append(": ");
label.accept("name");
value.accept(getName());
label.accept("desc");
value.accept(getDescription());
label.accept("template");
value.accept(getTemplate());
label.accept("expected");
value.accept(getExpectedOutput());
return sb.toString();
}
}
@@ -12,9 +12,10 @@ import com.samskivert.mustache.Mustache;
public class SpecAwareTemplateLoader implements Mustache.TemplateLoader
{
private static final String EMPTY_STRING = "";
private Spec spec;
private final Spec spec;
public void setSpec (Spec spec) {
public SpecAwareTemplateLoader(Spec spec) {
super();
this.spec = spec;
}
@@ -7,19 +7,14 @@ import java.util.Map;
import java.util.Objects;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.error.YAMLException;
import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Template;
@RunWith(Parameterized.class)
public class SpecTest {
public abstract class SpecTest {
private static final Yaml yaml = new Yaml();
@@ -32,19 +27,11 @@ public class SpecTest {
this.name = name;
}
private static Mustache.Compiler compiler;
private static SpecAwareTemplateLoader loader;
@BeforeClass
public static void setUp () {
loader = new SpecAwareTemplateLoader();
compiler = Mustache.compiler().defaultValue("").withLoader(loader);
}
@Test
public void test () throws Exception {
//System.out.println("Testing: " + name);
loader.setSpec(spec);
SpecAwareTemplateLoader loader = new SpecAwareTemplateLoader(spec);
Mustache.Compiler compiler = Mustache.compiler().defaultValue("").withLoader(loader);
String tmpl = spec.getTemplate();
String desc = String.format("Template: '''%s'''\nData: '%s'\n",
uncrlf(tmpl), uncrlf(spec.getData().toString()));
@@ -75,7 +62,7 @@ public class SpecTest {
}
}
private static String showWhitespace (String s) {
public static String showWhitespace (String s) {
s = s.replace("\r\n", "\u240D");
s = s.replace('\t', '\u21E5');
s = s.replace("\n", "\u21B5\n");
@@ -88,20 +75,12 @@ public class SpecTest {
return (text == null) ? null : text.replace("\r", "\\r").replace("\n", "\\n");
}
@Parameters(name = "{1}")
public static Collection<Object[]> data () {
String[] groups = new String[] {
"comments",
"delimiters",
"interpolation",
"inverted",
"sections",
"partials"
};
public static Collection<Object[]> data (String specPath, String[] groups) {
List<Object[]> tuples = new ArrayList<>();
int i = 0;
for (String g : groups) {
Iterable<Spec> specs = getTestsForGroup(g);
Iterable<Spec> specs = getTestsForGroup(specPath, g);
for (Spec s : specs) {
Object[] tuple = new Object[] {s, g + "-" + s.getName() + "-" + i++};
tuples.add(tuple);
@@ -110,8 +89,13 @@ public class SpecTest {
return tuples;
}
private static Iterable<Spec> getTestsForGroup (String name) {
String ymlPath = "/specs/specs/" + name + ".yml";
private static Iterable<Spec> getTestsForGroup (String specPath, String name) {
//String ymlPath = "/specs/specs/" + name + ".yml";
String ymlPath = specPath + name + ".yml";
return getTestsFromYaml(name, ymlPath);
}
private static Iterable<Spec> getTestsFromYaml(String name, String ymlPath) {
try {
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) yaml.load(SpecTest.class.getResourceAsStream(ymlPath));
@@ -0,0 +1,45 @@
overview: |
Partial tags are used to expand an external template into the current
template.
The tag's content MUST be a non-whitespace character sequence NOT containing
the current closing delimiter.
This tag's content names the partial to inject. Set Delimiter tags MUST NOT
affect the parsing of a partial. The partial MUST be rendered against the
context stack local to the tag. If the named partial cannot be found, the
empty string SHOULD be used instead, as in interpolations.
Partial tags SHOULD be treated as standalone when appropriate. If this tag
is used standalone, any whitespace preceding the tag should treated as
indentation, and prepended to each line of the partial before rendering.
tests:
- name: Nested Partial Indent
desc: "Nested partials should including the parent partials indent"
data: { content: "<\n->" }
template: "|\n {{>partial}}\n|\n"
partials:
partial: "1\n {{>nest}}\n1\n"
nest: "2\n{{{content}}}\n2\n"
expected: "|\n 1\n 2\n <\n->\n 2\n 1\n|\n"
- name: Standalone Indentation
desc: Each line of the partial should be indented before rendering.
data: { content: "<\n->" }
template: |
\
{{>partial}}
/
partials:
partial: |
|
{{{content}}}
|
expected: |
\
|
<
->
|
/