From 906367f6211ac672484a9f89d3b6b9550900f454 Mon Sep 17 00:00:00 2001 From: Kieu Xuan Thoai Date: Sun, 20 Jul 2025 14:45:44 +0700 Subject: [PATCH 1/3] Add WebDriverListener example with logging and updated JUnit(pom.xml) config for Windows - Added example of `WebDriverListener` with logging before `findElement` and `click`. - Included logic to capture screenshots on error for better debugging. - Updated `junit-platform.properties` for Windows compatibility: - forkCount = 0 - junit.jupiter.execution.parallel.config.fixed.parallelism = 1 - junit.jupiter.execution.parallel.config.fixed.max-pool-size = 1 -Run test : mvn clean test -Dtest=WebDriverListenerTest --- examples/java/pom.xml | 32 ++-- .../listeners/CustomWebDriverListener.java | 157 ++++++++++++++++++ .../listeners/WebDriverListenerTest.java | 57 +++++++ .../support_features/listeners.en.md | 5 +- 4 files changed, 236 insertions(+), 15 deletions(-) create mode 100644 examples/java/src/test/java/dev/selenium/listeners/CustomWebDriverListener.java create mode 100644 examples/java/src/test/java/dev/selenium/listeners/WebDriverListenerTest.java diff --git a/examples/java/pom.xml b/examples/java/pom.xml index 898f27c657a4..5c43d8ea9af5 100644 --- a/examples/java/pom.xml +++ b/examples/java/pom.xml @@ -1,7 +1,7 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 dev.selenium @@ -18,11 +18,11 @@ - sonatype-nexus-snapshots - https://oss.sonatype.org/content/repositories/snapshots/ - - true - + sonatype-nexus-snapshots + https://oss.sonatype.org/content/repositories/snapshots/ + + true + @@ -56,19 +56,23 @@ org.apache.maven.plugins maven-surefire-plugin 3.5.3 + + + 0 - - junit.jupiter.execution.parallel.enabled = true + junit.jupiter.execution.parallel.enabled = true junit.jupiter.execution.parallel.mode.default = concurrent - junit.jupiter.execution.parallel.config.strategy = fixed - junit.jupiter.execution.parallel.config.fixed.parallelism = ${surefire.parallel} - junit.jupiter.execution.parallel.config.fixed.max-pool-size = ${surefire.parallel} - + junit.jupiter.execution.parallel.config.strategy = fixed + junit.jupiter.execution.parallel.config.fixed.parallelism = 1 + junit.jupiter.execution.parallel.config.fixed.max-pool-size = 1 3 - + \ No newline at end of file diff --git a/examples/java/src/test/java/dev/selenium/listeners/CustomWebDriverListener.java b/examples/java/src/test/java/dev/selenium/listeners/CustomWebDriverListener.java new file mode 100644 index 000000000000..efc300b28ed8 --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/listeners/CustomWebDriverListener.java @@ -0,0 +1,157 @@ +package dev.selenium.listeners; + +import org.openqa.selenium.*; +import org.openqa.selenium.remote.RemoteWebDriver; +import org.openqa.selenium.support.events.WebDriverListener; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class CustomWebDriverListener implements WebDriverListener { + + private static final Logger logger = Logger.getLogger(CustomWebDriverListener.class.getName()); + // Link refe color https://gist.github.com/dainkaplan/4651352 + + private static final String RESET = "\u001B[0m"; + private static final String GREEN = "\u001B[32m"; + + private String formatArgs(Object[] args) { + if (args == null || args.length == 0) + return "[]"; + StringBuilder sb = new StringBuilder("["); + for (Object arg : args) { + sb.append(arg).append(", "); + } + if (sb.length() > 1) + sb.setLength(sb.length() - 2); + sb.append("]"); + return sb.toString(); + } + + private String getDriverInfo(WebDriver driver) { + if (driver instanceof RemoteWebDriver remoteDriver) { + Capabilities caps = remoteDriver.getCapabilities(); + String sessionId = remoteDriver.getSessionId().toString(); + return String.format("Browser: %s, Version: %s, Platform: %s, SessionId: %s", + caps.getBrowserName(), + caps.getBrowserVersion(), + caps.getPlatformName(), + sessionId); + } else { + return "Unknown WebDriver instance"; + } + } + + private String getElementInfo(WebElement element) { + try { + return element.toString().replaceAll(".*-> ", "").replaceAll("]", ""); + } catch (Exception e) { + return "Unknown Element"; + } + } + + // -- WebDriver call hooks -- + + @Override + public void beforeAnyWebDriverCall(WebDriver driver, Method method, Object[] args) { + logger.info("BEFORE: Driver: " + GREEN + driver + RESET + ", Method: " + method.getName() + ", Args: " + + formatArgs(args)); + } + + @Override + public void afterAnyWebDriverCall(WebDriver driver, Method method, Object[] args, Object result) { + logger.info("AFTER: Driver: " + GREEN + driver + RESET + ", Method: " + method.getName() + + ", Args: " + formatArgs(args) + ", Result: " + result); + } + + // -- Click hooks -- + + @Override + public void beforeClick(WebElement element) { + logger.info("BEFORE click -> Element: " + getElementInfo(element)); + } + + @Override + public void afterClick(WebElement element) { + logger.info("AFTER click -> Element: " + getElementInfo(element)); + } + + // -- FindElement hooks -- + + @Override + public void beforeFindElement(WebDriver driver, By locator) { + logger.info("BEFORE findElement -> Locator: " + locator); + try { + if (driver instanceof HasCapabilities) { + List elements = driver.findElements(locator); + logger.info("DEBUG: Number of elements matching '" + locator + "': " + elements.size()); + } + } catch (Exception e) { + logger.warning("Error while counting elements for locator " + locator + ": " + e.getMessage()); + } + } + + @Override + public void afterFindElement(WebDriver driver, By locator, WebElement result) { + logger.info("AFTER findElement -> Locator: " + locator + ", Result: " + getElementInfo(result)); + } + + @Override + public void beforeFindElements(WebDriver driver, By locator) { + logger.info("BEFORE findElements -> Locator: " + locator); + try { + if (driver instanceof HasCapabilities) { + List elements = driver.findElements(locator); + logger.info("DEBUG: Number of elements matching '" + locator + "': " + elements.size()); + } + } catch (Exception e) { + logger.warning("Error while counting elements for locator " + locator + ": " + e.getMessage()); + } + } + + @Override + public void afterFindElements(WebDriver driver, By locator, List elements) { + logger.info("AFTER findElements -> Locator: " + locator + ", Elements found: " + elements.size()); + } + + @Override + public void onError(Object target, Method method, Object[] args, InvocationTargetException e) { + logger.log(Level.SEVERE, "Exception in method '" + method.getName() + "': " + e.getCause(), e); + takeScreenshotOnError(target, method.getName()); + } + + private void takeScreenshotOnError(Object target, String methodName) { + if (target instanceof TakesScreenshot) { + try { + String directoryPath = "images"; + File directory = new File(directoryPath); + if (!directory.exists()) { + directory.mkdirs(); + } + + File screenshot = ((TakesScreenshot) target).getScreenshotAs(OutputType.FILE); + String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); + String fileName = directoryPath + File.separator + "screenshot_error_" + methodName + "_" + timestamp + + ".png"; + Files.copy(screenshot.toPath(), Paths.get(fileName)); + + logger.info(GREEN + "Screenshot saved: " + fileName); + } catch (IOException ioException) { + logger.severe("Failed to save screenshot: " + ioException.getMessage()); + } catch (Exception ex) { + logger.severe("Unexpected error during screenshot capture: " + ex.getMessage()); + } + } else { + logger.info("Target does not support screenshots."); + } + } +} diff --git a/examples/java/src/test/java/dev/selenium/listeners/WebDriverListenerTest.java b/examples/java/src/test/java/dev/selenium/listeners/WebDriverListenerTest.java new file mode 100644 index 000000000000..89e64a7ffa62 --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/listeners/WebDriverListenerTest.java @@ -0,0 +1,57 @@ +package dev.selenium.listeners; + +import org.junit.jupiter.api.*; +import org.openqa.selenium.*; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.support.events.EventFiringDecorator; +import org.openqa.selenium.support.events.WebDriverListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +public class WebDriverListenerTest { + + private WebDriver driver; + + @Test + @Order(1) + public void testWebDriverListener() { + WebDriverListener listener = new CustomWebDriverListener(); + driver = new EventFiringDecorator<>(listener).decorate(new ChromeDriver()); + + driver.get("https://www.selenium.dev/"); + driver.manage().window().maximize(); + + WebElement documentation = driver.findElement(By.cssSelector("a[href='/documentation']")); + + documentation.click(); + + driver.quit(); + + } + + @Test + @Order(2) + public void testWebDriverListenerOnError() { + WebDriverListener listener = new CustomWebDriverListener(); + driver = new EventFiringDecorator<>(listener).decorate(new ChromeDriver()); + + try { + driver.get(null); + } catch (NullPointerException e) { + e.printStackTrace(); + } + + driver.quit(); + + } + +} diff --git a/website_and_docs/content/documentation/webdriver/support_features/listeners.en.md b/website_and_docs/content/documentation/webdriver/support_features/listeners.en.md index cfecbd7cb65f..1cf68f8b26ec 100644 --- a/website_and_docs/content/documentation/webdriver/support_features/listeners.en.md +++ b/website_and_docs/content/documentation/webdriver/support_features/listeners.en.md @@ -10,9 +10,12 @@ aliases: [ These allow you to execute custom actions in every time specific Selenium commands are sent {{< tabpane text=true >}} + {{< tab header="Java" >}} -{{< badge-code >}} +{{< gh-codeblock path="/examples/java/src/test/java/dev/selenium/listeners/WebDriverListenerTest.java#L24-L40" >}} {{< /tab >}} + + {{% tab header="Python" %}} {{< badge-code >}} {{% /tab %}} From f4d4c6f4b604be2f17304f8f2fb5433cc4faca13 Mon Sep 17 00:00:00 2001 From: testervippro <92687973+testervippro@users.noreply.github.com> Date: Sun, 20 Jul 2025 15:14:52 +0700 Subject: [PATCH 2/3] remove Fix malformed XML comment --- examples/java/pom.xml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/examples/java/pom.xml b/examples/java/pom.xml index 5c43d8ea9af5..7d25d0d6a8dc 100644 --- a/examples/java/pom.xml +++ b/examples/java/pom.xml @@ -63,10 +63,7 @@ junit.jupiter.execution.parallel.enabled = true junit.jupiter.execution.parallel.mode.default = concurrent - junit.jupiter.execution.parallel.config.strategy = fixed + junit.jupiter.execution.parallel.config.strategy = fixed junit.jupiter.execution.parallel.config.fixed.parallelism = 1 junit.jupiter.execution.parallel.config.fixed.max-pool-size = 1 @@ -75,4 +72,4 @@ - \ No newline at end of file + From 465c6aab481d67bd20bdedca24a5d277406320f6 Mon Sep 17 00:00:00 2001 From: testervippro <92687973+testervippro@users.noreply.github.com> Date: Sun, 20 Jul 2025 15:16:09 +0700 Subject: [PATCH 3/3] Remove recursive findElements call beforeFindElement --- .../selenium/listeners/CustomWebDriverListener.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/examples/java/src/test/java/dev/selenium/listeners/CustomWebDriverListener.java b/examples/java/src/test/java/dev/selenium/listeners/CustomWebDriverListener.java index efc300b28ed8..835c4bba8795 100644 --- a/examples/java/src/test/java/dev/selenium/listeners/CustomWebDriverListener.java +++ b/examples/java/src/test/java/dev/selenium/listeners/CustomWebDriverListener.java @@ -87,18 +87,6 @@ public void afterClick(WebElement element) { // -- FindElement hooks -- - @Override - public void beforeFindElement(WebDriver driver, By locator) { - logger.info("BEFORE findElement -> Locator: " + locator); - try { - if (driver instanceof HasCapabilities) { - List elements = driver.findElements(locator); - logger.info("DEBUG: Number of elements matching '" + locator + "': " + elements.size()); - } - } catch (Exception e) { - logger.warning("Error while counting elements for locator " + locator + ": " + e.getMessage()); - } - } @Override public void afterFindElement(WebDriver driver, By locator, WebElement result) {