This is an automated email from the ASF dual-hosted git repository. jonnybot pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/groovy-geb.git
commit d1a35ca7048dc5bd35b647a39cc01215dc6d4390 Author: Jonny Carter <[email protected]> AuthorDate: Tue Mar 24 14:31:05 2026 -0500 Use annotation-driven configuration --- .../ContainerFileDetectorAnnotationSpec.groovy | 8 +--- .../HostNameConfigurationSpec.groovy | 6 +-- .../groovy/geb/testcontainers/RootPageSpec.groovy | 6 +-- .../ContainerGebConfiguration.groovy | 51 ++++++++++++---------- .../geb/testcontainers/ContainerGebSpec.groovy | 50 +++++++++++++++++++-- .../testcontainers/WebDriverContainerHolder.groovy | 25 +++++++---- 6 files changed, 94 insertions(+), 52 deletions(-) diff --git a/integration/geb-testcontainers/src/integration-test/groovy/geb/testcontainers/ContainerFileDetectorAnnotationSpec.groovy b/integration/geb-testcontainers/src/integration-test/groovy/geb/testcontainers/ContainerFileDetectorAnnotationSpec.groovy index 881e3c1b..040ef620 100644 --- a/integration/geb-testcontainers/src/integration-test/groovy/geb/testcontainers/ContainerFileDetectorAnnotationSpec.groovy +++ b/integration/geb-testcontainers/src/integration-test/groovy/geb/testcontainers/ContainerFileDetectorAnnotationSpec.groovy @@ -25,14 +25,10 @@ import org.openqa.selenium.WebDriverException * Altered copy of {@link ContainerFileDetectorDefaultSpec} * that throws {@link org.openqa.selenium.InvalidArgumentException} */ +@ContainerGebConfiguration(fileDetector = UselessContainerFileDetector) class ContainerFileDetectorAnnotationSpec extends ContainerGebSpecWithServer { - @Override - Class<? extends ContainerFileDetector> fileDetector() { - UselessContainerFileDetector - } - - def "should fail to find file with fileDetector changed to UselessContainerFileDetector via interface"() { + def "should fail to find file with fileDetector changed to UselessContainerFileDetector via annotation"() { given: def uploadPage = to UploadPage diff --git a/integration/geb-testcontainers/src/integration-test/groovy/geb/testcontainers/HostNameConfigurationSpec.groovy b/integration/geb-testcontainers/src/integration-test/groovy/geb/testcontainers/HostNameConfigurationSpec.groovy index 9e6fae98..a2029a4b 100644 --- a/integration/geb-testcontainers/src/integration-test/groovy/geb/testcontainers/HostNameConfigurationSpec.groovy +++ b/integration/geb-testcontainers/src/integration-test/groovy/geb/testcontainers/HostNameConfigurationSpec.groovy @@ -26,13 +26,9 @@ import spock.lang.Title * for more instructions on how to write functional tests with Geb. */ @Title("host name configuration test") +@ContainerGebConfiguration(hostName = 'testing.example.com') class HostNameConfigurationSpec extends ContainerGebSpecWithServer { - @Override - String hostName() { - 'testing.example.com' - } - def "should show the right server name when visiting home page"() { when: "visiting the hpme page with a configured host name" to(HomePage) diff --git a/integration/geb-testcontainers/src/integration-test/groovy/geb/testcontainers/RootPageSpec.groovy b/integration/geb-testcontainers/src/integration-test/groovy/geb/testcontainers/RootPageSpec.groovy index 9789242a..aa599443 100644 --- a/integration/geb-testcontainers/src/integration-test/groovy/geb/testcontainers/RootPageSpec.groovy +++ b/integration/geb-testcontainers/src/integration-test/groovy/geb/testcontainers/RootPageSpec.groovy @@ -27,13 +27,9 @@ import geb.testcontainers.pages.HomePage * See https://groovy.apache.org/geb/manual/current/ * for more instructions on how to write functional tests with Geb. */ +@ContainerGebConfiguration(reporting = true) class RootPageSpec extends ContainerGebSpecWithServer { - @Override - boolean reporting() { - true - } - @Override Reporter createReporter() { // Override the default reporter to demonstrate how this can be customized diff --git a/integration/geb-testcontainers/src/main/groovy/geb/testcontainers/ContainerGebConfiguration.groovy b/integration/geb-testcontainers/src/main/groovy/geb/testcontainers/ContainerGebConfiguration.groovy index b5f6b985..ac19b2af 100644 --- a/integration/geb-testcontainers/src/main/groovy/geb/testcontainers/ContainerGebConfiguration.groovy +++ b/integration/geb-testcontainers/src/main/groovy/geb/testcontainers/ContainerGebConfiguration.groovy @@ -20,53 +20,58 @@ package geb.testcontainers import org.testcontainers.containers.GenericContainer +import java.lang.annotation.ElementType +import java.lang.annotation.Inherited +import java.lang.annotation.Retention +import java.lang.annotation.RetentionPolicy +import java.lang.annotation.Target + /** - * Implement this interface on a {@link ContainerGebSpec} subclass to configure the - * protocol, hostname, reporting, and file detector for container-based browser tests. + * Annotation to configure the protocol, hostname, reporting, and file detector + * for container-based browser tests. Apply to a {@link ContainerGebSpec} subclass. + * + * <p>Values set here override the defaults from {@link ContainerGebSpec}. + * This annotation is {@link Inherited}, so subclasses inherit their parent's configuration. + * Subclasses can re-apply the annotation to override specific values. * - * <p>Configuration is inherited by subclasses, and can be overridden by re-implementing - * the desired methods. + * <p>For configuration that requires dynamic values or complex logic, override + * the corresponding methods on {@link ContainerGebSpec} directly instead. * * <p>Example: * <pre><code> + * @ContainerGebConfiguration(hostName = 'testing.example.com', reporting = true) * class MySpec extends ContainerGebSpec { - * @Override - * String hostName() { 'testing.example.com' } - * - * @Override - * boolean reporting() { true } + * // ... * } * </code></pre> * * @author James Daugherty * @since 4.1 */ -interface ContainerGebConfiguration { +@Inherited +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@interface ContainerGebConfiguration { + + static final String DEFAULT_HOSTNAME_FROM_CONTAINER = GenericContainer.INTERNAL_HOST_HOSTNAME + static final String DEFAULT_PROTOCOL = 'http' /** * The protocol that the container's browser will use to access the server under test. * <p>Defaults to {@code http}. */ - default String protocol() { - 'http' - } + String protocol() default DEFAULT_PROTOCOL /** * The hostname that the container's browser will use to access the server under test. * <p>Defaults to {@code host.testcontainers.internal}. - * <p>This is useful when the server under test needs to be accessed with a certain hostname. */ - default String hostName() { - GenericContainer.INTERNAL_HOST_HOSTNAME - } + String hostName() default DEFAULT_HOSTNAME_FROM_CONTAINER /** * Whether reporting should be enabled for this test. - * Add a {@code GebConfig.groovy} to customize the reporter configuration. */ - default boolean reporting() { - false - } + boolean reporting() default false /** * The {@link ContainerFileDetector} implementation to use for this class. @@ -75,7 +80,5 @@ interface ContainerGebConfiguration { * @see DefaultContainerFileDetector * @see UselessContainerFileDetector */ - default Class<? extends ContainerFileDetector> fileDetector() { - DefaultContainerFileDetector - } + Class<? extends ContainerFileDetector> fileDetector() default DefaultContainerFileDetector } diff --git a/integration/geb-testcontainers/src/main/groovy/geb/testcontainers/ContainerGebSpec.groovy b/integration/geb-testcontainers/src/main/groovy/geb/testcontainers/ContainerGebSpec.groovy index 79e0759a..68d6b638 100644 --- a/integration/geb-testcontainers/src/main/groovy/geb/testcontainers/ContainerGebSpec.groovy +++ b/integration/geb-testcontainers/src/main/groovy/geb/testcontainers/ContainerGebSpec.groovy @@ -27,6 +27,7 @@ import geb.test.GebTestManager import geb.transform.DynamicallyDispatchesToBrowser import geb.testcontainers.support.ContainerGebFileInputSource import org.testcontainers.containers.BrowserWebDriverContainer +import org.testcontainers.containers.GenericContainer import org.testcontainers.images.builder.Transferable import spock.lang.Shared import spock.lang.Specification @@ -43,7 +44,14 @@ import spock.lang.Specification * </li> * </ul> * - * @see ContainerGebConfiguration for how to customize the container's connection information + * <p>Configuration can be customized in two ways: + * <ul> + * <li>Apply {@link ContainerGebConfiguration @ContainerGebConfiguration} to set values declaratively.</li> + * <li>Override methods ({@link #protocol()}, {@link #hostName()}, {@link #reporting()}, + * {@link #fileDetector()}) for dynamic or inheritable configuration.</li> + * </ul> + * + * @see ContainerGebConfiguration * * @author Søren Berg Glasius * @author Mattias Reichel @@ -51,7 +59,7 @@ import spock.lang.Specification * @since 4.1 */ @DynamicallyDispatchesToBrowser -abstract class ContainerGebSpec extends Specification implements ContainerGebConfiguration { +abstract class ContainerGebSpec extends Specification { @Shared WebDriverContainerHolder holder @@ -92,7 +100,6 @@ abstract class ContainerGebSpec extends Specification implements ContainerGebCon /** * Copies a file from the host to the container for assignment to a Geb FileInput module. - * This method is useful when you need to upload a file to a form in a Geb test and will work cross-platform. * * @param hostPath relative path to the file on the host * @param containerPath absolute path to where to put the file in the container @@ -103,4 +110,41 @@ abstract class ContainerGebSpec extends Specification implements ContainerGebCon container.copyFileToContainer(Transferable.of(new File(hostPath).bytes), containerPath) new ContainerGebFileInputSource(containerPath) } + + // --- Configuration defaults (overridable by subclasses or @ContainerGebConfiguration) --- + + /** + * The protocol that the container's browser will use to access the server under test. + * <p>Defaults to {@code http}. Can also be set via {@link ContainerGebConfiguration#protocol()}. + */ + String protocol() { + ContainerGebConfiguration.DEFAULT_PROTOCOL + } + + /** + * The hostname that the container's browser will use to access the server under test. + * <p>Defaults to {@code host.testcontainers.internal}. + * Can also be set via {@link ContainerGebConfiguration#hostName()}. + */ + String hostName() { + ContainerGebConfiguration.DEFAULT_HOSTNAME_FROM_CONTAINER + } + + /** + * Whether reporting should be enabled for this test. + * Can also be set via {@link ContainerGebConfiguration#reporting()}. + */ + boolean reporting() { + false + } + + /** + * The {@link ContainerFileDetector} implementation to use for this class. + * Can also be set via {@link ContainerGebConfiguration#fileDetector()}. + * + * @since 4.2 + */ + Class<? extends ContainerFileDetector> fileDetector() { + DefaultContainerFileDetector + } } diff --git a/integration/geb-testcontainers/src/main/groovy/geb/testcontainers/WebDriverContainerHolder.groovy b/integration/geb-testcontainers/src/main/groovy/geb/testcontainers/WebDriverContainerHolder.groovy index 34335c06..42b5fb1b 100644 --- a/integration/geb-testcontainers/src/main/groovy/geb/testcontainers/WebDriverContainerHolder.groovy +++ b/integration/geb-testcontainers/src/main/groovy/geb/testcontainers/WebDriverContainerHolder.groovy @@ -431,16 +431,23 @@ class WebDriverContainerHolder { Class<? extends ContainerFileDetector> fileDetector SpecContainerConfiguration(SpecInfo spec) { - ContainerGebConfiguration conf = null - - if (ContainerGebConfiguration.isAssignableFrom(spec.reflection)) { - conf = spec.reflection.getConstructor().newInstance() as ContainerGebConfiguration + // Check for @ContainerGebConfiguration annotation first (including inherited) + def annotation = spec.reflection.getAnnotation(ContainerGebConfiguration) + + if (annotation) { + // Annotation values take priority + protocol = annotation.protocol() + hostName = annotation.hostName() + reporting = annotation.reporting() + fileDetector = annotation.fileDetector() + } else { + // Fall back to instance methods on the spec (overridable defaults from ContainerGebSpec) + def instance = spec.reflection.getConstructor().newInstance() as ContainerGebSpec + protocol = instance.protocol() + hostName = instance.hostName() + reporting = instance.reporting() + fileDetector = instance.fileDetector() } - - protocol = conf?.protocol() ?: DEFAULT_PROTOCOL - hostName = conf?.hostName() ?: DEFAULT_HOSTNAME - reporting = conf?.reporting() ?: false - fileDetector = conf?.fileDetector() ?: DEFAULT_FILE_DETECTOR } } }
