/**
* This file Copyright (c) 2003-2012 Magnolia International
* Ltd. (http://www.magnolia-cms.com). All rights reserved.
*
*
* This file is dual-licensed under both the Magnolia
* Network Agreement and the GNU General Public License.
* You may elect to use one or the other of these licenses.
*
* This file is distributed in the hope that it will be
* useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
* Redistribution, except as permitted by whichever of the GPL
* or MNA you select, is prohibited.
*
* 1. For the GPL license (GPL), you can redistribute and/or
* modify this file under the terms of the GNU General
* Public License, Version 3, as published by the Free Software
* Foundation. You should have received a copy of the GNU
* General Public License, Version 3 along with this program;
* if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* 2. For the Magnolia Network Agreement (MNA), this file
* and the accompanying materials are made available under the
* terms of the MNA which accompanies this distribution, and
* is available at http://www.magnolia-cms.com/mna.html
*
* Any modifications to this file must keep this entire header
* intact.
*
*/
package info.magnolia.freemarker;
import static org.easymock.EasyMock.*;
import static org.junit.Assert.*;
import info.magnolia.cms.beans.config.URI2RepositoryManager;
import info.magnolia.cms.core.AggregationState;
import info.magnolia.cms.core.Content;
import info.magnolia.cms.i18n.I18nContentSupport;
import info.magnolia.cms.security.User;
import info.magnolia.context.Context;
import info.magnolia.context.MgnlContext;
import info.magnolia.context.SystemContext;
import info.magnolia.context.WebContext;
import info.magnolia.link.LinkTransformerManager;
import info.magnolia.repository.RepositoryConstants;
import info.magnolia.test.ComponentsTestUtil;
import info.magnolia.test.mock.MockAggregationState;
import info.magnolia.test.mock.MockContent;
import info.magnolia.test.mock.MockHierarchyManager;
import info.magnolia.test.mock.MockUtil;
import info.magnolia.test.mock.jcr.MockSession;
import info.magnolia.test.model.Color;
import info.magnolia.test.model.Pair;
import java.io.IOException;
import java.io.StringWriter;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javax.jcr.RepositoryException;
import org.junit.Test;
import freemarker.core.InvalidReferenceException;
import freemarker.template.TemplateException;
/**
* @version $Id$
*/
public class FreemarkerHelperTest extends AbstractFreemarkerTestCase {
@Test
public void testWeCanUseAnyObjectTypeAsOurRoot() throws IOException, TemplateException {
tplLoader.putTemplate("test.ftl", "${left} ${right.left.blue - left} ${right.right.green} ${right.right.name}");
final Pair<Integer, Pair<Color, Color>> root = new Pair<Integer, Pair<Color, Color>>(Integer.valueOf(100), new Pair<Color, Color>(Color.PINK, Color.ORANGE));
assertRendereredContent("100 75 200 orange", root, "test.ftl");
}
@Test
public void testSimpleNodeReferenceOutputsItsName() throws TemplateException, IOException {
final MockContent foo = new MockContent("foo");
foo.addContent(new MockContent("bar"));
foo.addContent(new MockContent("baz"));
foo.addContent(new MockContent("gazonk"));
final Pair<Color, Content> pair = new Pair<Color, Content>(Color.ORANGE, foo);
final Map<String, Object> map = createSingleValueMap("pair", pair);
tplLoader.putTemplate("test.ftl", "${pair.right} ${pair.right.gazonk} ${pair.left?string} ${pair.right?string} ${pair.right.gazonk?string}");
assertRendereredContent("foo gazonk color:orange foo gazonk", map, "test.ftl");
}
@Test
public void testSubNodesAreReachable() throws TemplateException, IOException {
tplLoader.putTemplate("test_sub.ftl", "The child node's bli'bla property is ${bli['bla']}");
final MockContent c = new MockContent("plop");
c.setUUID("123");
c.addNodeData("foo", "bar");
final MockContent bli = new MockContent("bli");
c.addContent(bli);
bli.addNodeData("bla", "bloup");
assertRendereredContent("The child node's bli'bla property is bloup", c, "test_sub.ftl");
}
@Test
public void testSubSubNode() throws TemplateException, IOException {
final MockContent baz = new MockContent("baz");
baz.addNodeData("prop", "wassup");
final MockContent bar = new MockContent("bar");
final MockContent foo = new MockContent("foo");
final MockContent c = new MockContent("root");
bar.addContent(baz);
foo.addContent(bar);
c.addContent(foo);
tplLoader.putTemplate("test.ftl", "yo, ${foo.bar.baz['prop']} ?");
assertRendereredContent("yo, wassup ?", c, "test.ftl");
}
@Test
public void testCanReachParentWithBuiltIn() throws Exception {
final Content c = MockUtil.createNode("/foo/bar",
"/foo.myProp=this is foo",
"/foo/bar.myProp=this is bar");
tplLoader.putTemplate("test.ftl", "${content.myProp} and ${content?parent.myProp}");
assertRendereredContent("this is bar and this is foo", createSingleValueMap("content", c), "test.ftl");
}
/** not supported:
@Test
public void testCanReachParentWithProperty() throws Exception {
final Content c = MockUtil.createNode("/foo/bar",
"/foo.myProp=this is foo",
"/foo/bar.myProp=this is bar");
tplLoader.putTemplate("test.ftl", "${content.myProp} and ${content.parent.myProp}");
assertRendereredContent("this is bar and this is foo", createSingleValueMap("content", c), "test.ftl");
}*/
/** not supported:
@Test
public void testCanReachParentWithMethod() throws Exception {
final Content c = MockUtil.createNode("/foo/bar",
"/foo.myProp=this is foo",
"/foo/bar.myProp=this is bar");
tplLoader.putTemplate("test.ftl", "${content.myProp} and ${content.getParent().myProp}");
assertRendereredContent("this is bar and this is foo", createSingleValueMap("content", c), "test.ftl");
}*/
@Test
public void testCanLoopThroughNodes() throws TemplateException, IOException {
final MockContent foo = new MockContent("foo");
final MockContent c = new MockContent("root");
foo.addContent(new MockContent("bar"));
foo.addContent(new MockContent("baz"));
foo.addContent(new MockContent("gazonk"));
c.addContent(foo);
tplLoader.putTemplate("test.ftl", "${foo?children?size}: <#list foo?children as n>${n.@handle} </#list>");
assertRendereredContent("3: /root/foo/bar /root/foo/baz /root/foo/gazonk ", c, "test.ftl");
}
@Test
public void testCanLoopThroughNodesNestedInBean() throws TemplateException, IOException {
final MockContent foo = new MockContent("foo");
foo.addContent(new MockContent("bar"));
foo.addContent(new MockContent("baz"));
foo.addContent(new MockContent("gazonk"));
final Object pair = new Pair<Color, Content>(Color.ORANGE, foo);
final String key = "pair";
Map map = createSingleValueMap(key, pair);
tplLoader.putTemplate("test.ftl", "${pair.right?children?size}: <#list pair.right?children as n>${n.@handle} </#list>");
assertRendereredContent("3: /foo/bar /foo/baz /foo/gazonk ", map, "test.ftl");
}
@Test
public void testCanLoopThroughPropertiesUsingTheKeysBuiltIn() throws TemplateException, IOException {
final MockContent f = new MockContent("flebele");
f.addNodeData("foo", "bar");
f.addNodeData("bar", "baz");
f.addNodeData("baz", "gazonk");
final MockContent c = new MockContent("root");
c.addContent(f);
tplLoader.putTemplate("test.ftl", "${flebele?keys?size}:<#list flebele?keys as n> ${n}=${flebele[n]}</#list>");
assertRendereredContent("3: foo=bar bar=baz baz=gazonk", c, "test.ftl");
}
@Test
public void testCanLoopThroughPropertiesUsingTheValuesBuiltIn() throws TemplateException, IOException {
final MockContent f = new MockContent("flebele");
f.addNodeData("foo", "bar");
f.addNodeData("bar", "baz");
f.addNodeData("baz", "gazonk");
final MockContent c = new MockContent("root");
c.addContent(f);
tplLoader.putTemplate("test.ftl", "${flebele?values?size}:<#list flebele?values as v> ${v}</#list>");
assertRendereredContent("3: bar baz gazonk", c, "test.ftl");
}
// public void testCanGetPropertyHandle() throws TemplateException, IOException {
// final MockContent f = new MockContent("flebele");
// f.addNodeData(new MockNodeData("foo", "bar"));
// final MockContent c = new MockContent("root");
// c.addContent(f);
// tplLoader.putTemplate("test2.ftl", "${flebele['foo'].@handle} ${flebele['foo'].name}");// ${flebele.foo.@handle}");
// assertRendereredContent("/root/flebele/foo", c, "test2.ftl");
// }
@Test
public void testBooleanPropertiesAreHandledProperly() throws TemplateException, IOException {
final MockContent c = new MockContent("root");
final MockContent foo = new MockContent("foo");
foo.addNodeData("hop", Boolean.TRUE);
foo.addNodeData("hip", Boolean.FALSE);
c.addContent(foo);
tplLoader.putTemplate("test.ftl", "${foo['hop']?string(\"yes\", \"no\")}" +
" ${foo.hop?string(\"yes\", \"no\")}" +
" ${foo['hip']?string(\"yes\", \"no\")}" +
" ${foo.hip?string(\"yes\", \"no\")}");
assertRendereredContent("yes yes no no", c, "test.ftl");
}
@Test
public void testDatePropertiesAreHandledProperly() throws TemplateException, IOException {
final MockContent c = new MockContent("root");
final MockContent foo = new MockContent("foo");
foo.addNodeData("date", new GregorianCalendar(2007, 5, 3, 15, 39, 46));
c.addContent(foo);
tplLoader.putTemplate("test.ftl", "${foo['date']?string('yyyy-MM-dd HH:mm:ss')}");
assertRendereredContent("2007-06-03 15:39:46", c, "test.ftl");
}
@Test
public void testNumberProperties() throws TemplateException, IOException {
final MockContent c = new MockContent("root");
final MockContent foo = new MockContent("foo");
foo.addNodeData("aLong", new Long(1234567890123456789l));
foo.addNodeData("aDouble", new Double(12345678.901234567890d));
c.addContent(foo);
tplLoader.putTemplate("test.ftl", "${foo['aLong']} , ${foo.aDouble}");
// ! TODO ! this is locale dependent
assertRendereredContent("1,234,567,890,123,456,789 , 12,345,678.901", c, "test.ftl");
}
@Test
public void testReferenceProperties() throws TemplateException, IOException, RepositoryException {
final MockContent foo = new MockContent("foo");
final MockContent bar = new MockContent("bar");
foo.addNodeData("some-ref", bar);
bar.addNodeData("baz", "gazonk");
final MockHierarchyManager hm = new MockHierarchyManager();
hm.getRoot().addContent(foo);
hm.getRoot().addContent(bar);
tplLoader.putTemplate("test.ftl", "${foo['some-ref']} ${foo['some-ref'].baz}");
assertRendereredContent("bar gazonk", createSingleValueMap("foo", foo), "test.ftl");
}
@Test
public void testRendereredWithCurrentLocale() throws TemplateException, IOException {
tplLoader.putTemplate("test.ftl", "this is a test template.");
tplLoader.putTemplate("test_en.ftl", "this is a test template in english.");
tplLoader.putTemplate("test_fr_BE.ftl", "Ceci est une template belge hein une fois.");
tplLoader.putTemplate("test_fr.ftl", "Ceci est une template de test en français.");
final MockContent c = new MockContent("pouet");
c.setUUID("123");
c.addNodeData("foo", "bar");
assertRendereredContent("Ceci est une template belge hein une fois.", new Locale("fr", "BE"), c, "test.ftl");
}
@Test
public void testMissingAndDefaultValueOperatorsActsAsIExceptThemTo() throws IOException, TemplateException {
tplLoader.putTemplate("test.ftl", "[#if content.title?has_content]<h2>${content.title}</h2>[/#if]");
final MockContent c = new MockContent("pouet");
final Map m = createSingleValueMap("content", c);
assertRendereredContent("", m, "test.ftl");
c.addNodeData("title", "");
assertRendereredContent("", m, "test.ftl");
c.addNodeData("title", "pouet");
assertRendereredContent("<h2>pouet</h2>", m, "test.ftl");
}
@Test
public void testContextPathIsAddedWithWebContext() throws IOException, TemplateException {
tplLoader.putTemplate("pouet", ":${contextPath}:");
final WebContext context = createStrictMock(WebContext.class);
expect(context.getLocale()).andReturn(Locale.US);
expect(context.getContextPath()).andReturn("/tralala");
expect(context.getAggregationState()).andReturn(new MockAggregationState());
expect(context.getServletContext()).andReturn(null);
expect(context.getRequest()).andReturn(null);
expect(context.getResponse()).andReturn(null);
replay(context);
MgnlContext.setInstance(context);
assertRendereredContentWithoutCheckingContext(":/tralala:", new HashMap(), "pouet");
verify(context);
}
@Test
public void testContextPathIsNotAddedWithNotWebContext() throws IOException, TemplateException {
tplLoader.putTemplate("pouet", ":${contextPath}:");
final Context context = createStrictMock(Context.class);
expect(context.getLocale()).andReturn(Locale.US);
replay(context);
MgnlContext.setInstance(context);
final StringWriter out = new StringWriter();
try {
fmHelper.render("pouet", new HashMap(), out);
fail("should have failed");
} catch (InvalidReferenceException e) {
assertEquals("Expression contextPath is undefined on line 1, column 4 in pouet.", e.getMessage());
}
verify(context);
}
@Test
public void testContextPathIsAlsoAvailableThroughMagnoliaContext() throws IOException, TemplateException {
tplLoader.putTemplate("pouet", ":${ctx.contextPath}:");
final WebContext context = createStrictMock(WebContext.class);
expect(context.getLocale()).andReturn(Locale.US);
expect(context.getContextPath()).andReturn("/tralala"); // called when preparing the freemarker data model
expect(context.getAggregationState()).andReturn(new MockAggregationState());
expect(context.getServletContext()).andReturn(null);
expect(context.getRequest()).andReturn(null);
expect(context.getResponse()).andReturn(null);
expect(context.getContextPath()).andReturn("/tralala"); // actual call from the template
replay(context);
MgnlContext.setInstance(context);
assertRendereredContentWithoutCheckingContext(":/tralala:", new HashMap(), "pouet");
verify(context);
}
@Test
public void testMagnoliaContextIsExposed() throws IOException, TemplateException {
tplLoader.putTemplate("pouet", ":${ctx.user.name}:");
final Context context = createStrictMock(Context.class);
final User user = createStrictMock(User.class);
expect(context.getLocale()).andReturn(Locale.US);
expect(context.getUser()).andReturn(user);
expect(user.getName()).andReturn("Hiro Nakamura");
replay(context, user);
MgnlContext.setInstance(context);
assertRendereredContentWithoutCheckingContext(":Hiro Nakamura:", new HashMap(), "pouet");
verify(context, user);
}
@Test
public void testMagnoliaContextAttributesAreAvailableWithMapSyntax() throws IOException, TemplateException {
tplLoader.putTemplate("pouet", ":${ctx.foo}:${ctx['baz']}:");
final Context context = createStrictMock(Context.class);
expect(context.getLocale()).andReturn(Locale.US);
expect(context.get("foo")).andReturn("bar");
expect(context.get("baz")).andReturn("buzz");
replay(context);
MgnlContext.setInstance(context);
assertRendereredContentWithoutCheckingContext(":bar:buzz:", new HashMap(), "pouet");
verify(context);
}
@Test
public void testEvalCanEvaluateDynamicNodeProperties() throws IOException, TemplateException {
tplLoader.putTemplate("test.ftl", "evaluated result: ${'content.title'?eval}");
final MockContent c = new MockContent("content");
c.setUUID("123");
c.addNodeData("title", "This is my title");
c.addNodeData("other", "other-value");
Map<String, Object> m = new HashMap<String, Object>();
m.put("content", c);
assertRendereredContent("evaluated result: This is my title", m, "test.ftl");
}
@Test
public void testInterpretCanBeUsedForDynamicNodeProperties() throws IOException, TemplateException {
tplLoader.putTemplate("test.ftl", "[#assign dynTpl='${content.title}'?interpret]\n" +
"evaluated result: [@dynTpl/]");
final MockContent c = new MockContent("content");
c.setUUID("123");
c.addNodeData("title", "This is my ${content.other} title");
c.addNodeData("other", "other-value");
Map<String, Object> m = new HashMap<String, Object>();
m.put("content", c);
assertRendereredContent("evaluated result: This is my other-value title", m, "test.ftl");
}
@Test
public void testEvalCanAlsoBeUsedForNestedExpressions() throws IOException, TemplateException {
// except we need lots of quotes
tplLoader.putTemplate("test.ftl", "evaluated result: ${'\"${content.title}\"'?eval}");
final MockContent c = new MockContent("content");
c.setUUID("123");
c.addNodeData("title", "This is my ${content.other}");
c.addNodeData("other", "other-value");
Map<String, Object> m = new HashMap<String, Object>();
m.put("content", c);
assertRendereredContent("evaluated result: This is my other-value", m, "test.ftl");
}
@Test
public void testInterpretCanBeUsedEvenIfPropertyHasNoFreemarkerStuff() throws IOException, TemplateException {
tplLoader.putTemplate("test.ftl", "[#assign dynTpl='${content.title}'?interpret]evaluated title: [@dynTpl/]");
final MockContent c = new MockContent("content");
c.setUUID("123");
c.addNodeData("title", "This is my plain title");
c.addNodeData("other", "other-value");
Map<String, Object> m = new HashMap<String, Object>();
m.put("content", c);
assertRendereredContent("evaluated title: This is my plain title", m, "test.ftl");
}
private final static String SOME_UUID = "deb0c7d0-402f-4e04-9db3-cb308695733e";
@Test
public void testUuidLinksAreTransformedToRelativeLinksInWebContext() throws IOException, TemplateException, RepositoryException {
final MockContent page = new MockContent("baz");
final MockHierarchyManager hm = prepareHM(page);
final AggregationState agg = new AggregationState();
agg.setMainContent(page);
final WebContext context = createStrictMock(WebContext.class);
final MockSession session = new MockSession("website");
MgnlContext.setInstance(context);
expect(context.getContextPath()).andReturn("/");
expect(context.getLocale()).andReturn(Locale.CANADA);
expect(context.getAggregationState()).andReturn(agg);
expect(MgnlContext.getJCRSession("website")).andReturn(session).times(2);
LinkTransformerManager.getInstance().setMakeBrowserLinksRelative(true);
replay(context);
agg.setOriginalURI("/foo/bar/boo.html");
doTestUuidLinksAreTransformed(context, "== Some text... blah blah... <a href=\"baz.html\">Bleh</a> ! ==");
verify(context);
}
@Test
public void testUuidLinksAreTransformedToAbsoluteLinksInWebContextWithoutAggregationState() throws IOException, TemplateException, RepositoryException {
final MockContent page = new MockContent("baz");
final MockHierarchyManager hm = prepareHM(page);
final MockSession session = new MockSession("website");
LinkTransformerManager.getInstance().setAddContextPathToBrowserLinks(true);
final WebContext context = createStrictMock(WebContext.class);
MgnlContext.setInstance(context);
expect(context.getLocale()).andReturn(Locale.CANADA);
expect(context.getAggregationState()).andReturn(null);
expect(MgnlContext.getJCRSession("website")).andReturn(session).anyTimes();
expect(context.getContextPath()).andReturn("/some-context");
replay(context);
doTestUuidLinksAreTransformed(context, "== Some text... blah blah... <a href=\"/some-context/foo/bar/baz.html\">Bleh</a> ! ==");
verify(context);
}
@Test
public void testUuidLinksAreTransformedToFullUrlLinksInNonWebContext() throws IOException, TemplateException, RepositoryException {
doTestUuidLinksAreTransformed(null, "== Some text... blah blah... <a href=\"http://myTests:1234/yay/foo/bar/baz.html\">Bleh</a> ! ==");
}
private void doTestUuidLinksAreTransformed(Context webCtx, String expectedOutput) throws IOException, TemplateException, RepositoryException {
MockHierarchyManager cfgHM = MockUtil.createHierarchyManager(RepositoryConstants.WEBSITE, "fakeemptyrepo");
MockUtil.mockObservation(cfgHM);
final MockSession session = new MockSession("website");
final SystemContext sysMockCtx = createStrictMock(SystemContext.class);
if (webCtx == null) {
expect(sysMockCtx.getLocale()).andReturn(Locale.KOREA);
final MockHierarchyManager hm = prepareHM(new MockContent("baz"));
expect(sysMockCtx.getJCRSession("website")).andReturn(session).anyTimes();
}
ComponentsTestUtil.setInstance(SystemContext.class, sysMockCtx);
replay(sysMockCtx);
ComponentsTestUtil.setImplementation(URI2RepositoryManager.class, URI2RepositoryManager.class);
final I18nContentSupport i18NSupportMock = createStrictMock(I18nContentSupport.class);
ComponentsTestUtil.setInstance(I18nContentSupport.class, i18NSupportMock);
expect(i18NSupportMock.toI18NURI("/foo/bar.html")).andReturn("/foo/bar/baz.html").times(1, 2);
final String text = "Some text... blah blah... <a href=\"${link:{uuid:{" + SOME_UUID + "},repository:{website},handle:{/foo/bar},nodeData:{},extension:{html}}}\">Bleh</a> !";
final MockContent c = new MockContent("content");
c.addNodeData("text", text);
tplLoader.putTemplate("test", "== ${text} ==");
replay(i18NSupportMock);
MgnlContext.setInstance(webCtx == null ? sysMockCtx : webCtx);
assertRendereredContentWithoutCheckingContext(expectedOutput, c, "test");
verify(i18NSupportMock);
verify(sysMockCtx);
}
@Test
public void testUserPropertiesAreAvailable() throws IOException, TemplateException {
tplLoader.putTemplate("test.ftl", "${user.name} is my name, is speak ${user.language}, I'm ${user.enabled?string('', 'not ')}enabled, and testProp has a value of ${user.testProp} !");
final User user = createStrictMock(User.class);
expect(user.getName()).andReturn("myName");
expect(user.getLanguage()).andReturn("fr");
expect(user.isEnabled()).andReturn(false);
expect(user.getProperty("testProp")).andReturn("testValue");
replay(user);
assertRendereredContent("myName is my name, is speak fr, I'm not enabled, and testProp has a value of testValue !", createSingleValueMap("user", user), "test.ftl");
verify(user);
}
@Test
public void testUserUnsupportedExceptionFallback() throws Exception {
tplLoader.putTemplate("test.ftl", "${user.name} is my name, fullName: ${user.fullName!user.name}, testProp: ${user.testProp!'default'} !");
final User user = createStrictMock(User.class);
expect(user.getName()).andReturn("myName");
expect(user.getProperty("fullName")).andThrow(new UnsupportedOperationException("getProperty:fullName"));
expect(user.getName()).andReturn("myName");
expect(user.getProperty("testProp")).andThrow(new UnsupportedOperationException("getProperty:testValue"));
replay(user);
assertRendereredContent("myName is my name, fullName: myName, testProp: default !", createSingleValueMap("user", user), "test.ftl");
verify(user);
}
@Test
public void testNodeNameCanBeRenderedImplicitly() throws Exception {
tplLoader.putTemplate("test.ftl", "This should output the node's name: ${content}");
final Map root = createSingleValueMap("content", new MockContent("myNode"));
assertRendereredContent("This should output the node's name: myNode", root, "test.ftl");
}
@Test
public void testNodeNameCanBeRenderedExplicitly() throws Exception {
tplLoader.putTemplate("test.ftl", "This should also output the node's name: ${content.@name}");
final Map root = createSingleValueMap("content", new MockContent("myOtherNode"));
assertRendereredContent("This should also output the node's name: myOtherNode", root, "test.ftl");
}
@Test
public void testGivenLocaleTakesOverAnyContextLocale() throws IOException, TemplateException {
tplLoader.putTemplate("test_en.ftl", "in english");
tplLoader.putTemplate("test_de.ftl", "in deutscher Sprache");
tplLoader.putTemplate("test_fr.ftl", "en francais");
tplLoader.putTemplate("test.ftl", "fallback template - no specific language");
assertRendereredContentWithSpecifiedLocale("en francais", Locale.FRENCH, new HashMap(), "test.ftl");
}
@Test
public void testSimpleI18NMessageCanBeUsedInTemplates() throws Exception {
tplLoader.putTemplate("test.ftl", "ouais: ${i18n.get('testMessage')}");
assertRendereredContentWithSpecifiedLocale("ouais: mon message en francais", Locale.FRENCH, new HashMap(), "test.ftl");
}
@Test
public void testSimpleI18NMessageFallsBackToEnglishIfNotSpecifiedGivenLanguage() throws Exception {
tplLoader.putTemplate("test.ftl", "hop: ${i18n.get('testMessage')}");
assertRendereredContentWithSpecifiedLocale("hop: my message in english", Locale.GERMAN, new HashMap(), "test.ftl");
}
// TODO this test can't work at the moment since we're in core and the default bundle is in the admininterface module.
// public void testI18NFallsBackToDefaultBundle() throws Exception {
// tplLoader.putTemplate("test.ftl", "ouais: ${i18n['buttons.admincentral']}");
// assertRendereredContentWithSpecifiedLocale("ouais: Console d'administration", Locale.FRENCH, new HashMap(), "test.ftl");
// }
@Test
public void testCanUseDotSyntaxToGetASimpleI18NMessage() throws Exception {
tplLoader.putTemplate("test.ftl", "ouais: ${i18n.testMessage}");
assertRendereredContentWithSpecifiedLocale("ouais: mon message en francais", Locale.FRENCH, new HashMap(), "test.ftl");
}
@Test
public void testCanUseBracketSyntaxToGetASimpleI18NMessage() throws Exception {
tplLoader.putTemplate("test.ftl", "ouais: ${i18n['testMessage']}");
assertRendereredContentWithSpecifiedLocale("ouais: mon message en francais", Locale.FRENCH, new HashMap(), "test.ftl");
}
@Test
public void testMustUseMethodCallSyntaxToGetAParameterizedI18NMessage() throws Exception {
tplLoader.putTemplate("test.ftl", "result: ${i18n.get('withOneParam', ['bar'])}");
assertRendereredContentWithSpecifiedLocale("result: foo:bar", Locale.FRENCH, new HashMap(), "test.ftl");
}
@Test
public void testSupportsI18NMessagesWithMultipleParameters() throws Exception {
tplLoader.putTemplate("test.ftl", "result: ${i18n.get('withMoreParams', ['one', 'two', 'three'])}");
assertRendereredContentWithSpecifiedLocale("result: 1:one, 2:two, 3:three", Locale.FRENCH, new HashMap(), "test.ftl");
}
@Test
public void testOutputsInterrogationMarksAroundI18NKeyIfUnknown() throws IOException, TemplateException {
tplLoader.putTemplate("test.ftl", "ouais: ${i18n['bleh.blah']}");
assertRendereredContentWithSpecifiedLocale("ouais: ???bleh.blah???", Locale.FRENCH, new HashMap(), "test.ftl");
}
@Test
public void testI18NMessageParametersCanComeFromData() throws IOException, TemplateException {
tplLoader.putTemplate("test.ftl", "result: ${i18n.get('withOneParam', [value])}");
assertRendereredContentWithSpecifiedLocale("result: foo:wesh t'as vu", Locale.FRENCH, createSingleValueMap("value", "wesh t'as vu"), "test.ftl");
}
@Test
public void testCanPassBundleNameFromTemplateWithMethodCallSyntaxToGetSimple18NMessage() throws Exception {
tplLoader.putTemplate("test.ftl", "result: ${i18n.get('testMessage', 'info.magnolia.freemarker.other')}");
assertRendereredContentWithSpecifiedLocale("result: this is the other bundle", Locale.FRENCH, new HashMap(), "test.ftl");
}
@Test
public void testCanPassBundleNameFromTemplateWithMethodCallSyntaxToGetAParameterizedI18NMessage() throws Exception {
tplLoader.putTemplate("test.ftl", "result: ${i18n.get('withOneParam', ['bar'], 'info.magnolia.freemarker.other')}");
assertRendereredContentWithSpecifiedLocale("result: bling:bar", Locale.FRENCH, new HashMap(), "test.ftl");
}
@Test
public void testCanPassBundleNameFromTemplateAndSupportsI18NMessagesWithMultipleParameters() throws Exception {
tplLoader.putTemplate("test.ftl", "result: ${i18n.get('withMoreParams', ['one', 'two', 'three'], 'info.magnolia.freemarker.other')}");
assertRendereredContentWithSpecifiedLocale("result: bling:one, bling:two, bling:three", Locale.FRENCH, new HashMap(), "test.ftl");
}
@Test
public void testCanUseSharedVariables() throws Exception {
fmConfig.addSharedVariable("mySharedVar", "default value");
tplLoader.putTemplate("test.ftl", "shared: ${mySharedVar} - something from the context: ${foobar}");
final Map ctx = createSingleValueMap("foobar", "chalala");
assertRendereredContentWithoutCheckingContext("shared: default value - something from the context: chalala", ctx, "test.ftl");
}
@Test
public void testContextVariablesOverloadSharedVariables() throws Exception {
fmConfig.addSharedVariable("mySharedVar", "default value");
tplLoader.putTemplate("test.ftl", "shared: ${mySharedVar} - something from the context: ${foobar}");
final Map ctx = createSingleValueMap("foobar", "chalala");
ctx.put("mySharedVar", "overridden value");
assertRendereredContentWithoutCheckingContext("shared: overridden value - something from the context: chalala", ctx, "test.ftl");
}
@Test
public void testCanAccessStaticMethodsOfSharedVariables() throws Exception {
// we still have to instantiate the class, but if it's registered as a sharedVariable, it has been instantiated by c2b anyhow
fmConfig.addSharedVariable("foo", new FooBar());
tplLoader.putTemplate("test.ftl", "Coco: ${foo.say('hello')}");
assertRendereredContentWithoutCheckingContext("Coco: FooBar says hello", new HashMap(), "test.ftl");
}
@Test
public void testEnumMembersCanBeUsedInTemplates() throws Exception {
fmConfig.addSharedVariable("chalala", Chalala.class);
tplLoader.putTemplate("test.ftl", "3: ${chalala.three}");
assertRendereredContentWithoutCheckingContext("3: three", new HashMap(), "test.ftl");
}
@Test
public void testEnumCanBeComparedWith() throws Exception {
fmConfig.addSharedVariable("chalala", Chalala.class);
fmConfig.addSharedVariable("three", Chalala.three);
fmConfig.addSharedVariable("two", Chalala.two);
tplLoader.putTemplate("test.ftl", "${(three == chalala.three)?string}, ${(two == chalala.three)?string}, ${(three == chalala.two)?string}, ${(three != chalala.two)?string}");
assertRendereredContentWithoutCheckingContext("true, false, false, true", new HashMap(), "test.ftl");
}
@Test
public void testEnumCanBeListed() throws Exception {
fmConfig.addSharedVariable("chalala", Chalala.class);
tplLoader.putTemplate("test.ftl", "" +
"list enum by keys: [#list chalala?keys as val]${val} [/#list]\n" +
"list enum by values: [#list chalala?values as val]${val} [/#list]");
assertRendereredContentWithoutCheckingContext("" +
"list enum by keys: one two three \n" +
"list enum by values: one two three ", new HashMap(), "test.ftl");
}
@Test
public void testCanAccessEnumPropertiesOfVariables() throws Exception {
fmConfig.addSharedVariable("shared", new EnumContainer(Chalala.two));
final Map ctx = createSingleValueMap("fromContext", new EnumContainer(Chalala.one));
tplLoader.putTemplate("test.ftl", "shared: ${shared.chalala} - from context: ${fromContext.chalala}");
assertRendereredContentWithoutCheckingContext("shared: two - from context: one", ctx, "test.ftl");
}
@Test
public void testUseCombinationOfPadSubStringAndTrimForSafeSubstring() throws Exception {
tplLoader.putTemplate("test.ftl", "[#assign foo='a fairly short string']\n" +
"${foo?right_pad(50)?substring(0, 50)?trim}");
// "${foo[0..50]}"); // this syntax is deprecated and doesn't help in this case
// if there was a ?max built-in or function we could also do
// "${foo?substring(0, foo?length?max(50)}"
assertRendereredContent("a fairly short string", null, "test.ftl");
}
private MockHierarchyManager prepareHM(MockContent page) {
final MockContent root = new MockContent("foo");
final MockContent bar = new MockContent("bar");
page.setUUID(SOME_UUID);
root.addContent(bar);
bar.addContent(page);
MockHierarchyManager hm = new MockHierarchyManager();
hm.getRoot().addContent(root);
return hm;
}
public static class FooBar {
public static String say(String s) {
return FooBar.class.getSimpleName() + " says " + s;
}
}
public enum Chalala {
one, two, three
}
public static class EnumContainer {
private final Chalala c;
public EnumContainer(Chalala c) {
this.c = c;
}
public Chalala getChalala() {
return c;
}
}
}