package com.google.sitebricks.compiler; import com.google.common.collect.Maps; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Provider; import com.google.inject.TypeLiteral; import com.google.sitebricks.Bricks; import com.google.sitebricks.Evaluator; import com.google.sitebricks.MemoryTemplateSource; import com.google.sitebricks.MvelEvaluator; import com.google.sitebricks.Renderable; import com.google.sitebricks.Respond; import com.google.sitebricks.RespondersForTesting; import com.google.sitebricks.Template; import com.google.sitebricks.compiler.template.freemarker.FreemarkerTemplateCompiler; import com.google.sitebricks.http.Delete; import com.google.sitebricks.http.Get; import com.google.sitebricks.http.Patch; import com.google.sitebricks.http.Post; import com.google.sitebricks.http.Put; import com.google.sitebricks.rendering.EmbedAs; import com.google.sitebricks.rendering.control.WidgetRegistry; import com.google.sitebricks.routing.PageBook; import com.google.sitebricks.routing.SystemMetrics; import org.testng.annotations.BeforeMethod; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.lang.annotation.Annotation; import java.util.Map; import javax.servlet.http.HttpServletRequest; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.createNiceMock; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; import static org.testng.Assert.assertEquals; /** * @author Dhanji R. Prasanna (dhanji@gmail.com) */ public class FreemarkerTemplateCompilerTest { private static final String ANNOTATION_EXPRESSIONS = "Annotation expressions"; private Injector injector; private PageBook pageBook; private SystemMetrics metrics; private final Map<String, Class<? extends Annotation>> methods = Maps.newHashMap(); @BeforeMethod public void pre() { methods.put("get", Get.class); methods.put("post", Post.class); methods.put("put", Put.class); methods.put("patch", Patch.class); methods.put("delete", Delete.class); injector = Guice.createInjector(new AbstractModule() { protected void configure() { bind(HttpServletRequest.class).toProvider(mockRequestProviderForContext()); bind(new TypeLiteral<Map<String, Class<? extends Annotation>>>() { }) .annotatedWith(Bricks.class) .toInstance(methods); } }); pageBook = createNiceMock(PageBook.class); metrics = createNiceMock(SystemMetrics.class); } @Test public final void annotationKeyExtraction() { assert "link".equals(Dom.extractKeyAndContent("@Link")[0]) : "Extraction wrong: "; assert "thing".equals(Dom.extractKeyAndContent("@Thing()")[0]) : "Extraction wrong: "; assert "thing".equals(Dom.extractKeyAndContent("@Thing(asodkoas)")[0]) : "Extraction wrong: "; assert "thing".equals(Dom.extractKeyAndContent("@Thing(asodkoas) ")[0]) : "Extraction wrong: "; assert "thing".equals(Dom.extractKeyAndContent("@Thing(asodkoas) kko")[0]) : "Extraction wrong: "; assert "".equals(Dom.extractKeyAndContent("@Link")[1]) : "Extraction wrong: "; final String val = Dom.extractKeyAndContent("@Thing()")[1]; assert null == (val) : "Extraction wrong: " + val; assert "asodkoas".equals(Dom.extractKeyAndContent("@Thing(asodkoas)")[1]) : "Extraction wrong: "; assert "asodkoas".equals(Dom.extractKeyAndContent("@Thing(asodkoas) ")[1]) : "Extraction wrong: "; assert "asodkoas".equals(Dom.extractKeyAndContent("@Thing(asodkoas) kko")[1]) : "Extraction wrong: "; } private Template template(String text) { return new Template("template.fml", text, new MemoryTemplateSource()); } @Test public final void readShowIfWidgetTrue() { Renderable widget = new FreemarkerTemplateCompiler() .compile(Object.class, template("<html><#if true><p>hello</p></#if></html>")); assert null != widget : " null "; final StringBuilder builder = new StringBuilder(); final Respond mockRespond = RespondersForTesting.newRespond(); widget.render(new Object(), mockRespond); final String value = mockRespond.toString(); System.out.println(value); assert "<html><p>hello</p></html>".equals(value) : "Did not write expected output, instead: " + value; } @DataProvider(name = ANNOTATION_EXPRESSIONS) public Object[][] get() { return new Object[][]{ {"true"}, // {"java.lang.Boolean.TRUE"}, // {"java.lang.Boolean.valueOf('true')"}, // {"true ? true : true"}, @TODO (BD): Disabled until I actually investigate if this is a valid test. {"'x' == 'x'"}, {"\"x\" == \"x\""}, // {"'hello' instanceof java.io.Serializable"}, // {"true; return true"}, // {" 5 >= 2 "}, }; } @Test(dataProvider = ANNOTATION_EXPRESSIONS) public final void readAWidgetWithVariousExpressions(String expression) { final Evaluator evaluator = new MvelEvaluator(); final WidgetRegistry registry = injector.getInstance(WidgetRegistry.class); String templateValue = String.format("<html><#if %s><p>hello</p></#if></html>", expression); System.out.println( templateValue ); Renderable widget = new FreemarkerTemplateCompiler() .compile(Object.class, template(templateValue)); assert null != widget : " null "; final StringBuilder builder = new StringBuilder(); final Respond mockRespond = RespondersForTesting.newRespond(); widget.render(new Object(), mockRespond); final String value = mockRespond.toString(); System.out.println(value); assert "<html><p>hello</p></html>".equals(value) : "Did not write expected output, instead: " + value; } @Test public final void readShowIfWidgetFalse() { final Injector injector = Guice.createInjector(new AbstractModule() { protected void configure() { bind(HttpServletRequest.class).toProvider(mockRequestProviderForContext()); } }); final Evaluator evaluator = new MvelEvaluator(); final WidgetRegistry registry = injector.getInstance(WidgetRegistry.class); Renderable widget = new FreemarkerTemplateCompiler() .compile(Object.class, template("<html><#if false><p>hello</p></#if></html>")); assert null != widget : " null "; final StringBuilder builder = new StringBuilder(); final Respond mockRespond = RespondersForTesting.newRespond(); widget.render(new Object(), mockRespond); final String value = mockRespond.toString(); assert "<html></html>".equals(value) : "Did not write expected output, instead: " + value; } @Test public final void readTextWidgetValues() { final Injector injector = Guice.createInjector(new AbstractModule() { protected void configure() { bind(HttpServletRequest.class).toProvider(mockRequestProviderForContext()); } }); Renderable widget = new FreemarkerTemplateCompiler() .compile(Object.class, template("<html><div class='${clazz}'>hello <a href='/people/${id}'>${name}</a></div></html>")); assert null != widget : " null "; final Respond mockRespond = RespondersForTesting.newRespond(); widget.render(new TestBackingType("Dhanji", "content", 12), mockRespond); final String value = mockRespond.toString(); assert "<html><div class='content'>hello <a href='/people/12'>Dhanji</a></div></html>" .replace("\"", "'") .equals(value) : "Did not write expected output, instead: " + value; } public static class TestBackingType { private String name; private String clazz; private Integer id; public TestBackingType(String name, String clazz, Integer id) { this.name = name; this.clazz = clazz; this.id = id; } public String getName() { return name; } public String getClazz() { return clazz; } public Integer getId() { return id; } } // @Test // public final void readAndRenderRequireWidget() { // final Injector injector = Guice.createInjector(new AbstractModule() { // protected void configure() { // bind(HttpServletRequest.class).toProvider(mockRequestProviderForContext()); // bind(new TypeLiteral<Map<String, Class<? extends Annotation>>>() { // }) // .annotatedWith(Bricks.class) // .toInstance(methods); // } // }); // // // final PageBook pageBook = injector.getInstance(PageBook.class); // // // final WidgetRegistry registry = injector.getInstance(WidgetRegistry.class); // // // Renderable widget = // new FreemarkerTemplateCompiler(Object.class) // .compile("<html> <head>" + // " @Require <script type='text/javascript' src='my.js'> </script>" + // " @Require <script type='text/javascript' src='my.js'> </script>" + // "</head><body>" + // "<div class='${clazz}'>hello <a href='/people/${id}'>${name}</a></div>" + // "</body></html>"); // // assert null != widget : " null "; // // final Respond respond = RespondersForTesting.newRespond(); // // widget.render(new TestBackingType("Dhanji", "content", 12), respond); // // final String value = respond.toString(); // String expected = "<html> <head>" + // " <script type='text/javascript' src='my.js'></script>" + // "</head><body>" + // "<div class='content'>hello <a href='/people/12'>Dhanji</a></div></body></html>"; // expected = expected.replaceAll("'", "\""); // // assertEquals(value, expected); // } @Test public final void readHtmlWidget() { final WidgetRegistry registry = injector.getInstance(WidgetRegistry.class); Renderable widget = new FreemarkerTemplateCompiler() .compile(Object.class, template("<html><div class='${clazz}'>hello</div></html>")); assert null != widget : " null "; final Respond mockRespond = RespondersForTesting.newRespond(); widget.render(new TestBackingType("Dhanji", "content", 12), mockRespond); final String s = mockRespond.toString(); assert "<html><div class=\"content\">hello</div></html>" .replace( "\"", "'") .equals(s) : "Did not write expected output, instead: " + s; } @Test public final void readHtmlWidgetWithChildren() { final WidgetRegistry registry = injector.getInstance(WidgetRegistry.class); Renderable widget = new FreemarkerTemplateCompiler() .compile(Object.class, template("<!doctype html><html><body><div class='${clazz}'>hello <#if false><a href='/hi/${id}'>hideme</a></#if></div></body></html>")); assert null != widget : " null "; final Respond mockRespond = RespondersForTesting.newRespond(); widget.render(new TestBackingType("Dhanji", "content", 12), mockRespond); final String s = mockRespond.toString(); assertEquals(s, "<!doctype html><html><body><div class=\"content\">hello </div></body></html>".replace("\"", "'")); } @EmbedAs(MyEmbeddedPage.MY_FAVE_ANNOTATION) public static class MyEmbeddedPage { protected static final String MY_FAVE_ANNOTATION = "MyFave"; private boolean should = true; public boolean isShould() { return should; } public void setShould(boolean should) { this.should = should; } } // @Test // public final void readEmbedWidgetAndStoreAsPage() { // final Injector injector = Guice.createInjector(new AbstractModule() { // protected void configure() { // bind(HttpServletRequest.class).toProvider(mockRequestProviderForContext()); // bind(new TypeLiteral<Map<String, Class<? extends Annotation>>>() { // }) // .annotatedWith(Bricks.class) // .toInstance(methods); // } // }); // final PageBook book = injector //hacky, where are you super-packages! // .getInstance(PageBook.class); // // book.at("/somewhere", MyEmbeddedPage.class).apply(Chains.terminal()); // // // final WidgetRegistry registry = injector.getInstance(WidgetRegistry.class); // registry.addEmbed("myfave"); // // Renderable widget = // new FreemarkerTemplateCompiler(Object.class) // .compile("<xml><div class='content'>hello @MyFave(should=false)<a href='/hi/${id}'>hideme</a></div></xml>"); // // assert null != widget : " null "; // // //tell pagebook to track this as an embedded widget // book.embedAs(MyEmbeddedPage.class, MyEmbeddedPage.MY_FAVE_ANNOTATION) // .apply(Chains.terminal()); // // final Respond mockRespond = RespondersForTesting.newRespond(); // // widget.render(new TestBackingType("Dhanji", "content", 12), mockRespond); // // final String s = mockRespond.toString(); // assert "<xml><div class=\"content\">hello </div></xml>" // .equals(s) : "Did not write expected output, instead: " + s; // } // @Test // public final void readEmbedWidgetOnly() { // final Injector injector = Guice.createInjector(new AbstractModule() { // protected void configure() { // bind(HttpServletRequest.class).toProvider(mockRequestProviderForContext()); // bind(new TypeLiteral<Map<String, Class<? extends Annotation>>>() { // }) // .annotatedWith(Bricks.class) // .toInstance(methods); // } // }); // final PageBook book = injector //hacky, where are you super-packages! // .getInstance(PageBook.class); // // // final WidgetRegistry registry = injector.getInstance(WidgetRegistry.class); // registry.addEmbed("myfave"); // // Renderable widget = // new FreemarkerTemplateCompiler(Object.class) // .compile("<html><div class='content'>hello @MyFave(should=false)<a href='/hi/${id}'>hideme</a></div></html>"); // // assert null != widget : " null "; // // //tell pagebook to track this as an embedded widget // book.embedAs(MyEmbeddedPage.class, MyEmbeddedPage.MY_FAVE_ANNOTATION) // .apply(Chains.terminal()); // // final Respond mockRespond = RespondersForTesting.newRespond(); // // widget.render(new TestBackingType("Dhanji", "content", 12), mockRespond); // // final String s = mockRespond.toString(); // assert "<html><div class=\"content\">hello </div></html>" // .replace( "\"", "'" ) // .equals(s) : "Did not write expected output, instead: " + s; // } static Provider<HttpServletRequest> mockRequestProviderForContext() { return new Provider<HttpServletRequest>() { public HttpServletRequest get() { final HttpServletRequest request = createMock(HttpServletRequest.class); expect(request.getContextPath()) .andReturn("") .anyTimes(); replay(request); return request; } }; } }