Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
41cb6bb
failing test
JonasPammer Feb 21, 2025
2352d27
first try at ServiceLoader impl.
JonasPammer Feb 21, 2025
10b0d43
chore: intellij
JonasPammer Feb 21, 2025
29271bb
docs: much friendlier and easier english
JonasPammer Feb 22, 2025
56c074f
refactor: factory pattern
JonasPammer Feb 22, 2025
ac29ef2
chore: keep the META-INF for showcase purpose?
JonasPammer Feb 22, 2025
edd4410
chore: make custom implementation clear
JonasPammer Feb 22, 2025
f96e0ca
docs: document factory
JonasPammer Feb 22, 2025
019b3a2
docs: missing `
JonasPammer Feb 22, 2025
4ab0034
chore: javadoc
JonasPammer Feb 22, 2025
d40e279
suggestion: also add ContainerGebConfiguration ability
JonasPammer Feb 22, 2025
74e99de
code review comments
JonasPammer Feb 25, 2025
1e9d3aa
chore: update year in license header
JonasPammer Feb 25, 2025
a1e1df1
feat: allow implementation to access errorInfo
JonasPammer Feb 25, 2025
1688a72
refactor: ensure getter of implementation is used, to open up mocking…
JonasPammer Feb 25, 2025
51e159b
chore: code review <3
JonasPammer Feb 25, 2025
4dca4dc
chore: code review
JonasPammer Feb 25, 2025
ee8e02b
code review alterations
JonasPammer Feb 26, 2025
3e57616
code review alterations
JonasPammer Feb 27, 2025
f68e603
move *Loader to grails.geb.serviceloader. as per https://github.com/g…
JonasPammer Feb 27, 2025
d97443f
dropped https://github.com/grails/geb/pull/143/commits/058d3b22b4522f…
JonasPammer Feb 27, 2025
41b4590
oki
JonasPammer Feb 27, 2025
20298a2
refactor to last PRs
JonasPammer Apr 20, 2025
f2b0a6b
rebase fixes
JonasPammer Apr 20, 2025
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
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,19 @@ By default, no test recording will be performed. Here are the system properties
* possible values are `FLV` or `MP4`
* defaults to `MP4`

To customize the naming convention for recording files:

1. Create a class that implements [`ContainerGebTestDescription`](./src/testFixtures/groovy/grails/plugin/geb/ContainerGebTestDescription.groovy)
and specify its fully qualified class name in a `META-INF/services/grails.plugin.geb.ContainerGebTestDescription` file
on the classpath (e.g., `src/integration-test/resources`).
2. Use the `ContainerGebConfiguration` annotation and set its `testDescription` property to your implementation class.
3. Call [`ServiceRegistry.setInstance()`](./src/testFixtures/groovy/grails/plugin/geb/serviceloader/ServiceRegistry.groovy)
in a Spock `setupSpec` method to apply your implementation class,
optionally using a `cleanupSpec` to limiting it to one class.

You may extend the default implementation, [`DefaultContainerGebTestDescription`](./src/testFixtures/groovy/grails/plugin/geb/DefaultContainerGebTestDescription.groovy), as a base for your custom implementation.
To modify the naming convention, override at least the `getTestId()` method.

#### Uploads

Uploading a file is more complicated for Remote WebDriver sessions because the file you want to upload
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.demo.spock

import grails.plugin.geb.ContainerGebConfiguration
import grails.plugin.geb.ContainerGebSpec
import grails.testing.mixin.integration.Integration

@Integration
@ContainerGebConfiguration(testDescription = ContainerGebTestDescriptionImpl)
class ContainerGebTestDescriptionAnnotationSpec extends ContainerGebSpec {

void 'should not raise a FileSystemException on recording save by loading custom implementation of ContainerGebTestDescription'(String data_driven_testing_param_1) {
when:
go '/'

then:
true

where:
data_driven_testing_param_1 << ['very long text lorem ipsum dolor sit amet lorem ipsum dolor sit amet lorem ipsum dolor sit amet Liskov Substitution']
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.demo.spock

import grails.plugin.geb.DefaultContainerGebTestDescription
import groovy.transform.CompileStatic

@CompileStatic
class ContainerGebTestDescriptionImpl extends DefaultContainerGebTestDescription {

@Override
String getTestId() {
return "Custom_${getIterationInfo().feature.spec.displayName}"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.demo.spock

import grails.plugin.geb.ContainerGebSpec
import grails.plugin.geb.ContainerGebTestDescription
import grails.plugin.geb.serviceloader.ServiceRegistry
import grails.testing.mixin.integration.Integration

@Integration
class ContainerGebTestDescriptionSpec extends ContainerGebSpec {

def setupSpec(){
ServiceRegistry.setInstance(ContainerGebTestDescription, new ContainerGebTestDescriptionImpl())
}

def cleanupSpec(){
ServiceRegistry.setInstance(ContainerGebTestDescription, null)
}

/**
* "The filename, directory name, or volume label syntax is incorrect" (too long)
*/
void 'should not raise a FileSystemException on recording save by loading custom implementation of ContainerGebTestDescription'(String data_driven_testing_param_1, String param_2, int param_3) {
when:
go '/'

then:
true

where:
data_driven_testing_param_1 << ['very long text lorem ipsum dolor sit amet lorem ipsum dolor sit amet lorem ipsum dolor sit amet Liskov Substitution']
param_2 << ['If an implementation is hard to explain, it is an established design pattern.']
param_3 << Integer.MAX_VALUE
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import java.lang.annotation.Target
static final String DEFAULT_HOSTNAME_FROM_CONTAINER = GenericContainer.INTERNAL_HOST_HOSTNAME
static final String DEFAULT_PROTOCOL = 'http'
static final Class<? extends ContainerFileDetector> DEFAULT_FILE_DETECTOR = DefaultContainerFileDetector
static final Class<? extends ContainerGebTestDescription> DEFAULT_TEST_DESCRIPTION = DefaultContainerGebTestDescription

/**
* The protocol that the container's browser will use to access the server under test.
Expand Down Expand Up @@ -64,6 +65,17 @@ import java.lang.annotation.Target
* @see grails.plugin.geb.UselessContainerFileDetector UselessContainerFileDetector
*/
Class<? extends ContainerFileDetector> fileDetector() default DefaultContainerFileDetector

/**
* The {@link org.testcontainers.lifecycle.TestDescription} implementation to use for this class.
* <p> {@link NullContainerGebTestDescription} results in the
* {@link grails.plugin.geb.serviceloader.ServiceRegistry last set} instance being used.
*
* @since 4.2
* @see grails.plugin.geb.DefaultContainerGebTestDescription DefaultContainerGebTestDescription
* @see grails.plugin.geb.UselessContainerFileDetector UselessContainerFileDetector
*/
Class<? extends ContainerGebTestDescription> testDescription() default DefaultContainerGebTestDescription
}

/**
Expand All @@ -88,4 +100,8 @@ interface IContainerGebConfiguration {
default Class<? extends ContainerFileDetector> fileDetector() {
ContainerGebConfiguration.DEFAULT_FILE_DETECTOR
}

default Class<? extends ContainerGebTestDescription> testDescription() {
ContainerGebConfiguration.DEFAULT_TEST_DESCRIPTION
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024 original author or authors
* Copyright 2024-2025 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.
Expand All @@ -16,30 +16,44 @@
package grails.plugin.geb

import groovy.transform.CompileStatic
import org.spockframework.runtime.model.ErrorInfo
import org.spockframework.runtime.model.IterationInfo
import org.testcontainers.lifecycle.TestDescription

/**
* Implements {@link org.testcontainers.lifecycle.TestDescription} to customize recording names.
* An extension of {@link org.testcontainers.lifecycle.TestDescription}
* that allows customization of recording file names.
* <p>
* Implementations must provide a zero-argument constructor to ensure compatibility with {@link java.util.ServiceLoader}.
*
* @author James Daugherty
* @since 4.1
* @since 4.2
* @see GebRecordingTestListener
*/
@CompileStatic
class ContainerGebTestDescription implements TestDescription {
interface ContainerGebTestDescription extends TestDescription {

String testId
String filesystemFriendlyName
IterationInfo getIterationInfo()

/**
* Will be set to value of
* {@link org.spockframework.runtime.IRunListener#afterIteration(org.spockframework.runtime.model.IterationInfo)}
*/
void setIterationInfo(IterationInfo iterationInfo)

ContainerGebTestDescription(IterationInfo testInfo) {
testId = [
testInfo.feature.spec.displayName,
testInfo.feature.displayName,
testInfo.displayName != testInfo.feature.displayName ? testInfo.displayName : null,
testInfo.displayName != testInfo.feature.displayName ? testInfo.iterationIndex : null
].findAll(/* Remove nulls */).join(' ')

String safeName = testId.replaceAll('\\W+', '_')
filesystemFriendlyName = safeName
}
/**
* Will be used by
* {@link org.testcontainers.containers.BrowserWebDriverContainer#afterTest(org.testcontainers.lifecycle.TestDescription, java.util.Optional)}
*
* You could overwrite {@link ErrorInfo#getException()} to mimic that an Exception occurred, resulting in a recording file
* if {@link org.testcontainers.containers.BrowserWebDriverContainer#recordingMode} is RECORD_FAILING.
*
* @return null or the last value set by {@link #setErrorInfo(org.spockframework.runtime.model.ErrorInfo)}
*/
ErrorInfo getErrorInfo()

/**
* Will be set to value of
* {@link org.spockframework.runtime.IRunListener#error(org.spockframework.runtime.model.ErrorInfo)}
*/
void setErrorInfo(ErrorInfo errorInfo)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 2024-2025 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.
*/
package grails.plugin.geb

import groovy.transform.CompileStatic
import org.spockframework.runtime.model.ErrorInfo
import org.spockframework.runtime.model.IterationInfo

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

IterationInfo iterationInfo
ErrorInfo errorInfo

@Override
String getTestId() {
return [
iterationInfo.feature.spec.displayName,
iterationInfo.feature.displayName,
iterationInfo.displayName != iterationInfo.feature.displayName ? iterationInfo.displayName : null,
iterationInfo.displayName != iterationInfo.feature.displayName ? iterationInfo.iterationIndex : null
].findAll(/* Remove nulls */).join(' ')
}

@Override
String getFilesystemFriendlyName() {
String safeName = getTestId().replaceAll('\\W+', '_')
return safeName
}
}

/**
* Used by {@link ContainerGebConfiguration#testDescription()} interface to represent a null value.
*/
class NullContainerGebTestDescription extends DefaultContainerGebTestDescription {}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024 original author or authors
* Copyright 2024-2025 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.
Expand All @@ -15,6 +15,8 @@
*/
package grails.plugin.geb


import grails.plugin.geb.serviceloader.ServiceRegistry
import groovy.transform.CompileStatic
import org.spockframework.runtime.AbstractRunListener
import org.spockframework.runtime.model.ErrorInfo
Expand All @@ -41,10 +43,12 @@ class GebRecordingTestListener extends AbstractRunListener {

@Override
void afterIteration(IterationInfo iteration) {
containerHolder.currentContainer.afterTest(
new ContainerGebTestDescription(iteration),
Optional.ofNullable(errorInfo?.exception)
)
ContainerGebTestDescription testContainerDescription = ServiceRegistry.getInstance(ContainerGebTestDescription, ContainerGebConfiguration.DEFAULT_TEST_DESCRIPTION)
testContainerDescription.errorInfo = errorInfo
testContainerDescription.iterationInfo = iteration

containerHolder.currentContainer.afterTest(testContainerDescription,
Optional.ofNullable(testContainerDescription.errorInfo?.exception))
errorInfo = null
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ class WebDriverContainerHolder {
if (currentConfiguration.fileDetector != NullContainerFileDetector) {
ServiceRegistry.setInstance(ContainerFileDetector, currentConfiguration.fileDetector)
}
if (currentConfiguration.testDescription != NullContainerGebTestDescription) {
ServiceRegistry.setInstance(ContainerGebTestDescription, currentConfiguration.testDescription)
}

currentBrowser = new Browser(new Configuration(configObject, new Properties(), null, null))

Expand Down Expand Up @@ -210,6 +213,7 @@ class WebDriverContainerHolder {
String hostName
boolean reporting
Class<? extends ContainerFileDetector> fileDetector
Class<? extends ContainerGebTestDescription> testDescription

WebDriverContainerConfiguration(SpecInfo spec) {
ContainerGebConfiguration configuration
Expand All @@ -228,6 +232,7 @@ class WebDriverContainerHolder {
hostName = configuration?.hostName() ?: ContainerGebConfiguration.DEFAULT_HOSTNAME_FROM_CONTAINER
reporting = configuration?.reporting() ?: false
fileDetector = configuration?.fileDetector() ?: ContainerGebConfiguration.DEFAULT_FILE_DETECTOR
testDescription = configuration?.testDescription() ?: ContainerGebConfiguration.DEFAULT_TEST_DESCRIPTION
}
}
}
Expand Down
Loading