/subprojects/platform-play/src/integTest/groovy/org/gradle/play/tasks/TwirlCompileIntegrationTest.groovy
Groovy | 594 lines | 517 code | 60 blank | 17 comment | 2 complexity | 75c3e095b96ba05317537afd590f1864 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0, LGPL-2.1
- /*
- * Copyright 2014 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.gradle.play.tasks
- import org.gradle.integtests.fixtures.ToBeFixedForInstantExecution
- import org.gradle.play.integtest.fixtures.PlayMultiVersionIntegrationTest
- import org.gradle.test.fixtures.archive.JarTestFixture
- import org.gradle.util.VersionNumber
- import spock.lang.Unroll
- import static org.gradle.play.integtest.fixtures.Repositories.PLAY_REPOSITORIES
- import static org.hamcrest.CoreMatchers.containsString
- class TwirlCompileIntegrationTest extends PlayMultiVersionIntegrationTest {
- def destinationDir = file("build/src/play/binary/twirlTemplatesScalaSources/views")
- def setup() {
- settingsFile << """ rootProject.name = 'twirl-play-app' """
- buildFile << """
- plugins {
- id 'play-application'
- }
- ${PLAY_REPOSITORIES}
- model {
- components {
- play {
- targetPlatform "play-${version}"
- }
- }
- }
- """
- }
- @Unroll
- def "can run TwirlCompile with #format template"() {
- given:
- twirlTemplate("test.scala.${format}") << template
- when:
- succeeds("compilePlayBinaryScala")
- then:
- def generatedFile = destinationDir.file("${format}/test.template.scala")
- generatedFile.assertIsFile()
- generatedFile.assertContents(containsString("import views.${format}._"))
- generatedFile.assertContents(containsString(templateFormat))
- where:
- format | templateFormat | template
- "js" | 'JavaScriptFormat' | '@(username: String) alert(@helper.json(username));'
- "xml" | 'XmlFormat' | '@(username: String) <xml> <username> @username </username>'
- "txt" | 'TxtFormat' | '@(username: String) @username'
- "html" | 'HtmlFormat' | '@(username: String) <html> <body> <h1>Hello @username</h1> </body> </html>'
- }
- def "can compile custom Twirl templates"() {
- given:
- twirlTemplate("test.scala.csv") << """
- @(username: String)(content: Csv)
- # generated by @username
- @content
- """
- addCsvFormat()
- when:
- succeeds("compilePlayBinaryScala")
- then:
- def generatedFile = destinationDir.file("csv/test.template.scala")
- generatedFile.assertIsFile()
- generatedFile.assertContents(containsString("import views.formats.csv._"))
- generatedFile.assertContents(containsString("CsvFormat"))
- // Modifying user templates causes TwirlCompile to be out-of-date
- when:
- executer.noDeprecationChecks()
- buildFile << """
- model {
- components {
- play {
- sources {
- withType(TwirlSourceSet) {
- addUserTemplateFormat("unused", "views.formats.unused.UnusedFormat")
- }
- }
- }
- }
- }
- """
- and:
- succeeds("compilePlayBinaryScala")
- then:
- result.assertTasksNotSkipped(":compilePlayBinaryPlayTwirlTemplates", ":compilePlayBinaryScala")
- }
- def "can specify additional imports for a Twirl template"() {
- given:
- withTwirlTemplate()
- buildFile << """
- model {
- components {
- play {
- sources {
- twirlTemplates {
- additionalImports = [ 'my.pkg._' ]
- }
- }
- }
- }
- }
- """
- file("app/my/pkg/MyClass.scala") << """
- package my.pkg
-
- object MyClass;
- """
- when:
- succeeds("compilePlayBinaryScala")
- then:
- def generatedFile = destinationDir.file("html/index.template.scala")
- generatedFile.assertIsFile()
- generatedFile.assertContents(containsString("import my.pkg._"))
- // Changing the imports causes TwirlCompile to be out-of-date
- when:
- executer.noDeprecationChecks()
- buildFile << """
- model {
- components {
- play {
- sources {
- twirlTemplates {
- additionalImports = [ 'my.pkg._', 'my.pkg.MyClass' ]
- }
- }
- }
- }
- }
- """
- and:
- succeeds("compilePlayBinaryScala")
- then:
- result.assertTasksNotSkipped(":compilePlayBinaryPlayTwirlTemplates", ":compilePlayBinaryScala")
- generatedFile.assertContents(containsString("import my.pkg._"))
- generatedFile.assertContents(containsString("import my.pkg.MyClass"))
- }
- @ToBeFixedForInstantExecution
- def "runs compiler incrementally"() {
- when:
- withTwirlTemplate("input1.scala.html")
- then:
- succeeds("compilePlayBinaryPlayTwirlTemplates")
- and:
- destinationDir.assertHasDescendants("html/input1.template.scala")
- def input1FirstCompileSnapshot = destinationDir.file("html/input1.template.scala").snapshot()
- when:
- executer.noDeprecationChecks()
- succeeds("compilePlayBinaryPlayTwirlTemplates")
- then:
- skipped(":compilePlayBinaryPlayTwirlTemplates")
- when:
- executer.noDeprecationChecks()
- withTwirlTemplate("input2.scala.html")
- and:
- succeeds("compilePlayBinaryPlayTwirlTemplates")
- then:
- destinationDir.assertHasDescendants("html/input1.template.scala", "html/input2.template.scala")
- and:
- destinationDir.file("html/input1.template.scala").assertHasNotChangedSince(input1FirstCompileSnapshot)
- when:
- executer.noDeprecationChecks()
- file("app/views/input2.scala.html").delete()
- then:
- succeeds("compilePlayBinaryPlayTwirlTemplates")
- and:
- destinationDir.assertHasDescendants("html/input1.template.scala")
- }
- @ToBeFixedForInstantExecution
- def "removes stale output files in incremental compile"(){
- given:
- withTwirlTemplate("input1.scala.html")
- withTwirlTemplate("input2.scala.html")
- succeeds("compilePlayBinaryPlayTwirlTemplates")
- and:
- destinationDir.assertHasDescendants("html/input1.template.scala", "html/input2.template.scala")
- def input1FirstCompileSnapshot = destinationDir.file("html/input1.template.scala").snapshot()
- when:
- executer.noDeprecationChecks()
- file("app/views/input2.scala.html").delete()
- then:
- succeeds("compilePlayBinaryPlayTwirlTemplates")
- and:
- destinationDir.assertHasDescendants("html/input1.template.scala")
- destinationDir.file("html/input1.template.scala").assertHasNotChangedSince(input1FirstCompileSnapshot)
- destinationDir.file("html/input2.template.scala").assertDoesNotExist()
- }
- def "builds multiple twirl source sets as part of play build" () {
- withExtraSourceSets()
- withTemplateSource(file("app", "views", "index.scala.html"))
- withTemplateSource(file("otherSources", "templates", "other.scala.html"))
- withTemplateSource(file("extraSources", "extra.scala.html"))
- when:
- succeeds "assemble"
- then:
- executedAndNotSkipped(
- ":compilePlayBinaryPlayTwirlTemplates",
- ":compilePlayBinaryPlayExtraTwirl",
- ":compilePlayBinaryPlayOtherTwirl"
- )
- and:
- destinationDir.assertHasDescendants("html/index.template.scala")
- file("build/src/play/binary/otherTwirlScalaSources").assertHasDescendants("templates/html/other.template.scala")
- file("build/src/play/binary/extraTwirlScalaSources").assertHasDescendants("html/extra.template.scala")
- and:
- jar("build/playBinary/lib/twirl-play-app.jar")
- .containsDescendants("views/html/index.class", "templates/html/other.class", "html/extra.class")
- }
- @ToBeFixedForInstantExecution
- def "can build twirl source set with default Java imports" () {
- withTwirlJavaSourceSets()
- withTemplateSourceExpectingJavaImports(file("twirlJava", "javaTemplate.scala.html"))
- validateThatPlayJavaDependencyIsAdded()
- when:
- succeeds "assemble"
- then:
- executedAndNotSkipped ":compilePlayBinaryPlayTwirlJava"
- and:
- jar("build/playBinary/lib/twirl-play-app.jar")
- .containsDescendants("html/javaTemplate.class")
- }
- def "can build twirl source sets both with and without default Java imports" () {
- withTwirlJavaSourceSets()
- withTemplateSource(file("app", "views", "index.scala.html"))
- withTemplateSourceExpectingJavaImports(file("twirlJava", "javaTemplate.scala.html"))
- when:
- succeeds "assemble"
- then:
- executedAndNotSkipped(
- ":compilePlayBinaryPlayTwirlTemplates",
- ":compilePlayBinaryPlayTwirlJava"
- )
- and:
- jar("build/playBinary/lib/twirl-play-app.jar")
- .containsDescendants("html/javaTemplate.class", "views/html/index.class")
- }
- @ToBeFixedForInstantExecution
- def "twirl source sets default to Scala imports" () {
- withTemplateSource(file("app", "views", "index.scala.html"))
- validateThatPlayJavaDependencyIsNotAdded()
- validateThatSourceSetsDefaultToScalaImports()
- when:
- succeeds "assemble"
- then:
- executedAndNotSkipped ":compilePlayBinaryPlayTwirlTemplates"
- }
- @ToBeFixedForInstantExecution(because = ":components")
- def "extra sources appear in the component report"() {
- withExtraSourceSets()
- when:
- succeeds "components"
- then:
- output.contains """
- Play Application 'play'
- -----------------------
- Source sets
- Java source 'play:java'
- srcDir: app
- includes: **/*.java
- JVM resources 'play:resources'
- srcDir: conf
- Routes source 'play:routes'
- srcDir: conf
- includes: routes, *.routes
- Scala source 'play:scala'
- srcDir: app
- includes: **/*.scala
- Twirl template source 'play:extraTwirl'
- srcDir: extraSources
- Twirl template source 'play:otherTwirl'
- srcDir: otherSources
- Twirl template source 'play:twirlTemplates'
- srcDir: app
- includes: **/*.scala.*
- Binaries
- """
- }
- @Unroll
- def "has reasonable error if Twirl template is configured incorrectly with (#template)"() {
- given:
- executer.noDeprecationChecks()
- buildFile << """
- model {
- components {
- play {
- sources {
- withType(TwirlSourceSet) {
- addUserTemplateFormat($template)
- }
- }
- }
- }
- }
- """
- when:
- result = executer.withTasks('components').runWithFailure()
- then:
- result.assertHasCause(errorMessage)
- where:
- template | errorMessage
- "null, 'CustomFormat'" | "Custom template extension cannot be null."
- "'.ext', 'CustomFormat'" | "Custom template extension should not start with a dot."
- "'ext', null" | "Custom template format type cannot be null."
- }
- def "has reasonable error if Twirl template cannot be found"() {
- twirlTemplate("test.scala.custom") << "@(username: String) Custom template, @username!"
- when:
- fails("compilePlayBinaryScala")
- then:
- failure.assertHasCause("Twirl compiler could not find a matching template for 'test.scala.custom'.")
- }
- def withTemplateSource(File templateFile) {
- templateFile << """@(message: String)
- <h1>@message</h1>
- """
- }
- def twirlTemplate(String fileName) {
- file("app", "views", fileName)
- }
- def withTwirlTemplate(String fileName = "index.scala.html") {
- def templateFile = file("app", "views", fileName)
- templateFile.createFile()
- withTemplateSource(templateFile)
- }
- def withTemplateSourceExpectingJavaImports(File templateFile) {
- templateFile << """
- <!DOCTYPE html>
- <html>
- <body>
- <p>@UUID.randomUUID().toString()</p>
- </body>
- </html>
- """
- }
- def withExtraSourceSets() {
- buildFile << """
- model {
- components {
- play {
- sources {
- extraTwirl(TwirlSourceSet) {
- source.srcDir "extraSources"
- }
- otherTwirl(TwirlSourceSet) {
- source.srcDir "otherSources"
- }
- }
- }
- }
- }
- """
- }
- def withTwirlJavaSourceSets() {
- buildFile << """
- model {
- components {
- play {
- sources {
- twirlJava(TwirlSourceSet) {
- defaultImports = TwirlImports.JAVA
- source.srcDir "twirlJava"
- }
- }
- }
- }
- }
- """
- }
- def validateThatPlayJavaDependencyIsAdded() {
- validateThatPlayJavaDependency(true)
- }
- def validateThatPlayJavaDependencyIsNotAdded() {
- validateThatPlayJavaDependency(false)
- }
- def validateThatPlayJavaDependency(boolean shouldBePresent) {
- buildFile << """
- model {
- components {
- play {
- binaries.all { binary ->
- tasks.withType(TwirlCompile) {
- doFirst {
- assert ${shouldBePresent ? "" : "!"} configurations.play.dependencies.any {
- it.group == "com.typesafe.play" &&
- it.name == "play-java_\${binary.targetPlatform.scalaPlatform.scalaCompatibilityVersion}" &&
- it.version == binary.targetPlatform.playVersion
- }
- }
- }
- }
- }
- }
- }
- """
- }
- def validateThatSourceSetsDefaultToScalaImports() {
- buildFile << """
- model {
- components {
- play {
- binaries.all { binary ->
- tasks.withType(TwirlCompile) {
- doFirst {
- assert defaultImports == TwirlImports.SCALA
- assert binary.inputs.withType(TwirlSourceSet).every {
- it.defaultImports == TwirlImports.SCALA
- }
- }
- }
- }
- }
- }
- }
- """
- }
- JarTestFixture jar(String fileName) {
- new JarTestFixture(file(fileName))
- }
- private void addCsvFormat() {
- buildFile << """
- model {
- components {
- play {
- sources {
- withType(TwirlSourceSet) {
- addUserTemplateFormat("csv", "views.formats.csv.CsvFormat", "views.formats.csv._")
- }
- }
- }
- }
- }
- """
- if (versionNumber < VersionNumber.parse("2.3.0")) {
- file("app/views/formats/csv/Csv.scala") << """
- package views.formats.csv
- import play.api.http.ContentTypeOf
- import play.api.mvc.Codec
- import play.api.templates.BufferedContent
- import play.templates.Format
- class Csv(buffer: StringBuilder) extends BufferedContent[Csv](buffer) {
- val contentType = Csv.contentType
- }
- object Csv {
- val contentType = "text/csv"
- implicit def contentTypeCsv(implicit codec: Codec): ContentTypeOf[Csv] = ContentTypeOf[Csv](Some(Csv.contentType))
- def apply(text: String): Csv = new Csv(new StringBuilder(text))
-
- def empty: Csv = new Csv(new StringBuilder)
- }
- object CsvFormat extends Format[Csv] {
- def raw(text: String): Csv = Csv(text)
- def escape(text: String): Csv = Csv(text)
- }
- """
- } else {
- file("app/views/formats/csv/Csv.scala") << """
- package views.formats.csv
- import scala.collection.immutable
- import play.twirl.api.BufferedContent
- import play.twirl.api.Format
- class Csv private (elements: immutable.Seq[Csv], text: String) extends BufferedContent[Csv](elements, text) {
- def this(text: String) = this(Nil, if (text eq null) "" else text)
- def this(elements: immutable.Seq[Csv]) = this(elements, "")
- /**
- * Content type of CSV.
- */
- def contentType = "text/csv"
- }
- /**
- * Helper for CSV utility methods.
- */
- object Csv {
- /**
- * Creates an CSV fragment with initial content specified.
- */
- def apply(text: String): Csv = {
- new Csv(text)
- }
- }
- /**
- * Formatter for CSV content.
- */
- object CsvFormat extends Format[Csv] {
- /**
- * Creates a CSV fragment.
- */
- def raw(text: String) = Csv(text)
- /**
- * Creates an escaped CSV fragment.
- */
- def escape(text: String) = Csv(text)
- /**
- * Generate an empty CSV fragment
- */
- val empty: Csv = new Csv("")
- /**
- * Create a CSV Fragment that holds other fragments.
- */
- def fill(elements: immutable.Seq[Csv]): Csv = new Csv(elements)
- }
- """
- }
- }
- }