Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,54 @@ 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) {
Assert.hasLength(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()) {
Expand All @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
}
}
Original file line number Diff line number Diff line change
@@ -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";
}
}
}
70 changes: 70 additions & 0 deletions src/test/java/org/thymeleaf/spring/support/LayoutTest.java
Original file line number Diff line number Diff line change
@@ -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"));
}
}
Original file line number Diff line number Diff line change
@@ -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.<Class<?>>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 {}
}
2 changes: 2 additions & 0 deletions src/test/resources/application-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ spring:
database: HSQL
datasource:
initialize: false
thymeleaf:
prefix: classpath:/templates/
6 changes: 6 additions & 0 deletions src/test/resources/templates/fragments/footer.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<footer th:fragment="footer">Footer</footer>
</body>
</html>
6 changes: 6 additions & 0 deletions src/test/resources/templates/fragments/header.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<nav th:fragment="header">Nav</nav>
</body>
</html>
8 changes: 8 additions & 0 deletions src/test/resources/templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<main th:fragment="content">
<p>Using default layout</p>
</main>
</body>
</html>
13 changes: 13 additions & 0 deletions src/test/resources/templates/layouts/default.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>I am default layout</title>
</head>
<body>
<h1>Header in default layout!</h1>
<div th:replace="fragments/header :: header">Header goes here!</div>
<div th:replace="${view} :: content">Content goes here!</div>
<div th:replace="fragments/footer :: footer">Footer goes here!</div>
</body>
</html>
11 changes: 11 additions & 0 deletions src/test/resources/templates/layouts/simple.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>I am simple layout</title>
</head>
<body>
<h1>Header in simple layout!</h1>
<div class="container" th:replace="${view} :: content">Content goes here!</div>
</body>
</html>
Loading