From 46d8b8bfc10c53b03782c69c99ae5a0da3469471 Mon Sep 17 00:00:00 2001 From: Matvey Kazakov Date: Tue, 14 Mar 2017 15:22:22 +0300 Subject: [PATCH] Added tests for ThymeleafInterceptor and Layout test (copied from the same place, but modified upon our needs) --- .../support/ThymeleafLayoutInterceptor.java | 40 +++++- .../neerc/volunteers/config/MvcConfig.java | 2 +- .../spring/support/CustomLayoutTestApp.java | 54 ++++++++ .../thymeleaf/spring/support/LayoutTest.java | 70 ++++++++++ .../ThymeleafLayoutInterceptorTest.java | 127 ++++++++++++++++++ src/test/resources/application-test.yml | 2 + .../resources/templates/fragments/footer.html | 6 + .../resources/templates/fragments/header.html | 6 + src/test/resources/templates/index.html | 8 ++ .../resources/templates/layouts/default.html | 13 ++ .../resources/templates/layouts/simple.html | 11 ++ src/test/resources/templates/noLayout.html | 11 ++ src/test/resources/templates/simple.html | 8 ++ .../resources/templates/viewController.html | 8 ++ 14 files changed, 363 insertions(+), 3 deletions(-) create mode 100644 src/test/java/org/thymeleaf/spring/support/CustomLayoutTestApp.java create mode 100644 src/test/java/org/thymeleaf/spring/support/LayoutTest.java create mode 100644 src/test/java/org/thymeleaf/spring/support/ThymeleafLayoutInterceptorTest.java create mode 100644 src/test/resources/templates/fragments/footer.html create mode 100644 src/test/resources/templates/fragments/header.html create mode 100644 src/test/resources/templates/index.html create mode 100644 src/test/resources/templates/layouts/default.html create mode 100644 src/test/resources/templates/layouts/simple.html create mode 100644 src/test/resources/templates/noLayout.html create mode 100644 src/test/resources/templates/simple.html create mode 100644 src/test/resources/templates/viewController.html diff --git a/src/main/java/org/thymeleaf/spring/support/ThymeleafLayoutInterceptor.java b/src/main/java/org/thymeleaf/spring/support/ThymeleafLayoutInterceptor.java index 242a9a8..996db64 100644 --- a/src/main/java/org/thymeleaf/spring/support/ThymeleafLayoutInterceptor.java +++ b/src/main/java/org/thymeleaf/spring/support/ThymeleafLayoutInterceptor.java @@ -15,6 +15,7 @@ public class ThymeleafLayoutInterceptor extends HandlerInterceptorAdapter { private static final String DEFAULT_VIEW_ATTRIBUTE_NAME = "view"; private String defaultLayout = DEFAULT_LAYOUT; + private String layoutPrefix = LAYOUT_PREFIX; private String viewAttributeName = DEFAULT_VIEW_ATTRIBUTE_NAME; public void setDefaultLayout(final String defaultLayout) { @@ -22,11 +23,46 @@ public void setDefaultLayout(final String defaultLayout) { this.defaultLayout = defaultLayout; } + public void setLayoutPrefix(final String layoutPrefix) { + Assert.notNull(layoutPrefix); + this.layoutPrefix = layoutPrefix; + } + public void setViewAttributeName(final String viewAttributeName) { - Assert.hasLength(defaultLayout); + Assert.hasLength(viewAttributeName); this.viewAttributeName = viewAttributeName; } + /** + * Builder method to {@link #setViewAttributeName(String)} + * @param viewAttributeName view attribute name + * @return this + */ + public ThymeleafLayoutInterceptor viewAttributeName(final String viewAttributeName) { + setViewAttributeName(viewAttributeName); + return this; + } + + /** + * Builder method to {@link #setLayoutPrefix(String)} + * @param layoutPrefix layouts prefix name + * @return this + */ + public ThymeleafLayoutInterceptor layoutPrefix(final String layoutPrefix) { + setLayoutPrefix(layoutPrefix); + return this; + } + + /** + * Builder method to {@link #setDefaultLayout(String)} + * @param defaultLayout default layout name + * @return this + */ + public ThymeleafLayoutInterceptor defaultLayout(final String defaultLayout) { + setDefaultLayout(defaultLayout); + return this; + } + @Override public void postHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final ModelAndView modelAndView) throws Exception { if (modelAndView == null || !modelAndView.hasView()) { @@ -41,7 +77,7 @@ public void postHandle(final HttpServletRequest request, final HttpServletRespon if (Layout.NONE.equals(layoutName)) { return; } - modelAndView.setViewName(LAYOUT_PREFIX + layoutName); + modelAndView.setViewName(layoutPrefix + layoutName); modelAndView.addObject(this.viewAttributeName, originalViewName); } diff --git a/src/main/java/ru/ifmo/neerc/volunteers/config/MvcConfig.java b/src/main/java/ru/ifmo/neerc/volunteers/config/MvcConfig.java index 152cead..d579f1c 100644 --- a/src/main/java/ru/ifmo/neerc/volunteers/config/MvcConfig.java +++ b/src/main/java/ru/ifmo/neerc/volunteers/config/MvcConfig.java @@ -30,6 +30,6 @@ public void addViewControllers(final ViewControllerRegistry registry) { @Override public void addInterceptors(final InterceptorRegistry registry) { - registry.addInterceptor(new ThymeleafLayoutInterceptor()); + registry.addInterceptor(new ThymeleafLayoutInterceptor().layoutPrefix("layouts/").viewAttributeName("view").defaultLayout("public")); } } diff --git a/src/test/java/org/thymeleaf/spring/support/CustomLayoutTestApp.java b/src/test/java/org/thymeleaf/spring/support/CustomLayoutTestApp.java new file mode 100644 index 0000000..5efdc3a --- /dev/null +++ b/src/test/java/org/thymeleaf/spring/support/CustomLayoutTestApp.java @@ -0,0 +1,54 @@ +package org.thymeleaf.spring.support; + + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; +import org.thymeleaf.spring.support.Layout; +import org.thymeleaf.spring.support.ThymeleafLayoutInterceptor; + +/** + * Copied from here: https://github.com/kolorobot/thymeleaf-custom-layout/blob/master/src/test/java/it/CustomLayoutTestApp.java + * Created by Matvey on 3/14/17. + */ +@SpringBootApplication +public class CustomLayoutTestApp { + @Configuration + public static class ThymeleafLayoutInterceptorConfig extends WebMvcConfigurationSupport { + @Override + protected void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new ThymeleafLayoutInterceptor().layoutPrefix("layouts/").defaultLayout("default")); + } + + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/vc").setViewName("viewController"); + } + } + + @Controller + @Layout(value = "default") + public static class LayoutController { + + @RequestMapping({"/", "index"}) + public String defaultLayout() { + return "index"; + } + + @RequestMapping("simple") + @Layout("simple") + public String simpleLayout() { + return "simple"; + } + + @RequestMapping("no-layout") + @Layout(Layout.NONE) + public String noLayout() { + return "noLayout"; + } + } +} \ No newline at end of file diff --git a/src/test/java/org/thymeleaf/spring/support/LayoutTest.java b/src/test/java/org/thymeleaf/spring/support/LayoutTest.java new file mode 100644 index 0000000..fd54737 --- /dev/null +++ b/src/test/java/org/thymeleaf/spring/support/LayoutTest.java @@ -0,0 +1,70 @@ +package org.thymeleaf.spring.support; + +/** + * Copied from here https://github.com/kolorobot/thymeleaf-custom-layout/blob/master/src/test/java/it/LayoutTest.java + * Created by Matvey on 3/14/17. + */ +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.xpath; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = CustomLayoutTestApp.class) +@ActiveProfiles("test") +public class LayoutTest { + + @Autowired + private WebApplicationContext wac; + + private MockMvc mockMvc; + + @Test + public void usesDefaultLayout() throws Exception { + mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); + mockMvc.perform(MockMvcRequestBuilders.get("/index")) + .andDo(print()) + .andExpect(xpath("/html/head/title").string("I am default layout")) + .andExpect(xpath("/html/body/h1").string("Header in default layout!")) + .andExpect(xpath("/html/body/nav").string("Nav")) + .andExpect(xpath("/html/body/main/p").string("Using default layout")) + .andExpect(xpath("/html/body/footer").string("Footer")); + } + + @Test + public void usesSimpleLayout() throws Exception { + mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); + mockMvc.perform(MockMvcRequestBuilders.get("/simple")) + .andDo(print()) + .andExpect(xpath("/html/head/title").string("I am simple layout")) + .andExpect(xpath("/html/body/h1").string("Header in simple layout!")) + .andExpect(xpath("/html/body/nav").doesNotExist()) + .andExpect(xpath("/html/body/main/p").string("Using simple layout")) + .andExpect(xpath("/html/body/footer").doesNotExist()); + } + + @Test + public void usesNoLayout() throws Exception { + mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); + mockMvc.perform(MockMvcRequestBuilders.get("/no-layout")) + .andDo(print()) + .andExpect(xpath("/html/head/title").string("No layout!")); + } + + @Test + public void parameterizableViewControllerHasDefaultLayout() throws Exception { + mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); + mockMvc.perform(MockMvcRequestBuilders.get("/vc")) + .andDo(print()) + .andExpect(xpath("/html/head/title").string("I am default layout")); + } +} diff --git a/src/test/java/org/thymeleaf/spring/support/ThymeleafLayoutInterceptorTest.java b/src/test/java/org/thymeleaf/spring/support/ThymeleafLayoutInterceptorTest.java new file mode 100644 index 0000000..3cf7656 --- /dev/null +++ b/src/test/java/org/thymeleaf/spring/support/ThymeleafLayoutInterceptorTest.java @@ -0,0 +1,127 @@ +package org.thymeleaf.spring.support; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.ModelAndView; + +import static org.mockito.Mockito.*; + +/** + * Tests Interceptor. + * Copied from here: https://github.com/kolorobot/thymeleaf-custom-layout/blob/master/src/test/java/org/thymeleaf/spring/support/ThymeleafLayoutInterceptorTest.java + * Created by matvey on 3/14/17. + */ +@RunWith(MockitoJUnitRunner.class) +public class ThymeleafLayoutInterceptorTest { + private ThymeleafLayoutInterceptor interceptor; + @Mock + private ModelAndView modelAndView; + @Mock + private HandlerMethod handlerMethod; + + @Before + public void before() { + interceptor = new ThymeleafLayoutInterceptor().defaultLayout("someLayout").viewAttributeName("attributeName").layoutPrefix(""); + } + + @Test + public void returnsWhenModelAndViewIsNull() throws Exception { + interceptor.postHandle(null, null, null, null); + } + + @Test + public void returnsWhenNoViewIsPresent() throws Exception { + // arrange + when(modelAndView.hasView()).thenReturn(false); + // act + interceptor.postHandle(null, null, null, modelAndView); + // assert + verify(modelAndView).hasView(); + verifyNoMoreInteractions(modelAndView); + } + + @Test + public void returnsWhenRedirect() throws Exception { + // arrange + when(modelAndView.hasView()).thenReturn(true); + when(modelAndView.getViewName()).thenReturn("redirect:/"); + // act + interceptor.postHandle(null, null, null, modelAndView); + // assert + verify(modelAndView).hasView(); + verify(modelAndView).getViewName(); + verifyNoMoreInteractions(modelAndView); + } + + @Test + public void returnsWhenForward() throws Exception { + // arrange + when(modelAndView.hasView()).thenReturn(true); + when(modelAndView.getViewName()).thenReturn("forward:/"); + // act + interceptor.postHandle(null, null, null, modelAndView); + // assert + verify(modelAndView).hasView(); + verify(modelAndView).getViewName(); + verifyNoMoreInteractions(modelAndView); + } + + @Test + public void layoutBecomesView() throws Exception { + // arrange + when(modelAndView.hasView()).thenReturn(true); + when(modelAndView.getViewName()).thenReturn("someView"); + + when(handlerMethod.getMethodAnnotation(Layout.class)).thenReturn(null); + Mockito.>when(handlerMethod.getBeanType()).thenReturn(Object.class); + // act + interceptor.postHandle(null, null, handlerMethod, modelAndView); + // assert + verify(modelAndView).setViewName("someLayout"); + verify(modelAndView).addObject("attributeName", "someView"); + } + + @Test + public void layoutChangesWhenAnnotationIsPresentOnAHandlerMethod() throws Exception { + // arrange + when(modelAndView.hasView()).thenReturn(true); + when(modelAndView.getViewName()).thenReturn("someView"); + + Layout layout = layoutAnnotation(); + when(handlerMethod.getMethodAnnotation(Layout.class)).thenReturn(layout); + + // act + interceptor.postHandle(null, null, handlerMethod, modelAndView); + // assert + verify(modelAndView).setViewName("newLayout"); + verify(modelAndView).addObject("attributeName", "someView"); + } + + @Test + public void layoutChangesWhenAnnotationIsPresentOnAHandlerClass() throws Exception { + // arrange + when(modelAndView.hasView()).thenReturn(true); + when(modelAndView.getViewName()).thenReturn("someView"); + + Layout layout = layoutAnnotation(); + when(handlerMethod.getMethodAnnotation(Layout.class)).thenReturn(null); + when(handlerMethod.getMethodAnnotation(Layout.class)).thenReturn(layout); + // act + interceptor.postHandle(null, null, handlerMethod, modelAndView); + // assert + verify(modelAndView).setViewName("newLayout"); + verify(modelAndView).addObject("attributeName", "someView"); + } + + private static Layout layoutAnnotation() { + return AnnotatedHandler.class.getAnnotation(Layout.class); + } + + @Layout(value = "newLayout") + private static final class AnnotatedHandler {} +} \ No newline at end of file diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml index f006f5a..326b941 100644 --- a/src/test/resources/application-test.yml +++ b/src/test/resources/application-test.yml @@ -3,3 +3,5 @@ spring: database: HSQL datasource: initialize: false + thymeleaf: + prefix: classpath:/templates/ diff --git a/src/test/resources/templates/fragments/footer.html b/src/test/resources/templates/fragments/footer.html new file mode 100644 index 0000000..662f564 --- /dev/null +++ b/src/test/resources/templates/fragments/footer.html @@ -0,0 +1,6 @@ + + + +
Footer
+ + \ No newline at end of file diff --git a/src/test/resources/templates/fragments/header.html b/src/test/resources/templates/fragments/header.html new file mode 100644 index 0000000..1a4b467 --- /dev/null +++ b/src/test/resources/templates/fragments/header.html @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/test/resources/templates/index.html b/src/test/resources/templates/index.html new file mode 100644 index 0000000..2d4b771 --- /dev/null +++ b/src/test/resources/templates/index.html @@ -0,0 +1,8 @@ + + + +
+

Using default layout

+
+ + \ No newline at end of file diff --git a/src/test/resources/templates/layouts/default.html b/src/test/resources/templates/layouts/default.html new file mode 100644 index 0000000..7e2980a --- /dev/null +++ b/src/test/resources/templates/layouts/default.html @@ -0,0 +1,13 @@ + + + + + I am default layout + + +

Header in default layout!

+
Header goes here!
+
Content goes here!
+
Footer goes here!
+ + \ No newline at end of file diff --git a/src/test/resources/templates/layouts/simple.html b/src/test/resources/templates/layouts/simple.html new file mode 100644 index 0000000..7e4c103 --- /dev/null +++ b/src/test/resources/templates/layouts/simple.html @@ -0,0 +1,11 @@ + + + + + I am simple layout + + +

Header in simple layout!

+
Content goes here!
+ + \ No newline at end of file diff --git a/src/test/resources/templates/noLayout.html b/src/test/resources/templates/noLayout.html new file mode 100644 index 0000000..9e6d6f8 --- /dev/null +++ b/src/test/resources/templates/noLayout.html @@ -0,0 +1,11 @@ + + + + No layout! + + +
+

Using no layout

+
+ + \ No newline at end of file diff --git a/src/test/resources/templates/simple.html b/src/test/resources/templates/simple.html new file mode 100644 index 0000000..06c3e75 --- /dev/null +++ b/src/test/resources/templates/simple.html @@ -0,0 +1,8 @@ + + + +
+

Using simple layout

+
+ + \ No newline at end of file diff --git a/src/test/resources/templates/viewController.html b/src/test/resources/templates/viewController.html new file mode 100644 index 0000000..d92a7c4 --- /dev/null +++ b/src/test/resources/templates/viewController.html @@ -0,0 +1,8 @@ + + + +
+

Using view controller (ParameterizableViewController) with default layout

+
+ + \ No newline at end of file