This is an automated email from the ASF dual-hosted git repository.

jdaugherty pushed a commit to branch 8.0.x
in repository https://gitbox.apache.org/repos/asf/grails-core.git


The following commit(s) were added to refs/heads/8.0.x by this push:
     new 565f438322 address test flakiness
565f438322 is described below

commit 565f43832230b430af34873503049b606ab66a7d
Author: James Daugherty <[email protected]>
AuthorDate: Thu Jul 9 19:58:39 2026 -0400

    address test flakiness
---
 .../groovy/com/example/pages/LoginPage.groovy      |  7 +--
 .../groovy/com/example/pages/LogoutPage.groovy     |  7 +--
 .../groovy/com/example/pages/NavigationPage.groovy | 53 ++++++++++++++++++++++
 3 files changed, 57 insertions(+), 10 deletions(-)

diff --git 
a/grails-test-examples/scaffolding/src/integrationTest/groovy/com/example/pages/LoginPage.groovy
 
b/grails-test-examples/scaffolding/src/integrationTest/groovy/com/example/pages/LoginPage.groovy
index 94da225537..39c61e9eae 100644
--- 
a/grails-test-examples/scaffolding/src/integrationTest/groovy/com/example/pages/LoginPage.groovy
+++ 
b/grails-test-examples/scaffolding/src/integrationTest/groovy/com/example/pages/LoginPage.groovy
@@ -18,9 +18,7 @@
  */
 package com.example.pages
 
-import geb.Page
-
-class LoginPage extends Page {
+class LoginPage extends NavigationPage {
 
     static String pageTitle = 'Please sign in'
 
@@ -35,7 +33,6 @@ class LoginPage extends Page {
     void login(String username = '[email protected]', String password = 
'letmein') {
         this.username = username
         this.password = password
-        loginButton.click()
-        waitFor { title && title != pageTitle }
+        clickAndWaitForNavigation(loginButton)
     }
 }
diff --git 
a/grails-test-examples/scaffolding/src/integrationTest/groovy/com/example/pages/LogoutPage.groovy
 
b/grails-test-examples/scaffolding/src/integrationTest/groovy/com/example/pages/LogoutPage.groovy
index dcaaae57cf..5bfdf4f56c 100644
--- 
a/grails-test-examples/scaffolding/src/integrationTest/groovy/com/example/pages/LogoutPage.groovy
+++ 
b/grails-test-examples/scaffolding/src/integrationTest/groovy/com/example/pages/LogoutPage.groovy
@@ -18,9 +18,7 @@
  */
 package com.example.pages
 
-import geb.Page
-
-class LogoutPage extends Page {
+class LogoutPage extends NavigationPage {
 
     static String pageTitle = 'Confirm Log Out?'
 
@@ -31,7 +29,6 @@ class LogoutPage extends Page {
     }
 
     void logout() {
-        logoutButton.click()
-        waitFor { title != pageTitle }
+        clickAndWaitForNavigation(logoutButton)
     }
 }
diff --git 
a/grails-test-examples/scaffolding/src/integrationTest/groovy/com/example/pages/NavigationPage.groovy
 
b/grails-test-examples/scaffolding/src/integrationTest/groovy/com/example/pages/NavigationPage.groovy
new file mode 100644
index 0000000000..aac0f30556
--- /dev/null
+++ 
b/grails-test-examples/scaffolding/src/integrationTest/groovy/com/example/pages/NavigationPage.groovy
@@ -0,0 +1,53 @@
+/*
+ *  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 com.example.pages
+
+import org.openqa.selenium.StaleElementReferenceException
+import org.openqa.selenium.WebElement
+
+import geb.Page
+import geb.navigator.Navigator
+
+abstract class NavigationPage extends Page {
+
+    /**
+     * Clicks a button that triggers navigation and waits for the new document 
to
+     * replace the current one. Required when the destination can share the 
current
+     * page's at-check (e.g. a failed login re-rendering the sign-in page), 
where a
+     * title-based wait would otherwise pass against the outgoing document.
+     */
+    protected void clickAndWaitForNavigation(Navigator button) {
+        WebElement oldElement = button.firstElement()
+        button.click()
+        waitForStale(oldElement)
+        waitFor { title }
+    }
+
+    private void waitForStale(WebElement oldElement) {
+        waitFor {
+            try {
+                oldElement.enabled
+                false
+            }
+            catch (StaleElementReferenceException ignored) {
+                true
+            }
+        }
+    }
+}

Reply via email to