From badde3a479a53e1dd0777dd1bd5b55cb1021cf9e Mon Sep 17 00:00:00 2001 From: Sebastien Deleuze Date: Sun, 1 Jan 2017 13:52:54 +0100 Subject: [PATCH] Add Kotlin based ScriptTemplateView rendering test Kotlin JSR 223 support currently requires kotlin-script-util dependency (jcabi-aether, maven-core and aether-api can be excluded since they are only used for live import of dependencies and bring a lot of JARs in the classpath) and a /META-INF/services/javax.script.ScriptEngineFactory file specifying the ScriptEngineFactory to use, in that case org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngineFactory. Issue: SPR-15059 --- build.gradle | 9 ++ .../script/KotlinScriptTemplateTests.java | 100 ++++++++++++++++++ .../services/javax.script.ScriptEngineFactory | 1 + .../web/servlet/view/script/kotlin/render.kts | 10 ++ .../servlet/view/script/kotlin/template.kts | 1 + 5 files changed, 121 insertions(+) create mode 100644 spring-webmvc/src/test/java/org/springframework/web/servlet/view/script/KotlinScriptTemplateTests.java create mode 100644 spring-webmvc/src/test/resources/META-INF/services/javax.script.ScriptEngineFactory create mode 100644 spring-webmvc/src/test/resources/org/springframework/web/servlet/view/script/kotlin/render.kts create mode 100644 spring-webmvc/src/test/resources/org/springframework/web/servlet/view/script/kotlin/template.kts diff --git a/build.gradle b/build.gradle index ca2cf37bcc0e..6f949375e686 100644 --- a/build.gradle +++ b/build.gradle @@ -969,6 +969,15 @@ project("spring-webmvc") { testCompile("org.mozilla:rhino:1.7.7.1") testRuntime("org.jruby:jruby:9.1.6.0") testRuntime("org.python:jython-standalone:2.5.3") + // Ideally, kotlin-script-runtime should be enough for JSR-223, but that's not + // the case yet, so we depend on kotlin-script-util and exclude these + // dependencies only used for artifact retrieval. Point raised to Kotlin team. + testRuntime("org.jetbrains.kotlin:kotlin-compiler:${kotlinVersion}") + testRuntime("org.jetbrains.kotlin:kotlin-script-util:${kotlinVersion}") { + exclude group: "com.jcabi", module: "jcabi-aether" + exclude group: "org.apache.maven", module: "maven-core" + exclude group: "org.sonatype.aether", module: "aether-api" + } testRuntime("org.webjars:underscorejs:1.8.3") testRuntime("org.glassfish:javax.el:3.0.1-b08") testRuntime("com.sun.xml.bind:jaxb-core:${jaxbVersion}") diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/view/script/KotlinScriptTemplateTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/script/KotlinScriptTemplateTests.java new file mode 100644 index 000000000000..362b78b31db3 --- /dev/null +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/view/script/KotlinScriptTemplateTests.java @@ -0,0 +1,100 @@ +/* + * Copyright 2002-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.servlet.view.script; + +import java.util.HashMap; +import java.util.Map; +import javax.servlet.ServletContext; + +import static org.junit.Assert.assertEquals; +import org.junit.Before; +import org.junit.Test; +import static org.mockito.Mockito.mock; + +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.mock.web.test.MockHttpServletRequest; +import org.springframework.mock.web.test.MockHttpServletResponse; +import org.springframework.mock.web.test.MockServletContext; +import org.springframework.web.context.WebApplicationContext; + +/** + * Unit tests for Kotlin script templates running on Kotlin JSR 223 support + * + * @author Sebastien Deleuze + */ +public class KotlinScriptTemplateTests { + + private WebApplicationContext webAppContext; + + private ServletContext servletContext; + + + @Before + public void setup() { + this.webAppContext = mock(WebApplicationContext.class); + this.servletContext = new MockServletContext(); + this.servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.webAppContext); + } + + @Test + public void renderTemplate() throws Exception { + Map model = new HashMap<>(); + model.put("title", "Layout example"); + model.put("body", "This is the body"); + MockHttpServletResponse response = renderViewWithModel("org/springframework/web/servlet/view/script/kotlin/template.kts", + model, ScriptTemplatingConfiguration.class); + assertEquals("Layout example

This is the body

", + response.getContentAsString()); + } + + private MockHttpServletResponse renderViewWithModel(String viewUrl, Map model, Class configuration) throws Exception { + ScriptTemplateView view = createViewWithUrl(viewUrl, configuration); + MockHttpServletResponse response = new MockHttpServletResponse(); + MockHttpServletRequest request = new MockHttpServletRequest(); + view.renderMergedOutputModel(model, request, response); + return response; + } + + private ScriptTemplateView createViewWithUrl(String viewUrl, Class configuration) throws Exception { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(configuration); + ctx.refresh(); + + ScriptTemplateView view = new ScriptTemplateView(); + view.setApplicationContext(ctx); + view.setUrl(viewUrl); + view.afterPropertiesSet(); + return view; + } + + + @Configuration + static class ScriptTemplatingConfiguration { + + @Bean + public ScriptTemplateConfigurer kotlinScriptConfigurer() { + ScriptTemplateConfigurer configurer = new ScriptTemplateConfigurer(); + configurer.setEngineName("kotlin"); + configurer.setScripts("org/springframework/web/servlet/view/script/kotlin/render.kts"); + configurer.setRenderFunction("render"); + return configurer; + } + } + +} diff --git a/spring-webmvc/src/test/resources/META-INF/services/javax.script.ScriptEngineFactory b/spring-webmvc/src/test/resources/META-INF/services/javax.script.ScriptEngineFactory new file mode 100644 index 000000000000..fa84d5e49970 --- /dev/null +++ b/spring-webmvc/src/test/resources/META-INF/services/javax.script.ScriptEngineFactory @@ -0,0 +1 @@ +org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngineFactory diff --git a/spring-webmvc/src/test/resources/org/springframework/web/servlet/view/script/kotlin/render.kts b/spring-webmvc/src/test/resources/org/springframework/web/servlet/view/script/kotlin/render.kts new file mode 100644 index 000000000000..d1ab1ae6db5e --- /dev/null +++ b/spring-webmvc/src/test/resources/org/springframework/web/servlet/view/script/kotlin/render.kts @@ -0,0 +1,10 @@ +import javax.script.* + +// TODO Use engine.eval(String, Bindings) when https://youtrack.jetbrains.com/issue/KT-15450 will be fixed +fun render(template: String, model: Map, url: String): String { + val engine = ScriptEngineManager().getEngineByName("kotlin") + val bindings = SimpleBindings() + bindings.putAll(model) + engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE) + return engine.eval(template) as String +} diff --git a/spring-webmvc/src/test/resources/org/springframework/web/servlet/view/script/kotlin/template.kts b/spring-webmvc/src/test/resources/org/springframework/web/servlet/view/script/kotlin/template.kts new file mode 100644 index 000000000000..bacaa3f3d018 --- /dev/null +++ b/spring-webmvc/src/test/resources/org/springframework/web/servlet/view/script/kotlin/template.kts @@ -0,0 +1 @@ +"""${bindings["title"]}

${bindings["body"]}

"""