Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix reporting support for ContainerGebSpec #81

Merged
merged 8 commits into from
Nov 28, 2024
Merged
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
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,21 @@ This requires a [compatible container runtime](https://java.testcontainers.org/s
If you choose to use the `ContainerGebSpec` class, as long as you have a compatible container runtime installed, you don't need to do anything else.
Just run `./gradlew integrationTest` and a container will be started and configured to start a browser that can access your application under test.

#### Parallel Execution

Parallel execution of `ContainerGebSpec` specifications is not currently supported.

#### Custom Host Configuration

The annotation `ContainerGebConfiguration` exists to customize the connection the container will use to access the application under test. The annotation is not required and `ContainerGebSpec` will use the default values in this annotation if it's not present.
The annotation `ContainerGebConfiguration` exists to customize the connection the container will use to access the application under test. The annotation is not required and `ContainerGebSpec` will use the default values in this annotation if it's not present. A traditional `GebConfig.groovy` can be provided to configure non-container specific settings.

#### Reporting

To configure reporting, enable it using the `recording` property on the annotation `ContainerGebConfiguration`. The following system properties exist for reporting configuration:

* `grails.geb.reporting.directory`
* purpose: if the test enables reporting, the directory to save the reports relative to the project directory
* defaults to `build/gebContainer/reports`

#### Recording

Expand All @@ -74,7 +86,7 @@ By default, no test recording will be performed. Various system properties exis

* `grails.geb.recording.directory`
* purpose: the directory to save the recordings relative to the project directory
* defaults to `build/recordings`
* defaults to `build/gebContainer/recordings`


* `grails.geb.recording.format`
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ projectVersion=5.0.0-SNAPSHOT
grailsVersion=7.0.0-SNAPSHOT
grailsGradlePluginVersion=7.0.0-SNAPSHOT
seleniumVersion=4.25.0
testcontainersVersion=1.20.2
testcontainersVersion=1.20.4

# This prevents the Grails Gradle Plugin from unnecessarily excluding slf4j-simple in the generated POMs
# https://github.com/grails/grails-gradle-plugin/issues/222
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package org.demo.spock

import geb.report.CompositeReporter
import geb.report.PageSourceReporter
import geb.report.Reporter
import geb.report.ScreenshotReporter
import grails.plugin.geb.ContainerGebConfiguration
import grails.plugin.geb.ContainerGebSpec
import grails.testing.mixin.integration.Integration

Expand All @@ -8,13 +13,21 @@ import grails.testing.mixin.integration.Integration
* for more instructions on how to write functional tests with Grails and Geb.
*/
@Integration
@ContainerGebConfiguration(reporting = true)
class RootPageSpec extends ContainerGebSpec {

@Override
Reporter createReporter() {
// Override the default reporter to demonstrate how this can be customized
new CompositeReporter(new ScreenshotReporter(), new PageSourceReporter())
}

void 'should display the correct title on the home page'() {
when: 'visiting the home page'
go '/'

then: 'the page title is correct'
report('root page report')
title == 'Welcome to Grails'
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,10 @@
*/
package grails.plugin.geb

import geb.Browser
import geb.download.DefaultDownloadSupport
import geb.download.DownloadSupport
import groovy.transform.CompileStatic
import groovy.transform.SelfType

import java.util.regex.Pattern
import spock.lang.Shared

/**
* A custom implementation of {@link geb.download.DownloadSupport} for enabling the use of its {@code download*()} methods
jdaugherty marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -35,41 +32,13 @@ import java.util.regex.Pattern
* setups, ensuring the host network context is used for download requests.</p>
*
* @author Mattias Reichel
* @since 5.0
* @since 4.1
*/
@CompileStatic
@SelfType(ContainerGebSpec)
trait ContainerAwareDownloadSupport implements DownloadSupport {

@Delegate
private final DownloadSupport downloadSupport = new LocalhostDownloadSupport(browser, this)

abstract Browser getBrowser()

abstract String getHostNameFromHost()

private static class LocalhostDownloadSupport extends DefaultDownloadSupport {

private final static Pattern urlPattern = ~/(https?:\/\/)([^\/:]+)(:\d+\/.*)/

private final ContainerAwareDownloadSupport parent
private final Browser browser

LocalhostDownloadSupport(Browser browser, ContainerAwareDownloadSupport parent) {
super(browser)
this.browser = browser
this.parent = parent
}

@Override
HttpURLConnection download(Map options) {
return super.download([*: options, base: resolveBase(options)])
}

private String resolveBase(Map options) {
return options.base ?: browser.driver.currentUrl.replaceAll(urlPattern) { match, proto, host, rest ->
"${proto}${parent.hostNameFromHost}${rest}"
}
}
}
@Shared
static DownloadSupport downloadSupport
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package grails.plugin.geb

import org.testcontainers.containers.GenericContainer

import java.lang.annotation.ElementType
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
Expand All @@ -24,13 +26,13 @@ import java.lang.annotation.Target
* Can be used to configure the protocol and hostname that the container's browser will use
*
* @author James Daugherty
* @since 5.0
* @since 4.1
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface ContainerGebConfiguration {

static final String DEFAULT_HOSTNAME_FROM_CONTAINER = 'host.testcontainers.internal'
static final String DEFAULT_HOSTNAME_FROM_CONTAINER = GenericContainer.INTERNAL_HOST_HOSTNAME
static final String DEFAULT_PROTOCOL = 'http'

/**
Expand All @@ -45,4 +47,9 @@ import java.lang.annotation.Target
* <p>This is useful when the server under test needs to be accessed with a certain hostname.
*/
String hostName() default DEFAULT_HOSTNAME_FROM_CONTAINER

/**
* Whether reporting should be enabled for this test. Add a `GebConfig.groovy` to customize the reporter configuration.
*/
boolean reporting() default false
}

This file was deleted.

45 changes: 13 additions & 32 deletions src/testFixtures/groovy/grails/plugin/geb/ContainerGebSpec.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
*/
package grails.plugin.geb

import geb.report.CompositeReporter
import geb.report.PageSourceReporter
import geb.report.Reporter
import geb.test.GebTestManager
import geb.test.ManagedGebTest
import geb.transform.DynamicallyDispatchesToBrowser
import org.testcontainers.containers.BrowserWebDriverContainer
import spock.lang.Shared
Expand All @@ -41,24 +43,15 @@ import spock.lang.Specification
* @author Søren Berg Glasius
* @author Mattias Reichel
* @author James Daugherty
* @since 5.0
* @since 4.1
*/
@DynamicallyDispatchesToBrowser
abstract class ContainerGebSpec extends Specification implements ManagedGebTest, ContainerAwareDownloadSupport {

private static final String DEFAULT_HOSTNAME_FROM_HOST = 'localhost'
boolean reportingEnabled = false

@Override
@Delegate(includes = ['getBrowser', 'report'])
GebTestManager getTestManager() {
return isReportingEnabled() ?
GebTestManagerProvider.getReportingInstance() :
GebTestManagerProvider.getInstance()
}
abstract class ContainerGebSpec extends Specification implements ContainerAwareDownloadSupport {

@Shared
BrowserWebDriverContainer webDriverContainer
@Delegate(includes = ['getBrowser', 'report'])
@SuppressWarnings('unused')
static GebTestManager testManager

/**
* Get access to container running the web-driver, for convenience to execInContainer, copyFileToContainer etc.
Expand All @@ -68,25 +61,13 @@ abstract class ContainerGebSpec extends Specification implements ManagedGebTest,
* @see org.testcontainers.containers.ContainerState#copyFileFromContainer(java.lang.String, java.lang.String)
* @see org.testcontainers.containers.ContainerState
*/
BrowserWebDriverContainer getContainer() {
return webDriverContainer
}
@Shared
static BrowserWebDriverContainer container

/**
* Returns the hostname that the server under test is available on from the host.
* <p>This is useful when using any of the {@code download*()} methods as they will connect from the host,
* and not from within the container.
* <p>Defaults to {@code localhost}. If the value returned by {@code webDriverContainer.getHost()}
* is different from the default, this method will return the same value same as {@code webDriverContainer.getHost()}.
*
* @return the hostname for accessing the server under test from the host
* The reporter that Geb should use when reporting is enabled.
*/
@Override
String getHostNameFromHost() {
return hostNameChanged ? webDriverContainer.host : DEFAULT_HOSTNAME_FROM_HOST
}

private boolean isHostNameChanged() {
return webDriverContainer.host != ContainerGebConfiguration.DEFAULT_HOSTNAME_FROM_CONTAINER
Reporter createReporter() {
new CompositeReporter(new PageSourceReporter())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,21 @@ import groovy.transform.CompileStatic
import org.spockframework.runtime.model.IterationInfo
import org.testcontainers.lifecycle.TestDescription

import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

/**
* Implements {@link org.testcontainers.lifecycle.TestDescription} to customize recording names.
*
* @see org.testcontainers.lifecycle.TestDescription
* @author James Daugherty
* @since 5.0
* @since 4.1
*/
@CompileStatic
class ContainerGebTestDescription implements TestDescription {

String testId
String filesystemFriendlyName

ContainerGebTestDescription(IterationInfo testInfo, LocalDateTime runDate) {
ContainerGebTestDescription(IterationInfo testInfo) {
testId = testInfo.displayName
String safeName = testId.replaceAll('\\W+', '_')
filesystemFriendlyName = "${DateTimeFormatter.ofPattern('yyyyMMdd_HHmmss').format(runDate)}_${safeName}"
filesystemFriendlyName = safeName
}
}
Loading
Loading