diff --git a/grails-doc/src/en/guide/upgrading/upgrading71x.adoc b/grails-doc/src/en/guide/upgrading/upgrading71x.adoc index a8511a90435..9f674e6beae 100644 --- a/grails-doc/src/en/guide/upgrading/upgrading71x.adoc +++ b/grails-doc/src/en/guide/upgrading/upgrading71x.adoc @@ -721,4 +721,38 @@ If this causes issues with existing URL mappings, you can disable it in `applica grails: urlmapping: validateWildcards: false ----- \ No newline at end of file +---- + +===== 2.10 ContainerGebSpec Context Path Support + +`ContainerGebSpec` now automatically includes the server's servlet context path in the browser's base URL. Previously, if your application configured a context path via `server.servlet.context-path`, the `ContainerGebSpec` base URL would only include the protocol, hostname, and port — causing all page navigations to miss the context path and result in 404 errors. + +Starting in Grails 7.1, the context path is looked up from the Spring `Environment` at test setup time and appended to the base URL automatically. No changes are required in your test code — Geb page URLs should remain relative to the context root (e.g., `static url = 'greeting'`), and the framework handles prepending the context path. + +====== Example + +[source,yaml] +.application.yml +---- +server: + servlet: + context-path: /myapp +---- + +[source,groovy] +---- +// Page URL is relative — no need to include /myapp +class GreetingPage extends Page { + static url = 'greeting' + static at = { title == 'Greeting' } +} + +// Navigation automatically resolves to http://host:port/myapp/greeting +@Integration +class MySpec extends ContainerGebSpec { + void 'should reach the greeting page'() { + expect: + to(GreetingPage) + } +} +---- diff --git a/grails-geb/README.md b/grails-geb/README.md index ad01f92df52..f25326fff03 100644 --- a/grails-geb/README.md +++ b/grails-geb/README.md @@ -68,6 +68,24 @@ This requires a [compatible container runtime](https://java.testcontainers.org/s If you choose to use the `ContainerGebSpec` class, as long as you have a compatible container runtime installed, you don't need to do anything else. Just run `./gradlew integrationTest` and a container will be started and configured to start a browser that can access your application under test. +#### Context Path Support + +If your application configures a servlet context path (e.g., `server.servlet.context-path: /myapp`), `ContainerGebSpec` automatically includes it in the browser's base URL. No changes are needed in your test code — page URLs remain relative to the context root: + +```yaml +# application.yml +server: + servlet: + context-path: /myapp +``` + +```groovy +class GreetingPage extends Page { + static url = '/greeting' // relative — resolves to /myapp/greeting + static at = { title == 'Greeting' } +} +``` + #### Parallel Execution Parallel execution of `ContainerGebSpec` specifications is not currently supported. 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 83cc4892963..40cac514b7d 100644 --- a/grails-geb/src/testFixtures/groovy/grails/plugin/geb/WebDriverContainerHolder.groovy +++ b/grails-geb/src/testFixtures/groovy/grails/plugin/geb/WebDriverContainerHolder.groovy @@ -50,6 +50,7 @@ import org.testcontainers.images.PullPolicy import org.testcontainers.utility.DockerImageName import grails.plugin.geb.serviceloader.ServiceRegistry +import grails.util.Holders import static GrailsGebSettings.DEFAULT_AT_CHECK_WAITING import static GrailsGebSettings.DEFAULT_TIMEOUT_IMPLICITLY_WAIT @@ -115,6 +116,15 @@ class WebDriverContainerHolder { } } + private static String findServerContextPath() { + try { + def applicationContext = Holders.findApplicationContext() + return applicationContext?.environment?.getProperty('server.servlet.context-path', '/') + } catch (ignored) { + return '/' + } + } + @PackageScope boolean reinitialize(IMethodInvocation methodInvocation) { def specConf = new WebDriverContainerConfiguration( @@ -285,8 +295,19 @@ class WebDriverContainerHolder { void setupBrowserUrl(IMethodInvocation methodInvocation) { if (!browser) return int hostPort = findServerPort(methodInvocation) + String contextPath = findServerContextPath() Testcontainers.exposeHostPorts(hostPort) - browser.baseUrl = "$containerConf.protocol://$containerConf.hostName:$hostPort" + String baseUrl = "$containerConf.protocol://$containerConf.hostName:$hostPort" + if (contextPath && contextPath != '/') { + if (!contextPath.startsWith('/')) { + contextPath = "/$contextPath" + } + if (!contextPath.endsWith('/')) { + contextPath = "$contextPath/" + } + baseUrl += contextPath + } + browser.baseUrl = baseUrl } private GebTestManager createTestManager() { diff --git a/grails-test-examples/geb-context-path/build.gradle b/grails-test-examples/geb-context-path/build.gradle new file mode 100644 index 00000000000..14205713c24 --- /dev/null +++ b/grails-test-examples/geb-context-path/build.gradle @@ -0,0 +1,79 @@ +/* + * 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. + */ +plugins { + id 'groovy' + id 'org.apache.grails.buildsrc.properties' + id 'org.apache.grails.buildsrc.compile' +} + +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.contextpath' +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-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.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') +} + +apply { + from rootProject.layout.projectDirectory.file('gradle/functional-test-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/test-webjar-asset-config.gradle') + from rootProject.layout.projectDirectory.file('gradle/grails-extension-gradle-config.gradle') +} diff --git a/grails-test-examples/geb-context-path/grails-app/conf/application.yml b/grails-test-examples/geb-context-path/grails-app/conf/application.yml new file mode 100644 index 00000000000..9b1791a3f06 --- /dev/null +++ b/grails-test-examples/geb-context-path/grails-app/conf/application.yml @@ -0,0 +1,94 @@ +# 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. + +server: + servlet: + context-path: /myapp +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.contextpath + 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 +hibernate: + cache: + queries: false + use_second_level_cache: false + use_query_cache: false diff --git a/grails-test-examples/geb-context-path/grails-app/controllers/org/demo/contextpath/GreetingController.groovy b/grails-test-examples/geb-context-path/grails-app/controllers/org/demo/contextpath/GreetingController.groovy new file mode 100644 index 00000000000..f66b61c61a5 --- /dev/null +++ b/grails-test-examples/geb-context-path/grails-app/controllers/org/demo/contextpath/GreetingController.groovy @@ -0,0 +1,27 @@ +/* + * 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.contextpath + +class GreetingController { + + def index() { + [message: 'Hello from Grails'] + } +} diff --git a/grails-test-examples/geb-context-path/grails-app/controllers/org/demo/contextpath/UrlMappings.groovy b/grails-test-examples/geb-context-path/grails-app/controllers/org/demo/contextpath/UrlMappings.groovy new file mode 100644 index 00000000000..0bee3250f85 --- /dev/null +++ b/grails-test-examples/geb-context-path/grails-app/controllers/org/demo/contextpath/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.contextpath + +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-context-path/grails-app/i18n/messages.properties b/grails-test-examples/geb-context-path/grails-app/i18n/messages.properties new file mode 100644 index 00000000000..5b5b72e72b2 --- /dev/null +++ b/grails-test-examples/geb-context-path/grails-app/i18n/messages.properties @@ -0,0 +1,14 @@ +# 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-context-path/grails-app/init/org/demo/contextpath/Application.groovy b/grails-test-examples/geb-context-path/grails-app/init/org/demo/contextpath/Application.groovy new file mode 100644 index 00000000000..7419c991a2b --- /dev/null +++ b/grails-test-examples/geb-context-path/grails-app/init/org/demo/contextpath/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.contextpath + +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-context-path/grails-app/init/org/demo/contextpath/BootStrap.groovy b/grails-test-examples/geb-context-path/grails-app/init/org/demo/contextpath/BootStrap.groovy new file mode 100644 index 00000000000..0f720b306ed --- /dev/null +++ b/grails-test-examples/geb-context-path/grails-app/init/org/demo/contextpath/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.contextpath + +class BootStrap { + + def init = { + } + + def destroy = { + } +} diff --git a/grails-test-examples/geb-context-path/grails-app/views/error.gsp b/grails-test-examples/geb-context-path/grails-app/views/error.gsp new file mode 100644 index 00000000000..8d47085a5f5 --- /dev/null +++ b/grails-test-examples/geb-context-path/grails-app/views/error.gsp @@ -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. + --%> + + + + Error + + + + + diff --git a/grails-test-examples/geb-context-path/grails-app/views/greeting/index.gsp b/grails-test-examples/geb-context-path/grails-app/views/greeting/index.gsp new file mode 100644 index 00000000000..513387d99b5 --- /dev/null +++ b/grails-test-examples/geb-context-path/grails-app/views/greeting/index.gsp @@ -0,0 +1,28 @@ +<%-- + ~ 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. + --%> + + + + Greeting + + +

Greeting

+

${message}

+ + diff --git a/grails-test-examples/geb-context-path/grails-app/views/index.gsp b/grails-test-examples/geb-context-path/grails-app/views/index.gsp new file mode 100644 index 00000000000..47028646bee --- /dev/null +++ b/grails-test-examples/geb-context-path/grails-app/views/index.gsp @@ -0,0 +1,27 @@ +<%-- + ~ 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 + + +

Welcome to Grails

+ + diff --git a/grails-test-examples/geb-context-path/grails-app/views/notFound.gsp b/grails-test-examples/geb-context-path/grails-app/views/notFound.gsp new file mode 100644 index 00000000000..2fe070c2e45 --- /dev/null +++ b/grails-test-examples/geb-context-path/grails-app/views/notFound.gsp @@ -0,0 +1,30 @@ +<%-- + ~ 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 + + + + + diff --git a/grails-test-examples/geb-context-path/grails-cli.yml b/grails-test-examples/geb-context-path/grails-cli.yml new file mode 100644 index 00000000000..ca81c1c1d63 --- /dev/null +++ b/grails-test-examples/geb-context-path/grails-cli.yml @@ -0,0 +1,23 @@ +# 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. + +applicationType: web +defaultPackage: org.demo.contextpath +reloading: devtools +sourceLanguage: groovy +buildTool: gradle +gormImpl: gorm-hibernate5 +servletImpl: spring-boot-starter-tomcat +features: [app-name, asset-pipeline-grails, base, geb-with-testcontainers, gorm-hibernate5, gradle, grails-application, grails-console, grails-dependencies, grails-gorm-testing-support, grails-gradle-plugin, grails-gsp, grails-profiles, grails-url-mappings, grails-web, grails-web-testing-support, grails-wrapper, h2, logback, readme, spock, spring-boot-autoconfigure, spring-boot-starter, spring-boot-starter-tomcat, spring-resources, yaml] diff --git a/grails-test-examples/geb-context-path/src/integration-test/groovy/org/demo/contextpath/ContextPathSpec.groovy b/grails-test-examples/geb-context-path/src/integration-test/groovy/org/demo/contextpath/ContextPathSpec.groovy new file mode 100644 index 00000000000..1d36ebd194d --- /dev/null +++ b/grails-test-examples/geb-context-path/src/integration-test/groovy/org/demo/contextpath/ContextPathSpec.groovy @@ -0,0 +1,48 @@ +/* + * 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.contextpath + +import org.demo.contextpath.pages.GreetingPage +import org.demo.contextpath.pages.HomePage + +import grails.plugin.geb.ContainerGebSpec +import grails.testing.mixin.integration.Integration + +/** + * Verifies that ContainerGebSpec correctly includes the server's + * context path in the browser's base URL. Without the context path + * in the base URL, page navigation would result in 404 errors. + */ +@Integration +class ContextPathSpec extends ContainerGebSpec { + + void 'should navigate to the home page under the configured context path'() { + expect: 'visiting the home page' + to(HomePage) + } + + void 'should navigate to a controller under the configured context path'() { + when: 'visiting a controller page' + to(GreetingPage) + + then: 'the controller action is reached and renders correctly' + messageText == 'Hello from Grails' + } +} diff --git a/grails-test-examples/geb-context-path/src/integration-test/groovy/org/demo/contextpath/pages/GreetingPage.groovy b/grails-test-examples/geb-context-path/src/integration-test/groovy/org/demo/contextpath/pages/GreetingPage.groovy new file mode 100644 index 00000000000..c89d5cb785b --- /dev/null +++ b/grails-test-examples/geb-context-path/src/integration-test/groovy/org/demo/contextpath/pages/GreetingPage.groovy @@ -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. + */ + +package org.demo.contextpath.pages + +import geb.Page + +class GreetingPage extends Page { + + static url = 'greeting' + static at = { title == 'Greeting' } + + static content = { + messageText { $('#message').text() } + } +} diff --git a/grails-test-examples/geb-context-path/src/integration-test/groovy/org/demo/contextpath/pages/HomePage.groovy b/grails-test-examples/geb-context-path/src/integration-test/groovy/org/demo/contextpath/pages/HomePage.groovy new file mode 100644 index 00000000000..fcf6e2bc873 --- /dev/null +++ b/grails-test-examples/geb-context-path/src/integration-test/groovy/org/demo/contextpath/pages/HomePage.groovy @@ -0,0 +1,28 @@ +/* + * 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.contextpath.pages + +import geb.Page + +class HomePage extends Page { + + static url = '' + static at = { title == 'Welcome to Grails' } +} diff --git a/settings.gradle b/settings.gradle index d5577b78322..8134f68c9c5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -374,6 +374,7 @@ include( 'grails-test-examples-exploded', 'grails-test-examples-external-configuration', 'grails-test-examples-geb', + 'grails-test-examples-geb-context-path', 'grails-test-examples-geb-gebconfig', 'grails-test-examples-gorm', 'grails-test-examples-gsp-layout', @@ -412,6 +413,7 @@ project(':grails-test-examples-exploded').projectDir = file('grails-test-example project(':grails-test-examples-datasources').projectDir = file('grails-test-examples/datasources') project(':grails-test-examples-database-cleanup').projectDir = file('grails-test-examples/database-cleanup') project(':grails-test-examples-geb').projectDir = file('grails-test-examples/geb') +project(':grails-test-examples-geb-context-path').projectDir = file('grails-test-examples/geb-context-path') 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')