package handlebarsjs.spec;
import java.io.IOException;
import java.util.List;
import org.junit.Test;
import com.github.jknack.handlebars.AbstractTest;
import com.github.jknack.handlebars.Helper;
import com.github.jknack.handlebars.Options;
public class BlockTest extends AbstractTest {
@Test
public void array() throws IOException {
String string = "{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!";
Object hash = $("goodbyes", new Object[]{
$("text", "goodbye"),
$("text", "Goodbye"),
$("text", "GOODBYE") },
"world", "world"
);
shouldCompileTo(string, hash, "goodbye! Goodbye! GOODBYE! cruel world!",
"Arrays iterate over the contents when not empty");
shouldCompileTo(string, $("goodbyes", new Object[0], "world", "world"), "cruel world!",
"Arrays ignore the contents when empty");
}
@Test
public void arrayWithIndex() throws IOException {
String string = "{{#goodbyes}}{{@index}}. {{text}}! {{/goodbyes}}cruel {{world}}!";
Object hash = $("goodbyes", new Object[]{
$("text", "goodbye"),
$("text", "Goodbye"),
$("text", "GOODBYE") },
"world", "world"
);
shouldCompileTo(string, hash, "0. goodbye! 1. Goodbye! 2. GOODBYE! cruel world!",
"The @index variable is used");
}
@Test
public void emptyBlock() throws IOException {
String string = "{{#goodbyes}}{{/goodbyes}}cruel {{world}}!";
Object hash = $("goodbyes", new Object[]{
$("text", "goodbye"),
$("text", "Goodbye"),
$("text", "GOODBYE") },
"world", "world"
);
shouldCompileTo(string, hash, "cruel world!",
"Arrays iterate over the contents when not empty");
hash = $("goodbyes", new Object[0], "world", "world");
shouldCompileTo(string, hash, "cruel world!",
"Arrays ignore the contents when empty");
}
@Test
public void blockWithComplexLookup() throws IOException {
String string = "{{#goodbyes}}{{text}} cruel {{../name}}! {{/goodbyes}}";
Object hash = $("goodbyes", new Object[]{
$("text", "goodbye"),
$("text", "Goodbye"),
$("text", "GOODBYE") },
"name", "Alan"
);
shouldCompileTo(string, hash, "goodbye cruel Alan! Goodbye cruel Alan! GOODBYE cruel Alan! ",
"Templates can access variables in contexts up the stack with relative path syntax");
}
@Test
public void helperWithComplexLookup$() throws IOException {
String string = "{{#goodbyes}}{{{link ../prefix}}}{{/goodbyes}}";
Object hash = $("prefix", "/root", "goodbyes",
new Object[]{$("text", "Goodbye", "url", "goodbye") });
Hash helpers = $("link", new Helper<Object>() {
@Override
public Object apply(final Object prefix, final Options options) throws IOException {
Object url = options.context.get("url");
Object text = options.context.get("text");
return "<a href='" + prefix + "/" + url + "'>" + text + "</a>";
}
});
shouldCompileTo(string, hash, helpers, "<a href='/root/goodbye'>Goodbye</a>");
}
@Test
public void helperWithComplexLookupExpression() throws IOException {
String string = "{{#goodbyes}}{{../name}}{{/goodbyes}}";
String hash = "{name: Alan}";
Hash helpers = $("goodbyes", new Helper<Object>() {
@Override
public Object apply(final Object context, final Options options) throws IOException {
String out = "";
String[] byes = {"Goodbye", "goodbye", "GOODBYE" };
for (String bye : byes) {
out += bye + " " + options.fn(this) + "! ";
}
return out;
}
});
shouldCompileTo(string, hash, helpers, "Goodbye Alan! goodbye Alan! GOODBYE Alan! ");
}
@Test
public void helperWithComplexLookupAndNestedTemplate() throws IOException {
String string = "{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}";
Object hash = $("prefix", "/root", "goodbyes",
new Object[]{$("text", "Goodbye", "url", "goodbye") });
Hash helpers = $("link", new Helper<Object>() {
@Override
public Object apply(final Object prefix, final Options options) throws IOException {
Object url = options.context.get("url");
Object text = options.context.get("text");
return "<a href='" + prefix + "/" + url + "'>" + text + "</a>";
}
});
shouldCompileTo(string, hash, helpers, "<a href='/root/goodbye'>Goodbye</a>");
}
@Test
public void blockWithDeepNestedComplexLookup() throws IOException {
String string = "{{#outer}}Goodbye {{#inner}}cruel {{../../omg}}{{/inner}}{{/outer}}";
Object hash = $("omg", "OMG!", "outer",
new Object[]{$("inner", new Object[]{$("text", "goodbye") }) });
shouldCompileTo(string, hash, "Goodbye cruel OMG!");
}
@Test
public void blockHelper() throws IOException {
String string = "{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!";
String hash = "{world: world}";
Hash helpers = $("goodbyes", new Helper<Object>() {
@Override
public Object apply(final Object context, final Options options) throws IOException {
return options.fn($("text", "GOODBYE"));
}
});
shouldCompileTo(string, hash, helpers, "GOODBYE! cruel world!", "Block helper executed");
}
@Test
public void blockHelperStayingInTheSameContext() throws IOException {
String string = "{{#form}}<p>{{name}}</p>{{/form}}";
String hash = "{name: Yehuda}";
Hash helpers = $("form", new Helper<Object>() {
@Override
public Object apply(final Object context, final Options options) throws IOException {
return "<form>" + options.fn(this) + "</form>";
}
});
shouldCompileTo(string, hash, helpers, "<form><p>Yehuda</p></form>",
"Block helper executed with current context");
}
@Test
public void blockHelperShouldHaveContextInThis() throws IOException {
String string = "<ul>{{#people}}<li>{{#link}}{{name}}{{/link}}</li>{{/people}}</ul>";
Object hash = $("people", new Object[]{
$("name", "Alan", "id", 1),
$("name", "Yehuda", "id", 2)
});
Hash helpers = $("link", new Helper<Object>() {
@Override
public Object apply(final Object context, final Options options) throws IOException {
return "<a href=\"/people/" + options.get("id") + "\">" + options.fn(this) + "</a>";
}
});
shouldCompileTo(string, hash, helpers,
"<ul><li><a href=\"/people/1\">Alan</a></li><li><a href=\"/people/2\">Yehuda</a></li></ul>");
}
@Test
public void blockHelperForUndefinedValue() throws IOException {
shouldCompileTo("{{#_empty}}shouldn't render{{/_empty}}", $, "");
}
@Test
public void blockHelperPassingNewContext() throws IOException {
String string = "{{#form yehuda}}<p>{{name}}</p>{{/form}}";
String hash = "{yehuda: {name: Yehuda}}";
Hash helpers = $("form", new Helper<Object>() {
@Override
public Object apply(final Object context, final Options options) throws IOException {
return "<form>" + options.fn(context) + "</form>";
}
});
shouldCompileTo(string, hash, helpers, "<form><p>Yehuda</p></form>",
"Context variable resolved");
}
@Test
public void blockHelperPassingComplexContextPath() throws IOException {
String string = "{{#form yehuda/cat}}<p>{{name}}</p>{{/form}}";
String hash = "{yehuda: {name: Yehuda, cat: {name: Harold}}}";
Hash helpers = $("form", new Helper<Object>() {
@Override
public Object apply(final Object context, final Options options) throws IOException {
return "<form>" + options.fn(context) + "</form>";
}
});
shouldCompileTo(string, hash, helpers, "<form><p>Harold</p></form>",
"Complex path variable resolved");
}
@Test
public void nestedBlockHelpers() throws IOException {
String string = "{{#form yehuda}}<p>{{name}}</p>{{#link}}Hello{{/link}}{{/form}}";
String hash = "yehuda: {name: Yehuda}";
Hash helpers = $("form", new Helper<Object>() {
@Override
public Object apply(final Object context, final Options options) throws IOException {
return "<form>" + options.fn(context) + "</form>";
}
}, "link", new Helper<Object>() {
@Override
public Object apply(final Object context, final Options options) throws IOException {
return "<a href='" + options.get("name") + "'>" + options.fn(this) + "</a>";
}
});
shouldCompileTo(string, hash, helpers, "<form><p>Yehuda</p><a href='Yehuda'>Hello</a></form>",
"Both blocks executed");
}
@Test
public void blockInvertedSections() throws IOException {
shouldCompileTo("{{#people}}{{name}}{{^}}{{none}}{{/people}}", "{none: No people}",
"No people");
}
@Test
public void blockInvertedSectionsWithEmptyArrays() throws IOException {
shouldCompileTo("{{#people}}{{name}}{{^}}{{none}}{{/people}}", $("none", "No people",
"people", new Object[0]), "No people");
}
@Test
public void blockHelperInvertedSections() throws Exception {
String string = "{{#list people}}{{name}}{{^}}<em>Nobody's here</em>{{/list}}";
Hash helpers = $("list", new Helper<List<Object>>() {
@Override
public Object apply(final List<Object> context, final Options options) throws IOException {
if (context.size() > 0) {
String out = "<ul>";
for (Object element : context) {
out += "<li>";
out += options.fn(element);
out += "</li>";
}
out += "</ul>";
return out;
} else {
return "<p>" + options.inverse(this) + "</p>";
}
}
});
Object hash = $("people", new Object[]{$("name", "Alan"), $("name", "Yehuda") });
Object empty = $("people", new Object[0]);
Object rootMessage = $("people", new Object[0], "message", "Nobody's here");
String messageString = "{{#list people}}Hello{{^}}{{message}}{{/list}}";
// the meaning here may be kind of hard to catch, but list.not is always called,
// so we should see the output of both
shouldCompileTo(string, hash, helpers, "<ul><li>Alan</li><li>Yehuda</li></ul>",
"an inverse wrapper is passed in as a new context");
shouldCompileTo(string, empty, helpers, "<p><em>Nobody's here</em></p>",
"an inverse wrapper can be optionally called");
shouldCompileTo(messageString, rootMessage, helpers, "<p>Nobody's here</p>",
"the context of an inverse is the parent of the block");
}
}