Skip to content

Commit daefce9

Browse files
JonasPammerJonasPammerWorkmatrei
authored
feat(grails-geb): Read GebConfig.groovy to allow overriding RemoteWebDriver (#14987)
* refactor(grails-geb): Read GebConfig.groovy to allow overriding RemoteWebDriver, Close #14801 I hereby lived under the assumption that this does not need to be as dynamic as the other grails-geb/connection settings (which can be defined per Class using Annotation/Interface), and just needs to be project-configureable. Though that may raise the question of some users why FileDetector was made configurable in this way too. This now should also open up all other gebconfig functionality. Holding out with writing Documentation until feedback on idea's was provided. Maybe I also did something in an geb-unwanted way. _Also possibly currently trying to make a diagram to better wrap my head around grails-geb's moving parts and necessities for future encounters._ <details> <summary>Unrelevant Thoughts</summary> I had originally tried to allow the use of normal Drivers and then extracting their capabilities, but as far as I was able to jungle through this is not possible. One option there would've been to intercept e.g. ChromeOptions construction when calling closure, or another hacking interception way, but i chose not to. I also had the idea to introduce a new GebConfig key, like other *-geb extensions do (e.g. grailsGeb, ). </details> * test(geb): extract `GebConfig` testing to separate project Do a separate project for testing using default `GebConfig` file so we don't pollute the other tests. * refactor: prefer `.with` over `.tap` `.tap` returns the receiver, which suggests fluent chaining. Since the return value is unused, use `.with`. Behavior is unchanged. * chore: cleanup * fix: add back options to default chrome driver --------- Co-authored-by: JonasPammer <[email protected]> Co-authored-by: Mattias Reichel <[email protected]> Co-authored-by: Mattias Reichel <[email protected]>
1 parent 7ab10e8 commit daefce9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+3053
-33
lines changed

grails-geb/src/testFixtures/groovy/grails/plugin/geb/GrailsGebSettings.groovy

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ class GrailsGebSettings {
4040

4141
private static VncRecordingMode DEFAULT_RECORDING_MODE = VncRecordingMode.SKIP
4242
private static VncRecordingFormat DEFAULT_RECORDING_FORMAT = VncRecordingFormat.MP4
43-
private static int DEFAULT_TIMEOUT_IMPLICITLY_WAIT = 0
44-
private static int DEFAULT_TIMEOUT_PAGE_LOAD = 300
45-
private static int DEFAULT_TIMEOUT_SCRIPT = 30
43+
public static int DEFAULT_TIMEOUT_IMPLICITLY_WAIT = 0
44+
public static int DEFAULT_TIMEOUT_PAGE_LOAD = 300
45+
public static int DEFAULT_TIMEOUT_SCRIPT = 30
4646

4747
String tracingEnabled
4848
String recordingDirectoryName

grails-geb/src/testFixtures/groovy/grails/plugin/geb/WebDriverContainerHolder.groovy

Lines changed: 62 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ import groovy.util.logging.Slf4j
3131
import com.github.dockerjava.api.model.ContainerNetwork
3232
import geb.Browser
3333
import geb.Configuration
34+
import geb.ConfigurationLoader
3435
import geb.spock.SpockGebTestManagerBuilder
3536
import geb.test.GebTestManager
36-
import org.openqa.selenium.WebDriver
3737
import org.openqa.selenium.chrome.ChromeOptions
3838
import org.openqa.selenium.remote.RemoteWebDriver
3939
import org.spockframework.runtime.extension.IMethodInvocation
@@ -114,50 +114,82 @@ class WebDriverContainerHolder {
114114
grailsGebSettings.recordingFormat
115115
)
116116

117-
Map prefs = [
118-
'credentials_enable_service': false,
119-
'profile.password_manager_enabled': false,
120-
'profile.password_manager_leak_detection': false
121-
]
122-
123-
ChromeOptions chromeOptions = new ChromeOptions()
124-
// TODO: guest would be preferred, but this causes issues with downloads
125-
// see https://issues.chromium.org/issues/42323769
126-
// chromeOptions.addArguments("--guest")
127-
chromeOptions.setExperimentalOption('prefs', prefs)
128-
129-
currentContainer.tap {
117+
currentContainer.with {
130118
withEnv('SE_ENABLE_TRACING', grailsGebSettings.tracingEnabled)
131119
withAccessToHost(true)
132120
withImagePullPolicy(PullPolicy.ageBased(Duration.of(1, ChronoUnit.DAYS)))
133-
withCapabilities(chromeOptions)
134121
start()
135122
}
136123
if (hostnameChanged) {
137124
currentContainer.execInContainer('/bin/sh', '-c', "echo '$hostIp\t${currentConfiguration.hostName}' | sudo tee -a /etc/hosts")
138125
}
139126

140-
ConfigObject configObject = new ConfigObject()
127+
// Create a Geb Configuration the same way as an empty Browser constructor would do
128+
Configuration gebConfig = new ConfigurationLoader().conf
129+
130+
// Ensure driver points to re-initialized container with correct host
131+
// Driver is explicitly quit by us in stop() method to fulfill our resulting responsibility
132+
gebConfig.cacheDriver = false
133+
134+
// "If driver caching is disabled then this setting defaults to true" - we override to false
135+
gebConfig.quitDriverOnBrowserReset = false
136+
137+
gebConfig.baseUrl = currentContainer.seleniumAddress.toString()
141138
if (currentConfiguration.reporting) {
142-
configObject.reportsDir = grailsGebSettings.reportingDirectory
143-
configObject.reporter = (invocation.sharedInstance as ContainerGebSpec).createReporter()
144-
}
145-
if (currentConfiguration.fileDetector != NullContainerFileDetector) {
146-
ServiceRegistry.setInstance(ContainerFileDetector, currentConfiguration.fileDetector)
139+
gebConfig.reportsDir = grailsGebSettings.reportingDirectory
140+
gebConfig.reporter = (invocation.sharedInstance as ContainerGebSpec).createReporter()
147141
}
148142

149-
currentBrowser = new Browser(new Configuration(configObject, new Properties(), null, null))
143+
if (gebConfig.driverConf instanceof RemoteWebDriver) {
144+
// Similar to Browser#getDriverConf's Exception
145+
throw new IllegalStateException(
146+
"The 'driver' config value is an instance of RemoteWebDriver. " +
147+
'You need to wrap the driver instance in a closure.'
148+
)
149+
}
150+
if (gebConfig.driverConf == null) {
151+
// If no driver was set in GebConfig.groovy, default to Chrome
152+
gebConfig.driverConf = { ->
153+
log.info('Using default Chrome RemoteWebDriver for {}', currentContainer.seleniumAddress)
154+
new RemoteWebDriver(currentContainer.seleniumAddress, new ChromeOptions().tap {
155+
// See https://issues.chromium.org/issues/42323769
156+
setExperimentalOption('prefs', [
157+
'credentials_enable_service': false,
158+
'profile.password_manager_enabled': false,
159+
'profile.password_manager_leak_detection': false
160+
])
161+
})
162+
}
163+
}
150164

151-
WebDriver driver = new RemoteWebDriver(currentContainer.seleniumAddress, chromeOptions)
152-
ContainerFileDetector fileDetector = ServiceRegistry.getInstance(ContainerFileDetector, DefaultContainerFileDetector)
153-
((RemoteWebDriver) driver).setFileDetector(fileDetector)
154-
driver.manage().timeouts().with {
155-
implicitlyWait(Duration.ofSeconds(grailsGebSettings.implicitlyWait))
156-
pageLoadTimeout(Duration.ofSeconds(grailsGebSettings.pageLoadTimeout))
157-
scriptTimeout(Duration.ofSeconds(grailsGebSettings.scriptTimeout))
165+
// If `GebConfig` instantiates a `RemoteWebDriver` without using it's `remoteAddress` constructor,
166+
// the `RemoteWebDriver` will be instantiated using the `webdriver.remote.server` system property.
167+
String existingPropertyValue = System.getProperty('webdriver.remote.server')
168+
System.setProperty('webdriver.remote.server', currentContainer.seleniumAddress.toString())
169+
gebConfig.driver // This will implicitly call `createDriver()`
170+
171+
// Restore the `webdriver.remote.server` system property
172+
if (existingPropertyValue == null) {
173+
System.clearProperty('webdriver.remote.server')
174+
} else {
175+
System.setProperty('webdriver.remote.server', existingPropertyValue)
158176
}
159177

160-
currentBrowser.driver = driver
178+
currentBrowser = new Browser(gebConfig)
179+
180+
if (currentConfiguration.fileDetector != NullContainerFileDetector) {
181+
ServiceRegistry.setInstance(ContainerFileDetector, currentConfiguration.fileDetector)
182+
}
183+
ContainerFileDetector fileDetector = ServiceRegistry.getInstance(ContainerFileDetector, DefaultContainerFileDetector)
184+
((RemoteWebDriver) currentBrowser.driver).setFileDetector(fileDetector)
185+
186+
// Overwrite `GebConfig` timeouts with values explicitly set in `GrailsGebSettings` (via system properties)
187+
if (grailsGebSettings.implicitlyWait != GrailsGebSettings.DEFAULT_TIMEOUT_IMPLICITLY_WAIT)
188+
currentBrowser.driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(grailsGebSettings.implicitlyWait))
189+
if (grailsGebSettings.pageLoadTimeout != GrailsGebSettings.DEFAULT_TIMEOUT_PAGE_LOAD)
190+
currentBrowser.driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(grailsGebSettings.pageLoadTimeout))
191+
if (grailsGebSettings.scriptTimeout != GrailsGebSettings.DEFAULT_TIMEOUT_SCRIPT)
192+
currentBrowser.driver.manage().timeouts().scriptTimeout(Duration.ofSeconds(grailsGebSettings.scriptTimeout))
161193

162194
// There's a bit of a chicken and egg problem here: the container & browser are initialized when
163195
// the static/shared fields are initialized, which is before the grails server has started so the
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
apply plugin: 'groovy'
21+
apply plugin: 'org.apache.grails.gradle.grails-web'
22+
apply plugin: 'org.apache.grails.gradle.grails-gsp'
23+
apply plugin: 'cloud.wondrify.asset-pipeline'
24+
25+
group = 'org.demo.spock'
26+
version = projectVersion
27+
28+
dependencies {
29+
30+
implementation platform(project(':grails-bom'))
31+
32+
implementation 'org.apache.grails:grails-core'
33+
implementation 'org.apache.grails:grails-logging'
34+
implementation 'org.apache.grails:grails-databinding'
35+
implementation 'org.apache.grails:grails-i18n'
36+
implementation 'org.apache.grails:grails-interceptors'
37+
implementation 'org.apache.grails:grails-rest-transforms'
38+
implementation 'org.apache.grails:grails-services'
39+
implementation 'org.apache.grails:grails-url-mappings'
40+
implementation 'org.apache.grails:grails-web-boot'
41+
implementation 'org.apache.grails:grails-gsp'
42+
if(System.getenv('SITEMESH3_TESTING_ENABLED') == 'true') {
43+
implementation 'org.apache.grails:grails-sitemesh3'
44+
}
45+
else {
46+
implementation 'org.apache.grails:grails-layout'
47+
}
48+
implementation 'org.apache.grails:grails-data-hibernate5'
49+
implementation 'org.apache.grails:grails-scaffolding'
50+
implementation 'org.springframework.boot:spring-boot-autoconfigure'
51+
implementation 'org.springframework.boot:spring-boot-starter'
52+
implementation 'org.springframework.boot:spring-boot-starter-actuator'
53+
implementation 'org.springframework.boot:spring-boot-starter-logging'
54+
implementation 'org.springframework.boot:spring-boot-starter-tomcat'
55+
implementation 'org.springframework.boot:spring-boot-starter-validation'
56+
57+
testAndDevelopmentOnly platform(project(':grails-bom'))
58+
testAndDevelopmentOnly 'org.webjars.npm:bootstrap'
59+
testAndDevelopmentOnly 'org.webjars.npm:jquery'
60+
61+
runtimeOnly 'cloud.wondrify:asset-pipeline-grails'
62+
runtimeOnly 'com.h2database:h2'
63+
runtimeOnly 'org.apache.tomcat:tomcat-jdbc'
64+
runtimeOnly 'org.fusesource.jansi:jansi'
65+
66+
testImplementation 'org.apache.grails:grails-testing-support-datamapping'
67+
testImplementation 'org.apache.grails:grails-testing-support-web'
68+
testImplementation 'org.spockframework:spock-core'
69+
70+
integrationTestImplementation testFixtures('org.apache.grails:grails-geb')
71+
}
72+
73+
//tasks.withType(Test).configureEach {
74+
// //systemProperty('grails.geb.recording.mode', 'RECORD_ALL')
75+
//}
76+
77+
apply {
78+
from rootProject.layout.projectDirectory.file('gradle/functional-test-config.gradle')
79+
from rootProject.layout.projectDirectory.file('gradle/java-config.gradle')
80+
from rootProject.layout.projectDirectory.file('gradle/test-webjar-asset-config.gradle')
81+
from rootProject.layout.projectDirectory.file('gradle/grails-extension-gradle-config.gradle')
82+
}
Lines changed: 27 additions & 0 deletions
Loading
6.87 KB
Loading
3 KB
Loading
Lines changed: 19 additions & 0 deletions
Loading
Binary file not shown.

0 commit comments

Comments
 (0)