diff --git a/grails-geb/src/testFixtures/groovy/grails/plugin/geb/GrailsGebSettings.groovy b/grails-geb/src/testFixtures/groovy/grails/plugin/geb/GrailsGebSettings.groovy
index 5aefb502801..ee6db82106a 100644
--- a/grails-geb/src/testFixtures/groovy/grails/plugin/geb/GrailsGebSettings.groovy
+++ b/grails-geb/src/testFixtures/groovy/grails/plugin/geb/GrailsGebSettings.groovy
@@ -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
diff --git a/grails-geb/src/testFixtures/groovy/grails/plugin/geb/WebDriverContainerHolder.groovy b/grails-geb/src/testFixtures/groovy/grails/plugin/geb/WebDriverContainerHolder.groovy
index 5a7018c7ebb..c1f21992e87 100644
--- a/grails-geb/src/testFixtures/groovy/grails/plugin/geb/WebDriverContainerHolder.groovy
+++ b/grails-geb/src/testFixtures/groovy/grails/plugin/geb/WebDriverContainerHolder.groovy
@@ -31,9 +31,9 @@ import groovy.util.logging.Slf4j
import com.github.dockerjava.api.model.ContainerNetwork
import geb.Browser
import geb.Configuration
+import geb.ConfigurationLoader
import geb.spock.SpockGebTestManagerBuilder
import geb.test.GebTestManager
-import org.openqa.selenium.WebDriver
import org.openqa.selenium.chrome.ChromeOptions
import org.openqa.selenium.remote.RemoteWebDriver
import org.spockframework.runtime.extension.IMethodInvocation
@@ -114,50 +114,82 @@ 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 {
+ currentContainer.with {
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()
+ // Create a Geb Configuration the same way as an 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.cacheDriver = 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()
- }
- if (currentConfiguration.fileDetector != NullContainerFileDetector) {
- ServiceRegistry.setInstance(ContainerFileDetector, currentConfiguration.fileDetector)
+ gebConfig.reportsDir = grailsGebSettings.reportingDirectory
+ gebConfig.reporter = (invocation.sharedInstance as ContainerGebSpec).createReporter()
}
- currentBrowser = new Browser(new Configuration(configObject, new Properties(), null, null))
+ if (gebConfig.driverConf 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.driverConf == null) {
+ // If no driver was set in GebConfig.groovy, default to Chrome
+ gebConfig.driverConf = { ->
+ log.info('Using default Chrome RemoteWebDriver for {}', currentContainer.seleniumAddress)
+ new RemoteWebDriver(currentContainer.seleniumAddress, new ChromeOptions().tap {
+ // See https://issues.chromium.org/issues/42323769
+ setExperimentalOption('prefs', [
+ 'credentials_enable_service': false,
+ 'profile.password_manager_enabled': false,
+ 'profile.password_manager_leak_detection': false
+ ])
+ })
+ }
+ }
- 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))
+ // If `GebConfig` instantiates a `RemoteWebDriver` without using it's `remoteAddress` constructor,
+ // the `RemoteWebDriver` will be instantiated using the `webdriver.remote.server` system property.
+ String existingPropertyValue = System.getProperty('webdriver.remote.server')
+ System.setProperty('webdriver.remote.server', currentContainer.seleniumAddress.toString())
+ gebConfig.driver // This will implicitly call `createDriver()`
+
+ // Restore the `webdriver.remote.server` system property
+ if (existingPropertyValue == null) {
+ System.clearProperty('webdriver.remote.server')
+ } else {
+ System.setProperty('webdriver.remote.server', existingPropertyValue)
}
- currentBrowser.driver = driver
+ currentBrowser = new Browser(gebConfig)
+
+ if (currentConfiguration.fileDetector != NullContainerFileDetector) {
+ ServiceRegistry.setInstance(ContainerFileDetector, currentConfiguration.fileDetector)
+ }
+ ContainerFileDetector fileDetector = ServiceRegistry.getInstance(ContainerFileDetector, DefaultContainerFileDetector)
+ ((RemoteWebDriver) currentBrowser.driver).setFileDetector(fileDetector)
+
+ // Overwrite `GebConfig` timeouts with values explicitly set in `GrailsGebSettings` (via system properties)
+ 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
diff --git a/grails-test-examples/geb-gebconfig/build.gradle b/grails-test-examples/geb-gebconfig/build.gradle
new file mode 100644
index 00000000000..d6b4ffaf06f
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/build.gradle
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+apply plugin: 'groovy'
+apply plugin: 'org.apache.grails.gradle.grails-web'
+apply plugin: 'org.apache.grails.gradle.grails-gsp'
+apply plugin: 'cloud.wondrify.asset-pipeline'
+
+group = 'org.demo.spock'
+version = projectVersion
+
+dependencies {
+
+ implementation platform(project(':grails-bom'))
+
+ implementation 'org.apache.grails:grails-core'
+ implementation 'org.apache.grails:grails-logging'
+ implementation 'org.apache.grails:grails-databinding'
+ implementation 'org.apache.grails:grails-i18n'
+ implementation 'org.apache.grails:grails-interceptors'
+ implementation 'org.apache.grails:grails-rest-transforms'
+ implementation 'org.apache.grails:grails-services'
+ implementation 'org.apache.grails:grails-url-mappings'
+ implementation 'org.apache.grails:grails-web-boot'
+ implementation 'org.apache.grails:grails-gsp'
+ if(System.getenv('SITEMESH3_TESTING_ENABLED') == 'true') {
+ implementation 'org.apache.grails:grails-sitemesh3'
+ }
+ else {
+ implementation 'org.apache.grails:grails-layout'
+ }
+ implementation 'org.apache.grails:grails-data-hibernate5'
+ implementation 'org.apache.grails:grails-scaffolding'
+ implementation 'org.springframework.boot:spring-boot-autoconfigure'
+ implementation 'org.springframework.boot:spring-boot-starter'
+ implementation 'org.springframework.boot:spring-boot-starter-actuator'
+ implementation 'org.springframework.boot:spring-boot-starter-logging'
+ implementation 'org.springframework.boot:spring-boot-starter-tomcat'
+ implementation 'org.springframework.boot:spring-boot-starter-validation'
+
+ testAndDevelopmentOnly platform(project(':grails-bom'))
+ testAndDevelopmentOnly 'org.webjars.npm:bootstrap'
+ testAndDevelopmentOnly 'org.webjars.npm:jquery'
+
+ runtimeOnly 'cloud.wondrify:asset-pipeline-grails'
+ runtimeOnly 'com.h2database:h2'
+ runtimeOnly 'org.apache.tomcat:tomcat-jdbc'
+ runtimeOnly 'org.fusesource.jansi:jansi'
+
+ testImplementation 'org.apache.grails:grails-testing-support-datamapping'
+ testImplementation 'org.apache.grails:grails-testing-support-web'
+ testImplementation 'org.spockframework:spock-core'
+
+ integrationTestImplementation testFixtures('org.apache.grails:grails-geb')
+}
+
+//tasks.withType(Test).configureEach {
+// //systemProperty('grails.geb.recording.mode', 'RECORD_ALL')
+//}
+
+apply {
+ from rootProject.layout.projectDirectory.file('gradle/functional-test-config.gradle')
+ from rootProject.layout.projectDirectory.file('gradle/java-config.gradle')
+ from rootProject.layout.projectDirectory.file('gradle/test-webjar-asset-config.gradle')
+ from rootProject.layout.projectDirectory.file('gradle/grails-extension-gradle-config.gradle')
+}
\ No newline at end of file
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/images/advancedgrails.svg b/grails-test-examples/geb-gebconfig/grails-app/assets/images/advancedgrails.svg
new file mode 100644
index 00000000000..8b63ec8be50
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/assets/images/advancedgrails.svg
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/images/apple-touch-icon-retina.png b/grails-test-examples/geb-gebconfig/grails-app/assets/images/apple-touch-icon-retina.png
new file mode 100644
index 00000000000..d5bc4c0da0b
Binary files /dev/null and b/grails-test-examples/geb-gebconfig/grails-app/assets/images/apple-touch-icon-retina.png differ
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/images/apple-touch-icon.png b/grails-test-examples/geb-gebconfig/grails-app/assets/images/apple-touch-icon.png
new file mode 100644
index 00000000000..c3681cc467d
Binary files /dev/null and b/grails-test-examples/geb-gebconfig/grails-app/assets/images/apple-touch-icon.png differ
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/images/documentation.svg b/grails-test-examples/geb-gebconfig/grails-app/assets/images/documentation.svg
new file mode 100644
index 00000000000..29bc9d57d39
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/assets/images/documentation.svg
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/images/favicon.ico b/grails-test-examples/geb-gebconfig/grails-app/assets/images/favicon.ico
new file mode 100644
index 00000000000..76e4b11feda
Binary files /dev/null and b/grails-test-examples/geb-gebconfig/grails-app/assets/images/favicon.ico differ
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/images/grails-cupsonly-logo-white.svg b/grails-test-examples/geb-gebconfig/grails-app/assets/images/grails-cupsonly-logo-white.svg
new file mode 100644
index 00000000000..d3fe882c4bc
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/assets/images/grails-cupsonly-logo-white.svg
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/images/grails.svg b/grails-test-examples/geb-gebconfig/grails-app/assets/images/grails.svg
new file mode 100644
index 00000000000..79f698b698a
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/assets/images/grails.svg
@@ -0,0 +1,13 @@
+
+
+
+ grails
+ Created with Sketch.
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/database_add.png b/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/database_add.png
new file mode 100644
index 00000000000..802bd6cde02
Binary files /dev/null and b/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/database_add.png differ
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/database_delete.png b/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/database_delete.png
new file mode 100644
index 00000000000..cce652e845c
Binary files /dev/null and b/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/database_delete.png differ
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/database_edit.png b/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/database_edit.png
new file mode 100644
index 00000000000..e501b668c70
Binary files /dev/null and b/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/database_edit.png differ
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/database_save.png b/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/database_save.png
new file mode 100644
index 00000000000..44c06dddf19
Binary files /dev/null and b/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/database_save.png differ
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/database_table.png b/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/database_table.png
new file mode 100644
index 00000000000..693709cbc1b
Binary files /dev/null and b/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/database_table.png differ
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/exclamation.png b/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/exclamation.png
new file mode 100644
index 00000000000..c37bd062e60
Binary files /dev/null and b/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/exclamation.png differ
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/house.png b/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/house.png
new file mode 100644
index 00000000000..fed62219f57
Binary files /dev/null and b/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/house.png differ
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/information.png b/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/information.png
new file mode 100644
index 00000000000..12cd1aef900
Binary files /dev/null and b/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/information.png differ
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/sorted_asc.gif b/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/sorted_asc.gif
new file mode 100644
index 00000000000..6b179c11cf7
Binary files /dev/null and b/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/sorted_asc.gif differ
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/sorted_desc.gif b/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/sorted_desc.gif
new file mode 100644
index 00000000000..38b3a01d078
Binary files /dev/null and b/grails-test-examples/geb-gebconfig/grails-app/assets/images/skin/sorted_desc.gif differ
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/images/slack.svg b/grails-test-examples/geb-gebconfig/grails-app/assets/images/slack.svg
new file mode 100644
index 00000000000..34fcf4ce098
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/assets/images/slack.svg
@@ -0,0 +1,18 @@
+
+
+
+ slack_orange
+ Created with Sketch.
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/images/spinner.gif b/grails-test-examples/geb-gebconfig/grails-app/assets/images/spinner.gif
new file mode 100644
index 00000000000..1ed786f2ece
Binary files /dev/null and b/grails-test-examples/geb-gebconfig/grails-app/assets/images/spinner.gif differ
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/javascripts/application.js b/grails-test-examples/geb-gebconfig/grails-app/assets/javascripts/application.js
new file mode 100644
index 00000000000..acc80699ea9
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/assets/javascripts/application.js
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+// This is a manifest file that'll be compiled into application.js.
+//
+// Any JavaScript file within this directory can be referenced here using a relative path.
+//
+// You're free to add application-wide JavaScript to this file, but it's generally better
+// to create separate JavaScript files as needed.
+//
+//= require webjars/jquery/3.7.1/dist/jquery.js
+//= require webjars/bootstrap/5.3.7/dist/js/bootstrap.bundle
+//= require_self
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/stylesheets/application.css b/grails-test-examples/geb-gebconfig/grails-app/assets/stylesheets/application.css
new file mode 100644
index 00000000000..a68aa0b40a7
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/assets/stylesheets/application.css
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+/*
+* This is a manifest file that'll be compiled into application.css, which will include all the files
+* listed below.
+*
+* Any CSS file within this directory can be referenced here using a relative path.
+*
+* You're free to add application-wide styles to this file and they'll appear at the top of the
+* compiled file, but it's generally better to create a new file per style scope.
+*
+*= require webjars/bootstrap/5.3.7/dist/css/bootstrap
+*= require grails
+*= require main
+*= require mobile
+*= require_self
+*/
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/stylesheets/errors.css b/grails-test-examples/geb-gebconfig/grails-app/assets/stylesheets/errors.css
new file mode 100644
index 00000000000..6296629cc73
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/assets/stylesheets/errors.css
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+h1 {
+ font-size: 2rem;
+}
+
+h2 {
+ font-size: 1.25rem;
+}
+
+.filename {
+ font-style: italic;
+}
+
+.exceptionMessage {
+ margin: 10px;
+ border: 1px solid #000;
+ padding: 5px;
+ background-color: #E9E9E9;
+}
+
+.stack,
+.snippet {
+ margin: 10px 0;
+}
+
+.stack,
+.snippet {
+ border: 1px solid #ccc;
+}
+
+/* error details */
+.error-details {
+ border: 1px solid #FFAAAA;
+ background-color:#FFF3F3;
+ line-height: 1.5;
+ overflow: hidden;
+ padding: 10px 0 5px 25px;
+}
+
+.error-details dt {
+ clear: left;
+ float: left;
+ font-weight: bold;
+ margin-right: 5px;
+}
+
+.error-details dt:after {
+ content: ":";
+}
+
+.error-details dd {
+ display: block;
+}
+
+/* stack trace */
+.stack {
+ padding: 5px;
+ overflow: auto;
+ height: 300px;
+}
+
+/* code snippet */
+.snippet {
+ background-color: #fff;
+ font-family: monospace;
+}
+
+.snippet .line {
+ display: block;
+}
+
+.snippet .lineNumber {
+ background-color: #ddd;
+ color: #999;
+ display: inline-block;
+ margin-right: 5px;
+ padding: 0 3px;
+ text-align: right;
+ width: 3em;
+}
+
+.snippet .error {
+ background-color: #fff3f3;
+ font-weight: bold;
+}
+
+.snippet .error .lineNumber {
+ background-color: #faa;
+ color: #333;
+ font-weight: bold;
+}
+
+.snippet .line:first-child .lineNumber {
+ padding-top: 5px;
+}
+
+.snippet .line:last-child .lineNumber {
+ padding-bottom: 5px;
+}
\ No newline at end of file
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/stylesheets/grails.css b/grails-test-examples/geb-gebconfig/grails-app/assets/stylesheets/grails.css
new file mode 100644
index 00000000000..4ba5cc0d5db
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/assets/stylesheets/grails.css
@@ -0,0 +1,1071 @@
+/*
+ * 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.
+ */
+
+html, code, kbd, pre, samp {
+ -ms-text-size-adjust: 100%;
+ -webkit-text-size-adjust: 100%;
+}
+
+html, body {
+ height: 100%;
+ -webkit-overflow-scrolling: touch;
+}
+
+p, ul, pre, h1, h2, h3, h4, h5, h6, h7, h8 {
+ margin: 1em 0;
+}
+
+p {
+ display: block;
+}
+
+h1, h2, h3, h4, h5, h6, h7, h8 {
+ font-weight: bold;
+}
+
+pre {
+ border-radius: 0;
+ border: 0;
+ font-size: 14px;
+}
+
+/* customizing bootstrap nav bar */
+.navbar {
+ margin-bottom: 0px;
+}
+
+.navbar-dark a {
+ color: #ffffff !important;
+ font-size: 18px !important;
+ text-decoration: none;
+}
+.grails-icon img {
+ width: 40px;
+
+}
+.navbar-dark, .navbar-static-top {
+ background-color: #424649;
+ border: 0px;
+}
+a.navbar-brand {
+ color: white !important;
+ font-size: 19px !important;
+}
+.navbar-dark .navbar-nav>.active>a, .navbar-dark .navbar-nav>.active>a:hover, .navbar-dark .navbar-nav>.active>a:focus {
+ background-color: transparent;
+ color: white;
+}
+.navbar-nav>li.active>a {
+ color: white !important;
+}
+.navbar-nav>li>a:hover {
+ background-color: #2559a7 !important;
+ color: white !important;
+}
+.navbar-nav>li>a {
+ color: #c0d3db;
+}
+.navbar-dark .navbar-toggler .icon-bar {
+ background-color: white;
+}
+.navbar-dark .navbar-toggle:hover, .navbar-dark .navbar-toggle:focus {
+ background-color: #2559a7;
+}
+
+.navbar-toggler {
+ position: relative;
+ float: right;
+ padding: 9px 10px;
+ margin-top: 8px;
+ margin-right: 15px;
+ margin-bottom: 8px;
+ background-color: transparent;
+ background-image: none;
+ border: 1px solid transparent;
+ border-radius: 4px;
+}
+
+.nav .dropdown a.dropdown-toggle {
+ padding-top: 25px;
+ padding-bottom: 25px;
+}
+
+@media (min-width: 768px) {
+ .container {
+ width: auto;
+ }
+}
+
+/* specific to index.html */
+
+@media (max-width: 999px) {
+ #fork-me {
+ display: none;
+ }
+
+ .navbar {
+ padding-right: 0px;
+ }
+}
+
+#fork-me{
+ position: fixed;
+ padding: 0px 50px 0px 50px;
+ top: 40px;
+ right: -60px;
+ background-color: #a60000;
+ color: #ffffff;
+ font-size: 1em;
+ z-index: 100;
+ transform: rotate(+45deg);
+ text-align: center;
+ font-weight: bolder;
+ border: #c14646;
+ border-style: dashed;
+ border-width: 1px;
+}
+
+#fork-me p {
+ margin: 0em 0;
+}
+
+#band {
+ /*grey =#808080*/
+ background: #2559a7 no-repeat 50% 30%;
+ height: 400px;
+}
+
+.svg #band {
+ background-image: url(../img/grails-cupsonly-logo-white.svg);
+}
+
+.no-svg #band {
+ background-image: url(../img/groovy-logo-white.png);
+}
+
+@media (max-width: 1010px) {
+ #band {
+ background-size: 90%;
+ height: 300px;
+ }
+}
+
+@media (max-width: 690px) {
+ #band {
+ background-size: 80%;
+ height: 200px;
+ }
+}
+
+@media (max-width: 475px) {
+ #band {
+ background-size: 70%;
+ height: 100px;
+ }
+}
+
+#they-use-groovy {
+ width: 100%;
+ height: 450px;
+ background-color: #db4800;
+ margin-bottom: 20px;
+ text-align: center;
+}
+
+#they-use-groovy .item {
+ text-align: center;
+ color: white;
+}
+
+#logos-holder {
+ display: inline-block;
+ padding: 0px;
+ margin: 0px;
+ text-align: center;
+}
+
+#logos-holder .logo {
+ padding: 0px;
+ margin: 0px;
+ display: inline-block;
+ width: 100px;
+ height: 80px;
+ background-size: 95%;
+ background-repeat: no-repeat;
+ background-position: 50% 50%;
+}
+
+@media (min-width: 330px) {
+ #logos-holder {
+ width: 320px;
+ }
+
+ #they-use-groovy {
+ height: 1130px;
+ }
+}
+
+@media (min-width: 475px) {
+ #logos-holder {
+ width: 420px;
+ }
+
+ #they-use-groovy {
+ height: 900px;
+ }
+}
+
+@media (min-width: 690px) {
+ #logos-holder {
+ width: 630px;
+ }
+
+ #they-use-groovy {
+ height: 600px;
+ }
+}
+
+@media (min-width: 1010px) {
+ #logos-holder {
+ width: 940px;
+ }
+
+ #they-use-groovy {
+ height: 450px;
+ }
+}
+
+.centered {
+ text-align: center;
+}
+
+.event-img {
+ margin: -20px -20px 20px -20px;
+ background-repeat: no-repeat;
+ background-position: 50% top;
+ height: 180px;
+}
+
+.event-logo {
+ height: 180px;
+ float: right;
+}
+
+@media (max-width: 1010px) {
+ .event-logo {
+ height: 100px;
+ }
+}
+
+@media (max-width: 690px) {
+ .event-logo {
+ height: 60px;
+ }
+}
+
+@media (max-width: 475px) {
+ .event-logo {
+ display: none;
+ }
+}
+
+article .content time {
+ font-weight: bold;
+}
+
+.doc-embed {
+ border: 0;
+ width: 100%;
+ min-height: 100%;
+}
+
+.download-table {
+ width: 100%;
+ text-align: center;
+}
+
+.download-table td {
+ width: 20%;
+}
+
+#mc-embedded-subscribe {
+ width: 200px;
+ font-weight: bold;
+}
+
+#mc-embedded-subscribe:hover {
+ background-color: #F2F2F2;
+ font-weight: bold;
+}
+
+#footer .colset-3-footer .col-1 h1, #footer .colset-3-footer .col-2 h1, #footer .colset-3-footer .col-3 h1 {
+ font-size: 15px !important;
+}
+
+.anchor-link:before {
+ content: ' # ';
+ color: lightgray;
+}
+
+.anchor-link:hover:before {
+ color: orange;
+}
+
+code, kbd, pre, samp {
+ font-family: "Source Code Pro", "Consolas", "Monaco", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace;
+}
+
+#contribute-btn {
+ position: absolute;
+ right: 15px;
+}
+
+@media (max-width: 767px) {
+ #contribute-btn {
+ width: 100%;
+ position: relative;
+ margin-top: 30px;
+ right: 0px;
+ }
+
+ #contribute-btn button {
+ width: 100%;
+ right: 15px;
+ }
+}
+
+@media (min-width: 1200px) {
+ #contribute-btn {
+ top: 25px;
+ right: 15px;
+ }
+}
+
+#big-download-button {
+ float: right;
+ font-size: 30px;
+ padding: 15px;
+ margin: 10px 0px 10px 20px;
+ border: 2px solid #db4800;
+ border-radius: 6px;
+ background-color: #db4800;
+ color: white;
+}
+
+#big-download-button:hover {
+ background-color: #e6e6e6;
+ color: #db4800;
+}
+
+.colset-3-footer .col-1, .colset-3-footer .col-2, .colset-3-footer .col-3 {
+ min-width: 180px;
+ float: left;
+}
+
+.colset-3-footer .col-3 {
+ min-width: 220px;
+}
+
+.colset-3-article article {
+ float: left;
+}
+
+.col1, .col2 {
+ min-width: 300px;
+ float: left;
+}
+
+@media (max-width: 988px) {
+ .col1, .col2 {
+ width: 98% !important;
+ max-width: 98%;
+ }
+
+ .colset-3-article article {
+ width: 98% !important;
+ max-width: 98%;
+ }
+}
+
+body, html {
+ font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ padding: 0;
+ margin: 0;
+ background: #FFF;
+ color: #343437;
+ line-height: 25px;
+ font-weight: normal;
+ font-size: 14px;
+}
+
+a {
+ color: #2559a7;
+ text-decoration: underline;
+}
+
+a:hover {
+ color: #2559a7;
+ text-decoration: none
+}
+
+h1 {
+ font-size: 2.125em;
+ margin: .67em 0
+}
+
+h2 {
+ font-size: 1.6875em;
+ font-weight: bold;
+}
+
+h3, #toctitle, .sidebarblock > .content > .title {
+ font-size: 1.375em;
+ font-weight: bold;
+}
+
+h4 {
+ font-size: 1.125em;
+ font-weight: bold;
+}
+
+h5 {
+ font-size: 1.125em;
+ font-weight: bold;
+ color: #2559a7;
+}
+
+h6 {
+ font-size: 1.08em;
+ font-weight: normal;
+ color: #2559a7;
+}
+
+h7 {
+ font-weight: bold;
+ color: #245f78;
+}
+
+h8 {
+ color: #245f78;
+}
+
+#footer {
+ background: #f2f2f2;
+ text-align: center;
+ font-size: 14px;
+ padding: 20px 0 30px;
+ margin-top: 30px;
+ color: #AAA
+}
+
+#footer .col-right {
+ float: right;
+ width: 300px;
+ text-align: right;
+ padding-top: 10px
+}
+
+#footer .colset-3-footer {
+ color: #222;
+ font-size: 14px
+}
+
+#footer .colset-3-footer:before, #footer .colset-3-footer:after {
+ content: " ";
+ display: table
+}
+
+#footer .colset-3-footer:after {
+ clear: both
+}
+
+#footer .colset-3-footer .col-1, #footer .colset-3-footer .col-2, #footer .colset-3-footer .col-3 {
+ width: 18%;
+ padding: 20px 0 30px;
+ padding-right: 3%;
+ float: left;
+ text-align: left
+}
+
+#footer .colset-3-footer .col-3 {
+ width: 24%;
+}
+
+#footer .colset-3-footer .col-1 h1, #footer .colset-3-footer .col-2 h1, #footer .colset-3-footer .col-3 h1 {
+ font-weight: 600;
+ font-size: 15px;
+ line-height: 30px;
+ margin: 0
+}
+
+#footer .colset-3-footer .col-1 ul, #footer .colset-3-footer .col-2 ul, #footer .colset-3-footer .col-3 ul {
+ list-style-type: none;
+ margin: 0;
+ padding: 0
+}
+
+#footer .colset-3-footer .col-1 ul li, #footer .colset-3-footer .col-2 ul li, #footer .colset-3-footer .col-3 ul li {
+ margin: 0;
+ padding: 0
+}
+
+#footer .colset-3-footer .col-1 ul li a, #footer .colset-3-footer .col-2 ul li a, #footer .colset-3-footer .col-3 ul li a {
+ color: #343437;
+ text-decoration: none
+}
+
+#footer .colset-3-footer .col-1 ul li a:hover, #footer .colset-3-footer .col-2 ul li a:hover, #footer .colset-3-footer .col-3 ul li a:hover {
+ text-decoration: underline
+}
+
+#footer .second a {
+ color: #db4800
+}
+
+.band {
+ background: #4298b8;
+ height: 400px;
+ margin-bottom: 20px;
+ color: white
+}
+
+.band .item {
+ text-align: center
+}
+
+.band .item:before, .band .item:after {
+ content: " ";
+ display: table
+}
+
+.band .item:after {
+ clear: both
+}
+
+#content {
+ margin: 2em 0;
+ padding: 1em 0;
+ background: white;
+}
+
+#content .row > h1 {
+ font-size: 34px;
+ line-height: 40px;
+ font-weight: 200;
+ text-align: center;
+ margin: 0;
+ padding: 20px 0;
+ width: 100%;
+}
+
+#content hr.divider {
+ border: 0 none;
+ border-top: 1px solid #EEE;
+ margin: 0 5%;
+ margin-top: 40px
+}
+
+#content hr.divider {
+ margin: 0;
+ margin-top: 40px;
+ margin-bottom: 30px
+}
+
+#content .colset-2-its:before, #content .colset-2-its:after {
+ content: " ";
+ display: table
+}
+
+#content .colset-2-its:after {
+ clear: both
+}
+
+#content .colset-2-its > h1 {
+ padding-bottom: 15px;
+ margin-top: 15px;
+ margin-bottom: 0
+}
+
+#content .colset-2-its > p {
+ margin-top: 0;
+ padding-bottom: 5px;
+ text-align: center;
+ color: #222;
+ font-size: 15px
+}
+
+#content .colset-2-its .col1, #content .colset-2-its .col2 {
+ float: left;
+ width: 48%;
+ padding-right: 1%;
+ padding-left: 1%;
+}
+
+#content .colset-2-its .col2 {
+ padding-left: 1%;
+ padding-right: 1%;
+}
+
+#content .colset-2-its article {
+ padding: 10px 0
+}
+
+#content .colset-2-its article:before, #content .colset-2-its article:after {
+ content: " ";
+ display: table
+}
+
+#content .colset-2-its article:after {
+ clear: both
+}
+
+#content .colset-2-its article .icon {
+ display: block;
+ width: 80px;
+ height: 80px;
+ background-image: url(../images/icons-colset-2-its.png);
+ float: left;
+ margin-top: 12px;
+ margin-right: 15px
+}
+
+#content .colset-2-its article .icon.icon-1 {
+ background-position: 0 0
+}
+
+#content .colset-2-its article .icon.icon-2 {
+ background-position: 0 -80px
+}
+
+#content .colset-2-its article .icon.icon-3 {
+ background-position: 0 -160px
+}
+
+#content .colset-2-its article .icon.icon-4 {
+ background-position: 0 -240px
+}
+
+#content .colset-2-its article .icon.icon-5 {
+ background-position: 0 -320px
+}
+
+#content .colset-2-its article .icon.icon-6 {
+ background-position: 0 -400px
+}
+
+#content .colset-2-its article > h1 {
+ font-size: 19px;
+ font-weight: 600;
+ margin-bottom: 0;
+ line-height: 30px
+}
+
+#content .colset-2-its article p {
+ margin: 0;
+ line-height: 24px;
+ font-size: 14px
+}
+
+#content .first-event-row {
+ padding-top: 30px;
+}
+
+#content .last-event-row {
+ padding-bottom: 30px
+}
+
+#content .colset-3-article > h1 {
+ font-size: 24px
+}
+
+#content .colset-3-article div.content {
+ padding: 20px;
+ padding-bottom: 5px
+}
+
+#content .colset-3-article article {
+ float: left;
+ width: 29%;
+ margin: 10px 2%;
+ -webkit-box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
+ box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1)
+}
+
+#content .colset-3-article article .img {
+ margin: -20px -20px 20px -20px;
+ background-position: center top;
+ height: 180px
+}
+
+#content .colset-3-article article h1 {
+ margin: 0;
+ font-size: 18px;
+ font-weight: normal;
+ line-height: 25px
+}
+
+#content .colset-3-article article h1 a {
+ color: #343437;
+ cursor: pointer
+}
+
+#content .colset-3-article article h1 a:hover {
+ color: #46a5c8
+}
+
+#content .colset-3-article article p, #content .colset-3-article article time {
+ font-size: 13px
+}
+
+#content .colset-3-article article .author a {
+ color: #db4800
+}
+
+#content .colset-3-article article:first-child {
+ padding-left: 0
+}
+
+#content .colset-3-article article:last-child {
+ padding-right: 0
+}
+
+#content.page-1 .row {
+ padding-top: 10px;
+ padding-bottom: 10px
+}
+
+#content.page-1 .row h1 {
+ text-align: left;
+ font-size: 36px
+}
+
+#content.page-1 .row article {
+ font-size: 14px
+}
+
+#content.page-1 .row article .desc {
+ font-size: 16px
+}
+
+#content.page-1 .row article h1 {
+ margin: 0;
+ padding: 0;
+ text-align: left;
+ font-size: 26px
+}
+
+#content.page-1 .row article h2 {
+ margin: 0;
+ padding: 0
+}
+
+#content.page-1 .row article h3 {
+ font-weight: bold
+}
+
+#content.page-1 .row article pre {
+ display: block;
+ background: #f2f2f2;
+ padding: 12px 20px
+}
+
+ul.nav-sidebar {
+ margin: 0;
+ margin-top: 20px;
+ padding: 5px 0;
+ border: 1px solid #EEE;
+ list-style-type: none
+}
+
+ul.nav-sidebar li a {
+ display: block;
+ cursor: pointer;
+ padding: 5px 10px;
+ font-weight: 400;
+ text-decoration: none;
+ color: #343437
+}
+
+ul.nav-sidebar li.active a:hover, ul.nav-sidebar li a:hover {
+ color: white;
+ background-color: #db4800;
+}
+
+ul.nav-sidebar li.active a {
+ background-color: #f2f2f2
+}
+
+.table {
+ margin: 20px 0
+}
+
+.table thead tr th {
+ padding: 10px;
+ font-weight: normal;
+ font-size: 18px
+}
+
+.table tbody tr td {
+ vertical-align: top;
+ font-size: 12px;
+ padding: 10px;
+ border-top: 1px solid #EEE
+}
+
+*, *:after, *::before {
+ -moz-box-sizing: border-box;
+ box-sizing: border-box
+}
+
+body {
+ background: #444
+}
+
+html.noScroll {
+ overflow: hidden
+}
+
+html.noScroll body, html.noScroll .st-container, html.noScroll .st-pusher, html.noScroll .st-content {
+ overflow: hidden
+}
+
+html, body, .st-container, .st-pusher, .st-content {
+ overflow: auto
+}
+
+.sign-in-fa-icon:before {
+ font-family: FontAwesome;
+ content: '\f090';
+ padding-right: 10px;
+}
+
+#st-container {
+ height: 100%;
+}
+
+.st-content {
+ background: white
+}
+
+.st-content, .st-content-inner {
+ position: relative;
+ height: 100%;
+}
+
+.st-container {
+ position: relative;
+ overflow: hidden
+}
+
+.st-pusher {
+ position: relative;
+ left: 0;
+ z-index: 99;
+ height: 100%;
+ -webkit-transition: -webkit-transform .5s;
+ transition: transform .5s
+}
+
+.st-pusher::after {
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 0;
+ height: 0;
+ background: rgba(0, 0, 0, 0.3);
+ content: '';
+ opacity: 0;
+ -webkit-transition: opacity .5s, width .1s .5s, height .1s .5s;
+ transition: opacity .5s, width .1s .5s, height .1s .5s
+}
+
+.st-menu-open .st-pusher::after {
+ width: 100%;
+ height: 100%;
+ opacity: 1;
+ -webkit-transition: opacity .5s;
+ transition: opacity .5s
+}
+
+.st-menu {
+ position: fixed;
+ top: 0;
+ left: auto;
+ z-index: 100;
+ visibility: hidden;
+ width: 300px;
+ height: 100%;
+ background: #2559a7;
+ -webkit-transition: all .5s;
+ transition: all .5s;
+ right: -600px
+}
+
+.st-menu::after {
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.2);
+ content: '';
+ opacity: 1;
+ -webkit-transition: opacity .5s;
+ transition: opacity .5s
+}
+
+.st-menu-open .st-menu::after {
+ width: 0;
+ height: 0;
+ opacity: 0;
+ -webkit-transition: opacity .5s, width .1s .5s, height .1s .5s;
+ transition: opacity .5s, width .1s .5s, height .1s .5s
+}
+
+.st-menu ul {
+ margin: 0;
+ padding: 0;
+ list-style: none
+}
+
+.st-menu h2 {
+ margin: 0;
+ padding: 1em;
+ color: white;
+ text-shadow: 0 0 1px rgba(0, 0, 0, 0.1);
+ font-weight: 300;
+ font-size: 2em
+}
+
+.st-menu ul li {
+ display: block
+}
+
+.st-menu ul li a {
+ display: block;
+ position: relative;
+ padding: 1em 1em 1em 45px;
+ outline: 0;
+ box-shadow: inset 0 -1px rgba(0, 0, 0, 0.2);
+ color: #f3efe0;
+ text-shadow: 0 0 1px rgba(255, 255, 255, 0.1);
+ letter-spacing: 1px;
+ font-weight: 400;
+ text-decoration: none
+}
+
+.st-menu ul li a span.fa {
+ display: block;
+ position: absolute;
+ left: 12px;
+ top: 17px;
+ font-size: 20px;
+ width: 30px;
+ text-align: center
+}
+
+.st-menu ul li a span.fa.fa-tasks, .st-menu ul li a span.fa.fa-envelope {
+ top: 18px;
+ font-size: 18px
+}
+
+.st-menu ul li:first-child a {
+ box-shadow: inset 0 -1px rgba(0, 0, 0, 0.2), inset 0 1px rgba(0, 0, 0, 0.2)
+}
+
+.st-menu ul li a:hover {
+ background: rgba(0, 0, 0, 0.2);
+ box-shadow: inset 0 -1px rgba(0, 0, 0, 0);
+ color: #fff
+}
+
+.st-effect-9.st-container {
+ -webkit-perspective: 10000px;
+ perspective: 10000px
+}
+
+.st-effect-9 .st-pusher {
+ -webkit-transform-style: preserve-3d;
+ transform-style: preserve-3d
+}
+
+.st-effect-9.st-menu-open .st-pusher {
+ -webkit-transform: translate3d(0, 0, -300px);
+ transform: translate3d(0, 0, -300px)
+}
+
+.st-effect-9.st-menu {
+ right: -600px;
+ opacity: 1;
+ -webkit-transform: translate3d(-100%, 0, 0);
+ transform: translate3d(-100%, 0, 0)
+}
+
+.st-effect-9.st-menu-open .st-effect-9.st-menu {
+ visibility: visible;
+ right: -300px
+}
+
+.st-effect-9.st-menu::after {
+ display: none
+}
+
+/* Video from the learn page */
+.presentations {
+ margin-top: 30px;
+ margin-bottom: 30px;
+}
+
+.presentations img.screenshot {
+ float: left;
+ margin-right: 40px;
+ margin-top: 1em;
+ margin-bottom: 0px;
+ width: 300px;
+ height: auto;
+}
+
+.presentations .metadata {
+ display: table-cell;
+ min-width: 328px;
+}
+
+.presentations .title {
+ margin-top: 1em !important;
+ margin-bottom: 0.5em !important;
+}
+
+
+.presentations .speaker {
+ color: #245f78;
+ margin-bottom: 0.5em;
+}
+
+.presentations .summary {
+ line-height: 1.3;
+}
+
+.presentations .urls {
+}
+
+@media screen and (max-width: 767px) {
+ .presentations .img.screenshot, .video .metadata {
+ float: none;
+ }
+}
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/stylesheets/main.css b/grails-test-examples/geb-gebconfig/grails-app/assets/stylesheets/main.css
new file mode 100644
index 00000000000..8b6bd163d68
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/assets/stylesheets/main.css
@@ -0,0 +1,608 @@
+/*
+ * 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.
+ */
+
+/* FONT STACK */
+body,
+input, select, textarea {
+ font-family: "Open Sans", "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ line-height: 1.1;
+}
+
+/* BASE LAYOUT */
+
+html {
+ background-color: #ddd;
+ background-image: -moz-linear-gradient(center top, #aaa, #ddd);
+ background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #aaa), color-stop(1, #ddd));
+ background-image: linear-gradient(to bottom, #aaa, #ddd);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorStr = '#aaaaaa', EndColorStr = '#dddddd');
+ background-repeat: no-repeat;
+ height: 100%;
+ /* change the box model to exclude the padding from the calculation of 100% height (IE8+) */
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+html.no-cssgradients {
+ background-color: #aaa;
+}
+
+html * {
+ margin: 0;
+}
+
+body {
+ background-color: #F5F5F5;
+ color: #333333;
+ overflow-x: hidden; /* prevents box-shadow causing a horizontal scrollbar in firefox when viewport < 960px wide */
+ -moz-box-shadow: 0 0 0.3em #424649;
+ -webkit-box-shadow: 0 0 0.3em #424649;
+ box-shadow: 0 0 0.3em #424649;
+}
+
+#grailsLogo {
+ background-color: #feb672;
+}
+
+a:hover, a:active {
+ outline: none; /* prevents outline in webkit on active links but retains it for tab focus */
+}
+
+h1, h2, h3 {
+ font-weight: normal;
+ font-size: 1.25em;
+ margin: 0.8em 0 0.3em 0;
+}
+
+ul {
+ padding: 0;
+}
+
+img {
+ border: 0;
+}
+
+/* GENERAL */
+
+#grailsLogo a {
+ display: inline-block;
+ margin: 1em;
+}
+
+.content {
+}
+
+.content h1 {
+ border-bottom: 1px solid #CCCCCC;
+ padding: 0 0 1em 0;
+}
+
+.scaffold-list h1 {
+ border: none;
+}
+
+.footer img {
+ height: 80px;
+ margin-right: 25px;
+ margin-bottom: 15px;
+ clear: bottom;
+}
+
+.footer strong a {
+ color: white;
+ text-decoration: none;
+ font-size: 1.1rem;
+}
+
+.footer {
+ background: #424649;
+ color: #ffffff;
+ clear: both;
+ font-size: 1em;
+ padding: 2em 0;
+ min-height: 1em;
+}
+
+.footer a {
+ color: #feb672;
+}
+
+.spinner {
+ background: url(../images/spinner.gif) 50% 50% no-repeat transparent;
+ height: 16px;
+ width: 16px;
+ padding: 0.5em;
+ position: absolute;
+ right: 0;
+ top: 0;
+ text-indent: -9999px;
+}
+
+/* NAVIGATION MENU */
+
+.nav {
+ zoom: 1;
+}
+
+.nav ul {
+ overflow: hidden;
+ padding-left: 0;
+ zoom: 1;
+}
+
+.nav li {
+ display: block;
+ float: left;
+ list-style-type: none;
+ margin-right: 0.5em;
+ padding: 0;
+}
+
+.nav a {
+ color: #666666;
+ display: block;
+ padding: 0.25em 0.7em;
+ text-decoration: none;
+ -moz-border-radius: 0.3em;
+ -webkit-border-radius: 0.3em;
+ border-radius: 0.3em;
+}
+
+.nav li.dropdown-item a {
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+
+.nav a:active, .nav a:visited {
+ color: #666666;
+}
+
+.nav a:focus, .nav a:hover {
+ background-color: #999999;
+ color: #ffffff;
+ outline: none;
+ text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.8);
+}
+
+.no-borderradius .nav a:focus, .no-borderradius .nav a:hover {
+ background-color: transparent;
+ color: #444444;
+ text-decoration: underline;
+}
+
+.nav a.home, .nav a.list, .nav a.create {
+ background-position: 0.7em center;
+ background-repeat: no-repeat;
+ text-indent: 25px;
+}
+
+.nav a.home {
+ background-image: url(../images/skin/house.png);
+}
+
+.nav a.list {
+ background-image: url(../images/skin/database_table.png);
+}
+
+.nav a.create {
+ background-image: url(../images/skin/database_add.png);
+}
+
+.nav li.dropdown.show ul.dropdown-menu {
+ background-color: #424649;
+}
+
+/* CREATE/EDIT FORMS AND SHOW PAGES */
+
+fieldset,
+.property-list {
+ margin: 0.6em 1.25em 0 1.25em;
+ padding: 0.3em 1.8em 1.25em;
+ position: relative;
+ zoom: 1;
+ border: none;
+}
+
+.property-list .fieldcontain {
+ list-style: none;
+ overflow: hidden;
+ zoom: 1;
+}
+
+.fieldcontain {
+ margin-top: 1em;
+}
+
+.fieldcontain label,
+.fieldcontain .property-label {
+ color: #666666;
+ text-align: right;
+ width: 25%;
+}
+
+.fieldcontain .property-label {
+ float: left;
+}
+
+.fieldcontain .property-value {
+ display: block;
+ margin-left: 27%;
+}
+
+label {
+ cursor: pointer;
+ display: inline-block;
+ margin: 0 0.5em 0 0;
+}
+
+input, select, textarea {
+ background-color: #fcfcfc;
+ border: 1px solid #cccccc;
+ font-size: 1em;
+ padding: 0.2em 0.4em;
+}
+
+select {
+ padding: 0.2em 0.2em 0.2em 0;
+}
+
+select[multiple] {
+ vertical-align: top;
+}
+
+textarea {
+ width: 250px;
+ height: 150px;
+ overflow: auto; /* IE always renders vertical scrollbar without this */
+ vertical-align: top;
+}
+
+input[type=checkbox], input[type=radio] {
+ background-color: transparent;
+ border: 0;
+ padding: 0;
+}
+
+input:focus, select:focus, textarea:focus {
+ background-color: #ffffff;
+ border: 1px solid #eeeeee;
+ outline: 0;
+ -moz-box-shadow: 0 0 0.5em #ffffff;
+ -webkit-box-shadow: 0 0 0.5em #ffffff;
+ box-shadow: 0 0 0.5em #ffffff;
+}
+
+.required-indicator {
+ color: #cc0000;
+ display: inline-block;
+ font-weight: bold;
+ margin-left: 0.3em;
+ position: relative;
+ top: 0.1em;
+}
+
+ul.one-to-many {
+ display: inline-block;
+ list-style-position: inside;
+ vertical-align: top;
+}
+
+ul.one-to-many li.add {
+ list-style-type: none;
+}
+
+/* EMBEDDED PROPERTIES */
+
+fieldset.embedded {
+ background-color: transparent;
+ border: 1px solid #CCCCCC;
+ margin-left: 0;
+ margin-right: 0;
+ padding-left: 0;
+ padding-right: 0;
+ -moz-box-shadow: none;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
+
+fieldset.embedded legend {
+ margin: 0 1em;
+}
+
+/* MESSAGES AND ERRORS */
+
+.errors,
+.message {
+ margin: 1em 0;
+ padding: 0.5em;
+}
+
+.message {
+ background: #f3f3ff;
+ border: 1px solid #b2d1ff;
+ color: #006dba;
+ -moz-box-shadow: 0 0 0.25em #b2d1ff;
+ -webkit-box-shadow: 0 0 0.25em #b2d1ff;
+ box-shadow: 0 0 0.25em #b2d1ff;
+}
+
+.errors {
+ background: #fff3f3;
+ border: 1px solid #ffaaaa;
+ color: #cc0000;
+ -moz-box-shadow: 0 0 0.25em #ff8888;
+ -webkit-box-shadow: 0 0 0.25em #ff8888;
+ box-shadow: 0 0 0.25em #ff8888;
+}
+
+.errors ul,
+.message {
+ padding: 0;
+}
+
+.errors li {
+ list-style: none;
+ background: transparent url(../images/skin/exclamation.png) 0.5em 50% no-repeat;
+ text-indent: 2.2em;
+}
+
+.message {
+ background: transparent url(../images/skin/information.png) 0.5em 50% no-repeat;
+ text-indent: 2.2em;
+}
+
+/* form fields with errors */
+
+.error input, .error select, .error textarea {
+ background: #fff3f3;
+ border-color: #ffaaaa;
+ color: #cc0000;
+}
+
+.error input:focus, .error select:focus, .error textarea:focus {
+ -moz-box-shadow: 0 0 0.5em #ffaaaa;
+ -webkit-box-shadow: 0 0 0.5em #ffaaaa;
+ box-shadow: 0 0 0.5em #ffaaaa;
+}
+
+/* same effects for browsers that support HTML5 client-side validation (these have to be specified separately or IE will ignore the entire rule) */
+
+input:invalid, select:invalid, textarea:invalid {
+ background: #fff3f3;
+ border-color: #ffaaaa;
+ color: #cc0000;
+}
+
+input:invalid:focus, select:invalid:focus, textarea:invalid:focus {
+ -moz-box-shadow: 0 0 0.5em #ffaaaa;
+ -webkit-box-shadow: 0 0 0.5em #ffaaaa;
+ box-shadow: 0 0 0.5em #ffaaaa;
+}
+
+/* TABLES */
+
+table {
+ border: 1px solid #DFDFDF;
+ border-collapse: collapse;
+ width: 100%;
+ margin-bottom: 1em;
+}
+
+tr {
+ border: 0;
+}
+
+tr>td:first-child, tr>th:first-child {
+ padding-left: 1.25em;
+}
+
+tr>td:last-child, tr>th:last-child {
+ padding-right: 1.25em;
+}
+
+td, th {
+ line-height: 1.5em;
+ padding: 0.5em 0.6em;
+ text-align: left;
+ vertical-align: top;
+}
+
+th {
+ background-color: #efefef;
+ background-image: -moz-linear-gradient(top, #ffffff, #eaeaea);
+ background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #ffffff), color-stop(1, #eaeaea));
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorStr = '#ffffff', EndColorStr = '#eaeaea');
+ -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#ffffff', EndColorStr='#eaeaea')";
+ color: #666666;
+ font-weight: bold;
+ line-height: 1.7em;
+ padding: 0.2em 0.6em;
+}
+
+thead th {
+ white-space: nowrap;
+}
+
+th a {
+ display: block;
+ text-decoration: none;
+}
+
+th a:link, th a:visited {
+ color: #666666;
+}
+
+th a:hover, th a:focus {
+ color: #333333;
+}
+
+th.sortable a {
+ background-position: right;
+ background-repeat: no-repeat;
+ padding-right: 1.1em;
+}
+
+th.asc a {
+ background-image: url(../images/skin/sorted_asc.gif);
+}
+
+th.desc a {
+ background-image: url(../images/skin/sorted_desc.gif);
+}
+
+.odd {
+ background: #f7f7f7;
+}
+
+.even {
+ background: #ffffff;
+}
+
+th:hover, tr:hover {
+ background: #f5f5f5;
+}
+
+/* PAGINATION */
+
+.pagination {
+ border-top: 0;
+ margin: 0.8em 1em 0.3em;
+ padding: 0.3em 0.2em;
+ text-align: center;
+ -moz-box-shadow: 0 0 3px 1px #AAAAAA;
+ -webkit-box-shadow: 0 0 3px 1px #AAAAAA;
+ box-shadow: 0 0 3px 1px #AAAAAA;
+ background-color: #EFEFEF;
+}
+
+.pagination a,
+.pagination .currentStep {
+ color: #666666;
+ display: inline-block;
+ margin: 0 0.1em;
+ padding: 0.25em 0.7em;
+ text-decoration: none;
+ -moz-border-radius: 0.3em;
+ -webkit-border-radius: 0.3em;
+ border-radius: 0.3em;
+}
+
+.pagination a:hover, .pagination a:focus,
+.pagination .currentStep {
+ background-color: #999999;
+ color: #ffffff;
+ outline: none;
+ text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.8);
+}
+
+.no-borderradius .pagination a:hover, .no-borderradius .pagination a:focus,
+.no-borderradius .pagination .currentStep {
+ background-color: transparent;
+ color: #444444;
+ text-decoration: underline;
+}
+
+/* ACTION BUTTONS */
+
+.buttons {
+ background-color: #efefef;
+ overflow: hidden;
+ padding: 0.3em;
+ -moz-box-shadow: 0 0 3px 1px #aaaaaa;
+ -webkit-box-shadow: 0 0 3px 1px #aaaaaa;
+ box-shadow: 0 0 3px 1px #aaaaaa;
+ margin: 0.1em 0 0 0;
+ border: none;
+}
+
+.buttons input,
+.buttons a {
+ background-color: transparent;
+ border: 0;
+ color: #666666;
+ cursor: pointer;
+ display: inline-block;
+ margin: 0 0.25em 0;
+ overflow: visible;
+ padding: 0.25em 0.7em;
+ text-decoration: none;
+
+ -moz-border-radius: 0.3em;
+ -webkit-border-radius: 0.3em;
+ border-radius: 0.3em;
+}
+
+.buttons input:hover, .buttons input:focus,
+.buttons a:hover, .buttons a:focus {
+ background-color: #999999;
+ color: #ffffff;
+ outline: none;
+ text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.8);
+ -moz-box-shadow: none;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
+
+.no-borderradius .buttons input:hover, .no-borderradius .buttons input:focus,
+.no-borderradius .buttons a:hover, .no-borderradius .buttons a:focus {
+ background-color: transparent;
+ color: #444444;
+ text-decoration: underline;
+}
+
+.buttons .delete, .buttons .edit, .buttons .save {
+ background-position: 0.7em center;
+ background-repeat: no-repeat;
+ text-indent: 25px;
+}
+
+.buttons .delete {
+ background-image: url(../images/skin/database_delete.png);
+}
+
+.buttons .edit {
+ background-image: url(../images/skin/database_edit.png);
+}
+
+.buttons .save {
+ background-image: url(../images/skin/database_save.png);
+}
+
+a.skip {
+ position: absolute;
+ left: -9999px;
+}
+
+.grails-logo-container {
+ background: #7c7c7c no-repeat 50% 30%;
+ margin-bottom: 20px;
+ color: white;
+ height:300px;
+ text-align:center;
+}
+
+img.grails-logo {
+ height:340px;
+ margin-top:-10px;
+}
diff --git a/grails-test-examples/geb-gebconfig/grails-app/assets/stylesheets/mobile.css b/grails-test-examples/geb-gebconfig/grails-app/assets/stylesheets/mobile.css
new file mode 100644
index 00000000000..36feca9ceeb
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/assets/stylesheets/mobile.css
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+/* Styles for mobile devices */
+
+@media screen and (max-width: 480px) {
+ .nav {
+ padding: 0.5em;
+ }
+
+ .nav li {
+ margin: 0 0.5em 0 0;
+ padding: 0.25em;
+ }
+
+ /* Hide individual steps in pagination, just have next & previous */
+ .pagination .step, .pagination .currentStep {
+ display: none;
+ }
+
+ .pagination .prevLink {
+ float: left;
+ }
+
+ .pagination .nextLink {
+ float: right;
+ }
+
+ /* pagination needs to wrap around floated buttons */
+ .pagination {
+ overflow: hidden;
+ }
+
+ /* slightly smaller margin around content body */
+ fieldset,
+ .property-list {
+ padding: 0.3em 1em 1em;
+ }
+
+ input, textarea {
+ width: 100%;
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ box-sizing: border-box;
+ }
+
+ select, input[type=checkbox], input[type=radio], input[type=submit], input[type=button], input[type=reset] {
+ width: auto;
+ }
+
+ /* hide all but the first column of list tables */
+ .scaffold-list td:not(:first-child),
+ .scaffold-list th:not(:first-child) {
+ display: none;
+ }
+
+ .scaffold-list thead th {
+ text-align: center;
+ }
+
+ /* stack form elements */
+ .fieldcontain {
+ margin-top: 0.6em;
+ }
+
+ .fieldcontain label,
+ .fieldcontain .property-label,
+ .fieldcontain .property-value {
+ display: block;
+ float: none;
+ margin: 0 0 0.25em 0;
+ text-align: left;
+ width: auto;
+ }
+
+ .errors ul,
+ .message p {
+ margin: 0.5em;
+ }
+
+ .error ul {
+ margin-left: 0;
+ }
+}
diff --git a/grails-test-examples/geb-gebconfig/grails-app/conf/application.yml b/grails-test-examples/geb-gebconfig/grails-app/conf/application.yml
new file mode 100644
index 00000000000..f77a9078618
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/conf/application.yml
@@ -0,0 +1,109 @@
+# 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.
+
+info:
+ app:
+ name: '@info.app.name@'
+ version: '@info.app.version@'
+ grailsVersion: '@info.app.grailsVersion@'
+grails:
+ views:
+ default:
+ codec: html
+ gsp:
+ encoding: UTF-8
+ htmlcodec: xml
+ codecs:
+ expression: html
+ scriptlet: html
+ taglib: none
+ staticparts: none
+ mime:
+ disable:
+ accept:
+ header:
+ userAgents:
+ - Gecko
+ - WebKit
+ - Presto
+ - Trident
+ types:
+ all: '*/*'
+ atom: application/atom+xml
+ css: text/css
+ csv: text/csv
+ form: application/x-www-form-urlencoded
+ html:
+ - text/html
+ - application/xhtml+xml
+ js: text/javascript
+ json:
+ - application/json
+ - text/json
+ multipartForm: multipart/form-data
+ pdf: application/pdf
+ rss: application/rss+xml
+ text: text/plain
+ hal:
+ - application/hal+json
+ - application/hal+xml
+ xml:
+ - text/xml
+ - application/xml
+ codegen:
+ defaultPackage: org.demo.spock
+ profile: web
+dataSource:
+ driverClassName: org.h2.Driver
+ username: sa
+ password: ''
+ pooled: true
+ jmxExport: true
+environments:
+ development:
+ dataSource:
+ dbCreate: create-drop
+ url: jdbc:h2:mem:devDb;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
+ test:
+ dataSource:
+ dbCreate: update
+ url: jdbc:h2:mem:testDb;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
+ production:
+ dataSource:
+ dbCreate: none
+ url: jdbc:h2:./prodDb;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
+ properties:
+ jmxEnabled: true
+ initialSize: 5
+ maxActive: 50
+ minIdle: 5
+ maxIdle: 25
+ maxWait: 10000
+ maxAge: 600000
+ timeBetweenEvictionRunsMillis: 5000
+ minEvictableIdleTimeMillis: 60000
+ validationQuery: SELECT 1
+ validationQueryTimeout: 3
+ validationInterval: 15000
+ testOnBorrow: true
+ testWhileIdle: true
+ testOnReturn: false
+ jdbcInterceptors: ConnectionState
+ defaultTransactionIsolation: 2
+hibernate:
+ cache:
+ queries: false
+ use_second_level_cache: false
+ use_query_cache: false
diff --git a/grails-test-examples/geb-gebconfig/grails-app/conf/logback-spring.xml b/grails-test-examples/geb-gebconfig/grails-app/conf/logback-spring.xml
new file mode 100644
index 00000000000..0606d099f9c
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/conf/logback-spring.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+ true
+
+ ${CONSOLE_LOG_THRESHOLD}
+
+
+ ${CONSOLE_LOG_PATTERN}
+ ${CONSOLE_LOG_CHARSET}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/grails-test-examples/geb-gebconfig/grails-app/conf/spring/resources.groovy b/grails-test-examples/geb-gebconfig/grails-app/conf/spring/resources.groovy
new file mode 100644
index 00000000000..87cc2c3b528
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/conf/spring/resources.groovy
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+// Place your Spring DSL code here
+beans = {
+}
\ No newline at end of file
diff --git a/grails-test-examples/geb-gebconfig/grails-app/controllers/org/demo/spock/UrlMappings.groovy b/grails-test-examples/geb-gebconfig/grails-app/controllers/org/demo/spock/UrlMappings.groovy
new file mode 100644
index 00000000000..07fb1bc9e49
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/controllers/org/demo/spock/UrlMappings.groovy
@@ -0,0 +1,35 @@
+/*
+ * 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
+
+class UrlMappings {
+ static mappings = {
+ "/$controller/$action?/$id?(.$format)?"{
+ constraints {
+ // apply constraints here
+ }
+ }
+
+ "/"(view:"/index")
+ "500"(view:'/error')
+ "404"(view:'/notFound')
+
+ }
+}
diff --git a/grails-test-examples/geb-gebconfig/grails-app/i18n/messages.properties b/grails-test-examples/geb-gebconfig/grails-app/i18n/messages.properties
new file mode 100644
index 00000000000..6d72d209d5d
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/i18n/messages.properties
@@ -0,0 +1,71 @@
+# 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
+#
+# 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.
+
+default.doesnt.match.message=Property [{0}] of class [{1}] with value [{2}] does not match the required pattern [{3}]
+default.invalid.url.message=Property [{0}] of class [{1}] with value [{2}] is not a valid URL
+default.invalid.creditCard.message=Property [{0}] of class [{1}] with value [{2}] is not a valid credit card number
+default.invalid.email.message=Property [{0}] of class [{1}] with value [{2}] is not a valid e-mail address
+default.invalid.range.message=Property [{0}] of class [{1}] with value [{2}] does not fall within the valid range from [{3}] to [{4}]
+default.invalid.size.message=Property [{0}] of class [{1}] with value [{2}] does not fall within the valid size range from [{3}] to [{4}]
+default.invalid.max.message=Property [{0}] of class [{1}] with value [{2}] exceeds maximum value [{3}]
+default.invalid.min.message=Property [{0}] of class [{1}] with value [{2}] is less than minimum value [{3}]
+default.invalid.max.size.message=Property [{0}] of class [{1}] with value [{2}] exceeds the maximum size of [{3}]
+default.invalid.min.size.message=Property [{0}] of class [{1}] with value [{2}] is less than the minimum size of [{3}]
+default.invalid.validator.message=Property [{0}] of class [{1}] with value [{2}] does not pass custom validation
+default.not.inlist.message=Property [{0}] of class [{1}] with value [{2}] is not contained within the list [{3}]
+default.blank.message=Property [{0}] of class [{1}] cannot be blank
+default.not.equal.message=Property [{0}] of class [{1}] with value [{2}] cannot equal [{3}]
+default.null.message=Property [{0}] of class [{1}] cannot be null
+default.not.unique.message=Property [{0}] of class [{1}] with value [{2}] must be unique
+
+default.paginate.prev=Previous
+default.paginate.next=Next
+default.boolean.true=True
+default.boolean.false=False
+default.date.format=yyyy-MM-dd HH:mm:ss z
+default.number.format=0
+
+default.created.message={0} {1} created
+default.updated.message={0} {1} updated
+default.deleted.message={0} {1} deleted
+default.not.deleted.message={0} {1} could not be deleted
+default.not.found.message={0} not found with id {1}
+default.optimistic.locking.failure=Another user has updated this {0} while you were editing
+
+default.home.label=Home
+default.list.label={0} List
+default.add.label=Add {0}
+default.new.label=New {0}
+default.create.label=Create {0}
+default.show.label=Show {0}
+default.edit.label=Edit {0}
+
+default.button.create.label=Create
+default.button.edit.label=Edit
+default.button.update.label=Update
+default.button.delete.label=Delete
+default.button.delete.confirm.message=Are you sure?
+
+# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
+typeMismatch.java.net.URL=Property {0} must be a valid URL
+typeMismatch.java.net.URI=Property {0} must be a valid URI
+typeMismatch.java.util.Date=Property {0} must be a valid Date
+typeMismatch.java.lang.Double=Property {0} must be a valid number
+typeMismatch.java.lang.Integer=Property {0} must be a valid number
+typeMismatch.java.lang.Long=Property {0} must be a valid number
+typeMismatch.java.lang.Short=Property {0} must be a valid number
+typeMismatch.java.math.BigDecimal=Property {0} must be a valid number
+typeMismatch.java.math.BigInteger=Property {0} must be a valid number
+typeMismatch=Property {0} is type-mismatched
diff --git a/grails-test-examples/geb-gebconfig/grails-app/init/org/demo/spock/Application.groovy b/grails-test-examples/geb-gebconfig/grails-app/init/org/demo/spock/Application.groovy
new file mode 100644
index 00000000000..e106e3fc890
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/init/org/demo/spock/Application.groovy
@@ -0,0 +1,31 @@
+/*
+ * 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.boot.GrailsApp
+import grails.boot.config.GrailsAutoConfiguration
+import groovy.transform.CompileStatic
+
+@CompileStatic
+class Application extends GrailsAutoConfiguration {
+ static void main(String[] args) {
+ GrailsApp.run(Application, args)
+ }
+}
diff --git a/grails-test-examples/geb-gebconfig/grails-app/init/org/demo/spock/BootStrap.groovy b/grails-test-examples/geb-gebconfig/grails-app/init/org/demo/spock/BootStrap.groovy
new file mode 100644
index 00000000000..5d4b18e5921
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/init/org/demo/spock/BootStrap.groovy
@@ -0,0 +1,29 @@
+/*
+ * 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
+
+class BootStrap {
+
+ def init = {
+ }
+
+ def destroy = {
+ }
+}
\ No newline at end of file
diff --git a/grails-test-examples/geb-gebconfig/grails-app/views/error.gsp b/grails-test-examples/geb-gebconfig/grails-app/views/error.gsp
new file mode 100644
index 00000000000..313089cae41
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/views/error.gsp
@@ -0,0 +1,49 @@
+<%--
+ ~ 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.
+ --%>
+
+
+
+ Grails Runtime Exception Error
+
+
+
+
+
+
+
+
+
+
+
+
+
+ An error has occurred
+ Exception: ${exception}
+ Message: ${message}
+ Path: ${path}
+
+
+
+
+
+ An error has occurred
+
+
+
+
diff --git a/grails-test-examples/geb-gebconfig/grails-app/views/index.gsp b/grails-test-examples/geb-gebconfig/grails-app/views/index.gsp
new file mode 100644
index 00000000000..730260d1cbb
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/views/index.gsp
@@ -0,0 +1,96 @@
+<%--
+ ~ 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.
+ --%>
+
+
+
+
+ Welcome to Grails
+
+
+
+
+ Application Status
+
+
+
+ Artefacts
+
+
+
+ Installed Plugins
+
+
+
+
+
+
+
+
+
+ Welcome to Grails
+
+
+ Congratulations, you have successfully started your first Grails application! At the moment
+ this is the default page, feel free to modify it to either redirect to a controller or display
+ whatever content you may choose. Below is a list of controllers that are currently deployed in
+ this application, click on each to execute its default action:
+
+
+
+
Available Controllers:
+
+
+
+ ${c.fullName}
+
+
+
+
+
+
+
+
+
+
diff --git a/grails-test-examples/geb-gebconfig/grails-app/views/layouts/main.gsp b/grails-test-examples/geb-gebconfig/grails-app/views/layouts/main.gsp
new file mode 100644
index 00000000000..645477d0074
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/views/layouts/main.gsp
@@ -0,0 +1,91 @@
+<%--
+ ~ 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.
+ --%>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/grails-test-examples/geb-gebconfig/grails-app/views/notFound.gsp b/grails-test-examples/geb-gebconfig/grails-app/views/notFound.gsp
new file mode 100644
index 00000000000..710257a64ab
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/views/notFound.gsp
@@ -0,0 +1,32 @@
+<%--
+ ~ 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.
+ --%>
+
+
+
+ Page Not Found
+
+
+
+
+
+ Error: Page Not Found (404)
+ Path: ${request.forwardURI}
+
+
+
diff --git a/grails-test-examples/geb-gebconfig/grails-app/views/serverName/index.gsp b/grails-test-examples/geb-gebconfig/grails-app/views/serverName/index.gsp
new file mode 100644
index 00000000000..c90faadae31
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/views/serverName/index.gsp
@@ -0,0 +1,35 @@
+<%--
+ ~ 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.
+ --%>
+<%--
+ Created by IntelliJ IDEA.
+ User: sbglasius
+ Date: 25/11/2024
+ Time: 08.09
+--%>
+
+<%@ page contentType="text/html;charset=UTF-8" %>
+
+
+
+
+
+
+Server name: ${request.serverName}
+
+
\ No newline at end of file
diff --git a/grails-test-examples/geb-gebconfig/grails-app/views/upload/index.gsp b/grails-test-examples/geb-gebconfig/grails-app/views/upload/index.gsp
new file mode 100644
index 00000000000..94f3b5d44fc
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/views/upload/index.gsp
@@ -0,0 +1,30 @@
+<%@ page contentType="text/html;charset=UTF-8" %>
+<%--
+ ~ 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.
+ --%>
+
+
+ Upload Test
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/grails-test-examples/geb-gebconfig/grails-app/views/upload/store.gsp b/grails-test-examples/geb-gebconfig/grails-app/views/upload/store.gsp
new file mode 100644
index 00000000000..a40099744fc
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/grails-app/views/upload/store.gsp
@@ -0,0 +1,27 @@
+<%@ page contentType="text/html;charset=UTF-8" %>
+<%--
+ ~ 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.
+ --%>
+
+
+ File Uploaded
+
+
+${text}
+
+
\ No newline at end of file
diff --git a/grails-test-examples/geb-gebconfig/src/integration-test/groovy/org/demo/spock/GebConfigSpec.groovy b/grails-test-examples/geb-gebconfig/src/integration-test/groovy/org/demo/spock/GebConfigSpec.groovy
new file mode 100644
index 00000000000..782bd9ccfe8
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/src/integration-test/groovy/org/demo/spock/GebConfigSpec.groovy
@@ -0,0 +1,54 @@
+/*
+ * 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 org.openqa.selenium.remote.RemoteWebDriver
+import org.demo.spock.pages.HomePage
+
+import grails.plugin.geb.ContainerGebSpec
+import grails.testing.mixin.integration.Integration
+
+/**
+ * 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'() {
+ expect: 'the driver to be a RemoteWebDriver'
+ driver instanceof RemoteWebDriver
+
+ when: 'getting the capabilities of the driver'
+ def capabilities = ((RemoteWebDriver) driver).capabilities
+
+ then: 'our custom capability set in GebConfig is available'
+ capabilities.getCapability('grails:gebConfigUsed') == true
+
+ and: 'the driver should have Chrome-specific capabilities'
+ capabilities.browserName == 'chrome'
+
+ when: 'navigating to a page'
+ to(HomePage)
+
+ then: 'the session should be active'
+ driver.sessionId != null
+ }
+}
diff --git a/grails-test-examples/geb-gebconfig/src/integration-test/groovy/org/demo/spock/pages/HomePage.groovy b/grails-test-examples/geb-gebconfig/src/integration-test/groovy/org/demo/spock/pages/HomePage.groovy
new file mode 100644
index 00000000000..4c4e5896461
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/src/integration-test/groovy/org/demo/spock/pages/HomePage.groovy
@@ -0,0 +1,29 @@
+/*
+ * 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.pages
+
+import geb.Page
+
+class HomePage extends Page {
+
+ static url = '/'
+ static at = { title == 'Welcome to Grails' }
+
+}
\ No newline at end of file
diff --git a/grails-test-examples/geb-gebconfig/src/integration-test/resources/GebConfig.groovy b/grails-test-examples/geb-gebconfig/src/integration-test/resources/GebConfig.groovy
new file mode 100644
index 00000000000..fe22ed89f8f
--- /dev/null
+++ b/grails-test-examples/geb-gebconfig/src/integration-test/resources/GebConfig.groovy
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+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
+ def prefs = [
+ 'credentials_enable_service': false,
+ 'profile.password_manager_enabled': false,
+ 'profile.password_manager_leak_detection': false
+ ]
+
+ def 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 reportFiles) {
+ reportFiles.each {
+ println "[[ATTACHMENT|$it.absolutePath]]"
+ }
+ }
+}
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 69ee480af15..8be9e206cfc 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -349,6 +349,7 @@ include(
'grails-test-examples-datasources',
'grails-test-examples-namespaces',
'grails-test-examples-geb',
+ 'grails-test-examples-geb-gebconfig',
'grails-test-examples-gorm',
'grails-test-examples-gsp-layout',
'grails-test-examples-gsp-sitemesh3',
@@ -376,6 +377,7 @@ project(':grails-test-examples-app3').projectDir = file('grails-test-examples/ap
project(':grails-test-examples-external-configuration').projectDir = file('grails-test-examples/external-configuration')
project(':grails-test-examples-datasources').projectDir = file('grails-test-examples/datasources')
project(':grails-test-examples-geb').projectDir = file('grails-test-examples/geb')
+project(':grails-test-examples-geb-gebconfig').projectDir = file('grails-test-examples/geb-gebconfig')
project(':grails-test-examples-namespaces').projectDir = file('grails-test-examples/namespaces')
project(':grails-test-examples-gorm').projectDir = file('grails-test-examples/gorm')
project(':grails-test-examples-gsp-layout').projectDir = file('grails-test-examples/gsp-layout')