/sitebricks/src/test/java/com/google/sitebricks/compiler/HtmlTemplateCompilerTest.java

http://github.com/dhanji/sitebricks · Java · 432 lines · 289 code · 75 blank · 68 comment · 10 complexity · 6da522ed9bdc2e4bed67a04da53efba3 MD5 · raw file

  1. package com.google.sitebricks.compiler;
  2. import com.google.common.collect.ImmutableMap;
  3. import com.google.common.collect.Maps;
  4. import com.google.inject.AbstractModule;
  5. import com.google.inject.Guice;
  6. import com.google.inject.Injector;
  7. import com.google.inject.Provider;
  8. import com.google.inject.TypeLiteral;
  9. import com.google.sitebricks.Bricks;
  10. import com.google.sitebricks.Renderable;
  11. import com.google.sitebricks.Respond;
  12. import com.google.sitebricks.RespondersForTesting;
  13. import com.google.sitebricks.Template;
  14. import com.google.sitebricks.TestRequestCreator;
  15. import com.google.sitebricks.conversion.MvelTypeConverter;
  16. import com.google.sitebricks.conversion.TypeConverter;
  17. import com.google.sitebricks.headless.Request;
  18. import com.google.sitebricks.http.Delete;
  19. import com.google.sitebricks.http.Get;
  20. import com.google.sitebricks.http.Patch;
  21. import com.google.sitebricks.http.Post;
  22. import com.google.sitebricks.http.Put;
  23. import com.google.sitebricks.rendering.EmbedAs;
  24. import com.google.sitebricks.rendering.control.Chains;
  25. import com.google.sitebricks.rendering.control.WidgetRegistry;
  26. import com.google.sitebricks.routing.PageBook;
  27. import com.google.sitebricks.routing.SystemMetrics;
  28. import org.testng.annotations.BeforeMethod;
  29. import org.testng.annotations.DataProvider;
  30. import org.testng.annotations.Test;
  31. import java.lang.annotation.Annotation;
  32. import java.util.Map;
  33. import javax.servlet.http.HttpServletRequest;
  34. import static org.easymock.EasyMock.createNiceMock;
  35. import static org.easymock.EasyMock.expect;
  36. import static org.easymock.EasyMock.replay;
  37. import static org.testng.Assert.assertEquals;
  38. import static org.testng.Assert.fail;
  39. /**
  40. * @author Dhanji R. Prasanna (dhanji@gmail.com)
  41. */
  42. public class HtmlTemplateCompilerTest {
  43. private static final String ANNOTATION_EXPRESSIONS = "Annotation expressions";
  44. private Injector injector;
  45. private WidgetRegistry registry;
  46. private PageBook pageBook;
  47. private SystemMetrics metrics;
  48. private final Map<String, Class<? extends Annotation>> methods = Maps.newHashMap();
  49. private HtmlTemplateCompiler compiler() {
  50. registry = injector.getInstance(WidgetRegistry.class);
  51. registry.addEmbed("myfave");
  52. pageBook = injector.getInstance(PageBook.class);
  53. pageBook.at("/somewhere", MyEmbeddedPage.class).apply(Chains.terminal());
  54. return new HtmlTemplateCompiler(registry, pageBook, metrics);
  55. }
  56. @BeforeMethod
  57. public void pre() {
  58. methods.put("get", Get.class);
  59. methods.put("post", Post.class);
  60. methods.put("put", Put.class);
  61. methods.put("patch", Patch.class);
  62. methods.put("delete", Delete.class);
  63. injector = Guice.createInjector(new AbstractModule() {
  64. protected void configure() {
  65. bind(new TypeLiteral<Request>(){}).toProvider(mockRequestProviderForContext());
  66. bind(new TypeLiteral<Map<String, Class<? extends Annotation>>>() {
  67. })
  68. .annotatedWith(Bricks.class)
  69. .toInstance(methods);
  70. }
  71. });
  72. pageBook = createNiceMock(PageBook.class);
  73. metrics = createNiceMock(SystemMetrics.class);
  74. }
  75. @Test
  76. public final void annotationKeyExtraction() {
  77. assert "link".equals(Dom.extractKeyAndContent("@Link")[0]) : "Extraction wrong: ";
  78. assert "thing".equals(Dom.extractKeyAndContent("@Thing()")[0]) : "Extraction wrong: ";
  79. assert "thing".equals(Dom.extractKeyAndContent("@Thing(asodkoas)")[0]) : "Extraction wrong: ";
  80. assert "thing".equals(Dom.extractKeyAndContent("@Thing(asodkoas) ")[0]) : "Extraction wrong: ";
  81. assert "thing".equals(Dom.extractKeyAndContent("@Thing(asodkoas) kko")[0]) : "Extraction wrong: ";
  82. assert "".equals(Dom.extractKeyAndContent("@Link")[1]) : "Extraction wrong: ";
  83. final String val = Dom.extractKeyAndContent("@Thing()")[1];
  84. assert null == (val) : "Extraction wrong: " + val;
  85. assert "asodkoas".equals(Dom.extractKeyAndContent("@Thing(asodkoas)")[1]) : "Extraction wrong: ";
  86. assert "asodkoas".equals(Dom.extractKeyAndContent("@Thing(asodkoas) ")[1]) : "Extraction wrong: ";
  87. assert "asodkoas".equals(Dom.extractKeyAndContent("@Thing(asodkoas) kko")[1]) : "Extraction wrong: ";
  88. }
  89. @Test
  90. public final void readShowIfWidgetTrue() {
  91. Renderable widget = compiler()
  92. .compile(Object.class, new Template("<html>@ShowIf(true)<p>hello</p></html>"));
  93. // .compile("<!doctype html>\n" +
  94. // "<html><head><meta charset=\"UTF-8\"><title>small test</title></head><body>\n" +
  95. // "@ShowIf(true)<p>hello</p>" +
  96. // "\n</body></html>");
  97. assert null != widget : " null ";
  98. final Respond mockRespond = RespondersForTesting.newRespond();
  99. // final Respond mockRespond = new StringBuilderRespond() {
  100. // @Override
  101. // public void write(String text) {
  102. // builder.append(text);
  103. // }
  104. //
  105. // @Override
  106. // public void write(char text) {
  107. // builder.append(text);
  108. // }
  109. //
  110. // @Override
  111. // public void chew() {
  112. // builder.deleteCharAt(builder.length() - 1);
  113. // }
  114. // };
  115. widget.render(new Object(), mockRespond);
  116. final String value = mockRespond.toString();
  117. System.out.println(value);
  118. assert "<html><p>hello</p></html>".equals(value) : "Did not write expected output, instead: " + value;
  119. // assert "<!doctype html><html><head><meta charset=\"UTF-8\"><title>small test</title></head><body><p>hello</p></body></html>".equals(value) : "Did not write expected output, instead: " + value;
  120. }
  121. @DataProvider(name = ANNOTATION_EXPRESSIONS)
  122. public Object[][] get() {
  123. return new Object[][]{
  124. {"true"},
  125. {"java.lang.Boolean.TRUE"},
  126. {"java.lang.Boolean.valueOf('true')"},
  127. // {"true ? true : true"}, @TODO (BD): Disabled until I actually investigate if this is a valid test.
  128. {"'x' == 'x'"},
  129. {"\"x\" == \"x\""},
  130. {"'hello' instanceof java.io.Serializable"},
  131. {"true; return true"},
  132. {" 5 >= 2 "},
  133. };
  134. }
  135. @Test(dataProvider = ANNOTATION_EXPRESSIONS)
  136. public final void readAWidgetWithVariousExpressions(String expression) {
  137. Renderable widget = compiler()
  138. .compile(Object.class, new Template(String.format("<html>@ShowIf(%s)<p>hello</p></html>", expression)));
  139. assert null != widget : " null ";
  140. final Respond mockRespond = RespondersForTesting.newRespond();
  141. widget.render(new Object(), mockRespond);
  142. final String value = mockRespond.toString();
  143. assert "<html><p>hello</p></html>".equals(value) : "Did not write expected output, instead: " + value;
  144. }
  145. @Test
  146. public final void readShowIfWidgetFalse() {
  147. Renderable widget = compiler()
  148. .compile(Object.class, new Template("<html>@ShowIf(false)<p>hello</p></html>"));
  149. assert null != widget : " null ";
  150. final Respond mockRespond = RespondersForTesting.newRespond();
  151. widget.render(new Object(), mockRespond);
  152. final String value = mockRespond.toString();
  153. assert "<html></html>".equals(value) : "Did not write expected output, instead: " + value;
  154. }
  155. @Test
  156. public final void readTextWidgetValues() {
  157. // make a basic type converter without creating
  158. TypeConverter converter = new MvelTypeConverter();
  159. Parsing.setTypeConverter(converter);
  160. Renderable widget = compiler()
  161. .compile(TestBackingType.class, new Template("<html><div class='${clazz}'>hello <a href='/people/${id}'>${name}</a></div></html>"));
  162. assert null != widget : " null ";
  163. final Respond mockRespond = RespondersForTesting.newRespond();
  164. widget.render(new TestBackingType("Dhanji", "content", 12), mockRespond);
  165. final String value = mockRespond.toString();
  166. assert "<html><div class='content'>hello <a href='/people/12'>Dhanji</a></div></html>"
  167. .replaceAll("'", "\"")
  168. .equals(value) : "Did not write expected output, instead: " + value;
  169. }
  170. public static class TestBackingType {
  171. private String name;
  172. private String clazz;
  173. private Integer id;
  174. public TestBackingType(String name, String clazz, Integer id) {
  175. this.name = name;
  176. this.clazz = clazz;
  177. this.id = id;
  178. }
  179. public String getName() {
  180. return name;
  181. }
  182. public String getClazz() {
  183. return clazz;
  184. }
  185. public Integer getId() {
  186. return id;
  187. }
  188. }
  189. @Test
  190. public final void readAndRenderRequireWidget() {
  191. // make a basic type converter without creating
  192. TypeConverter converter = new MvelTypeConverter();
  193. Parsing.setTypeConverter(converter);
  194. Renderable widget = compiler()
  195. //new HtmlTemplateCompiler(registry, pageBook, metrics)
  196. .compile(TestBackingType.class, new Template("<html> <head>" +
  197. " @Require <script type='text/javascript' src='my.js'> </script>" +
  198. " @Require <script type='text/javascript' src='my.js'> </script>" +
  199. "</head><body>" +
  200. "<div class='${clazz}'>hello <a href='/people/${id}'>${name}</a></div>" +
  201. "</body></html>"));
  202. assert null != widget : " null ";
  203. final Respond respond = RespondersForTesting.newRespond();
  204. widget.render(new TestBackingType("Dhanji", "content", 12), respond);
  205. final String value = respond.toString();
  206. String expected = "<html> <head>" +
  207. " <script type='text/javascript' src='my.js'> </script>" +
  208. "</head><body>" +
  209. "<div class='content'>hello <a href='/people/12'>Dhanji</a></div></body></html>";
  210. expected = expected.replaceAll("'", "\"");
  211. assertEquals(value, expected);
  212. }
  213. @Test
  214. public final void readHtmlWidgetWithError() {
  215. try{
  216. Renderable widget = compiler()
  217. .compile(TestBackingType.class, new Template("<html>\n<div class='${clazz}'>hello</div>\n</html>${qwe}"));
  218. fail();
  219. } catch (Exception ex){
  220. assertEquals(ex.getClass(), TemplateCompileException.class);
  221. TemplateCompileException te = (TemplateCompileException) ex;
  222. assertEquals(te.getErrors().size(), 1);
  223. CompileError error = te.getErrors().get(0);
  224. assertEquals(error.getLine(), 2);
  225. }
  226. }
  227. @Test
  228. public final void readHtmlWidgetWithErrorAndWidget() {
  229. try{
  230. Renderable widget = compiler()
  231. .compile(TestBackingType.class, new Template("<html>\n<div class='${clazz}'>hello</div>\n\n</html>@ShowIf(true)\n${qwe}"));
  232. fail();
  233. } catch (Exception ex){
  234. assertEquals(ex.getClass(), TemplateCompileException.class);
  235. TemplateCompileException te = (TemplateCompileException) ex;
  236. assertEquals(te.getErrors().size(), 1);
  237. CompileError error = te.getErrors().get(0);
  238. assertEquals(error.getLine(), 4);
  239. }
  240. }
  241. @Test
  242. public final void readHtmlWidget() {
  243. Renderable widget = compiler()
  244. .compile(TestBackingType.class, new Template("<html><div class='${clazz}'>hello</div></html>"));
  245. assert null != widget : " null ";
  246. final Respond mockRespond = RespondersForTesting.newRespond();
  247. widget.render(new TestBackingType("Dhanji", "content", 12), mockRespond);
  248. final String s = mockRespond.toString();
  249. assert "<html><div class=\"content\">hello</div></html>"
  250. .equals(s) : "Did not write expected output, instead: " + s;
  251. }
  252. @Test
  253. public final void readHtmlWidgetWithChildren() {
  254. Renderable widget = compiler()
  255. .compile(TestBackingType.class, new Template("<!doctype html><html><body><div class='${clazz}'>hello @ShowIf(false)<a href='/hi/${id}'>hideme</a></div></body></html>"));
  256. assert null != widget : " null ";
  257. final Respond mockRespond = RespondersForTesting.newRespond();
  258. widget.render(new TestBackingType("Dhanji", "content", 12), mockRespond);
  259. final String s = mockRespond.toString();
  260. assertEquals(s, "<!doctype html><html><body><div class=\"content\">hello </div></body></html>");
  261. }
  262. @EmbedAs(MyEmbeddedPage.MY_FAVE_ANNOTATION)
  263. public static class MyEmbeddedPage {
  264. protected static final String MY_FAVE_ANNOTATION = "MyFave";
  265. private boolean should = true;
  266. public boolean isShould() {
  267. return should;
  268. }
  269. public void setShould(boolean should) {
  270. this.should = should;
  271. }
  272. }
  273. @Test
  274. public final void readEmbedWidgetAndStoreAsPage() {
  275. Renderable widget = compiler()
  276. .compile(TestBackingType.class, new Template("<xml><div class='content'>hello @MyFave(should=false)<a href='/hi/${id}'>hideme</a></div></xml>"));
  277. assert null != widget : " null ";
  278. //tell pagebook to track this as an embedded widget
  279. pageBook.embedAs(MyEmbeddedPage.class, MyEmbeddedPage.MY_FAVE_ANNOTATION)
  280. .apply(Chains.terminal());
  281. final Respond mockRespond = RespondersForTesting.newRespond();
  282. widget.render(new TestBackingType("Dhanji", "content", 12), mockRespond);
  283. final String s = mockRespond.toString();
  284. assert "<xml><div class=\"content\">hello </div></xml>"
  285. .equals(s) : "Did not write expected output, instead: " + s;
  286. }
  287. @Test
  288. public final void readEmbedWidgetOnly() {
  289. Renderable widget = compiler()
  290. .compile(TestBackingType.class, new Template("<html><div class='content'>hello @MyFave(should=false)<a href='/hi/${id}'>hideme</a></div></html>"));
  291. assert null != widget : " null ";
  292. //tell pagebook to track this as an embedded widget
  293. pageBook.embedAs(MyEmbeddedPage.class, MyEmbeddedPage.MY_FAVE_ANNOTATION)
  294. .apply(Chains.terminal());
  295. final Respond mockRespond = RespondersForTesting.newRespond();
  296. widget.render(new TestBackingType("Dhanji", "content", 12), mockRespond);
  297. final String s = mockRespond.toString();
  298. assert "<html><div class=\"content\">hello </div></html>"
  299. .equals(s) : "Did not write expected output, instead: " + s;
  300. }
  301. //TODO Fix this test!
  302. // @Test
  303. // public final void readEmbedWidgetWithArgs() throws ExpressionCompileException {
  304. //
  305. // final Evaluator evaluator = new MvelEvaluator();
  306. // final Injector injector = Guice.createInjector(new AbstractModule() {
  307. // protected void configure() {
  308. // bind(HttpServletRequest.class).toProvider(mockRequestProviderForContext());
  309. // }
  310. // });
  311. // final PageBook book = injector.getInstance(PageBook.class); //hacky, where are you super-packages!
  312. //
  313. // final WidgetRegistry registry = injector.getInstance(WidgetRegistry.class);
  314. //
  315. // final MvelEvaluatorCompiler compiler = new MvelEvaluatorCompiler(TestBackingType.class);
  316. // Renderable widget =
  317. // new HtmlTemplateCompiler(Object.class, compiler, registry, book, metrics)
  318. // .compile("<xml><div class='content'>hello @MyFave(should=true)<a href='/hi/${id}'> @With(\"me\")<p>showme</p></a></div></xml>");
  319. //
  320. // assert null != widget : " null ";
  321. //
  322. //
  323. // HtmlWidget bodyWrapper = new XmlWidget(Chains.proceeding().addWidget(new IncludeWidget(new TerminalWidgetChain(), "'me'", evaluator)),
  324. // "body", compiler, Collections.<String, String>emptyMap());
  325. //
  326. // bodyWrapper.setRequestProvider(mockRequestProviderForContext());
  327. //
  328. // //should include the @With("me") annotated widget from the template above (discarding the <p> tag).
  329. // book.embedAs(MyEmbeddedPage.class).apply(bodyWrapper);
  330. //
  331. // final Respond mockRespond = new StringBuilderRespond();
  332. //
  333. // widget.render(new TestBackingType("Dhanji", "content", 12), mockRespond);
  334. //
  335. // final String s = mockRespond.toString();
  336. // assert "<xml><div class=\"content\">hello showme</div></xml>"
  337. // .equals(s) : "Did not write expected output, instead: " + s;
  338. // }
  339. public static Provider<Request> mockRequestProviderForContext() {
  340. return new Provider<Request>() {
  341. public Request get() {
  342. final HttpServletRequest request = createNiceMock(HttpServletRequest.class);
  343. expect(request.getContextPath())
  344. .andReturn("")
  345. .anyTimes();
  346. expect(request.getMethod())
  347. .andReturn("POST")
  348. .anyTimes();
  349. expect(request.getParameterMap())
  350. .andReturn(ImmutableMap.of())
  351. .anyTimes();
  352. replay(request);
  353. return TestRequestCreator.from(request, null);
  354. }
  355. };
  356. }
  357. }