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

benw pushed a commit to branch javax
in repository https://gitbox.apache.org/repos/asf/tapestry-5.git

commit a56749e6c2d4241c76d8a93ed8e6b2942d13da2a
Author: Ben Weidig <[email protected]>
AuthorDate: Sat Apr 4 16:46:40 2026 +0200

    SeleniumTestCase always write error report (HTML+screenshot)
---
 .../apache/tapestry5/test/SeleniumTestCase.java    | 127 +++++++++++++--------
 1 file changed, 82 insertions(+), 45 deletions(-)

diff --git 
a/tapestry-test/src/main/java/org/apache/tapestry5/test/SeleniumTestCase.java 
b/tapestry-test/src/main/java/org/apache/tapestry5/test/SeleniumTestCase.java
index 70568ad66..b96d1c4a4 100644
--- 
a/tapestry-test/src/main/java/org/apache/tapestry5/test/SeleniumTestCase.java
+++ 
b/tapestry-test/src/main/java/org/apache/tapestry5/test/SeleniumTestCase.java
@@ -12,21 +12,22 @@
 
 package org.apache.tapestry5.test;
 
-import com.thoughtworks.selenium.CommandProcessor;
-import com.thoughtworks.selenium.Selenium;
-import com.thoughtworks.selenium.webdriven.WebDriverBackedSelenium;
-import com.thoughtworks.selenium.webdriven.WebDriverCommandProcessor;
+import java.io.File;
+import java.lang.reflect.Method;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 import org.apache.tapestry5.test.constants.TapestryRunnerConstants;
 import org.openqa.selenium.By;
-import org.openqa.selenium.Capabilities;
 import org.openqa.selenium.JavascriptExecutor;
 import org.openqa.selenium.NoSuchElementException;
 import org.openqa.selenium.StaleElementReferenceException;
 import org.openqa.selenium.WebDriver;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.firefox.FirefoxDriver;
-import org.openqa.selenium.firefox.FirefoxDriverLogLevel;
 import org.openqa.selenium.firefox.FirefoxOptions;
 import org.openqa.selenium.firefox.FirefoxProfile;
 import org.openqa.selenium.firefox.GeckoDriverService;
@@ -38,19 +39,21 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
 import org.testng.ITestContext;
-import org.testng.annotations.*;
+import org.testng.ITestResult;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.BeforeTest;
 import org.testng.xml.XmlTest;
 
-import io.github.bonigarcia.wdm.managers.FirefoxDriverManager;
+import com.thoughtworks.selenium.CommandProcessor;
+import com.thoughtworks.selenium.Selenium;
+import com.thoughtworks.selenium.webdriven.WebDriverBackedSelenium;
+import com.thoughtworks.selenium.webdriven.WebDriverCommandProcessor;
 
-import java.io.File;
-import java.lang.reflect.Method;
-import java.time.Duration;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
+import io.github.bonigarcia.wdm.managers.FirefoxDriverManager;
 
 /**
  * Base class for creating Selenium-based integration test cases. This class 
implements all the
@@ -63,6 +66,8 @@ public abstract class SeleniumTestCase extends Assert 
implements Selenium
 {
     public final static Logger LOGGER = 
LoggerFactory.getLogger(SeleniumTestCase.class);
 
+    public static final long WAIT_TIMEOUT = 
Long.getLong("selenium.wait.timeout", 15L);
+
     /**
      * 15 seconds
      */
@@ -99,6 +104,8 @@ public abstract class SeleniumTestCase extends Assert 
implements Selenium
 
     private ITestContext testContext;
 
+    private boolean errorReportWritten = false;
+
     /**
      * Starts up the servers for the entire test (i.e., for multiple 
TestCases). By placing &lt;parameter&gt; elements
      * inside the appropriate &lt;test&gt; (of your testng.xml configuration
@@ -220,37 +227,54 @@ public abstract class SeleniumTestCase extends Assert 
implements Selenium
 
         FirefoxDriverManager.firefoxdriver().setup();
 
-        File ffProfileTemplate = new 
File(TapestryRunnerConstants.MODULE_BASE_DIR, 
"src/test/conf/ff_profile_template");
         DesiredCapabilities desiredCapabilities = new DesiredCapabilities();
-
         FirefoxOptions options = new FirefoxOptions(desiredCapabilities); 
-        // options.setHeadless(true);
-        // options.setLogLevel(FirefoxDriverLogLevel.TRACE);
-        
-        if (ffProfileTemplate.isDirectory() && ffProfileTemplate.exists())
-        {
-            LOGGER.info("Loading Firefox profile from: {}", ffProfileTemplate);
-            FirefoxProfile profile = new FirefoxProfile(ffProfileTemplate);
-            options.setProfile(profile);
-            // profile.layoutOnDisk();
-        }
-        else 
+
+        // TAP5-2819: Run headless on CI
+        if (Boolean.parseBoolean(System.getProperty("ci", "false")))
         {
-            FirefoxProfile profile = new FirefoxProfile();
-            options.setProfile(profile);
-            profile.setPreference("intl.accept_languages", "en,fr,de");
+            options.addArguments("-headless");
+            options.addArguments("--width=1920");
+            options.addArguments("--height=1080");
         }
-        
+
+        File ffProfileTemplate = new 
File(TapestryRunnerConstants.MODULE_BASE_DIR, 
"src/test/conf/ff_profile_template");
         // From 
https://forums.parasoft.com/discussion/5682/using-selenium-with-firefox-snap-ubuntu
         String osName = System.getProperty("os.name");
-        String profileRoot = osName.contains("Linux") && new 
File("/snap/firefox").exists()
+        String snapProfileRoot = osName.contains("Linux") && new 
File("/snap/firefox").exists()
                 ? createProfileRootInUserHome()
                 : null;
-        FirefoxDriver driver = profileRoot != null
-                ? new FirefoxDriver(createGeckoDriverService(profileRoot), 
options)
+
+        FirefoxProfile profile;
+        if (ffProfileTemplate.isDirectory())
+        {
+            LOGGER.info("Loading Firefox profile from: {}", ffProfileTemplate);
+            profile = new FirefoxProfile(ffProfileTemplate);
+        }
+        else if (snapProfileRoot != null)
+        {
+            File snapSafeDir = new File(snapProfileRoot, "tmp-profile");
+            snapSafeDir.mkdirs();
+            LOGGER.info("Creating Snap-compatible Firefox profile in: {}", 
snapSafeDir);
+
+            profile = new FirefoxProfile(snapSafeDir);
+            profile.setPreference("intl.accept_languages", "en,fr,de");
+        }
+        else
+        {
+            LOGGER.info("Using default Firefox profile");
+            profile = new FirefoxProfile();
+            profile.setPreference("intl.accept_languages", "en,fr,de");
+        }
+
+        options.setProfile(profile);
+
+        FirefoxDriver driver = snapProfileRoot != null
+                ? new FirefoxDriver(createGeckoDriverService(snapProfileRoot), 
options)
                 : new FirefoxDriver(options);
-        
-        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
+
+        // Implicit waiting can interfere with WebDriverWait
+        // driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10L));
 
         CommandProcessor webDriverCommandProcessor = new 
WebDriverCommandProcessor(baseURL, driver);
 
@@ -328,10 +352,8 @@ public abstract class SeleniumTestCase extends Assert 
implements Selenium
     private static String createProfileRootInUserHome() {
         String userHome = System.getProperty("user.home");
         File profileRoot = new File(userHome, 
"snap/firefox/common/.firefox-profile-root");
-        if (!profileRoot.exists()) {
-            if (!profileRoot.mkdirs()) {
-                return null;
-            }
+        if (!profileRoot.exists() && !profileRoot.mkdirs()) {
+            return null;
         }
         return profileRoot.getAbsolutePath();
     }    
@@ -351,12 +373,18 @@ public abstract class SeleniumTestCase extends Assert 
implements Selenium
     {
         String value = xmlTest.getParameter(key);
 
+        if (value == null)
+        {
+            // fall back to JVM system property
+            value = System.getProperty(key);
+        }
+
         return value != null ? value : defaultValue;
     }
 
     private final int getIntParameter(XmlTest xmlTest, String key, int 
defaultValue)
     {
-        String value = xmlTest.getParameter(key);
+        String value = getParameter(xmlTest, key, null);
 
         return value != null ? Integer.parseInt(value) : defaultValue;
     }
@@ -455,6 +483,7 @@ public abstract class SeleniumTestCase extends Assert 
implements Selenium
      */
     protected void writeErrorReport(String reportText)
     {
+        errorReportWritten = true;
         errorReporter.writeErrorReport(reportText);
     }
 
@@ -472,6 +501,8 @@ public abstract class SeleniumTestCase extends Assert 
implements Selenium
     @BeforeMethod
     public void indicateTestMethodName(Method testMethod)
     {
+        errorReportWritten = false;
+
         LOGGER.info("Executing " + testMethod);
 
         
testContext.setAttribute(TapestryTestConstants.CURRENT_TEST_METHOD_ATTRIBUTE, 
testMethod);
@@ -483,8 +514,14 @@ public abstract class SeleniumTestCase extends Assert 
implements Selenium
     }
 
     @AfterMethod
-    public void cleanupTestMethod()
+    public void cleanupTestMethod(ITestResult result)
     {
+        if (result.getStatus() == ITestResult.FAILURE && !errorReportWritten)
+        {
+            Throwable t = result.getThrowable();
+            String message = t != null ? t.toString() : "Test failed";
+            writeErrorReport(message);
+        }
         
testContext.setAttribute(TapestryTestConstants.CURRENT_TEST_METHOD_ATTRIBUTE, 
null);
     }
 
@@ -1403,7 +1440,7 @@ public abstract class SeleniumTestCase extends Assert 
implements Selenium
 
     protected void waitForCondition(ExpectedCondition condition)
     {
-      waitForCondition(condition, 10l);
+      waitForCondition(condition, WAIT_TIMEOUT);
     }
 
     protected void waitForCondition(ExpectedCondition condition, long 
timeoutSeconds)

Reply via email to