Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ class GrailsGebSettings {

private static VncRecordingMode DEFAULT_RECORDING_MODE = VncRecordingMode.SKIP
private static VncRecordingFormat DEFAULT_RECORDING_FORMAT = VncRecordingFormat.MP4
private static int DEFAULT_TIMEOUT_IMPLICITLY_WAIT = 0
private static int DEFAULT_TIMEOUT_PAGE_LOAD = 300
private static int DEFAULT_TIMEOUT_SCRIPT = 30
public static int DEFAULT_TIMEOUT_IMPLICITLY_WAIT = 0
public static int DEFAULT_TIMEOUT_PAGE_LOAD = 300
public static int DEFAULT_TIMEOUT_SCRIPT = 30

String tracingEnabled
String recordingDirectoryName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ package grails.plugin.geb
import com.github.dockerjava.api.model.ContainerNetwork
import geb.Browser
import geb.Configuration
import geb.ConfigurationLoader
import geb.driver.CallbackDriverFactory
import geb.driver.DefaultDriverFactory
import geb.driver.DriverCreationException
import geb.driver.NameBasedDriverFactory
import geb.spock.SpockGebTestManagerBuilder
import geb.test.GebTestManager
import grails.plugin.geb.serviceloader.ServiceRegistry
Expand Down Expand Up @@ -110,50 +115,71 @@ class WebDriverContainerHolder {
grailsGebSettings.recordingFormat
)

Map prefs = [
"credentials_enable_service" : false,
"profile.password_manager_enabled" : false,
"profile.password_manager_leak_detection": false
]

ChromeOptions chromeOptions = new ChromeOptions()
// TODO: guest would be preferred, but this causes issues with downloads
// see https://issues.chromium.org/issues/42323769
// chromeOptions.addArguments("--guest")
chromeOptions.setExperimentalOption("prefs", prefs)

currentContainer.tap {
withEnv('SE_ENABLE_TRACING', grailsGebSettings.tracingEnabled)
withAccessToHost(true)
withImagePullPolicy(PullPolicy.ageBased(Duration.of(1, ChronoUnit.DAYS)))
withCapabilities(chromeOptions)
start()
}
if (hostnameChanged) {
currentContainer.execInContainer('/bin/sh', '-c', "echo '$hostIp\t${currentConfiguration.hostName}' | sudo tee -a /etc/hosts")
}

ConfigObject configObject = new ConfigObject()
// same as empty Browser constructor would do
Configuration gebConfig = new ConfigurationLoader().conf
// Ensure driver points to re-initialized container with correct host
// Driver is explicitly quit by us in stop() method to fulfill our resulting responsibility
gebConfig.setCacheDriver(false)
// "If driver caching is disabled then this setting defaults to true" - we override to false
gebConfig.quitDriverOnBrowserReset = false
gebConfig.baseUrl = currentContainer.seleniumAddress.toString()
if (currentConfiguration.reporting) {
configObject.reportsDir = grailsGebSettings.reportingDirectory
configObject.reporter = (invocation.sharedInstance as ContainerGebSpec).createReporter()
gebConfig.reportsDir = grailsGebSettings.reportingDirectory
gebConfig.reporter = (invocation.sharedInstance as ContainerGebSpec).createReporter()
}
if (currentConfiguration.fileDetector != NullContainerFileDetector) {
ServiceRegistry.setInstance(ContainerFileDetector, currentConfiguration.fileDetector)

if (gebConfig.getDriverConf() instanceof RemoteWebDriver) {
// similar to Browser#getDriverConf's Exception
throw new IllegalStateException(
"The 'driver' config value is an instance of RemoteWebDriver. " +
"You need to wrap the driver instance in a closure."
)
}
if (gebConfig.getDriverConf() == null) {
// If no driver was set in GebConfig.groovy, this makes it not use DefaultDriverFactory
// (which would lead to local browser)
gebConfig.setDriverConf(/* Closure: */ {
log.info("Using default Chrome RemoteWebDriver for {}", currentContainer.seleniumAddress)
new RemoteWebDriver(currentContainer.seleniumAddress, new ChromeOptions())
})
}

currentBrowser = new Browser(new Configuration(configObject, new Properties(), null, null))
// If GebConfig instantiates a RemoteWebDriver without it's remoteAddress constructor, this is what it uses:
String oldDefault = System.getProperty("webdriver.remote.server", null)
System.setProperty("webdriver.remote.server", currentContainer.seleniumAddress.toString())
gebConfig.driver // implicitly calls createDriver because it's null
if (oldDefault == null) {
System.clearProperty("webdriver.remote.server")
} else {
System.setProperty("webdriver.remote.server", oldDefault)
}

WebDriver driver = new RemoteWebDriver(currentContainer.seleniumAddress, chromeOptions)
ContainerFileDetector fileDetector = ServiceRegistry.getInstance(ContainerFileDetector, DefaultContainerFileDetector)
((RemoteWebDriver) driver).setFileDetector(fileDetector)
driver.manage().timeouts().with {
implicitlyWait(Duration.ofSeconds(grailsGebSettings.implicitlyWait))
pageLoadTimeout(Duration.ofSeconds(grailsGebSettings.pageLoadTimeout))
scriptTimeout(Duration.ofSeconds(grailsGebSettings.scriptTimeout))
}
currentBrowser = new Browser(gebConfig)

currentBrowser.driver = driver
if (currentConfiguration.fileDetector != NullContainerFileDetector) {
ServiceRegistry.setInstance(ContainerFileDetector, currentConfiguration.fileDetector)
}
ContainerFileDetector fileDetector = ServiceRegistry.getInstance(ContainerFileDetector, DefaultContainerFileDetector)
((RemoteWebDriver) currentBrowser.driver).setFileDetector(fileDetector)

System.out.println(currentBrowser.driver.manage().timeouts())
// Overwrite GebConfig's values if current ContainerGebConfiguration (which may differ per test) has different timeouts
if (grailsGebSettings.implicitlyWait != GrailsGebSettings.DEFAULT_TIMEOUT_IMPLICITLY_WAIT)
currentBrowser.driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(grailsGebSettings.implicitlyWait))
if (grailsGebSettings.pageLoadTimeout != GrailsGebSettings.DEFAULT_TIMEOUT_PAGE_LOAD)
currentBrowser.driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(grailsGebSettings.pageLoadTimeout))
if (grailsGebSettings.scriptTimeout != GrailsGebSettings.DEFAULT_TIMEOUT_SCRIPT)
currentBrowser.driver.manage().timeouts().scriptTimeout(Duration.ofSeconds(grailsGebSettings.scriptTimeout))

// There's a bit of a chicken and egg problem here: the container & browser are initialized when
// the static/shared fields are initialized, which is before the grails server has started so the
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.demo.spock

import grails.plugin.geb.ContainerGebSpec
import grails.testing.mixin.integration.Integration
import org.openqa.selenium.remote.RemoteWebDriver

/**
* Test spec to verify that our custom GebConfig.groovy driver configuration
* is being used instead of the default WebDriverContainerHolder configuration.
*/
@Integration
class GebConfigSpec extends ContainerGebSpec {

void 'should use custom RemoteWebDriver from GebConfig.groovy'() {
when: 'accessing the driver'
def driver = browser.driver

then: 'the driver should be a RemoteWebDriver'
driver instanceof RemoteWebDriver

and: 'the driver should have our custom capability indicating GebConfig was used'
def capabilities = ((RemoteWebDriver) driver).capabilities
capabilities.getCapability("grails:gebConfigUsed") == true

and: 'the driver should have Chrome-specific capabilities'
capabilities.getBrowserName() == "chrome"

and: 'the driver should be using our custom configuration'
// The custom capability we set proves our GebConfig.groovy was used
capabilities.getCapability("grails:gebConfigUsed") == true
}

void 'should verify our GebConfig driver configuration is active'() {
when: 'navigating to a page'
go('/')

then: 'the page loads successfully'
title == 'Welcome to Grails'

and: 'the driver capabilities should reflect our configuration'
def driver = browser.driver as RemoteWebDriver
def capabilities = driver.capabilities

// Verify our custom marker capability that proves GebConfig.groovy was used
capabilities.getCapability("grails:gebConfigUsed") == true

and: 'the driver should be Chrome with our configuration'
capabilities.getBrowserName() == "chrome"

and: 'the driver should be a RemoteWebDriver as configured in GebConfig'
driver instanceof RemoteWebDriver

and: 'the session should be active'
driver.sessionId != null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2025 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.
*/


import geb.report.ReportState
import geb.report.Reporter
import org.openqa.selenium.chrome.ChromeOptions
import org.openqa.selenium.remote.RemoteWebDriver
import geb.report.ReportingListener

// Configuration for container-based Geb testing
// This driver configuration will be used by WebDriverContainerHolder

driver = {
// Chrome preferences to disable password manager and credentials service
Map prefs = [
"credentials_enable_service" : false,
"profile.password_manager_enabled" : false,
"profile.password_manager_leak_detection": false
]

ChromeOptions chromeOptions = new ChromeOptions()
// TO DO: guest would be preferred, but this causes issues with downloads
// see https://issues.chromium.org/issues/42323769
// chromeOptions.addArguments("--guest")
chromeOptions.setExperimentalOption("prefs", prefs)

// Add a custom capability that we can test for to verify our configuration is being used
chromeOptions.setCapability("grails:gebConfigUsed", true)

// The remote address will be set by WebDriverContainerHolder via system property
// webdriver.remote.server before this closure is called
new RemoteWebDriver(chromeOptions)
}

// Another proof that GebConfig.groovy is being utilized, next to GebConfigSpec
reportingListener = new ReportingListener() {
void onReport(Reporter reporter, ReportState reportState, List<File> reportFiles) {
reportFiles.each {
println "[[ATTACHMENT|$it.absolutePath]]"
}
}
}
Loading