PageRenderTime 50ms CodeModel.GetById 18ms app.highlight 27ms RepoModel.GetById 2ms app.codeStats 0ms

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