Skip to content

Commit

Permalink
fix: Major updates to ContainerGebSpec feature
Browse files Browse the repository at this point in the history
- Use Gradle testFixtures feature to make test dependencies available to downstream projects without leaking them to the wrong scopes
- Refactor `ContainerGebSpec` in co-operation with sbglasius to add more features and make it work without Docker Desktop.
  • Loading branch information
matrei committed Oct 16, 2024
1 parent a368a26 commit d9ce199
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 60 deletions.
53 changes: 26 additions & 27 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ version projectVersion
group "org.grails.plugins"

apply plugin: 'java-library'
apply plugin: 'idea'
apply plugin: 'java-test-fixtures'
apply plugin: 'org.grails.grails-plugin'
apply plugin: 'org.grails.internal.grails-plugin-publish'
apply plugin: 'maven-publish'

java {
toolchain {
Expand All @@ -30,27 +31,26 @@ configurations {
}

dependencies {
compileOnly "org.grails:grails-core:$grailsVersion"
compileOnlyApi "jakarta.servlet:jakarta.servlet-api:$servletApiVersion"
compileOnly "org.grails:grails-core:$grailsVersion" // Provided as this is a Grails plugin
compileOnly "jakarta.servlet:jakarta.servlet-api:$servletApiVersion" // Provided by the servlet container

// used transitively by the generated tests
// These are used transitively by the tests generated by the 'create-functional-test' script
// If they are not included as api, the generated tests will not compile without adding the dependencies manually
api "org.gebish:geb-spock:$gebSpock"
api "org.grails:grails-testing-support:$testingSupportVersion"
api "org.grails:grails-datastore-gorm:$datastoreVersion"

implementation "org.seleniumhq.selenium:selenium-chrome-driver:$seleniumVersion"
implementation "org.seleniumhq.selenium:selenium-remote-driver:$seleniumVersion"
implementation "org.testcontainers:spock:$testcontainersVersion"
implementation "org.testcontainers:selenium:$testcontainersVersion"
testFixturesCompileOnly "jakarta.servlet:jakarta.servlet-api:$servletApiVersion"
testFixturesApi "org.testcontainers:selenium:$testcontainersVersion"
testFixturesApi "org.seleniumhq.selenium:selenium-chrome-driver:$seleniumVersion"
testFixturesApi "org.seleniumhq.selenium:selenium-remote-driver:$seleniumVersion"

documentation "org.apache.groovy:groovy:$groovyVersion"
documentation "org.apache.groovy:groovy-ant:$groovyVersion"
documentation "org.apache.groovy:groovy-templates:$groovyVersion"
documentation "com.github.javaparser:javaparser-core:$javaParserCoreVersion"
}

findMainClass.enabled = false

grailsPublish {
userOrg = 'grails'
githubSlug = 'grails/geb'
Expand All @@ -59,36 +59,35 @@ grailsPublish {
}
title = "Grails Geb Plugin"
desc = "Provides Integration with Geb for Functional Testing"
developers = [graemerocher: "Graeme Rocher", puneetbehl: "Puneet Behl"]
developers = [
graemerocher: 'Graeme Rocher',
puneetbehl: 'Puneet Behl',
sbglasius: 'Søren Berg Glasius',
matrei: 'Mattias Reichel'
]
}

tasks.withType(Groovydoc) {
destinationDir = new File(buildDir, 'docs/api')
tasks.withType(Groovydoc).configureEach {
destinationDir = layout.buildDirectory.dir('docs/api').get().asFile
docTitle = "Grails Geb Plugin ${version}"
classpath = configurations.documentation
}

tasks.withType(GroovyCompile) {
configure(groovyOptions) {
forkOptions.jvmArgs = ['-Xmx1024m']
}
tasks.withType(GroovyCompile).configureEach {
groovyOptions.forkOptions.jvmArgs = ['-Xmx1024m']
}

tasks.withType(Test) {
tasks.withType(Test).configureEach {
useJUnitPlatform()
}

test {
testLogging {
events "passed", "skipped", "failed"

events 'passed', 'skipped', 'failed'
showExceptions true
exceptionFormat "full"
exceptionFormat 'full'
showCauses true
showStackTraces true
}
}

bootJar.enabled = false
bootRun.enabled = false
bootTestRun.enabled = false
tasks.named('bootJar').configure { enabled = false }
tasks.named('bootRun').configure { enabled = false }
tasks.named('bootTestRun').configure { enabled = false }
33 changes: 0 additions & 33 deletions src/main/groovy/grails/plugin/geb/ContainerGebSpec.groovy

This file was deleted.

109 changes: 109 additions & 0 deletions src/testFixtures/groovy/grails/plugin/geb/ContainerGebSpec.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package grails.plugin.geb
/*
* Copyright 2024 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
*
* https://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.
*/


import geb.spock.GebSpec
import groovy.transform.PackageScope
import org.openqa.selenium.chrome.ChromeOptions
import org.openqa.selenium.remote.RemoteWebDriver
import org.testcontainers.Testcontainers
import org.testcontainers.containers.BrowserWebDriverContainer
import org.testcontainers.containers.PortForwardingContainer
import spock.lang.Shared

import java.time.Duration

/**
* A {@link geb.spock.GebSpec GebSpec} that leverages Testcontainers to run the browser inside a container.
*
* <p>Prerequisites:
* <ul>
* <li>
* The test class must be annotated with {@link grails.testing.mixin.integration.Integration @Integration}.
* </li>
* <li>
* A <a href="https://java.testcontainers.org/supported_docker_environment/">compatible container runtime</a>
* (e.g., Docker) must be available for Testcontainers to utilize.
* </li>
* </ul>
*
* @author Søren Berg Glasius
* @author Mattias Reichel
* @since 5.0.0
*/
class ContainerGebSpec extends GebSpec {

private static final String DEFAULT_PROTOCOL = 'http'
private static final String DEFAULT_HOSTNAME = 'host.testcontainers.internal'

@Shared
BrowserWebDriverContainer webDriverContainer

@PackageScope
void initialize() {
if (!webDriverContainer) {
if (!hasProperty('serverPort')) {
throw new IllegalStateException('Test class must be annotated with @Integration for serverPort to be injected')
}
webDriverContainer = new BrowserWebDriverContainer()
webDriverContainer.tap {
addExposedPort(serverPort)
withAccessToHost(true)
start()
}
Testcontainers.exposeHostPorts(serverPort)
if (hostName != DEFAULT_HOSTNAME) {
webDriverContainer.execInContainer('/bin/sh', '-c', "echo '$hostIp\t$hostName' | sudo tee -a /etc/hosts")
}
browser.driver = new RemoteWebDriver(webDriverContainer.seleniumAddress, new ChromeOptions())
browser.driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(30))
}
}

void setup() {
initialize()
baseUrl = "$protocol://$hostName:$serverPort"
}

def cleanupSpec() {
webDriverContainer.stop()
}

/**
* Returns the protocol that the browser will use to access the server under test.
* <p>Defaults to {@code http}.
*
* @return the protocol for accessing the server under test
*/
String getProtocol() {
return DEFAULT_PROTOCOL
}

/**
* Returns the hostname that the browser will use to access the server under test.
* <p>Defaults to {@code host.testcontainers.internal}.
*
* @return the hostname for accessing the server under test
*/
String getHostName() {
return DEFAULT_HOSTNAME
}

private static String getHostIp() {
PortForwardingContainer.INSTANCE.network.get().ipAddress
}
}

0 comments on commit d9ce199

Please sign in to comment.