diff --git a/examples/java/pom.xml b/examples/java/pom.xml index 898f27c657a4..7d25d0d6a8dc 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,15 +56,16 @@ 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 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..835c4bba8795 --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/listeners/CustomWebDriverListener.java @@ -0,0 +1,145 @@ +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 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 %}}