mirror of https://github.com/halo-dev/halo
feat: support custom rendering templates configure through themes (#2638)
#### What type of PR is this? /kind feature /milestone 2.0 /area core #### What this PR does / why we need it: 文章/自定义页面/分类支持通过主题配置多套渲染模板 首先需要在 theme.yaml 中声明模板,以文章为例: ```yaml apiVersion: theme.halo.run/v1alpha1 kind: Theme metadata: name: fake-theme-name spec: # ... customTemplates: # 支持通过以下形式配置 post, category, page post: - name: 新闻 description: 新闻类型的文章模板 screenshot: foo.png # file 路径相对于主题目录的 templates 目录,.html 后缀可以带也可以省略 # 默认 thymeleaf 查找 html 文件,除非修改了 thymeleaf suffix 配置 file: post_news.html ``` 当文章页选择模板时便可得到下拉列表以配置使用哪个模板 see https://github.com/halo-dev/halo/issues/2322#issuecomment-1215135195 选择了模板之后将模板名存储到 post 的 spec.template 中,渲染时便会首先使用 spec.template,如果该模板不存在则渲染默认模板 #### Which issue(s) this PR fixes: Fixes #2569 #### Special notes for your reviewer: how to test it? 1. 创建一篇文章并发布 2. 安装一个主题并查看文章详情,默认渲染主题提供的 post.html 页面 3. 修改此文章的 spec.template 字段为一个新的 html 例如 spec.template=post_docs 4. 查看该文章的详情页会渲染为 post_docs.html 5. 分类、自定义页面亦如是 /cc @halo-dev/sig-halo #### Does this PR introduce a user-facing change? ```release-note 文章/自定义页面/分类支持通过主题配置多套渲染模板 ```pull/2663/head
parent
c0986a4b4c
commit
1078145b18
|
@ -1,6 +1,7 @@
|
|||
package run.halo.app.core.extension;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
@ -10,6 +11,8 @@ import run.halo.app.extension.AbstractExtension;
|
|||
import run.halo.app.extension.GVK;
|
||||
|
||||
/**
|
||||
* <p>Theme extension.</p>
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.0.0
|
||||
*/
|
||||
|
@ -54,6 +57,9 @@ public class Theme extends AbstractExtension {
|
|||
|
||||
private String configMapName;
|
||||
|
||||
@Schema
|
||||
private CustomTemplates customTemplates;
|
||||
|
||||
@NonNull
|
||||
public String getVersion() {
|
||||
if (StringUtils.isBlank(this.version)) {
|
||||
|
@ -85,4 +91,33 @@ public class Theme extends AbstractExtension {
|
|||
|
||||
private String website;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class CustomTemplates {
|
||||
private List<TemplateDescriptor> post;
|
||||
private List<TemplateDescriptor> category;
|
||||
private List<TemplateDescriptor> page;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Type used to describe custom template page.
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Data
|
||||
public static class TemplateDescriptor {
|
||||
|
||||
@Schema(required = true, minLength = 1)
|
||||
private String name;
|
||||
|
||||
private String description;
|
||||
|
||||
private String screenshot;
|
||||
|
||||
@Schema(required = true, minLength = 1)
|
||||
private String file;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package run.halo.app.theme.router;
|
||||
|
||||
import java.util.Locale;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.theme.HaloViewResolver;
|
||||
|
||||
/**
|
||||
* The {@link ViewNameResolver} is used to resolve view name.
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class ViewNameResolver {
|
||||
private final HaloViewResolver haloViewResolver;
|
||||
private final ThymeleafProperties thymeleafProperties;
|
||||
|
||||
/**
|
||||
* Resolves view name.
|
||||
* If the {@param #name} cannot be resolved to the view, the {@param #defaultName} is returned.
|
||||
*/
|
||||
public Mono<String> resolveViewNameOrDefault(ServerRequest request, String name,
|
||||
String defaultName) {
|
||||
if (StringUtils.isBlank(name)) {
|
||||
return Mono.just(defaultName);
|
||||
}
|
||||
final String nameToUse = processName(name);
|
||||
Locale locale = LocaleContextHolder.getLocale(request.exchange().getLocaleContext());
|
||||
return haloViewResolver.resolveViewName(nameToUse, locale)
|
||||
.map(view -> nameToUse)
|
||||
.switchIfEmpty(Mono.just(defaultName));
|
||||
}
|
||||
|
||||
String processName(String name) {
|
||||
String nameToLookup = name;
|
||||
if (StringUtils.endsWith(name, thymeleafProperties.getSuffix())) {
|
||||
nameToLookup = StringUtils.substringBeforeLast(name, thymeleafProperties.getSuffix());
|
||||
}
|
||||
return nameToLookup;
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ package run.halo.app.theme.router.strategy;
|
|||
import static run.halo.app.theme.router.PageUrlUtils.pageNum;
|
||||
import static run.halo.app.theme.router.PageUrlUtils.totalPage;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.server.HandlerFunction;
|
||||
|
@ -20,6 +21,7 @@ import run.halo.app.theme.finders.vo.CategoryVo;
|
|||
import run.halo.app.theme.finders.vo.PostVo;
|
||||
import run.halo.app.theme.router.PageUrlUtils;
|
||||
import run.halo.app.theme.router.UrlContextListResult;
|
||||
import run.halo.app.theme.router.ViewNameResolver;
|
||||
|
||||
/**
|
||||
* The {@link CategoryRouteStrategy} for generate {@link HandlerFunction} specific to the template
|
||||
|
@ -35,10 +37,13 @@ public class CategoryRouteStrategy implements DetailsPageRouteHandlerStrategy {
|
|||
|
||||
private final CategoryFinder categoryFinder;
|
||||
|
||||
private final ViewNameResolver viewNameResolver;
|
||||
|
||||
public CategoryRouteStrategy(PostFinder postFinder,
|
||||
CategoryFinder categoryFinder) {
|
||||
CategoryFinder categoryFinder, ViewNameResolver viewNameResolver) {
|
||||
this.postFinder = postFinder;
|
||||
this.categoryFinder = categoryFinder;
|
||||
this.viewNameResolver = viewNameResolver;
|
||||
}
|
||||
|
||||
private Mono<UrlContextListResult<PostVo>> postListByCategoryName(String name,
|
||||
|
@ -54,19 +59,27 @@ public class CategoryRouteStrategy implements DetailsPageRouteHandlerStrategy {
|
|||
}
|
||||
|
||||
private Mono<CategoryVo> categoryByName(String name) {
|
||||
return Mono.defer(() -> Mono.just(categoryFinder.getByName(name)))
|
||||
.publishOn(Schedulers.boundedElastic());
|
||||
return Mono.fromCallable(() -> categoryFinder.getByName(name))
|
||||
.subscribeOn(Schedulers.boundedElastic());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerFunction<ServerResponse> getHandler(SystemSetting.ThemeRouteRules routeRules,
|
||||
String name) {
|
||||
return request -> ServerResponse.ok()
|
||||
.render(DefaultTemplateEnum.CATEGORY.getValue(),
|
||||
Map.of("name", name,
|
||||
"posts", postListByCategoryName(name, request),
|
||||
"category", categoryByName(name),
|
||||
ModelConst.TEMPLATE_ID, DefaultTemplateEnum.CATEGORY.getValue()));
|
||||
return request -> {
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
model.put("name", name);
|
||||
model.put("posts", postListByCategoryName(name, request));
|
||||
|
||||
model.put(ModelConst.TEMPLATE_ID, DefaultTemplateEnum.CATEGORY.getValue());
|
||||
return categoryByName(name).flatMap(categoryVo -> {
|
||||
model.put("category", categoryVo);
|
||||
String template = categoryVo.getSpec().getTemplate();
|
||||
return viewNameResolver.resolveViewNameOrDefault(request, template,
|
||||
DefaultTemplateEnum.CATEGORY.getValue())
|
||||
.flatMap(viewName -> ServerResponse.ok().render(viewName, model));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,6 +17,7 @@ import run.halo.app.infra.SystemSetting;
|
|||
import run.halo.app.theme.DefaultTemplateEnum;
|
||||
import run.halo.app.theme.finders.PostFinder;
|
||||
import run.halo.app.theme.finders.vo.PostVo;
|
||||
import run.halo.app.theme.router.ViewNameResolver;
|
||||
|
||||
/**
|
||||
* The {@link PostRouteStrategy} for generate {@link HandlerFunction} specific to the template
|
||||
|
@ -30,9 +31,11 @@ public class PostRouteStrategy implements DetailsPageRouteHandlerStrategy {
|
|||
static final String NAME_PARAM = "name";
|
||||
private final GroupVersionKind groupVersionKind = GroupVersionKind.fromExtension(Post.class);
|
||||
private final PostFinder postFinder;
|
||||
private final ViewNameResolver viewNameResolver;
|
||||
|
||||
public PostRouteStrategy(PostFinder postFinder) {
|
||||
public PostRouteStrategy(PostFinder postFinder, ViewNameResolver viewNameResolver) {
|
||||
this.postFinder = postFinder;
|
||||
this.viewNameResolver = viewNameResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -49,15 +52,19 @@ public class PostRouteStrategy implements DetailsPageRouteHandlerStrategy {
|
|||
if (pathMatchInfo != null) {
|
||||
model.putAll(pathMatchInfo.getUriVariables());
|
||||
}
|
||||
model.put("post", postByName(name));
|
||||
// used by HaloTrackerProcessor
|
||||
model.put("groupVersionKind", groupVersionKind);
|
||||
model.put("plural", gvk.plural());
|
||||
// used by TemplateGlobalHeadProcessor and PostTemplateHeadProcessor
|
||||
model.put(ModelConst.TEMPLATE_ID, DefaultTemplateEnum.POST.getValue());
|
||||
|
||||
return ServerResponse.ok()
|
||||
.render(DefaultTemplateEnum.POST.getValue(), model);
|
||||
return postByName(name)
|
||||
.flatMap(postVo -> {
|
||||
model.put("post", postVo);
|
||||
String template = postVo.getSpec().getTemplate();
|
||||
return viewNameResolver.resolveViewNameOrDefault(request, template,
|
||||
DefaultTemplateEnum.POST.getValue())
|
||||
.flatMap(templateName -> ServerResponse.ok().render(templateName, model));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -67,7 +74,7 @@ public class PostRouteStrategy implements DetailsPageRouteHandlerStrategy {
|
|||
}
|
||||
|
||||
private Mono<PostVo> postByName(String name) {
|
||||
return Mono.defer(() -> Mono.just(postFinder.getByName(name)))
|
||||
.publishOn(Schedulers.boundedElastic());
|
||||
return Mono.fromCallable(() -> postFinder.getByName(name))
|
||||
.subscribeOn(Schedulers.boundedElastic());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package run.halo.app.theme.router.strategy;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.server.HandlerFunction;
|
||||
|
@ -13,6 +14,7 @@ import run.halo.app.infra.SystemSetting;
|
|||
import run.halo.app.theme.DefaultTemplateEnum;
|
||||
import run.halo.app.theme.finders.SinglePageFinder;
|
||||
import run.halo.app.theme.finders.vo.SinglePageVo;
|
||||
import run.halo.app.theme.router.ViewNameResolver;
|
||||
|
||||
/**
|
||||
* The {@link SinglePageRouteStrategy} for generate {@link HandlerFunction} specific to the template
|
||||
|
@ -25,9 +27,12 @@ import run.halo.app.theme.finders.vo.SinglePageVo;
|
|||
public class SinglePageRouteStrategy implements DetailsPageRouteHandlerStrategy {
|
||||
private final GroupVersionKind gvk = GroupVersionKind.fromExtension(SinglePage.class);
|
||||
private final SinglePageFinder singlePageFinder;
|
||||
private final ViewNameResolver viewNameResolver;
|
||||
|
||||
public SinglePageRouteStrategy(SinglePageFinder singlePageFinder) {
|
||||
public SinglePageRouteStrategy(SinglePageFinder singlePageFinder,
|
||||
ViewNameResolver viewNameResolver) {
|
||||
this.singlePageFinder = singlePageFinder;
|
||||
this.viewNameResolver = viewNameResolver;
|
||||
}
|
||||
|
||||
private String getPlural() {
|
||||
|
@ -36,22 +41,27 @@ public class SinglePageRouteStrategy implements DetailsPageRouteHandlerStrategy
|
|||
}
|
||||
|
||||
private Mono<SinglePageVo> singlePageByName(String name) {
|
||||
return Mono.defer(() -> Mono.just(singlePageFinder.getByName(name)))
|
||||
.publishOn(Schedulers.boundedElastic());
|
||||
return Mono.fromCallable(() -> singlePageFinder.getByName(name))
|
||||
.subscribeOn(Schedulers.boundedElastic());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerFunction<ServerResponse> getHandler(SystemSetting.ThemeRouteRules routeRules,
|
||||
String name) {
|
||||
return request -> ServerResponse.ok()
|
||||
.render(DefaultTemplateEnum.SINGLE_PAGE.getValue(),
|
||||
Map.of("name", name,
|
||||
"groupVersionKind", gvk,
|
||||
"plural", getPlural(),
|
||||
"singlePage", singlePageByName(name),
|
||||
ModelConst.TEMPLATE_ID, DefaultTemplateEnum.SINGLE_PAGE.getValue()
|
||||
)
|
||||
);
|
||||
return request -> {
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
model.put("groupVersionKind", gvk);
|
||||
model.put("plural", getPlural());
|
||||
model.put(ModelConst.TEMPLATE_ID, DefaultTemplateEnum.SINGLE_PAGE.getValue());
|
||||
|
||||
return singlePageByName(name).flatMap(singlePageVo -> {
|
||||
model.put("singlePage", singlePageVo);
|
||||
String template = singlePageVo.getSpec().getTemplate();
|
||||
return viewNameResolver.resolveViewNameOrDefault(request, template,
|
||||
DefaultTemplateEnum.SINGLE_PAGE.getValue())
|
||||
.flatMap(viewName -> ServerResponse.ok().render(viewName, model));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -2,11 +2,15 @@ package run.halo.app.core.extension;
|
|||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.List;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import org.springframework.security.util.InMemoryResource;
|
||||
import run.halo.app.extension.Metadata;
|
||||
import run.halo.app.extension.Unstructured;
|
||||
import run.halo.app.infra.utils.JsonUtils;
|
||||
import run.halo.app.infra.utils.YamlUnstructuredLoader;
|
||||
|
||||
/**
|
||||
* Tests for {@link Theme}.
|
||||
|
@ -74,4 +78,75 @@ class ThemeTest {
|
|||
assertThat(themeSpec.getVersion()).isEqualTo("1.0.0");
|
||||
assertThat(themeSpec.getRequire()).isEqualTo("2.0.0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void themeCustomTemplate() throws JSONException {
|
||||
String themeYaml = """
|
||||
apiVersion: theme.halo.run/v1alpha1
|
||||
kind: Theme
|
||||
metadata:
|
||||
name: guqing-higan
|
||||
spec:
|
||||
displayName: higan
|
||||
customTemplates:
|
||||
post:
|
||||
- name: post-template-1
|
||||
description: description for post-template-1
|
||||
screenshot: foo.png
|
||||
file: post_template_1.html
|
||||
- name: post-template-2
|
||||
description: description for post-template-2
|
||||
screenshot: bar.png
|
||||
file: post_template_2.html
|
||||
category:
|
||||
- name: category-template-1
|
||||
description: description for category-template-1
|
||||
screenshot: foo.png
|
||||
file: category_template_1.html
|
||||
page:
|
||||
- name: page-template-1
|
||||
description: description for page-template-1
|
||||
screenshot: foo.png
|
||||
file: page_template_1.html
|
||||
""";
|
||||
List<Unstructured> unstructuredList =
|
||||
new YamlUnstructuredLoader(new InMemoryResource(themeYaml)).load();
|
||||
assertThat(unstructuredList).hasSize(1);
|
||||
Theme theme = Unstructured.OBJECT_MAPPER.convertValue(unstructuredList.get(0), Theme.class);
|
||||
assertThat(theme).isNotNull();
|
||||
JSONAssert.assertEquals("""
|
||||
{
|
||||
"post": [
|
||||
{
|
||||
"name": "post-template-1",
|
||||
"description": "description for post-template-1",
|
||||
"screenshot": "foo.png",
|
||||
"file": "post_template_1.html"
|
||||
},
|
||||
{
|
||||
"name": "post-template-2",
|
||||
"description": "description for post-template-2",
|
||||
"screenshot": "bar.png",
|
||||
"file": "post_template_2.html"
|
||||
}
|
||||
],
|
||||
"category": [
|
||||
{
|
||||
"name": "category-template-1",
|
||||
"description": "description for category-template-1",
|
||||
"screenshot": "foo.png",
|
||||
"file": "category_template_1.html"
|
||||
}],
|
||||
"page": [
|
||||
{
|
||||
"name": "page-template-1",
|
||||
"description": "description for page-template-1",
|
||||
"screenshot": "foo.png",
|
||||
"file": "page_template_1.html"
|
||||
}]
|
||||
}
|
||||
""",
|
||||
JsonUtils.objectToJson(theme.getSpec().getCustomTemplates()),
|
||||
true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package run.halo.app.theme.router;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.mock.web.reactive.function.server.MockServerRequest;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.web.reactive.result.view.View;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
import run.halo.app.theme.HaloViewResolver;
|
||||
import run.halo.app.theme.router.strategy.EmptyView;
|
||||
|
||||
/**
|
||||
* Tests for {@link ViewNameResolver}.
|
||||
*
|
||||
* @author guqing
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
class ViewNameResolverTest {
|
||||
|
||||
@Mock
|
||||
private HaloViewResolver haloViewResolver;
|
||||
|
||||
@Mock
|
||||
private ThymeleafProperties thymeleafProperties;
|
||||
|
||||
@InjectMocks
|
||||
private ViewNameResolver viewNameResolver;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
when(thymeleafProperties.getSuffix()).thenReturn(ThymeleafProperties.DEFAULT_SUFFIX);
|
||||
|
||||
when(haloViewResolver.resolveViewName(eq("post_news"), any()))
|
||||
.thenReturn(Mono.just(Mockito.mock(View.class)));
|
||||
when(haloViewResolver.resolveViewName(eq("post_docs"), any()))
|
||||
.thenReturn(Mono.just(new EmptyView()));
|
||||
|
||||
when(haloViewResolver.resolveViewName(eq("post_nothing"), any()))
|
||||
.thenReturn(Mono.empty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveViewNameOrDefault() throws URISyntaxException {
|
||||
ServerWebExchange exchange = Mockito.mock(ServerWebExchange.class);
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.uri(new URI("/")).method(HttpMethod.GET)
|
||||
.exchange(exchange)
|
||||
.build();
|
||||
|
||||
viewNameResolver.resolveViewNameOrDefault(request, "post_news", "post")
|
||||
.as(StepVerifier::create)
|
||||
.expectNext("post_news")
|
||||
.verifyComplete();
|
||||
|
||||
// post_docs.html
|
||||
String viewName = "post_docs" + thymeleafProperties.getSuffix();
|
||||
viewNameResolver.resolveViewNameOrDefault(request, viewName, "post")
|
||||
.as(StepVerifier::create)
|
||||
.expectNext("post_docs")
|
||||
.verifyComplete();
|
||||
|
||||
viewNameResolver.resolveViewNameOrDefault(request, "post_nothing", "post")
|
||||
.as(StepVerifier::create)
|
||||
.expectNext("post")
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void processName() {
|
||||
assertThat(viewNameResolver.processName("post_news")).isEqualTo("post_news");
|
||||
assertThat(viewNameResolver.processName("post_news" + thymeleafProperties.getSuffix()))
|
||||
.isEqualTo("post_news");
|
||||
assertThat(viewNameResolver.processName("post_news.test"))
|
||||
.isEqualTo("post_news.test");
|
||||
assertThat(viewNameResolver.processName(null)).isNull();
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@ import org.springframework.test.web.reactive.server.WebTestClient;
|
|||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import run.halo.app.extension.ListResult;
|
||||
import run.halo.app.theme.finders.CategoryFinder;
|
||||
import run.halo.app.theme.finders.PostFinder;
|
||||
|
||||
/**
|
||||
|
@ -29,6 +30,9 @@ class CategoryRouteStrategyTest extends RouterStrategyTestSuite {
|
|||
@Mock
|
||||
private PostFinder postFinder;
|
||||
|
||||
@Mock
|
||||
private CategoryFinder categoryFinder;
|
||||
|
||||
@InjectMocks
|
||||
private CategoryRouteStrategy categoryRouteStrategy;
|
||||
|
||||
|
|
|
@ -12,10 +12,13 @@ import org.springframework.http.HttpStatus;
|
|||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.content.TestPost;
|
||||
import run.halo.app.infra.SystemSetting;
|
||||
import run.halo.app.theme.DefaultTemplateEnum;
|
||||
import run.halo.app.theme.finders.PostFinder;
|
||||
import run.halo.app.theme.finders.vo.PostVo;
|
||||
import run.halo.app.theme.router.ViewNameResolver;
|
||||
|
||||
/**
|
||||
* Tests for {@link PostRouteStrategy}.
|
||||
|
@ -29,11 +32,16 @@ class PostRouteStrategyTest extends RouterStrategyTestSuite {
|
|||
@Mock
|
||||
private PostFinder postFinder;
|
||||
|
||||
@Mock
|
||||
private ViewNameResolver viewNameResolver;
|
||||
|
||||
@InjectMocks
|
||||
private PostRouteStrategy postRouteStrategy;
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
lenient().when(viewNameResolver.resolveViewNameOrDefault(any(), any(), any()))
|
||||
.thenReturn(Mono.just(DefaultTemplateEnum.POST.getValue()));
|
||||
lenient().when(postFinder.getByName(any())).thenReturn(PostVo.from(TestPost.postV1()));
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ package run.halo.app.theme.router.strategy;
|
|||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
@ -51,7 +50,7 @@ abstract class RouterStrategyTestSuite {
|
|||
lenient().when(environmentFetcher.fetch(eq(SystemSetting.ThemeRouteRules.GROUP),
|
||||
eq(SystemSetting.ThemeRouteRules.class))).thenReturn(Mono.just(getThemeRouteRules()));
|
||||
lenient().when(haloProperties.getExternalUrl()).thenReturn(new URI("http://example.com"));
|
||||
when(viewResolver.resolveViewName(any(), any()))
|
||||
lenient().when(viewResolver.resolveViewName(any(), any()))
|
||||
.thenReturn(Mono.just(new EmptyView()));
|
||||
setUp();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue